Browse Source

refactor(usage): 优化统计服务和用户模型代码结构

重构统计服务中的变量命名,将realtimeStats和todayStats改为更明确的realtimeTranslationStats和todayTranslationStats
简化用户模型中的初始化逻辑,添加日志记录
合并积分显示行,优化UI展示
newdev_shunjiawei
lxm 11 months ago
parent
commit
39b09d9e24
  1. 4
      lib/data/models/meeting_usage_stats.dart
  2. 37
      lib/data/models/user_Info.dart
  3. 105
      lib/data/services/usage_stats_service.dart
  4. 15
      lib/modules/usage/controllers/translation_stats_controller.dart
  5. 33
      lib/modules/usage/views/translation_stats_view.dart

4
lib/data/models/meeting_usage_stats.dart

@ -1,3 +1,5 @@
///会议模块用量统计的模型类
const int asrStatusProcessing = 0; // 处理中 const int asrStatusProcessing = 0; // 处理中
const int asrStatusFailed = 1; // 处理失败 const int asrStatusFailed = 1; // 处理失败
const int asrStatusCompleted = 2; // 已完成 const int asrStatusCompleted = 2; // 已完成
@ -95,7 +97,7 @@ class MeetingAsrDailyStats {
} }
} }
// 会议音频文件转文字历史统计 // 会议音频文件转文字历史统计(所有的用量)
class MeetingAsrHistoryStats { class MeetingAsrHistoryStats {
String userId; // 用户ID String userId; // 用户ID
int allAsrCalls; // 所有ASR调用次数 int allAsrCalls; // 所有ASR调用次数

37
lib/data/models/user_Info.dart

@ -83,11 +83,9 @@ class User {
final devicesData = json['devices'] as List<dynamic>?; // 允许空值 final devicesData = json['devices'] as List<dynamic>?; // 允许空值
//final token = json['data']['token'] as String? ?? ''; //final token = json['data']['token'] as String? ?? '';
//final token = ''; //final token = '';
// 更新单例实例 final u = User._(
return _instance = User._(
token: '', token: '',
//token: token as String? ?? '', name: (userData?['name'] as String?) ?? '默认名称',
name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值
uid: (userData?['uid'] as String?) ?? '', uid: (userData?['uid'] as String?) ?? '',
avatar: (userData?['avatar'] as String?) ?? '', avatar: (userData?['avatar'] as String?) ?? '',
language: (userData?['language'] as String?) ?? 'en', language: (userData?['language'] as String?) ?? 'en',
@ -95,42 +93,43 @@ class User {
phone: (userData?['phone'] as String?) ?? '', phone: (userData?['phone'] as String?) ?? '',
phonemac: (userData?['phonemac'] as String?) ?? '', phonemac: (userData?['phonemac'] as String?) ?? '',
password: (userData?['password'] as String?) ?? '', password: (userData?['password'] as String?) ?? '',
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0,
viptime: (userData?['viptime'] as int?) ?? 0, // 提供默认值 viptime: (userData?['viptime'] as int?) ?? 0,
aichatintegral: (userData?['aichatintegral'] as int?) ?? 0, aichatintegral: (userData?['aichatintegral'] as int?) ?? 0,
aichattotalintegral: (userData?['aichattotalintegral'] as int?) ?? 0, aichattotalintegral: (userData?['aichattotalintegral'] as int?) ?? 0,
tradeintegral: (userData?['tradeintegral'] as int?) ?? 0, tradeintegral: (userData?['tradeintegral'] as int?) ?? 0,
tradetotalintegral: (userData?['tradetotalintegral'] as int?) ?? 0, tradetotalintegral: (userData?['tradetotalintegral'] as int?) ?? 0,
meetintegral: (userData?['meetintegral'] as int?) ?? 0, meetintegral: (userData?['meetintegral'] as int?) ?? 0,
meettotalintegral: (userData?['meettotalintegral'] as int?) ?? 0, meettotalintegral: (userData?['meettotalintegral'] as int?) ?? 0,
devices: devicesData devices: devicesData
?.map((e) => UserDevice.fromJson(e as Map<String, dynamic>)) ?.map((e) => UserDevice.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
[], // 处理设备列表 [],
); );
_instance = u;
Logger.i("User",
"初始化用户: uid=${u.uid}, name=${u.name}, viplv=${u.viplv}, viptime=${u.viptime}, ai聊天=${u.aichatintegral}/${u.aichattotalintegral}, 翻译=${u.tradeintegral}/${u.tradetotalintegral}, 会议=${u.meetintegral}/${u.meettotalintegral}");
return u;
} }
factory User.fronTokenJson(Map<String, dynamic> json) { factory User.fronTokenJson(Map<String, dynamic> json) {
// 修复1:安全获取嵌套的 user 字段 // 修复1:安全获取嵌套的 user 字段
final userData = json['user'] as Map<String, dynamic>?; // 允许空值 final userData = json['user'] as Map<String, dynamic>?; // 允许空值
final devicesData = json['devices'] as List<dynamic>?; // 允许空值 final devicesData = json['devices'] as List<dynamic>?; // 允许空值
print("数据获取fronTokenJson$userData $devicesData"); Logger.d("User",
// 更新单例实例 "fronTokenJson: user=$userData, devices=${devicesData?.length ?? 0}");
return _instance = User._( final u = User._(
token: '', token: '',
name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值 name: (userData?['name'] as String?) ?? '默认名称',
uid: (userData?['uid'] as String?) ?? '', uid: (userData?['uid'] as String?) ?? '',
avatar: (userData?['avatar'] as String?) ?? '', avatar: (userData?['avatar'] as String?) ?? '',
language: (userData?['language'] as String?) ?? 'en', language: (userData?['language'] as String?) ?? 'en',
mail: (userData?['mail'] as String?) ?? '', mail: (userData?['mail'] as String?) ?? '',
phone: (userData?['phone'] as String?) ?? '', phone: (userData?['phone'] as String?) ?? '',
phonemac: (userData?['phonemac'] as String?) ?? '', phonemac: (userData?['phonemac'] as String?) ?? '',
password: (userData?['password'] as String?) ?? '', password: (userData?['password'] as String?) ?? '',
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0,
viptime: (userData?['viptime'] as int?) ?? 0, // 提供默认值 viptime: (userData?['viptime'] as int?) ?? 0,
aichatintegral: (userData?['aichatintegral'] as int?) ?? 0, aichatintegral: (userData?['aichatintegral'] as int?) ?? 0,
aichattotalintegral: (userData?['aichattotalintegral'] as int?) ?? 0, aichattotalintegral: (userData?['aichattotalintegral'] as int?) ?? 0,
tradeintegral: (userData?['tradeintegral'] as int?) ?? 0, tradeintegral: (userData?['tradeintegral'] as int?) ?? 0,
@ -141,8 +140,12 @@ class User {
? (devicesData) ? (devicesData)
.map((e) => UserDevice.fromJson(e as Map<String, dynamic>)) .map((e) => UserDevice.fromJson(e as Map<String, dynamic>))
.toList() .toList()
: [], // 处理设备列表 : [],
); );
_instance = u;
Logger.i("User",
"初始化用户(token): uid=${u.uid}, name=${u.name}, viplv=${u.viplv}, viptime=${u.viptime}, ai聊天=${u.aichatintegral}/${u.aichattotalintegral}, 翻译=${u.tradeintegral}/${u.tradetotalintegral}, 会议=${u.meetintegral}/${u.meettotalintegral}");
return u;
} }
updateUserInfo(Map<String, dynamic> user) { updateUserInfo(Map<String, dynamic> user) {

105
lib/data/services/usage_stats_service.dart

@ -1,4 +1,3 @@
import 'dart:convert';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import '../models/translation_usage_stats.dart'; import '../models/translation_usage_stats.dart';
@ -11,17 +10,17 @@ import '../models/user_Info.dart';
/// 使用量统计服务 /// 使用量统计服务
/// ///
class UsageStatsService extends GetxService { class UsageStatsService extends GetxService {
static const String _storageKey = 'usage_stats';
static const String _dailyStatsKey = 'daily_translation_stats'; static const String _dailyStatsKey = 'daily_translation_stats';
static const String _meetingDailyKey = 'daily_meeting_asr_stats'; static const String _meetingDailyKey = 'daily_meeting_asr_stats';
final GetStorage _storage = GetStorage(); final GetStorage _storage = GetStorage();
// 实时统计数据 // 实时统计数据
final Rx<RealtimeUsageStats> realtimeStats = RealtimeUsageStats().obs; final Rx<RealtimeUsageStats> realtimeTranslationStats =
RealtimeUsageStats().obs;
// 当日统计数据 // 当日统计数据
final Rx<TranslationUsageStats> todayStats = final Rx<TranslationUsageStats> todayTranslationStats =
TranslationUsageStats.empty('').obs; TranslationUsageStats.empty('').obs;
@override @override
@ -32,12 +31,12 @@ class UsageStatsService extends GetxService {
/// 初始化服务 /// 初始化服务
void _initializeService() { void _initializeService() {
_loadTodayStats(); _loadTodayTranslationStats();
Logger.info('lxm--使用量统计服务初始化完成'); Logger.info('lxm--使用量统计服务初始化完成');
} }
/// 加载当日统计数据 /// 加载当日翻译统计数据
void _loadTodayStats() { void _loadTodayTranslationStats() {
try { try {
final today = DateTime.now(); final today = DateTime.now();
final todayKey = final todayKey =
@ -45,14 +44,16 @@ class UsageStatsService extends GetxService {
final statsData = _storage.read('${_dailyStatsKey}_$todayKey'); final statsData = _storage.read('${_dailyStatsKey}_$todayKey');
if (statsData != null) { if (statsData != null) {
todayStats.value = TranslationUsageStats.fromJson( todayTranslationStats.value = TranslationUsageStats.fromJson(
Map<String, dynamic>.from(statsData)); Map<String, dynamic>.from(statsData));
} else { } else {
todayStats.value = TranslationUsageStats.empty(getCurrentUserId()); todayTranslationStats.value =
TranslationUsageStats.empty(getCurrentUserId());
} }
} catch (e) { } catch (e) {
Logger.error('加载当日统计数据失败: $e'); Logger.error('加载当日翻译统计数据失败: $e');
todayStats.value = TranslationUsageStats.empty(getCurrentUserId()); todayTranslationStats.value =
TranslationUsageStats.empty(getCurrentUserId());
} }
} }
@ -142,20 +143,20 @@ class UsageStatsService extends GetxService {
}) { }) {
try { try {
// 更新实时统计 // 更新实时统计
realtimeStats.value.translationCount++; realtimeTranslationStats.value.translationCount++;
realtimeStats.value.sourceTextCharacters += sourceText.length; realtimeTranslationStats.value.sourceTextCharacters += sourceText.length;
realtimeStats.value.ttsTextCharacters += targetText.length; realtimeTranslationStats.value.ttsTextCharacters += targetText.length;
realtimeStats.value.currentLanguagePair = realtimeTranslationStats.value.currentLanguagePair =
'$sourceLanguage-$targetLanguage'; '$sourceLanguage-$targetLanguage';
realtimeStats.value.currentMode = mode; realtimeTranslationStats.value.currentMode = mode;
realtimeStats.refresh(); realtimeTranslationStats.refresh();
// 更新当日统计 - 使用多维度统计 // 更新当日统计 - 使用多维度统计
final languagePair = '$sourceLanguage→$targetLanguage'; final languagePair = '$sourceLanguage→$targetLanguage';
// 更新语言对统计 // 更新语言对统计
final newLanguagePairStats = Map<String, LanguagePairStats>.from( final newLanguagePairStats = Map<String, LanguagePairStats>.from(
todayStats.value.languagePairStats); todayTranslationStats.value.languagePairStats);
if (newLanguagePairStats.containsKey(languagePair)) { if (newLanguagePairStats.containsKey(languagePair)) {
final existing = newLanguagePairStats[languagePair]!; final existing = newLanguagePairStats[languagePair]!;
newLanguagePairStats[languagePair] = LanguagePairStats( newLanguagePairStats[languagePair] = LanguagePairStats(
@ -172,8 +173,8 @@ class UsageStatsService extends GetxService {
} }
// 更新模式统计 // 更新模式统计
final newModeStats = final newModeStats = Map<String, TranslationModeStats>.from(
Map<String, TranslationModeStats>.from(todayStats.value.modeStats); todayTranslationStats.value.modeStats);
if (newModeStats.containsKey(mode)) { if (newModeStats.containsKey(mode)) {
final existing = newModeStats[mode]!; final existing = newModeStats[mode]!;
newModeStats[mode] = existing.copyWith( newModeStats[mode] = existing.copyWith(
@ -189,12 +190,13 @@ class UsageStatsService extends GetxService {
); );
} }
todayStats.value = todayStats.value.copyWith( todayTranslationStats.value = todayTranslationStats.value.copyWith(
translationApiCalls: todayStats.value.translationApiCalls + 1, translationApiCalls:
sourceTextCharacters: todayTranslationStats.value.translationApiCalls + 1,
todayStats.value.sourceTextCharacters + sourceText.length, sourceTextCharacters: todayTranslationStats.value.sourceTextCharacters +
sourceText.length,
ttsTextCharacters: ttsTextCharacters:
todayStats.value.ttsTextCharacters + targetText.length, todayTranslationStats.value.ttsTextCharacters + targetText.length,
languagePairStats: newLanguagePairStats, languagePairStats: newLanguagePairStats,
modeStats: newModeStats, modeStats: newModeStats,
); );
@ -219,13 +221,15 @@ class UsageStatsService extends GetxService {
}) { }) {
try { try {
// 更新实时统计(使用有效语音时长作为主要计费依据) // 更新实时统计(使用有效语音时长作为主要计费依据)
realtimeStats.value.effectiveAudioDuration += effectiveDurationSeconds; realtimeTranslationStats.value.effectiveAudioDuration +=
realtimeStats.value.totalSessionDuration += totalDurationSeconds; effectiveDurationSeconds;
realtimeStats.refresh(); realtimeTranslationStats.value.totalSessionDuration +=
totalDurationSeconds;
realtimeTranslationStats.refresh();
// 更新模式统计 // 更新模式统计
final newModeStats = final newModeStats = Map<String, TranslationModeStats>.from(
Map<String, TranslationModeStats>.from(todayStats.value.modeStats); todayTranslationStats.value.modeStats);
if (newModeStats.containsKey(mode)) { if (newModeStats.containsKey(mode)) {
final existing = newModeStats[mode]!; final existing = newModeStats[mode]!;
newModeStats[mode] = existing.copyWith( newModeStats[mode] = existing.copyWith(
@ -239,12 +243,13 @@ class UsageStatsService extends GetxService {
} }
// 更新当日统计 // 更新当日统计
todayStats.value = todayStats.value.copyWith( todayTranslationStats.value = todayTranslationStats.value.copyWith(
asrApiCalls: todayStats.value.asrApiCalls + 1, asrApiCalls: todayTranslationStats.value.asrApiCalls + 1,
effectiveAudioDuration: effectiveAudioDuration:
todayStats.value.effectiveAudioDuration + effectiveDurationSeconds, todayTranslationStats.value.effectiveAudioDuration +
totalSessionDuration: effectiveDurationSeconds,
todayStats.value.totalSessionDuration + totalDurationSeconds, totalSessionDuration: todayTranslationStats.value.totalSessionDuration +
totalDurationSeconds,
modeStats: newModeStats, modeStats: newModeStats,
); );
@ -280,9 +285,9 @@ class UsageStatsService extends GetxService {
'TRANSLATION_MODE_$mode': effectiveDurationSeconds, 'TRANSLATION_MODE_$mode': effectiveDurationSeconds,
'TRANSLATION_ASR_LANGUAGE_$language': 1, 'TRANSLATION_ASR_LANGUAGE_$language': 1,
'TRANSLATION_SOURCE_CHARACTERS': 'TRANSLATION_SOURCE_CHARACTERS':
realtimeStats.value.sourceTextCharacters, realtimeTranslationStats.value.sourceTextCharacters,
'TRANSLATION_TARGET_CHARACTERS': 'TRANSLATION_TARGET_CHARACTERS':
realtimeStats.value.ttsTextCharacters, realtimeTranslationStats.value.ttsTextCharacters,
}, },
}; };
@ -348,15 +353,16 @@ class UsageStatsService extends GetxService {
}) { }) {
try { try {
// 更新实时统计 // 更新实时统计
realtimeStats.value.totalSessionDuration += durationSeconds; realtimeTranslationStats.value.totalSessionDuration += durationSeconds;
realtimeStats.refresh(); realtimeTranslationStats.refresh();
// 更新当日统计(TTS按字符数计费,不是按时长) // 更新当日统计(TTS按字符数计费,不是按时长)
todayStats.value = todayStats.value.copyWith( todayTranslationStats.value = todayTranslationStats.value.copyWith(
ttsApiCalls: todayStats.value.ttsApiCalls + 1, ttsApiCalls: todayTranslationStats.value.ttsApiCalls + 1,
ttsTextCharacters: todayStats.value.ttsTextCharacters + text.length, ttsTextCharacters:
todayTranslationStats.value.ttsTextCharacters + text.length,
totalSessionDuration: totalSessionDuration:
todayStats.value.totalSessionDuration + durationSeconds, todayTranslationStats.value.totalSessionDuration + durationSeconds,
); );
_saveTodayStats(); _saveTodayStats();
@ -371,8 +377,8 @@ class UsageStatsService extends GetxService {
void recordAiSummaryApiCall() { void recordAiSummaryApiCall() {
try { try {
// 更新当日统计 // 更新当日统计
todayStats.value = todayStats.value.copyWith( todayTranslationStats.value = todayTranslationStats.value.copyWith(
aiSummaryApiCalls: todayStats.value.aiSummaryApiCalls + 1, aiSummaryApiCalls: todayTranslationStats.value.aiSummaryApiCalls + 1,
); );
_saveTodayStats(); _saveTodayStats();
@ -385,11 +391,11 @@ class UsageStatsService extends GetxService {
/// 开始新会话 /// 开始新会话
void startNewSession() { void startNewSession() {
realtimeStats.value.reset(); realtimeTranslationStats.value.reset();
// 更新当日会话数 // 更新当日会话数
todayStats.value = todayStats.value.copyWith( todayTranslationStats.value = todayTranslationStats.value.copyWith(
sessionCount: todayStats.value.sessionCount + 1, sessionCount: todayTranslationStats.value.sessionCount + 1,
); );
_saveTodayStats(); _saveTodayStats();
@ -402,7 +408,8 @@ class UsageStatsService extends GetxService {
final today = DateTime.now(); final today = DateTime.now();
final todayKey = final todayKey =
'${today.year}-${today.month.toString().padLeft(2, '0')}-${today.day.toString().padLeft(2, '0')}'; '${today.year}-${today.month.toString().padLeft(2, '0')}-${today.day.toString().padLeft(2, '0')}';
_storage.write('${_dailyStatsKey}_$todayKey', todayStats.value.toJson()); _storage.write(
'${_dailyStatsKey}_$todayKey', todayTranslationStats.value.toJson());
} catch (e) { } catch (e) {
Logger.error('保存当日统计数据失败: $e'); Logger.error('保存当日统计数据失败: $e');
} }

15
lib/modules/usage/controllers/translation_stats_controller.dart

@ -19,7 +19,8 @@ class TranslationStatsController extends GetxController {
final RxList<TranslationUsageStats> historyStats = final RxList<TranslationUsageStats> historyStats =
<TranslationUsageStats>[].obs; <TranslationUsageStats>[].obs;
RealtimeUsageStats get realtimeStats => _usageService.realtimeStats.value; RealtimeUsageStats get realtimeStats =>
_usageService.realtimeTranslationStats.value;
final RxMap<String, double> costConfig = <String, double>{}.obs; final RxMap<String, double> costConfig = <String, double>{}.obs;
final RxDouble dailyCostThreshold = 10.0.obs; final RxDouble dailyCostThreshold = 10.0.obs;
@ -34,17 +35,11 @@ class TranslationStatsController extends GetxController {
} }
void _initializeData() { void _initializeData() {
ever(_usageService.todayStats, (stats) { ever(_usageService.todayTranslationStats, (stats) {
todayStats.value = stats; todayStats.value = stats;
_updateChartData(); _updateChartData();
}); });
loadAllStats(); loadAllStats();
} }
@ -52,7 +47,7 @@ class TranslationStatsController extends GetxController {
isLoading.value = true; isLoading.value = true;
try { try {
todayStats.value = _usageService.todayStats.value; todayStats.value = _usageService.todayTranslationStats.value;
await _loadWeekStats(); await _loadWeekStats();
@ -136,7 +131,6 @@ class TranslationStatsController extends GetxController {
int totalTtsPlayDuration = 0; int totalTtsPlayDuration = 0;
int totalSessions = 0; int totalSessions = 0;
final Map<String, int> combinedLanguagePairUsage = {}; final Map<String, int> combinedLanguagePairUsage = {};
final Map<String, int> combinedModeUsage = {}; final Map<String, int> combinedModeUsage = {};
@ -151,7 +145,6 @@ class TranslationStatsController extends GetxController {
totalTtsPlayDuration += stats.totalSessionDuration; totalTtsPlayDuration += stats.totalSessionDuration;
totalSessions += stats.sessionCount; totalSessions += stats.sessionCount;
stats.languagePairUsage.forEach((key, value) { stats.languagePairUsage.forEach((key, value) {
combinedLanguagePairUsage[key] = combinedLanguagePairUsage[key] =
(combinedLanguagePairUsage[key] ?? 0) + value; (combinedLanguagePairUsage[key] ?? 0) + value;

33
lib/modules/usage/views/translation_stats_view.dart

@ -466,46 +466,25 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
return Column( return Column(
children: [ children: [
_buildIntegralRow( _buildIntegralRow(
'AI聊天积分', 'AI聊天',
controller.formatNumber(u.aichatintegral), '${controller.formatNumber(u.aichatintegral)}/${controller.formatNumber(u.aichattotalintegral)}',
Icons.chat_bubble_outline, Icons.chat_bubble_outline,
isDarkMode, isDarkMode,
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
_buildIntegralRow( _buildIntegralRow(
'AI聊天总积分', '翻译',
controller.formatNumber(u.aichattotalintegral), '${controller.formatNumber(u.tradeintegral)}/${controller.formatNumber(u.tradetotalintegral)}',
Icons.chat_outlined,
isDarkMode,
),
SizedBox(height: 12.h),
_buildIntegralRow(
'翻译积分',
controller.formatNumber(u.tradeintegral),
Icons.translate, Icons.translate,
isDarkMode, isDarkMode,
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
_buildIntegralRow( _buildIntegralRow(
'翻译总积分', '会议',
controller.formatNumber(u.tradetotalintegral), '${controller.formatNumber(u.meetintegral)}/${controller.formatNumber(u.meettotalintegral)}',
Icons.g_translate,
isDarkMode,
),
SizedBox(height: 12.h),
_buildIntegralRow(
'会议积分',
controller.formatNumber(u.meetintegral),
Icons.groups, Icons.groups,
isDarkMode, isDarkMode,
), ),
SizedBox(height: 12.h),
_buildIntegralRow(
'会议总积分',
controller.formatNumber(u.meettotalintegral),
Icons.meeting_room,
isDarkMode,
),
], ],
); );
}), }),

Loading…
Cancel
Save