[F] Data import fix (#153)

This commit is contained in:
Menci 2025-07-05 00:45:09 +08:00 committed by GitHub
parent d79a4e5499
commit e3486042a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 30 additions and 22 deletions

View File

@ -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<java.time.LocalDateTime>() {
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)

View File

@ -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<JDict>
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

View File

@ -70,7 +70,7 @@ abstract class ImportController<ExportModel: IExportClass<UserModel>, 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<ExportModel: IExportClass<UserModel>, UserModel:
}
}
Fedy.getGameName(game)?.let { fedy?.onImported(it, u.ghostCard.extId) }
Fedy.getGameName(game)?.let { fedy.onImported(it, u.ghostCard.extId) }
SUCCESS
}

View File

@ -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) {