升级 AppStore 缓存 Key 并在覆盖安装时强制重新登录。

统一账号作用域 Key 对齐 Android,新增缓存 schema 迁移清理旧数据,并同步更新相关模块与单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 09:35:55 +08:00
parent 05804ba7d6
commit c083f1d4b3
31 changed files with 852 additions and 123 deletions

View File

@ -38,7 +38,7 @@ final class AppStoreModuleTests: XCTestCase {
XCTAssertEqual(AppAccountType.unknown("future_account").rawValue, "future_account")
}
func testSessionUsesExistingKeysAndPreservesUnknownAccountType() {
func testSessionUsesGlobalIdentityAndScopedBusinessContext() {
defaults.set("legacy-token", forKey: "key_in_token")
defaults.set("future_account", forKey: "key_in_account_type")
@ -46,11 +46,37 @@ final class AppStoreModuleTests: XCTestCase {
XCTAssertEqual(appStore.session.accountType, .unknown("future_account"))
appStore.session.userId = "1001"
appStore.session.roleCode = "future_role"
appStore.session.currentScenicId = 22
appStore.session.currentScenicName = "测试景区"
appStore.session.currentStoreId = 31
XCTAssertEqual(defaults.string(forKey: "key_in_user_id"), "1001")
XCTAssertEqual(defaults.string(forKey: "key_in_current_scenic_id"), "22")
XCTAssertEqual(appStore.session.accountCachePrefix, "1001_future_account")
XCTAssertNil(defaults.object(forKey: "key_in_current_scenic_id"))
XCTAssertEqual(defaults.string(forKey: "future_account_1001_key_in_role_code"), "future_role")
XCTAssertEqual(defaults.string(forKey: "future_account_1001_key_in_current_scenic_id"), "22")
XCTAssertEqual(defaults.string(forKey: "future_account_1001_key_in_current_scenic_name"), "测试景区")
XCTAssertEqual(defaults.integer(forKey: "future_account_1001_scenic_22_scenic_store_id"), 31)
XCTAssertEqual(appStore.session.accountCachePrefix, "future_account_1001")
}
func testIncompleteAccountScopeDoesNotWriteBusinessCache() {
appStore.session.userId = "1001"
appStore.session.currentScenicId = 22
appStore.session.roleCode = "photographer"
appStore.permissions.savePermissionItems([HomePermissionItem(id: "1", name: "钱包", uri: "wallet")])
appStore.location.onlineStatus = true
appStore.payment.isOpenReceiveVoice = true
appStore.scenicQueue.punchSpotId = "33"
XCTAssertEqual(appStore.session.accountCachePrefix, "")
XCTAssertEqual(appStore.session.currentScenicId, 0)
XCTAssertEqual(appStore.session.roleCode, "")
XCTAssertTrue(appStore.permissions.permissionItems().isEmpty)
XCTAssertFalse(appStore.location.onlineStatus)
XCTAssertFalse(appStore.payment.isOpenReceiveVoice)
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
XCTAssertFalse(defaults.dictionaryRepresentation().keys.contains { $0.contains("guest") })
}
func testFeatureStoresAreIsolatedByAccountAndDecodeSnapshots() {
@ -68,8 +94,11 @@ final class AppStoreModuleTests: XCTestCase {
appStore.payment.isOpenReceiveVoice = true
appStore.scenicQueue.punchSpotId = "33"
appStore.scenicQueue.punchSpotName = "东门"
appStore.scenicQueue.remark = "请提前准备"
appStore.scenicQueue.saveSettingsSnapshot(queueSettings)
appStore.scenicQueue.savePresetVoices([" A ", "", "B"])
XCTAssertTrue(appStore.scenicQueue.useOfflineTTS)
appStore.scenicQueue.useOfflineTTS = false
selectAccount(userId: "2002", type: .storeUser)
XCTAssertTrue(appStore.permissions.permissionItems().isEmpty)
@ -80,6 +109,7 @@ final class AppStoreModuleTests: XCTestCase {
XCTAssertFalse(appStore.payment.isOpenReceiveVoice)
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
XCTAssertNil(appStore.scenicQueue.settingsSnapshot())
XCTAssertTrue(appStore.scenicQueue.useOfflineTTS)
selectAccount(userId: "1001", type: .scenicUser)
XCTAssertEqual(appStore.permissions.permissionItems(), permissions)
@ -90,24 +120,68 @@ final class AppStoreModuleTests: XCTestCase {
XCTAssertTrue(appStore.payment.isOpenReceiveVoice)
XCTAssertTrue(appStore.scenicQueue.hasPunchSpotSaved())
XCTAssertEqual(appStore.scenicQueue.punchSpotName, "东门")
XCTAssertEqual(appStore.scenicQueue.remark, "请提前准备")
XCTAssertEqual(appStore.scenicQueue.settingsSnapshot(), queueSettings)
XCTAssertEqual(appStore.scenicQueue.presetVoices(), ["A", "B"])
XCTAssertFalse(appStore.scenicQueue.useOfflineTTS)
}
func testStoreAndQueueCachesAreIsolatedByScenicAndSpot() {
selectAccount(userId: "1001", type: .scenicUser, scenicId: 22)
appStore.session.currentStoreId = 220
appStore.scenicQueue.punchSpotId = "33"
appStore.scenicQueue.punchSpotName = "东门"
appStore.scenicQueue.remark = "东门备注"
appStore.scenicQueue.useOfflineTTS = false
appStore.scenicQueue.punchSpotId = "44"
XCTAssertEqual(appStore.scenicQueue.punchSpotName, "")
XCTAssertEqual(appStore.scenicQueue.remark, "")
appStore.scenicQueue.punchSpotName = "西门"
appStore.scenicQueue.remark = "西门备注"
appStore.scenicQueue.punchSpotId = "33"
XCTAssertEqual(appStore.scenicQueue.punchSpotName, "东门")
XCTAssertEqual(appStore.scenicQueue.remark, "东门备注")
appStore.session.currentScenicId = 23
XCTAssertEqual(appStore.session.currentStoreId, 0)
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
XCTAssertTrue(appStore.scenicQueue.useOfflineTTS)
appStore.session.currentStoreId = 230
appStore.scenicQueue.punchSpotId = "55"
appStore.scenicQueue.punchSpotName = "南门"
appStore.session.currentScenicId = 22
XCTAssertEqual(appStore.session.currentStoreId, 220)
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "33")
XCTAssertEqual(appStore.scenicQueue.punchSpotName, "东门")
XCTAssertFalse(appStore.scenicQueue.useOfflineTTS)
XCTAssertEqual(defaults.string(forKey: "scenic_user_1001_scenic_22_scenic_queue_punch_spot_id"), "33")
XCTAssertEqual(defaults.string(forKey: "scenic_user_1001_scenic_22_spot_33_scenic_queue_punch_spot_name"), "东门")
XCTAssertEqual(defaults.string(forKey: "scenic_user_1001_scenic_22_spot_33_scenic_queue_remark"), "东门备注")
XCTAssertFalse(defaults.dictionaryRepresentation().keys.contains { $0.contains("__scenic_queue") })
}
func testLogoutClearsCurrentAccountModulesAndPreservesLoginPreferences() {
appStore.session.lastLoginUsername = "13800000000"
appStore.session.privacyAgreementAccepted = true
selectAccount(userId: "other", type: .storeUser)
selectAccount(userId: "other", type: .storeUser, scenicId: 22)
appStore.scenicQueue.punchSpotId = "99"
selectAccount(userId: "current", type: .scenicUser)
selectAccount(userId: "current", type: .scenicUser, scenicId: 22)
appStore.session.token = "token"
appStore.permissions.legacyRoleId = 41
appStore.permissions.savePermissionItems([HomePermissionItem(id: "1", name: "钱包", uri: "wallet")])
appStore.location.onlineStatus = true
appStore.payment.isOpenReceiveVoice = true
appStore.scenicQueue.punchSpotId = "33"
appStore.scenicQueue.punchSpotName = "东门"
appStore.session.currentScenicId = 23
appStore.scenicQueue.punchSpotId = "44"
appStore.scenicQueue.punchSpotName = "西门"
appStore.logout()
@ -116,19 +190,22 @@ final class AppStoreModuleTests: XCTestCase {
XCTAssertEqual(appStore.session.lastLoginUsername, "13800000000")
XCTAssertTrue(appStore.session.privacyAgreementAccepted)
selectAccount(userId: "current", type: .scenicUser)
selectAccount(userId: "current", type: .scenicUser, scenicId: 22)
XCTAssertEqual(appStore.permissions.legacyRoleId, 0)
XCTAssertTrue(appStore.permissions.permissionItems().isEmpty)
XCTAssertFalse(appStore.location.onlineStatus)
XCTAssertFalse(appStore.payment.isOpenReceiveVoice)
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
appStore.session.currentScenicId = 23
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "")
selectAccount(userId: "other", type: .storeUser)
selectAccount(userId: "other", type: .storeUser, scenicId: 22)
XCTAssertEqual(appStore.scenicQueue.punchSpotId, "99")
}
private func selectAccount(userId: String, type: AppAccountType) {
private func selectAccount(userId: String, type: AppAccountType, scenicId: Int = 22) {
appStore.session.userId = userId
appStore.session.accountType = type
appStore.session.currentScenicId = scenicId
}
}