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.
461 lines
16 KiB
461 lines
16 KiB
/// 翻译模式统计数据
|
|
class TranslationModeStats {
|
|
final int apiCalls; // API调用次数
|
|
final int sourceCharacters; // 源文本字符数
|
|
final int effectiveAudioDuration; // 有效音频时长
|
|
final int ttsCharacters; // TTS字符数
|
|
final double cost; // 成本
|
|
|
|
TranslationModeStats({
|
|
this.apiCalls = 0,
|
|
this.sourceCharacters = 0,
|
|
this.effectiveAudioDuration = 0,
|
|
this.ttsCharacters = 0,
|
|
this.cost = 0.0,
|
|
});
|
|
|
|
factory TranslationModeStats.fromJson(Map<String, dynamic> json) {
|
|
return TranslationModeStats(
|
|
apiCalls: json['apiCalls'] ?? 0,
|
|
sourceCharacters: json['sourceCharacters'] ?? 0,
|
|
effectiveAudioDuration: json['effectiveAudioDuration'] ?? 0,
|
|
ttsCharacters: json['ttsCharacters'] ?? 0,
|
|
cost: (json['cost'] ?? 0.0).toDouble(),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'apiCalls': apiCalls,
|
|
'sourceCharacters': sourceCharacters,
|
|
'effectiveAudioDuration': effectiveAudioDuration,
|
|
'ttsCharacters': ttsCharacters,
|
|
'cost': cost,
|
|
};
|
|
}
|
|
|
|
TranslationModeStats copyWith({
|
|
int? apiCalls,
|
|
int? sourceCharacters,
|
|
int? effectiveAudioDuration,
|
|
int? ttsCharacters,
|
|
double? cost,
|
|
}) {
|
|
return TranslationModeStats(
|
|
apiCalls: apiCalls ?? this.apiCalls,
|
|
sourceCharacters: sourceCharacters ?? this.sourceCharacters,
|
|
effectiveAudioDuration:
|
|
effectiveAudioDuration ?? this.effectiveAudioDuration,
|
|
ttsCharacters: ttsCharacters ?? this.ttsCharacters,
|
|
cost: cost ?? this.cost,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 语言对统计数据
|
|
class LanguagePairStats {
|
|
final String languagePair; // 语言对,如"zh-CN→en-US"
|
|
final int translationCount; // 翻译次数
|
|
final int sourceCharacters; // 源文本字符数
|
|
final double cost; // 成本
|
|
|
|
LanguagePairStats({
|
|
required this.languagePair,
|
|
this.translationCount = 0,
|
|
this.sourceCharacters = 0,
|
|
this.cost = 0.0,
|
|
});
|
|
|
|
factory LanguagePairStats.fromJson(Map<String, dynamic> json) {
|
|
return LanguagePairStats(
|
|
languagePair: json['languagePair'] ?? '',
|
|
translationCount: json['translationCount'] ?? 0,
|
|
sourceCharacters: json['sourceCharacters'] ?? 0,
|
|
cost: (json['cost'] ?? 0.0).toDouble(),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'languagePair': languagePair,
|
|
'translationCount': translationCount,
|
|
'sourceCharacters': sourceCharacters,
|
|
'cost': cost,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// 小时统计数据
|
|
class HourlyStats {
|
|
final int hour; // 小时(0-23)
|
|
final int apiCalls; // API调用次数
|
|
final int sourceCharacters; // 源文本字符数
|
|
final int effectiveAudioDuration; // 有效音频时长
|
|
final double cost; // 成本
|
|
|
|
HourlyStats({
|
|
required this.hour,
|
|
this.apiCalls = 0,
|
|
this.sourceCharacters = 0,
|
|
this.effectiveAudioDuration = 0,
|
|
this.cost = 0.0,
|
|
});
|
|
|
|
factory HourlyStats.fromJson(Map<String, dynamic> json) {
|
|
return HourlyStats(
|
|
hour: json['hour'] ?? 0,
|
|
apiCalls: json['apiCalls'] ?? 0,
|
|
sourceCharacters: json['sourceCharacters'] ?? 0,
|
|
effectiveAudioDuration: json['effectiveAudioDuration'] ?? 0,
|
|
cost: (json['cost'] ?? 0.0).toDouble(),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'hour': hour,
|
|
'apiCalls': apiCalls,
|
|
'sourceCharacters': sourceCharacters,
|
|
'effectiveAudioDuration': effectiveAudioDuration,
|
|
'cost': cost,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// 翻译使用统计数据模型 - 优化版
|
|
class TranslationUsageStats {
|
|
// 基础信息
|
|
final String id; // 统计记录ID
|
|
final DateTime date; // 统计日期
|
|
final String userId; // 用户ID
|
|
final String timeGranularity; // 时间粒度:realtime/hour/day/week/month
|
|
|
|
// API调用统计 - 精确化
|
|
final int translationApiCalls; // 翻译API调用次数
|
|
final int asrApiCalls; // ASR语音识别调用次数
|
|
final int ttsApiCalls; // TTS语音合成调用次数
|
|
final int aiSummaryApiCalls; // AI摘要生成调用次数(已废弃但保留兼容性)
|
|
|
|
// 精确计费参数统计
|
|
final int sourceTextCharacters; // 源文本字符数(翻译API计费基础)
|
|
final int effectiveAudioDuration; // ASR有效语音时长(秒,ASR计费基础)
|
|
final int ttsTextCharacters; // TTS文本字符数(TTS计费基础)
|
|
final int totalSessionDuration; // 总会话时长(秒,用于分析)
|
|
|
|
// 网络流量统计
|
|
final int asrNetworkBytes; // ASR网络流量(字节)
|
|
final int ttsNetworkBytes; // TTS网络流量(字节)
|
|
final int translationNetworkBytes; // 翻译API网络流量(字节)
|
|
|
|
// 多维度分类统计
|
|
final int sessionCount; // 翻译会话数量
|
|
final Map<String, TranslationModeStats> modeStats; // 按翻译模式分类统计
|
|
final Map<String, LanguagePairStats> languagePairStats; // 按语言对分类统计
|
|
final Map<String, HourlyStats> hourlyStats; // 按小时分类统计(仅日统计包含)
|
|
|
|
// 成本相关
|
|
final double estimatedCost; // 预估成本
|
|
final String currency; // 货币单位
|
|
final Map<String, double> costBreakdown; // 成本分解(ASR/TTS/Translation)
|
|
|
|
TranslationUsageStats({
|
|
required this.id,
|
|
required this.date,
|
|
required this.userId,
|
|
this.timeGranularity = 'day',
|
|
this.translationApiCalls = 0,
|
|
this.asrApiCalls = 0,
|
|
this.ttsApiCalls = 0,
|
|
this.aiSummaryApiCalls = 0,
|
|
this.sourceTextCharacters = 0,
|
|
this.effectiveAudioDuration = 0,
|
|
this.ttsTextCharacters = 0,
|
|
this.totalSessionDuration = 0,
|
|
this.asrNetworkBytes = 0,
|
|
this.ttsNetworkBytes = 0,
|
|
this.translationNetworkBytes = 0,
|
|
this.sessionCount = 0,
|
|
this.modeStats = const {},
|
|
this.languagePairStats = const {},
|
|
this.hourlyStats = const {},
|
|
this.estimatedCost = 0.0,
|
|
this.currency = 'CNY',
|
|
this.costBreakdown = const {},
|
|
});
|
|
|
|
factory TranslationUsageStats.fromJson(Map<String, dynamic> json) {
|
|
// 处理多维度统计数据的反序列化
|
|
final modeStatsMap = <String, TranslationModeStats>{};
|
|
if (json['modeStats'] != null) {
|
|
final modeStatsJson = json['modeStats'] as Map<String, dynamic>;
|
|
modeStatsJson.forEach((key, value) {
|
|
modeStatsMap[key] = TranslationModeStats.fromJson(value);
|
|
});
|
|
}
|
|
|
|
final languagePairStatsMap = <String, LanguagePairStats>{};
|
|
if (json['languagePairStats'] != null) {
|
|
final languagePairStatsJson =
|
|
json['languagePairStats'] as Map<String, dynamic>;
|
|
languagePairStatsJson.forEach((key, value) {
|
|
languagePairStatsMap[key] = LanguagePairStats.fromJson(value);
|
|
});
|
|
}
|
|
|
|
final hourlyStatsMap = <String, HourlyStats>{};
|
|
if (json['hourlyStats'] != null) {
|
|
final hourlyStatsJson = json['hourlyStats'] as Map<String, dynamic>;
|
|
hourlyStatsJson.forEach((key, value) {
|
|
hourlyStatsMap[key] = HourlyStats.fromJson(value);
|
|
});
|
|
}
|
|
|
|
return TranslationUsageStats(
|
|
id: json['id'] ?? '',
|
|
date: DateTime.parse(json['date']),
|
|
userId: json['userId'] ?? '',
|
|
timeGranularity: json['timeGranularity'] ?? 'day',
|
|
translationApiCalls: json['translationApiCalls'] ?? 0,
|
|
asrApiCalls: json['asrApiCalls'] ?? 0,
|
|
ttsApiCalls: json['ttsApiCalls'] ?? 0,
|
|
aiSummaryApiCalls: json['aiSummaryApiCalls'] ?? 0,
|
|
sourceTextCharacters: json['sourceTextCharacters'] ??
|
|
json['sourceCharacterCount'] ??
|
|
0, // 兼容旧字段
|
|
effectiveAudioDuration: json['effectiveAudioDuration'] ??
|
|
json['audioRecordingDuration'] ??
|
|
0, // 兼容旧字段
|
|
ttsTextCharacters: json['ttsTextCharacters'] ??
|
|
json['targetCharacterCount'] ??
|
|
0, // 兼容旧字段
|
|
totalSessionDuration: json['totalSessionDuration'] ?? 0,
|
|
asrNetworkBytes: json['asrNetworkBytes'] ?? 0,
|
|
ttsNetworkBytes: json['ttsNetworkBytes'] ?? 0,
|
|
translationNetworkBytes: json['translationNetworkBytes'] ?? 0,
|
|
sessionCount: json['sessionCount'] ?? 0,
|
|
modeStats: modeStatsMap,
|
|
languagePairStats: languagePairStatsMap,
|
|
hourlyStats: hourlyStatsMap,
|
|
estimatedCost: (json['estimatedCost'] ?? 0.0).toDouble(),
|
|
currency: json['currency'] ?? 'CNY',
|
|
costBreakdown: Map<String, double>.from(json['costBreakdown'] ?? {}),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
// 序列化多维度统计数据
|
|
final modeStatsJson = <String, dynamic>{};
|
|
modeStats.forEach((key, value) {
|
|
modeStatsJson[key] = value.toJson();
|
|
});
|
|
|
|
final languagePairStatsJson = <String, dynamic>{};
|
|
languagePairStats.forEach((key, value) {
|
|
languagePairStatsJson[key] = value.toJson();
|
|
});
|
|
|
|
final hourlyStatsJson = <String, dynamic>{};
|
|
hourlyStats.forEach((key, value) {
|
|
hourlyStatsJson[key] = value.toJson();
|
|
});
|
|
|
|
return {
|
|
'id': id,
|
|
'date': date.toIso8601String(),
|
|
'userId': userId,
|
|
'timeGranularity': timeGranularity,
|
|
'translationApiCalls': translationApiCalls,
|
|
'asrApiCalls': asrApiCalls,
|
|
'ttsApiCalls': ttsApiCalls,
|
|
'aiSummaryApiCalls': aiSummaryApiCalls,
|
|
'sourceTextCharacters': sourceTextCharacters,
|
|
'effectiveAudioDuration': effectiveAudioDuration,
|
|
'ttsTextCharacters': ttsTextCharacters,
|
|
'totalSessionDuration': totalSessionDuration,
|
|
'asrNetworkBytes': asrNetworkBytes,
|
|
'ttsNetworkBytes': ttsNetworkBytes,
|
|
'translationNetworkBytes': translationNetworkBytes,
|
|
'sessionCount': sessionCount,
|
|
'modeStats': modeStatsJson,
|
|
'languagePairStats': languagePairStatsJson,
|
|
'hourlyStats': hourlyStatsJson,
|
|
'estimatedCost': estimatedCost,
|
|
'currency': currency,
|
|
'costBreakdown': costBreakdown,
|
|
// 保持向后兼容性
|
|
'sourceCharacterCount': sourceTextCharacters,
|
|
'targetCharacterCount': ttsTextCharacters,
|
|
'audioRecordingDuration': effectiveAudioDuration,
|
|
'ttsPlaybackDuration': totalSessionDuration,
|
|
};
|
|
}
|
|
|
|
/// 创建空的统计记录
|
|
factory TranslationUsageStats.empty(String userId) {
|
|
return TranslationUsageStats(
|
|
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
|
date: DateTime.now(),
|
|
userId: userId,
|
|
);
|
|
}
|
|
|
|
/// 复制并更新统计数据
|
|
TranslationUsageStats copyWith({
|
|
String? id,
|
|
DateTime? date,
|
|
String? userId,
|
|
String? timeGranularity,
|
|
int? translationApiCalls,
|
|
int? asrApiCalls,
|
|
int? ttsApiCalls,
|
|
int? aiSummaryApiCalls,
|
|
int? sourceTextCharacters,
|
|
int? effectiveAudioDuration,
|
|
int? ttsTextCharacters,
|
|
int? totalSessionDuration,
|
|
int? asrNetworkBytes,
|
|
int? ttsNetworkBytes,
|
|
int? translationNetworkBytes,
|
|
int? sessionCount,
|
|
Map<String, TranslationModeStats>? modeStats,
|
|
Map<String, LanguagePairStats>? languagePairStats,
|
|
Map<String, HourlyStats>? hourlyStats,
|
|
double? estimatedCost,
|
|
String? currency,
|
|
Map<String, double>? costBreakdown,
|
|
}) {
|
|
return TranslationUsageStats(
|
|
id: id ?? this.id,
|
|
date: date ?? this.date,
|
|
userId: userId ?? this.userId,
|
|
timeGranularity: timeGranularity ?? this.timeGranularity,
|
|
translationApiCalls: translationApiCalls ?? this.translationApiCalls,
|
|
asrApiCalls: asrApiCalls ?? this.asrApiCalls,
|
|
ttsApiCalls: ttsApiCalls ?? this.ttsApiCalls,
|
|
aiSummaryApiCalls: aiSummaryApiCalls ?? this.aiSummaryApiCalls,
|
|
sourceTextCharacters: sourceTextCharacters ?? this.sourceTextCharacters,
|
|
effectiveAudioDuration:
|
|
effectiveAudioDuration ?? this.effectiveAudioDuration,
|
|
ttsTextCharacters: ttsTextCharacters ?? this.ttsTextCharacters,
|
|
totalSessionDuration: totalSessionDuration ?? this.totalSessionDuration,
|
|
asrNetworkBytes: asrNetworkBytes ?? this.asrNetworkBytes,
|
|
ttsNetworkBytes: ttsNetworkBytes ?? this.ttsNetworkBytes,
|
|
translationNetworkBytes:
|
|
translationNetworkBytes ?? this.translationNetworkBytes,
|
|
sessionCount: sessionCount ?? this.sessionCount,
|
|
modeStats: modeStats ?? this.modeStats,
|
|
languagePairStats: languagePairStats ?? this.languagePairStats,
|
|
hourlyStats: hourlyStats ?? this.hourlyStats,
|
|
estimatedCost: estimatedCost ?? this.estimatedCost,
|
|
currency: currency ?? this.currency,
|
|
costBreakdown: costBreakdown ?? this.costBreakdown,
|
|
);
|
|
}
|
|
|
|
/// 获取总API调用次数
|
|
int get totalApiCalls =>
|
|
translationApiCalls + asrApiCalls + ttsApiCalls + aiSummaryApiCalls;
|
|
|
|
/// 获取总字符数(精确计费基础)
|
|
int get totalCharacterCount => sourceTextCharacters + ttsTextCharacters;
|
|
|
|
/// 获取总音频时长(包含有效时长和总会话时长)
|
|
int get totalAudioDuration => effectiveAudioDuration + totalSessionDuration;
|
|
|
|
/// 获取总网络流量
|
|
int get totalNetworkBytes =>
|
|
asrNetworkBytes + ttsNetworkBytes + translationNetworkBytes;
|
|
|
|
/// 获取最常用的语言对
|
|
String get mostUsedLanguagePair {
|
|
if (languagePairStats.isEmpty) return 'null';
|
|
return languagePairStats.entries
|
|
.reduce((a, b) =>
|
|
a.value.translationCount > b.value.translationCount ? a : b)
|
|
.key;
|
|
}
|
|
|
|
/// 获取最常用的翻译模式
|
|
String get mostUsedMode {
|
|
if (modeStats.isEmpty) return 'null';
|
|
return modeStats.entries
|
|
.reduce((a, b) => a.value.apiCalls > b.value.apiCalls ? a : b)
|
|
.key;
|
|
}
|
|
|
|
// 向后兼容性getter
|
|
/// @deprecated 使用 sourceTextCharacters 替代
|
|
int get sourceCharacterCount => sourceTextCharacters;
|
|
|
|
/// @deprecated 使用 ttsTextCharacters 替代
|
|
int get targetCharacterCount => ttsTextCharacters;
|
|
|
|
/// @deprecated 使用 effectiveAudioDuration 替代
|
|
int get audioRecordingDuration => effectiveAudioDuration;
|
|
|
|
/// @deprecated 使用 totalSessionDuration 替代
|
|
int get ttsPlaybackDuration => totalSessionDuration;
|
|
|
|
/// @deprecated 使用 languagePairStats 替代
|
|
Map<String, int> get languagePairUsage {
|
|
final result = <String, int>{};
|
|
languagePairStats.forEach((key, value) {
|
|
result[key] = value.translationCount;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/// @deprecated 使用 modeStats 替代
|
|
Map<String, int> get modeUsage {
|
|
final result = <String, int>{};
|
|
modeStats.forEach((key, value) {
|
|
result[key] = value.apiCalls;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// 实时统计数据模型(用于当前会话)- 优化版
|
|
class RealtimeUsageStats {
|
|
// 精确计费参数
|
|
int translationCount = 0; // 当前会话翻译次数
|
|
int sourceTextCharacters = 0; // 当前会话源文本字符数(翻译计费基础)
|
|
int ttsTextCharacters = 0; // 当前会话TTS文本字符数(TTS计费基础)
|
|
int effectiveAudioDuration = 0; // 当前会话有效语音时长(ASR计费基础)
|
|
int totalSessionDuration = 0; // 当前会话总时长
|
|
|
|
// 会话信息
|
|
String currentLanguagePair = ''; // 当前语言对
|
|
String currentMode = ''; // 当前翻译模式
|
|
DateTime sessionStartTime = DateTime.now(); // 会话开始时间
|
|
|
|
/// 重置统计数据
|
|
void reset() {
|
|
translationCount = 0;
|
|
sourceTextCharacters = 0;
|
|
ttsTextCharacters = 0;
|
|
effectiveAudioDuration = 0;
|
|
totalSessionDuration = 0;
|
|
currentLanguagePair = '';
|
|
currentMode = '';
|
|
sessionStartTime = DateTime.now();
|
|
}
|
|
|
|
/// 获取会话持续时间(分钟)
|
|
int get sessionDurationMinutes {
|
|
return DateTime.now().difference(sessionStartTime).inMinutes;
|
|
}
|
|
|
|
// 向后兼容性getter
|
|
/// @deprecated 使用 sourceTextCharacters 替代
|
|
int get sourceCharacters => sourceTextCharacters;
|
|
|
|
/// @deprecated 使用 ttsTextCharacters 替代
|
|
int get targetCharacters => ttsTextCharacters;
|
|
|
|
/// @deprecated 使用 effectiveAudioDuration 替代
|
|
int get recordingDuration => effectiveAudioDuration;
|
|
|
|
/// @deprecated 使用 totalSessionDuration 替代
|
|
int get ttsPlayDuration => totalSessionDuration;
|
|
}
|
|
|