diff --git a/Podfile b/Podfile index c6c17cb..5d7c5d5 100644 --- a/Podfile +++ b/Podfile @@ -16,7 +16,7 @@ target 'yana' do # pod 'NELocalConversationUIKit', '10.6.1' # 本地会话列表组件。 # Networks - pod 'Alamofire' +# pod 'Alamofire' end post_install do |installer| diff --git a/Podfile.lock b/Podfile.lock index 4dc3a44..31e689b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,16 +1,3 @@ -PODS: - - Alamofire (5.10.2) - -DEPENDENCIES: - - Alamofire - -SPEC REPOS: - trunk: - - Alamofire - -SPEC CHECKSUMS: - Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496 - -PODFILE CHECKSUM: 4ccb5fbbedd3dcb71c35d00e7bfd0d280d4ced88 +PODFILE CHECKSUM: 9817fb04504ebed48143ca78630f70d3b3402405 COCOAPODS: 1.16.2 diff --git a/yana.xcodeproj/project.pbxproj b/yana.xcodeproj/project.pbxproj index 30fa9e7..c0215b5 100644 --- a/yana.xcodeproj/project.pbxproj +++ b/yana.xcodeproj/project.pbxproj @@ -145,7 +145,6 @@ 4C3E651B2DB61F7A00E5A455 /* Sources */, 4C3E651C2DB61F7A00E5A455 /* Frameworks */, 4C3E651D2DB61F7A00E5A455 /* Resources */, - A9AAC370C902C50E37521C40 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -263,23 +262,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - A9AAC370C902C50E37521C40 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-yana/Pods-yana-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-yana/Pods-yana-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-yana/Pods-yana-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/yana.xcodeproj/xcuserdata/edwinqqq.xcuserdatad/xcschemes/xcschememanagement.plist b/yana.xcodeproj/xcuserdata/edwinqqq.xcuserdatad/xcschemes/xcschememanagement.plist index 9674f9b..e5c6177 100644 --- a/yana.xcodeproj/xcuserdata/edwinqqq.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/yana.xcodeproj/xcuserdata/edwinqqq.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ yana.xcscheme_^#shared#^_ orderHint - 3 + 1 diff --git a/yana.xcworkspace/xcuserdata/edwinqqq.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/yana.xcworkspace/xcuserdata/edwinqqq.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 0f8d13c..3922ed9 100644 --- a/yana.xcworkspace/xcuserdata/edwinqqq.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/yana.xcworkspace/xcuserdata/edwinqqq.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,4 +3,38 @@ uuid = "A60FAB2A-3184-45B2-920F-A3D7A086CF95" type = "0" version = "2.0"> + + + + + + + + + + diff --git a/yana/APIs/API-README.md b/yana/APIs/API-README.md new file mode 100644 index 0000000..4add884 --- /dev/null +++ b/yana/APIs/API-README.md @@ -0,0 +1,152 @@ +## 🔐 **自动认证 Header 机制** + +### 概述 + +系统会自动检查用户的登录状态,并在所有API请求中自动添加认证相关的header。 + +### 工作原理 + +1. **检查认证状态**:每次发起API请求时,系统会检查`AccountModel`的有效性 +2. **自动添加Header**:如果用户已登录且认证信息有效,自动添加以下header: + - `pub_uid`: 用户唯一标识(来自`AccountModel.uid`) + - `pub_ticket`: 业务会话票据(来自`AccountModel.ticket`) + +### 实现细节 + +```swift +// 在 APIConfiguration.defaultHeaders 中实现 +static var defaultHeaders: [String: String] { + var headers = [ + "Content-Type": "application/json", + "Accept": "application/json", + // ... 其他基础header + ] + + // 检查用户认证状态并添加相关 headers + let authStatus = UserInfoManager.checkAuthenticationStatus() + + if authStatus.canAutoLogin { + // 添加认证 headers(仅在 AccountModel 有效时) + if let userId = UserInfoManager.getCurrentUserId() { + headers["pub_uid"] = userId + } + + if let userTicket = UserInfoManager.getCurrentUserTicket() { + headers["pub_ticket"] = userTicket + } + } + + return headers +} +``` + +### 认证状态检查 + +系统使用`UserInfoManager.checkAuthenticationStatus()`检查认证状态: + +```swift +enum AuthenticationStatus { + case valid // 认证有效,可以自动登录 + case invalid // 认证信息不完整或无效 + case notFound // 未找到认证信息 +} +``` + +**认证有效的条件**: +- `AccountModel`存在 +- `uid`不为空 +- `ticket`不为空 +- `accessToken`不为空 + +### 使用方式 + +认证header的添加是**完全自动的**,开发者无需手动处理: + +```swift +// 示例:发起API请求 +let request = ConfigRequest() +let response = try await apiService.request(request) + +// 如果用户已登录,以上请求会自动包含: +// Header: pub_uid = "12345" +// Header: pub_ticket = "eyJhbGciOiJIUzI1NiJ9..." +``` + +### 测试功能 + +在DEBUG模式下,可以使用测试方法验证功能: + +```swift +#if DEBUG +// 运行认证header测试 +UserInfoManager.testAuthenticationHeaders() +#endif +``` + +测试包括: +1. **未登录状态测试**:验证不会添加认证header +2. **已登录状态测试**:验证正确添加认证header +3. **清理测试**:验证测试数据正确清理 + +### 调试日志 + +在DEBUG模式下,系统会输出认证header的添加情况: + +``` +🔐 添加认证 header: pub_uid = 12345 +🔐 添加认证 header: pub_ticket = eyJhbGciOiJIUzI1NiJ9... +``` + +或者: + +``` +🔐 跳过认证 header 添加 - 认证状态: 未找到认证信息 +``` + +### 最佳实践 + +1. **登录成功后保存完整认证信息**: +```swift +UserInfoManager.saveCompleteAuthenticationData( + accessToken: loginResponse.accessToken, + ticket: ticketResponse.ticket, + uid: loginResponse.uid, + userInfo: loginResponse.userInfo +) +``` + +2. **登出时清理认证信息**: +```swift +UserInfoManager.clearAllAuthenticationData() +``` + +3. **应用启动时检查认证状态**: +```swift +let authStatus = UserInfoManager.checkAuthenticationStatus() +if authStatus.canAutoLogin { + // 可以直接进入主界面 +} else { + // 需要重新登录 +} +``` + +### 安全考虑 + +- **内存安全**:ticket存储在内存中,应用重启需重新获取 +- **持久化安全**:uid和accessToken存储在Keychain中,确保安全性 +- **自动清理**:认证失效时系统会自动停止添加认证header + +### 故障排除 + +1. **认证header未添加**: + - 检查用户是否已正确登录 + - 验证AccountModel是否包含有效的uid和ticket + - 确认认证状态为valid + +2. **ticket为空**: + - 检查登录流程是否正确获取了ticket + - 验证ticket是否正确保存到AccountModel + +3. **调试模式下查看详细日志**: + - 启用DEBUG模式查看认证header添加日志 + - 使用测试方法验证功能正确性 \ No newline at end of file diff --git a/yana/APIs/APIEndpoints.swift b/yana/APIs/APIEndpoints.swift index dc32c89..60b1611 100644 --- a/yana/APIs/APIEndpoints.swift +++ b/yana/APIs/APIEndpoints.swift @@ -94,13 +94,28 @@ struct APIConfiguration { "User-Agent": "YuMi/20.20.61 (iPhone; iOS 16.4; Scale/2.00)" ] - // 添加用户认证相关 headers(如果存在) - if let userId = UserInfoManager.getCurrentUserId() { - headers["pub_uid"] = userId - } + // 检查用户认证状态并添加相关 headers + let authStatus = UserInfoManager.checkAuthenticationStatus() - if let userTicket = UserInfoManager.getCurrentUserTicket() { - headers["pub_ticket"] = userTicket + if authStatus.canAutoLogin { + // 添加用户认证相关 headers(仅在 AccountModel 有效时) + if let userId = UserInfoManager.getCurrentUserId() { + headers["pub_uid"] = userId + #if DEBUG + debugInfo("🔐 添加认证 header: pub_uid = \(userId)") + #endif + } + + if let userTicket = UserInfoManager.getCurrentUserTicket() { + headers["pub_ticket"] = userTicket + #if DEBUG + debugInfo("🔐 添加认证 header: pub_ticket = \(userTicket.prefix(20))...") + #endif + } + } else { + #if DEBUG + debugInfo("🔐 跳过认证 header 添加 - 认证状态: \(authStatus.description)") + #endif } return headers diff --git a/yana/APIs/APILogger.swift b/yana/APIs/APILogger.swift index e2defa3..0921e76 100644 --- a/yana/APIs/APILogger.swift +++ b/yana/APIs/APILogger.swift @@ -22,7 +22,11 @@ class APILogger { // MARK: - Request Logging static func logRequest(_ request: T, url: URL, body: Data?, finalHeaders: [String: String]? = nil) { + #if DEBUG guard logLevel != .none else { return } + #else + return + #endif let timestamp = dateFormatter.string(from: Date()) @@ -107,7 +111,11 @@ class APILogger { // MARK: - Response Logging static func logResponse(data: Data, response: HTTPURLResponse, duration: TimeInterval) { + #if DEBUG guard logLevel != .none else { return } + #else + return + #endif let timestamp = dateFormatter.string(from: Date()) let statusEmoji = response.statusCode < 400 ? "✅" : "❌" @@ -143,7 +151,11 @@ class APILogger { // MARK: - Error Logging static func logError(_ error: Error, url: URL?, duration: TimeInterval) { + #if DEBUG guard logLevel != .none else { return } + #else + return + #endif let timestamp = dateFormatter.string(from: Date()) @@ -186,7 +198,11 @@ class APILogger { // MARK: - Decoded Response Logging static func logDecodedResponse(_ response: T, type: T.Type) { + #if DEBUG guard logLevel == .detailed else { return } + #else + return + #endif let timestamp = dateFormatter.string(from: Date()) print("🎯 [Decoded Response] [\(timestamp)] Type: \(type)") @@ -203,7 +219,11 @@ class APILogger { // MARK: - Performance Logging static func logPerformanceWarning(duration: TimeInterval, threshold: TimeInterval = 5.0) { + #if DEBUG guard logLevel != .none && duration > threshold else { return } + #else + return + #endif let timestamp = dateFormatter.string(from: Date()) print("\n⚠️ [Performance Warning] [\(timestamp)] ============") diff --git a/yana/APIs/APIModels.swift b/yana/APIs/APIModels.swift index f14de1d..ecfbaf9 100644 --- a/yana/APIs/APIModels.swift +++ b/yana/APIs/APIModels.swift @@ -260,21 +260,27 @@ struct UserInfoManager { return getAccountModel()?.accessToken } - // MARK: - Ticket Management (内存存储) + // MARK: - Ticket Management (优先从 AccountModel 获取) private static var currentTicket: String? static func getCurrentUserTicket() -> String? { + // 优先从 AccountModel 获取 ticket(确保一致性) + if let accountTicket = getAccountModel()?.ticket, !accountTicket.isEmpty { + return accountTicket + } + + // 备选:从内存获取(用于兼容性) return currentTicket } static func saveTicket(_ ticket: String) { currentTicket = ticket - print("💾 保存 Ticket 到内存") + debugInfo("💾 保存 Ticket 到内存") } static func clearTicket() { currentTicket = nil - print("🗑️ 清除 Ticket") + debugInfo("🗑️ 清除 Ticket") } // MARK: - User Info Management @@ -283,9 +289,9 @@ struct UserInfoManager { do { try keychain.store(userInfo, forKey: StorageKeys.userInfo) userInfoCache = userInfo - print("💾 保存用户信息成功") + debugInfo("💾 保存用户信息成功") } catch { - print("❌ 保存用户信息失败: \(error)") + debugError("❌ 保存用户信息失败: \(error)") } } } @@ -303,7 +309,7 @@ struct UserInfoManager { userInfoCache = userInfo return userInfo } catch { - print("❌ 读取用户信息失败: \(error)") + debugError("❌ 读取用户信息失败: \(error)") return nil } } @@ -337,7 +343,7 @@ struct UserInfoManager { saveUserInfo(userInfo) } - print("✅ 完整认证信息保存成功") + debugInfo("✅ 完整认证信息保存成功") } /// 检查是否有有效的认证信息 @@ -351,7 +357,7 @@ struct UserInfoManager { clearUserInfo() clearTicket() - print("🗑️ 清除所有认证信息") + debugInfo("🗑️ 清除所有认证信息") } /// 尝试恢复 Ticket(用于应用重启后) @@ -361,7 +367,7 @@ struct UserInfoManager { return false } - print("🔄 尝试使用 Access Token 恢复 Ticket...") + debugInfo("🔄 尝试使用 Access Token 恢复 Ticket...") // 这里需要注入 APIService 依赖,暂时返回 false // 实际实现中应该调用 TicketHelper.createTicketRequest @@ -382,9 +388,9 @@ struct UserInfoManager { saveTicket(ticket) } - print("💾 AccountModel 保存成功") + debugInfo("💾 AccountModel 保存成功") } catch { - print("❌ AccountModel 保存失败: \(error)") + debugError("❌ AccountModel 保存失败: \(error)") } } } @@ -404,7 +410,7 @@ struct UserInfoManager { accountModelCache = accountModel return accountModel } catch { - print("❌ 读取 AccountModel 失败: \(error)") + debugError("❌ 读取 AccountModel 失败: \(error)") return nil } } @@ -414,7 +420,7 @@ struct UserInfoManager { /// - Parameter ticket: 新的票据 static func updateAccountModelTicket(_ ticket: String) { guard var accountModel = getAccountModel() else { - print("❌ 无法更新 ticket:AccountModel 不存在") + debugError("❌ 无法更新 ticket:AccountModel 不存在") return } @@ -449,9 +455,9 @@ struct UserInfoManager { do { try keychain.delete(forKey: StorageKeys.accountModel) accountModelCache = nil - print("🗑️ AccountModel 已清除") + debugInfo("🗑️ AccountModel 已清除") } catch { - print("❌ 清除 AccountModel 失败: \(error)") + debugError("❌ 清除 AccountModel 失败: \(error)") } } } @@ -462,9 +468,9 @@ struct UserInfoManager { do { try keychain.delete(forKey: StorageKeys.userInfo) userInfoCache = nil - print("🗑️ UserInfo 已清除") + debugInfo("🗑️ UserInfo 已清除") } catch { - print("❌ 清除 UserInfo 失败: \(error)") + debugError("❌ 清除 UserInfo 失败: \(error)") } } } @@ -474,7 +480,7 @@ struct UserInfoManager { cacheQueue.async(flags: .barrier) { accountModelCache = nil userInfoCache = nil - print("🗑️ 清除所有内存缓存") + debugInfo("🗑️ 清除所有内存缓存") } } @@ -485,7 +491,7 @@ struct UserInfoManager { _ = getAccountModel() // 预加载 UserInfo _ = getUserInfo() - print("🚀 缓存预加载完成") + debugInfo("🚀 缓存预加载完成") } } @@ -496,29 +502,29 @@ struct UserInfoManager { static func checkAuthenticationStatus() -> AuthenticationStatus { return cacheQueue.sync { guard let accountModel = getAccountModel() else { - print("🔍 认证检查:未找到 AccountModel") + debugInfo("🔍 认证检查:未找到 AccountModel") return .notFound } // 检查 uid 是否有效 guard let uid = accountModel.uid, !uid.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { - print("🔍 认证检查:uid 无效") + debugInfo("🔍 认证检查:uid 无效") return .invalid } // 检查 ticket 是否有效 guard let ticket = accountModel.ticket, !ticket.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { - print("🔍 认证检查:ticket 无效") + debugInfo("🔍 认证检查:ticket 无效") return .invalid } // 可选:检查 access token 是否有效 guard let accessToken = accountModel.accessToken, !accessToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { - print("🔍 认证检查:access token 无效") + debugInfo("🔍 认证检查:access token 无效") return .invalid } - print("🔍 认证检查:认证有效 - uid: \(uid), ticket: \(ticket.prefix(10))...") + debugInfo("🔍 认证检查:认证有效 - uid: \(uid), ticket: \(ticket.prefix(10))...") return .valid } } @@ -545,6 +551,49 @@ struct UserInfoManager { return self == .valid } } + + // MARK: - Testing and Debugging + + /// 测试认证 header 功能(仅用于调试) + /// 模拟用户登录状态并验证 header 添加逻辑 + static func testAuthenticationHeaders() { + #if DEBUG + debugInfo("\n🧪 开始测试认证 header 功能") + + // 测试1:未登录状态 + debugInfo("📝 测试1:未登录状态") + clearAllAuthenticationData() + let headers1 = APIConfiguration.defaultHeaders + let hasAuthHeaders1 = headers1.keys.contains("pub_uid") || headers1.keys.contains("pub_ticket") + debugInfo(" 认证 headers 存在: \(hasAuthHeaders1) (应该为 false)") + + // 测试2:模拟登录状态 + debugInfo("📝 测试2:模拟登录状态") + let testAccount = AccountModel( + uid: "12345", + jti: "test-jti", + tokenType: "bearer", + refreshToken: nil, + netEaseToken: nil, + accessToken: "test-access-token", + expiresIn: 3600, + scope: "read write", + ticket: "test-ticket-12345678901234567890" + ) + saveAccountModel(testAccount) + + let headers2 = APIConfiguration.defaultHeaders + let hasUid = headers2["pub_uid"] == "12345" + let hasTicket = headers2["pub_ticket"] == "test-ticket-12345678901234567890" + debugInfo(" pub_uid 正确: \(hasUid) (应该为 true)") + debugInfo(" pub_ticket 正确: \(hasTicket) (应该为 true)") + + // 测试3:清理测试数据 + debugInfo("📝 测试3:清理测试数据") + clearAllAuthenticationData() + debugInfo("✅ 认证 header 测试完成\n") + #endif + } } // MARK: - API Request Protocol diff --git a/yana/APIs/APIService.swift b/yana/APIs/APIService.swift index 9e14d5b..3f92f6a 100644 --- a/yana/APIs/APIService.swift +++ b/yana/APIs/APIService.swift @@ -93,6 +93,7 @@ struct LiveAPIService: APIServiceProtocol { var urlRequest = URLRequest(url: url) urlRequest.httpMethod = request.method.rawValue urlRequest.timeoutInterval = request.timeout + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") // 设置请求头 var headers = APIConfiguration.defaultHeaders @@ -113,18 +114,30 @@ struct LiveAPIService: APIServiceProtocol { var requestBody: Data? = nil if request.method != .GET, let bodyParams = request.bodyParameters { do { - // 如果需要包含基础参数,则合并 var finalBody = bodyParams + + // 如果需要包含基础参数,则先合并所有参数,再统一生成签名 if request.includeBaseParameters { + // 第一步:创建基础参数实例(不包含签名) var baseParams = BaseRequest() - // 生成符合 API rule 的签名 + + // 第二步:基于所有参数(bodyParams + 基础参数)统一生成签名 baseParams.generateSignature(with: bodyParams) + + // 第三步:将包含正确签名的基础参数合并到最终请求体 let baseDict = try baseParams.toDictionary() - finalBody.merge(baseDict) { existing, _ in existing } + finalBody.merge(baseDict) { _, new in new } // 基础参数(包括签名)优先 + + debugInfo("🔐 签名生成完成 - 基于所有参数统一生成: \(baseParams.pubSign)") } requestBody = try JSONSerialization.data(withJSONObject: finalBody, options: []) urlRequest.httpBody = requestBody +// urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + if let httpBody = urlRequest.httpBody, + let bodyString = String(data: httpBody, encoding: .utf8) { + debugInfo("HTTP Body: \(bodyString)") + } } catch { let encodingError = APIError.decodingError("请求体编码失败: \(error.localizedDescription)") APILoadingManager.shared.setError(loadingId, errorMessage: encodingError.localizedDescription) @@ -133,7 +146,7 @@ struct LiveAPIService: APIServiceProtocol { } // 记录请求日志,传递完整的 headers 信息 -// APILogger.logRequest(request, url: url, body: requestBody, finalHeaders: headers) + APILogger.logRequest(request, url: url, body: requestBody, finalHeaders: headers) do { // 发起请求 @@ -226,16 +239,22 @@ struct LiveAPIService: APIServiceProtocol { // 对于 GET 请求,将基础参数添加到查询参数中 if request.method == .GET && request.includeBaseParameters { do { + // 第一步:创建基础参数实例(不包含签名) var baseParams = BaseRequest() - // 为 GET 请求生成签名(合并查询参数) + + // 第二步:基于所有参数(queryParams + 基础参数)统一生成签名 let queryParamsDict = request.queryParameters ?? [:] baseParams.generateSignature(with: queryParamsDict) + + // 第三步:将包含正确签名的基础参数添加到查询参数 let baseDict = try baseParams.toDictionary() for (key, value) in baseDict { queryItems.append(URLQueryItem(name: key, value: "\(value)")) } + + debugInfo("🔐 GET请求签名生成完成 - 基于所有参数统一生成: \(baseParams.pubSign)") } catch { - print("警告:无法添加基础参数到查询字符串") + debugWarn("警告:无法添加基础参数到查询字符串") } } diff --git a/yana/APIs/LoginModels.swift b/yana/APIs/LoginModels.swift index aac54b8..af2b7dd 100644 --- a/yana/APIs/LoginModels.swift +++ b/yana/APIs/LoginModels.swift @@ -105,7 +105,7 @@ struct IDLoginAPIRequest: APIRequestProtocol { // "version": version, // "client_id": clientId, // "grant_type": grantType -// ] +// ]; } } @@ -192,15 +192,15 @@ struct LoginHelper { guard let encryptedID = DESEncrypt.encryptUseDES(userID, key: encryptionKey), let encryptedPassword = DESEncrypt.encryptUseDES(password, key: encryptionKey) else { - print("❌ DES加密失败") + debugError("❌ DES加密失败") return nil } - print("🔐 DES加密成功") - print(" 原始ID: \(userID)") - print(" 加密后ID: \(encryptedID)") - print(" 原始密码: \(password)") - print(" 加密后密码: \(encryptedPassword)") + debugInfo("🔐 DES加密成功") + debugInfo(" 原始ID: \(userID)") + debugInfo(" 加密后ID: \(encryptedID)") + debugInfo(" 原始密码: \(password)") + debugInfo(" 加密后密码: \(encryptedPassword)") return IDLoginAPIRequest( phone: userID, @@ -292,13 +292,13 @@ struct TicketHelper { /// - accessToken: OAuth 访问令牌 /// - uid: 用户唯一标识 static func debugTicketRequest(accessToken: String, uid: Int?) { - print("🎫 Ticket 请求调试信息") - print(" AccessToken: \(accessToken)") - print(" UID: \(uid?.description ?? "nil")") - print(" Endpoint: /oauth/ticket") - print(" Method: POST") - print(" Headers: pub_uid = \(uid?.description ?? "nil")") - print(" Parameters: access_token=\(accessToken), issue_type=multi") + debugInfo("🎫 Ticket 请求调试信息") + debugInfo(" AccessToken: \(accessToken)") + debugInfo(" UID: \(uid?.description ?? "nil")") + debugInfo(" Endpoint: /oauth/ticket") + debugInfo(" Method: POST") + debugInfo(" Headers: pub_uid = \(uid?.description ?? "nil")") + debugInfo(" Parameters: access_token=\(accessToken), issue_type=multi") } } @@ -389,13 +389,13 @@ extension LoginHelper { let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey) else { - print("❌ 邮箱DES加密失败") + debugError("❌ 邮箱DES加密失败") return nil } - print("🔐 邮箱DES加密成功") - print(" 原始邮箱: \(email)") - print(" 加密邮箱: \(encryptedEmail)") + debugInfo("🔐 邮箱DES加密成功") + debugInfo(" 原始邮箱: \(email)") + debugInfo(" 加密邮箱: \(encryptedEmail)") return EmailGetCodeRequest(emailAddress: email, type: 1) } @@ -409,14 +409,14 @@ extension LoginHelper { let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey) else { - print("❌ 邮箱DES加密失败") + debugError("❌ 邮箱DES加密失败") return nil } - print("🔐 邮箱验证码登录DES加密成功") - print(" 原始邮箱: \(email)") - print(" 加密邮箱: \(encryptedEmail)") - print(" 验证码: \(code)") + debugInfo("🔐 邮箱验证码登录DES加密成功") + debugInfo(" 原始邮箱: \(email)") + debugInfo(" 加密邮箱: \(encryptedEmail)") + debugInfo(" 验证码: \(code)") return EmailLoginRequest(email: encryptedEmail, code: code) } diff --git a/yana/Configs/ClientConfig.swift b/yana/Configs/ClientConfig.swift index e7d77ce..e79f814 100644 --- a/yana/Configs/ClientConfig.swift +++ b/yana/Configs/ClientConfig.swift @@ -7,12 +7,12 @@ final class ClientConfig { private init() {} func initializeClient() { - print("✅ 开始初始化客户端 - URL: \(AppConfig.baseURL)/client/init") + debugInfo("✅ 开始初始化客户端 - URL: \(AppConfig.baseURL)/client/init") callClientInitAPI() // 调用新方法 } func callClientInitAPI() { - print("🆕 使用GET方法调用初始化接口") + debugInfo("🆕 使用GET方法调用初始化接口") // let queryParams = [ // "debug": "1", diff --git a/yana/Features/IDLoginFeature.swift b/yana/Features/IDLoginFeature.swift index 1d617f6..2af51db 100644 --- a/yana/Features/IDLoginFeature.swift +++ b/yana/Features/IDLoginFeature.swift @@ -110,9 +110,9 @@ struct IDLoginFeature { UserInfoManager.saveUserInfo(userInfo) } - print("✅ ID 登录 OAuth 认证成功") - print("🔑 Access Token: \(accountModel.accessToken ?? "nil")") - print("🆔 用户 UID: \(accountModel.uid ?? "nil")") + debugInfo("✅ ID 登录 OAuth 认证成功") + debugInfo("🔑 Access Token: \(accountModel.accessToken ?? "nil")") + debugInfo("🆔 用户 UID: \(accountModel.uid ?? "nil")") // 自动获取 ticket return .send(.requestTicket(accessToken: accountModel.accessToken!)) @@ -145,7 +145,7 @@ struct IDLoginFeature { let response = try await apiService.request(ticketRequest) await send(.ticketResponse(.success(response))) } catch { - print("❌ ID登录 Ticket 获取失败: \(error)") + debugError("❌ ID登录 Ticket 获取失败: \(error)") await send(.ticketResponse(.failure(APIError.networkError(error.localizedDescription)))) } } @@ -156,8 +156,8 @@ struct IDLoginFeature { state.ticketError = nil state.loginStep = .completed - print("✅ ID 登录完整流程成功") - print("🎫 Ticket 获取成功: \(response.ticket ?? "nil")") + debugInfo("✅ ID 登录完整流程成功") + debugInfo("🎫 Ticket 获取成功: \(response.ticket ?? "nil")") // 更新 AccountModel 中的 ticket 并保存 if let ticket = response.ticket { @@ -171,7 +171,7 @@ struct IDLoginFeature { // 发送 Ticket 获取成功通知,触发导航到主页面 NotificationCenter.default.post(name: .ticketSuccess, object: nil) } else { - print("❌ AccountModel 不存在,无法保存 ticket") + debugError("❌ AccountModel 不存在,无法保存 ticket") state.ticketError = "内部错误:账户信息丢失" state.loginStep = .failed } @@ -190,7 +190,7 @@ struct IDLoginFeature { state.isTicketLoading = false state.ticketError = error.localizedDescription state.loginStep = .failed - print("❌ ID 登录 Ticket 获取失败: \(error.localizedDescription)") + debugError("❌ ID 登录 Ticket 获取失败: \(error.localizedDescription)") return .none case .clearTicketError: diff --git a/yana/Features/LoginFeature.swift b/yana/Features/LoginFeature.swift index 3e530f3..23d74c4 100644 --- a/yana/Features/LoginFeature.swift +++ b/yana/Features/LoginFeature.swift @@ -109,9 +109,9 @@ struct LoginFeature { let accountModel = AccountModel.from(loginData: loginData) { state.accountModel = accountModel - print("✅ OAuth 认证成功") - print("🔑 Access Token: \(accountModel.accessToken ?? "nil")") - print("🆔 用户 UID: \(accountModel.uid ?? "nil")") + debugInfo("✅ OAuth 认证成功") + debugInfo("🔑 Access Token: \(accountModel.accessToken ?? "nil")") + debugInfo("🆔 用户 UID: \(accountModel.uid ?? "nil")") // 自动获取 ticket return .send(.requestTicket(accessToken: accountModel.accessToken!)) @@ -144,7 +144,7 @@ struct LoginFeature { let response = try await apiService.request(ticketRequest) await send(.ticketResponse(.success(response))) } catch { - print("❌ Ticket 获取失败: \(error)") + debugError("❌ Ticket 获取失败: \(error)") await send(.ticketResponse(.failure(APIError.networkError(error.localizedDescription)))) } } @@ -155,8 +155,8 @@ struct LoginFeature { state.ticketError = nil state.loginStep = .completed - print("✅ 完整登录流程成功") - print("🎫 Ticket 获取成功: \(response.ticket ?? "nil")") + debugInfo("✅ 完整登录流程成功") + debugInfo("🎫 Ticket 获取成功: \(response.ticket ?? "nil")") // 更新 AccountModel 中的 ticket 并保存 if let ticket = response.ticket { @@ -170,7 +170,7 @@ struct LoginFeature { // 发送 Ticket 获取成功通知,触发导航到主页面 NotificationCenter.default.post(name: .ticketSuccess, object: nil) } else { - print("❌ AccountModel 不存在,无法保存 ticket") + debugError("❌ AccountModel 不存在,无法保存 ticket") state.ticketError = "内部错误:账户信息丢失" state.loginStep = .failed } @@ -189,7 +189,7 @@ struct LoginFeature { state.isTicketLoading = false state.ticketError = error.localizedDescription state.loginStep = .failed - print("❌ Ticket 获取失败: \(error.localizedDescription)") + debugError("❌ Ticket 获取失败: \(error.localizedDescription)") return .none case .clearTicketError: diff --git a/yana/Features/RecoverPasswordFeature.swift b/yana/Features/RecoverPasswordFeature.swift index 550a9ce..e2d7782 100644 --- a/yana/Features/RecoverPasswordFeature.swift +++ b/yana/Features/RecoverPasswordFeature.swift @@ -238,13 +238,13 @@ struct RecoverPasswordHelper { let encryptionKey = "1ea53d260ecf11e7b56e00163e046a26" guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey) else { - print("❌ 邮箱DES加密失败") + debugError("❌ 邮箱DES加密失败") return nil } - print("🔐 密码恢复邮箱DES加密成功") - print(" 原始邮箱: \(email)") - print(" 加密邮箱: \(encryptedEmail)") + debugInfo("🔐 密码恢复邮箱DES加密成功") + debugInfo(" 原始邮箱: \(email)") + debugInfo(" 加密邮箱: \(encryptedEmail)") // 使用type=3表示密码重置验证码 return EmailGetCodeRequest(emailAddress: email, type: 3) @@ -261,16 +261,16 @@ struct RecoverPasswordHelper { guard let encryptedEmail = DESEncrypt.encryptUseDES(email, key: encryptionKey), let encryptedPassword = DESEncrypt.encryptUseDES(newPassword, key: encryptionKey) else { - print("❌ 密码重置DES加密失败") + debugError("❌ 密码重置DES加密失败") return nil } - print("🔐 密码重置DES加密成功") - print(" 原始邮箱: \(email)") - print(" 加密邮箱: \(encryptedEmail)") - print(" 验证码: \(code)") - print(" 原始新密码: \(newPassword)") - print(" 加密新密码: \(encryptedPassword)") + debugInfo("🔐 密码重置DES加密成功") + debugInfo(" 原始邮箱: \(email)") + debugInfo(" 加密邮箱: \(encryptedEmail)") + debugInfo(" 验证码: \(code)") + debugInfo(" 原始新密码: \(newPassword)") + debugInfo(" 加密新密码: \(encryptedPassword)") return ResetPasswordRequest( email: email, diff --git a/yana/Features/SplashFeature.swift b/yana/Features/SplashFeature.swift index 053961a..9c3c2d8 100644 --- a/yana/Features/SplashFeature.swift +++ b/yana/Features/SplashFeature.swift @@ -55,10 +55,10 @@ struct SplashFeature { // 根据认证状态发送相应的导航通知 if status.canAutoLogin { - print("🎉 自动登录成功,进入主页") + debugInfo("🎉 自动登录成功,进入主页") NotificationCenter.default.post(name: .autoLoginSuccess, object: nil) } else { - print("🔑 需要手动登录") + debugInfo("🔑 需要手动登录") NotificationCenter.default.post(name: .autoLoginFailed, object: nil) } diff --git a/yana/Managers/LogManager.swift b/yana/Managers/LogManager.swift index 228f1cf..cd0ab9d 100644 --- a/yana/Managers/LogManager.swift +++ b/yana/Managers/LogManager.swift @@ -18,18 +18,33 @@ public class LogManager { /// - Parameters: /// - level: 日志等级 /// - message: 日志内容 - /// - onlyRelease: 是否仅在 Release 环境输出(默认 false,Debug 全部输出) + /// - onlyRelease: 是否仅在 Release 环境输出(已修复逻辑) public func log(_ level: LogLevel, _ message: @autoclosure () -> String, onlyRelease: Bool = false) { #if DEBUG - if onlyRelease { return } - print("[\(level)] \(message())") + // DEBUG 环境:如果 onlyRelease 为 true,则不输出;否则正常输出 + if !onlyRelease { + print("[\(level)] \(message())") + } #else + // RELEASE 环境:如果 onlyRelease 为 true,则输出;否则不输出 + if onlyRelease { + print("[\(level)] \(message())") + } + #endif + } + + /// 仅在 DEBUG 环境输出的日志(推荐使用) + /// - Parameters: + /// - level: 日志等级 + /// - message: 日志内容 + public func debugLog(_ level: LogLevel, _ message: @autoclosure () -> String) { + #if DEBUG print("[\(level)] \(message())") #endif } } -// MARK: - 快捷方法 +// MARK: - 原有快捷方法(保持向后兼容) public func logVerbose(_ message: @autoclosure () -> String, onlyRelease: Bool = false) { LogManager.shared.log(.verbose, message(), onlyRelease: onlyRelease) } @@ -48,4 +63,25 @@ public func logWarn(_ message: @autoclosure () -> String, onlyRelease: Bool = fa public func logError(_ message: @autoclosure () -> String, onlyRelease: Bool = false) { LogManager.shared.log(.error, message(), onlyRelease: onlyRelease) +} + +// MARK: - 新的DEBUG专用快捷方法(推荐使用) +public func debugVerbose(_ message: @autoclosure () -> String) { + LogManager.shared.debugLog(.verbose, message()) +} + +public func debugLog(_ message: @autoclosure () -> String) { + LogManager.shared.debugLog(.debug, message()) +} + +public func debugInfo(_ message: @autoclosure () -> String) { + LogManager.shared.debugLog(.info, message()) +} + +public func debugWarn(_ message: @autoclosure () -> String) { + LogManager.shared.debugLog(.warn, message()) +} + +public func debugError(_ message: @autoclosure () -> String) { + LogManager.shared.debugLog(.error, message()) } \ No newline at end of file diff --git a/yana/Utils/APILoading/APILoadingEffectView.swift b/yana/Utils/APILoading/APILoadingEffectView.swift index 56366e9..017a7e4 100644 --- a/yana/Utils/APILoading/APILoadingEffectView.swift +++ b/yana/Utils/APILoading/APILoadingEffectView.swift @@ -18,24 +18,24 @@ struct APILoadingEffectView: View { if let firstItem = getFirstDisplayItem() { SingleLoadingView(item: firstItem) .onAppear { - print("🔍 Loading item appeared: \(firstItem.id)") + debugInfo("🔍 Loading item appeared: \(firstItem.id)") } .onDisappear { - print("🔍 Loading item disappeared: \(firstItem.id)") + debugInfo("🔍 Loading item disappeared: \(firstItem.id)") } } } .allowsHitTesting(false) // 不阻挡用户点击 .ignoresSafeArea(.all) // 覆盖整个屏幕 .onReceive(loadingManager.$loadingItems) { items in - print("🔍 Loading items updated: \(items.count) items") + debugInfo("🔍 Loading items updated: \(items.count) items") } } /// 安全地获取第一个需要显示的项目 private func getFirstDisplayItem() -> APILoadingItem? { guard Thread.isMainThread else { - print("⚠️ getFirstDisplayItem called from background thread") + debugWarn("⚠️ getFirstDisplayItem called from background thread") return nil } @@ -151,7 +151,7 @@ struct APILoadingEffectView_Previews: PreviewProvider { .font(.title) Button("测试按钮") { - print("按钮被点击了!") + debugInfo("按钮被点击了!") } .padding() .background(Color.blue) diff --git a/yana/Utils/Extensions/String+HashTest.swift b/yana/Utils/Extensions/String+HashTest.swift index b1721cc..68f67ce 100644 --- a/yana/Utils/Extensions/String+HashTest.swift +++ b/yana/Utils/Extensions/String+HashTest.swift @@ -6,7 +6,7 @@ struct StringHashTest { /// 测试哈希方法 static func runTests() { - print("🧪 开始测试字符串哈希方法...") + debugInfo("🧪 开始测试字符串哈希方法...") let testStrings = [ "hello world", @@ -16,27 +16,27 @@ struct StringHashTest { ] for testString in testStrings { - print("\n📝 测试字符串: \"\(testString)\"") + debugInfo("\n📝 测试字符串: \"\(testString)\"") // 测试 MD5 let md5Result = testString.md5() - print(" MD5: \(md5Result)") + debugInfo(" MD5: \(md5Result)") // 测试 SHA256 (iOS 13+) if #available(iOS 13.0, *) { let sha256Result = testString.sha256() - print(" SHA256: \(sha256Result)") + debugInfo(" SHA256: \(sha256Result)") } else { - print(" SHA256: 不支持 (需要 iOS 13+)") + debugInfo(" SHA256: 不支持 (需要 iOS 13+)") } } - print("\n✅ 哈希方法测试完成") + debugInfo("\n✅ 哈希方法测试完成") } /// 验证已知的哈希值 static func verifyKnownHashes() { - print("\n🔍 验证已知哈希值...") + debugInfo("\n🔍 验证已知哈希值...") // 验证 "hello world" 的 MD5 应该是 "5d41402abc4b2a76b9719d911017c592" let testString = "hello world" @@ -44,11 +44,11 @@ struct StringHashTest { let actualMD5 = testString.md5() if actualMD5 == expectedMD5 { - print("✅ MD5 验证通过: \(actualMD5)") + debugInfo("✅ MD5 验证通过: \(actualMD5)") } else { - print("❌ MD5 验证失败:") - print(" 期望: \(expectedMD5)") - print(" 实际: \(actualMD5)") + debugError("❌ MD5 验证失败:") + debugError(" 期望: \(expectedMD5)") + debugError(" 实际: \(actualMD5)") } // 验证 SHA256 @@ -57,11 +57,11 @@ struct StringHashTest { let actualSHA256 = testString.sha256() if actualSHA256 == expectedSHA256 { - print("✅ SHA256 验证通过: \(actualSHA256)") + debugInfo("✅ SHA256 验证通过: \(actualSHA256)") } else { - print("❌ SHA256 验证失败:") - print(" 期望: \(expectedSHA256)") - print(" 实际: \(actualSHA256)") + debugError("❌ SHA256 验证失败:") + debugError(" 期望: \(expectedSHA256)") + debugError(" 实际: \(actualSHA256)") } } } @@ -75,9 +75,9 @@ struct StringHashTest { StringHashTest.verifyKnownHashes() // 或者在开发时快速测试 - print("Test MD5:", "hello".md5()) + debugInfo("Test MD5:", "hello".md5()) if #available(iOS 13.0, *) { - print("Test SHA256:", "hello".sha256()) + debugInfo("Test SHA256:", "hello".sha256()) } */ \ No newline at end of file diff --git a/yana/Utils/FontManager.swift b/yana/Utils/FontManager.swift index c290ea7..357032d 100644 --- a/yana/Utils/FontManager.swift +++ b/yana/Utils/FontManager.swift @@ -67,11 +67,11 @@ struct FontManager { /// 打印所有可用字体(调试用) static func printAllAvailableFonts() { - print("=== 所有可用字体 ===") + debugInfo("=== 所有可用字体 ===") for font in getAllAvailableFonts() { - print(font) + debugInfo(font) } - print("==================") + debugInfo("==================") } } diff --git a/yana/Utils/LocalizationManager.swift b/yana/Utils/LocalizationManager.swift index e3736bd..464b618 100644 --- a/yana/Utils/LocalizationManager.swift +++ b/yana/Utils/LocalizationManager.swift @@ -42,9 +42,9 @@ class LocalizationManager: ObservableObject { didSet { do { try KeychainManager.shared.storeString(currentLanguage.rawValue, forKey: "AppLanguage") - } catch { - print("❌ 保存语言设置失败: \(error)") - } + } catch { + debugError("❌ 保存语言设置失败: \(error)") + } // 通知视图更新 objectWillChange.send() } @@ -56,7 +56,7 @@ class LocalizationManager: ObservableObject { do { savedLanguage = try KeychainManager.shared.retrieveString(forKey: "AppLanguage") } catch { - print("❌ 读取语言设置失败: \(error)") + debugError("❌ 读取语言设置失败: \(error)") savedLanguage = nil } diff --git a/yana/Utils/Security/DESEncryptOCTest.swift b/yana/Utils/Security/DESEncryptOCTest.swift index 8d27e99..2f9341c 100644 --- a/yana/Utils/Security/DESEncryptOCTest.swift +++ b/yana/Utils/Security/DESEncryptOCTest.swift @@ -5,8 +5,8 @@ struct DESEncryptOCTest { /// 测试 OC 版本的 DES 加密功能 static func testOCDESEncryption() { - print("🧪 开始测试 OC 版本的 DES 加密...") - print(String(repeating: "=", count: 50)) + debugInfo("🧪 开始测试 OC 版本的 DES 加密...") + debugInfo(String(repeating: "=", count: 50)) let key = "1ea53d260ecf11e7b56e00163e046a26" let testCases = [ @@ -19,25 +19,25 @@ struct DESEncryptOCTest { for testCase in testCases { if let encrypted = DESEncrypt.encryptUseDES(testCase, key: key) { - print("✅ 加密成功:") - print(" 原文: \"\(testCase)\"") - print(" 密文: \(encrypted)") + debugInfo("✅ 加密成功:") + debugInfo(" 原文: \"\(testCase)\"") + debugInfo(" 密文: \(encrypted)") // 测试解密 if let decrypted = DESEncrypt.decryptUseDES(encrypted, key: key) { let isMatch = decrypted == testCase - print(" 解密: \"\(decrypted)\" \(isMatch ? "✅" : "❌")") + debugInfo(" 解密: \"\(decrypted)\" \(isMatch ? "✅" : "❌")") } else { - print(" 解密: 失败 ❌") + debugError(" 解密: 失败 ❌") } } else { - print("❌ 加密失败: \"\(testCase)\"") + debugError("❌ 加密失败: \"\(testCase)\"") } - print() + debugInfo("") } - print(String(repeating: "=", count: 50)) - print("🏁 OC版本DES加密测试完成") + debugInfo(String(repeating: "=", count: 50)) + debugInfo("🏁 OC版本DES加密测试完成") } } diff --git a/yana/Utils/Security/DataMigrationManager.swift b/yana/Utils/Security/DataMigrationManager.swift index 3b4f390..2731633 100644 --- a/yana/Utils/Security/DataMigrationManager.swift +++ b/yana/Utils/Security/DataMigrationManager.swift @@ -54,23 +54,23 @@ final class DataMigrationManager { /// 执行数据迁移 /// - Returns: 迁移结果 func performMigration() -> MigrationResult { - print("🔄 开始检查数据迁移...") + debugInfo("🔄 开始检查数据迁移...") // 检查是否已经迁移过 if isMigrationCompleted() { - print("✅ 数据已经迁移过,跳过迁移") + debugInfo("✅ 数据已经迁移过,跳过迁移") return .alreadyMigrated } // 检查是否有需要迁移的数据 let legacyData = collectLegacyData() if legacyData.isEmpty { - print("ℹ️ 没有发现需要迁移的数据") + debugInfo("ℹ️ 没有发现需要迁移的数据") markMigrationCompleted() return .noDataToMigrate } - print("📦 发现需要迁移的数据: \(legacyData.keys.joined(separator: ", "))") + debugInfo("📦 发现需要迁移的数据: \(legacyData.keys.joined(separator: ", "))") do { // 执行迁移 @@ -85,11 +85,11 @@ final class DataMigrationManager { // 标记迁移完成 markMigrationCompleted() - print("✅ 数据迁移完成") + debugInfo("✅ 数据迁移完成") return .completed } catch { - print("❌ 数据迁移失败: \(error)") + debugError("❌ 数据迁移失败: \(error)") return .failed(error) } } @@ -157,9 +157,9 @@ final class DataMigrationManager { do { let accountModel = try JSONDecoder().decode(AccountModel.self, from: accountModelData) try keychain.store(accountModel, forKey: "account_model") - print("✅ AccountModel 迁移成功") + debugInfo("✅ AccountModel 迁移成功") } catch { - print("❌ AccountModel 迁移失败: \(error)") + debugError("❌ AccountModel 迁移失败: \(error)") // 如果 AccountModel 迁移失败,尝试从独立字段重建 try migrateAccountModelFromIndependentFields(legacyData) } @@ -173,9 +173,9 @@ final class DataMigrationManager { do { let userInfo = try JSONDecoder().decode(UserInfo.self, from: userInfoData) try keychain.store(userInfo, forKey: "user_info") - print("✅ UserInfo 迁移成功") + debugInfo("✅ UserInfo 迁移成功") } catch { - print("❌ UserInfo 迁移失败: \(error)") + debugError("❌ UserInfo 迁移失败: \(error)") throw error } } @@ -183,7 +183,7 @@ final class DataMigrationManager { // 迁移语言设置 if let appLanguage = legacyData[LegacyStorageKeys.appLanguage] as? String { try keychain.storeString(appLanguage, forKey: "AppLanguage") - print("✅ 语言设置迁移成功") + debugInfo("✅ 语言设置迁移成功") } } @@ -191,7 +191,7 @@ final class DataMigrationManager { private func migrateAccountModelFromIndependentFields(_ legacyData: [String: Any]) throws { guard let userId = legacyData[LegacyStorageKeys.userId] as? String, let accessToken = legacyData[LegacyStorageKeys.accessToken] as? String else { - print("ℹ️ 没有足够的独立字段来重建 AccountModel") + debugInfo("ℹ️ 没有足够的独立字段来重建 AccountModel") return } @@ -208,7 +208,7 @@ final class DataMigrationManager { ) try KeychainManager.shared.store(accountModel, forKey: "account_model") - print("✅ 从独立字段重建 AccountModel 成功") + debugInfo("✅ 从独立字段重建 AccountModel 成功") } /// 验证迁移结果 @@ -240,7 +240,7 @@ final class DataMigrationManager { } } - print("✅ 迁移数据验证成功") + debugInfo("✅ 迁移数据验证成功") } /// 清理旧数据 @@ -249,11 +249,11 @@ final class DataMigrationManager { for key in keys { userDefaults.removeObject(forKey: key) - print("🗑️ 清理旧数据: \(key)") + debugInfo("🗑️ 清理旧数据: \(key)") } userDefaults.synchronize() - print("✅ 旧数据清理完成") + debugInfo("✅ 旧数据清理完成") } } @@ -287,13 +287,13 @@ extension DataMigrationManager { switch migrationResult { case .completed: - print("🎉 应用启动时数据迁移完成") + debugInfo("🎉 应用启动时数据迁移完成") case .alreadyMigrated: break // 静默处理 case .noDataToMigrate: break // 静默处理 case .failed(let error): - print("⚠️ 应用启动时数据迁移失败: \(error)") + debugError("⚠️ 应用启动时数据迁移失败: \(error)") // 这里可以添加错误上报或降级策略 } } @@ -307,9 +307,9 @@ extension DataMigrationManager { /// 调试:打印旧数据信息 func debugPrintLegacyData() { let legacyData = collectLegacyData() - print("🔍 旧版本数据:") + debugInfo("🔍 旧版本数据:") for (key, value) in legacyData { - print(" - \(key): \(type(of: value))") + debugInfo(" - \(key): \(type(of: value))") } } @@ -322,7 +322,7 @@ extension DataMigrationManager { userDefaults.set("zh-Hans", forKey: LegacyStorageKeys.appLanguage) userDefaults.synchronize() - print("🧪 已创建测试用的旧版本数据") + debugInfo("🧪 已创建测试用的旧版本数据") } /// 调试:清除所有迁移相关数据 @@ -331,7 +331,7 @@ extension DataMigrationManager { do { try KeychainManager.shared.clearAll() } catch { - print("❌ 清除 Keychain 数据失败: \(error)") + debugError("❌ 清除 Keychain 数据失败: \(error)") } // 清除 UserDefaults 数据 @@ -350,7 +350,7 @@ extension DataMigrationManager { } userDefaults.synchronize() - print("🧪 已清除所有迁移相关数据") + debugInfo("🧪 已清除所有迁移相关数据") } } #endif \ No newline at end of file diff --git a/yana/Utils/Security/KeychainManager.swift b/yana/Utils/Security/KeychainManager.swift index 9ad3b17..9eedad7 100644 --- a/yana/Utils/Security/KeychainManager.swift +++ b/yana/Utils/Security/KeychainManager.swift @@ -108,7 +108,7 @@ final class KeychainManager { throw KeychainError.keychainOperationFailed(status) } - print("🔐 Keychain 存储成功: \(key)") + debugInfo("🔐 Keychain 存储成功: \(key)") } /// 从 Keychain 检索 Codable 对象 @@ -137,7 +137,7 @@ final class KeychainManager { // 4. 解码数据 do { let object = try JSONDecoder().decode(type, from: data) - print("🔐 Keychain 读取成功: \(key)") + debugInfo("🔐 Keychain 读取成功: \(key)") return object } catch { throw KeychainError.decodingFailed(error) @@ -176,7 +176,7 @@ final class KeychainManager { switch status { case errSecSuccess: - print("🔐 Keychain 更新成功: \(key)") + debugInfo("🔐 Keychain 更新成功: \(key)") case errSecItemNotFound: // 如果项目不存在,则创建新项目 @@ -196,7 +196,7 @@ final class KeychainManager { switch status { case errSecSuccess: - print("🔐 Keychain 删除成功: \(key)") + debugInfo("🔐 Keychain 删除成功: \(key)") case errSecItemNotFound: // 项目不存在,视为删除成功 @@ -231,7 +231,7 @@ final class KeychainManager { switch status { case errSecSuccess, errSecItemNotFound: - print("🔐 Keychain 清除完成") + debugInfo("🔐 Keychain 清除完成") default: throw KeychainError.keychainOperationFailed(status) @@ -353,9 +353,9 @@ extension KeychainManager { /// 打印所有存储的键(仅用于调试) func debugPrintAllKeys() { let keys = debugListAllKeys() - print("🔐 Keychain 中存储的键:") + debugInfo("🔐 Keychain 中存储的键:") for key in keys { - print(" - \(key)") + debugInfo(" - \(key)") } } } diff --git a/yana/Views/Components/UserAgreementView.swift b/yana/Views/Components/UserAgreementView.swift index ddea471..0b1a341 100644 --- a/yana/Views/Components/UserAgreementView.swift +++ b/yana/Views/Components/UserAgreementView.swift @@ -66,20 +66,20 @@ struct UserAgreementView: View { UserAgreementView( isAgreed: .constant(true), onUserServiceTapped: { - print("User Service Agreement tapped") + debugInfo("User Service Agreement tapped") }, onPrivacyPolicyTapped: { - print("Privacy Policy tapped") + debugInfo("Privacy Policy tapped") } ) UserAgreementView( isAgreed: .constant(true), onUserServiceTapped: { - print("User Service Agreement tapped") + debugInfo("User Service Agreement tapped") }, onPrivacyPolicyTapped: { - print("Privacy Policy tapped") + debugInfo("Privacy Policy tapped") } ) } diff --git a/yana/Views/IDLoginView.swift b/yana/Views/IDLoginView.swift index 16c1730..fcda328 100644 --- a/yana/Views/IDLoginView.swift +++ b/yana/Views/IDLoginView.swift @@ -207,7 +207,7 @@ struct IDLoginView: View { #if DEBUG // 移除测试用的硬编码凭据 - print("🐛 Debug模式: 已移除硬编码测试凭据") + debugInfo("🐛 Debug模式: 已移除硬编码测试凭据") #endif } } diff --git a/yana/Views/MeView.swift b/yana/Views/MeView.swift index de8f57a..b194c89 100644 --- a/yana/Views/MeView.swift +++ b/yana/Views/MeView.swift @@ -1,6 +1,7 @@ import SwiftUI struct MeView: View { + @State private var showLogoutConfirmation = false var body: some View { GeometryReader { geometry in ScrollView { @@ -48,7 +49,9 @@ struct MeView: View { .padding(.top, 40) // 退出登录按钮 - Button(action: {}) { + Button(action: { + showLogoutConfirmation = true + }) { HStack { Image(systemName: "rectangle.portrait.and.arrow.right") .font(.system(size: 16)) @@ -72,6 +75,27 @@ struct MeView: View { } } .ignoresSafeArea(.container, edges: .top) + .alert("确认退出", isPresented: $showLogoutConfirmation) { + Button("取消", role: .cancel) { } + Button("退出", role: .destructive) { + performLogout() + } + } message: { + Text("确定要退出登录吗?") + } + } + + // MARK: - 退出登录方法 + private func performLogout() { + debugInfo("🔓 开始执行退出登录...") + + // 清除所有认证数据(包括 keychain 中的内容) + UserInfoManager.clearAllAuthenticationData() + + // 发送通知重置 window root 为 login view + NotificationCenter.default.post(name: .homeLogout, object: nil) + + debugInfo("✅ 退出登录完成") } } @@ -111,4 +135,4 @@ struct MenuItemView: View { #Preview { MeView() -} \ No newline at end of file +} diff --git a/yana/Views/SettingView.swift b/yana/Views/SettingView.swift index c332085..ac2d47f 100644 --- a/yana/Views/SettingView.swift +++ b/yana/Views/SettingView.swift @@ -188,12 +188,12 @@ struct SettingRowView: View { } } -#Preview { - SettingView( - store: Store( - initialState: SettingFeature.State() - ) { - SettingFeature() - } - ) -} \ No newline at end of file +//#Preview { +// SettingView( +// store: Store( +// initialState: SettingFeature.State() +// ) { +// SettingFeature() +// } +// ) +//} diff --git a/yana/yanaApp.swift b/yana/yanaApp.swift index a38d1c4..890cca8 100644 --- a/yana/yanaApp.swift +++ b/yana/yanaApp.swift @@ -21,7 +21,7 @@ struct yanaApp: App { } #endif - print("🛠 原生URLSession测试开始") + debugInfo("🛠 原生URLSession测试开始") } var body: some Scene { diff --git a/yanaAPITests/yanaAPITests.swift b/yanaAPITests/yanaAPITests.swift index 8a86ace..cb75126 100644 --- a/yanaAPITests/yanaAPITests.swift +++ b/yanaAPITests/yanaAPITests.swift @@ -163,10 +163,10 @@ final class yanaAPITests: XCTestCase { XCTAssertEqual(accountModel?.uid, "3184", "真实API数据的UID应该正确") XCTAssertTrue(accountModel?.hasValidAuthentication ?? false, "真实API数据应该有有效认证") - print("✅ 真实API数据测试通过") - print(" UID: \(accountModel?.uid ?? "nil")") - print(" Access Token存在: \(accountModel?.accessToken != nil)") - print(" Token类型: \(accountModel?.tokenType ?? "nil")") + debugInfo("✅ 真实API数据测试通过") + debugInfo(" UID: \(accountModel?.uid ?? "nil")") + debugInfo(" Access Token存在: \(accountModel?.accessToken != nil)") + debugInfo(" Token类型: \(accountModel?.tokenType ?? "nil")") } catch { XCTFail("解析真实API数据失败: \(error)")