Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 43 additions & 14 deletions src/main/java/icu/samnyan/aqua/net/CardController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
}

Expand All @@ -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
Expand All @@ -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
Expand All @@ -136,15 +146,15 @@ 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
async { repo.findByCard(ghost) }?.let {
// 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()
Expand All @@ -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(
Expand Down Expand Up @@ -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)
Comment on lines +227 to +229
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): 未知游戏的静默返回可能隐藏迁移问题。

考虑在 dataRepos 中找不到游戏时添加一条日志消息,以帮助调试迁移问题。

Suggested change
val dataRepo = dataRepos[game] ?: return@forEach
migrateCard(game, dataRepo, cardRepo, crd)
remainingGames.remove(game)
val dataRepo = dataRepos[game]
if (dataRepo == null) {
logger.warn("Migration issue: dataRepo not found for game '$game'. Card id: ${crd.id}")
return@forEach
}
migrateCard(game, dataRepo, cardRepo, crd)
remainingGames.remove(game)
Original comment in English

suggestion (bug_risk): Silent return for unknown game may hide migration issues.

Consider adding a log message when a game is not found in dataRepos to aid in debugging migration issues.

Suggested change
val dataRepo = dataRepos[game] ?: return@forEach
migrateCard(game, dataRepo, cardRepo, crd)
remainingGames.remove(game)
val dataRepo = dataRepos[game]
if (dataRepo == null) {
logger.warn("Migration issue: dataRepo not found for game '$game'. Card id: ${crd.id}")
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 {
Expand Down
Loading