Browse Source

feat(usage): 添加翻译使用统计模块,包括视图、控制器和绑定

实现翻译使用统计功能,包含以下主要组件:
1. 翻译统计视图,展示实时和历史使用数据
2. 翻译统计控制器,处理数据加载和业务逻辑
3. 翻译成本控制器,管理成本配置和阈值设置
4. 相关绑定类,用于依赖注入

模块支持查看今日、本周、本月和历史统计数据,包括API调用次数、数据量和成本分析
newdev_shunjiawei
lxm 11 months ago
parent
commit
09b22c24ac
  1. 10
      lib/modules/usage/bindings/translation_stats_binding.dart
  2. 84
      lib/modules/usage/controllers/translation_cost_controller.dart
  3. 62
      lib/modules/usage/controllers/translation_stats_controller.dart
  4. 55
      lib/modules/usage/views/translation_stats_view.dart
  5. 4
      lib/routes/app_pages.dart

10
lib/modules/translation/bindings/translation_stats_binding.dart → lib/modules/usage/bindings/translation_stats_binding.dart

@ -2,18 +2,12 @@ import 'package:get/get.dart';
import '../controllers/translation_stats_controller.dart'; import '../controllers/translation_stats_controller.dart';
import '../../../data/services/translation_usage_service.dart'; import '../../../data/services/translation_usage_service.dart';
//翻译统计页面依赖绑定
class TranslationStatsBinding implements Bindings { class TranslationStatsBinding implements Bindings {
@override @override
void dependencies() { void dependencies() {
// 确保服务已注册
if (!Get.isRegistered<TranslationUsageService>()) { if (!Get.isRegistered<TranslationUsageService>()) {
Get.put(TranslationUsageService(), permanent: true); Get.put(TranslationUsageService(), permanent: true);
} }
Get.lazyPut<TranslationStatsController>(() => TranslationStatsController());
// 注册控制器
Get.lazyPut<TranslationStatsController>(
() => TranslationStatsController(),
);
} }
} }

84
lib/modules/translation/controllers/translation_cost_controller.dart → lib/modules/usage/controllers/translation_cost_controller.dart

@ -3,25 +3,18 @@ import 'package:flutter/material.dart';
import '../../../data/services/translation_usage_service.dart'; import '../../../data/services/translation_usage_service.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
/// 翻译成本配置控制器
class TranslationCostController extends GetxController { class TranslationCostController extends GetxController {
// 依赖注入 final TranslationUsageService _usageService =
final TranslationUsageService _usageService = Get.find<TranslationUsageService>(); Get.find<TranslationUsageService>();
// 成本配置
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;
final RxDouble monthlyCostThreshold = 300.0.obs; final RxDouble monthlyCostThreshold = 300.0.obs;
// 表单控制器
final translationCostController = TextEditingController(); final translationCostController = TextEditingController();
final asrCostController = TextEditingController(); final asrCostController = TextEditingController();
final ttsCostController = TextEditingController(); final ttsCostController = TextEditingController();
final aiSummaryCostController = TextEditingController(); final aiSummaryCostController = TextEditingController();
final dailyThresholdController = TextEditingController(); final dailyThresholdController = TextEditingController();
final monthlyThresholdController = TextEditingController(); final monthlyThresholdController = TextEditingController();
// 状态变量
final isLoading = false.obs; final isLoading = false.obs;
final isSaving = false.obs; final isSaving = false.obs;
@ -33,7 +26,6 @@ class TranslationCostController extends GetxController {
@override @override
void onClose() { void onClose() {
// 释放控制器
translationCostController.dispose(); translationCostController.dispose();
asrCostController.dispose(); asrCostController.dispose();
ttsCostController.dispose(); ttsCostController.dispose();
@ -43,24 +35,22 @@ class TranslationCostController extends GetxController {
super.onClose(); super.onClose();
} }
/// 加载当前配置
void _loadCurrentConfig() { void _loadCurrentConfig() {
isLoading.value = true; isLoading.value = true;
try { try {
// 从服务获取当前配置
costConfig.value = Map<String, double>.from(_usageService.costConfig); costConfig.value = Map<String, double>.from(_usageService.costConfig);
dailyCostThreshold.value = _usageService.dailyCostThreshold.value; dailyCostThreshold.value = _usageService.dailyCostThreshold.value;
monthlyCostThreshold.value = _usageService.monthlyCostThreshold.value; monthlyCostThreshold.value = _usageService.monthlyCostThreshold.value;
translationCostController.text =
// 更新表单控制器 (costConfig['translation_per_1k_chars'] ?? 0.05).toString();
translationCostController.text = (costConfig['translation_per_1k_chars'] ?? 0.05).toString(); asrCostController.text =
asrCostController.text = (costConfig['asr_per_minute'] ?? 0.02).toString(); (costConfig['asr_per_minute'] ?? 0.02).toString();
ttsCostController.text = (costConfig['tts_per_1k_chars'] ?? 0.03).toString(); ttsCostController.text =
aiSummaryCostController.text = (costConfig['ai_summary_per_request'] ?? 0.01).toString(); (costConfig['tts_per_1k_chars'] ?? 0.03).toString();
aiSummaryCostController.text =
(costConfig['ai_summary_per_request'] ?? 0.01).toString();
dailyThresholdController.text = dailyCostThreshold.value.toString(); dailyThresholdController.text = dailyCostThreshold.value.toString();
monthlyThresholdController.text = monthlyCostThreshold.value.toString(); monthlyThresholdController.text = monthlyCostThreshold.value.toString();
Logger.info('成本配置加载完成'); Logger.info('成本配置加载完成');
} catch (e) { } catch (e) {
Logger.error('加载成本配置失败: $e'); Logger.error('加载成本配置失败: $e');
@ -70,44 +60,32 @@ class TranslationCostController extends GetxController {
} }
} }
/// 保存配置
Future<void> saveConfig() async { Future<void> saveConfig() async {
if (isSaving.value) return; if (isSaving.value) return;
isSaving.value = true; isSaving.value = true;
try { try {
// 验证输入
if (!_validateInputs()) { if (!_validateInputs()) {
return; return;
} }
// 更新成本配置
final newCostConfig = { final newCostConfig = {
'translation_per_1k_chars': double.parse(translationCostController.text), 'translation_per_1k_chars':
double.parse(translationCostController.text),
'asr_per_minute': double.parse(asrCostController.text), 'asr_per_minute': double.parse(asrCostController.text),
'tts_per_1k_chars': double.parse(ttsCostController.text), 'tts_per_1k_chars': double.parse(ttsCostController.text),
'ai_summary_per_request': double.parse(aiSummaryCostController.text), 'ai_summary_per_request': double.parse(aiSummaryCostController.text),
}; };
final newDailyThreshold = double.parse(dailyThresholdController.text); final newDailyThreshold = double.parse(dailyThresholdController.text);
final newMonthlyThreshold = double.parse(monthlyThresholdController.text); final newMonthlyThreshold = double.parse(monthlyThresholdController.text);
// 保存到服务
_usageService.updateCostConfig(newCostConfig); _usageService.updateCostConfig(newCostConfig);
_usageService.updateThresholds( _usageService.updateThresholds(
dailyThreshold: newDailyThreshold, dailyThreshold: newDailyThreshold,
monthlyThreshold: newMonthlyThreshold, monthlyThreshold: newMonthlyThreshold,
); );
// 更新本地状态
costConfig.value = newCostConfig; costConfig.value = newCostConfig;
dailyCostThreshold.value = newDailyThreshold; dailyCostThreshold.value = newDailyThreshold;
monthlyCostThreshold.value = newMonthlyThreshold; monthlyCostThreshold.value = newMonthlyThreshold;
_showSuccessSnackbar('配置保存成功'); _showSuccessSnackbar('配置保存成功');
Logger.info('成本配置保存成功'); Logger.info('成本配置保存成功');
} catch (e) { } catch (e) {
Logger.error('保存成本配置失败: $e'); Logger.error('保存成本配置失败: $e');
_showErrorSnackbar('保存配置失败'); _showErrorSnackbar('保存配置失败');
@ -116,7 +94,6 @@ class TranslationCostController extends GetxController {
} }
} }
/// 重置为默认配置
void resetToDefault() { void resetToDefault() {
translationCostController.text = '0.05'; translationCostController.text = '0.05';
asrCostController.text = '0.02'; asrCostController.text = '0.02';
@ -124,11 +101,9 @@ class TranslationCostController extends GetxController {
aiSummaryCostController.text = '0.01'; aiSummaryCostController.text = '0.01';
dailyThresholdController.text = '10.0'; dailyThresholdController.text = '10.0';
monthlyThresholdController.text = '300.0'; monthlyThresholdController.text = '300.0';
_showInfoSnackbar('已重置为默认配置,请点击保存生效'); _showInfoSnackbar('已重置为默认配置,请点击保存生效');
} }
/// 验证输入
bool _validateInputs() { bool _validateInputs() {
try { try {
final translationCost = double.parse(translationCostController.text); final translationCost = double.parse(translationCostController.text);
@ -137,22 +112,21 @@ class TranslationCostController extends GetxController {
final aiSummaryCost = double.parse(aiSummaryCostController.text); final aiSummaryCost = double.parse(aiSummaryCostController.text);
final dailyThreshold = double.parse(dailyThresholdController.text); final dailyThreshold = double.parse(dailyThresholdController.text);
final monthlyThreshold = double.parse(monthlyThresholdController.text); final monthlyThreshold = double.parse(monthlyThresholdController.text);
if (translationCost < 0 ||
if (translationCost < 0 || asrCost < 0 || ttsCost < 0 || aiSummaryCost < 0) { asrCost < 0 ||
ttsCost < 0 ||
aiSummaryCost < 0) {
_showErrorSnackbar('成本不能为负数'); _showErrorSnackbar('成本不能为负数');
return false; return false;
} }
if (dailyThreshold < 0 || monthlyThreshold < 0) { if (dailyThreshold < 0 || monthlyThreshold < 0) {
_showErrorSnackbar('预警阈值不能为负数'); _showErrorSnackbar('预警阈值不能为负数');
return false; return false;
} }
if (dailyThreshold > monthlyThreshold) { if (dailyThreshold > monthlyThreshold) {
_showErrorSnackbar('日预警阈值不能大于月预警阈值'); _showErrorSnackbar('日预警阈值不能大于月预警阈值');
return false; return false;
} }
return true; return true;
} catch (e) { } catch (e) {
_showErrorSnackbar('请输入有效的数字'); _showErrorSnackbar('请输入有效的数字');
@ -160,13 +134,10 @@ class TranslationCostController extends GetxController {
} }
} }
/// 计算预估月成本
double calculateEstimatedMonthlyCost() { double calculateEstimatedMonthlyCost() {
try { try {
final dailyStats = _usageService.todayStats.value; final dailyStats = _usageService.todayStats.value;
if (dailyStats.estimatedCost == 0) return 0.0; if (dailyStats.estimatedCost == 0) return 0.0;
// 简单按30天计算
return dailyStats.estimatedCost * 30; return dailyStats.estimatedCost * 30;
} catch (e) { } catch (e) {
Logger.error('计算预估月成本失败: $e'); Logger.error('计算预估月成本失败: $e');
@ -174,84 +145,69 @@ class TranslationCostController extends GetxController {
} }
} }
/// 获取成本节约建议
List<String> getCostSavingTips() { List<String> getCostSavingTips() {
final tips = <String>[]; final tips = <String>[];
try { try {
final dailyStats = _usageService.todayStats.value; final dailyStats = _usageService.todayStats.value;
// 基于使用情况给出建议
if (dailyStats.translationApiCalls > 100) { if (dailyStats.translationApiCalls > 100) {
tips.add('考虑批量翻译以减少API调用次数'); tips.add('考虑批量翻译以减少API调用次数');
} }
if (dailyStats.ttsApiCalls > 50) { if (dailyStats.ttsApiCalls > 50) {
tips.add('可以关闭自动语音播放以节省TTS成本'); tips.add('可以关闭自动语音播放以节省TTS成本');
} }
if (dailyStats.aiSummaryApiCalls > 10) { if (dailyStats.aiSummaryApiCalls > 10) {
tips.add('AI摘要功能按需使用,避免频繁调用'); tips.add('AI摘要功能按需使用,避免频繁调用');
} }
if (dailyStats.effectiveAudioDuration > 3600) {
if (dailyStats.effectiveAudioDuration > 3600) { // 超过1小时
tips.add('长时间录音会增加ASR成本,建议分段录音'); tips.add('长时间录音会增加ASR成本,建议分段录音');
} }
if (tips.isEmpty) { if (tips.isEmpty) {
tips.add('您的使用习惯很好,继续保持!'); tips.add('您的使用习惯很好,继续保持!');
} }
} catch (e) { } catch (e) {
Logger.error('获取成本节约建议失败: $e'); Logger.error('获取成本节约建议失败: $e');
tips.add('暂无建议'); tips.add('暂无建议');
} }
return tips; return tips;
} }
/// 显示成功提示
void _showSuccessSnackbar(String message) { void _showSuccessSnackbar(String message) {
Get.snackbar( Get.snackbar(
'成功', '成功',
message, message,
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.primary.withOpacity(0.1), backgroundColor: Get.theme.colorScheme.primary.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.primary, colorText: Get.theme.colorScheme.primary,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
); );
} }
/// 显示错误提示
void _showErrorSnackbar(String message) { void _showErrorSnackbar(String message) {
Get.snackbar( Get.snackbar(
'错误', '错误',
message, message,
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.error.withOpacity(0.1), backgroundColor: Get.theme.colorScheme.error.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.error, colorText: Get.theme.colorScheme.error,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
); );
} }
/// 显示信息提示
void _showInfoSnackbar(String message) { void _showInfoSnackbar(String message) {
Get.snackbar( Get.snackbar(
'提示', '提示',
message, message,
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.surface.withOpacity(0.9), backgroundColor: Get.theme.colorScheme.surface.withValues(alpha: 0.9),
colorText: Get.theme.colorScheme.onSurface, colorText: Get.theme.colorScheme.onSurface,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
); );
} }
/// 格式化成本显示
String formatCost(double cost) { String formatCost(double cost) {
return '¥${cost.toStringAsFixed(2)}'; return '¥${cost.toStringAsFixed(2)}';
} }
/// 格式化百分比显示
String formatPercentage(double percentage) { String formatPercentage(double percentage) {
return '${percentage.toStringAsFixed(1)}%'; return '${percentage.toStringAsFixed(1)}%';
} }

62
lib/modules/translation/controllers/translation_stats_controller.dart → lib/modules/usage/controllers/translation_stats_controller.dart

@ -1,21 +1,16 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart';
import '../../../data/services/translation_usage_service.dart'; import '../../../data/services/translation_usage_service.dart';
import '../../../data/models/translation_usage_stats.dart'; import '../../../data/models/translation_usage_stats.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
/// 翻译统计页面控制器
class TranslationStatsController extends GetxController { class TranslationStatsController extends GetxController {
// 依赖注入
final TranslationUsageService _usageService = final TranslationUsageService _usageService =
Get.find<TranslationUsageService>(); Get.find<TranslationUsageService>();
// 状态变量
final isLoading = false.obs; final isLoading = false.obs;
final selectedTabIndex = 0.obs; // 0=今日, 1=本周, 2=本月, 3=历史 final selectedTabIndex = 0.obs;
final selectedTimeRange = '今日'.obs; final selectedTimeRange = '今日'.obs;
// 统计数据
final Rx<TranslationUsageStats> todayStats = final Rx<TranslationUsageStats> todayStats =
TranslationUsageStats.empty('').obs; TranslationUsageStats.empty('').obs;
final Rx<TranslationUsageStats> weekStats = final Rx<TranslationUsageStats> weekStats =
@ -25,15 +20,12 @@ 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.realtimeStats.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;
final RxDouble monthlyCostThreshold = 300.0.obs; final RxDouble monthlyCostThreshold = 300.0.obs;
// 图表数据
final RxList<Map<String, dynamic>> chartData = <Map<String, dynamic>>[].obs; final RxList<Map<String, dynamic>> chartData = <Map<String, dynamic>>[].obs;
@override @override
@ -42,9 +34,7 @@ class TranslationStatsController extends GetxController {
_initializeData(); _initializeData();
} }
/// 初始化数据
void _initializeData() { void _initializeData() {
// 监听使用服务的数据变化
ever(_usageService.todayStats, (stats) { ever(_usageService.todayStats, (stats) {
todayStats.value = stats; todayStats.value = stats;
_updateChartData(); _updateChartData();
@ -62,28 +52,21 @@ class TranslationStatsController extends GetxController {
monthlyCostThreshold.value = threshold; monthlyCostThreshold.value = threshold;
}); });
// 初始加载数据
loadAllStats(); loadAllStats();
} }
/// 加载所有统计数据
Future<void> loadAllStats() async { Future<void> loadAllStats() async {
isLoading.value = true; isLoading.value = true;
try { try {
// 加载今日数据
todayStats.value = _usageService.todayStats.value; todayStats.value = _usageService.todayStats.value;
// 加载本周数据
await _loadWeekStats(); await _loadWeekStats();
// 加载本月数据
await _loadMonthStats(); await _loadMonthStats();
// 加载历史数据(最近7天)
await _loadHistoryStats(); await _loadHistoryStats();
// 更新图表数据
_updateChartData(); _updateChartData();
Logger.info('统计数据加载完成'); Logger.info('统计数据加载完成');
@ -93,7 +76,8 @@ class TranslationStatsController extends GetxController {
'加载失败', '加载失败',
'统计数据加载失败,请稍后重试', '统计数据加载失败,请稍后重试',
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.error.withOpacity(0.1), backgroundColor:
Get.theme.colorScheme.error.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.error, colorText: Get.theme.colorScheme.error,
); );
} finally { } finally {
@ -101,7 +85,6 @@ class TranslationStatsController extends GetxController {
} }
} }
/// 加载本周统计数据
Future<void> _loadWeekStats() async { Future<void> _loadWeekStats() async {
try { try {
final now = DateTime.now(); final now = DateTime.now();
@ -119,7 +102,6 @@ class TranslationStatsController extends GetxController {
} }
} }
/// 加载本月统计数据
Future<void> _loadMonthStats() async { Future<void> _loadMonthStats() async {
try { try {
final now = DateTime.now(); final now = DateTime.now();
@ -130,7 +112,6 @@ class TranslationStatsController extends GetxController {
} }
} }
/// 加载历史统计数据
Future<void> _loadHistoryStats() async { Future<void> _loadHistoryStats() async {
try { try {
final endDate = DateTime.now(); final endDate = DateTime.now();
@ -141,14 +122,12 @@ class TranslationStatsController extends GetxController {
endDate: endDate, endDate: endDate,
); );
// 按日期排序
historyStats.sort((a, b) => b.date.compareTo(a.date)); historyStats.sort((a, b) => b.date.compareTo(a.date));
} catch (e) { } catch (e) {
Logger.error('加载历史统计失败: $e'); Logger.error('加载历史统计失败: $e');
} }
} }
/// 聚合统计数据
TranslationUsageStats _aggregateStats( TranslationUsageStats _aggregateStats(
List<TranslationUsageStats> statsList, String period) { List<TranslationUsageStats> statsList, String period) {
if (statsList.isEmpty) { if (statsList.isEmpty) {
@ -181,13 +160,11 @@ class TranslationStatsController extends GetxController {
totalSessions += stats.sessionCount; totalSessions += stats.sessionCount;
totalCost += stats.estimatedCost; totalCost += stats.estimatedCost;
// 合并语言对使用统计(使用兼容性getter)
stats.languagePairUsage.forEach((key, value) { stats.languagePairUsage.forEach((key, value) {
combinedLanguagePairUsage[key] = combinedLanguagePairUsage[key] =
(combinedLanguagePairUsage[key] ?? 0) + value; (combinedLanguagePairUsage[key] ?? 0) + value;
}); });
// 合并模式使用统计(使用兼容性getter)
stats.modeUsage.forEach((key, value) { stats.modeUsage.forEach((key, value) {
combinedModeUsage[key] = (combinedModeUsage[key] ?? 0) + value; combinedModeUsage[key] = (combinedModeUsage[key] ?? 0) + value;
}); });
@ -211,13 +188,11 @@ class TranslationStatsController extends GetxController {
); );
} }
/// 更新图表数据
void _updateChartData() { void _updateChartData() {
final List<Map<String, dynamic>> data = []; final List<Map<String, dynamic>> data = [];
// 根据选中的时间范围生成图表数据
switch (selectedTabIndex.value) { switch (selectedTabIndex.value) {
case 0: // 今日 case 0:
data.add({ data.add({
'label': '今日', 'label': '今日',
'apiCalls': todayStats.value.totalApiCalls, 'apiCalls': todayStats.value.totalApiCalls,
@ -225,7 +200,7 @@ class TranslationStatsController extends GetxController {
'characters': todayStats.value.totalCharacterCount, 'characters': todayStats.value.totalCharacterCount,
}); });
break; break;
case 1: // 本周 case 1:
data.add({ data.add({
'label': '本周', 'label': '本周',
'apiCalls': weekStats.value.totalApiCalls, 'apiCalls': weekStats.value.totalApiCalls,
@ -233,7 +208,7 @@ class TranslationStatsController extends GetxController {
'characters': weekStats.value.totalCharacterCount, 'characters': weekStats.value.totalCharacterCount,
}); });
break; break;
case 2: // 本月 case 2:
data.add({ data.add({
'label': '本月', 'label': '本月',
'apiCalls': monthStats.value.totalApiCalls, 'apiCalls': monthStats.value.totalApiCalls,
@ -241,7 +216,7 @@ class TranslationStatsController extends GetxController {
'characters': monthStats.value.totalCharacterCount, 'characters': monthStats.value.totalCharacterCount,
}); });
break; break;
case 3: // 历史 case 3:
for (final stats in historyStats.take(7)) { for (final stats in historyStats.take(7)) {
data.add({ data.add({
'label': '${stats.date.month}/${stats.date.day}', 'label': '${stats.date.month}/${stats.date.day}',
@ -256,7 +231,6 @@ class TranslationStatsController extends GetxController {
chartData.value = data; chartData.value = data;
} }
/// 切换时间范围
void switchTimeRange(int index) { void switchTimeRange(int index) {
selectedTabIndex.value = index; selectedTabIndex.value = index;
@ -278,7 +252,6 @@ class TranslationStatsController extends GetxController {
_updateChartData(); _updateChartData();
} }
/// 获取当前显示的统计数据
TranslationUsageStats get currentStats { TranslationUsageStats get currentStats {
switch (selectedTabIndex.value) { switch (selectedTabIndex.value) {
case 0: case 0:
@ -292,32 +265,30 @@ class TranslationStatsController extends GetxController {
} }
} }
/// 刷新数据
Future<void> refreshData() async { Future<void> refreshData() async {
await loadAllStats(); await loadAllStats();
} }
/// 清理过期数据
void cleanupOldData() { void cleanupOldData() {
_usageService.cleanupOldData(); _usageService.cleanupOldData();
Get.snackbar( Get.snackbar(
'清理完成', '清理完成',
'已清理90天前的过期数据', '已清理90天前的过期数据',
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.primary.withOpacity(0.1), backgroundColor:
Get.theme.colorScheme.primary.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.primary, colorText: Get.theme.colorScheme.primary,
); );
} }
/// 导出统计数据
Future<void> exportStats() async { Future<void> exportStats() async {
try { try {
// TODO: 实现数据导出功能
Get.snackbar( Get.snackbar(
'导出功能', '导出功能',
'数据导出功能正在开发中', '数据导出功能正在开发中',
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Get.theme.colorScheme.primary.withOpacity(0.1), backgroundColor:
Get.theme.colorScheme.primary.withValues(alpha: 0.1),
colorText: Get.theme.colorScheme.primary, colorText: Get.theme.colorScheme.primary,
); );
} catch (e) { } catch (e) {
@ -325,27 +296,24 @@ class TranslationStatsController extends GetxController {
} }
} }
/// 格式化时长显示
String formatDuration(int seconds) { String formatDuration(int seconds) {
if (seconds < 60) { if (seconds < 60) {
return '${seconds}秒'; return '$seconds秒';
} else if (seconds < 3600) { } else if (seconds < 3600) {
final minutes = seconds ~/ 60; final minutes = seconds ~/ 60;
final remainingSeconds = seconds % 60; final remainingSeconds = seconds % 60;
return '${minutes}分${remainingSeconds}秒'; return '$minutes分$remainingSeconds秒';
} else { } else {
final hours = seconds ~/ 3600; final hours = seconds ~/ 3600;
final minutes = (seconds % 3600) ~/ 60; final minutes = (seconds % 3600) ~/ 60;
return '${hours}小时${minutes}分'; return '$hours小时$minutes分';
} }
} }
/// 格式化成本显示
String formatCost(double cost) { String formatCost(double cost) {
return '¥${cost.toStringAsFixed(2)}'; return '¥${cost.toStringAsFixed(2)}';
} }
/// 格式化数字显示
String formatNumber(int number) { String formatNumber(int number) {
if (number < 1000) { if (number < 1000) {
return number.toString(); return number.toString();
@ -355,4 +323,4 @@ class TranslationStatsController extends GetxController {
return '${(number / 1000000).toStringAsFixed(1)}M'; return '${(number / 1000000).toStringAsFixed(1)}M';
} }
} }
} }

55
lib/modules/translation/views/translation_stats_view.dart → lib/modules/usage/views/translation_stats_view.dart

@ -4,7 +4,6 @@ import 'package:get/get.dart';
import '../controllers/translation_stats_controller.dart'; import '../controllers/translation_stats_controller.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
/// 翻译统计页面
class TranslationStatsView extends GetView<TranslationStatsController> { class TranslationStatsView extends GetView<TranslationStatsController> {
const TranslationStatsView({Key? key}) : super(key: key); const TranslationStatsView({Key? key}) : super(key: key);
@ -21,7 +20,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建应用栏
PreferredSizeWidget _buildAppBar(bool isDarkMode) { PreferredSizeWidget _buildAppBar(bool isDarkMode) {
return AppBar( return AppBar(
leading: IconButton( leading: IconButton(
@ -35,7 +33,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white, backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white,
elevation: 0.5, elevation: 0.5,
title: Text( title: Text(
'翻译统计', // translationStats '翻译统计',
style: TextStyle( style: TextStyle(
fontSize: 17.sp, fontSize: 17.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -44,7 +42,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
centerTitle: true, centerTitle: true,
actions: [ actions: [
// 刷新按钮
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.refresh, Icons.refresh,
@ -52,7 +49,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
onPressed: controller.refreshData, onPressed: controller.refreshData,
), ),
// 更多操作菜单
PopupMenuButton<String>( PopupMenuButton<String>(
icon: Icon( icon: Icon(
Icons.more_vert, Icons.more_vert,
@ -68,12 +64,12 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
break; break;
} }
}, },
itemBuilder: (context) => [ itemBuilder: (context) => const [
const PopupMenuItem( PopupMenuItem(
value: 'cleanup', value: 'cleanup',
child: Text('清理过期数据'), child: Text('清理过期数据'),
), ),
const PopupMenuItem( PopupMenuItem(
value: 'export', value: 'export',
child: Text('导出数据'), child: Text('导出数据'),
), ),
@ -83,42 +79,32 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建加载组件
Widget _buildLoadingWidget() { Widget _buildLoadingWidget() {
return const Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
} }
/// 构建主体内容
Widget _buildBody(bool isDarkMode) { Widget _buildBody(bool isDarkMode) {
return Column( return Column(
children: [ children: [
// 时间范围选择器
_buildTimeRangeSelector(isDarkMode), _buildTimeRangeSelector(isDarkMode),
// 统计内容
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
child: Column( child: Column(
children: [ children: [
// 实时统计卡片
if (controller.selectedTabIndex.value == 0) if (controller.selectedTabIndex.value == 0)
_buildRealtimeStatsCard(isDarkMode), _buildRealtimeStatsCard(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// 概览统计卡片
_buildOverviewCard(isDarkMode), _buildOverviewCard(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// API调用统计
_buildApiCallsCard(isDarkMode), _buildApiCallsCard(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// 数据量统计
_buildDataVolumeCard(isDarkMode), _buildDataVolumeCard(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// 成本统计
_buildCostCard(isDarkMode), _buildCostCard(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// 使用行为统计
_buildUsageBehaviorCard(isDarkMode), _buildUsageBehaviorCard(isDarkMode),
], ],
), ),
@ -128,7 +114,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建时间范围选择器
Widget _buildTimeRangeSelector(bool isDarkMode) { Widget _buildTimeRangeSelector(bool isDarkMode) {
return Container( return Container(
margin: EdgeInsets.all(16.w), margin: EdgeInsets.all(16.w),
@ -137,7 +122,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -154,7 +139,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建时间范围标签
Widget _buildTimeRangeTab(String title, int index, bool isDarkMode) { Widget _buildTimeRangeTab(String title, int index, bool isDarkMode) {
final isSelected = controller.selectedTabIndex.value == index; final isSelected = controller.selectedTabIndex.value == index;
@ -185,7 +169,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建实时统计卡片
Widget _buildRealtimeStatsCard(bool isDarkMode) { Widget _buildRealtimeStatsCard(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -194,7 +177,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -272,7 +255,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建概览卡片
Widget _buildOverviewCard(bool isDarkMode) { Widget _buildOverviewCard(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -281,7 +263,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -358,7 +340,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建统计项
Widget _buildStatItem( Widget _buildStatItem(
String label, String value, IconData icon, bool isDarkMode) { String label, String value, IconData icon, bool isDarkMode) {
return Column( return Column(
@ -389,7 +370,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建API调用统计卡片
Widget _buildApiCallsCard(bool isDarkMode) { Widget _buildApiCallsCard(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -398,7 +378,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -452,7 +432,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建API调用行
Widget _buildApiCallRow( Widget _buildApiCallRow(
String label, int count, IconData icon, bool isDarkMode) { String label, int count, IconData icon, bool isDarkMode) {
return Row( return Row(
@ -484,7 +463,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建数据量统计卡片
Widget _buildDataVolumeCard(bool isDarkMode) { Widget _buildDataVolumeCard(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -493,7 +471,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -575,7 +553,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建成本统计卡片
Widget _buildCostCard(bool isDarkMode) { Widget _buildCostCard(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -584,7 +561,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -614,11 +591,10 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Obx(() => Column( Obx(() => Column(
children: [ children: [
// 当前成本
Container( Container(
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1), color: AppColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
child: Row( child: Row(
@ -645,7 +621,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
), ),
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
// 预警阈值
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -694,7 +669,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建使用行为统计卡片
Widget _buildUsageBehaviorCard(bool isDarkMode) { Widget _buildUsageBehaviorCard(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -703,7 +677,7 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -733,7 +707,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Obx(() => Column( Obx(() => Column(
children: [ children: [
// 最常用语言对
_buildUsageBehaviorRow( _buildUsageBehaviorRow(
'最常用语言对', '最常用语言对',
controller.currentStats.mostUsedLanguagePair, controller.currentStats.mostUsedLanguagePair,
@ -741,7 +714,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
isDarkMode, isDarkMode,
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
// 最常用翻译模式
_buildUsageBehaviorRow( _buildUsageBehaviorRow(
'最常用模式', '最常用模式',
controller.currentStats.mostUsedMode, controller.currentStats.mostUsedMode,
@ -755,7 +727,6 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
); );
} }
/// 构建使用行为行
Widget _buildUsageBehaviorRow( Widget _buildUsageBehaviorRow(
String label, String value, IconData icon, bool isDarkMode) { String label, String value, IconData icon, bool isDarkMode) {
return Row( return Row(
@ -786,4 +757,4 @@ class TranslationStatsView extends GetView<TranslationStatsController> {
], ],
); );
} }
} }

4
lib/routes/app_pages.dart

@ -34,8 +34,8 @@ import '../modules/timbre/views/timbre_view.dart';
import '../modules/translation/views/translation_view.dart'; import '../modules/translation/views/translation_view.dart';
import '../modules/translation/bindings/translation_binding.dart'; import '../modules/translation/bindings/translation_binding.dart';
import '../modules/translation/views/translation_history_view.dart'; import '../modules/translation/views/translation_history_view.dart';
import '../modules/translation/views/translation_stats_view.dart'; import '../modules/usage/views/translation_stats_view.dart';
import '../modules/translation/bindings/translation_stats_binding.dart'; import '../modules/usage/bindings/translation_stats_binding.dart';
import '../modules/translation/bindings/translation_list_binding.dart'; import '../modules/translation/bindings/translation_list_binding.dart';
import '../modules/translation/views/translation_list_view.dart'; import '../modules/translation/views/translation_list_view.dart';
import '../modules/translation/bindings/translation_history_binding.dart'; import '../modules/translation/bindings/translation_history_binding.dart';

Loading…
Cancel
Save