/// 翻译模式统计数据 class TranslationModeStats { final int apiCalls; // API调用次数 final int sourceCharacters; // 源文本字符数 final int effectiveAudioDuration; // 有效音频时长 final int ttsCharacters; // TTS字符数 final double cost; // 成本 TranslationModeStats({ this.apiCalls = 0, this.sourceCharacters = 0, this.effectiveAudioDuration = 0, this.ttsCharacters = 0, this.cost = 0.0, }); factory TranslationModeStats.fromJson(Map json) { return TranslationModeStats( apiCalls: json['apiCalls'] ?? 0, sourceCharacters: json['sourceCharacters'] ?? 0, effectiveAudioDuration: json['effectiveAudioDuration'] ?? 0, ttsCharacters: json['ttsCharacters'] ?? 0, cost: (json['cost'] ?? 0.0).toDouble(), ); } Map toJson() { return { 'apiCalls': apiCalls, 'sourceCharacters': sourceCharacters, 'effectiveAudioDuration': effectiveAudioDuration, 'ttsCharacters': ttsCharacters, 'cost': cost, }; } TranslationModeStats copyWith({ int? apiCalls, int? sourceCharacters, int? effectiveAudioDuration, int? ttsCharacters, double? cost, }) { return TranslationModeStats( apiCalls: apiCalls ?? this.apiCalls, sourceCharacters: sourceCharacters ?? this.sourceCharacters, effectiveAudioDuration: effectiveAudioDuration ?? this.effectiveAudioDuration, ttsCharacters: ttsCharacters ?? this.ttsCharacters, cost: cost ?? this.cost, ); } } /// 语言对统计数据 class LanguagePairStats { final String languagePair; // 语言对,如"zh-CN→en-US" final int translationCount; // 翻译次数 final int sourceCharacters; // 源文本字符数 final double cost; // 成本 LanguagePairStats({ required this.languagePair, this.translationCount = 0, this.sourceCharacters = 0, this.cost = 0.0, }); factory LanguagePairStats.fromJson(Map json) { return LanguagePairStats( languagePair: json['languagePair'] ?? '', translationCount: json['translationCount'] ?? 0, sourceCharacters: json['sourceCharacters'] ?? 0, cost: (json['cost'] ?? 0.0).toDouble(), ); } Map toJson() { return { 'languagePair': languagePair, 'translationCount': translationCount, 'sourceCharacters': sourceCharacters, 'cost': cost, }; } } /// 小时统计数据 class HourlyStats { final int hour; // 小时(0-23) final int apiCalls; // API调用次数 final int sourceCharacters; // 源文本字符数 final int effectiveAudioDuration; // 有效音频时长 final double cost; // 成本 HourlyStats({ required this.hour, this.apiCalls = 0, this.sourceCharacters = 0, this.effectiveAudioDuration = 0, this.cost = 0.0, }); factory HourlyStats.fromJson(Map json) { return HourlyStats( hour: json['hour'] ?? 0, apiCalls: json['apiCalls'] ?? 0, sourceCharacters: json['sourceCharacters'] ?? 0, effectiveAudioDuration: json['effectiveAudioDuration'] ?? 0, cost: (json['cost'] ?? 0.0).toDouble(), ); } Map toJson() { return { 'hour': hour, 'apiCalls': apiCalls, 'sourceCharacters': sourceCharacters, 'effectiveAudioDuration': effectiveAudioDuration, 'cost': cost, }; } } /// 翻译使用统计数据模型 - 优化版 class TranslationUsageStats { // 基础信息 final String id; // 统计记录ID final DateTime date; // 统计日期 final String userId; // 用户ID final String timeGranularity; // 时间粒度:realtime/hour/day/week/month // API调用统计 - 精确化 final int translationApiCalls; // 翻译API调用次数 final int asrApiCalls; // ASR语音识别调用次数 final int ttsApiCalls; // TTS语音合成调用次数 final int aiSummaryApiCalls; // AI摘要生成调用次数(已废弃但保留兼容性) // 精确计费参数统计 final int sourceTextCharacters; // 源文本字符数(翻译API计费基础) final int effectiveAudioDuration; // ASR有效语音时长(秒,ASR计费基础) final int ttsTextCharacters; // TTS文本字符数(TTS计费基础) final int totalSessionDuration; // 总会话时长(秒,用于分析) // 网络流量统计 final int asrNetworkBytes; // ASR网络流量(字节) final int ttsNetworkBytes; // TTS网络流量(字节) final int translationNetworkBytes; // 翻译API网络流量(字节) // 多维度分类统计 final int sessionCount; // 翻译会话数量 final Map modeStats; // 按翻译模式分类统计 final Map languagePairStats; // 按语言对分类统计 final Map hourlyStats; // 按小时分类统计(仅日统计包含) // 成本相关 final double estimatedCost; // 预估成本 final String currency; // 货币单位 final Map costBreakdown; // 成本分解(ASR/TTS/Translation) TranslationUsageStats({ required this.id, required this.date, required this.userId, this.timeGranularity = 'day', this.translationApiCalls = 0, this.asrApiCalls = 0, this.ttsApiCalls = 0, this.aiSummaryApiCalls = 0, this.sourceTextCharacters = 0, this.effectiveAudioDuration = 0, this.ttsTextCharacters = 0, this.totalSessionDuration = 0, this.asrNetworkBytes = 0, this.ttsNetworkBytes = 0, this.translationNetworkBytes = 0, this.sessionCount = 0, this.modeStats = const {}, this.languagePairStats = const {}, this.hourlyStats = const {}, this.estimatedCost = 0.0, this.currency = 'CNY', this.costBreakdown = const {}, }); factory TranslationUsageStats.fromJson(Map json) { // 处理多维度统计数据的反序列化 final modeStatsMap = {}; if (json['modeStats'] != null) { final modeStatsJson = json['modeStats'] as Map; modeStatsJson.forEach((key, value) { modeStatsMap[key] = TranslationModeStats.fromJson(value); }); } final languagePairStatsMap = {}; if (json['languagePairStats'] != null) { final languagePairStatsJson = json['languagePairStats'] as Map; languagePairStatsJson.forEach((key, value) { languagePairStatsMap[key] = LanguagePairStats.fromJson(value); }); } final hourlyStatsMap = {}; if (json['hourlyStats'] != null) { final hourlyStatsJson = json['hourlyStats'] as Map; hourlyStatsJson.forEach((key, value) { hourlyStatsMap[key] = HourlyStats.fromJson(value); }); } return TranslationUsageStats( id: json['id'] ?? '', date: DateTime.parse(json['date']), userId: json['userId'] ?? '', timeGranularity: json['timeGranularity'] ?? 'day', translationApiCalls: json['translationApiCalls'] ?? 0, asrApiCalls: json['asrApiCalls'] ?? 0, ttsApiCalls: json['ttsApiCalls'] ?? 0, aiSummaryApiCalls: json['aiSummaryApiCalls'] ?? 0, sourceTextCharacters: json['sourceTextCharacters'] ?? json['sourceCharacterCount'] ?? 0, // 兼容旧字段 effectiveAudioDuration: json['effectiveAudioDuration'] ?? json['audioRecordingDuration'] ?? 0, // 兼容旧字段 ttsTextCharacters: json['ttsTextCharacters'] ?? json['targetCharacterCount'] ?? 0, // 兼容旧字段 totalSessionDuration: json['totalSessionDuration'] ?? 0, asrNetworkBytes: json['asrNetworkBytes'] ?? 0, ttsNetworkBytes: json['ttsNetworkBytes'] ?? 0, translationNetworkBytes: json['translationNetworkBytes'] ?? 0, sessionCount: json['sessionCount'] ?? 0, modeStats: modeStatsMap, languagePairStats: languagePairStatsMap, hourlyStats: hourlyStatsMap, estimatedCost: (json['estimatedCost'] ?? 0.0).toDouble(), currency: json['currency'] ?? 'CNY', costBreakdown: Map.from(json['costBreakdown'] ?? {}), ); } Map toJson() { // 序列化多维度统计数据 final modeStatsJson = {}; modeStats.forEach((key, value) { modeStatsJson[key] = value.toJson(); }); final languagePairStatsJson = {}; languagePairStats.forEach((key, value) { languagePairStatsJson[key] = value.toJson(); }); final hourlyStatsJson = {}; hourlyStats.forEach((key, value) { hourlyStatsJson[key] = value.toJson(); }); return { 'id': id, 'date': date.toIso8601String(), 'userId': userId, 'timeGranularity': timeGranularity, 'translationApiCalls': translationApiCalls, 'asrApiCalls': asrApiCalls, 'ttsApiCalls': ttsApiCalls, 'aiSummaryApiCalls': aiSummaryApiCalls, 'sourceTextCharacters': sourceTextCharacters, 'effectiveAudioDuration': effectiveAudioDuration, 'ttsTextCharacters': ttsTextCharacters, 'totalSessionDuration': totalSessionDuration, 'asrNetworkBytes': asrNetworkBytes, 'ttsNetworkBytes': ttsNetworkBytes, 'translationNetworkBytes': translationNetworkBytes, 'sessionCount': sessionCount, 'modeStats': modeStatsJson, 'languagePairStats': languagePairStatsJson, 'hourlyStats': hourlyStatsJson, 'estimatedCost': estimatedCost, 'currency': currency, 'costBreakdown': costBreakdown, // 保持向后兼容性 'sourceCharacterCount': sourceTextCharacters, 'targetCharacterCount': ttsTextCharacters, 'audioRecordingDuration': effectiveAudioDuration, 'ttsPlaybackDuration': totalSessionDuration, }; } /// 创建空的统计记录 factory TranslationUsageStats.empty(String userId) { return TranslationUsageStats( id: DateTime.now().millisecondsSinceEpoch.toString(), date: DateTime.now(), userId: userId, ); } /// 复制并更新统计数据 TranslationUsageStats copyWith({ String? id, DateTime? date, String? userId, String? timeGranularity, int? translationApiCalls, int? asrApiCalls, int? ttsApiCalls, int? aiSummaryApiCalls, int? sourceTextCharacters, int? effectiveAudioDuration, int? ttsTextCharacters, int? totalSessionDuration, int? asrNetworkBytes, int? ttsNetworkBytes, int? translationNetworkBytes, int? sessionCount, Map? modeStats, Map? languagePairStats, Map? hourlyStats, double? estimatedCost, String? currency, Map? costBreakdown, }) { return TranslationUsageStats( id: id ?? this.id, date: date ?? this.date, userId: userId ?? this.userId, timeGranularity: timeGranularity ?? this.timeGranularity, translationApiCalls: translationApiCalls ?? this.translationApiCalls, asrApiCalls: asrApiCalls ?? this.asrApiCalls, ttsApiCalls: ttsApiCalls ?? this.ttsApiCalls, aiSummaryApiCalls: aiSummaryApiCalls ?? this.aiSummaryApiCalls, sourceTextCharacters: sourceTextCharacters ?? this.sourceTextCharacters, effectiveAudioDuration: effectiveAudioDuration ?? this.effectiveAudioDuration, ttsTextCharacters: ttsTextCharacters ?? this.ttsTextCharacters, totalSessionDuration: totalSessionDuration ?? this.totalSessionDuration, asrNetworkBytes: asrNetworkBytes ?? this.asrNetworkBytes, ttsNetworkBytes: ttsNetworkBytes ?? this.ttsNetworkBytes, translationNetworkBytes: translationNetworkBytes ?? this.translationNetworkBytes, sessionCount: sessionCount ?? this.sessionCount, modeStats: modeStats ?? this.modeStats, languagePairStats: languagePairStats ?? this.languagePairStats, hourlyStats: hourlyStats ?? this.hourlyStats, estimatedCost: estimatedCost ?? this.estimatedCost, currency: currency ?? this.currency, costBreakdown: costBreakdown ?? this.costBreakdown, ); } /// 获取总API调用次数 int get totalApiCalls => translationApiCalls + asrApiCalls + ttsApiCalls + aiSummaryApiCalls; /// 获取总字符数(精确计费基础) int get totalCharacterCount => sourceTextCharacters + ttsTextCharacters; /// 获取总音频时长(包含有效时长和总会话时长) int get totalAudioDuration => effectiveAudioDuration + totalSessionDuration; /// 获取总网络流量 int get totalNetworkBytes => asrNetworkBytes + ttsNetworkBytes + translationNetworkBytes; /// 获取最常用的语言对 String get mostUsedLanguagePair { if (languagePairStats.isEmpty) return '无数据'; return languagePairStats.entries .reduce((a, b) => a.value.translationCount > b.value.translationCount ? a : b) .key; } /// 获取最常用的翻译模式 String get mostUsedMode { if (modeStats.isEmpty) return '无数据'; return modeStats.entries .reduce((a, b) => a.value.apiCalls > b.value.apiCalls ? a : b) .key; } // 向后兼容性getter /// @deprecated 使用 sourceTextCharacters 替代 int get sourceCharacterCount => sourceTextCharacters; /// @deprecated 使用 ttsTextCharacters 替代 int get targetCharacterCount => ttsTextCharacters; /// @deprecated 使用 effectiveAudioDuration 替代 int get audioRecordingDuration => effectiveAudioDuration; /// @deprecated 使用 totalSessionDuration 替代 int get ttsPlaybackDuration => totalSessionDuration; /// @deprecated 使用 languagePairStats 替代 Map get languagePairUsage { final result = {}; languagePairStats.forEach((key, value) { result[key] = value.translationCount; }); return result; } /// @deprecated 使用 modeStats 替代 Map get modeUsage { final result = {}; modeStats.forEach((key, value) { result[key] = value.apiCalls; }); return result; } } /// 实时统计数据模型(用于当前会话)- 优化版 class RealtimeUsageStats { // 精确计费参数 int translationCount = 0; // 当前会话翻译次数 int sourceTextCharacters = 0; // 当前会话源文本字符数(翻译计费基础) int ttsTextCharacters = 0; // 当前会话TTS文本字符数(TTS计费基础) int effectiveAudioDuration = 0; // 当前会话有效语音时长(ASR计费基础) int totalSessionDuration = 0; // 当前会话总时长 // 会话信息 String currentLanguagePair = ''; // 当前语言对 String currentMode = ''; // 当前翻译模式 DateTime sessionStartTime = DateTime.now(); // 会话开始时间 /// 重置统计数据 void reset() { translationCount = 0; sourceTextCharacters = 0; ttsTextCharacters = 0; effectiveAudioDuration = 0; totalSessionDuration = 0; currentLanguagePair = ''; currentMode = ''; sessionStartTime = DateTime.now(); } /// 获取会话持续时间(分钟) int get sessionDurationMinutes { return DateTime.now().difference(sessionStartTime).inMinutes; } // 向后兼容性getter /// @deprecated 使用 sourceTextCharacters 替代 int get sourceCharacters => sourceTextCharacters; /// @deprecated 使用 ttsTextCharacters 替代 int get targetCharacters => ttsTextCharacters; /// @deprecated 使用 effectiveAudioDuration 替代 int get recordingDuration => effectiveAudioDuration; /// @deprecated 使用 totalSessionDuration 替代 int get ttsPlayDuration => totalSessionDuration; }