添加埋点
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifndef KB_MAI_POINT_BASE_URL
|
||||
#define KB_MAI_POINT_BASE_URL @"http://192.168.2.188:35310/api"
|
||||
#define KB_MAI_POINT_BASE_URL @"http://192.168.2.21:35310/api"
|
||||
#endif
|
||||
|
||||
#ifndef KB_MAI_POINT_PATH_NEW_ACCOUNT
|
||||
@@ -20,6 +20,8 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString * const KBMaiPointErrorDomain;
|
||||
extern NSString * const KBMaiPointEventTypePageExposure;
|
||||
extern NSString * const KBMaiPointEventTypeClick;
|
||||
|
||||
typedef void (^KBMaiPointReportCompletion)(BOOL success, NSError * _Nullable error);
|
||||
|
||||
@@ -39,6 +41,28 @@ typedef NS_ENUM(NSInteger, KBMaiPointGenericReportType) {
|
||||
|
||||
+ (instancetype)sharedReporter;
|
||||
|
||||
/// 统一埋点:POST /genericData
|
||||
/// - eventType: 建议取值 `page_exposure` / `click`
|
||||
/// - eventName: 统一事件名(如 enter_xxx / click_xxx)
|
||||
/// - value: 事件参数字典(内部会自动注入 token;无 token 时为 @"")
|
||||
- (void)reportEventType:(NSString *)eventType
|
||||
eventName:(NSString *)eventName
|
||||
value:(nullable NSDictionary *)value
|
||||
completion:(KBMaiPointReportCompletion _Nullable)completion;
|
||||
|
||||
/// 页面曝光快捷方法:内部会补齐 page_id
|
||||
- (void)reportPageExposureWithEventName:(NSString *)eventName
|
||||
pageId:(NSString *)pageId
|
||||
extra:(nullable NSDictionary *)extra
|
||||
completion:(KBMaiPointReportCompletion _Nullable)completion;
|
||||
|
||||
/// 点击快捷方法:内部会补齐 page_id / element_id
|
||||
- (void)reportClickWithEventName:(NSString *)eventName
|
||||
pageId:(NSString *)pageId
|
||||
elementId:(NSString *)elementId
|
||||
extra:(nullable NSDictionary *)extra
|
||||
completion:(KBMaiPointReportCompletion _Nullable)completion;
|
||||
|
||||
/// POST /newAccount with type + account.
|
||||
- (void)reportNewAccountWithType:(NSString *)type
|
||||
account:(nullable NSString *)account
|
||||
|
||||
@@ -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 {
|
||||
// 若外部传了 token,也做一次兜底(nil -> @"" / 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
|
||||
|
||||
Reference in New Issue
Block a user