迁移合作订单完整能力,并统一 AppRoleCode 角色权限与首页菜单展示。

新增 CooperationOrder 模块(双 Tab 列表、获客员绑定、主 Tab 扫码)、订单来源/带客单绑定及配套 API 测试;同步引入 roleCode 权限匹配与相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 13:33:00 +08:00
parent d310d26293
commit 1970572edd
66 changed files with 4247 additions and 201 deletions

View File

@ -13,6 +13,8 @@ struct AccountSnapshot: Codable, Equatable {
var profile: AccountProfile?
var accountType: String?
var businessUserId: Int?
var currentRoleCode: String?
///
var currentRoleId: Int?
var scenicScopes: [BusinessScope]
var storeScopes: [BusinessScope]
@ -24,6 +26,7 @@ struct AccountSnapshot: Codable, Equatable {
profile: AccountProfile? = nil,
accountType: String? = nil,
businessUserId: Int? = nil,
currentRoleCode: String? = nil,
currentRoleId: Int? = nil,
scenicScopes: [BusinessScope] = [],
storeScopes: [BusinessScope] = [],
@ -33,12 +36,25 @@ struct AccountSnapshot: Codable, Equatable {
self.profile = profile
self.accountType = accountType
self.businessUserId = businessUserId
self.currentRoleCode = currentRoleCode
self.currentRoleId = currentRoleId
self.scenicScopes = scenicScopes
self.storeScopes = storeScopes
self.currentScenicId = currentScenicId
self.currentStoreId = currentStoreId
}
/// legacy role id role_code id
mutating func migrateLegacyRoleIfNeeded() {
guard AppRoleCode.fromCode(currentRoleCode) == nil,
let legacyId = currentRoleId,
legacyId > 0,
let migrated = AppRoleCode.fromLegacyRoleId(legacyId) else {
return
}
currentRoleCode = migrated.rawValue
currentRoleId = nil
}
}
/// 使 UserDefaults
@ -67,11 +83,17 @@ final class AccountSnapshotStore {
defaults.set(data, forKey: key)
}
///
/// legacy
func load() -> AccountSnapshot? {
guard let data = defaults.data(forKey: key) else { return nil }
do {
return try decoder.decode(AccountSnapshot.self, from: data)
var snapshot = try decoder.decode(AccountSnapshot.self, from: data)
let beforeCode = snapshot.currentRoleCode
snapshot.migrateLegacyRoleIfNeeded()
if snapshot.currentRoleCode != beforeCode {
save(snapshot)
}
return snapshot
} catch {
clear()
return nil
@ -87,7 +109,7 @@ final class AccountSnapshotStore {
@MainActor
func saveCurrentSelection(
accountContext: AccountContext,
currentRoleId: Int?
currentRoleCode: String?
) {
let existing = load()
save(
@ -95,7 +117,7 @@ final class AccountSnapshotStore {
profile: accountContext.profile ?? existing?.profile,
accountType: existing?.accountType,
businessUserId: existing?.businessUserId,
currentRoleId: currentRoleId ?? existing?.currentRoleId,
currentRoleCode: currentRoleCode ?? existing?.currentRoleCode,
scenicScopes: accountContext.scenicScopes,
storeScopes: accountContext.storeScopes,
currentScenicId: accountContext.currentScenic?.id,