添加埋点

This commit is contained in:
2026-01-06 19:25:34 +08:00
parent 1096f24c57
commit c3909d63da
30 changed files with 784 additions and 39 deletions

View File

@@ -5,8 +5,15 @@
#import "KBMaiPointReporter.h"
#import "KBLog.h"
#import "KBAuthManager.h"
#if __has_include(<UIKit/UIKit.h>)
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#endif
NSString * const KBMaiPointErrorDomain = @"KBMaiPointErrorDomain";
NSString * const KBMaiPointEventTypePageExposure = @"page_exposure";
NSString * const KBMaiPointEventTypeClick = @"click";
#if DEBUG
static void KBMaiPoint_DebugLogURL(NSURLRequest *request) {
@@ -47,12 +54,91 @@ static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) {
return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] ?: @"";
}
- (NSString *)kb_currentTokenOrEmpty {
NSString *t = [KBAuthManager shared].current.accessToken;
return [self kb_trimmedStringOrEmpty:t];
}
- (void)reportEventType:(NSString *)eventType
eventName:(NSString *)eventName
value:(NSDictionary * _Nullable)value
completion:(KBMaiPointReportCompletion _Nullable)completion {
NSString *trimmedType = [self kb_trimmedStringOrEmpty:eventType];
NSString *trimmedName = [self kb_trimmedStringOrEmpty:eventName];
if (trimmedType.length == 0 || trimmedName.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;
}
NSMutableDictionary *val = [NSMutableDictionary dictionary];
if ([value isKindOfClass:[NSDictionary class]] && value.count > 0) {
[val addEntriesFromDictionary:value];
}
if (![val[@"token"] isKindOfClass:NSString.class]) {
val[@"token"] = [self kb_currentTokenOrEmpty];
} else {
// tokennil -> @"" / trim
val[@"token"] = [self kb_trimmedStringOrEmpty:val[@"token"]];
}
NSDictionary *params = @{
// eventId eventName
@"eventType": trimmedType,
@"eventName": trimmedName,
@"eventId": trimmedName,
@"value": val.copy
};
[self postPath:KB_MAI_POINT_PATH_GENERIC_DATA parameters:params completion:completion];
}
- (void)reportPageExposureWithEventName:(NSString *)eventName
pageId:(NSString *)pageId
extra:(NSDictionary * _Nullable)extra
completion:(KBMaiPointReportCompletion _Nullable)completion {
NSString *pid = [self kb_trimmedStringOrEmpty:pageId];
NSMutableDictionary *val = [NSMutableDictionary dictionary];
if (pid.length > 0) {
val[@"page_id"] = pid;
}
if ([extra isKindOfClass:[NSDictionary class]] && extra.count > 0) {
[val addEntriesFromDictionary:extra];
}
[self reportEventType:KBMaiPointEventTypePageExposure eventName:eventName value:val completion:completion];
}
- (void)reportClickWithEventName:(NSString *)eventName
pageId:(NSString *)pageId
elementId:(NSString *)elementId
extra:(NSDictionary * _Nullable)extra
completion:(KBMaiPointReportCompletion _Nullable)completion {
NSString *pid = [self kb_trimmedStringOrEmpty:pageId];
NSString *eid = [self kb_trimmedStringOrEmpty:elementId];
NSMutableDictionary *val = [NSMutableDictionary dictionary];
if (pid.length > 0) {
val[@"page_id"] = pid;
}
if (eid.length > 0) {
val[@"element_id"] = eid;
}
if ([extra isKindOfClass:[NSDictionary class]] && extra.count > 0) {
[val addEntriesFromDictionary:extra];
}
[self reportEventType:KBMaiPointEventTypeClick eventName:eventName value:val completion:completion];
}
- (void)reportNewAccountWithType:(NSString *)type
account:(NSString * _Nullable)account
completion:(KBMaiPointReportCompletion _Nullable)completion {
NSString *trimmedType = [self kb_trimmedStringOrEmpty:type];
NSString *trimmedAccount = [self kb_trimmedStringOrEmpty:account];
if (trimmedType.length == 0 || trimmedAccount.length == 0) {
if (trimmedType.length == 0) {
NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"Invalid parameter"}];
@@ -66,7 +152,8 @@ static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) {
NSDictionary *params = @{
@"type": trimmedType,
@"account": trimmedAccount
@"account": trimmedAccount ?: @"",
@"token": [self kb_currentTokenOrEmpty]
};
[self postPath:KB_MAI_POINT_PATH_NEW_ACCOUNT parameters:params completion:completion];
}
@@ -83,27 +170,20 @@ static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) {
- (void)reportGenericDataWithEventType:(KBMaiPointGenericReportType)eventType
account:(nullable NSString *)account
completion:(KBMaiPointReportCompletion _Nullable)completion{
// if ([KBUserSessionManager shared].isLoggedIn == false) {
// return;
// }
NSString *trimmedAccount = [self kb_trimmedStringOrEmpty:account];
if (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;
// eventName
NSString *typeStr = @"unknown";
switch (eventType) {
case KBMaiPointGenericReportTypeClick: typeStr = KBMaiPointEventTypeClick; break;
case KBMaiPointGenericReportTypeExposure: typeStr = @"exposure"; break;
case KBMaiPointGenericReportTypePage: typeStr = KBMaiPointEventTypePageExposure; break;
default: break;
}
NSDictionary *params = @{
@"eventId": @"123",
@"account": account
};
[self postPath:KB_MAI_POINT_PATH_GENERIC_DATA parameters:params completion:completion];
NSMutableDictionary *val = [NSMutableDictionary dictionary];
NSString *trimmedAccount = [self kb_trimmedStringOrEmpty:account];
if (trimmedAccount.length > 0) {
val[@"account"] = trimmedAccount;
}
[self reportEventType:typeStr eventName:@"generic_event" value:val completion:completion];
}
- (void)postPath:(NSString *)path
@@ -197,3 +277,123 @@ static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) {
}
@end
#if __has_include(<UIKit/UIKit.h>)
// ============================
// viewDidAppear
// VC VC
// ============================
static NSDictionary<NSString *, NSDictionary *> *KBMaiPoint_PageExposureMap(void) {
static NSDictionary<NSString *, NSDictionary *> *m;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
m = @{
//
@"HomeMainVC": @{@"event_name": @"enter_home_main", @"page_id": @"home_main"},
@"HomeVC": @{@"event_name": @"enter_home", @"page_id": @"home"},
@"HomeHotVC": @{@"event_name": @"enter_home_hot", @"page_id": @"home_hot"},
@"HomeRankVC": @{@"event_name": @"enter_home_rank", @"page_id": @"home_rank"},
@"HomeRankContentVC": @{@"event_name": @"enter_home_rank_content", @"page_id": @"home_rank_content"},
@"HomeSheetVC": @{@"event_name": @"enter_home_sheet", @"page_id": @"home_sheet"},
@"KBCommunityVC": @{@"event_name": @"enter_community", @"page_id": @"community"},
@"KBSearchVC": @{@"event_name": @"enter_search", @"page_id": @"search"},
@"KBSearchResultVC": @{@"event_name": @"enter_search_result", @"page_id": @"search_result"},
@"KBShopVC": @{@"event_name": @"enter_shop", @"page_id": @"shop"},
@"KBShopItemVC": @{@"event_name": @"enter_shop_item_list", @"page_id": @"shop_item_list"},
@"KBSkinDetailVC": @{@"event_name": @"enter_skin_detail", @"page_id": @"skin_detail"},
@"MyVC": @{@"event_name": @"enter_my", @"page_id": @"my"},
@"MySkinVC": @{@"event_name": @"enter_my_skin", @"page_id": @"my_skin"},
@"KBMyKeyBoardVC": @{@"event_name": @"enter_my_keyboard", @"page_id": @"my_keyboard"},
@"KBPersonInfoVC": @{@"event_name": @"enter_person_info", @"page_id": @"person_info"},
@"KBFeedBackVC": @{@"event_name": @"enter_feedback", @"page_id": @"feedback"},
@"KBNoticeVC": @{@"event_name": @"enter_notice", @"page_id": @"notice"},
@"KBConsumptionRecordVC": @{@"event_name": @"enter_consumption_record", @"page_id": @"consumption_record"},
@"KBVipPay": @{@"event_name": @"enter_vip_pay", @"page_id": @"vip_pay"},
@"KBJfPay": @{@"event_name": @"enter_points_recharge", @"page_id": @"points_recharge"},
@"KBLoginVC": @{@"event_name": @"enter_login", @"page_id": @"login"},
@"KBEmailLoginVC": @{@"event_name": @"enter_login_email", @"page_id": @"login_email"},
@"KBEmailRegistVC": @{@"event_name": @"enter_register_email", @"page_id": @"register_email"},
@"KBRegistVerEmailVC": @{@"event_name": @"enter_register_verify_email", @"page_id": @"register_verify_email"},
@"KBForgetPwdVC": @{@"event_name": @"enter_forgot_password_email", @"page_id": @"forgot_password_email"},
@"KBForgetVerPwdVC": @{@"event_name": @"enter_forgot_password_verify", @"page_id": @"forgot_password_verify"},
@"KBForgetPwdNewPwdVC": @{@"event_name": @"enter_forgot_password_newpwd", @"page_id": @"forgot_password_newpwd"},
@"KBPermissionViewController": @{@"event_name": @"enter_keyboard_permission_guide", @"page_id": @"keyboard_permission_guide"},
@"KBGuideVC": @{@"event_name": @"enter_guide", @"page_id": @"guide"},
@"KBSexSelVC": @{@"event_name": @"enter_sex_select", @"page_id": @"sex_select"},
@"KBWebViewViewController": @{@"event_name": @"enter_webview", @"page_id": @"webview"},
//
@"KeyboardViewController": @{@"event_name": @"enter_keyboard", @"page_id": @"keyboard"},
};
});
return m;
}
static inline void KBMaiPoint_SwizzleInstanceMethod(Class cls, SEL originalSel, SEL swizzledSel) {
Method original = class_getInstanceMethod(cls, originalSel);
Method swizzled = class_getInstanceMethod(cls, swizzledSel);
if (!original || !swizzled) return;
BOOL added = class_addMethod(cls,
originalSel,
method_getImplementation(swizzled),
method_getTypeEncoding(swizzled));
if (added) {
class_replaceMethod(cls,
swizzledSel,
method_getImplementation(original),
method_getTypeEncoding(original));
} else {
method_exchangeImplementations(original, swizzled);
}
}
@interface UIViewController (KBMaiPointAutoReport)
@end
@implementation UIViewController (KBMaiPointAutoReport)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
KBMaiPoint_SwizzleInstanceMethod(self, @selector(viewDidAppear:), @selector(kb_maipoint_viewDidAppear:));
});
}
- (void)kb_maipoint_viewDidAppear:(BOOL)animated {
[self kb_maipoint_viewDidAppear:animated];
NSString *clsName = NSStringFromClass(self.class);
NSDictionary *cfg = KBMaiPoint_PageExposureMap()[clsName];
if (![cfg isKindOfClass:NSDictionary.class]) { return; }
NSString *eventName = cfg[@"event_name"];
NSString *pageId = cfg[@"page_id"];
if (![eventName isKindOfClass:NSString.class] || ![pageId isKindOfClass:NSString.class]) { return; }
//
NSMutableDictionary *extra = [NSMutableDictionary dictionary];
if ([clsName isEqualToString:@"KBSkinDetailVC"]) {
id themeId = nil;
@try { themeId = [self valueForKey:@"themeId"]; } @catch (__unused NSException *e) { themeId = nil; }
if ([themeId isKindOfClass:NSString.class] && ((NSString *)themeId).length > 0) {
extra[@"theme_id"] = themeId;
}
} else if ([clsName isEqualToString:@"KBWebViewViewController"]) {
id url = nil;
@try { url = [self valueForKey:@"url"]; } @catch (__unused NSException *e) { url = nil; }
if ([url isKindOfClass:NSString.class] && ((NSString *)url).length > 0) {
extra[@"url"] = url;
}
}
[[KBMaiPointReporter sharedReporter] reportPageExposureWithEventName:eventName
pageId:pageId
extra:(extra.count > 0 ? extra.copy : nil)
completion:nil];
}
@end
#endif