DefaultPlayerInventoryManagementService.kt
package io.github.lishangbu.avalon.game.player
import io.github.lishangbu.avalon.dataset.api.service.ItemReader
import io.github.lishangbu.avalon.game.entity.PlayerInventoryItem
import io.github.lishangbu.avalon.game.repository.PlayerInventoryItemRepository
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 DefaultPlayerInventoryManagementService(
private val playerRepository: PlayerRepository,
private val playerInventoryItemRepository: PlayerInventoryItemRepository,
private val itemReader: ItemReader,
) : PlayerInventoryManagementService {
override fun listByPlayerId(playerId: String): List<PlayerInventoryItemView> {
val parsedPlayerId = playerId.toLongOrNull() ?: error("playerId must be a valid long value.")
val inventoryItems =
playerInventoryItemRepository
.findAll()
.filter { item -> item.playerId == parsedPlayerId }
if (inventoryItems.isEmpty()) {
return emptyList()
}
val itemsById = itemReader.findItemsByIds(inventoryItems.map { inventoryItem -> inventoryItem.itemId }.toSet())
return inventoryItems
.sortedBy { inventoryItem -> inventoryItem.itemId }
.map { inventoryItem ->
val item =
requireNotNull(itemsById[inventoryItem.itemId]) {
"Item '${inventoryItem.itemId}' was not found."
}
PlayerInventoryItemView(
playerId = inventoryItem.playerId.toString(),
itemId = item.id.toString(),
itemInternalName = item.internalName,
itemName = item.name,
quantity = inventoryItem.quantity,
)
}
}
@Transactional(rollbackFor = [Exception::class])
override fun grant(command: GrantInventoryItemCommand): PlayerInventoryItemView {
require(command.quantity > 0) { "Grant quantity must be greater than 0." }
val playerId = command.playerId.toLongOrNull() ?: error("playerId must be a valid long value.")
requireNotNull(playerRepository.findNullable(playerId)) {
"Player '$playerId' was not found."
}
val itemData = itemReader.findItemByInternalName(command.itemInternalName) ?: error("Item '${command.itemInternalName}' was not found.")
val itemId = itemData.id
val existing =
playerInventoryItemRepository
.findAll()
.firstOrNull { inventoryItem -> inventoryItem.playerId == playerId && inventoryItem.itemId == itemId }
val saved =
if (existing == null) {
playerInventoryItemRepository.save(
PlayerInventoryItem {
this.playerId = playerId
this.itemId = itemId
quantity = command.quantity
updatedAt = Instant.now()
},
SaveMode.INSERT_ONLY,
)
} else {
playerInventoryItemRepository.save(
PlayerInventoryItem(existing) {
quantity = existing.quantity + command.quantity
updatedAt = Instant.now()
},
)
}
return PlayerInventoryItemView(
playerId = saved.playerId.toString(),
itemId = itemId.toString(),
itemInternalName = itemData.internalName,
itemName = itemData.name,
quantity = saved.quantity,
)
}
}