// 远程日记仓库 — 通过 API 客户端连接后端 import '../models/journal_element.dart'; import '../models/journal_entry.dart'; import '../remote/api_client.dart'; import 'journal_repository.dart'; /// 远程日记仓库 — 通过 HTTP API 操作后端数据 /// /// 所有操作需要网络连接。离线场景由 SyncEngine 协调 Isar 本地仓库处理。 class RemoteJournalRepository implements JournalRepository { final ApiClient _api; RemoteJournalRepository({required ApiClient api}) : _api = api; @override Future> getJournals({ DateTime? dateFrom, DateTime? dateTo, int? page, int? pageSize, String? mood, String? tag, String? classId, }) async { final queryParams = {}; // 后端 NaiveDateTime 格式: "2026-06-01T00:00:00"(不带毫秒) if (dateFrom != null) { queryParams['date_from'] = dateFrom.toIso8601String().replaceFirst(RegExp(r'\.\d+'), ''); } if (dateTo != null) { queryParams['date_to'] = dateTo.toIso8601String().replaceFirst(RegExp(r'\.\d+'), ''); } if (page != null) queryParams['page'] = page; if (pageSize != null) queryParams['page_size'] = pageSize; if (mood != null) queryParams['mood'] = mood; if (tag != null) queryParams['tag'] = tag; if (classId != null) queryParams['class_id'] = classId; final response = await _api.get('/diary/journals', queryParams: queryParams); final body = response.data as Map; final items = body['data'] as List? ?? []; return items .map((json) => JournalEntry.fromJson(json as Map)) .toList(); } @override Future getJournalCount() async { final response = await _api.get('/diary/journals', queryParams: { 'page': 1, 'page_size': 1, }); final body = response.data as Map; return (body['total'] as int?) ?? 0; } @override Future getJournal(String id) async { try { final response = await _api.get('/diary/journals/$id'); final body = response.data as Map; return JournalEntry.fromJson(body['data'] as Map); } on ApiException catch (e) { if (e.statusCode == 404) return null; rethrow; } } @override Future createJournal(JournalEntry entry) async { final response = await _api.post('/diary/journals', data: entry.toJson()); final body = response.data as Map; return JournalEntry.fromJson(body['data'] as Map); } @override Future updateJournal(JournalEntry entry) async { final response = await _api.put( '/diary/journals/${entry.id}', data: { 'title': entry.title, 'mood': entry.mood.value, 'weather': entry.weather.value, 'tags': entry.tags, 'is_private': entry.isPrivate, 'shared_to_class': entry.sharedToClass, 'version': entry.version, }, ); final body = response.data as Map; return JournalEntry.fromJson(body['data'] as Map); } @override Future deleteJournal(String id) async { await _api.delete('/diary/journals/$id'); } @override Future> getElements(String journalId) async { final response = await _api.get('/diary/journals/$journalId/elements'); final body = response.data as Map; final items = body['data'] as List? ?? []; return items .map((json) => JournalElement.fromJson(json as Map)) .toList(); } @override Future addElement(JournalElement element) async { final response = await _api.post( '/diary/journals/${element.journalId}/elements', data: element.toJson(), ); final body = response.data as Map; return JournalElement.fromJson(body['data'] as Map); } @override Future updateElement(JournalElement element) async { final response = await _api.put( '/diary/journals/${element.journalId}/elements/${element.id}', data: element.toJson(), ); final body = response.data as Map; return JournalElement.fromJson(body['data'] as Map); } @override Future removeElement(String elementId) async { await _api.delete('/diary/elements/$elementId'); } /// 远程仓库不提供本地变更通知,返回空流 @override Stream get onJournalChanged => const Stream.empty(); } /// API 异常封装 — 后端返回非 2xx 状态码时抛出 class ApiException implements Exception { final String message; final int statusCode; final dynamic responseBody; const ApiException({ required this.message, required this.statusCode, this.responseBody, }); @override String toString() => 'ApiException($statusCode): $message'; }