import 'dart:convert'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import '../models/translation_usage_stats.dart'; import '../../core/utils/logger.dart'; /// 使用量统计服务 class UsageStatsService extends GetxService { static const String _storageKey = 'usage_stats'; static const String _dailyStatsKey = 'daily_translation_stats'; final GetStorage _storage = GetStorage(); // 实时统计数据 final Rx realtimeStats = RealtimeUsageStats().obs; // 当日统计数据 final Rx todayStats = TranslationUsageStats.empty('').obs; @override void onInit() { super.onInit(); _initializeService(); } /// 初始化服务 void _initializeService() { _loadTodayStats(); Logger.info('lxm--使用量统计服务初始化完成'); } /// 加载当日统计数据 void _loadTodayStats() { try { final today = DateTime.now(); final todayKey = '${today.year}-${today.month.toString().padLeft(2, '0')}-${today.day.toString().padLeft(2, '0')}'; final statsData = _storage.read('${_dailyStatsKey}_$todayKey'); if (statsData != null) { todayStats.value = TranslationUsageStats.fromJson( Map.from(statsData)); } else { todayStats.value = TranslationUsageStats.empty(getCurrentUserId()); } } catch (e) { Logger.error('加载当日统计数据失败: $e'); todayStats.value = TranslationUsageStats.empty(getCurrentUserId()); } } /// 获取当前用户ID String getCurrentUserId() { try { final userInfo = _storage.read('user_info'); if (userInfo != null && userInfo['user'] != null) { return userInfo['user']['uid'] ?? 'anonymous'; } } catch (e) { Logger.warning('获取用户ID失败: $e'); } return 'anonymous'; } /// 记录翻译API调用 void recordTranslationApiCall({ required String sourceText, required String targetText, required String sourceLanguage, required String targetLanguage, required String mode, }) { try { // 更新实时统计 realtimeStats.value.translationCount++; realtimeStats.value.sourceTextCharacters += sourceText.length; realtimeStats.value.ttsTextCharacters += targetText.length; realtimeStats.value.currentLanguagePair = '$sourceLanguage-$targetLanguage'; realtimeStats.value.currentMode = mode; realtimeStats.refresh(); // 更新当日统计 - 使用多维度统计 final languagePair = '$sourceLanguage→$targetLanguage'; // 更新语言对统计 final newLanguagePairStats = Map.from( todayStats.value.languagePairStats); if (newLanguagePairStats.containsKey(languagePair)) { final existing = newLanguagePairStats[languagePair]!; newLanguagePairStats[languagePair] = LanguagePairStats( languagePair: languagePair, translationCount: existing.translationCount + 1, sourceCharacters: existing.sourceCharacters + sourceText.length, ); } else { newLanguagePairStats[languagePair] = LanguagePairStats( languagePair: languagePair, translationCount: 1, sourceCharacters: sourceText.length, ); } // 更新模式统计 final newModeStats = Map.from(todayStats.value.modeStats); if (newModeStats.containsKey(mode)) { final existing = newModeStats[mode]!; newModeStats[mode] = existing.copyWith( apiCalls: existing.apiCalls + 1, sourceCharacters: existing.sourceCharacters + sourceText.length, ttsCharacters: existing.ttsCharacters + targetText.length, ); } else { newModeStats[mode] = TranslationModeStats( apiCalls: 1, sourceCharacters: sourceText.length, ttsCharacters: targetText.length, ); } todayStats.value = todayStats.value.copyWith( translationApiCalls: todayStats.value.translationApiCalls + 1, sourceTextCharacters: todayStats.value.sourceTextCharacters + sourceText.length, ttsTextCharacters: todayStats.value.ttsTextCharacters + targetText.length, languagePairStats: newLanguagePairStats, modeStats: newModeStats, ); _saveTodayStats(); Logger.info( '记录翻译API调用: 源文本${sourceText.length}字符, 目标文本${targetText.length}字符'); } catch (e) { Logger.error('记录翻译API调用失败: $e'); } } /// 记录ASR API调用 - 优化版(精确统计参数) void recordAsrApiCall({ required int effectiveDurationSeconds, // 有效语音时长(VAD检测的实际语音) required int totalDurationSeconds, // 总会话时长(包含静音) required String language, required String mode, // 翻译模式 String? audioFormat, // 音频格式(可选) int? sampleRate, // 采样率(可选) }) { try { // 更新实时统计(使用有效语音时长作为主要计费依据) realtimeStats.value.effectiveAudioDuration += effectiveDurationSeconds; realtimeStats.value.totalSessionDuration += totalDurationSeconds; realtimeStats.refresh(); // 更新模式统计 final newModeStats = Map.from(todayStats.value.modeStats); if (newModeStats.containsKey(mode)) { final existing = newModeStats[mode]!; newModeStats[mode] = existing.copyWith( effectiveAudioDuration: existing.effectiveAudioDuration + effectiveDurationSeconds, ); } else { newModeStats[mode] = TranslationModeStats( effectiveAudioDuration: effectiveDurationSeconds, ); } // 更新当日统计 todayStats.value = todayStats.value.copyWith( asrApiCalls: todayStats.value.asrApiCalls + 1, effectiveAudioDuration: todayStats.value.effectiveAudioDuration + effectiveDurationSeconds, totalSessionDuration: todayStats.value.totalSessionDuration + totalDurationSeconds, modeStats: newModeStats, ); _saveTodayStats(); Logger.info( 'ASR统计: 有效语音${effectiveDurationSeconds}秒, 总时长${totalDurationSeconds}秒, 语言$language, 模式$mode'); } catch (e) { Logger.error('记录ASR API调用失败: $e'); } } /// 记录TTS API调用 void recordTtsApiCall({ required String text, required int durationSeconds, required String language, }) { try { // 更新实时统计 realtimeStats.value.totalSessionDuration += durationSeconds; realtimeStats.refresh(); // 更新当日统计(TTS按字符数计费,不是按时长) todayStats.value = todayStats.value.copyWith( ttsApiCalls: todayStats.value.ttsApiCalls + 1, ttsTextCharacters: todayStats.value.ttsTextCharacters + text.length, totalSessionDuration: todayStats.value.totalSessionDuration + durationSeconds, ); _saveTodayStats(); Logger.info('记录TTS API调用: 文本${text.length}字符, 时长${durationSeconds}秒'); } catch (e) { Logger.error('记录TTS API调用失败: $e'); } } /// 记录AI摘要API调用 void recordAiSummaryApiCall() { try { // 更新当日统计 todayStats.value = todayStats.value.copyWith( aiSummaryApiCalls: todayStats.value.aiSummaryApiCalls + 1, ); _saveTodayStats(); Logger.info('记录AI摘要API调用'); } catch (e) { Logger.error('记录AI摘要API调用失败: $e'); } } /// 开始新会话 void startNewSession() { realtimeStats.value.reset(); // 更新当日会话数 todayStats.value = todayStats.value.copyWith( sessionCount: todayStats.value.sessionCount + 1, ); _saveTodayStats(); Logger.info('开始新的翻译会话'); } /// 保存当日统计数据 void _saveTodayStats() { try { final today = DateTime.now(); final todayKey = '${today.year}-${today.month.toString().padLeft(2, '0')}-${today.day.toString().padLeft(2, '0')}'; _storage.write('${_dailyStatsKey}_$todayKey', todayStats.value.toJson()); } catch (e) { Logger.error('保存当日统计数据失败: $e'); } } /// 获取历史统计数据 Future> getHistoryStats({ required DateTime startDate, required DateTime endDate, }) async { try { final List historyStats = []; for (DateTime date = startDate; date.isBefore(endDate) || date.isAtSameMomentAs(endDate); date = date.add(const Duration(days: 1))) { final dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; final statsData = _storage.read('${_dailyStatsKey}_$dateKey'); if (statsData != null) { historyStats.add(TranslationUsageStats.fromJson( Map.from(statsData))); } } return historyStats; } catch (e) { Logger.error('获取历史统计数据失败: $e'); return []; } } /// 清理过期数据(保留最近90天) void cleanupOldData() { try { final cutoffDate = DateTime.now().subtract(const Duration(days: 90)); final allKeys = _storage.getKeys(); for (final key in allKeys) { if (key.startsWith(_dailyStatsKey)) { final dateStr = key.replaceFirst('${_dailyStatsKey}_', ''); try { final date = DateTime.parse(dateStr); if (date.isBefore(cutoffDate)) { _storage.remove(key); Logger.info('清理过期统计数据: $key'); } } catch (e) { // 忽略解析错误的键 } } } } catch (e) { Logger.error('清理过期数据失败: $e'); } } /// 获取月度统计汇总 Future getMonthlyStats(int year, int month) async { try { final startDate = DateTime(year, month, 1); final endDate = DateTime(year, month + 1, 0); // 月末 final dailyStats = await getHistoryStats( startDate: startDate, endDate: endDate, ); if (dailyStats.isEmpty) { return TranslationUsageStats.empty(getCurrentUserId()); } // 汇总月度数据 int totalTranslationCalls = 0; int totalAsrCalls = 0; int totalTtsCalls = 0; int totalAiSummaryCalls = 0; int totalSourceChars = 0; int totalTargetChars = 0; int totalRecordingDuration = 0; int totalTtsPlayDuration = 0; int totalSessions = 0; //double totalCost = 0.0; final Map combinedLanguagePairUsage = {}; final Map combinedModeUsage = {}; for (final stats in dailyStats) { totalTranslationCalls += stats.translationApiCalls; totalAsrCalls += stats.asrApiCalls; totalTtsCalls += stats.ttsApiCalls; totalAiSummaryCalls += stats.aiSummaryApiCalls; totalSourceChars += stats.sourceCharacterCount; totalTargetChars += stats.targetCharacterCount; totalRecordingDuration += stats.audioRecordingDuration; totalTtsPlayDuration += stats.ttsPlaybackDuration; totalSessions += stats.sessionCount; // totalCost += stats.estimatedCost; // 合并语言对使用统计 stats.languagePairUsage.forEach((key, value) { combinedLanguagePairUsage[key] = (combinedLanguagePairUsage[key] ?? 0) + value; }); // 合并模式使用统计 stats.modeUsage.forEach((key, value) { combinedModeUsage[key] = (combinedModeUsage[key] ?? 0) + value; }); } return TranslationUsageStats( id: 'monthly_${year}_$month', date: startDate, userId: getCurrentUserId(), timeGranularity: 'month', translationApiCalls: totalTranslationCalls, asrApiCalls: totalAsrCalls, ttsApiCalls: totalTtsCalls, aiSummaryApiCalls: totalAiSummaryCalls, sourceTextCharacters: totalSourceChars, effectiveAudioDuration: totalRecordingDuration, ttsTextCharacters: totalTargetChars, totalSessionDuration: totalTtsPlayDuration, sessionCount: totalSessions, // estimatedCost: totalCost, ); } catch (e) { Logger.error('获取月度统计失败: $e'); return TranslationUsageStats.empty(getCurrentUserId()); } } }