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