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

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

214 lines
7.2 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.

//
// AccountContext.swift
// suixinkan
//
// Created by Codex on 2026/6/18.
//
import Combine
import Foundation
///
struct AccountProfile: Codable, Equatable {
var userId: String
var displayName: String
var phone: String?
var avatarURL: String?
}
///
struct BusinessScope: Codable, Equatable, Identifiable {
///
enum Kind: Codable, Equatable {
case scenic
case store
}
var id: Int
var name: String
var kind: Kind
var parentScenicId: Int?
var status: Int?
var address: String?
var latitude: Double?
var longitude: Double?
var coverURLString: String?
/// parentScenicId
init(
id: Int,
name: String,
kind: Kind,
parentScenicId: Int? = nil,
status: Int? = nil,
address: String? = nil,
latitude: Double? = nil,
longitude: Double? = nil,
coverURLString: String? = nil
) {
self.id = id
self.name = name
self.kind = kind
self.parentScenicId = parentScenicId
self.status = status
self.address = address
self.latitude = latitude
self.longitude = longitude
self.coverURLString = coverURLString
}
}
@MainActor
/// /
final class AccountContext: ObservableObject {
@Published private(set) var profile: AccountProfile?
@Published private(set) var accountType: String?
@Published private(set) var businessUserId: Int?
/// /
@Published private(set) var currentAccountDisplayTitle: String?
@Published private(set) var scenicScopes: [BusinessScope] = []
@Published private(set) var storeScopes: [BusinessScope] = []
@Published var currentScenic: BusinessScope?
@Published var currentStore: BusinessScope?
/// AccountSwitchAccount.id
var currentAccountIdentifier: String? {
guard let businessUserId, businessUserId > 0 else { return nil }
let type = accountType?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !type.isEmpty else { return nil }
return "\(type)_\(businessUserId)"
}
/// Android
var accountCachePrefix: String? {
let userId = profile?.userId.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !userId.isEmpty else { return nil }
let type = accountType?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return type.isEmpty ? userId : "\(type)_\(userId)"
}
///
func applyLogin(
profile: AccountProfile? = nil,
accountType: String? = nil,
businessUserId: Int? = nil,
displayTitle: String? = nil
) {
self.profile = profile
self.accountType = accountType?.trimmingCharacters(in: .whitespacesAndNewlines)
if let businessUserId, businessUserId > 0 {
self.businessUserId = businessUserId
}
if let displayTitle = normalizedText(displayTitle) {
currentAccountDisplayTitle = displayTitle
}
}
///
func replaceProfile(_ profile: AccountProfile?) {
self.profile = profile
}
///
func replaceScopes(
scenic: [BusinessScope],
stores: [BusinessScope],
currentScenicId: Int? = nil,
currentStoreId: Int? = nil,
explicitSelection: Bool = false
) {
scenicScopes = scenic
storeScopes = stores
if explicitSelection {
currentScenic = currentScenicId.flatMap { id in
scenic.first { $0.id == id }
} ?? scenic.first
if let storeId = currentStoreId, storeId > 0 {
currentStore = resolvedStore(
in: stores,
currentScenic: currentScenic,
preferredStoreId: storeId,
allowFallbackToFirst: false
)
} else {
currentStore = nil
}
return
}
currentScenic = currentScenicId.flatMap { id in
scenic.first { $0.id == id }
} ?? currentScenic.flatMap { current in
scenic.first { $0.id == current.id }
} ?? scenic.first
currentStore = resolvedStore(
in: stores,
currentScenic: currentScenic,
preferredStoreId: currentStoreId ?? currentStore?.id,
allowFallbackToFirst: true
)
}
///
func selectScenic(id scenicId: Int) {
guard let scenic = scenicScopes.first(where: { $0.id == scenicId }) else { return }
currentScenic = scenic
currentStore = resolvedStore(
in: storeScopes,
currentScenic: scenic,
preferredStoreId: currentStore?.id,
allowFallbackToFirst: true
)
}
///
func selectStore(id storeId: Int) {
guard let store = storeScopes.first(where: { $0.id == storeId }) else { return }
currentStore = store
if let scenicId = store.parentScenicId,
currentScenic?.id != scenicId,
let scenic = scenicScopes.first(where: { $0.id == scenicId }) {
currentScenic = scenic
}
}
/// 退
func reset() {
profile = nil
accountType = nil
businessUserId = nil
currentAccountDisplayTitle = nil
scenicScopes = []
storeScopes = []
currentScenic = nil
currentStore = nil
}
/// ID
private func resolvedStore(
in stores: [BusinessScope],
currentScenic: BusinessScope?,
preferredStoreId: Int?,
allowFallbackToFirst: Bool
) -> BusinessScope? {
let scenicId = currentScenic?.id
let storesForScenic = stores.filter { store in
guard let scenicId else { return true }
return store.parentScenicId == nil || store.parentScenicId == scenicId
}
if let preferredStoreId,
let store = storesForScenic.first(where: { $0.id == preferredStoreId }) {
return store
}
guard allowFallbackToFirst else { return nil }
return storesForScenic.first
}
///
private func normalizedText(_ value: String?) -> String? {
let text = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return text.isEmpty ? nil : text
}
}