From 6be90ebb10100f9dfc1b9e85ff4562de93ef4064 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Tue, 2 Dec 2025 21:32:49 +0800 Subject: [PATCH] 1 --- Shared/KBBizCode.h | 62 +++++++++++++++++++++ keyBoard.xcodeproj/project.pbxproj | 2 + keyBoard/Class/Network/KBNetworkManager.h | 1 + keyBoard/Class/Network/KBNetworkManager.m | 65 ++++++++++++++++++++++- 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 Shared/KBBizCode.h diff --git a/Shared/KBBizCode.h b/Shared/KBBizCode.h new file mode 100644 index 0000000..776b9d0 --- /dev/null +++ b/Shared/KBBizCode.h @@ -0,0 +1,62 @@ +// +// KBBizCode.h +// 服务端业务状态码与通用解析工具 +// +// 说明: +// - 将所有常用的业务 code 集中到这里,避免在项目里到处写裸数字; +// - 实际数值请按照你们后端的约定修改; +// - 若后端新增/调整 code,只需要维护这一处即可。 +// + +#ifndef KBBizCode_h +#define KBBizCode_h + +#import +#import "KBAPI.h" // 引入 SUCCESS_CODE/ERROR_CODE 等基础定义 + +/// 服务端业务状态码(按项目实际调整) +typedef NS_ENUM(NSInteger, KBBizCode) { + /// 通用成功(通常为 200,对应 SUCCESS_CODE) + KBBizCodeSuccess = SUCCESS_CODE, + + /// token 失效/未登录(示例值;请按后端实际 code 修改) + KBBizCodeTokenInvalid = 40101, + + /// token 过期(可选;如无区分可与 KBBizCodeTokenInvalid 复用) + KBBizCodeTokenExpired = 40102, + + /// 账号在其他设备登录,被服务端强制下线 + KBBizCodeAccountKicked = 40103, +}; + +NS_ASSUME_NONNULL_BEGIN + +/// 从 JSON 响应中提取业务 code。 +/// 支持常见字段:code / status / retcode(数字或字符串)。 +/// 若未找到返回 NSNotFound。 +static inline NSInteger KBBizCodeFromJSONObject(id obj) { + if (![obj isKindOfClass:NSDictionary.class]) return NSNotFound; + NSDictionary *d = (NSDictionary *)obj; + id code = d[@"code"] ?: d[@"status"] ?: d[@"retcode"]; + if ([code isKindOfClass:NSNumber.class]) { + return ((NSNumber *)code).integerValue; + } + if ([code isKindOfClass:NSString.class]) { + return ((NSString *)code).integerValue; + } + return NSNotFound; +} + +/// 从 JSON 响应中提取错误文案,支持 message/msg/error。 +static inline NSString *KBBizMessageFromJSONObject(id obj) { + if (![obj isKindOfClass:NSDictionary.class]) return nil; + NSDictionary *d = (NSDictionary *)obj; + NSString *msg = d[@"message"] ?: d[@"msg"] ?: d[@"error"]; + if (![msg isKindOfClass:NSString.class]) return nil; + return msg; +} + +NS_ASSUME_NONNULL_END + +#endif /* KBBizCode_h */ + diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index db58e60..029936b 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -363,6 +363,7 @@ 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBCategoryTitleImageView.m; sourceTree = ""; }; 04890B102EC2F00000FABA60 /* KBMyHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyHeaderView.h; sourceTree = ""; }; 04890B112EC2F00000FABA60 /* KBMyHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyHeaderView.m; sourceTree = ""; }; + 0498BD5E2EDF2157006CC1D5 /* KBBizCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBBizCode.h; sourceTree = ""; }; 049FB2092EC1C13800FAB05D /* KBSkinBottomActionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinBottomActionView.h; sourceTree = ""; }; 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinBottomActionView.m; sourceTree = ""; }; 049FB20C2EC1CD2800FAB05D /* KBAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAlert.h; sourceTree = ""; }; @@ -1356,6 +1357,7 @@ 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */, 04791F8C2ED469C0004E8522 /* KBHostAppLauncher.h */, 04791F8D2ED469C0004E8522 /* KBHostAppLauncher.m */, + 0498BD5E2EDF2157006CC1D5 /* KBBizCode.h */, ); path = Shared; sourceTree = ""; diff --git a/keyBoard/Class/Network/KBNetworkManager.h b/keyBoard/Class/Network/KBNetworkManager.h index c0dfc9e..8ac946d 100644 --- a/keyBoard/Class/Network/KBNetworkManager.h +++ b/keyBoard/Class/Network/KBNetworkManager.h @@ -17,6 +17,7 @@ typedef NS_ERROR_ENUM(KBNetworkErrorDomain, KBNetworkError) { KBNetworkErrorInvalidURL = 2, KBNetworkErrorInvalidResponse = 3, KBNetworkErrorDecodeFailed = 4, + KBNetworkErrorBusiness = 5, // 服务端返回非成功业务 code }; /// 简单的 JSON 回调:json 为 NSDictionary/NSArray 或者在非 JSON 情况下返回 NSData diff --git a/keyBoard/Class/Network/KBNetworkManager.m b/keyBoard/Class/Network/KBNetworkManager.m index c224320..6d42316 100644 --- a/keyBoard/Class/Network/KBNetworkManager.m +++ b/keyBoard/Class/Network/KBNetworkManager.m @@ -4,8 +4,13 @@ // #import "KBNetworkManager.h" +#import #import "AFNetworking.h" #import "KBAuthManager.h" +#import "KBBizCode.h" +// 仅在主 App 内需要的会话管理与 HUD 组件 +#import "KBUserSessionManager.h" +#import "KBHUD.h" NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; @@ -209,19 +214,37 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; NSString *pretty = [self kb_prettyJSONStringFromObject:json] ?: @"(null)"; pretty = [self kb_trimmedString:pretty maxLength:4096]; NSInteger status = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0; - KBLOG(@"响应成功(JSON)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@", + KBLOG(@"\n[KBNetwork] 响应成功(JSON)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@\n", req.URL.absoluteString, (long)status, ct ?: @"", pretty); #endif + // 统一解析业务 code:约定后端顶层包含 { code, message, data } + if ([json isKindOfClass:NSDictionary.class]) { + NSInteger bizCode = KBBizCodeFromJSONObject(json); + if (bizCode != NSNotFound && bizCode != KBBizCodeSuccess) { + // 非成功业务 code:执行通用处理(如 token 失效)并通过 error 方式回调 + [self kb_handleBizCode:bizCode json:json response:response]; + NSString *msg = KBBizMessageFromJSONObject(json) ?: KBLocalized(@"Server error"); + NSError *bizErr = [NSError errorWithDomain:KBNetworkErrorDomain + code:KBNetworkErrorBusiness + userInfo:@{ + NSLocalizedDescriptionKey : msg, + @"code" : @(bizCode) + }]; + if (completion) completion(json, response, bizErr); + return; + } + } + // code 缺失或为成功,按正常成功回调 if (completion) completion(json, response, nil); } else { #if DEBUG NSString *text = [self kb_textFromData:data]; text = [self kb_trimmedString:text maxLength:4096]; NSInteger status = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0; - KBLOG(@"响应成功(Data)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@", + KBLOG(@"\n[KBNetwork] 响应成功(Data)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@\n", req.URL.absoluteString, (long)status, ct ?: @"", @@ -251,6 +274,43 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; #pragma mark - Private helpers +/// 处理通用业务 code(token 失效、被踢下线等) +- (void)kb_handleBizCode:(NSInteger)bizCode + json:(id)json + response:(NSURLResponse *)response { + // 键盘扩展内暂不做复杂会话管理,交由宿主 App 处理。 + // 如需支持,可通过 Darwin 通知或 App Group 自行扩展。 + (void)bizCode; + (void)json; + (void)response; + return; + switch (bizCode) { + case KBBizCodeTokenInvalid: + case KBBizCodeTokenExpired: + case KBBizCodeAccountKicked: { + // 登录态异常:统一清理本地会话并提示用户重新登录 + NSString *msg = KBBizMessageFromJSONObject(json); + if (msg.length == 0) { + msg = KBLocalized(@"Your session has expired. Please sign in again."); + } + dispatch_async(dispatch_get_main_queue(), ^{ + // 清理本地用户会话(Keychain + 用户缓存) + [[KBUserSessionManager shared] logout]; + // 友好提示 + [KBHUD showInfo:msg]; + // 广播通知,便于 UI 侧跳转登录页等 + NSDictionary *info = @{ @"code": @(bizCode), + @"message": msg ?: @"" }; + [[NSNotificationCenter defaultCenter] postNotificationName:@"KBUserSessionInvalidNotification" + object:nil + userInfo:info]; + }); + } break; + default: + break; + } +} + - (void)fail:(KBNetworkError)code completion:(KBNetworkCompletion)completion { NSString *msg = KBLocalized(@"Network error"); switch (code) { @@ -258,6 +318,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; case KBNetworkErrorInvalidURL: msg = KBLocalized(@"Invalid URL"); break; case KBNetworkErrorInvalidResponse: msg = KBLocalized(@"Invalid response"); break; case KBNetworkErrorDecodeFailed: msg = KBLocalized(@"Parse failed"); break; + case KBNetworkErrorBusiness: msg = KBLocalized(@"Server error"); break; default: break; } NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain