Compare commits

..

7 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
9 changed files with 75 additions and 19 deletions
+4
View File
@@ -3,6 +3,10 @@ FROM gradle:9.5.1-jdk25 AS builder
WORKDIR /app WORKDIR /app
COPY . . COPY . .
ENV GRADLE_OPTS="-Xmx1536m -XX:MaxMetaspaceSize=512m"
ENV NODE_OPTIONS="--max-old-space-size=1536"
RUN gradle clean shadowJar --no-daemon RUN gradle clean shadowJar --no-daemon
# 運行階段 # 運行階段
@@ -8,11 +8,11 @@ import java.time.Instant
@Serializable @Serializable
data class MemberDto( data class MemberDto(
var uid: String, @SerialName("uid") var uid: String,
@SerialName("player_name") val playerName: String, @SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String, @SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String, @SerialName("discord_id") val discordID: String,
var leave: Boolean = false, @SerialName("leave") var leave: Boolean,
@SerialName("created_at") var createAt: Long? = null, @SerialName("created_at") var createAt: Long? = null,
@SerialName("updated_at") var updatedAt: Long? = null @SerialName("updated_at") var updatedAt: Long? = null
) { ) {
@@ -11,6 +11,8 @@ import com.mongodb.client.model.Filters.and
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.bson.Document import org.bson.Document
import java.time.LocalDate
import java.time.YearMonth
class BattleRecordService(private val database: MongoDatabase) { class BattleRecordService(private val database: MongoDatabase) {
var collection: MongoCollection<Document> var collection: MongoCollection<Document>
@@ -24,19 +26,19 @@ class BattleRecordService(private val database: MongoDatabase) {
memberCollection = database.getCollection("member") memberCollection = database.getCollection("member")
} }
// Create new car // Create new battle record
suspend fun create(data: BattleRecordDto): BattleRecordDto = withContext(Dispatchers.IO) { suspend fun create(data: BattleRecordDto): BattleRecordDto = withContext(Dispatchers.IO) {
val doc = data.toDocument() val doc = data.toDocument()
collection.insertOne(doc) collection.insertOne(doc)
doc.let(BattleRecordDto::fromDocument) doc.let(BattleRecordDto::fromDocument)
} }
// Read a car // Read a battle record
suspend fun read(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) { suspend fun read(date: String): List<BattleRecordDto> = withContext(Dispatchers.IO) {
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument) 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) { suspend fun read(date: String, uid: String): BattleRecordDto? = withContext(Dispatchers.IO) {
val filter = and( val filter = and(
Filters.eq("date", date), Filters.eq("date", date),
@@ -45,7 +47,7 @@ class BattleRecordService(private val database: MongoDatabase) {
collection.find(filter).first()?.let(BattleRecordDto::fromDocument) 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) { suspend fun update(date: String, data: BattleRecordDto): BattleRecordDto? = withContext(Dispatchers.IO) {
val filter = and( val filter = and(
Filters.eq("date", date), Filters.eq("date", date),
@@ -55,7 +57,7 @@ class BattleRecordService(private val database: MongoDatabase) {
collection.find(filter).first()?.let(BattleRecordDto::fromDocument) 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) { suspend fun delete(date: String, uid: String): Document? = withContext(Dispatchers.IO) {
val filter = and( val filter = and(
Filters.eq("date", date), Filters.eq("date", date),
@@ -70,4 +72,29 @@ class BattleRecordService(private val database: MongoDatabase) {
collection.insertMany(emptyBattleRecordList.map { it.toDocument() }) collection.insertMany(emptyBattleRecordList.map { it.toDocument() })
collection.find(Filters.eq("date", date)).toList().map(BattleRecordDto::fromDocument) 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 // Update a member
suspend fun update(uid: String, data: MemberDto): MemberDto? = withContext(Dispatchers.IO) { 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) collection.find(Filters.eq("uid", uid)).first()?.let(MemberDto::fromDocument)
} }
@@ -6,8 +6,8 @@ import java.time.Instant
@Serializable @Serializable
data class BattleRecord( data class BattleRecord(
val date: String, // yyyy-MM-dd @SerialName("date") val date: String, // yyyy-MM-dd
var uid: String, @SerialName("uid") var uid: String,
@SerialName("player_name") val playerName: String, @SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String, @SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String, @SerialName("discord_id") val discordID: String,
@@ -18,9 +18,9 @@ data class BattleRecord(
data class AttackDetail( data class AttackDetail(
@SerialName("boss_round") val bossRound: Int, // 周目 @SerialName("boss_round") val bossRound: Int, // 周目
@SerialName("boss_index") val bossIndex: Int, // 1~5 號王 @SerialName("boss_index") val bossIndex: Int, // 1~5 號王
val damage: Long, // 傷害 @SerialName("damage") val damage: Long, // 傷害
@SerialName("is_compensation") val isCompensation: Boolean = false, // true: 補償刀, false: 正刀 @SerialName("is_compensation") val isCompensation: Boolean, // true: 補償刀, false: 正刀
@SerialName("is_dropped") val isDropped: Boolean = false, // true: 掉刀, false: 沒掉刀 @SerialName("is_dropped") val isDropped: Boolean, // true: 掉刀, false: 沒掉刀
@SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(), @SerialName("created_at") val createdAt: Long = Instant.now().toEpochMilli(),
@SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli() @SerialName("updated_at") val updatedAt: Long = Instant.now().toEpochMilli()
) )
@@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class Member( data class Member(
var uid: String, @SerialName("uid") var uid: String,
@SerialName("player_name") val playerName: String, @SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String, @SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: String, @SerialName("discord_id") val discordID: String,
var leave: Boolean = false, @SerialName("leave") var leave: Boolean,
@SerialName("created_at") var createAt: Long? = null, @SerialName("created_at") var createAt: Long? = null,
@SerialName("updated_at") var updatedAt: Long? = null @SerialName("updated_at") var updatedAt: Long? = null
) )
@@ -8,5 +8,5 @@ data class UpdateMemberRequest(
@SerialName("player_name") val playerName: String, @SerialName("player_name") val playerName: String,
@SerialName("discord_name") val discordName: String, @SerialName("discord_name") val discordName: String,
@SerialName("discord_id") val discordID: 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) { install(CORS) {
allowHost("localhost:8080") allowHost("localhost:8080")
allowHost("table.sakunadaisuki.club") allowHost("table.sakunadaisuki.club")
allowHost("192.168.50.128:10002")
allowMethod(HttpMethod.Options) allowMethod(HttpMethod.Options)
allowMethod(HttpMethod.Put) allowMethod(HttpMethod.Put)
@@ -36,6 +36,7 @@ fun Application.configureMongo() {
call.respond(HttpStatusCode.OK, member) call.respond(HttpStatusCode.OK, member)
} }
} }
// Read member // Read member
get("/member/{uid}") { get("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found") 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.OK, member)
} ?: call.respond(HttpStatusCode.NotFound) } ?: call.respond(HttpStatusCode.NotFound)
} }
// Create member // Create member
post("/member") { post("/member") {
val member = call.receive<MemberRequest>() val member = call.receive<MemberRequest>()
@@ -54,14 +56,16 @@ fun Application.configureMongo() {
call.respond(HttpStatusCode.Conflict) call.respond(HttpStatusCode.Conflict)
} }
} }
// Update member // Update member
put("/member/{uid}") { put("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found") val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
val member = call.receive<UpdateMemberRequest>() val member = call.receive<UpdateMemberRequest>()
memberService.update(uid, member.toDto(uid))?.let { memberService.update(uid, member.toDto(uid))?.let { member ->
call.respond(HttpStatusCode.OK) call.respond(HttpStatusCode.OK, member)
} ?: call.respond(HttpStatusCode.NotFound) } ?: call.respond(HttpStatusCode.NotFound)
} }
// Delete member // Delete member
delete("/member/{uid}") { delete("/member/{uid}") {
val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found") val uid = call.parameters["uid"] ?: throw IllegalArgumentException("No ID found")
@@ -126,12 +130,24 @@ fun Application.configureMongo() {
} }
} }
post("/battle_record/create_table/{date}") { post("/battle_record/create_table_day/{date}") {
val date = call.parameters["date"] ?: throw IllegalArgumentException("No 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 -> battleRecordService.createTable(date).let { battleRecord ->
call.respond(HttpStatusCode.OK, 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)
}
}
} }
} }
} }