Compare commits

...

9 Commits

Author SHA1 Message Date
ray650128 f4c4c6906e 新增單日表格補上已有資料判斷 2026-08-11 11:55:25 +08:00
ray650128 0cf0f8c49b 加上記憶體限制 2026-08-11 11:19:10 +08:00
Raymond Yang f8a1328a03 加上新增整月的表格 2026-08-11 11:14:47 +08:00
ray650128 f677fdb162 補上所有序列化欄位mapping 2026-08-09 12:46:55 +08:00
ray650128 abd4c24c83 補上內網allowHost 2026-08-09 11:50:12 +08:00
ray650128 7c390aebdf 修正成員欄位屬性 2026-08-09 02:57:50 +08:00
ray650128 7f0d0f4a87 修正更新成員沒有回應資料的問題 2026-08-09 02:22:39 +08:00
ray650128 8b20758404 加上生成戰報表的功能 2026-08-09 01:44:19 +08:00
ray650128 0741d5846b 修正BattleRecord沒有加上@Serializable註釋 2026-08-09 01:30:40 +08:00
10 changed files with 108 additions and 18 deletions
+4
View File
@@ -3,6 +3,10 @@ FROM gradle:9.5.1-jdk25 AS builder
WORKDIR /app
COPY . .
ENV GRADLE_OPTS="-Xmx1536m -XX:MaxMetaspaceSize=512m"
ENV NODE_OPTIONS="--max-old-space-size=1536"
RUN gradle clean shadowJar --no-daemon
# 運行階段
@@ -8,11 +8,11 @@ import java.time.Instant
@Serializable
data class MemberDto(
var uid: String,
@SerialName("uid") var uid: String,
@SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String,
var leave: Boolean = false,
@SerialName("leave") var leave: Boolean,
@SerialName("created_at") var createAt: Long? = null,
@SerialName("updated_at") var updatedAt: Long? = null
) {
@@ -1,6 +1,9 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.database.service
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.MemberDto
import club.sakunadaisuki.pcrediveclanrecordbackend.mapper.toEmptyBattleRecordDto
import club.sakunadaisuki.pcrediveclanrecordbackend.model.BattleRecord
import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters
@@ -8,28 +11,34 @@ import com.mongodb.client.model.Filters.and
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.bson.Document
import java.time.LocalDate
import java.time.YearMonth
class BattleRecordService(private val database: MongoDatabase) {
var collection: MongoCollection<Document>
var memberCollection: MongoCollection<Document>
init {
database.createCollection("battle_records")
collection = database.getCollection("battle_records")
database.createCollection("member")
memberCollection = database.getCollection("member")
}
// Create new car
// Create new battle record
suspend fun create(data: BattleRecordDto): BattleRecordDto = withContext(Dispatchers.IO) {
val doc = data.toDocument()
collection.insertOne(doc)
doc.let(BattleRecordDto::fromDocument)
}
// Read a car
// Read a battle record
suspend fun read(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) {
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument)
}
// Read a car
// Read a battle record
suspend fun read(date: String, uid: String): BattleRecordDto? = withContext(Dispatchers.IO) {
val filter = and(
Filters.eq("date", date),
@@ -38,7 +47,7 @@ class BattleRecordService(private val database: MongoDatabase) {
collection.find(filter).first()?.let(BattleRecordDto::fromDocument)
}
// Update a car
// Update a battle record
suspend fun update(date: String, data: BattleRecordDto): BattleRecordDto? = withContext(Dispatchers.IO) {
val filter = and(
Filters.eq("date", date),
@@ -48,7 +57,7 @@ class BattleRecordService(private val database: MongoDatabase) {
collection.find(filter).first()?.let(BattleRecordDto::fromDocument)
}
// Delete a car
// Delete a battle record
suspend fun delete(date: String, uid: String): Document? = withContext(Dispatchers.IO) {
val filter = and(
Filters.eq("date", date),
@@ -56,4 +65,36 @@ class BattleRecordService(private val database: MongoDatabase) {
)
collection.findOneAndDelete(filter)
}
suspend fun createTable(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) {
val memberList = memberCollection.find(Filters.eq("leave", false)).toList().map(MemberDto::fromDocument)
val emptyBattleRecordList = memberList.map { it.toEmptyBattleRecordDto(date) }
collection.insertMany(emptyBattleRecordList.map { it.toDocument() })
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument)
}
suspend fun createTableMonth(date: String): List<List<BattleRecordDto>> = withContext(Dispatchers.IO) {
val memberList = memberCollection.find(Filters.eq("leave", false)).toList().map(MemberDto::fromDocument)
// 1. 指定年份與月份(例如:2026年8月)
val dateSplit = date.split("-").map { it.toInt() }
val yearMonth = YearMonth.of(dateSplit[0], dateSplit[1])
// 2. 取得該月最後一天的 LocalDate 物件
val lastDayOfMonth = yearMonth.atEndOfMonth()
// 3. 取得最後 5 天的日期列表(由舊到新排序)
val lastFiveDays: List<LocalDate> = (4 downTo 0).map { lastDayOfMonth.minusDays(it.toLong()) }
// 4. Temp list
val tables = mutableListOf<List<BattleRecordDto>>()
// 5. 寫入資料
lastFiveDays.forEach { date ->
val emptyBattleRecordList = memberList.map { it.toEmptyBattleRecordDto(date.toString()) }
tables.add(emptyBattleRecordList)
collection.insertMany(emptyBattleRecordList.map { it.toDocument() })
}
tables
}
}
@@ -38,7 +38,15 @@ class MemberService(private val database: MongoDatabase) {
// Update a member
suspend fun update(uid: String, data: MemberDto): MemberDto? = withContext(Dispatchers.IO) {
collection.findOneAndReplace(Filters.eq("uid", uid), data.toDocument())
collection.find(Filters.eq("uid", uid)).first()?.let(MemberDto::fromDocument)?.copy(
playerName = data.playerName,
discordName = data.discordName,
discordID = data.discordID,
leave = data.leave,
updatedAt = System.currentTimeMillis(),
)?.let {
collection.findOneAndReplace(Filters.eq("uid", uid), it.toDocument())
}
collection.find(Filters.eq("uid", uid)).first()?.let(MemberDto::fromDocument)
}
@@ -1,5 +1,6 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.mapper
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.BattleRecordDto
import club.sakunadaisuki.pcrediveclanrecordbackend.database.dto.MemberDto
import club.sakunadaisuki.pcrediveclanrecordbackend.model.Member
import club.sakunadaisuki.pcrediveclanrecordbackend.model.MemberRequest
@@ -36,3 +37,14 @@ fun UpdateMemberRequest.toDto(uid: String): MemberDto {
leave = leave,
)
}
fun MemberDto.toEmptyBattleRecordDto(date: String): BattleRecordDto {
return BattleRecordDto(
date = date,
uid = uid,
playerName = playerName,
discordName = discordName,
discordID = discordID,
attackDetailList = emptyList(),
)
}
@@ -1,23 +1,26 @@
package club.sakunadaisuki.pcrediveclanrecordbackend.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.Instant
@Serializable
data class BattleRecord(
val date: String, // yyyy-MM-dd
var uid: String,
@SerialName("date") val date: String, // yyyy-MM-dd
@SerialName("uid") 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<AttackDetail>
)
@Serializable
data class AttackDetail(
@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("is_dropped") val isDropped: Boolean = false, // true: 掉刀, false: 沒掉刀
@SerialName("damage") val damage: Long, // 傷害
@SerialName("is_compensation") val isCompensation: Boolean, // true: 補償刀, false: 正刀
@SerialName("is_dropped") val isDropped: Boolean, // true: 掉刀, false: 沒掉刀
@SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(),
@SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli()
)
@@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
@Serializable
data class Member(
var uid: String,
@SerialName("uid") var uid: String,
@SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String,
var leave: Boolean = false,
@SerialName("leave") var leave: Boolean,
@SerialName("created_at") var createAt: Long? = null,
@SerialName("updated_at") var updatedAt: Long? = null
)
@@ -8,5 +8,5 @@ data class UpdateMemberRequest(
@SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String,
var leave: Boolean = false,
@SerialName("leave") var leave: Boolean,
)
@@ -10,6 +10,7 @@ fun Application.configureHttp() {
install(CORS) {
allowHost("localhost:8080")
allowHost("table.sakunadaisuki.club")
allowHost("192.168.50.128:10002")
allowMethod(HttpMethod.Options)
allowMethod(HttpMethod.Put)
@@ -36,6 +36,7 @@ fun Application.configureMongo() {
call.respond(HttpStatusCode.OK, member)
}
}
// Read member
get("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
@@ -43,6 +44,7 @@ fun Application.configureMongo() {
call.respond(HttpStatusCode.OK, member)
} ?: call.respond(HttpStatusCode.NotFound)
}
// Create member
post("/member") {
val member = call.receive<MemberRequest>()
@@ -54,14 +56,16 @@ fun Application.configureMongo() {
call.respond(HttpStatusCode.Conflict)
}
}
// Update member
put("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
val member = call.receive<UpdateMemberRequest>()
memberService.update(uid, member.toDto(uid))?.let {
call.respond(HttpStatusCode.OK)
memberService.update(uid, member.toDto(uid))?.let { member ->
call.respond(HttpStatusCode.OK, member)
} ?: call.respond(HttpStatusCode.NotFound)
}
// Delete member
delete("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
@@ -126,7 +130,24 @@ fun Application.configureMongo() {
}
}
post("/battle_record/create_table_day/{date}") {
val date = call.parameters["date"] ?: throw IllegalArgumentException("No date")
val exist = battleRecordService.read(date)
if (exist.isNotEmpty()) {
call.respond(HttpStatusCode.Conflict)
return@post
}
battleRecordService.createTable(date).let { battleRecord ->
call.respond(HttpStatusCode.OK, battleRecord)
}
}
post("/battle_record/create_table_month/{date}") {
val date = call.parameters["date"] ?: throw IllegalArgumentException("No date")
battleRecordService.createTableMonth(date).let { battleRecordList ->
call.respond(HttpStatusCode.OK, battleRecordList)
}
}
}
}
}