feat: 重构会话管理与提醒通知系统

This commit is contained in:
qzl
2026-03-31 18:26:36 +08:00
parent a8c262e9c7
commit 9a231dae9e
31 changed files with 650 additions and 223 deletions
@@ -1,3 +1,6 @@
import 'dart:convert';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:social_app/core/notification/models/reminder_alarm.dart';
import 'package:social_app/core/notification/services/reminder_scheduler_service.dart';
@@ -63,4 +66,125 @@ void main() {
expect(alarms, isEmpty);
});
test('buildUpsertPlan skips unchanged pending reminders', () {
final alarm = ReminderAlarm(
eventId: 'evt_5',
title: 'Sync',
startAt: DateTime(2026, 4, 1, 10, 0),
endAt: DateTime(2026, 4, 1, 11, 0),
timezone: 'Asia/Shanghai',
reminderMinutes: 15,
fireAt: DateTime(2026, 4, 1, 9, 45),
fireTimeBucket: 29114145,
location: 'Room 1',
notes: 'Bring notes',
);
final id = ReminderSchedulerService.notificationIdFor(
alarm.eventId,
alarm.fireTimeBucket,
);
final pending = [
PendingNotificationRequest(
id,
alarm.title,
'开始时间 10:00 · Room 1',
jsonEncode(alarm.toJson()),
),
];
final plan = ReminderSchedulerService.buildUpsertPlan(
eventId: alarm.eventId,
pending: pending,
desired: [alarm],
);
expect(plan.idsToCancel, isEmpty);
expect(plan.alarmsToSchedule, isEmpty);
});
test('buildUpsertPlan reschedules when payload changed', () {
final desired = ReminderAlarm(
eventId: 'evt_6',
title: 'Daily',
startAt: DateTime(2026, 4, 1, 8, 0),
endAt: DateTime(2026, 4, 1, 9, 0),
timezone: 'Asia/Shanghai',
reminderMinutes: 10,
fireAt: DateTime(2026, 4, 1, 7, 50),
fireTimeBucket: 29114030,
location: 'Desk',
notes: 'Updated',
);
final stale = ReminderAlarm(
eventId: desired.eventId,
title: desired.title,
startAt: desired.startAt,
endAt: desired.endAt,
timezone: desired.timezone,
reminderMinutes: desired.reminderMinutes,
fireAt: desired.fireAt,
fireTimeBucket: desired.fireTimeBucket,
location: desired.location,
notes: 'Old notes',
);
final id = ReminderSchedulerService.notificationIdFor(
desired.eventId,
desired.fireTimeBucket,
);
final plan = ReminderSchedulerService.buildUpsertPlan(
eventId: desired.eventId,
pending: [
PendingNotificationRequest(
id,
stale.title,
'开始时间 08:00 · Desk',
jsonEncode(stale.toJson()),
),
],
desired: [desired],
);
expect(plan.idsToCancel, [id]);
expect(plan.alarmsToSchedule.length, 1);
expect(plan.alarmsToSchedule.first.notes, 'Updated');
});
test('buildUpsertPlan cancels and rebuilds when payload is invalid', () {
final desired = ReminderAlarm(
eventId: 'evt_7',
title: 'Check-in',
startAt: DateTime(2026, 4, 2, 9, 0),
endAt: DateTime(2026, 4, 2, 9, 30),
timezone: 'Asia/Shanghai',
reminderMinutes: 10,
fireAt: DateTime(2026, 4, 2, 8, 50),
fireTimeBucket: 29115530,
location: 'Room A',
notes: 'fresh',
);
final id = ReminderSchedulerService.notificationIdFor(
desired.eventId,
desired.fireTimeBucket,
);
final plan = ReminderSchedulerService.buildUpsertPlan(
eventId: desired.eventId,
pending: [
PendingNotificationRequest(
id,
desired.title,
'开始时间 09:00 · Room A',
'{"eventId":"${desired.eventId}"}',
),
],
desired: [desired],
);
expect(plan.idsToCancel, [id]);
expect(plan.alarmsToSchedule.length, 1);
expect(plan.alarmsToSchedule.first.eventId, desired.eventId);
});
}