积分、成就系统

This commit is contained in:
ytc1012
2025-11-27 13:37:10 +08:00
parent 0195cdf54b
commit 58f6ec39b7
35 changed files with 7786 additions and 199 deletions

View File

@@ -0,0 +1,144 @@
import 'package:hive/hive.dart';
part 'user_progress.g.dart';
@HiveType(typeId: 1)
class UserProgress extends HiveObject {
@HiveField(0)
int totalPoints;
@HiveField(1)
int currentPoints;
@HiveField(2)
DateTime? lastCheckInDate;
@HiveField(3)
int consecutiveCheckIns;
@HiveField(4)
Map<String, DateTime> unlockedAchievements;
@HiveField(5)
int totalFocusMinutes;
@HiveField(6)
int totalDistractions;
@HiveField(7)
int totalSessions;
@HiveField(8)
List<DateTime> checkInHistory;
UserProgress({
this.totalPoints = 0,
this.currentPoints = 0,
this.lastCheckInDate,
this.consecutiveCheckIns = 0,
Map<String, DateTime>? unlockedAchievements,
this.totalFocusMinutes = 0,
this.totalDistractions = 0,
this.totalSessions = 0,
List<DateTime>? checkInHistory,
}) : unlockedAchievements = unlockedAchievements ?? {},
checkInHistory = checkInHistory ?? [];
/// Get current level based on total points
int get level {
return LevelSystem.getLevel(totalPoints);
}
/// Get progress to next level (0.0 - 1.0)
double get levelProgress {
return LevelSystem.getLevelProgress(totalPoints);
}
/// Get points needed to reach next level
int get pointsToNextLevel {
return LevelSystem.getPointsToNextLevel(totalPoints);
}
/// Check if checked in today
bool get hasCheckedInToday {
if (lastCheckInDate == null) return false;
final now = DateTime.now();
return lastCheckInDate!.year == now.year &&
lastCheckInDate!.month == now.month &&
lastCheckInDate!.day == now.day;
}
/// Get longest check-in streak from history
int get longestCheckInStreak {
if (checkInHistory.isEmpty) return 0;
int maxStreak = 1;
int currentStreak = 1;
// Sort dates
final sortedDates = List<DateTime>.from(checkInHistory)
..sort((a, b) => a.compareTo(b));
for (int i = 1; i < sortedDates.length; i++) {
final diff = sortedDates[i].difference(sortedDates[i - 1]).inDays;
if (diff == 1) {
currentStreak++;
maxStreak = currentStreak > maxStreak ? currentStreak : maxStreak;
} else {
currentStreak = 1;
}
}
return maxStreak;
}
}
/// Level system configuration
class LevelSystem {
static const List<int> levelThresholds = [
0, // Level 0 → 1: 0 points
50, // Level 1 → 2: 50 points
150, // Level 2 → 3: 150 points
300, // Level 3 → 4: 300 points
500, // Level 4 → 5: 500 points
800, // Level 5 → 6: 800 points
1200, // Level 6 → 7: 1200 points
1800, // Level 7 → 8: 1800 points
2500, // Level 8 → 9: 2500 points
3500, // Level 9 → 10: 3500 points
];
static int getLevel(int points) {
for (int i = levelThresholds.length - 1; i >= 0; i--) {
if (points >= levelThresholds[i]) {
return i;
}
}
return 0;
}
static double getLevelProgress(int points) {
int currentLevel = getLevel(points);
if (currentLevel >= levelThresholds.length - 1) return 1.0;
int currentThreshold = levelThresholds[currentLevel];
int nextThreshold = levelThresholds[currentLevel + 1];
return (points - currentThreshold) / (nextThreshold - currentThreshold);
}
static int getPointsToNextLevel(int points) {
int currentLevel = getLevel(points);
if (currentLevel >= levelThresholds.length - 1) return 0;
return levelThresholds[currentLevel + 1] - points;
}
static int getNextLevelThreshold(int points) {
int currentLevel = getLevel(points);
if (currentLevel >= levelThresholds.length - 1) {
return levelThresholds.last;
}
return levelThresholds[currentLevel + 1];
}
}