DefaultPlayerManagementService.kt

package io.github.lishangbu.avalon.game.player

import io.github.lishangbu.avalon.game.entity.CreatureStorageBox
import io.github.lishangbu.avalon.game.entity.Player
import io.github.lishangbu.avalon.game.repository.CreatureStorageBoxRepository
import io.github.lishangbu.avalon.game.repository.PlayerRepository
import org.babyfish.jimmer.sql.ast.mutation.SaveMode
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.Instant

@Service
class DefaultPlayerManagementService(
    private val playerRepository: PlayerRepository,
    private val creatureStorageBoxRepository: CreatureStorageBoxRepository,
) : PlayerManagementService {
    @Transactional(rollbackFor = [Exception::class])
    override fun create(command: CreatePlayerCommand): PlayerView {
        val userId = command.userId.toLongOrNull() ?: error("userId must be a valid long value.")
        val nickname = command.nickname.trim()
        require(nickname.isNotEmpty()) { "nickname must not be blank." }

        val now = Instant.now()
        val slotNo =
            (
                playerRepository
                    .findAll()
                    .filter { player -> player.userId == userId }
                    .maxOfOrNull { player -> player.slotNo }
                    ?: 0
            ) + 1

        val saved =
            playerRepository.save(
                Player {
                    this.userId = userId
                    this.slotNo = slotNo
                    this.nickname = nickname
                    avatar = command.avatar?.trim()?.takeIf { value -> value.isNotEmpty() }
                    createdAt = now
                    updatedAt = now
                },
                SaveMode.INSERT_ONLY,
            )

        creatureStorageBoxRepository.save(
            CreatureStorageBox {
                playerId = saved.id
                name = DEFAULT_BOX_NAME
                sortingOrder = 1
                capacity = DEFAULT_BOX_CAPACITY
                createdAt = now
                updatedAt = now
            },
            SaveMode.INSERT_ONLY,
        )

        return PlayerView(
            id = saved.id.toString(),
            userId = saved.userId.toString(),
            slotNo = saved.slotNo,
            nickname = saved.nickname,
            avatar = saved.avatar,
            createdAt = saved.createdAt,
            updatedAt = saved.updatedAt,
        )
    }

    private companion object {
        const val DEFAULT_BOX_NAME: String = "Box 1"
        const val DEFAULT_BOX_CAPACITY: Int = 30
    }
}