00f37d7e19
- 新增 ReminderActionExecutor 处理取消/稍后提醒操作 - 新增 ReminderOutboxStore 本地存储待处理操作 - 重构 LocalNotificationService 支持聚合提醒和交互操作 - 新增 event_color_resolver 工具类统一颜色解析 - 新增 CalendarService.archiveEvent 归档方法 - 增强 ModelTracking 支持缓存命中、推理token和成本追踪 - 添加 qwen3.5-35b-a3b 模型配置 - 更新 AndroidManifest 全屏intent权限 - 补充相关单元测试和文档
81 lines
2.0 KiB
Dart
81 lines
2.0 KiB
Dart
import '../data/models/schedule_item_model.dart';
|
|
|
|
class ReminderOverlapGroup {
|
|
final DateTime fireAt;
|
|
final List<ScheduleItemModel> events;
|
|
|
|
const ReminderOverlapGroup({required this.fireAt, required this.events});
|
|
|
|
bool get isAggregate => events.length > 1;
|
|
}
|
|
|
|
class ReminderOverlapPolicy {
|
|
const ReminderOverlapPolicy();
|
|
|
|
List<ReminderOverlapGroup> groupByMinute(
|
|
Iterable<ScheduleItemModel> events, {
|
|
required DateTime now,
|
|
}) {
|
|
final buckets = <String, List<ScheduleItemModel>>{};
|
|
final minuteToFireAt = <String, DateTime>{};
|
|
|
|
for (final event in events) {
|
|
final fireAt = resolveFirstFireAt(event, now: now);
|
|
if (fireAt == null) {
|
|
continue;
|
|
}
|
|
final minute = DateTime(
|
|
fireAt.year,
|
|
fireAt.month,
|
|
fireAt.day,
|
|
fireAt.hour,
|
|
fireAt.minute,
|
|
);
|
|
final key = minute.toIso8601String();
|
|
buckets.putIfAbsent(key, () => <ScheduleItemModel>[]).add(event);
|
|
minuteToFireAt[key] = minuteToFireAt[key] ?? fireAt;
|
|
}
|
|
|
|
final groups = buckets.entries
|
|
.map(
|
|
(entry) => ReminderOverlapGroup(
|
|
fireAt: minuteToFireAt[entry.key]!,
|
|
events: entry.value,
|
|
),
|
|
)
|
|
.toList();
|
|
|
|
groups.sort((left, right) => left.fireAt.compareTo(right.fireAt));
|
|
return groups;
|
|
}
|
|
|
|
DateTime? resolveFirstFireAt(
|
|
ScheduleItemModel event, {
|
|
required DateTime now,
|
|
}) {
|
|
if (event.status != ScheduleStatus.active) {
|
|
return null;
|
|
}
|
|
final reminderMinutes = event.metadata?.reminderMinutes;
|
|
if (reminderMinutes == null) {
|
|
return null;
|
|
}
|
|
final remindAt = event.startAt.subtract(Duration(minutes: reminderMinutes));
|
|
final endAt = event.endAt;
|
|
|
|
if (endAt != null && !now.isBefore(endAt)) {
|
|
return null;
|
|
}
|
|
|
|
if (now.isBefore(remindAt)) {
|
|
return remindAt;
|
|
}
|
|
|
|
if (endAt != null && now.isBefore(endAt)) {
|
|
return now.add(const Duration(seconds: 5));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|