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 yearlyDeathCount; //
final int yearlySelfKillCount; // 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> vehiclePilotedDetails; //
final Map<String, int> accountSessionDetails; // final Map<String, int> accountSessionDetails; //
@ -77,6 +91,16 @@ class YearlyReportData {
required this.yearlyKillCount, required this.yearlyKillCount,
required this.yearlyDeathCount, required this.yearlyDeathCount,
required this.yearlySelfKillCount, 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.vehiclePilotedDetails,
required this.accountSessionDetails, required this.accountSessionDetails,
required this.locationDetails, required this.locationDetails,
@ -140,6 +164,20 @@ class YearlyReportData {
'yearlyDeathCount': yearlyDeathCount, 'yearlyDeathCount': yearlyDeathCount,
'yearlySelfKillCount': yearlySelfKillCount, '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, 'vehiclePilotedDetails': vehiclePilotedDetails,
'accountSessionDetails': accountSessionDetails, 'accountSessionDetails': accountSessionDetails,
@ -193,6 +231,9 @@ class _LogFileStats {
// 访: -> // 访: ->
Map<String, int> locationVisits = {}; Map<String, int> locationVisits = {};
// ( 2s )
DateTime? _lastDeathTime;
// //
List<_SessionInfo> yearlySessions = []; List<_SessionInfo> yearlySessions = [];
@ -279,9 +320,17 @@ class YearlyReportAnalyzer {
nameMatch ??= _legacyCharacterNamePattern.firstMatch(line); nameMatch ??= _legacyCharacterNamePattern.firstMatch(line);
if (nameMatch != null) { if (nameMatch != null) {
final playerName = nameMatch.group(1)?.trim(); 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.currentPlayerName = playerName;
stats.playerNames.add(playerName); // ()
if (!stats.playerNames.any((n) => n.toLowerCase() == playerName.toLowerCase())) {
stats.playerNames.add(playerName);
}
stats.firstPlayerName ??= playerName; stats.firstPlayerName ??= playerName;
} }
} }
@ -318,7 +367,11 @@ class YearlyReportAnalyzer {
if (deathMatch != null) { if (deathMatch != null) {
final victimId = deathMatch.group(1)?.trim(); final victimId = deathMatch.group(1)?.trim();
if (victimId != null && stats.currentPlayerName != null && victimId == stats.currentPlayerName) { 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(); final killerId = legacyDeathMatch.group(3)?.trim();
if (victimId != null && stats.currentPlayerName != null) { if (victimId != null && stats.currentPlayerName != null) {
bool isRecent =
stats._lastDeathTime != null && lineTime.difference(stats._lastDeathTime!).abs().inSeconds <= 2;
// //
if (victimId == killerId) { if (victimId == killerId) {
if (victimId == stats.currentPlayerName) { 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 { } else {
// // ()
if (victimId == stats.currentPlayerName) { if (victimId == stats.currentPlayerName) {
stats.deathCount++; // ()
if (!isRecent) {
stats.deathCount++;
stats._lastDeathTime = lineTime;
}
} }
// // ()
if (killerId == stats.currentPlayerName) { if (killerId == stats.currentPlayerName) {
stats.killCount++; stats.killCount++;
} }
@ -601,6 +671,90 @@ class YearlyReportAnalyzer {
final sortedLocations = locationDetails.entries.toList()..sort((a, b) => b.value.compareTo(a.value)); final sortedLocations = locationDetails.entries.toList()..sort((a, b) => b.value.compareTo(a.value));
final topLocations = sortedLocations.take(10).toList(); 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( return YearlyReportData(
totalLaunchCount: totalLaunchCount, totalLaunchCount: totalLaunchCount,
totalPlayTime: totalPlayTime, totalPlayTime: totalPlayTime,
@ -628,6 +782,16 @@ class YearlyReportAnalyzer {
yearlyKillCount: yearlyKillCount, yearlyKillCount: yearlyKillCount,
yearlyDeathCount: yearlyDeathCount, yearlyDeathCount: yearlyDeathCount,
yearlySelfKillCount: yearlySelfKillCount, yearlySelfKillCount: yearlySelfKillCount,
mostPlayedMonth: mostPlayedMonth,
mostPlayedMonthCount: mostPlayedMonthCount,
leastPlayedMonth: leastPlayedMonth,
leastPlayedMonthCount: leastPlayedMonthCount,
longestPlayStreak: longestPlayStreak,
playStreakStartDate: playStreakStartDate,
playStreakEndDate: playStreakEndDate,
longestOfflineStreak: longestOfflineStreak,
offlineStreakStartDate: offlineStreakStartDate,
offlineStreakEndDate: offlineStreakEndDate,
vehiclePilotedDetails: vehiclePilotedDetails, vehiclePilotedDetails: vehiclePilotedDetails,
accountSessionDetails: accountSessionDetails, accountSessionDetails: accountSessionDetails,
locationDetails: locationDetails, locationDetails: locationDetails,

File diff suppressed because it is too large Load Diff