From e3486042a5d3357e2bfc362a3d0096a8fbf3c0df Mon Sep 17 00:00:00 2001 From: Menci Date: Sat, 5 Jul 2025 00:45:09 +0800 Subject: [PATCH] [F] Data import fix (#153) --- src/main/java/ext/Json.kt | 12 ++++++-- src/main/java/icu/samnyan/aqua/net/Fedy.kt | 29 ++++++++++--------- .../aqua/net/games/ImportController.kt | 4 +-- .../sega/maimai2/Maimai2ServletController.kt | 7 ++--- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/main/java/ext/Json.kt b/src/main/java/ext/Json.kt index 61cd6545..d8a7e4c5 100644 --- a/src/main/java/ext/Json.kt +++ b/src/main/java/ext/Json.kt @@ -8,6 +8,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonNamingStrategy +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter // Jackson val ACCEPTABLE_FALSE = setOf("0", "false", "no", "off", "False", "None", "null") @@ -21,7 +23,13 @@ val JSON_FUZZY_BOOLEAN = SimpleModule().addDeserializer(Boolean::class.java, obj }) val JSON_DATETIME = SimpleModule().addDeserializer(java.time.LocalDateTime::class.java, object : JsonDeserializer() { override fun deserialize(parser: JsonParser, context: DeserializationContext) = - parser.text.asDateTime() ?: (400 - "Invalid date time value ${parser.text}") + // First try standard formats via asDateTime() method + parser.text.asDateTime() ?: try { + // Try maimai2 format (yyyy-MM-dd HH:mm:ss.0) + LocalDateTime.parse(parser.text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.0")) + } catch (e: Exception) { + 400 - "Invalid date time value ${parser.text}" + } }) val JACKSON = jacksonObjectMapper().apply { setSerializationInclusion(JsonInclude.Include.NON_NULL) @@ -73,4 +81,4 @@ val JSON = Json { // fun objectMapper(): ObjectMapper { // return JACKSON // } -//} \ No newline at end of file +//} diff --git a/src/main/java/icu/samnyan/aqua/net/Fedy.kt b/src/main/java/icu/samnyan/aqua/net/Fedy.kt index c09039b7..b6a89470 100644 --- a/src/main/java/icu/samnyan/aqua/net/Fedy.kt +++ b/src/main/java/icu/samnyan/aqua/net/Fedy.kt @@ -26,6 +26,7 @@ import org.springframework.transaction.support.TransactionTemplate import icu.samnyan.aqua.sega.maimai2.model.Mai2UserDataRepo import icu.samnyan.aqua.net.games.GenericUserDataRepo import icu.samnyan.aqua.net.games.IUserData +import java.util.concurrent.CompletableFuture @Configuration @ConfigurationProperties(prefix = "aqua-net.fedy") @@ -43,7 +44,6 @@ enum class FedyEvent { } @RestController -@ConditionalOnProperty("aqua-net.fedy.enabled", havingValue = "true") @API("/api/v2/fedy") class Fedy( val jwt: JWT, @@ -59,6 +59,7 @@ class Fedy( val transaction by lazy { TransactionTemplate(transactionManager) } private fun Str.checkKey() { + if (!props.enabled) 403 - "Fedy is disabled" if (!MessageDigest.isEqual(this.toByteArray(), props.key.toByteArray())) 403 - "Invalid Key" } @@ -77,7 +78,7 @@ class Fedy( val userFedy = AquaNetUserFedy(aquaNetUser = user) userFedyRepo.save(userFedy) - notifyRemote(FedyEvent.Linked, mapOf("auId" to user.auId, "nonce" to nonce)) + notify(FedyEvent.Linked, mapOf("auId" to user.auId, "nonce" to nonce)) return mapOf("linkedAt" to userFedy.createdAt.toEpochMilli()) } @@ -88,7 +89,7 @@ class Fedy( val userFedy = userFedyRepo.findByAquaNetUserAuId(user.auId) ?: 412 - "User not linked" userFedyRepo.delete(userFedy) - notifyRemote(FedyEvent.Unlinked, mapOf("auId" to user.auId)) + notify(FedyEvent.Unlinked, mapOf("auId" to user.auId)) return SUCCESS } @@ -140,10 +141,10 @@ class Fedy( transaction.execute { when (req.game) { "mai2" -> { if (req.removeOldData) { removeOldData(mai2UserDataRepo) } + val userAll = req.data["upsertUserAll"] as JDict // UserAll first, prevent using backlog + mai2UpsertUserAll.handle(mapOf("userId" to extId, "upsertUserAll" to userAll)) val playlogs = req.data["userPlaylogList"] as List playlogs.forEach { mai2UploadUserPlaylog.handle(mapOf("userId" to extId, "userPlaylog" to it)) } - val userAll = req.data["upsertUserAll"] as JDict - mai2UpsertUserAll.handle(mapOf("userId" to extId, "upsertUserAll" to userAll)) } else -> 406 - "Unsupported game" } } @@ -151,19 +152,19 @@ class Fedy( return SUCCESS } - fun onUpserted(game: Str, maybeExtId: Any?) = notifyRemote(FedyEvent.Upserted, game, maybeExtId) - fun onImported(game: Str, maybeExtId: Any?) = notifyRemote(FedyEvent.Imported, game, maybeExtId) + fun onUpserted(game: Str, maybeExtId: Any?) = maybeNotifyAsync(FedyEvent.Upserted, game, maybeExtId) + fun onImported(game: Str, maybeExtId: Any?) = maybeNotifyAsync(FedyEvent.Imported, game, maybeExtId) - private fun notifyRemote(event: FedyEvent, game: Str, maybeExtId: Any?) { try { - val extId = maybeExtId?.long ?: return - val user = userRepo.findByGhostCardExtId(extId) ?: return - val userFedy = userFedyRepo.findByAquaNetUserAuId(user.auId) ?: return - notifyRemote(event, mapOf("auId" to user.auId, "game" to game)) + private fun maybeNotifyAsync(event: FedyEvent, game: Str, maybeExtId: Any?) = if (!props.enabled) {} else CompletableFuture.runAsync { try { + val extId = maybeExtId?.long ?: return@runAsync + val user = userRepo.findByGhostCardExtId(extId) ?: return@runAsync + val userFedy = userFedyRepo.findByAquaNetUserAuId(user.auId) ?: return@runAsync + notify(event, mapOf("auId" to user.auId, "game" to game)) } catch (e: Exception) { - log.error("Error handling Fedy on notifyRemote($event, $game, $maybeExtId)", e) + log.error("Error handling Fedy on maybeNotifyAsync($event, $game, $maybeExtId)", e) } } - private fun notifyRemote(event: FedyEvent, body: Any?) { + private fun notify(event: FedyEvent, body: Any?) { val MAX_RETRY = 3 val body = body?.toJson() ?: "{}" var retry = 0 diff --git a/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt b/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt index a7d6e697..f9e822b3 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt @@ -70,7 +70,7 @@ abstract class ImportController, UserModel: @Autowired lateinit var netProps: AquaNetProps @Autowired lateinit var transManager: PlatformTransactionManager val trans by lazy { TransactionTemplate(transManager) } - @Autowired(required = false) @Lazy var fedy: Fedy? = null + @Autowired @Lazy lateinit var fedy: Fedy init { artemisRenames.values.forEach { @@ -147,7 +147,7 @@ abstract class ImportController, UserModel: } } - Fedy.getGameName(game)?.let { fedy?.onImported(it, u.ghostCard.extId) } + Fedy.getGameName(game)?.let { fedy.onImported(it, u.ghostCard.extId) } SUCCESS } diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/Maimai2ServletController.kt b/src/main/java/icu/samnyan/aqua/sega/maimai2/Maimai2ServletController.kt index e88f7371..f1dfe3d4 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/Maimai2ServletController.kt +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/Maimai2ServletController.kt @@ -17,6 +17,7 @@ import kotlin.reflect.full.declaredMemberProperties import icu.samnyan.aqua.net.Fedy import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Lazy +import org.springframework.beans.factory.ObjectProvider /** * @author samnyan (privateamusement@protonmail.com) @@ -40,7 +41,7 @@ class Maimai2ServletController( val net: Maimai2, ): MeowApi(serialize = { _, resp -> if (resp is String) resp else resp.toJson() }) { - @Autowired(required = false) @Lazy var fedy: Fedy? = null + @Autowired @Lazy lateinit var fedy: Fedy companion object { private val log = logger() @@ -94,9 +95,7 @@ class Maimai2ServletController( val ctx = RequestContext(req, data.mut) serialize(api, handlers[api]!!(ctx) ?: noop).also { log.info("$token : $api > ${it.truncate(500)}") - if (api == "UpsertUserAllApi") { - fedy?.onUpserted("mai2", data["userId"]) - } + if (api == "UpsertUserAllApi") { fedy.onUpserted("mai2", data["userId"]) } } } } catch (e: Exception) {