00f37d7e19
- 新增 ReminderActionExecutor 处理取消/稍后提醒操作 - 新增 ReminderOutboxStore 本地存储待处理操作 - 重构 LocalNotificationService 支持聚合提醒和交互操作 - 新增 event_color_resolver 工具类统一颜色解析 - 新增 CalendarService.archiveEvent 归档方法 - 增强 ModelTracking 支持缓存命中、推理token和成本追踪 - 添加 qwen3.5-35b-a3b 模型配置 - 更新 AndroidManifest 全屏intent权限 - 补充相关单元测试和文档
3.8 KiB
3.8 KiB
Calendar Reminder Alert Lifecycle Protocol
Version
- Current:
1.0 - Status: Draft
Goal
定义日程提醒弹窗在 Android/iOS 的统一行为语义,覆盖提醒触发、用户操作、超时、离线补偿、归档与重装恢复,确保多端一致性和可恢复性。
Canonical Rules
- 提醒弹窗动作语义跨平台一致:
cancel、snooze_10m、timeout_30s。 timeout_30s语义固定为忽略当前弹窗并按 10 分钟后再次提醒,不直接归档。cancel必须归档对应日程(status=archived)。- 到达事件结束时间后(
now >= end_at)必须停止提醒并归档。 - 归档后的 UI 渲染必须灰色显示;不强制改写原始 metadata 颜色。
- 前端动作上报后端采用最终一致性:本地 outbox + 重放机制。
Reminder Payload Contract
{
"eventId": "uuid",
"title": "string",
"startAt": "iso8601-with-offset",
"endAt": "iso8601-with-offset|null",
"timezone": "IANA",
"location": "string|null",
"notes": "string|null",
"color": "#RRGGBB|null",
"mode": "single|aggregate",
"aggregateIds": ["uuid"],
"version": 1
}
Constraints
eventId必填且为 UUID。startAt必须带时区偏移。mode=aggregate时,aggregateIds至少包含 2 个 id。version必填,用于后续协议升级兼容。
Action Contract
动作枚举:
cancel: 用户取消提醒并归档事件snooze_10m: 用户点击稍后提醒,重排到now + 10mtimeout_30s: 用户 30s 未处理,按snooze_10m处理auto_archive: 系统判定事件已过期,自动归档
Outbox Contract (Frontend)
{
"opId": "uuid",
"eventId": "uuid",
"action": "cancel|snooze_10m|timeout_30s|auto_archive",
"targetStatus": "archived|null",
"occurredAt": "iso8601-with-offset",
"retryCount": 0,
"nextRetryAt": "iso8601-with-offset|null",
"state": "pending|done|dead",
"lastError": "string|null"
}
Constraints
- 幂等键:
(eventId, action, occurredAtBucket)。 - 重试策略:指数退避,最大重试次数可配置。
cancel和auto_archive都映射到后端PATCH status=archived。- Outbox 记录必须本地持久化,App 重启后可恢复。
Scheduling and Compensation Rules
Normal schedule
remindAt = startAt - reminderMinutes
Bootstrap/reinstall compensation
启动重建时对每个 active 事件执行:
now < remindAt:按remindAt正常调度。remindAt <= now < endAt:立刻补偿提醒(建议+5s),然后进入 10 分钟节奏。now >= endAt:不再提醒,走归档流程。
Uniqueness and Dedupe Rules
- 通知唯一键:
hash(eventId + cycleStartEpochMinutes + mode)。 - 每次创建提醒前必须取消同 dedupe key 的旧提醒(upsert 语义)。
- 补偿提醒在同一 cycle 窗口内最多触发一次。
- 启动恢复时要同时参考 pending notification 和 outbox 状态,避免重复调度。
Overlap Rule
- 同一分钟内多个提醒合并为一个 aggregate 弹窗。
- aggregate 弹窗默认操作作用于全部成员事件。
- aggregate 负载中必须包含
aggregateIds以支持后续批处理。
Backend Contract Reuse
- 复用现有接口:
PATCH /schedule-items/{item_id},请求体传{"status":"archived"}。 - 建议提供/改造 overlap 查询语义用于启动补偿:
start_at <= window_endend_at IS NULL OR end_at >= window_startstatus=active
Platform Notes
Android
- 优先 full-screen intent;系统可能因策略降级为 heads-up/横幅。
- 声音和振动受通知通道及系统设置影响。
iOS
- 支持动作按钮与本地提醒语义。
- 不保证 Android 式强制全屏闹钟弹窗;以锁屏/横幅提醒为主。