// 日记仓库 — 抽象接口 + 内存实现(开发测试用) // // 设计思路: // - 抽象接口 [JournalRepository] 定义数据操作契约 // - 后续实现 IsarJournalRepository(本地)和 RemoteJournalRepository(远程) // - SyncEngine 负责协调本地和远程仓库之间的数据同步 // - 内存实现 [InMemoryJournalRepository] 用于开发阶段快速迭代 import 'dart:async'; import '../models/journal_entry.dart'; import '../models/journal_element.dart'; /// 日记仓库抽象接口 — 所有数据操作的统一契约 /// /// 查询参数说明: /// - [dateFrom]/[dateTo]: 日期范围过滤(闭区间) /// - [page]/[pageSize]: 分页参数,从 1 开始 abstract class JournalRepository { /// 获取日记列表(支持日期范围、心情、标签、班级过滤和分页) Future> getJournals({ DateTime? dateFrom, DateTime? dateTo, int? page, int? pageSize, String? mood, String? tag, String? classId, }); /// 获取日记总数 Future getJournalCount(); /// 获取单篇日记(返回 null 表示不存在) Future getJournal(String id); /// 创建新日记 Future createJournal(JournalEntry entry); /// 更新日记(使用 version 字段做乐观锁冲突检测) Future updateJournal(JournalEntry entry); /// 删除日记(软删除,设置 deleted_at) Future deleteJournal(String id); /// 获取指定日记的所有元素 Future> getElements(String journalId); /// 添加元素到日记 Future addElement(JournalElement element); /// 更新元素 Future updateElement(JournalElement element); /// 从日记中移除元素 Future removeElement(String elementId); /// 日记变更通知流 — create/update/delete 时发出信号 Stream get onJournalChanged; } /// 内存实现 — 用于开发阶段快速迭代和单元测试 /// /// 数据存储在内存中,应用重启后丢失。 /// 线程安全说明:Flutter 是单线程模型,无需加锁。 class InMemoryJournalRepository implements JournalRepository { final Map _journals = {}; final Map _elements = {}; final StreamController _changeController = StreamController.broadcast(); @override Stream get onJournalChanged => _changeController.stream; @override Future> getJournals({ DateTime? dateFrom, DateTime? dateTo, int? page, int? pageSize, String? mood, String? tag, String? classId, }) async { var results = _journals.values.toList(); // 日期范围过滤 if (dateFrom != null) { results = results.where((j) => j.date.isAfter(dateFrom)).toList(); } if (dateTo != null) { results = results.where((j) => j.date.isBefore(dateTo)).toList(); } // 心情过滤 if (mood != null) { results = results.where((j) => j.mood.value == mood).toList(); } // 标签过滤(日记 tags 列表包含指定标签) if (tag != null) { results = results.where((j) => j.tags.contains(tag)).toList(); } // 班级过滤 if (classId != null) { results = results.where((j) => j.classId == classId).toList(); } // 按日期降序排列(最新在前) results.sort((a, b) => b.date.compareTo(a.date)); // 分页 if (page != null && pageSize != null) { final start = (page - 1) * pageSize; if (start >= results.length) return []; final end = (start + pageSize).clamp(0, results.length); results = results.sublist(start, end); } return results; } @override Future getJournalCount() async => _journals.length; @override Future getJournal(String id) async { return _journals[id]; } @override Future createJournal(JournalEntry entry) async { _journals[entry.id] = entry; _changeController.add(null); return entry; } @override Future updateJournal(JournalEntry entry) async { final existing = _journals[entry.id]; if (existing == null) { throw StateError('日记不存在: ${entry.id}'); } // 乐观锁冲突检测:版本号必须匹配 if (existing.version != entry.version) { throw StateError( '版本冲突: 本地版本 ${entry.version}, 服务端版本 ${existing.version}', ); } // 版本号 +1,更新时间戳 final updated = entry.copyWith( version: entry.version + 1, updatedAt: DateTime.now(), ); _journals[entry.id] = updated; _changeController.add(null); return updated; } @override Future deleteJournal(String id) async { // 内存实现:直接移除(软删除由 Isar 实现处理) _journals.remove(id); // 同时移除关联元素 _elements.removeWhere((_, e) => e.journalId == id); _changeController.add(null); } @override Future> getElements(String journalId) async { return _elements.values .where((e) => e.journalId == journalId) .toList() ..sort((a, b) => a.zIndex.compareTo(b.zIndex)); } @override Future addElement(JournalElement element) async { _elements[element.id] = element; return element; } @override Future updateElement(JournalElement element) async { final existing = _elements[element.id]; if (existing == null) { throw StateError('元素不存在: ${element.id}'); } // 乐观锁冲突检测 if (existing.version != element.version) { throw StateError( '版本冲突: 本地版本 ${element.version}, 服务端版本 ${existing.version}', ); } final updated = element.copyWith( version: element.version + 1, updatedAt: DateTime.now(), ); _elements[element.id] = updated; return updated; } @override Future removeElement(String elementId) async { _elements.remove(elementId); } /// 清空所有数据(测试辅助方法) void clearAll() { _journals.clear(); _elements.clear(); } /// 获取日记总数(测试辅助方法) int get journalCount => _journals.length; /// 获取元素总数(测试辅助方法) int get elementCount => _elements.length; }