284 lines
10 KiB
Swift
284 lines
10 KiB
Swift
//
|
||
// 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]()
|
||
var allParams = parameters;
|
||
|
||
GuruAnalytics.BuiltinParametersKeys.allCases.forEach { paramKey in
|
||
if let value = allParams.removeValue(forKey: paramKey.rawValue) {
|
||
params[paramKey.rawValue] = normalizeValue(value);
|
||
}
|
||
}
|
||
|
||
var count = 0
|
||
allParams.sorted(by: { $0.key < $1.key }).forEach({ key, value in
|
||
|
||
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
|
||
}
|
||
|
||
params[normalizedKey] = normalizeValue(value)
|
||
|
||
count += 1
|
||
})
|
||
|
||
return params
|
||
}
|
||
|
||
static func normalizeValue(_ value: Any) -> EventValue {
|
||
|
||
var preprocessedValue = value
|
||
|
||
if let val = preprocessedValue as? NSNumber {
|
||
preprocessedValue = val.numricValue
|
||
}
|
||
|
||
let eventValue: EventValue
|
||
if let value = preprocessedValue as? String {
|
||
eventValue = Entity.EventValue(stringValue: normalizeStringValue(String(value.prefix(maxParameterStringValueLength))))
|
||
} else if let value = preprocessedValue as? Int {
|
||
eventValue = Entity.EventValue(longValue: Int64(value))
|
||
} else if let value = preprocessedValue as? Int64 {
|
||
eventValue = Entity.EventValue(longValue: value)
|
||
} else if let value = preprocessedValue as? Double {
|
||
eventValue = Entity.EventValue(doubleValue: value)
|
||
} else {
|
||
eventValue = Entity.EventValue(stringValue: normalizeStringValue(String("\(value)".prefix(maxParameterStringValueLength))))
|
||
}
|
||
return eventValue
|
||
}
|
||
|
||
static func normalizeStringValue(_ value: String) -> String {
|
||
let normalizedString = value.replacingOccurrences(of: "'", with: "''")
|
||
cdPrint("original string value: \(value)")
|
||
cdPrint("normalized string value: \(normalizedString)")
|
||
return normalizedString
|
||
}
|
||
|
||
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?
|
||
///appsFlyerId
|
||
let appsflyerId: String?
|
||
|
||
///IDFV
|
||
let vendorId: String? = UIDevice().identifierForVendor?.uuidString
|
||
|
||
enum CodingKeys: String, CodingKey {
|
||
case deviceId
|
||
case uid
|
||
case adjustId
|
||
case adId
|
||
case firebaseId
|
||
case vendorId
|
||
case appsflyerId
|
||
}
|
||
}
|
||
|
||
// 参数对应的值
|
||
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
|
||
}
|
||
}
|
||
|
||
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)
|
||
}
|
||
}
|
||
}
|
||
|