|
|
@ -106,80 +106,95 @@ class MeetingObjectStorageRecord { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ============== 单日统计 ============== |
|
|
// 会议音频文件转文字单日统计 |
|
|
// 会议模块用量单日统计 |
|
|
class MeetingAsrDailyStats { |
|
|
class MeetingUsageDailyStats { |
|
|
|
|
|
String date; // 日期字符串: YYYY-MM-DD |
|
|
String date; // 日期字符串: YYYY-MM-DD |
|
|
String userId; // 用户ID |
|
|
String userId; // 用户ID |
|
|
List<MeetingAsrRecord> items; // 当天所有单次ASR |
|
|
List<MeetingAsrRecord> items; // 当天所有单次ASR |
|
|
List<MeetingObjectStorageRecord> storageItems; // 当天所有单次对象存储上传记录 |
|
|
|
|
|
|
|
|
|
|
|
MeetingUsageDailyStats({ |
|
|
MeetingAsrDailyStats({ |
|
|
required this.date, |
|
|
required this.date, |
|
|
required this.userId, |
|
|
required this.userId, |
|
|
required this.items, |
|
|
required this.items, |
|
|
required this.storageItems, |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// asr聚合计算 |
|
|
// 计算型聚合,不冗余存储 |
|
|
int get asrCalls => items.length; |
|
|
int get asrCalls => items.length; |
|
|
int get audioSecondsTotal => items.fold(0, (s, r) => s + r.audioSeconds); |
|
|
int get audioSecondsTotal => items.fold(0, (s, r) => s + r.audioSeconds); |
|
|
int get audioBytesTotal => items.fold(0, (s, r) => s + r.fileSizeBytes); |
|
|
int get audioBytesTotal => items.fold(0, (s, r) => s + r.fileSizeBytes); |
|
|
int get transcriptBytesTotal => |
|
|
int get transcriptBytesTotal => |
|
|
items.fold(0, (s, r) => s + r.transcriptBytes); |
|
|
items.fold(0, (s, r) => s + r.transcriptBytes); |
|
|
|
|
|
|
|
|
// 对象存储聚合计算 |
|
|
|
|
|
int get storageUploads => storageItems.length; |
|
|
|
|
|
int get storageBytesTotal => |
|
|
|
|
|
storageItems.fold(0, (s, r) => s + r.storageBytes); |
|
|
|
|
|
int get transferBytesTotal => |
|
|
|
|
|
storageItems.fold(0, (s, r) => s + r.transferBytes); |
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() => { |
|
|
Map<String, dynamic> toJson() => { |
|
|
'date': date, |
|
|
'date': date, |
|
|
'userId': userId, |
|
|
'userId': userId, |
|
|
'items': items.map((e) => e.toJson()).toList(), |
|
|
'items': items.map((e) => e.toJson()).toList(), |
|
|
'storageItems': storageItems.map((e) => e.toJson()).toList(), |
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
factory MeetingUsageDailyStats.fromJson(Map<String, dynamic> json) { |
|
|
factory MeetingAsrDailyStats.fromJson(Map<String, dynamic> json) { |
|
|
final asrList = (json['items'] as List? ?? []) |
|
|
final list = (json['items'] as List? ?? []) |
|
|
.map((e) => MeetingAsrRecord.fromJson(Map<String, dynamic>.from(e))) |
|
|
.map((e) => MeetingAsrRecord.fromJson(Map<String, dynamic>.from(e))) |
|
|
.toList(); |
|
|
.toList(); |
|
|
final storageList = (json['storageItems'] as List? ?? []) |
|
|
return MeetingAsrDailyStats( |
|
|
|
|
|
date: json['date'] ?? '', |
|
|
|
|
|
userId: json['userId'] ?? 'anonymous', |
|
|
|
|
|
items: list, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ============== 对象存储单日统计 ============== |
|
|
|
|
|
// 👇 职责单一:仅统计对象存储相关数据 |
|
|
|
|
|
class MeetingStorageDailyStats { |
|
|
|
|
|
String date; // 日期字符串: YYYY-MM-DD |
|
|
|
|
|
String userId; // 用户ID |
|
|
|
|
|
List<MeetingObjectStorageRecord> items; // 当天所有单次对象存储记录 |
|
|
|
|
|
|
|
|
|
|
|
MeetingStorageDailyStats({ |
|
|
|
|
|
required this.date, |
|
|
|
|
|
required this.userId, |
|
|
|
|
|
required this.items, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// 对象存储 聚合计算 |
|
|
|
|
|
int get storageUploads => items.length; |
|
|
|
|
|
int get storageBytesTotal => items.fold(0, (s, r) => s + r.storageBytes); |
|
|
|
|
|
int get transferBytesTotal => items.fold(0, (s, r) => s + r.transferBytes); |
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() => { |
|
|
|
|
|
'date': date, |
|
|
|
|
|
'userId': userId, |
|
|
|
|
|
'items': items.map((e) => e.toJson()).toList(), |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
factory MeetingStorageDailyStats.fromJson(Map<String, dynamic> json) { |
|
|
|
|
|
final storageList = (json['items'] as List? ?? []) |
|
|
.map((e) => |
|
|
.map((e) => |
|
|
MeetingObjectStorageRecord.fromJson(Map<String, dynamic>.from(e))) |
|
|
MeetingObjectStorageRecord.fromJson(Map<String, dynamic>.from(e))) |
|
|
.toList(); |
|
|
.toList(); |
|
|
return MeetingUsageDailyStats( |
|
|
|
|
|
|
|
|
return MeetingStorageDailyStats( |
|
|
date: json['date'] ?? '', |
|
|
date: json['date'] ?? '', |
|
|
userId: json['userId'] ?? 'anonymous', |
|
|
userId: json['userId'] ?? 'anonymous', |
|
|
items: asrList, |
|
|
items: storageList, |
|
|
storageItems: storageList, |
|
|
|
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ============== 历史统计 ============== |
|
|
// 会议音频文件转文字历史统计(所有的用量) |
|
|
// 会议模块用量历史统计(所有的用量) |
|
|
class MeetingAsrHistoryStats { |
|
|
class MeetingUsageHistoryStats { |
|
|
|
|
|
String userId; // 用户ID |
|
|
String userId; // 用户ID |
|
|
int allAsrCalls; // 所有ASR调用次数 |
|
|
int allAsrCalls; // 所有ASR调用次数 |
|
|
int allAudioSeconds; // 所有音频总时长(秒) |
|
|
int allAudioSeconds; // 所有音频总时长(秒) |
|
|
int allAudioFileBytes; // 所有音频文件大小(字节) |
|
|
int allAudioFileBytes; // 所有音频文件大小(字节) |
|
|
int allTranscriptBytes; // 所有转写后的字节数(utf8) |
|
|
int allTranscriptBytes; // 所有转写后的字节数(utf8) |
|
|
int allStorageUploads; // 所有对象存储上传次数 |
|
|
|
|
|
int allStorageBytes; // 所有存储文件总大小(字节) |
|
|
|
|
|
int allTransferBytes; // 所有传输总字节数 |
|
|
|
|
|
|
|
|
|
|
|
MeetingUsageHistoryStats({ |
|
|
MeetingAsrHistoryStats({ |
|
|
required this.userId, |
|
|
required this.userId, |
|
|
required this.allAsrCalls, |
|
|
required this.allAsrCalls, |
|
|
required this.allAudioSeconds, |
|
|
required this.allAudioSeconds, |
|
|
required this.allAudioFileBytes, |
|
|
required this.allAudioFileBytes, |
|
|
required this.allTranscriptBytes, |
|
|
required this.allTranscriptBytes, |
|
|
required this.allStorageUploads, |
|
|
|
|
|
required this.allStorageBytes, |
|
|
|
|
|
required this.allTransferBytes, |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() => { |
|
|
Map<String, dynamic> toJson() => { |
|
|
@ -188,12 +203,9 @@ class MeetingUsageHistoryStats { |
|
|
'allAudioSeconds': allAudioSeconds, |
|
|
'allAudioSeconds': allAudioSeconds, |
|
|
'allAudioFileBytes': allAudioFileBytes, |
|
|
'allAudioFileBytes': allAudioFileBytes, |
|
|
'allTranscriptBytes': allTranscriptBytes, |
|
|
'allTranscriptBytes': allTranscriptBytes, |
|
|
'allStorageUploads': allStorageUploads, |
|
|
|
|
|
'allStorageBytes': allStorageBytes, |
|
|
|
|
|
'allTransferBytes': allTransferBytes, |
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
factory MeetingUsageHistoryStats.fromJson(Map<String, dynamic> json) { |
|
|
factory MeetingAsrHistoryStats.fromJson(Map<String, dynamic> json) { |
|
|
int _asInt(dynamic v) { |
|
|
int _asInt(dynamic v) { |
|
|
if (v == null) return 0; |
|
|
if (v == null) return 0; |
|
|
if (v is int) return v; |
|
|
if (v is int) return v; |
|
|
@ -202,15 +214,79 @@ class MeetingUsageHistoryStats { |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return MeetingUsageHistoryStats( |
|
|
return MeetingAsrHistoryStats( |
|
|
userId: json['userId'] ?? 'anonymous', |
|
|
userId: json['userId'] ?? 'anonymous', |
|
|
allAsrCalls: _asInt(json['allAsrCalls']), |
|
|
allAsrCalls: _asInt(json['allAsrCalls']), |
|
|
allAudioSeconds: _asInt(json['allAudioSeconds']), |
|
|
allAudioSeconds: _asInt(json['allAudioSeconds']), |
|
|
allAudioFileBytes: _asInt(json['allAudioFileBytes']), |
|
|
allAudioFileBytes: _asInt(json['allAudioFileBytes']), |
|
|
allTranscriptBytes: _asInt(json['allTranscriptBytes']), |
|
|
allTranscriptBytes: _asInt(json['allTranscriptBytes']), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ============== 对象存储历史统计 ============== |
|
|
|
|
|
// 👇 职责单一:仅统计对象存储累计数据 |
|
|
|
|
|
class MeetingStorageHistoryStats { |
|
|
|
|
|
String userId; // 用户ID |
|
|
|
|
|
int allStorageUploads; // 所有对象存储上传次数 |
|
|
|
|
|
int allStorageBytes; // 所有存储文件总大小(字节) |
|
|
|
|
|
int allTransferBytes; // 所有传输总字节数 |
|
|
|
|
|
|
|
|
|
|
|
MeetingStorageHistoryStats({ |
|
|
|
|
|
required this.userId, |
|
|
|
|
|
required this.allStorageUploads, |
|
|
|
|
|
required this.allStorageBytes, |
|
|
|
|
|
required this.allTransferBytes, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() => { |
|
|
|
|
|
'userId': userId, |
|
|
|
|
|
'allStorageUploads': allStorageUploads, |
|
|
|
|
|
'allStorageBytes': allStorageBytes, |
|
|
|
|
|
'allTransferBytes': allTransferBytes, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
factory MeetingStorageHistoryStats.fromJson(Map<String, dynamic> json) { |
|
|
|
|
|
int _asInt(dynamic v) { |
|
|
|
|
|
if (v == null) return 0; |
|
|
|
|
|
if (v is int) return v; |
|
|
|
|
|
if (v is num) return v.toInt(); |
|
|
|
|
|
if (v is String) return int.tryParse(v) ?? 0; |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return MeetingStorageHistoryStats( |
|
|
|
|
|
userId: json['userId'] ?? 'anonymous', |
|
|
allStorageUploads: _asInt(json['allStorageUploads']), |
|
|
allStorageUploads: _asInt(json['allStorageUploads']), |
|
|
allStorageBytes: _asInt(json['allStorageBytes']), |
|
|
allStorageBytes: _asInt(json['allStorageBytes']), |
|
|
allTransferBytes: _asInt(json['allTransferBytes']), |
|
|
allTransferBytes: _asInt(json['allTransferBytes']), |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 👇 当需要同时展示两种统计时使用 |
|
|
|
|
|
class MeetingUsageDailySnapshot { |
|
|
|
|
|
String date; |
|
|
|
|
|
String userId; |
|
|
|
|
|
MeetingAsrDailyStats? asrStats; |
|
|
|
|
|
MeetingStorageDailyStats? storageStats; |
|
|
|
|
|
|
|
|
|
|
|
MeetingUsageDailySnapshot({ |
|
|
|
|
|
required this.date, |
|
|
|
|
|
required this.userId, |
|
|
|
|
|
this.asrStats, |
|
|
|
|
|
this.storageStats, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class MeetingUsageHistorySnapshot { |
|
|
|
|
|
String userId; |
|
|
|
|
|
MeetingAsrHistoryStats? asrStats; |
|
|
|
|
|
MeetingStorageHistoryStats? storageStats; |
|
|
|
|
|
|
|
|
|
|
|
MeetingUsageHistorySnapshot({ |
|
|
|
|
|
required this.userId, |
|
|
|
|
|
this.asrStats, |
|
|
|
|
|
this.storageStats, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|