Files
suixinkan_ios_new/suixinkan/App/State/SessionBootstrapper.swift
汉秋 5ab9b1b324 修复账号切换后「我的」页当前账号展示错误,并统一举报摄影师首页 URI。
切换账号时以用户选中账号为准写入展示标题与业务作用域,避免沿用旧门店名称;同时将首页举报摄影师入口 URI 对齐为 report_photographer。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 17:21:04 +08:00

217 lines
7.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SessionBootstrapper.swift
// suixinkan
//
// Created by Codex on 2026/6/20.
//
import Foundation
import Combine
@MainActor
/// token
final class SessionBootstrapper {
private let tokenStore: SessionTokenStore
private let snapshotStore: AccountSnapshotStore
private var didAttemptRestore = false
/// token
init(
tokenStore: SessionTokenStore,
snapshotStore: AccountSnapshotStore
) {
self.tokenStore = tokenStore
self.snapshotStore = snapshotStore
}
/// 使
convenience init() {
self.init(
tokenStore: SessionTokenStore(),
snapshotStore: AccountSnapshotStore()
)
}
/// rolePermissions
func restore(
appSession: AppSession,
accountContext: AccountContext,
permissionContext: PermissionContext,
profileAPI: UserProfileServing,
accountContextAPI: AccountContextServing,
toastCenter: ToastCenter
) async {
guard !didAttemptRestore, !appSession.isLoggedIn else { return }
didAttemptRestore = true
guard let token = tokenStore.load() else {
appSession.logout()
return
}
appSession.beginRestoring(token: token)
let cachedSnapshot = restoreCachedSnapshot(to: accountContext)
let loader = AccountContextLoader()
do {
try await loader.restorePermissions(
permissionContext: permissionContext,
accountContext: accountContext,
accountContextAPI: accountContextAPI,
cachedCurrentRoleCode: cachedSnapshot?.currentRoleCode,
cachedLegacyRoleId: cachedSnapshot?.currentRoleId,
cachedCurrentScenicId: cachedSnapshot?.currentScenicId,
cachedCurrentStoreId: cachedSnapshot?.currentStoreId
)
appSession.markLoggedIn(token: token)
refreshSupplementalContextInBackground(
loader: loader,
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: profileAPI,
accountContextAPI: accountContextAPI,
toastCenter: toastCenter,
cachedSnapshot: cachedSnapshot
)
} catch {
handleBlockingRestoreFailure(
error,
token: token,
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
toastCenter: toastCenter
)
}
}
///
private func restoreCachedSnapshot(to accountContext: AccountContext) -> AccountSnapshot? {
guard let snapshot = snapshotStore.load() else { return nil }
accountContext.applyLogin(
profile: snapshot.profile,
accountType: snapshot.accountType,
businessUserId: snapshot.businessUserId,
displayTitle: snapshot.currentAccountDisplayTitle
)
accountContext.replaceScopes(
scenic: snapshot.scenicScopes,
stores: snapshot.storeScopes,
currentScenicId: snapshot.currentScenicId,
currentStoreId: snapshot.currentStoreId,
explicitSelection: true
)
return snapshot
}
///
private func refreshSupplementalContextInBackground(
loader: AccountContextLoader,
appSession: AppSession,
accountContext: AccountContext,
permissionContext: PermissionContext,
profileAPI: UserProfileServing,
accountContextAPI: AccountContextServing,
toastCenter: ToastCenter,
cachedSnapshot: AccountSnapshot?
) {
Task {
do {
try await loader.refreshSupplementalContext(
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: profileAPI,
accountContextAPI: accountContextAPI,
cachedCurrentScenicId: cachedSnapshot?.currentScenicId,
cachedCurrentStoreId: cachedSnapshot?.currentStoreId
)
saveLatestSnapshot(from: accountContext, permissionContext: permissionContext)
} catch {
handleSupplementalRefreshFailure(
error,
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
toastCenter: toastCenter
)
}
}
}
///
private func handleBlockingRestoreFailure(
_ error: Error,
token: String,
appSession: AppSession,
accountContext: AccountContext,
permissionContext: PermissionContext,
toastCenter: ToastCenter
) {
if APIError.isAuthenticationExpired(error) {
clearPersistedSession(
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext
)
toastCenter.show("登录状态已失效,请重新登录")
return
}
appSession.markLoggedIn(token: token)
toastCenter.show("网络异常,已使用本地登录状态")
}
///
private func handleSupplementalRefreshFailure(
_ error: Error,
appSession: AppSession,
accountContext: AccountContext,
permissionContext: PermissionContext,
toastCenter: ToastCenter
) {
if APIError.isAuthenticationExpired(error) {
clearPersistedSession(
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext
)
toastCenter.show("登录状态已失效,请重新登录")
return
}
toastCenter.show("网络异常,已使用本地登录状态")
}
/// token
private func clearPersistedSession(
appSession: AppSession,
accountContext: AccountContext,
permissionContext: PermissionContext
) {
try? tokenStore.clear()
snapshotStore.clear()
accountContext.reset()
permissionContext.reset()
appSession.logout()
}
///
private func saveLatestSnapshot(from accountContext: AccountContext, permissionContext: PermissionContext) {
let existing = snapshotStore.load()
snapshotStore.save(
AccountSnapshot(
profile: accountContext.profile,
accountType: accountContext.accountType ?? existing?.accountType,
businessUserId: existing?.businessUserId,
currentAccountDisplayTitle: accountContext.currentAccountDisplayTitle ?? existing?.currentAccountDisplayTitle,
currentRoleCode: permissionContext.currentAppRole?.rawValue ?? existing?.currentRoleCode,
scenicScopes: accountContext.scenicScopes,
storeScopes: accountContext.storeScopes,
currentScenicId: accountContext.currentScenic?.id,
currentStoreId: accountContext.currentStore?.id
)
)
}
}