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.

214 lines
7.2 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;
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) {
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.withValues(alpha: 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.withValues(alpha: 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.withValues(alpha: 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)}%';
}
}