feat: 重构 Reminder Notification 系统并更新应用包名

This commit is contained in:
qzl
2026-03-30 18:36:57 +08:00
parent 9fb2a6857b
commit 91bf3c3f96
90 changed files with 5133 additions and 3017 deletions
@@ -1,18 +1,24 @@
import '../../../../data/cache/cache_policy.dart';
import '../../../../data/cache/cached_repository.dart';
import '../../../../data/cache/cache_scope.dart';
import '../../../../data/network/i_api_client.dart';
import '../../../../core/notification/models/reminder_alarm.dart';
import '../../../../core/notification/services/reminder_reconcile_service.dart';
import '../models/schedule_item_model.dart';
class CalendarRepository extends CachedRepository<List<ScheduleItemModel>> {
final IApiClient _apiClient;
final ReminderReconcileService? _reminderReconcileService;
static const _prefix = '/api/v1/schedule-items';
CalendarRepository({
required super.store,
required IApiClient apiClient,
ReminderReconcileService? reminderReconcileService,
CachePolicy? policy,
super.now,
}) : _apiClient = apiClient,
_reminderReconcileService = reminderReconcileService,
super(
policy:
policy ??
@@ -69,7 +75,9 @@ class CalendarRepository extends CachedRepository<List<ScheduleItemModel>> {
if (data == null) {
throw StateError('Invalid getEventById response: empty payload');
}
return ScheduleItemModel.fromJson(data);
final event = ScheduleItemModel.fromJson(data);
await _reminderReconcileService?.reconcileEvent(_toReminderSnapshot(event));
return event;
}
Future<ScheduleItemModel> getById(String id) {
@@ -91,11 +99,20 @@ class CalendarRepository extends CachedRepository<List<ScheduleItemModel>> {
}
Future<void> acceptSubscription(String itemId) {
return _apiClient.post<void>('$_prefix/$itemId/accept');
return _applySubscriptionAction(itemId, accept: true);
}
Future<void> rejectSubscription(String itemId) {
return _apiClient.post<void>('$_prefix/$itemId/reject');
return _applySubscriptionAction(itemId, accept: false);
}
Future<void> _applySubscriptionAction(
String itemId, {
required bool accept,
}) async {
final action = accept ? 'accept' : 'reject';
await _apiClient.post<void>('$_prefix/$itemId/$action');
await store.clearByPrefix('cache:${CacheScope.token()}:calendar:');
}
Future<List<ScheduleItemModel>> _listByRange({
@@ -111,10 +128,28 @@ class CalendarRepository extends CachedRepository<List<ScheduleItemModel>> {
if (data == null) {
throw StateError('Invalid listByRange response: empty payload');
}
return data
final events = data
.whereType<Map<String, dynamic>>()
.map(ScheduleItemModel.fromJson)
.toList(growable: false);
await _reminderReconcileService?.reconcileEvents(
events.map(_toReminderSnapshot).toList(growable: false),
);
return events;
}
ReminderEventSnapshot _toReminderSnapshot(ScheduleItemModel event) {
return ReminderEventSnapshot(
eventId: event.id,
title: event.title,
startAt: event.startAt,
endAt: event.endAt,
timezone: event.timezone,
reminderMinutes: event.metadata?.reminderMinutes,
location: event.metadata?.location,
notes: event.metadata?.notes,
isArchived: event.status == ScheduleStatus.archived,
);
}
static Object? _encodeEventList(List<ScheduleItemModel> events) {
@@ -1,5 +1,7 @@
import '../../../../data/network/i_api_client.dart';
import '../../../../data/cache/cache_store.dart';
import '../../../../core/notification/models/reminder_alarm.dart';
import '../../../../core/notification/services/reminder_reconcile_service.dart';
import '../models/schedule_item_model.dart';
class CalendarService {
@@ -7,12 +9,15 @@ class CalendarService {
final IApiClient _apiClient;
final CacheInvalidator _invalidator;
final ReminderReconcileService? _reminderReconcileService;
CalendarService({
required IApiClient apiClient,
required CacheInvalidator invalidator,
ReminderReconcileService? reminderReconcileService,
}) : _apiClient = apiClient,
_invalidator = invalidator;
_invalidator = invalidator,
_reminderReconcileService = reminderReconcileService;
Future<List<ScheduleItemModel>> getEventsForDay(DateTime date) async {
final start = DateTime(date.year, date.month, date.day);
@@ -35,10 +40,14 @@ class CalendarService {
if (data == null) {
throw StateError('Invalid getEventsForRange response: empty payload');
}
return data
final events = data
.map((item) => item as Map<String, dynamic>)
.map(ScheduleItemModel.fromJson)
.toList(growable: false);
await _reminderReconcileService?.reconcileEvents(
events.map(_toReminderSnapshot).toList(growable: false),
);
return events;
}
Future<ScheduleItemModel> getEventById(String id) async {
@@ -47,7 +56,9 @@ class CalendarService {
if (data == null) {
throw StateError('Invalid getEventById response: empty payload');
}
return ScheduleItemModel.fromJson(data);
final event = ScheduleItemModel.fromJson(data);
await _reminderReconcileService?.reconcileEvent(_toReminderSnapshot(event));
return event;
}
Future<ScheduleItemModel> addEvent(ScheduleItemModel event) async {
@@ -61,6 +72,9 @@ class CalendarService {
}
final created = ScheduleItemModel.fromJson(data);
_invalidateEventCache(created);
await _reminderReconcileService?.reconcileEvent(
_toReminderSnapshot(created),
);
return created;
}
@@ -75,6 +89,9 @@ class CalendarService {
}
final updated = ScheduleItemModel.fromJson(data);
_invalidateEventCache(updated);
await _reminderReconcileService?.reconcileEvent(
_toReminderSnapshot(updated),
);
return updated;
}
@@ -84,6 +101,7 @@ class CalendarService {
event.copyWith(status: ScheduleStatus.archived),
);
_invalidateEventCache(updatedEvent);
await _reminderReconcileService?.archiveAndCancel(id);
return updatedEvent;
}
@@ -91,6 +109,7 @@ class CalendarService {
final event = await getEventById(id);
_invalidateEventCache(event);
await _apiClient.delete<void>('$_prefix/$id');
await _reminderReconcileService?.archiveAndCancel(id);
}
void _invalidateEventCache(ScheduleItemModel event) {
@@ -109,4 +128,18 @@ class CalendarService {
current = current.add(const Duration(days: 1));
}
}
ReminderEventSnapshot _toReminderSnapshot(ScheduleItemModel event) {
return ReminderEventSnapshot(
eventId: event.id,
title: event.title,
startAt: event.startAt,
endAt: event.endAt,
timezone: event.timezone,
reminderMinutes: event.metadata?.reminderMinutes,
location: event.metadata?.location,
notes: event.metadata?.notes,
isArchived: event.status == ScheduleStatus.archived,
);
}
}