feat: some data management APIs (#176)

This commit is contained in:
Menci
2025-10-07 00:27:39 +08:00
committed by GitHub
parent 967d311ee4
commit b0d0f8ef7d
8 changed files with 223 additions and 141 deletions

View File

@@ -30,7 +30,8 @@ class CardController(
val cardService: CardService,
val cardGameService: CardGameService,
val cardRepository: CardRepository,
val props: AquaNetProps
val props: AquaNetProps,
val fedy: Fedy
) {
companion object {
val log = logger()
@@ -80,10 +81,12 @@ class CardController(
val id = cardService.sanitizeCardId(cardId)
// Create a new card
cardService.registerByAccessCode(id, u)
val newCard = cardService.registerByAccessCode(id, u)
log.info("Net /card/link : Created new card $id for user ${u.username}")
fedy.onCardLinked(newCard.luid, oldExtId = null, ghostExtId = u.ghostCard.extId, emptyList())
return SUCCESS
}
@@ -98,6 +101,9 @@ class CardController(
val games = migrate.split(',')
cardGameService.migrate(card, games)
fedy.onCardLinked(card.luid, oldExtId = card.extId, ghostExtId = u.ghostCard.extId,
games.map { Fedy.getGameName(it) }.filterNotNull())
log.info("Net /card/link : Linked card ${card.id} to user ${u.username} and migrated data to ${games.joinToString()}")
SUCCESS
@@ -115,10 +121,14 @@ class CardController(
// Ghost cards cannot be unlinked
if (card.isGhost) 400 - "Account virtual cards cannot be unlinked"
val luid = card.luid
// Unbind the card
card.aquaUser = null
async { cardRepository.save(card) }
fedy.onCardUnlinked(luid)
log.info("Net /card/unlink : Unlinked card ${card.id} from user ${u.username}")
SUCCESS
@@ -136,7 +146,7 @@ class CardController(
*
* Assumption: The card is already linked to the user.
*/
suspend fun <T : IUserData> migrateCard(repo: GenericUserDataRepo<T>, cardRepo: CardRepository, card: Card): Bool {
suspend fun <T : IUserData> migrateCard(gameName: Str, repo: GenericUserDataRepo<T>, cardRepo: CardRepository, card: Card): Bool {
val ghost = card.aquaUser!!.ghostCard
// Check if data already exists in the user's ghost card
@@ -144,7 +154,7 @@ suspend fun <T : IUserData> migrateCard(repo: GenericUserDataRepo<T>, cardRepo:
// Create a new dummy card for deleted data
it.card = async {
cardRepo.save(Card().apply {
luid = "Migrated data of ghost card ${ghost.id} for user ${card.aquaUser!!.auId} on ${utcNow().isoDateTime()}"
luid = "Migrated data of ghost card ${ghost.id} for user ${card.aquaUser!!.auId} on ${utcNow().isoDateTime()} (${gameName})"
// Randomize an extId outside the normal range
extId = Random.nextLong(0x7FFFFFF7L shl 32, 0x7FFFFFFFL shl 32)
registerTime = LocalDateTime.now()
@@ -162,6 +172,23 @@ suspend fun <T : IUserData> migrateCard(repo: GenericUserDataRepo<T>, cardRepo:
return true
}
suspend fun <T : IUserData> orphanData(gameName: Str, repo: GenericUserDataRepo<T>, cardRepo: CardRepository, card: Card) {
// Orphan the data by assigning them to a dummy card
repo.findByCard(card)?.let {
// Create a new dummy card for orphaned data
it.card = async {
cardRepo.save(Card().apply {
luid = "Unmigrated data of card ${card.luid} for user ${card.aquaUser!!.auId} on ${utcNow().isoDateTime()} (${gameName})"
// Randomize an extId outside the normal range
extId = Random.nextLong(0x7FFFFFF7L shl 32, 0x7FFFFFFFL shl 32)
registerTime = LocalDateTime.now()
accessTime = registerTime
})
}
async { repo.save(it) }
}
}
suspend fun getSummaryFor(repo: GenericUserDataRepo<*>, card: Card): Map<Str, Any>? {
val data = async { repo.findByCard(card) } ?: return null
return mapOf(
@@ -189,18 +216,20 @@ class CardGameService(
suspend fun migrate(crd: Card, games: List<String>) = async {
// Migrate data from the card to the user's ghost card
// An easy migration is to change the UserData card field to the user's ghost card
val dataRepos = mapOf(
"mai2" to maimai2,
"chu3" to chusan,
"ongeki" to ongeki,
"wacca" to wacca,
)
val remainingGames = dataRepos.keys.toMutableSet()
games.forEach { game ->
when (game) {
"mai2" -> migrateCard(maimai2, cardRepo, crd)
"chu3" -> migrateCard(chusan, cardRepo, crd)
"ongeki" -> migrateCard(ongeki, cardRepo, crd)
"wacca" -> migrateCard(wacca, cardRepo, crd)
// TODO: diva
// "diva" -> diva.findByPdId(card.extId.toInt()).getOrNull()?.let {
// it.pdId = card.aquaUser!!.ghostCard
// }
}
val dataRepo = dataRepos[game] ?: return@forEach
migrateCard(game, dataRepo, cardRepo, crd)
remainingGames.remove(game)
}
// For remaining games, orphan the data by assigning them to a dummy card
remainingGames.forEach { game -> orphanData(game, dataRepos[game]!!, cardRepo, crd) }
}
suspend fun getSummary(card: Card) = async {