feat: YearlyReportUI Update

This commit is contained in:
xkeyC 2025-12-17 16:49:58 +08:00
parent 6ec973144e
commit 70c47a8b85
2 changed files with 842 additions and 327 deletions

View File

@ -45,6 +45,20 @@ class YearlyReportData {
final int yearlyDeathCount; //
final int yearlySelfKillCount; //
//
final int? mostPlayedMonth; // (1-12)
final int mostPlayedMonthCount; //
final int? leastPlayedMonth; // (1-12, )
final int leastPlayedMonthCount; //
// /线
final int longestPlayStreak; //
final DateTime? playStreakStartDate; //
final DateTime? playStreakEndDate; //
final int longestOfflineStreak; // 线
final DateTime? offlineStreakStartDate; // 线
final DateTime? offlineStreakEndDate; // 线
// ()
final Map<String, int> vehiclePilotedDetails; //
final Map<String, int> accountSessionDetails; //
@ -77,6 +91,16 @@ class YearlyReportData {
required this.yearlyKillCount,
required this.yearlyDeathCount,
required this.yearlySelfKillCount,
this.mostPlayedMonth,
required this.mostPlayedMonthCount,
this.leastPlayedMonth,
required this.leastPlayedMonthCount,
required this.longestPlayStreak,
this.playStreakStartDate,
this.playStreakEndDate,
required this.longestOfflineStreak,
this.offlineStreakStartDate,
this.offlineStreakEndDate,
required this.vehiclePilotedDetails,
required this.accountSessionDetails,
required this.locationDetails,
@ -140,6 +164,20 @@ class YearlyReportData {
'yearlyDeathCount': yearlyDeathCount,
'yearlySelfKillCount': yearlySelfKillCount,
//
'mostPlayedMonth': mostPlayedMonth,
'mostPlayedMonthCount': mostPlayedMonthCount,
'leastPlayedMonth': leastPlayedMonth,
'leastPlayedMonthCount': leastPlayedMonthCount,
// /线
'longestPlayStreak': longestPlayStreak,
'playStreakStartDateUtc': _toUtcTimestamp(playStreakStartDate),
'playStreakEndDateUtc': _toUtcTimestamp(playStreakEndDate),
'longestOfflineStreak': longestOfflineStreak,
'offlineStreakStartDateUtc': _toUtcTimestamp(offlineStreakStartDate),
'offlineStreakEndDateUtc': _toUtcTimestamp(offlineStreakEndDate),
//
'vehiclePilotedDetails': vehiclePilotedDetails,
'accountSessionDetails': accountSessionDetails,
@ -193,6 +231,9 @@ class _LogFileStats {
// 访: ->
Map<String, int> locationVisits = {};
// ( 2s )
DateTime? _lastDeathTime;
//
List<_SessionInfo> yearlySessions = [];
@ -279,9 +320,17 @@ class YearlyReportAnalyzer {
nameMatch ??= _legacyCharacterNamePattern.firstMatch(line);
if (nameMatch != null) {
final playerName = nameMatch.group(1)?.trim();
if (playerName != null && playerName.isNotEmpty && !playerName.contains(' ')) {
if (playerName != null &&
playerName.isNotEmpty &&
!playerName.contains(' ') &&
!playerName.contains('/') &&
!playerName.contains(r'\') &&
!playerName.contains('.')) {
stats.currentPlayerName = playerName;
stats.playerNames.add(playerName);
// ()
if (!stats.playerNames.any((n) => n.toLowerCase() == playerName.toLowerCase())) {
stats.playerNames.add(playerName);
}
stats.firstPlayerName ??= playerName;
}
}
@ -318,7 +367,11 @@ class YearlyReportAnalyzer {
if (deathMatch != null) {
final victimId = deathMatch.group(1)?.trim();
if (victimId != null && stats.currentPlayerName != null && victimId == stats.currentPlayerName) {
stats.deathCount++;
// (2)
if (stats._lastDeathTime == null || lineTime.difference(stats._lastDeathTime!).abs().inSeconds > 2) {
stats.deathCount++;
stats._lastDeathTime = lineTime;
}
}
}
@ -329,17 +382,34 @@ class YearlyReportAnalyzer {
final killerId = legacyDeathMatch.group(3)?.trim();
if (victimId != null && stats.currentPlayerName != null) {
bool isRecent =
stats._lastDeathTime != null && lineTime.difference(stats._lastDeathTime!).abs().inSeconds <= 2;
//
if (victimId == killerId) {
if (victimId == stats.currentPlayerName) {
stats.selfKillCount++;
if (isRecent) {
// ()
// deathCount++退 selfKillCount
if (stats.deathCount > 0) stats.deathCount--;
stats.selfKillCount++;
//
stats._lastDeathTime = lineTime;
} else {
stats.selfKillCount++;
stats._lastDeathTime = lineTime;
}
}
} else {
//
// ()
if (victimId == stats.currentPlayerName) {
stats.deathCount++;
// ()
if (!isRecent) {
stats.deathCount++;
stats._lastDeathTime = lineTime;
}
}
//
// ()
if (killerId == stats.currentPlayerName) {
stats.killCount++;
}
@ -601,6 +671,90 @@ class YearlyReportAnalyzer {
final sortedLocations = locationDetails.entries.toList()..sort((a, b) => b.value.compareTo(a.value));
final topLocations = sortedLocations.take(10).toList();
//
final Map<int, int> monthlyPlayCount = {};
final Set<DateTime> playDates = {}; // ()
for (final stats in allStats) {
for (final session in stats.yearlySessions) {
final month = session.startTime.month;
monthlyPlayCount[month] = (monthlyPlayCount[month] ?? 0) + 1;
// ()
playDates.add(DateTime(session.startTime.year, session.startTime.month, session.startTime.day));
}
}
int? mostPlayedMonth;
int mostPlayedMonthCount = 0;
int? leastPlayedMonth;
int leastPlayedMonthCount = 0;
if (monthlyPlayCount.isNotEmpty) {
//
for (final entry in monthlyPlayCount.entries) {
if (entry.value > mostPlayedMonthCount) {
mostPlayedMonth = entry.key;
mostPlayedMonthCount = entry.value;
}
}
// ()
leastPlayedMonthCount = monthlyPlayCount.values.first;
for (final entry in monthlyPlayCount.entries) {
if (entry.value <= leastPlayedMonthCount) {
leastPlayedMonth = entry.key;
leastPlayedMonthCount = entry.value;
}
}
}
// 线
int longestPlayStreak = 0;
DateTime? playStreakStartDate;
DateTime? playStreakEndDate;
int longestOfflineStreak = 0;
DateTime? offlineStreakStartDate;
DateTime? offlineStreakEndDate;
if (playDates.isNotEmpty) {
//
final sortedDates = playDates.toList()..sort();
//
int currentStreak = 1;
DateTime streakStart = sortedDates.first;
for (int i = 1; i < sortedDates.length; i++) {
final diff = sortedDates[i].difference(sortedDates[i - 1]).inDays;
if (diff == 1) {
currentStreak++;
} else {
if (currentStreak > longestPlayStreak) {
longestPlayStreak = currentStreak;
playStreakStartDate = streakStart;
playStreakEndDate = sortedDates[i - 1];
}
currentStreak = 1;
streakStart = sortedDates[i];
}
}
//
if (currentStreak > longestPlayStreak) {
longestPlayStreak = currentStreak;
playStreakStartDate = streakStart;
playStreakEndDate = sortedDates.last;
}
// 线 ()
for (int i = 1; i < sortedDates.length; i++) {
final gapDays = sortedDates[i].difference(sortedDates[i - 1]).inDays - 1;
if (gapDays > longestOfflineStreak) {
longestOfflineStreak = gapDays;
offlineStreakStartDate = sortedDates[i - 1].add(const Duration(days: 1));
offlineStreakEndDate = sortedDates[i].subtract(const Duration(days: 1));
}
}
}
return YearlyReportData(
totalLaunchCount: totalLaunchCount,
totalPlayTime: totalPlayTime,
@ -628,6 +782,16 @@ class YearlyReportAnalyzer {
yearlyKillCount: yearlyKillCount,
yearlyDeathCount: yearlyDeathCount,
yearlySelfKillCount: yearlySelfKillCount,
mostPlayedMonth: mostPlayedMonth,
mostPlayedMonthCount: mostPlayedMonthCount,
leastPlayedMonth: leastPlayedMonth,
leastPlayedMonthCount: leastPlayedMonthCount,
longestPlayStreak: longestPlayStreak,
playStreakStartDate: playStreakStartDate,
playStreakEndDate: playStreakEndDate,
longestOfflineStreak: longestOfflineStreak,
offlineStreakStartDate: offlineStreakStartDate,
offlineStreakEndDate: offlineStreakEndDate,
vehiclePilotedDetails: vehiclePilotedDetails,
accountSessionDetails: accountSessionDetails,
locationDetails: locationDetails,

File diff suppressed because it is too large Load Diff