1+ import 'package:flutter/foundation.dart' ;
2+ import 'package:shared_preferences/shared_preferences.dart' ;
3+ import 'package:uuid/uuid.dart' ;
4+ import '../models/punch_record.dart' ;
5+ import '../models/punch_log.dart' ;
6+ import '../services/storage_service.dart' ;
7+
8+ class PunchProvider with ChangeNotifier {
9+ final StorageService _storageService = StorageService ();
10+ final Uuid _uuid = const Uuid ();
11+
12+ List <PunchRecord > _records = [];
13+ List <PunchLog > _logs = [];
14+
15+ String _punchName = "每日打卡" ;
16+ int _idealIntervalDays = 1 ;
17+
18+ List <PunchRecord > get records => _records;
19+ List <PunchLog > get logs => _logs;
20+ String get punchName => _punchName;
21+ int get idealIntervalDays => _idealIntervalDays;
22+
23+ bool _isLoading = true ;
24+ bool get isLoading => _isLoading;
25+
26+ PunchProvider () {
27+ _loadData ();
28+ }
29+
30+ Future <void > _loadData () async {
31+ _isLoading = true ;
32+ notifyListeners ();
33+
34+ // Load Settings
35+ final prefs = await SharedPreferences .getInstance ();
36+ _punchName = prefs.getString ('punchName' ) ?? "每日打卡" ;
37+ _idealIntervalDays = prefs.getInt ('idealIntervalDays' ) ?? 1 ;
38+
39+ // Load Data
40+ _records = await _storageService.loadRecords ();
41+ _logs = await _storageService.loadLogs ();
42+
43+ _isLoading = false ;
44+ notifyListeners ();
45+ }
46+
47+ // --- Settings ---
48+
49+ Future <void > updateSettings (String name, int interval) async {
50+ _punchName = name;
51+ _idealIntervalDays = interval;
52+
53+ final prefs = await SharedPreferences .getInstance ();
54+ await prefs.setString ('punchName' , name);
55+ await prefs.setInt ('idealIntervalDays' , interval);
56+
57+ _addLog ("设置更新" , "名称: $name , 间隔: $interval 天" );
58+ notifyListeners ();
59+ }
60+
61+ // --- Punch Logic ---
62+
63+ int getCountForDate (DateTime date) {
64+ final record = _records.firstWhere (
65+ (r) => isSameDay (r.date, date),
66+ orElse: () => PunchRecord (date: date, count: 0 ),
67+ );
68+ return record.count;
69+ }
70+
71+ Future <void > updatePunch (DateTime date, int change) async {
72+ final index = _records.indexWhere ((r) => isSameDay (r.date, date));
73+ int newCount;
74+
75+ if (index != - 1 ) {
76+ newCount = _records[index].count + change;
77+ if (newCount < 0 ) newCount = 0 ; // Prevent negative
78+
79+ if (newCount == 0 ) {
80+ _records.removeAt (index);
81+ } else {
82+ _records[index] = PunchRecord (date: date, count: newCount);
83+ }
84+ } else {
85+ newCount = change > 0 ? change : 0 ;
86+ if (newCount > 0 ) {
87+ _records.add (PunchRecord (date: date, count: newCount));
88+ }
89+ }
90+
91+ await _storageService.saveRecords (_records);
92+
93+ String action = change > 0 ? "打卡 +$change " : "打卡 $change " ;
94+ _addLog (action, "日期: ${date .toString ().split (' ' )[0 ]}, 新数量: $newCount " );
95+
96+ notifyListeners ();
97+ }
98+
99+ // --- Log Logic ---
100+
101+ void _addLog (String action, String details) {
102+ final log = PunchLog (
103+ id: _uuid.v4 (),
104+ timestamp: DateTime .now (),
105+ action: action,
106+ details: details,
107+ );
108+ _logs.insert (0 , log); // Add to top
109+ _storageService.saveLogs (_logs);
110+ }
111+
112+ Future <void > softDeleteLog (String id) async {
113+ final index = _logs.indexWhere ((l) => l.id == id);
114+ if (index != - 1 ) {
115+ _logs[index].isDeleted = true ;
116+ // We don't save the 'isDeleted' state to file to keep history intact in file,
117+ // but requirement says "store in file but delete from surface".
118+ // So we actually need to save the isDeleted state to file so it persists across restarts.
119+ await _storageService.saveLogs (_logs);
120+ notifyListeners ();
121+ }
122+ }
123+
124+ // --- Helpers ---
125+
126+ DateTime ? getLastPunchDate () {
127+ if (_records.isEmpty) return null ;
128+ // Sort records by date descending
129+ final sorted = List <PunchRecord >.from (_records)
130+ ..sort ((a, b) => b.date.compareTo (a.date));
131+ return sorted.first.date;
132+ }
133+
134+ DateTime ? getNextIdealDate () {
135+ final last = getLastPunchDate ();
136+ if (last == null ) return null ;
137+ return last.add (Duration (days: _idealIntervalDays));
138+ }
139+
140+ bool isSameDay (DateTime ? a, DateTime ? b) {
141+ if (a == null || b == null ) return false ;
142+ return a.year == b.year && a.month == b.month && a.day == b.day;
143+ }
144+ }
0 commit comments