ソースを参照

feat: 增加 adjust 部分事件埋点逻辑

陈文艺 1 ヶ月 前
コミット
d67cdeedc9

+ 2 - 8
Lanu.xcodeproj/project.pbxproj

@@ -351,6 +351,8 @@
 		};
 		FBB67E232EC48B440070E686 /* ThirdParty */ = {
 			isa = PBXFileSystemSynchronizedRootGroup;
+			exceptions = (
+			);
 			path = ThirdParty;
 			sourceTree = "<group>";
 		};
@@ -503,14 +505,10 @@
 			inputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-resources-${CONFIGURATION}-input-files.xcfilelist",
 			);
-			inputPaths = (
-			);
 			name = "[CP] Copy Pods Resources";
 			outputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-resources-${CONFIGURATION}-output-files.xcfilelist",
 			);
-			outputPaths = (
-			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-resources.sh\"\n";
@@ -546,14 +544,10 @@
 			inputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 			);
-			inputPaths = (
-			);
 			name = "[CP] Embed Pods Frameworks";
 			outputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 			);
-			outputPaths = (
-			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-frameworks.sh\"\n";

+ 17 - 0
Lanu/Common/Config/LNAppConfig.swift

@@ -71,6 +71,7 @@ class LNAppConfig {
     
     @Published
     private(set) var hasNewVersion = false
+    private(set) var isForeground = true
     
     var curEnv: LNAppEnvType = LNUserDefaults[.appEnv, .official] {
         didSet {
@@ -78,6 +79,14 @@ class LNAppConfig {
         }
     }
     
+    var countryCode: String {
+        if #available(iOS 16.0, *) {
+            Locale.current.region?.identifier ?? ""
+        } else {
+            Locale.current.regionCode ?? ""
+        }
+    }
+    
     private static var defaultLanguage: LNAppLanguage {
         let lang = Locale.preferredLanguages.first ?? "en"
         return if lang == LNAppLanguage.chiness.bundleName {
@@ -114,6 +123,14 @@ class LNAppConfig {
     init() {
         checkNewVersion()
         LNEventDeliver.addObserver(self)
+        NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { [weak self] _ in
+            guard let self else { return }
+            isForeground = false
+        }
+        NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [weak self] _ in
+            guard let self else { return }
+            isForeground = true
+        }
     }
 }
 

+ 3 - 0
Lanu/Manager/Account/LNAccountManager.swift

@@ -152,6 +152,7 @@ extension LNAccountManager {
             handler?(true)
             
             self.notifyUserLogin()
+            LNStatisticManager.shared.reportRegister(method: .google)
         }
     }
     
@@ -169,6 +170,7 @@ extension LNAccountManager {
             handler?(true)
             
             self.notifyUserLogin()
+            LNStatisticManager.shared.reportRegister(method: .apple)
         }
     }
     
@@ -188,6 +190,7 @@ extension LNAccountManager {
             handler?(true)
             
             self.notifyUserLogin()
+            LNStatisticManager.shared.reportRegister(method: .phone)
         }
     }
     

+ 16 - 2
Lanu/Manager/IM/LNIMManager.swift

@@ -304,9 +304,13 @@ extension LNIMManager {
                 let param = TUICallParams()
                 param.offlinePushInfo = offlinePushInfo
                 TUICallEngine.createInstance().calls(userIdList: [uid], callMediaType: .audio, params: param) { [weak self] in
+                    LNStatisticManager.shared.reportStartCall(uid: uid, success: true)
+                    
                     guard let self else { return }
                     bellPlayer.startPlay(isInCome: false)
                 } fail: { [weak panel, weak floatingView] _, err in
+                    LNStatisticManager.shared.reportStartCall(uid: uid, success: false)
+                    
                     showToast(err)
                     if let panel {
                         panel.dismiss()
@@ -327,8 +331,15 @@ extension LNIMManager {
     }
     
     func acceptVoiceCall() {
-        TUICallEngine.createInstance().accept { }
-        fail: { _, err in
+        TUICallEngine.createInstance().accept { [weak self] in
+            if let self, let curCallInfo {
+                LNStatisticManager.shared.reportAcceptCall(uid: curCallInfo.uid, success: true)
+            }
+        }
+        fail: { [weak self] _, err in
+            if let self, let curCallInfo {
+                LNStatisticManager.shared.reportAcceptCall(uid: curCallInfo.uid, success: false)
+            }
             showToast(err)
         }
     }
@@ -439,6 +450,9 @@ extension LNIMManager: TUICallObserver {
     }
     
     func onCallEnd(roomId: TUIRoomId, callMediaType: TUICallMediaType, callRole: TUICallRole, totalTime: Float) {
+        if let curCallInfo {
+            LNStatisticManager.shared.reportEndCall(uid: curCallInfo.uid, duration: totalTime)
+        }
         curCallInfo = nil
         LNEventDeliver.notifyEvent { ($0 as? LNIMManagerNotify)?.onVoiceCallEnd() }
     }

+ 1 - 1
Lanu/Manager/Location/LNLocationManager.swift

@@ -37,7 +37,7 @@ class LNLocationManager: NSObject {
         }
     }
     private var shouldReportLocation: Bool {
-        guard let curLocation, let lastTime else { return true }
+        guard curLocation != nil, let lastTime else { return true }
         
         return curTime - lastTime > 3 * 60 * 60  // 3 小时内不拉取
     }

+ 12 - 7
Lanu/Manager/Network/Monitor/LNNetworkMonitor.swift

@@ -118,9 +118,19 @@ class LNNetworkMonitor {
         return .unknown
     }
     
-    // 替代 deprecated 的 currentRadioAccessTechnology
+    static var networkProvider: String {
+        if let carriers = telephonyInfo.serviceSubscriberCellularProviders {
+            // 遍历所有 SIM 卡的网络类型(取第一个有效类型)
+            for (_, carrier) in carriers {
+                if let name = carrier.carrierName, !name.isEmpty {
+                    return name
+                }
+            }
+        }
+        return ""
+    }
+    
     private static var cellularNetworkType: LNNetworkType {
-        // iOS 13+ 推荐使用 serviceCurrentRadioAccessTechnology(支持多 SIM 卡)
         if let serviceRadioMap = telephonyInfo.serviceCurrentRadioAccessTechnology {
             // 遍历所有 SIM 卡的网络类型(取第一个有效类型)
             for (_, radioType) in serviceRadioMap {
@@ -128,11 +138,6 @@ class LNNetworkMonitor {
             }
         }
         
-        // 兼容旧版本(虽然已弃用,但可作为降级方案)
-        if let legacyRadioType = telephonyInfo.currentRadioAccessTechnology {
-            return mapRadioTypeToNetworkType(legacyRadioType)
-        }
-        
         return .unknown
     }
     

+ 9 - 0
Lanu/Manager/Purchase/LNPurchaseManager.swift

@@ -63,6 +63,15 @@ class LNPurchaseManager {
         LNEventDeliver.addObserver(self)
     }
     
+    func goodsFro(_ code: String) -> (LNCurrencyType, LNPurchaseGoodsVO)? {
+        for (type, list) in goodsCache {
+            if let goods = list.first(where: { $0.code == code }) {
+                return (type, goods)
+            }
+        }
+        return nil
+    }
+    
     func reloadWalletInfo() {
         LNHttpManager.shared.getWalletInfo { [weak self] res, err in
             guard let self else { return }

+ 5 - 3
Lanu/Manager/Purchase/LNPurchaseManagerOld.swift

@@ -121,7 +121,6 @@ final class RechargeManager: NSObject {
     }
     
     // MARK: - 私有方法
-    /// 验证交易收据(本地验证,建议添加服务端验证)
     private func validateReceipt(for transaction: SKPaymentTransaction) {
         // 1. 获取应用收据
         guard let receiptURL = Bundle.main.appStoreReceiptURL,
@@ -137,8 +136,6 @@ final class RechargeManager: NSObject {
             
             let orderId = transaction.payment.applicationUsername ?? LNUserDefaults[.purchaseOrderId, ""]
             
-            // 3. 本地验证逻辑(简化版,生产环境需服务端验证)
-            // 实际应将 receiptString 发送到后端,由后端调用苹果验证接口
             LNHttpManager.shared.verifyPurchase(orderId: orderId, receipt: receiptString) { [weak self] err in
                 guard let self else { return }
                 let success = err == nil
@@ -146,6 +143,11 @@ final class RechargeManager: NSObject {
                     notifyPurchaseResult(err: nil)
                    Log.w("充值成功 - 商品ID: \(transaction.payment.productIdentifier)")
                     LNPurchaseManager.shared.reloadWalletInfo()
+                    
+                    let productId = transaction.payment.productIdentifier
+                    if let (type, goods) = LNPurchaseManager.shared.goodsFro(productId) {
+                        LNStatisticManager.shared.reportPayment(type: type, amount: goods.coinRechargeAmount, currency: goods.currency, price: goods.amount)
+                    }
                } else {
                    notifyPurchaseResult(err: .receiptVerifyFailed)
                    Log.w("充值失败 - 收据验证失败")

+ 122 - 0
Lanu/Manager/Statistic/LNStatisticManager.swift

@@ -8,6 +8,22 @@
 import Foundation
 import UIKit
 import SnapKit
+import AdjustSdk
+
+
+private enum LNStatisticEventToken: String {
+    case call_accept = "5xzpm9"
+    case call_end = "dr9456"
+    case call_start = "y39skp"
+    
+    case first_payment = "8p9mbg"
+    case register = "4vtp6f"
+    case start_chat = "mhhvkj"
+    
+    case view_playmate = "hivhhh"
+    case view_profile = "zfdf34"
+    case view_session = "z9bxom"
+}
 
 
 class LNStatisticManager {
@@ -31,3 +47,109 @@ class LNStatisticManager {
         }
     }
 }
+
+// MARK: 视图埋点
+extension LNStatisticManager {
+    func reportViewPlaymate(uid: String) {
+        let event = eventFor(.view_playmate)
+        event?.addCallbackParameter("user_id", value: uid)
+        Adjust.trackEvent(event)
+    }
+    
+    func reportViewProfile(uid: String) {
+        let event = eventFor(.view_profile)
+        event?.addCallbackParameter("user_id", value: uid)
+        Adjust.trackEvent(event)
+    }
+    
+    func reportViewChat(uid: String) {
+        let event = eventFor(.view_session)
+        event?.addCallbackParameter("user_id", value: uid)
+        Adjust.trackEvent(event)
+    }
+}
+
+// MARK: 点击埋点
+extension LNStatisticManager {
+    func reportClickChat(uid: String) {
+        let event = eventFor(.start_chat)
+        event?.addCallbackParameter("user_id", value: uid)
+        Adjust.trackEvent(event)
+    }
+}
+
+// MARK: 通话埋点
+extension LNStatisticManager {
+    func reportStartCall(uid: String, success: Bool) {
+        let event = eventFor(.call_start)
+        event?.addCallbackParameter("user_id", value: uid)
+        event?.addCallbackParameter("result", value: success ? "1" : "0")
+        Adjust.trackEvent(event)
+    }
+    
+    func reportAcceptCall(uid: String, success: Bool) {
+        let event = eventFor(.call_accept)
+        event?.addCallbackParameter("user_id", value: uid)
+        event?.addCallbackParameter("result", value: success ? "1" : "0")
+        Adjust.trackEvent(event)
+    }
+    
+    func reportEndCall(uid: String, duration: Float) {
+        let event = eventFor(.call_end)
+        event?.addCallbackParameter("user_id", value: uid)
+        event?.addCallbackParameter("duration", value: "\(duration)")
+        Adjust.trackEvent(event)
+    }
+}
+
+// MARK: 登录埋点
+enum LNRegisterMethod: String {
+    case email
+    case phone
+    case google
+    case apple
+}
+
+extension LNStatisticManager {
+    func reportRegister(method: LNRegisterMethod) {
+        let event = eventFor(.register)
+        event?.addCallbackParameter("user_id", value: myUid)
+        event?.addCallbackParameter("register_method", value: method.rawValue)
+        event?.addCallbackParameter("register_ts", value: "\(Int(curTime))")
+        Adjust.trackEvent(event)
+    }
+    
+    func reportPayment(type: LNCurrencyType, amount: Double, currency: String, price: Double) {
+        let event = eventFor(.first_payment)
+        event?.addCallbackParameter("user_id", value: myUid)
+        event?.addCallbackParameter("payment_type", value: "\(type.rawValue)")
+        event?.addCallbackParameter("payment_amount", value: "\(amount)")
+        event?.addCallbackParameter("payment_currency", value: currency)
+        event?.addCallbackParameter("payment_price", value: "\(price)")
+        event?.setRevenue(price, currency: currency)
+        Adjust.trackEvent(event)
+    }
+}
+
+extension LNStatisticManager {
+    private func eventFor(_ token: LNStatisticEventToken) -> ADJEvent? {
+        let event = ADJEvent(eventToken: token.rawValue)
+        event?.addCallbackParameter("platform", value: "iOS")
+        event?.addCallbackParameter("app", value: "gami")
+        event?.addCallbackParameter("app_channel", value: "apple")
+        event?.addCallbackParameter("version_code", value: curBuildVersion)
+        event?.addCallbackParameter("version_name", value: curAppVersion)
+        event?.addCallbackParameter("uid", value: myUid)
+        event?.addCallbackParameter("device_id", value: curDeviceId)
+        event?.addCallbackParameter("country_code", value: LNAppConfig.shared.countryCode)
+        event?.addCallbackParameter("language_code", value: LNAppConfig.shared.curLang.languageCode)
+        event?.addCallbackParameter("device_name", value: curDeviceModelName)
+        event?.addCallbackParameter("os_version", value: curSystemVersion)
+        event?.addCallbackParameter("network_type", value: LNNetworkMonitor.curNetworkType.desc)
+        event?.addCallbackParameter("network_operator", value: LNNetworkMonitor.networkProvider)
+        event?.addCallbackParameter("network_available", value: LNNetworkMonitor.curState == .available ? "true" : "false")
+        event?.addCallbackParameter("background", value: LNAppConfig.shared.isForeground ? "false" : "true")
+        
+        return event
+    }
+}

+ 1 - 0
Lanu/Views/Game/Skill/LNSkillBottomMenuView.swift

@@ -173,6 +173,7 @@ extension LNSkillBottomMenuView {
             guard let self else { return }
             guard let curDetail else { return }
             pushToChat(uid: curDetail.userNo, .locateSkill(id: curDetail.id))
+            LNStatisticManager.shared.reportClickChat(uid: curDetail.userNo)
         }), for: .touchUpInside)
         button.snp.makeConstraints { make in
             make.height.equalTo(47)

+ 1 - 0
Lanu/Views/Game/Skill/LNSkillDetailViewController.swift

@@ -76,6 +76,7 @@ extension LNSkillDetailViewController {
             guard let info else { return }
             if detail == nil {
                 LNStatisticManager.shared.reportVisitor(uid: info.userNo) { _ in }
+                LNStatisticManager.shared.reportViewPlaymate(uid: info.userNo)
             }
             self.detail = info
             

+ 2 - 0
Lanu/Views/IM/Chat/LNIMChatViewController.swift

@@ -62,6 +62,8 @@ class LNIMChatViewController: LNViewController {
         
         loadMessageList()
         updateUserInfo()
+        
+        LNStatisticManager.shared.reportViewChat(uid: viewModel.userId)
     }
     
     func handlerAction(_ action: LNIMChatAction?) {

+ 1 - 0
Lanu/Views/Profile/Profile/LNProfileBottomMenu.swift

@@ -160,6 +160,7 @@ extension LNProfileBottomMenu {
             guard let self else { return }
             guard let curDetail else { return }
             pushToChat(uid: curDetail.userNo)
+            LNStatisticManager.shared.reportClickChat(uid: curDetail.userNo)
         }), for: .touchUpInside)
         chatButton.snp.makeConstraints { make in
             make.height.equalTo(47)

+ 2 - 1
Lanu/Views/Profile/Profile/LNProfileViewController.swift

@@ -56,11 +56,12 @@ class LNProfileViewController: LNViewController {
         
         setupViews()
         
+        LNStatisticManager.shared.reportVisitor(uid: uid) { _ in }
+        LNStatisticManager.shared.reportViewProfile(uid: uid)
         LNProfileManager.shared.getUserProfile(uid: uid) { [weak self] info in
             guard let self else { return }
             guard let info else { return }
             
-            LNStatisticManager.shared.reportVisitor(uid: info.userNo) { _ in }
             self.detail = info
             updateContent()
         }