Files
social-app/docs/protocols/calendar/reminder-alert-lifecycle.md
T
qzl 00f37d7e19 feat: 实现日历提醒完整功能(操作执行、通知服务重构、归档)
- 新增 ReminderActionExecutor 处理取消/稍后提醒操作
- 新增 ReminderOutboxStore 本地存储待处理操作
- 重构 LocalNotificationService 支持聚合提醒和交互操作
- 新增 event_color_resolver 工具类统一颜色解析
- 新增 CalendarService.archiveEvent 归档方法
- 增强 ModelTracking 支持缓存命中、推理token和成本追踪
- 添加 qwen3.5-35b-a3b 模型配置
- 更新 AndroidManifest 全屏intent权限
- 补充相关单元测试和文档
2026-03-18 19:12:47 +08:00

3.8 KiB

Calendar Reminder Alert Lifecycle Protocol

Version

  • Current: 1.0
  • Status: Draft

Goal

定义日程提醒弹窗在 Android/iOS 的统一行为语义,覆盖提醒触发、用户操作、超时、离线补偿、归档与重装恢复,确保多端一致性和可恢复性。


Canonical Rules

  1. 提醒弹窗动作语义跨平台一致:cancelsnooze_10mtimeout_30s
  2. timeout_30s 语义固定为忽略当前弹窗并按 10 分钟后再次提醒,不直接归档。
  3. cancel 必须归档对应日程(status=archived)。
  4. 到达事件结束时间后(now >= end_at)必须停止提醒并归档。
  5. 归档后的 UI 渲染必须灰色显示;不强制改写原始 metadata 颜色。
  6. 前端动作上报后端采用最终一致性:本地 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 + 10m
  • timeout_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)
  • 重试策略:指数退避,最大重试次数可配置。
  • cancelauto_archive 都映射到后端 PATCH status=archived
  • Outbox 记录必须本地持久化,App 重启后可恢复。

Scheduling and Compensation Rules

Normal schedule

  • remindAt = startAt - reminderMinutes

Bootstrap/reinstall compensation

启动重建时对每个 active 事件执行:

  1. now < remindAt:按 remindAt 正常调度。
  2. remindAt <= now < endAt:立刻补偿提醒(建议 +5s),然后进入 10 分钟节奏。
  3. 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_end
    • end_at IS NULL OR end_at >= window_start
    • status=active

Platform Notes

Android

  • 优先 full-screen intent;系统可能因策略降级为 heads-up/横幅。
  • 声音和振动受通知通道及系统设置影响。

iOS

  • 支持动作按钮与本地提醒语义。
  • 不保证 Android 式强制全屏闹钟弹窗;以锁屏/横幅提醒为主。