初步實作出刀紀錄表

This commit is contained in:
2026-08-04 21:11:59 +08:00
parent fcb5285788
commit 8c99ba1147
10 changed files with 225 additions and 14 deletions
@@ -0,0 +1,43 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.database.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.bson.Document
import java.time.Instant
@Serializable
data class BattleRecordDto(
val date: String, // yyyy-MM-dd
var uid: String,
@SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String,
@SerialName("attack_detail_list") val attackDetailList: List<AttackDetailDto>
) {
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
companion object {
private val json = Json { ignoreUnknownKeys = true }
fun fromDocument(document: Document): BattleRecordDto = json.decodeFromString(document.toJson())
}
}
@Serializable
data class AttackDetailDto(
@SerialName("boss_round") val bossRound: Int, // 周目
@SerialName("boss_index") val bossIndex: Int, // 1~5 號王
val damage: Long, // 傷害
@SerialName("is_compensation") val isCompensation: Boolean = false, // true: 補償刀, false: 正刀
@SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(),
@SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli()
) {
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
companion object {
private val json = Json { ignoreUnknownKeys = true }
fun fromDocument(document: Document): AttackDetailDto = json.decodeFromString(document.toJson())
}
}
@@ -1,18 +1,20 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.database.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.bson.Document
import java.time.Instant
@Serializable
data class MemberDto(
var uid: String,
val playerName: String,
val nickName: String,
val discordID: String,
@SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String,
var leave: Boolean = false,
var createAt: Long? = null,
var updatedAt: Long? = null
@SerialName("created_at") var createAt: Long? = null,
@SerialName("updated_at") var updatedAt: Long? = null
) {
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
@@ -0,0 +1,58 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.database.service
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters
import com.mongodb.client.model.Filters.and
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.bson.Document
class BattleRecordService(private val database: MongoDatabase) {
var collection: MongoCollection<Document>
init {
database.createCollection("battle_records")
collection = database.getCollection("battle_records")
}
// Create new car
suspend fun create(data: BattleRecordDto): String = withContext(Dispatchers.IO) {
val doc = data.toDocument()
collection.insertOne(doc)
doc["uid"].toString()
}
// Read a car
suspend fun read(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) {
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument)
}
// Read a car
suspend fun read(date: String, uid: String): BattleRecordDto? = withContext(Dispatchers.IO) {
val filter = and(
Filters.eq("date", date),
Filters.eq("uid", uid)
)
collection.find(filter).first()?.let(BattleRecordDto::fromDocument)
}
// Update a car
suspend fun update(date: String, data: BattleRecordDto): Document? = withContext(Dispatchers.IO) {
val filter = and(
Filters.eq("date", date),
Filters.eq("uid", data.uid)
)
collection.findOneAndReplace(filter, data.toDocument())
}
// Delete a car
suspend fun delete(date: String, uid: String): Document? = withContext(Dispatchers.IO) {
val filter = and(
Filters.eq("date", date),
Filters.eq("uid", uid)
)
collection.findOneAndDelete(filter)
}
}
@@ -16,7 +16,7 @@ class MemberService(private val database: MongoDatabase) {
collection = database.getCollection("member")
}
// Create new car
// Create new member
suspend fun create(data: MemberDto): String = withContext(Dispatchers.IO) {
data.createAt = System.currentTimeMillis()
data.updatedAt = System.currentTimeMillis()
@@ -26,22 +26,22 @@ class MemberService(private val database: MongoDatabase) {
doc["uid"].toString()
}
// Read a car
// Read a member
suspend fun read(): List<MemberDto> = withContext(Dispatchers.IO) {
collection.find().toList().map(MemberDto::fromDocument)
}
// Read a car
// Read a member
suspend fun read(uid: String): MemberDto? = withContext(Dispatchers.IO) {
collection.find(Filters.eq("uid", uid)).first()?.let(MemberDto::fromDocument)
}
// Update a car
// Update a member
suspend fun update(uid: String, data: MemberDto): Document? = withContext(Dispatchers.IO) {
collection.findOneAndReplace(Filters.eq("uid", uid), data.toDocument())
}
// Delete a car
// Delete a member
suspend fun delete(uid: String): Document? = withContext(Dispatchers.IO) {
collection.findOneAndDelete(Filters.eq("uid", uid))
}
@@ -0,0 +1,24 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.mapper
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.AttackDetailDto
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
import club.sakunadaisuki.pcrediveclanrecordbackend.model.AttackDetail
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecord
fun BattleRecord.toDto(): BattleRecordDto = BattleRecordDto(
date = date,
uid = uid,
playerName = playerName,
discordName = discordName,
discordID = discordID,
attackDetailList = attackDetailList.map { it.toDto() },
)
fun AttackDetail.toDto(): AttackDetailDto = AttackDetailDto(
bossRound = bossRound,
bossIndex = bossIndex,
damage = damage,
isCompensation = isCompensation,
createdAt = createdAt,
updatedAt = updatedAt,
)
@@ -7,7 +7,7 @@ fun Member.toDto(): MemberDto {
return MemberDto(
uid = uid,
playerName = playerName,
nickName = nickName,
discordName = discordName,
discordID = discordID,
leave = leave,
createAt = createAt,
@@ -0,0 +1,21 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.model
import java.time.Instant
data class BattleRecord(
val date: String, // yyyy-MM-dd
var uid: String,
val playerName: String,
val discordName: String,
val discordID: String,
val attackDetailList: List<AttackDetail>
)
data class AttackDetail(
val bossRound: Int, // 周目
val bossIndex: Int, // 1~5 號王
val damage: Long, // 傷害
val isCompensation: Boolean = false, // true: 補償刀, false: 正刀
val createdAt: Long = Instant.now().toEpochMilli(),
val updatedAt: Long = Instant.now().toEpochMilli()
)
@@ -0,0 +1,17 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BattleRecordRequest(
val date: String,
val uid: String,
@SerialName("player_name") val name: String,
@SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String,
val round: Int,
val boss: Int,
val damage: Long,
@SerialName("is_compensation") val isCompensation: Boolean = false,
)
@@ -3,7 +3,7 @@ package club.sakunadaisuki.pcrediveclanrecordbackend.model
data class Member(
var uid: String,
val playerName: String,
val nickName: String,
val discordName: String,
val discordID: String,
var leave: Boolean = false,
var createAt: Long? = null,
@@ -1,8 +1,12 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.plugins
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
import club.sakunadaisuki.pcrediveclanrecordbackend.database.service.BattleRecordService
import club.sakunadaisuki.pcrediveclanrecordbackend.model.Member
import club.sakunadaisuki.pcrediveclanrecordbackend.database.service.MemberService
import club.sakunadaisuki.pcrediveclanrecordbackend.mapper.toDto
import club.sakunadaisuki.pcrediveclanrecordbackend.model.AttackDetail
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecordRequest
import com.mongodb.client.*
import io.ktor.http.*
import io.ktor.server.application.*
@@ -17,20 +21,23 @@ fun Application.configureMongo() {
val memberService = runCatching {
MemberService(mongoDatabase)
}.getOrNull() ?: return
val battleRecordService = runCatching {
BattleRecordService(mongoDatabase)
}.getOrNull() ?: return
routing {
route("/api") {
// Read member
get("/member") {
memberService.read().let { member ->
call.respond(member)
call.respond(HttpStatusCode.OK, member)
}
}
// Read member
get("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
memberService.read(uid)?.let { member ->
call.respond(member)
call.respond(HttpStatusCode.OK, member)
} ?: call.respond(HttpStatusCode.NotFound)
}
// Create member
@@ -56,6 +63,45 @@ fun Application.configureMongo() {
}
// TODO Battle Record
get("/battle_record/{date}") {
val date = call.parameters["date"] ?: throw IllegalArgumentException("No date")
battleRecordService.read(date).let { battleRecord ->
call.respond(HttpStatusCode.OK, battleRecord)
}
}
post("/battle_record") {
val request = call.receive<BattleRecordRequest>()
val newAttackDetail = AttackDetail(
bossRound = request.round,
bossIndex = request.boss,
damage = request.damage,
isCompensation = request.isCompensation,
)
val existData = battleRecordService.read(request.date, request.uid)
if (existData != null) {
val attackDetails = existData.attackDetailList.toMutableList()
attackDetails.add(newAttackDetail.toDto())
val newData = existData.copy(
attackDetailList = attackDetails
)
battleRecordService.update(request.date, newData)?.let {
call.respond(HttpStatusCode.OK, it)
} ?: call.respond(HttpStatusCode.NotFound)
} else {
val newData = BattleRecordDto(
date = request.date,
uid = request.uid,
playerName = request.name,
discordName = request.discordName,
discordID = request.discordID,
attackDetailList = listOf(newAttackDetail.toDto())
)
val id = battleRecordService.create(newData)
call.respond(HttpStatusCode.Created, id)
}
}
}
}