diff --git a/CustomKeyboard/PrefixHeader.pch b/CustomKeyboard/PrefixHeader.pch index 9011dfa..44cc28b 100644 --- a/CustomKeyboard/PrefixHeader.pch +++ b/CustomKeyboard/PrefixHeader.pch @@ -21,6 +21,8 @@ #import "Masonry.h" #import "KBHUD.h" // 复用 App 内的 HUD 封装 #import "KBLocalizationManager.h" // 复用多语言封装(可在扩展内使用) +#import "KBMaiPointReporter.h" + // 通用链接(Universal Links)统一配置 // 配置好 AASA 与 Associated Domains 后,只需修改这里即可切换域名/path。 diff --git a/Shared/KBMaiPointReporter.h b/Shared/KBMaiPointReporter.h new file mode 100644 index 0000000..0703a47 --- /dev/null +++ b/Shared/KBMaiPointReporter.h @@ -0,0 +1,39 @@ +// +// KBMaiPointReporter.h +// keyBoard +// + +#import + +#ifndef KB_MAI_POINT_BASE_URL +#define KB_MAI_POINT_BASE_URL @"http://192.168.1.188:35310/api" +#endif + +#ifndef KB_MAI_POINT_PATH_NEW_ACCOUNT +#define KB_MAI_POINT_PATH_NEW_ACCOUNT @"/newAccount" +#endif + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const KBMaiPointErrorDomain; + +typedef void (^KBMaiPointReportCompletion)(BOOL success, NSError * _Nullable error); + +/// Lightweight reporter for Mai point tracking. Safe for app + extension. +@interface KBMaiPointReporter : NSObject + ++ (instancetype)sharedReporter; + +/// POST /newAccount with type + account. +- (void)reportNewAccountWithType:(NSString *)type + account:(NSString *)account + completion:(KBMaiPointReportCompletion _Nullable)completion; + +/// Generic POST for future endpoints. +- (void)postPath:(NSString *)path + parameters:(NSDictionary *)parameters + completion:(KBMaiPointReportCompletion _Nullable)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Shared/KBMaiPointReporter.m b/Shared/KBMaiPointReporter.m new file mode 100644 index 0000000..8f83521 --- /dev/null +++ b/Shared/KBMaiPointReporter.m @@ -0,0 +1,127 @@ +// +// KBMaiPointReporter.m +// keyBoard +// + +#import "KBMaiPointReporter.h" + +NSString * const KBMaiPointErrorDomain = @"KBMaiPointErrorDomain"; + +@implementation KBMaiPointReporter + ++ (instancetype)sharedReporter { + static KBMaiPointReporter *reporter = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + reporter = [[KBMaiPointReporter alloc] init]; + }); + return reporter; +} + +- (void)reportNewAccountWithType:(NSString *)type + account:(NSString *)account + completion:(KBMaiPointReportCompletion)completion { + NSString *trimmedType = [type stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSString *trimmedAccount = [account stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (trimmedType.length == 0 || trimmedAccount.length == 0) { + NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain + code:-1 + userInfo:@{NSLocalizedDescriptionKey: @"Invalid parameter"}]; + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(NO, error); + }); + } + return; + } + + NSDictionary *params = @{ + @"type": trimmedType, + @"account": trimmedAccount + }; + [self postPath:KB_MAI_POINT_PATH_NEW_ACCOUNT parameters:params completion:completion]; +} + +- (void)postPath:(NSString *)path + parameters:(NSDictionary *)parameters + completion:(KBMaiPointReportCompletion)completion { + if (path.length == 0 || ![parameters isKindOfClass:[NSDictionary class]]) { + NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain + code:-1 + userInfo:@{NSLocalizedDescriptionKey: @"Invalid parameter"}]; + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(NO, error); + }); + } + return; + } + + NSString *safePath = [path hasPrefix:@"/"] ? path : [@"/" stringByAppendingString:path]; + NSString *urlString = [NSString stringWithFormat:@"%@%@", KB_MAI_POINT_BASE_URL, safePath]; + NSURL *url = [NSURL URLWithString:urlString]; + if (!url) { + NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain + code:-2 + userInfo:@{NSLocalizedDescriptionKey: @"Invalid URL"}]; + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(NO, error); + }); + } + return; + } + + NSError *jsonError = nil; + NSData *body = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&jsonError]; + if (jsonError) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(NO, jsonError); + }); + } + return; + } + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + request.HTTPMethod = @"POST"; + request.timeoutInterval = 10.0; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + request.HTTPBody = body; + + NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; + NSURLSessionDataTask *task = [session dataTaskWithRequest:request + completionHandler:^(NSData *data, + NSURLResponse *response, + NSError *error) { + BOOL success = NO; + NSError *finalError = error; + + if (!finalError) { + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; + success = (statusCode >= 200 && statusCode < 300); + if (!success) { + finalError = [NSError errorWithDomain:KBMaiPointErrorDomain + code:statusCode + userInfo:@{NSLocalizedDescriptionKey: @"Invalid response"}]; + } + } else { + finalError = [NSError errorWithDomain:KBMaiPointErrorDomain + code:-3 + userInfo:@{NSLocalizedDescriptionKey: @"Invalid response"}]; + } + } + + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(success, finalError); + }); + } + }]; + [task resume]; +} + +@end diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index d4c165b..72a0552 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -129,6 +129,8 @@ 0498BD8C2EE69E15006CC1D5 /* KBTagItemModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */; }; 0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; }; 0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; }; + A1F0C1D22FACAD0012345678 /* KBMaiPointReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1D12FACAD0012345678 /* KBMaiPointReporter.m */; }; + A1F0C1D32FACAD0012345678 /* KBMaiPointReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1D12FACAD0012345678 /* KBMaiPointReporter.m */; }; A1F0C1C22FABCDEF12345678 /* KBInviteCodeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1C12FABCDEF12345678 /* KBInviteCodeModel.m */; }; A1F0C1C32FABCDEF12345678 /* KBInviteCodeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1C12FABCDEF12345678 /* KBInviteCodeModel.m */; }; 0498BDDA2EE7ECEA006CC1D5 /* WJXEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */; }; @@ -459,6 +461,8 @@ 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyMainModel.m; sourceTree = ""; }; A1F0C1C02FABCDEF12345678 /* KBInviteCodeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBInviteCodeModel.h; sourceTree = ""; }; A1F0C1C12FABCDEF12345678 /* KBInviteCodeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBInviteCodeModel.m; sourceTree = ""; }; + A1F0C1D02FACAD0012345678 /* KBMaiPointReporter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMaiPointReporter.h; sourceTree = ""; }; + A1F0C1D12FACAD0012345678 /* KBMaiPointReporter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMaiPointReporter.m; sourceTree = ""; }; 0498BDD72EE7ECEA006CC1D5 /* WJXEventSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WJXEventSource.h; sourceTree = ""; }; 0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WJXEventSource.m; sourceTree = ""; }; 0498BDDC2EE81508006CC1D5 /* KBShopVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopVM.h; sourceTree = ""; }; @@ -1646,6 +1650,8 @@ 0498BD842EE1B255006CC1D5 /* KBSignUtils.m */, 047920482EDDCE25004E8522 /* KBUserSessionManager.h */, 047920492EDDCE25004E8522 /* KBUserSessionManager.m */, + A1F0C1D02FACAD0012345678 /* KBMaiPointReporter.h */, + A1F0C1D12FACAD0012345678 /* KBMaiPointReporter.m */, ); path = Shared; sourceTree = ""; @@ -1948,6 +1954,7 @@ A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */, 0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */, A1F0C1C22FABCDEF12345678 /* KBInviteCodeModel.m in Sources */, + A1F0C1D22FACAD0012345678 /* KBMaiPointReporter.m in Sources */, 04FEDC222F00020000999999 /* KBKeyboardSubscriptionProduct.m in Sources */, 0450AA742EF013D000B6AF06 /* KBEmojiCollectionCell.m in Sources */, 550CB2630FA4A7B4B9782EFA /* KBMyTheme.m in Sources */, @@ -2012,6 +2019,7 @@ 048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */, 0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */, A1F0C1C32FABCDEF12345678 /* KBInviteCodeModel.m in Sources */, + A1F0C1D32FACAD0012345678 /* KBMaiPointReporter.m in Sources */, 471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */, A1F0C1B12F1234567890ABCD /* KBConsumptionRecord.m in Sources */, A1F0C1B22F1234567890ABCD /* KBConsumptionRecordCell.m in Sources */, diff --git a/keyBoard/Class/Login/VC/KBEmailLoginVC.m b/keyBoard/Class/Login/VC/KBEmailLoginVC.m index ce461cd..30daf23 100644 --- a/keyBoard/Class/Login/VC/KBEmailLoginVC.m +++ b/keyBoard/Class/Login/VC/KBEmailLoginVC.m @@ -228,9 +228,10 @@ self.passwordTextField.text.length); NSString *email = self.emailTextField.text ? self.emailTextField.text : @""; NSString *password = self.passwordTextField.text ? self.passwordTextField.text : @""; - + KBWeakSelf; [self.loginVM emailLoginEmail:email password:password WithCompletion:^(BOOL success, NSError * _Nullable error) { if (success) { + [weakSelf report:email]; [[NSUserDefaults standardUserDefaults] setValue:email forKey:KBUserEmailKey]; [[NSUserDefaults standardUserDefaults] synchronize]; id appDelegate = UIApplication.sharedApplication.delegate; @@ -248,6 +249,11 @@ }]; } +- (void)report:(NSString *)account{ + [[KBMaiPointReporter sharedReporter] reportNewAccountWithType:@"登录用户" account:account completion:^(BOOL success, NSError * _Nullable error) { + + }]; +} - (void)onTapForgotPassword { KBLOG(@"KBEmailLoginVC onTapForgotPassword"); diff --git a/keyBoard/Class/Login/VC/KBRegistVerEmailVC.m b/keyBoard/Class/Login/VC/KBRegistVerEmailVC.m index 39b59b3..9772155 100644 --- a/keyBoard/Class/Login/VC/KBRegistVerEmailVC.m +++ b/keyBoard/Class/Login/VC/KBRegistVerEmailVC.m @@ -100,6 +100,7 @@ self.params[@"gender"] = genderNumber; } NSString *email = self.params[@"mailAddress"] ? self.params[@"mailAddress"] : @""; + KBWeakSelf [self.loginVM emailRegisterParams:self.params withCompletion:^(BOOL success, NSError * _Nullable error) { if (success) { [KBHUD showInfo:KBLocalized(@"Signed in successfully")]; @@ -107,6 +108,7 @@ dispatch_async(dispatch_get_main_queue(), ^{ [[NSUserDefaults standardUserDefaults] setValue:email forKey:KBUserEmailKey]; [[NSUserDefaults standardUserDefaults] synchronize]; + [weakSelf report:email]; KBEmailLoginVC *vc = [[KBEmailLoginVC alloc] init]; [KB_CURRENT_NAV pushViewController:vc animated:true]; // id appDelegate = UIApplication.sharedApplication.delegate; @@ -129,6 +131,12 @@ }]; } +- (void)report:(NSString *)account{ + [[KBMaiPointReporter sharedReporter] reportNewAccountWithType:@"注册账号" account:account completion:^(BOOL success, NSError * _Nullable error) { + + }]; +} + - (nullable NSNumber *)kb_localGenderParamIfAvailable { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; // 只有在用户真正看过性别选择页后,才认为本地的性别有效 diff --git a/keyBoard/KeyBoardPrefixHeader.pch b/keyBoard/KeyBoardPrefixHeader.pch index ca8f80f..f5f53ed 100644 --- a/keyBoard/KeyBoardPrefixHeader.pch +++ b/keyBoard/KeyBoardPrefixHeader.pch @@ -47,6 +47,8 @@ #import "BaseViewController.h" #import "KBLocalizationManager.h" // 全局多语言封装 +#import "KBMaiPointReporter.h" + //-----------------------------------------------宏定义全局----------------------------------------------------------/ diff --git a/keyBoard/VC/KBPermissionViewController.m b/keyBoard/VC/KBPermissionViewController.m index 95dc657..380543a 100644 --- a/keyBoard/VC/KBPermissionViewController.m +++ b/keyBoard/VC/KBPermissionViewController.m @@ -193,6 +193,7 @@ static void *KBPermPlayerPresentationSizeContext = &KBPermPlayerPresentationSize } - (void)openSettings { + [self report]; NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; UIApplication *app = [UIApplication sharedApplication]; if ([app canOpenURL:url]) { @@ -285,6 +286,13 @@ static void *KBPermPlayerPresentationSizeContext = &KBPermPlayerPresentationSize [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } +#pragma mark - network +- (void)report{ + [[KBMaiPointReporter sharedReporter] reportNewAccountWithType:@"键盘申请授权" account:nil completion:^(BOOL success, NSError * _Nullable error) { + + }]; +} + #pragma mark - Lazy Subviews - (UIButton *)backButton {