# App Push|每日提醒推送(客户端 + 后端)|Spec > 阶段:高层规范(spec) > > 目标:实现“每日提醒”推送闭环:用户在首次进入 Onboarding 可选择每天推送次数(0~5,可跳过),并可在个人主页的“每日提醒”弹窗随时调整次数或关闭;客户端使用首次生成的 UUID 作为用户标识与后端关联;后端按用户配置定时下发推送。 --- ## 1. 背景与动机(摘要) 当前客户端已有 Push 引导页(可跳过)与“每日提醒”设置入口,但尚未形成完整闭环: - 需要一个稳定的匿名用户标识(UUID)将“提醒配置”与“推送 token”绑定到后端 - 需要后端能够按用户选择的每日次数(0~5)进行定时推送 - 本期不追求高级推送能力(富媒体、复杂分群、到达率分析等) --- ## 2. 目标(Goals) - **G1:Onboarding 选择**:用户首次进入 Onboarding 可设置“每日提醒次数”(0~5),可跳过且不阻塞进入主功能。 - **G2:设置页可改可关**:用户可在个人主页的“每日提醒”弹窗调整次数(0~5)或关闭推送。 - **G3:UUID 关联**:客户端首次进入生成 `client_user_id`(UUID),用于与后端关联用户配置与推送 token(见 `spec_kit/Client User Identity/spec.md`)。 - **G4:推送闭环**:客户端拿到推送权限与 Push Token 后上报后端;后端存储并按计划每日推送。 - **G5:幂等与可恢复**:重复上报、token 轮换、用户反复开关不应导致重复推送或“僵尸任务”。 - **G6:多语言**:推送文案至少支持当前客户端语言体系(`zh-CN/en/es/pt/zh-TW`),或以安全默认语言回退。 --- ## 3. 非目标(Non-goals) - 不接入高级推送能力(富媒体、通知分类/动作按钮、复杂分群、A/B 实验、到达率看板等)。 - 不引入账号体系与“多设备同人合并”策略(未来可基于 `account_id` 扩展)。 - 不在本阶段做精细化内容策略(例如千人千面的复杂推荐推送);仅保证“按次数定时发送”与文案安全合规。 --- ## 4. 技术选型(高层) ### 4.1 客户端 - 继续使用 **Expo** 的 `expo-notifications` - Push 权限申请:通过引导页解释后再触发系统弹窗(降低拒绝率) - Token 获取:使用 Expo Push Token(后续计划阶段细化获取与上报时机) ### 4.2 后端 - 推送通道:**Expo Push Service**(与客户端 `expo-notifications` 配套) - 定时任务:使用现有后端技术栈约定(Celery + Redis / 或等价调度器),每日按用户时区/策略触发推送 > 说明:若未来需要更高上限(自建 APNs/FCM 直连),可在后续模块升级,不影响本期 API 字段语义(`client_user_id`、`push_token`、偏好配置)的大框架。 --- ## 5. 用户流程(User Flows) ### 5.1 首次进入(Onboarding) 1. App 首次启动 → 进入 Onboarding 流程 2. 在 Onboarding 某一页提供“每日提醒次数”选择(0~5) - 0 表示不接收每日提醒(等同关闭) - 可点击“跳过”(不设置/不打扰) 3. 若用户选择次数 > 0: - 展示 Push 引导说明页 - 用户点击“立即开启”触发系统权限申请 4. 无论是否开启成功,均可进入主功能(不阻塞) ### 5.2 个人主页(每日提醒弹窗) - 用户可: - 调整每日次数(0~5) - 开启/关闭提醒(关闭等价次数=0) - 若系统权限为 denied:提示用户前往系统设置开启(不做强制跳转要求,按平台能力实现) ### 5.3 后端推送执行 - 后端按用户配置与时区,在每日窗口内发送 \(N\) 次推送(\(N\in[0,5]\)) - 若用户关闭或次数=0:不再推送 --- ## 6. 数据与持久化(高层) ### 6.1 客户端本地存储(最小集合) - `client_user_id`: string(UUID,稳定持久化;见用户标识 spec) - `push.permission_state`: `unknown | granted | denied`(用于 UI 显示与引导) - `daily_reminder.enabled`: boolean(可选;或由次数是否为 0 推导) - `daily_reminder.times_per_day`: number(0~5) - `daily_reminder.updated_at`: ISO string(可选,用于排障/幂等) ### 6.2 后端持久化(逻辑约束) 最小需要表达两类信息: 1) **设备/Token 绑定** - `client_user_id` - `platform`(ios/android) - `push_token`(Expo Push Token) - `app_id`(bundle id / package name) - `env`(dev/prod) - `last_seen_at` - `is_active` 2) **用户推送偏好** - `client_user_id` - `enabled` - `times_per_day`(0~5) - `timezone`(建议 IANA,如 `Asia/Shanghai`;若拿不到则回退为服务器默认策略) - `locale`(用于推送文案语言选择;可从客户端上报或推送时推断) - `updated_at` --- ## 7. API 契约(高层) > 说明:路由名可在 plan 阶段对齐现有服务结构;此处先固定“字段语义 + 幂等行为”。 ### 7.1 注册/更新 Push Token(幂等) - `POST /v1/push/register` - 请求体(最小集,继承用户标识 spec): - `client_user_id`: string(UUID) - `platform`: `"ios" | "android"` - `push_token`: string(Expo Push Token) - `app_id`: string - `env`: `"dev" | "prod"` - `device_meta`(可选):`{ model, os_version, app_version, locale, timezone }` - 行为: - 幂等:重复上报同一 token 不产生多条“有效绑定” - token 变更:同一 `client_user_id` 上报新 token 后,后端应更新“当前有效 token” ### 7.2 设置每日提醒偏好(幂等) - `PUT /v1/push/preferences` - 请求体: - `client_user_id`: string - `enabled`: boolean - `times_per_day`: number(0~5;若 enabled=false 则可强制视为 0) - `timezone`: string(可选) - `locale`: string(可选) - 行为: - 幂等:相同配置重复提交不改变结果 - `enabled=false` 或 `times_per_day=0`:必须停止后续推送(不再产生新的发送任务) ### 7.3 查询当前偏好(可选但建议) - `GET /v1/push/preferences?client_user_id=...` - 返回: - `enabled` - `times_per_day` - `timezone` - `locale` - `updated_at` ### 7.4 立即测试推送(仅 dev,可选) - `POST /v1/push/test` - 请求体: - `client_user_id` - `title` / `body`(可选) - 用途:联调排障(token 绑定、证书/通道、Expo 配置) --- ## 8. 推送策略(高层) ### 8.1 发送次数与窗口 - 每日发送次数:0~5 - 建议定义“允许发送的时间窗口”(例如 09:00~21:00),避免深夜打扰 - 当 `times_per_day > 0` 时,在窗口内生成 \(N\) 个时间点并发送 > 时间点生成策略(均匀分布/固定时刻/随机抖动)在 plan 阶段确定;本期只要求“次数正确、用户可控、不会超发”。 ### 8.2 文案来源与语言 - 文案可先采用后端配置的模板(按 `locale` 选择,失败回退 `en` 或 `zh-CN`) - 若未来要接入推荐系统,可在后续迭代按用户画像生成更个性化文案(不在本期范围) --- ## 9. 边界场景与处理原则 - **用户跳过 Onboarding**:不强制开启;可在个人主页再次设置。 - **系统权限 denied**:客户端提示引导去系统设置;后端保存偏好但不保证可推送(无有效 token 时不发送)。 - **token 缺失/过期**:后端发送失败时应标记 token 为不可用,并等待客户端下次上报更新。 - **重复开关/改次数**:后端必须幂等更新,避免同一用户一天内重复排程导致超发。 - **环境隔离**:dev/prod 的 token 与配置必须隔离(`env + app_id` 维度)。 - **卸载/重装**:`client_user_id` 可能变化;视为新用户实例(符合匿名策略)。 --- ## 10. 验收标准(Acceptance Criteria) - 首次进入 Onboarding: - 用户可选择每日次数(0~5)或跳过 - 选择后不阻塞进入主功能 - 个人主页每日提醒弹窗: - 可设置次数(0~5)与关闭 - 权限 denied 时有明确提示 - 客户端: - `client_user_id` 稳定持久化 - 在获得权限与 token 后能上报后端(幂等) - 修改偏好会同步到后端(幂等) - 后端: - 能保存 token 与偏好 - 能按用户配置每日推送(次数正确、不会超发) - token 失效可被识别并停止对失效 token 推送 --- ## 11. 待确认问题清单(进入 plan/tasks 前必须确认) 1. **“0~5 次”的含义**:是否严格表示“每天发送 0~5 条通知”(看起来是),还是“提醒强度档位”? 2. **发送时间策略**:默认窗口与时间点生成方式(固定时刻 vs 均匀分布 vs 随机抖动)选哪一种? 3. **时区来源**:以客户端上报的 IANA 时区为准吗?若缺失回退到什么策略? 4. **推送文案**:本期文案是否完全由后端模板控制(便于随时调整),还是前端上报“文案 key/参数”由后端拼装? 5. **Push 允许发送的静默规则**:是否需要“勿扰时间段/睡眠模式”开关(例如 22:00~08:00 不推)?