Newer
Older
bremer / src / main / kotlin / service / PlaylistService.kt
/*
 * Copyright (c) 2023. yo-saito. All Rights Reserved.
 */

package net.piedpiper.bremer.service

import net.piedpiper.bremer.dao.AudioDao
import net.piedpiper.bremer.dao.PlaylistAudioDao
import net.piedpiper.bremer.dao.PlaylistDao
import net.piedpiper.bremer.entity.AudioEntity
import net.piedpiper.bremer.entity.PlaylistAudioEntity
import net.piedpiper.bremer.entity.PlaylistEntity
import net.piedpiper.bremer.exception.BadRequestException
import net.piedpiper.bremer.exception.NotFoundException
import net.piedpiper.bremer.model.api.AllPlaylistResponse
import net.piedpiper.bremer.model.api.PlaylistAudioRequest
import net.piedpiper.bremer.model.api.PlaylistAudioResponse
import net.piedpiper.bremer.model.api.PlaylistRequest
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.*

@Service("bremer.service.PlaylistService")
class PlaylistService(
    @Qualifier("bremer.dao.PlaylistDao")
    private val playlistDao: PlaylistDao,
    @Qualifier("bremer.dao.PlaylistAudioDao")
    private val playlistAudioDao: PlaylistAudioDao,
    @Qualifier("bremer.dao.AudioDao")
    private val audioDao: AudioDao
) {
    @Transactional
    fun getAllPlaylists(): AllPlaylistResponse =
        AllPlaylistResponse(playlistDao.findAllOrderBySequence())

    @Transactional
    fun getPlaylist(slug: String): PlaylistAudioResponse = playlistDao.findOneBySlug(slug)
        ?.let {
            PlaylistAudioResponse(
                playlistEntity = it,
                audioEntityList = it?.playlistAudio
                    ?.mapNotNull { it.audioId }
                    ?.let { ids ->
                        findAllByOrderedIds(ids)
                    }
            )
        } ?: throw NotFoundException()

    private fun findAllByOrderedIds(ids: List<Long>): List<AudioEntity> {
        if (ids.isEmpty()) {
            return emptyList()
        }
        val id2idx = ids.mapIndexed { index, id -> id to index }.toMap()
        return audioDao.findAllByIdIn(ids)
            .sortedBy { id2idx[it.id] }
    }

    private fun findAllByOrderedSlugs(slugs: List<String>): List<AudioEntity> {
        if (slugs.isEmpty()) {
            return emptyList()
        }
        val slug2idx = slugs.mapIndexed { index, slug -> slug to index }.toMap()
        return audioDao.findAllBySlugIn(slugs)
            .sortedBy { slug2idx[it.slug] }
    }

    @Transactional
    fun createPlaylist(request: PlaylistRequest) {
        playlistDao.insertOne(
            PlaylistEntity(
                slug = UUID.randomUUID().toString(),
                name = request.name
            )
        )
    }

    @Transactional
    fun updatePlaylist(slug: String, request: PlaylistRequest) {
        val entity = playlistDao.findOneBySlugWithLock(slug) ?: throw NotFoundException()
        entity.name = request.name
        playlistDao.updateOne(entity)
    }

    @Transactional
    fun deletePlaylist(slug: String) {
        val playlist = playlistDao.findOneBySlugWithLock(slug);
        if (playlist?.playlistAudio?.isNotEmpty() == true) {
            playlistAudioDao.deleteAllByIds(
                playlist.playlistAudio.map { it.id })
        }
        playlistDao.deleteOneBySlug(slug)
    }

    @Transactional
    fun updatePlaylistAudioList(
        slug: String,
        request: PlaylistAudioRequest
    ) {
        playlistDao.findOneBySlug(slug)
            ?.let { playlist ->
                val oldPlaylistAudioList = playlist.playlistAudio
                if (oldPlaylistAudioList.isNotEmpty()) {
                    playlistAudioDao.deleteAllByIds(playlist.playlistAudio
                        .map { it.id }
                        .toList())
                }
                val audioList = findAllByOrderedSlugs(request.audioSlugs)
                if (request.audioSlugs.size != audioList.size) {
                    throw BadRequestException()
                }
                audioList.forEachIndexed { index, audio ->
                    val playlistAudio = if (index < oldPlaylistAudioList.count()) {
                        oldPlaylistAudioList[index]
                    } else {
                        PlaylistAudioEntity()
                    }
                    playlistAudio.playlistId = playlist.id
                    playlistAudio.sequence = index
                    playlistAudio.audioId = audio.id
                    playlistAudioDao.insertOne(playlistAudio)
                }
            } ?: throw NotFoundException()
    }
}