[O] Complete kt rewrite

pull/99/head^2
Azalea 2025-01-04 19:59:46 -05:00
parent 8203a70b60
commit af9cd81220
6 changed files with 212 additions and 274 deletions

View File

@ -4,11 +4,10 @@ import ext.*
import icu.samnyan.aqua.net.db.AquaUserServices
import icu.samnyan.aqua.net.utils.simpleDescribe
import icu.samnyan.aqua.sega.allnet.TokenChecker
import icu.samnyan.aqua.sega.chusan.handler.GameLoginHandler
import icu.samnyan.aqua.sega.chusan.handler.UpsertUserAllHandler
import icu.samnyan.aqua.sega.chusan.handler.chusanInit
import icu.samnyan.aqua.sega.chusan.model.Chu3Repos
import icu.samnyan.aqua.sega.general.*
import icu.samnyan.aqua.sega.general.MeowApi
import icu.samnyan.aqua.sega.general.RequestContext
import icu.samnyan.aqua.sega.util.jackson.BasicMapper
import icu.samnyan.aqua.sega.util.jackson.StringMapper
import icu.samnyan.aqua.spring.Metrics
@ -16,7 +15,6 @@ import jakarta.servlet.http.HttpServletRequest
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.RestController
import kotlin.collections.set
import kotlin.reflect.full.declaredMemberProperties
/**
* @author samnyan (privateamusement@protonmail.com)
@ -25,9 +23,6 @@ import kotlin.reflect.full.declaredMemberProperties
@RestController
@API(value = ["/g/chu3/{version}/ChuniServlet", "/g/chu3/{version}"])
class ChusanController(
val gameLogin: GameLoginHandler,
val upsertUserAll: UpsertUserAllHandler,
val mapper: StringMapper,
val cmMapper: BasicMapper,
val db: Chu3Repos,
@ -40,22 +35,12 @@ class ChusanController(
}) {
val log = LoggerFactory.getLogger(ChusanController::class.java)
// Below are code related to handling the handlers
val externalHandlers = mutableListOf("GameLoginApi", "UpsertUserAllApi")
val noopEndpoint = setOf("UpsertClientBookkeepingApi", "UpsertClientDevelopApi", "UpsertClientErrorApi",
"UpsertClientSettingApi", "UpsertClientTestmodeApi", "CreateTokenApi", "RemoveTokenApi", "UpsertClientUploadApi",
"PrinterLoginApi", "PrinterLogoutApi", "Ping", "GameLogoutApi", "RemoveMatchingMemberApi")
init { chusanInit() }
val members = this::class.declaredMemberProperties
val handlers: Map<String, SpecialHandler> = initH + externalHandlers.associateWith { api ->
val name = api.replace("Api", "").lowercase()
(members.find { it.name.lowercase() == name } ?: members.find { it.name.lowercase() == name.replace("cm", "") })
?.let { (it.call(this) as BaseHandler).toSpecial() }
?: throw IllegalArgumentException("Chu3: No handler found for $api")
}
val handlers = initH
@API("/{endpoint}", "/MatchingServer/{endpoint}")
fun handle(@PV endpoint: Str, @RB data: MutableMap<Str, Any>, @PV version: Str, req: HttpServletRequest): Any {

View File

@ -5,17 +5,19 @@ import icu.samnyan.aqua.sega.allnet.TokenChecker
import icu.samnyan.aqua.sega.chusan.ChusanController
import icu.samnyan.aqua.sega.chusan.ChusanData
import icu.samnyan.aqua.sega.chusan.model.request.UserCMissionResp
import icu.samnyan.aqua.sega.chusan.model.request.UserEmoney
import icu.samnyan.aqua.sega.chusan.model.userdata.UserCharge
import icu.samnyan.aqua.sega.chusan.model.userdata.UserItem
import icu.samnyan.aqua.sega.chusan.model.userdata.UserLoginBonus
import icu.samnyan.aqua.sega.chusan.model.userdata.UserMusicDetail
import icu.samnyan.aqua.sega.general.model.response.UserRecentRating
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
@Suppress("UNCHECKED_CAST")
fun ChusanController.chusanInit() {
matchingApiInit()
cmApiInit()
upsertApiInit()
// Stub handlers
"GetGameRanking" { """{"type":"${data["type"]}","length":"0","gameRankingList":[]}""" }
@ -32,9 +34,6 @@ fun ChusanController.chusanInit() {
"GetUserNetBattleData" { """{"userId":"${data["userId"]}","userNetBattleData":{"recentNBSelectMusicList":[],"recentNBMusicList":[]}}""" }
"GetUserNetBattleRankingInfo" { """{"userId":"${data["userId"]}","length":"0","userNetBattleRankingInfoList":{}}""" }
"CMUpsertUserPrint" { """{"returnCode":1,"orderId":"0","serialId":"FAKECARDIMAG12345678","apiName":"CMUpsertUserPrintApi"}""" }
"CMUpsertUserPrintlog" { """{"returnCode":1,"orderId":"0","serialId":"FAKECARDIMAG12345678","apiName":"CMUpsertUserPrintlogApi"}""" }
// User handlers
"GetUserData" {
db.userData.findByCard_ExtId(uid)()?.let{ u -> mapOf("userId" to uid, "userData" to u) }
@ -233,29 +232,60 @@ fun ChusanController.chusanInit() {
)
}
// Upserts
"UpsertUserChargelog" {
val charge = parsing { mapper.convert<UserCharge>(data["userCharge"] as JDict) }
charge.user = db.userData.findByCard_ExtId(uid)() ?: (400 - "User not found")
charge.id = db.userCharge.findByUser_Card_ExtIdAndChargeId(uid, charge.chargeId)?.id ?: 0
db.userCharge.save(charge)
"""{"returnCode":"1"}"""
}
// Static
"GetGameEvent" static { db.gameEvent.findByEnable(true).let { mapOf("type" to 1, "length" to it.size, "gameEventList" to it) } }
"GetGameCharge" static { db.gameCharge.findAll().let { mapOf("length" to it.size, "gameChargeList" to it) } }
"GetGameGacha" static { db.gameGacha.findAll().let { mapOf("length" to it.size, "gameGachaList" to it, "registIdList" to empty) } }
"GetGameMapAreaCondition" static { ChusanData.mapAreaCondition }
// CardMaker (TODO: Somebody test this, I don't have a card maker)
"CMGetUserData" {
val user = db.userData.findByCard_ExtId(uid)() ?: (400 - "User not found")
user.userEmoney = UserEmoney()
mapOf("userId" to uid, "userData" to user, "userEmoney" to user.userEmoney)
}
"CMGetUserPreview" {
val user = db.userData.findByCard_ExtId(uid)() ?: (400 - "User not found")
mapOf("userName" to user.userName, "level" to user.level, "medal" to user.medal, "lastDataVersion" to user.lastDataVersion, "isLogin" to false)
// TODO: Test login bonus
"GameLogin" {
fun process() {
val u = db.userData.findByCard_ExtId(uid)() ?: return
db.userData.save(u.apply { lastLoginDate = LocalDateTime.now() })
if (!props.loginBonusEnable) return
val bonusList = db.gameLoginBonusPresets.findLoginBonusPresets(1, 1)
bonusList.forEach { preset ->
// Check if a user already has some progress and if not, add the login bonus entry
val bonus = db.userLoginBonus.findLoginBonus(uid.int, 1, preset.id)()
?: UserLoginBonus(1, uid.int, preset.id).let { db.userLoginBonus.save(it) }
if (bonus.isFinished) return@forEach
// last login is 24 hours+ ago
if (bonus.lastUpdateDate.toEpochSecond(ZoneOffset.ofHours(0)) <
(LocalDateTime.now().minusHours(24).toEpochSecond(ZoneOffset.ofHours(0)))
) {
var bCount = bonus.bonusCount + 1
val lastUpdate = LocalDateTime.now()
val allLoginBonus = db.gameLoginBonus.findGameLoginBonus(1, preset.id)
.ifEmpty { return@forEach }
val maxNeededDays = allLoginBonus[0].needLoginDayCount
// if all items are redeemed, then don't show the login bonuses.
var finished = false
if (bCount > maxNeededDays) {
if (preset.id < 3000) bCount = 1
else finished = true
}
db.gameLoginBonus.findByRequiredDays(1, preset.id, bCount)()?.let {
db.userItem.save(UserItem(6, it.presentId, it.itemNum).apply { user = u })
}
val toSave = db.userLoginBonus.findLoginBonus(uid.int, 1, preset.id)()
?: UserLoginBonus().apply { user = uid.int; presetId = preset.id; version = 1 }
db.userLoginBonus.save(toSave.apply {
bonusCount = bCount
lastUpdateDate = lastUpdate
isWatched = false
isFinished = finished
})
}
}
}
process()
"""{"returnCode":"1"}"""
}
}

View File

@ -1,17 +1,30 @@
package icu.samnyan.aqua.sega.chusan.handler
import com.fasterxml.jackson.core.type.TypeReference
import ext.int
import ext.invoke
import ext.mapApply
import ext.parsing
import ext.*
import icu.samnyan.aqua.sega.chusan.ChusanController
import icu.samnyan.aqua.sega.chusan.model.request.UpsertUserGacha
import icu.samnyan.aqua.sega.chusan.model.request.UserEmoney
import icu.samnyan.aqua.sega.chusan.model.userdata.UserCardPrintState
import icu.samnyan.aqua.sega.chusan.model.userdata.UserItem
import java.time.LocalDateTime
fun ChusanController.cmApiInit() {
"CMUpsertUserPrint" { """{"returnCode":1,"orderId":"0","serialId":"FAKECARDIMAG12345678","apiName":"CMUpsertUserPrintApi"}""" }
"CMUpsertUserPrintlog" { """{"returnCode":1,"orderId":"0","serialId":"FAKECARDIMAG12345678","apiName":"CMUpsertUserPrintlogApi"}""" }
// CardMaker (TODO: Somebody test this, I don't have a card maker)
"CMGetUserData" {
val user = db.userData.findByCard_ExtId(uid)() ?: (400 - "User not found")
user.userEmoney = UserEmoney()
mapOf("userId" to uid, "userData" to user, "userEmoney" to user.userEmoney)
}
"CMGetUserPreview" {
val user = db.userData.findByCard_ExtId(uid)() ?: (400 - "User not found")
mapOf("userName" to user.userName, "level" to user.level, "medal" to user.medal, "lastDataVersion" to user.lastDataVersion, "isLogin" to false)
}
"CMUpsertUserGacha" api@ {
val (gachaId, placeId) = parsing { data["gachaId"]!!.int to data["placeId"]!!.int }

View File

@ -0,0 +1,139 @@
package icu.samnyan.aqua.sega.chusan.handler
import ext.*
import icu.samnyan.aqua.sega.chusan.ChusanController
import icu.samnyan.aqua.sega.chusan.model.request.UpsertUserAll
import icu.samnyan.aqua.sega.chusan.model.userdata.*
import icu.samnyan.aqua.sega.general.model.response.UserRecentRating
import java.nio.charset.StandardCharsets
import java.time.LocalDateTime
@Suppress("UNCHECKED_CAST")
fun ChusanController.upsertApiInit() {
"UpsertUserChargelog" {
val charge = parsing { mapper.convert<UserCharge>(data["userCharge"] as JDict) }
charge.user = db.userData.findByCard_ExtId(uid)() ?: (400 - "User not found")
charge.id = db.userCharge.findByUser_Card_ExtIdAndChargeId(uid, charge.chargeId)?.id ?: 0
db.userCharge.save(charge)
"""{"returnCode":"1"}"""
}
"UpsertUserAll" api@ {
val req = mapper.convert(data["upsertUserAll"], UpsertUserAll::class.java)
req.run {
// UserData
val oldUser = db.userData.findByCard_ExtId(uid)()
val u = (userData?.get(0) ?: return@api null).apply {
id = oldUser?.id ?: 0
card = oldUser?.card ?: us.cardRepo.findByExtId(uid).expect("Card not found")
userName = String(userName.toByteArray(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)
userNameEx = ""
}.also { db.userData.saveAndFlush(it) }
versionHelper[u.lastClientId] = u.lastDataVersion
// Set users
listOfNotNull(
userPlaylogList, userGameOption, userMapAreaList, userCharacterList, userItemList,
userMusicDetailList, userActivityList, userChargeList, userCourseList, userDuelList,
).flatten().forEach { it.user = u }
// Ratings
fun Iterable<UserRecentRating>.str() = joinToString(",") { "${it.musicId}:${it.difficultId}:${it.score}" }
ls(
userRecentRatingList to "recent_rating_list", userRatingBaseList to "rating_base_list",
userRatingBaseHotList to "rating_hot_list", userRatingBaseNextList to "rating_next_list",
).filter { it.first != null }.forEach { (list, key) ->
val d = db.userGeneralData.findByUserAndPropertyKey(u, key)()
?: UserGeneralData().apply { user = u; propertyKey = key }
db.userGeneralData.save(d.apply { propertyValue = list!!.str() })
}
userFavoriteMusicList?.filter { it.musicId != -1 }?.let { list ->
val d = db.userGeneralData.findByUserAndPropertyKey(u, "favorite_music")()
?: UserGeneralData().apply { user = u; propertyKey = "favorite_music" }
db.userGeneralData.save(d.apply {
propertyValue = list.distinct().joinToString(",") { it.musicId.toString() } })
}
// Playlog
userPlaylogList?.let { db.userPlaylog.saveAll(it) }
// List data
userGameOption?.get(0)?.let { obj ->
db.userGameOption.saveAndFlush(obj.apply {
id = db.userGameOption.findSingleByUser(u)()?.id ?: 0 }) }
userMapAreaList?.let { list ->
db.userMap.saveAll(list.distinctBy { it.mapAreaId }.mapApply {
id = db.userMap.findByUserAndMapAreaId(u, mapAreaId)?.id ?: 0 }) }
userCharacterList?.let { list ->
db.userCharacter.saveAll(list.distinctBy { it.characterId }.mapApply {
id = db.userCharacter.findByUserAndCharacterId(u, characterId)?.id ?: 0 }) }
userItemList?.let { list ->
db.userItem.saveAll(list.distinctBy { it.itemId to it.itemKind }.mapApply {
id = db.userItem.findByUserAndItemIdAndItemKind(u, itemId, itemKind)?.id ?: 0 }) }
userMusicDetailList?.let { list ->
db.userMusicDetail.saveAll(list.distinctBy { it.musicId to it.level }.mapApply {
id = db.userMusicDetail.findByUserAndMusicIdAndLevel(u, musicId, level)?.id ?: 0 }) }
userActivityList?.let { list ->
db.userActivity.saveAll(list.distinctBy { it.activityId to it.kind }.mapApply {
id = db.userActivity.findByUserAndActivityIdAndKind(u, activityId, kind)?.id ?: 0 }) }
userChargeList?.let { list ->
db.userCharge.saveAll(list.distinctBy { it.chargeId }.mapApply {
id = db.userCharge.findByUserAndChargeId(u, chargeId)()?.id ?: 0 }) }
userCourseList?.let { list ->
db.userCourse.saveAll(list.distinctBy { it.courseId }.mapApply {
id = db.userCourse.findByUserAndCourseId(u, courseId)?.id ?: 0 }) }
userDuelList?.let { list ->
db.userDuel.saveAll(list.distinctBy { it.duelId }.mapApply {
id = db.userDuel.findByUserAndDuelId(u, duelId)?.id ?: 0 }) }
// Need testing
userLoginBonusList?.let { list ->
db.userLoginBonus.saveAll(list.distinctBy { it["presetId"] as String }.map {
val id = it["presetId"]!!.int
(db.userLoginBonus.findLoginBonus(uid.int, 1, id)() ?: UserLoginBonus()).apply {
user = u.id.toInt()
presetId = id
lastUpdateDate = LocalDateTime.now()
isWatched = true
}
})
}
req.userCMissionList?.forEach { d ->
(db.userCMission.findByUser_Card_ExtIdAndMissionId(uid, d.missionId)()
?: UserCMission().apply {
missionId = d.missionId
user = u
}
).apply { point = d.point }.also { db.userCMission.save(it) }
d.userCMissionProgressList?.forEach inner@ { p ->
(db.userCMissionProgress.findByUser_Card_ExtIdAndMissionIdAndOrder(uid, d.missionId, p.order)()
?: UserCMissionProgress().apply {
missionId = d.missionId
order = p.order
user = u
}
).apply {
progress = p.progress
stage = p.stage
}.also { db.userCMissionProgress.save(it) }
}
}
}
"""{"returnCode":1}"""
}
}

View File

@ -1,76 +0,0 @@
package icu.samnyan.aqua.sega.chusan.handler
import ext.int
import ext.invoke
import ext.long
import icu.samnyan.aqua.sega.chusan.ChusanProps
import icu.samnyan.aqua.sega.chusan.model.Chu3Repos
import icu.samnyan.aqua.sega.chusan.model.userdata.UserItem
import icu.samnyan.aqua.sega.chusan.model.userdata.UserLoginBonus
import icu.samnyan.aqua.sega.general.BaseHandler
import lombok.AllArgsConstructor
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@AllArgsConstructor
@Component("ChusanGameLoginHandler")
class GameLoginHandler(
val props: ChusanProps,
val db: Chu3Repos
) : BaseHandler {
override fun handle(request: Map<String, Any>): Any? {
val uid = request["userId"]!!.long
fun process() {
val u = db.userData.findByCard_ExtId(uid)() ?: return
db.userData.save(u.apply { lastLoginDate = LocalDateTime.now() })
if (!props.loginBonusEnable) return
val bonusList = db.gameLoginBonusPresets.findLoginBonusPresets(1, 1)
bonusList.forEach { preset ->
// Check if a user already has some progress and if not, add the login bonus entry
val bonus = db.userLoginBonus.findLoginBonus(uid.int, 1, preset.id)()
?: UserLoginBonus(1, uid.int, preset.id).let { db.userLoginBonus.save(it) }
if (bonus.isFinished) return@forEach
// last login is 24 hours+ ago
if (bonus.lastUpdateDate.toEpochSecond(ZoneOffset.ofHours(0)) <
(LocalDateTime.now().minusHours(24).toEpochSecond(ZoneOffset.ofHours(0)))
) {
var bCount = bonus.bonusCount + 1
val lastUpdate = LocalDateTime.now()
val allLoginBonus = db.gameLoginBonus.findGameLoginBonus(1, preset.id)
.ifEmpty { return@forEach }
val maxNeededDays = allLoginBonus[0].needLoginDayCount
// if all items are redeemed, then don't show the login bonuses.
var finished = false
if (bCount > maxNeededDays) {
if (preset.id < 3000) bCount = 1
else finished = true
}
db.gameLoginBonus.findByRequiredDays(1, preset.id, bCount)()?.let {
db.userItem.save(UserItem(6, it.presentId, it.itemNum).apply { user = u })
}
val toSave = db.userLoginBonus.findLoginBonus(uid.int, 1, preset.id)()
?: UserLoginBonus().apply { user = uid.int; presetId = preset.id; version = 1 }
db.userLoginBonus.save(toSave.apply {
bonusCount = bCount
lastUpdateDate = lastUpdate
isWatched = false
isFinished = finished
})
}
}
}
process()
return """{"returnCode":"1"}"""
}
}

View File

@ -1,153 +0,0 @@
package icu.samnyan.aqua.sega.chusan.handler
import ext.*
import icu.samnyan.aqua.sega.chusan.ChusanVersionHelper
import icu.samnyan.aqua.sega.chusan.model.Chu3Repos
import icu.samnyan.aqua.sega.chusan.model.request.UpsertUserAll
import icu.samnyan.aqua.sega.chusan.model.userdata.UserCMission
import icu.samnyan.aqua.sega.chusan.model.userdata.UserCMissionProgress
import icu.samnyan.aqua.sega.chusan.model.userdata.UserGeneralData
import icu.samnyan.aqua.sega.chusan.model.userdata.UserLoginBonus
import icu.samnyan.aqua.sega.general.BaseHandler
import icu.samnyan.aqua.sega.general.dao.CardRepository
import icu.samnyan.aqua.sega.general.model.response.UserRecentRating
import icu.samnyan.aqua.sega.util.jackson.StringMapper
import lombok.AllArgsConstructor
import org.springframework.stereotype.Component
import java.nio.charset.StandardCharsets
import java.time.LocalDateTime
/**
* The handler for save user data. Only send in the end of the session.
*
* @author samnyan (privateamusement@protonmail.com)
*/
@AllArgsConstructor
@Component("ChusanUpsertUserAllHandler")
class UpsertUserAllHandler(
val mapper: StringMapper,
val rp: Chu3Repos,
val cardRepo: CardRepository,
val versionHelper: ChusanVersionHelper,
) : BaseHandler {
val logger = logger()
override fun handle(request: Map<String, Any>): Any? {
val ext = request["userId"]?.long ?: return null
val req = mapper.convert(request["upsertUserAll"], UpsertUserAll::class.java)
req.run {
// UserData
val oldUser = rp.userData.findByCard_ExtId(ext)()
val u = (userData?.get(0) ?: return null).apply {
id = oldUser?.id ?: 0
card = oldUser?.card ?: cardRepo.findByExtId(ext).expect("Card not found")
userName = String(userName.toByteArray(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)
userNameEx = ""
}.also { rp.userData.saveAndFlush(it) }
versionHelper[u.lastClientId] = u.lastDataVersion
// Set users
listOfNotNull(
userPlaylogList, userGameOption, userMapAreaList, userCharacterList, userItemList,
userMusicDetailList, userActivityList, userChargeList, userCourseList, userDuelList,
).flatten().forEach { it.user = u }
// Ratings
fun Iterable<UserRecentRating>.str() = joinToString(",") { "${it.musicId}:${it.difficultId}:${it.score}" }
ls(
userRecentRatingList to "recent_rating_list", userRatingBaseList to "rating_base_list",
userRatingBaseHotList to "rating_hot_list", userRatingBaseNextList to "rating_next_list",
).filter { it.first != null }.forEach { (list, key) ->
val d = rp.userGeneralData.findByUserAndPropertyKey(u, key)()
?: UserGeneralData().apply { user = u; propertyKey = key }
rp.userGeneralData.save(d.apply { propertyValue = list!!.str() })
}
userFavoriteMusicList?.let { list ->
val d = rp.userGeneralData.findByUserAndPropertyKey(u, "favorite_music")()
?: UserGeneralData().apply { user = u; propertyKey = "favorite_music" }
rp.userGeneralData.save(d.apply { propertyValue = list.joinToString(",") { it.musicId.toString() } })
}
// Playlog
userPlaylogList?.let { rp.userPlaylog.saveAll(it) }
// List data
userGameOption?.get(0)?.let { obj ->
rp.userGameOption.saveAndFlush(obj.apply {
id = rp.userGameOption.findSingleByUser(u)()?.id ?: 0 }) }
userMapAreaList?.let { list ->
rp.userMap.saveAll(list.distinctBy { it.mapAreaId }.mapApply {
id = rp.userMap.findByUserAndMapAreaId(u, mapAreaId)?.id ?: 0 }) }
userCharacterList?.let { list ->
rp.userCharacter.saveAll(list.distinctBy { it.characterId }.mapApply {
id = rp.userCharacter.findByUserAndCharacterId(u, characterId)?.id ?: 0 }) }
userItemList?.let { list ->
rp.userItem.saveAll(list.distinctBy { it.itemId to it.itemKind }.mapApply {
id = rp.userItem.findByUserAndItemIdAndItemKind(u, itemId, itemKind)?.id ?: 0 }) }
userMusicDetailList?.let { list ->
rp.userMusicDetail.saveAll(list.distinctBy { it.musicId to it.level }.mapApply {
id = rp.userMusicDetail.findByUserAndMusicIdAndLevel(u, musicId, level)?.id ?: 0 }) }
userActivityList?.let { list ->
rp.userActivity.saveAll(list.distinctBy { it.activityId to it.kind }.mapApply {
id = rp.userActivity.findByUserAndActivityIdAndKind(u, activityId, kind)?.id ?: 0 }) }
userChargeList?.let { list ->
rp.userCharge.saveAll(list.distinctBy { it.chargeId }.mapApply {
id = rp.userCharge.findByUserAndChargeId(u, chargeId)()?.id ?: 0 }) }
userCourseList?.let { list ->
rp.userCourse.saveAll(list.distinctBy { it.courseId }.mapApply {
id = rp.userCourse.findByUserAndCourseId(u, courseId)?.id ?: 0 }) }
userDuelList?.let { list ->
rp.userDuel.saveAll(list.distinctBy { it.duelId }.mapApply {
id = rp.userDuel.findByUserAndDuelId(u, duelId)?.id ?: 0 }) }
// Need testing
userLoginBonusList?.let { list ->
rp.userLoginBonus.saveAll(list.distinctBy { it["presetId"] as String }.map {
val id = it["presetId"]!!.int
(rp.userLoginBonus.findLoginBonus(ext.int, 1, id)() ?: UserLoginBonus()).apply {
user = u.id.toInt()
presetId = id
lastUpdateDate = LocalDateTime.now()
isWatched = true
}
})
}
req.userCMissionList?.forEach { d ->
(rp.userCMission.findByUser_Card_ExtIdAndMissionId(ext, d.missionId)()
?: UserCMission().apply {
missionId = d.missionId
user = u
}
).apply { point = d.point }.also { rp.userCMission.save(it) }
d.userCMissionProgressList?.forEach inner@ { p ->
(rp.userCMissionProgress.findByUser_Card_ExtIdAndMissionIdAndOrder(ext, d.missionId, p.order)()
?: UserCMissionProgress().apply {
missionId = d.missionId
order = p.order
user = u
}
).apply {
progress = p.progress
stage = p.stage
}.also { rp.userCMissionProgress.save(it) }
}
}
}
return """{"returnCode":1}"""
}
}