Files
2026-02-03 17:43:58 +08:00

221 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# App Push每日提醒推送客户端 + 后端Spec
> 阶段高层规范spec
>
> 目标:实现“每日提醒”推送闭环:用户在首次进入 Onboarding 可选择每天推送次数05可跳过并可在个人主页的“每日提醒”弹窗随时调整次数或关闭客户端使用首次生成的 UUID 作为用户标识与后端关联;后端按用户配置定时下发推送。
---
## 1. 背景与动机(摘要)
当前客户端已有 Push 引导页(可跳过)与“每日提醒”设置入口,但尚未形成完整闭环:
- 需要一个稳定的匿名用户标识UUID将“提醒配置”与“推送 token”绑定到后端
- 需要后端能够按用户选择的每日次数05进行定时推送
- 本期不追求高级推送能力(富媒体、复杂分群、到达率分析等)
---
## 2. 目标Goals
- **G1Onboarding 选择**:用户首次进入 Onboarding 可设置“每日提醒次数”05可跳过且不阻塞进入主功能。
- **G2设置页可改可关**用户可在个人主页的“每日提醒”弹窗调整次数05或关闭推送。
- **G3UUID 关联**:客户端首次进入生成 `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 某一页提供“每日提醒次数”选择05
- 0 表示不接收每日提醒(等同关闭)
- 可点击“跳过”(不设置/不打扰)
3. 若用户选择次数 > 0
- 展示 Push 引导说明页
- 用户点击“立即开启”触发系统权限申请
4. 无论是否开启成功,均可进入主功能(不阻塞)
### 5.2 个人主页(每日提醒弹窗)
- 用户可:
- 调整每日次数05
- 开启/关闭提醒(关闭等价次数=0
- 若系统权限为 denied提示用户前往系统设置开启不做强制跳转要求按平台能力实现
### 5.3 后端推送执行
- 后端按用户配置与时区,在每日窗口内发送 \(N\) 次推送(\(N\in[0,5]\)
- 若用户关闭或次数=0不再推送
---
## 6. 数据与持久化(高层)
### 6.1 客户端本地存储(最小集合)
- `client_user_id`: stringUUID稳定持久化见用户标识 spec
- `push.permission_state`: `unknown | granted | denied`(用于 UI 显示与引导)
- `daily_reminder.enabled`: boolean可选或由次数是否为 0 推导)
- `daily_reminder.times_per_day`: number05
- `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`05
- `timezone`(建议 IANA`Asia/Shanghai`;若拿不到则回退为服务器默认策略)
- `locale`(用于推送文案语言选择;可从客户端上报或推送时推断)
- `updated_at`
---
## 7. API 契约(高层)
> 说明:路由名可在 plan 阶段对齐现有服务结构;此处先固定“字段语义 + 幂等行为”。
### 7.1 注册/更新 Push Token幂等
- `POST /v1/push/register`
- 请求体(最小集,继承用户标识 spec
- `client_user_id`: stringUUID
- `platform`: `"ios" | "android"`
- `push_token`: stringExpo 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`: number05若 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 发送次数与窗口
- 每日发送次数05
- 建议定义“允许发送的时间窗口”(例如 09:0021: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
- 用户可选择每日次数05或跳过
- 选择后不阻塞进入主功能
- 个人主页每日提醒弹窗:
- 可设置次数05与关闭
- 权限 denied 时有明确提示
- 客户端:
- `client_user_id` 稳定持久化
- 在获得权限与 token 后能上报后端(幂等)
- 修改偏好会同步到后端(幂等)
- 后端:
- 能保存 token 与偏好
- 能按用户配置每日推送(次数正确、不会超发)
- token 失效可被识别并停止对失效 token 推送
---
## 11. 待确认问题清单(进入 plan/tasks 前必须确认)
1. **“05 次”的含义**:是否严格表示“每天发送 05 条通知”(看起来是),还是“提醒强度档位”?
2. **发送时间策略**:默认窗口与时间点生成方式(固定时刻 vs 均匀分布 vs 随机抖动)选哪一种?
3. **时区来源**:以客户端上报的 IANA 时区为准吗?若缺失回退到什么策略?
4. **推送文案**:本期文案是否完全由后端模板控制(便于随时调整),还是前端上报“文案 key/参数”由后端拼装?
5. **Push 允许发送的静默规则**:是否需要“勿扰时间段/睡眠模式”开关(例如 22:0008:00 不推)?