Initial commit: 91dj android client dev_1.0.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
谢宇宁
2026-10-08 11:37:25 +07:00
co-authored by Claude Opus 5.5
commit 97dd6a739a
1262 changed files with 119813 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
/// API 配置管理类:统一管理所有 API 端点路径
class ApiConfig {
ApiConfig._();
// ==================== API 路径(固定不变)====================
/// 上报接口路径(固定)
static const String defaultReportPath = '/api/eventTracking/batchReport.json';
/// 上报接口路径
static String get reportPath => defaultReportPath;
// ==================== 配置方法 ====================
/// 构建完整的 URL
/// [baseDomain] 基础域名,如 'https://api.example.com'
/// [path] API 路径,如 '/api/analytics/report'
/// [appId] 应用ID(可选),如果提供会作为查询参数添加到URL中
/// 返回完整的 URL,如 'https://api.example.com/api/analytics/report?appId=xxx'
static String buildUrl(String baseDomain, String path, {String? appId}) {
// 确保路径以 / 开头
final normalizedPath = _normalizePath(path);
// 解析基础域名
final uri = Uri.parse(baseDomain);
final scheme = uri.scheme.isEmpty ? 'https' : uri.scheme;
final host = uri.host.isEmpty ? baseDomain : uri.host;
// 保留非默认端口(如 :8080),避免 IP+端口 场景下端口丢失
final defaultPort = scheme == 'https' ? 443 : 80;
final portSuffix =
(uri.port > 0 && uri.port != defaultPort) ? ':${uri.port}' : '';
// 保留 baseDomain 中的路径前缀(如 /v2),仅在 host 成功解析时使用
// 去掉末尾 / 避免与 normalizedPath 拼出双斜杠
final basePath = uri.host.isNotEmpty && uri.path.isNotEmpty && uri.path != '/'
? (uri.path.endsWith('/')
? uri.path.substring(0, uri.path.length - 1)
: uri.path)
: '';
// 构建完整 URL
String url = '$scheme://$host$portSuffix$basePath$normalizedPath';
// 如果提供了 appId,添加到查询参数
if (appId != null && appId.isNotEmpty) {
url = _addQueryParameter(url, 'appId', appId);
}
return url;
}
/// 规范化路径:确保路径以 / 开头
static String _normalizePath(String path) {
if (path.isEmpty) {
return '/';
}
return path.startsWith('/') ? path : '/$path';
}
/// 获取上报接口 URL
/// [baseDomain] 基础域名
/// [appId] 应用ID(可选,如果提供会作为查询参数)
static String getReportUrl(String baseDomain, {String? appId}) {
return buildUrl(baseDomain, reportPath, appId: appId);
}
/// 添加查询参数到 URL
/// [url] 原始 URL
/// [key] 参数名
/// [value] 参数值
static String _addQueryParameter(String url, String key, String value) {
try {
final uri = Uri.parse(url);
final queryParams = Map<String, String>.from(uri.queryParameters);
queryParams[key] = value;
return uri.replace(queryParameters: queryParams).toString();
} catch (e) {
// 如果 URL 解析失败,直接拼接
final separator = url.contains('?') ? '&' : '?';
return '$url$separator$key=${Uri.encodeComponent(value)}';
}
}
}
+334
View File
@@ -0,0 +1,334 @@
/// SDK 配置管理类:统一管理所有 SDK 相关的配置常量
class SdkConfig {
SdkConfig._();
// ==================== 默认值常量(单一来源)====================
// 字段声明和 reset() 均引用这些常量,避免三处重复。
static const int _defaultMaxQueueSize = 5000;
static const int _defaultAutoUploadThreshold = 10;
static const int _defaultMaxBatchSize = 500;
static const int _defaultIsolateEncodingThreshold = 100;
static const Duration _defaultUploadInterval = Duration(seconds: 5);
static const Duration _defaultPageLoadTimeout = Duration(seconds: 5);
static const Duration _defaultClickThrottleDuration =
Duration(milliseconds: 300);
static const Duration _defaultConnectionTimeout = Duration(seconds: 10);
static const Duration _defaultReadTimeout = Duration(seconds: 30);
static const Duration _defaultSpeedTestTimeout = Duration(seconds: 5);
static const Duration _defaultConfigRequestTimeout = Duration(seconds: 10);
static const int _defaultMaxBackoffLevel = 5;
static const Duration _defaultBaseRetryDelay = Duration(seconds: 2);
static const Duration _defaultMaxRetryDelay = Duration(seconds: 60);
static const String _defaultCacheFileName = 'data_plus_events_cache.jsonl';
static const String _defaultDebugReportEventLogFileName =
'data_plus_report_events_debug.jsonl';
static const bool _defaultEnableDebugReportLog = false;
static const String _defaultEventTypeConfigCacheFileName =
'analytics_event_types_config.json';
static const String _defaultDomainCacheFileName =
'data_plus_domains_cache.json';
static const int _defaultMaxCacheLines = 2000;
static const int _defaultMaxCacheBytes = 2 * 1024 * 1024;
static const int _defaultTombstoneCompactionThreshold = 200;
static const String _defaultTombstoneFileName =
'data_plus_events_tombstone.txt';
static const int _defaultMaxJsonSize = 5 * 1024 * 1024;
static const int _defaultMaxSingleEventSize = 300 * 1024;
static const int _defaultMaxConfigResponseSize = 1 * 1024 * 1024;
static const int _defaultMaxPageStackSize = 100;
static const int _defaultMaxAdImpressionCapacity = 10000;
static const int _defaultCacheBufferThreshold = 10;
static const Duration _defaultCacheFlushInterval = Duration(seconds: 5);
// ==================== 事件队列配置 ====================
/// 事件队列最大容量限制,防止内存无限增长
static int maxQueueSize = _defaultMaxQueueSize;
/// 自动上报阈值:队列达到此数量时立即触发上报
static int autoUploadThreshold = _defaultAutoUploadThreshold;
/// 单次上报的最大事件数量限制,避免请求过大导致超时
static int maxBatchSize = _defaultMaxBatchSize;
/// JSON 编码的批次大小阈值,超过此大小使用 isolate 编码
static int isolateEncodingThreshold = _defaultIsolateEncodingThreshold;
// ==================== 定时器配置 ====================
/// 定时上报间隔(秒)
static Duration uploadInterval = _defaultUploadInterval;
/// 页面加载超时时间(秒)
static Duration pageLoadTimeout = _defaultPageLoadTimeout;
/// 点击事件节流时间(毫秒)
static Duration clickThrottleDuration = _defaultClickThrottleDuration;
// ==================== 网络超时配置 ====================
/// HTTP 连接超时时间
static Duration connectionTimeout = _defaultConnectionTimeout;
/// HTTP 读取超时时间
static Duration readTimeout = _defaultReadTimeout;
/// HTTP 写入超时时间(与连接超时相同)
static Duration get writeTimeout => connectionTimeout;
/// HTTP 总超时时间(连接 + 写入 + 读取)
static Duration get totalRequestTimeout =>
connectionTimeout + writeTimeout + readTimeout;
/// 域名测速超时时间
static Duration speedTestTimeout = _defaultSpeedTestTimeout;
/// 配置请求超时时间
static Duration configRequestTimeout = _defaultConfigRequestTimeout;
// ==================== 加密配置 ====================
/// AES-GCM 解密密钥(固定密钥,用于 decryptResponseAuto 等)
static const String decryptKey =
'en1BNo0VBrN/zi+mI2LO7E9W40ehCBYwC+frBn8s3rQ';
// ==================== 重试配置 ====================
/// 最大退避级别
static int maxBackoffLevel = _defaultMaxBackoffLevel;
/// 基础延迟时间
static Duration baseRetryDelay = _defaultBaseRetryDelay;
/// 最大延迟时间
static Duration maxRetryDelay = _defaultMaxRetryDelay;
// ==================== 缓存配置 ====================
/// 事件缓存文件名
static String cacheFileName = _defaultCacheFileName;
/// Debug 包完整事件 JSONL 文件名(Native 平台 applicationDocumentsDirectory 下)
static String debugReportEventLogFileName =
_defaultDebugReportEventLogFileName;
/// 是否启用调试事件日志。
///
/// 默认关闭;开启后才会本地记录上传事件完整 JSONL,并打印 accepted event 完整 JSON。
static bool enableDebugReportLog = _defaultEnableDebugReportLog;
/// 事件类型配置缓存文件名
static String eventTypeConfigCacheFileName =
_defaultEventTypeConfigCacheFileName;
/// 域名列表缓存文件名
static String domainCacheFileName = _defaultDomainCacheFileName;
/// 最大缓存行数限制,防止文件过大导致内存问题
/// 默认 2000 行,避免在网络不稳定时生成过大的缓存文件
static int maxCacheLines = _defaultMaxCacheLines;
/// 缓存文件最大字节数限制,超过后从文件开头裁剪旧事件
/// 默认 2MB
static int maxCacheBytes = _defaultMaxCacheBytes;
/// tombstone 触发压缩的 event_id 数量阈值
/// 默认 200:每积累 200 条已上报 ID 就压缩一次主缓存文件
static int tombstoneCompactionThreshold =
_defaultTombstoneCompactionThreshold;
/// tombstone 缓存文件名
static String tombstoneFileName = _defaultTombstoneFileName;
/// 持久化 buffer 攒批阈值(达到此数量立即写盘)
static int cacheBufferThreshold = _defaultCacheBufferThreshold;
/// 持久化 buffer 定时刷写间隔
static Duration cacheFlushInterval = _defaultCacheFlushInterval;
// ==================== 请求大小限制 ====================
/// JSON 最大大小限制(5MB)
static int maxJsonSize = _defaultMaxJsonSize;
/// 单条事件最大字节数限制,超过则丢弃(300KB)
static int maxSingleEventSize = _defaultMaxSingleEventSize;
/// 配置接口响应体最大字节数限制(1MB),防止恶意/异常服务器返回超大数据导致 OOM
static int maxConfigResponseSize = _defaultMaxConfigResponseSize;
// ==================== 容量配置 ====================
/// 页面栈最大容量
static int maxPageStackSize = _defaultMaxPageStackSize;
/// 广告去重最大容量
static int maxAdImpressionCapacity = _defaultMaxAdImpressionCapacity;
// ==================== 配置方法 ====================
/// 配置所有 SDK 参数
///
/// 示例:
/// ```dart
/// SdkConfig.configure(
/// maxQueueSize: 20000,
/// uploadInterval: Duration(seconds: 30),
/// connectionTimeout: Duration(seconds: 15),
/// );
/// ```
static void configure({
int? maxQueueSize,
int? autoUploadThreshold,
int? maxBatchSize,
int? isolateEncodingThreshold,
Duration? uploadInterval,
Duration? pageLoadTimeout,
Duration? clickThrottleDuration,
Duration? connectionTimeout,
Duration? readTimeout,
Duration? speedTestTimeout,
Duration? configRequestTimeout,
int? maxBackoffLevel,
Duration? baseRetryDelay,
Duration? maxRetryDelay,
String? cacheFileName,
String? debugReportEventLogFileName,
bool? enableDebugReportLog,
String? eventTypeConfigCacheFileName,
String? domainCacheFileName,
int? maxCacheLines,
int? maxCacheBytes,
int? maxJsonSize,
int? maxSingleEventSize,
int? maxPageStackSize,
int? maxAdImpressionCapacity,
}) {
if (maxQueueSize != null && maxQueueSize > 0) {
SdkConfig.maxQueueSize = maxQueueSize;
}
if (autoUploadThreshold != null && autoUploadThreshold > 0) {
SdkConfig.autoUploadThreshold = autoUploadThreshold;
}
if (maxBatchSize != null && maxBatchSize > 0) {
SdkConfig.maxBatchSize = maxBatchSize;
}
if (isolateEncodingThreshold != null && isolateEncodingThreshold > 0) {
SdkConfig.isolateEncodingThreshold = isolateEncodingThreshold;
}
if (uploadInterval != null) {
SdkConfig.uploadInterval = uploadInterval;
}
if (pageLoadTimeout != null) {
SdkConfig.pageLoadTimeout = pageLoadTimeout;
}
if (clickThrottleDuration != null) {
SdkConfig.clickThrottleDuration = clickThrottleDuration;
}
if (connectionTimeout != null) {
SdkConfig.connectionTimeout = connectionTimeout;
}
if (readTimeout != null) {
SdkConfig.readTimeout = readTimeout;
}
if (speedTestTimeout != null) {
SdkConfig.speedTestTimeout = speedTestTimeout;
}
if (configRequestTimeout != null) {
SdkConfig.configRequestTimeout = configRequestTimeout;
}
if (maxBackoffLevel != null && maxBackoffLevel >= 0) {
SdkConfig.maxBackoffLevel = maxBackoffLevel;
}
if (baseRetryDelay != null) {
SdkConfig.baseRetryDelay = baseRetryDelay;
}
if (maxRetryDelay != null) {
SdkConfig.maxRetryDelay = maxRetryDelay;
}
if (cacheFileName != null &&
cacheFileName.isNotEmpty &&
!cacheFileName.contains('/') &&
!cacheFileName.contains('\\') &&
!cacheFileName.contains('..')) {
SdkConfig.cacheFileName = cacheFileName;
}
if (debugReportEventLogFileName != null &&
debugReportEventLogFileName.isNotEmpty &&
!debugReportEventLogFileName.contains('/') &&
!debugReportEventLogFileName.contains('\\') &&
!debugReportEventLogFileName.contains('..')) {
SdkConfig.debugReportEventLogFileName = debugReportEventLogFileName;
}
if (enableDebugReportLog != null) {
SdkConfig.enableDebugReportLog = enableDebugReportLog;
}
if (eventTypeConfigCacheFileName != null &&
eventTypeConfigCacheFileName.isNotEmpty &&
!eventTypeConfigCacheFileName.contains('/') &&
!eventTypeConfigCacheFileName.contains('\\') &&
!eventTypeConfigCacheFileName.contains('..')) {
SdkConfig.eventTypeConfigCacheFileName = eventTypeConfigCacheFileName;
}
if (domainCacheFileName != null &&
domainCacheFileName.isNotEmpty &&
!domainCacheFileName.contains('/') &&
!domainCacheFileName.contains('\\') &&
!domainCacheFileName.contains('..')) {
SdkConfig.domainCacheFileName = domainCacheFileName;
}
if (maxCacheLines != null && maxCacheLines > 0) {
SdkConfig.maxCacheLines = maxCacheLines;
}
if (maxCacheBytes != null && maxCacheBytes > 0) {
SdkConfig.maxCacheBytes = maxCacheBytes;
}
if (maxJsonSize != null && maxJsonSize > 0) {
SdkConfig.maxJsonSize = maxJsonSize;
}
if (maxSingleEventSize != null && maxSingleEventSize > 0) {
SdkConfig.maxSingleEventSize = maxSingleEventSize;
}
if (maxPageStackSize != null && maxPageStackSize > 0) {
SdkConfig.maxPageStackSize = maxPageStackSize;
}
if (maxAdImpressionCapacity != null && maxAdImpressionCapacity > 0) {
SdkConfig.maxAdImpressionCapacity = maxAdImpressionCapacity;
}
}
/// 重置所有配置为默认值
static void reset() {
maxQueueSize = _defaultMaxQueueSize;
autoUploadThreshold = _defaultAutoUploadThreshold;
maxBatchSize = _defaultMaxBatchSize;
isolateEncodingThreshold = _defaultIsolateEncodingThreshold;
uploadInterval = _defaultUploadInterval;
pageLoadTimeout = _defaultPageLoadTimeout;
clickThrottleDuration = _defaultClickThrottleDuration;
connectionTimeout = _defaultConnectionTimeout;
readTimeout = _defaultReadTimeout;
speedTestTimeout = _defaultSpeedTestTimeout;
configRequestTimeout = _defaultConfigRequestTimeout;
maxBackoffLevel = _defaultMaxBackoffLevel;
baseRetryDelay = _defaultBaseRetryDelay;
maxRetryDelay = _defaultMaxRetryDelay;
cacheFileName = _defaultCacheFileName;
debugReportEventLogFileName = _defaultDebugReportEventLogFileName;
enableDebugReportLog = _defaultEnableDebugReportLog;
eventTypeConfigCacheFileName = _defaultEventTypeConfigCacheFileName;
domainCacheFileName = _defaultDomainCacheFileName;
maxCacheLines = _defaultMaxCacheLines;
maxCacheBytes = _defaultMaxCacheBytes;
tombstoneCompactionThreshold = _defaultTombstoneCompactionThreshold;
tombstoneFileName = _defaultTombstoneFileName;
cacheBufferThreshold = _defaultCacheBufferThreshold;
cacheFlushInterval = _defaultCacheFlushInterval;
maxJsonSize = _defaultMaxJsonSize;
maxSingleEventSize = _defaultMaxSingleEventSize;
maxConfigResponseSize = _defaultMaxConfigResponseSize;
maxPageStackSize = _defaultMaxPageStackSize;
maxAdImpressionCapacity = _defaultMaxAdImpressionCapacity;
}
}