fix(ios): APIClient 401 错误码传递到 AuthManager,完成错误码分支闭环
- notifyTokenExpired 接受 errorCode 参数,通过 Notification userInfo 传递 - 401 响应提取 errorCode 后传给 notifyTokenExpired - AuthManager 观察者从 userInfo 读取 errorCode,调用 applyErrorCode - handleUnauthorized 根据 errorCode 切换精确状态(disabled/deleted/expired) - restoreSession 冷启动也检查 errorCode,禁用/删除用户不尝试 refresh Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
539b9a7d2b
commit
49bebad402
@ -30,9 +30,10 @@ final class AuthManager: ObservableObject {
|
||||
init() {
|
||||
tokenExpiredObserver = NotificationCenter.default.addObserver(
|
||||
forName: .tokenExpired, object: nil, queue: .main
|
||||
) { [weak self] _ in
|
||||
) { [weak self] notification in
|
||||
let errorCode = notification.userInfo?["errorCode"] as? String
|
||||
Task { @MainActor [weak self] in
|
||||
self?.handleUnauthorized()
|
||||
self?.handleUnauthorized(errorCode: errorCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,6 +60,14 @@ final class AuthManager: ObservableObject {
|
||||
let _: UserProfileResponse = try await APIClient.shared.request("/users/me")
|
||||
session = .authenticated
|
||||
} catch {
|
||||
// 如果 /users/me 返回了特定错误码(禁用/删除),直接切状态,不尝试 refresh
|
||||
if let apiError = error as? APIError, let code = apiError.errorCode {
|
||||
await APIClient.shared.setToken(nil)
|
||||
KeychainHelper.clear()
|
||||
applyErrorCode(code)
|
||||
return
|
||||
}
|
||||
|
||||
session = .refreshing
|
||||
if let refreshed = await tryRefresh() {
|
||||
await APIClient.shared.setToken(refreshed.accessToken)
|
||||
@ -120,11 +129,15 @@ final class AuthManager: ObservableObject {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func handleUnauthorized() {
|
||||
private func handleUnauthorized(errorCode: String? = nil) {
|
||||
Task {
|
||||
await APIClient.shared.setToken(nil)
|
||||
KeychainHelper.clear()
|
||||
session = .unauthenticated
|
||||
if let code = errorCode {
|
||||
applyErrorCode(code)
|
||||
} else {
|
||||
session = .unauthenticated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -69,10 +69,12 @@ actor APIClient {
|
||||
self.token = newToken
|
||||
return try await performRequest(path, method: method, body: body, queryItems: queryItems, isRetry: true)
|
||||
}
|
||||
await notifyTokenExpired()
|
||||
let info = extractErrorInfo(data)
|
||||
await notifyTokenExpired(errorCode: info.errorCode)
|
||||
throw decodeServerError(data, fallback: APIError.unauthorized)
|
||||
case 401:
|
||||
throw decodeServerError(data, fallback: APIError.unauthorized)
|
||||
let info = extractErrorInfo(data)
|
||||
throw APIError.serverError(info.message ?? "未授权", code: info.errorCode)
|
||||
case 400..<500:
|
||||
let msg = String(data: data, encoding: .utf8) ?? ""
|
||||
throw APIError.serverError(msg)
|
||||
@ -99,9 +101,13 @@ actor APIClient {
|
||||
}
|
||||
}
|
||||
|
||||
private func notifyTokenExpired() async {
|
||||
private func notifyTokenExpired(errorCode: String? = nil) async {
|
||||
var userInfo: [AnyHashable: Any]? = nil
|
||||
if let code = errorCode {
|
||||
userInfo = ["errorCode": code]
|
||||
}
|
||||
await MainActor.run {
|
||||
NotificationCenter.default.post(name: .tokenExpired, object: nil)
|
||||
NotificationCenter.default.post(name: .tokenExpired, object: nil, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user