| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  | // | 
					
						
							|  |  |  |  | //  DBEntities.swift | 
					
						
							|  |  |  |  | //  Alamofire | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | //  Created by mayue on 2022/11/4. | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | import Foundation | 
					
						
							|  |  |  |  | import CryptoSwift | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | internal enum Entity { | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | extension Entity { | 
					
						
							|  |  |  |  |     struct EventRecord: Codable { | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         enum Priority: Int, Codable { | 
					
						
							|  |  |  |  |             case EMERGENCE = 0 | 
					
						
							|  |  |  |  |             case HIGH = 5 | 
					
						
							|  |  |  |  |             case DEFAULT = 10 | 
					
						
							|  |  |  |  |             case LOW = 15 | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         enum TransitionStatus: Int, Codable { | 
					
						
							|  |  |  |  |             case idle = 0 | 
					
						
							|  |  |  |  |             case instransition = 1 | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         let recordId: String | 
					
						
							|  |  |  |  |         let eventName: String | 
					
						
							|  |  |  |  |         let eventJson: String | 
					
						
							|  |  |  |  |         ///单位毫秒 | 
					
						
							|  |  |  |  |         let timestamp: Int64 | 
					
						
							|  |  |  |  |         let priority: Priority | 
					
						
							|  |  |  |  |         let transitionStatus: TransitionStatus | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         init(eventName: String, event: Entity.Event, priority: Priority = .DEFAULT, transitionStatus: TransitionStatus = .idle) { | 
					
						
							|  |  |  |  |             let now = Date() | 
					
						
							|  |  |  |  |             let eventJson = event.asString ?? "" | 
					
						
							|  |  |  |  |             if eventJson.isEmpty { | 
					
						
							|  |  |  |  |                 cdPrint("[WARNING] error for convert event to json") | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             self.recordId = "\(eventName)\(eventJson)\(now.timeIntervalSince1970)\(Int.random(in: Int.min...Int.max))".md5() | 
					
						
							|  |  |  |  |             self.eventName = eventName | 
					
						
							|  |  |  |  |             self.eventJson = eventJson | 
					
						
							|  |  |  |  |             self.timestamp = event.timestamp | 
					
						
							|  |  |  |  |             self.priority = priority | 
					
						
							|  |  |  |  |             self.transitionStatus = transitionStatus | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         init(recordId: String, eventName: String, eventJson: String, timestamp: Int64, priority: Int, transitionStatus: Int) { | 
					
						
							|  |  |  |  |             self.recordId = recordId | 
					
						
							|  |  |  |  |             self.eventName = eventName | 
					
						
							|  |  |  |  |             self.eventJson = eventJson | 
					
						
							|  |  |  |  |             self.timestamp = timestamp | 
					
						
							|  |  |  |  |             self.priority = .init(rawValue: priority) ?? .DEFAULT | 
					
						
							|  |  |  |  |             self.transitionStatus = .init(rawValue: transitionStatus) ?? .idle | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         enum CodingKeys: String, CodingKey { | 
					
						
							|  |  |  |  |             case recordId | 
					
						
							|  |  |  |  |             case eventName | 
					
						
							|  |  |  |  |             case eventJson | 
					
						
							|  |  |  |  |             case timestamp | 
					
						
							|  |  |  |  |             case priority | 
					
						
							|  |  |  |  |             case transitionStatus | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         static func createTableSql(with name: String) -> String { | 
					
						
							|  |  |  |  |             return """
 | 
					
						
							|  |  |  |  |                 CREATE TABLE IF NOT EXISTS \(name)( | 
					
						
							|  |  |  |  |                 \(CodingKeys.recordId.rawValue) TEXT UNIQUE NOT NULL PRIMARY KEY, | 
					
						
							|  |  |  |  |                 \(CodingKeys.eventName.rawValue) TEXT NOT NULL, | 
					
						
							|  |  |  |  |                 \(CodingKeys.eventJson.rawValue) TEXT NOT NULL, | 
					
						
							|  |  |  |  |                 \(CodingKeys.timestamp.rawValue) INTEGER, | 
					
						
							|  |  |  |  |                 \(CodingKeys.priority.rawValue) INTEGER, | 
					
						
							|  |  |  |  |                 \(CodingKeys.transitionStatus.rawValue) INTEGER); | 
					
						
							|  |  |  |  |                 """
 | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         func insertSql(to tableName: String) -> String { | 
					
						
							|  |  |  |  |             return "INSERT INTO \(tableName) VALUES ('\(recordId)', '\(eventName)', '\(eventJson)', '\(timestamp)', '\(priority.rawValue)', '\(transitionStatus.rawValue)')" | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | extension Entity { | 
					
						
							|  |  |  |  |     struct Event: Codable { | 
					
						
							|  |  |  |  |         ///客户端中记录此事件的时间(采用世界协调时间,毫秒为单位) | 
					
						
							|  |  |  |  |         let timestamp: Int64 | 
					
						
							|  |  |  |  |         let event: String | 
					
						
							|  |  |  |  |         let userInfo: UserInfo | 
					
						
							|  |  |  |  |         let param: [String: EventValue] | 
					
						
							|  |  |  |  |         let properties: [String: String] | 
					
						
							|  |  |  |  |         let eventId: String | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         enum CodingKeys: String, CodingKey { | 
					
						
							|  |  |  |  |             case timestamp | 
					
						
							|  |  |  |  |             case userInfo = "info" | 
					
						
							|  |  |  |  |             case event, param, properties | 
					
						
							|  |  |  |  |             case eventId | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         init(timestamp: Int64, event: String, userInfo: UserInfo, parameters: [String : Any], properties: [String : String]) throws { | 
					
						
							|  |  |  |  |             guard let normalizedEvent = Self.normalizeKey(event), | 
					
						
							|  |  |  |  |                   normalizedEvent == event else { | 
					
						
							|  |  |  |  |                 cdPrint("drop event because of illegal event name: \(event)") | 
					
						
							|  |  |  |  |                 cdPrint("standard: https://developers.google.com/android/reference/com/google/firebase/analytics/FirebaseAnalytics.Event") | 
					
						
							|  |  |  |  |                 throw NSError(domain: "cunstrcting event error", code: 0, userInfo: [NSLocalizedDescriptionKey : "illegal event name: \(event)"]) | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             self.eventId = UUID().uuidString.lowercased() | 
					
						
							|  |  |  |  |             self.timestamp = timestamp | 
					
						
							|  |  |  |  |             self.event = normalizedEvent | 
					
						
							|  |  |  |  |             self.userInfo = userInfo | 
					
						
							|  |  |  |  |             self.param = Self.normalizeParameters(parameters) | 
					
						
							|  |  |  |  |             self.properties = properties | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         static let maxParametersCount = 25 | 
					
						
							|  |  |  |  |         static let maxKeyLength = 40 | 
					
						
							|  |  |  |  |         static let maxParameterStringValueLength = 128 | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         static func normalizeParameters(_ parameters: [String : Any]) -> [String : EventValue] { | 
					
						
							|  |  |  |  |             var params = [String : EventValue]() | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |             var allParams = parameters; | 
					
						
							|  |  |  |  |              | 
					
						
							|  |  |  |  |             GuruAnalytics.BuiltinParametersKeys.allCases.forEach { paramKey in | 
					
						
							|  |  |  |  |                 if let value = allParams.removeValue(forKey: paramKey.rawValue) { | 
					
						
							|  |  |  |  |                     params[paramKey.rawValue] = normalizeValue(value); | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |              | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |             var count = 0 | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |             allParams.sorted(by: { $0.key < $1.key }).forEach({ key, value in | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |                  | 
					
						
							|  |  |  |  |                 guard count < maxParametersCount else { | 
					
						
							|  |  |  |  |                     cdPrint("too many parameters") | 
					
						
							|  |  |  |  |                     cdPrint("standard: https://developers.google.com/android/reference/com/google/firebase/analytics/FirebaseAnalytics.Event") | 
					
						
							|  |  |  |  |                     return | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |                  | 
					
						
							|  |  |  |  |                 guard let normalizedKey = normalizeKey(key), | 
					
						
							|  |  |  |  |                       normalizedKey == key else { | 
					
						
							|  |  |  |  |                     cdPrint("drop event parameter because of illegal key: \(key)") | 
					
						
							|  |  |  |  |                     cdPrint("standard: https://developers.google.com/android/reference/com/google/firebase/analytics/FirebaseAnalytics.Event") | 
					
						
							|  |  |  |  |                     return | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |                  | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |                 params[normalizedKey] = normalizeValue(value) | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |                  | 
					
						
							|  |  |  |  |                 count += 1 | 
					
						
							|  |  |  |  |             }) | 
					
						
							|  |  |  |  |              | 
					
						
							|  |  |  |  |             return params | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |         static func normalizeValue(_ value: Any) -> EventValue { | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |              | 
					
						
							|  |  |  |  |             var preprocessedValue = value | 
					
						
							|  |  |  |  |              | 
					
						
							|  |  |  |  |             if let val = preprocessedValue as? NSNumber { | 
					
						
							|  |  |  |  |                 preprocessedValue = val.numricValue | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |              | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |             let eventValue: EventValue | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |             if let value = preprocessedValue as? String { | 
					
						
							|  |  |  |  |                 eventValue = Entity.EventValue(stringValue: normalizeStringValue(String(value.prefix(maxParameterStringValueLength)))) | 
					
						
							|  |  |  |  |             } else if let value = preprocessedValue as? Int { | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |                 eventValue = Entity.EventValue(longValue: Int64(value)) | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |             } else if let value = preprocessedValue as? Int64 { | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |                 eventValue = Entity.EventValue(longValue: value) | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |             } else if let value = preprocessedValue as? Double { | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |                 eventValue = Entity.EventValue(doubleValue: value) | 
					
						
							|  |  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |                 eventValue = Entity.EventValue(stringValue: normalizeStringValue(String("\(value)".prefix(maxParameterStringValueLength)))) | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |             } | 
					
						
							|  |  |  |  |             return eventValue | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |         static func normalizeStringValue(_ value: String) -> String { | 
					
						
							|  |  |  |  |             let normalizedString = value.replacingOccurrences(of: "'", with: "''") | 
					
						
							|  |  |  |  |             cdPrint("original string value: \(value)") | 
					
						
							|  |  |  |  |             cdPrint("normalized string value: \(normalizedString)") | 
					
						
							|  |  |  |  |             return normalizedString | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |         static func normalizeKey(_ key: String) -> String? { | 
					
						
							|  |  |  |  |             var mutableKey = key | 
					
						
							|  |  |  |  |              | 
					
						
							|  |  |  |  |             while let first = mutableKey.first, | 
					
						
							|  |  |  |  |                     !first.isLetter { | 
					
						
							|  |  |  |  |                 _ = mutableKey.removeFirst() | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |              | 
					
						
							|  |  |  |  |             var normalizedKey = "" | 
					
						
							|  |  |  |  |             var count = 0 | 
					
						
							|  |  |  |  |             mutableKey.forEach { c in | 
					
						
							|  |  |  |  |                 guard count < maxKeyLength, | 
					
						
							|  |  |  |  |                       c.isAlphabetic || c.isDigit || c == "_" else { return } | 
					
						
							|  |  |  |  |                 normalizedKey.append(c) | 
					
						
							|  |  |  |  |                 count += 1 | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |              | 
					
						
							|  |  |  |  |             return normalizedKey.isEmpty ? nil : normalizedKey | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     ///用户信息 | 
					
						
							|  |  |  |  |     struct UserInfo: Codable { | 
					
						
							|  |  |  |  |         ///中台ID。只在未获取到uid时可以为空 | 
					
						
							|  |  |  |  |         let uid: String? | 
					
						
							|  |  |  |  |         ///设备ID(用户的设备ID,iOS取用户的IDFV或UUID,Android取androidID) | 
					
						
							|  |  |  |  |         let deviceId: String? | 
					
						
							|  |  |  |  |         ///adjust_id。只在未获取到adjust时可以为空 | 
					
						
							|  |  |  |  |         let adjustId: String? | 
					
						
							|  |  |  |  |         ///广告 ID/广告标识符 (IDFA) | 
					
						
							|  |  |  |  |         let adId: String? | 
					
						
							|  |  |  |  |         ///用户的pseudo_id | 
					
						
							|  |  |  |  |         let firebaseId: String? | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |         ///appsFlyerId | 
					
						
							|  |  |  |  |         let appsflyerId: String? | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |          | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |         ///IDFV | 
					
						
							|  |  |  |  |         let vendorId: String? = UIDevice().identifierForVendor?.uuidString | 
					
						
							|  |  |  |  |          | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |         enum CodingKeys: String, CodingKey { | 
					
						
							|  |  |  |  |             case deviceId | 
					
						
							|  |  |  |  |             case uid | 
					
						
							|  |  |  |  |             case adjustId | 
					
						
							|  |  |  |  |             case adId | 
					
						
							|  |  |  |  |             case firebaseId | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  |             case vendorId | 
					
						
							| 
									
										
										
										
											2025-08-08 10:12:14 +00:00
										 |  |  |  |             case appsflyerId | 
					
						
							| 
									
										
										
										
											2024-09-04 05:01:33 +00:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     // 参数对应的值 | 
					
						
							|  |  |  |  |     struct EventValue: Codable { | 
					
						
							|  |  |  |  |         let stringValue: String?    // 事件参数的字符串值 | 
					
						
							|  |  |  |  |         let longValue: Int64?         // 事件参数的整数值 | 
					
						
							|  |  |  |  |         let doubleValue: Double?    // 事件参数的小数值。注意:APP序列化成JSON时,注意不要序列化成科学计数法 | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         init(stringValue: String? = nil, longValue: Int64? = nil, doubleValue: Double? = nil) { | 
					
						
							|  |  |  |  |             self.stringValue = stringValue | 
					
						
							|  |  |  |  |             self.longValue = longValue | 
					
						
							|  |  |  |  |             self.doubleValue = doubleValue | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         enum CodingKeys: String, CodingKey { | 
					
						
							|  |  |  |  |             case stringValue = "s" | 
					
						
							|  |  |  |  |             case longValue = "i" | 
					
						
							|  |  |  |  |             case doubleValue = "d" | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | extension Entity { | 
					
						
							|  |  |  |  |     struct SystemTimeResult: Codable { | 
					
						
							|  |  |  |  |         let data: Int64 | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-09-23 02:53:09 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | extension Entity { | 
					
						
							|  |  |  |  |     struct Session { | 
					
						
							|  |  |  |  |         let id: UUID = UUID() | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         var sessionId: Int { | 
					
						
							|  |  |  |  |             return id.uuidString.hash | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     struct SessionNumber: Codable { | 
					
						
							|  |  |  |  |         var number: Int | 
					
						
							|  |  |  |  |         let createdAtMs: Int64 | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         static func createNumber() -> SessionNumber { | 
					
						
							|  |  |  |  |             return SessionNumber(number: 0, createdAtMs: Date().msSince1970) | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 |