Files
mindfulness/spec_kit/App Push/plan.md
2026-02-03 17:43:58 +08:00

170 lines
5.6 KiB
Markdown
Raw 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每日提醒推送客户端 + 后端Plan
> 阶段技术计划plan
>
> 依据:`spec_kit/App Push/spec.md`
---
## 1. 总体思路
本期以 **Expo PushExpo Push Token + Expo Push Service** 为推送通道,实现“每日提醒”闭环:
- **客户端**:负责
- 生成/持久化 `client_user_id`UUID
- 引导用户设置每日次数05可跳过
- 申请通知权限、获取 Expo Push Token、并上报后端
- 在个人主页的“每日提醒”弹窗修改次数或关闭,并同步到后端
- 上报 `timezone`IANA`locale`(用于文案语言)
- **后端**:负责
- 保存 token 绑定与用户推送偏好
- 每日按用户时区在 9:0024:00 窗口内生成 \(N\in[0,5]\) 个随机抖动时间点
- 在这些时间点向用户发送推送(使用后端 `@overview.md` 约定的个性化推荐模板/文案策略)
- 幂等防重复(同一用户同一天同一序号只发一次)
---
## 2. 客户端设计
### 2.1 UUIDclient_user_id
- 复用 `spec_kit/Client User Identity/spec.md` 的结论:
- 默认 UUID v4
- 本地稳定持久化
- 获取时机:
- App 启动即确保已生成(便于后续任何上报都可带上)
### 2.2 Push 权限与 token 获取策略
- 在 Onboarding 的“每日提醒”设置页(或 push 引导页)进行权限申请:
- 先说明 → 再弹系统权限框
- token 获取与上报触发:
- 权限 granted 后立即获取 token 并上报
- 后续冷启动时可“补偿上报”(避免首轮失败导致后端缺 token
### 2.3 每日提醒设置05 次)
#### Onboarding
- 新增或复用一个 Onboarding 步骤:
- 05 次选择
- “跳过”按钮(跳过不阻塞)
- 当用户选择 `times_per_day > 0`
- 引导用户开启系统通知权限
- 成功与否都进入主功能
#### 个人主页弹窗Daily Reminder
- 现有弹窗:
- 次数 +/-(限制 05
- 开关(关闭等价 `times_per_day=0`
- 权限 denied给出清晰提示
- 修复:移除/关闭“测试模式强制无权限”逻辑(该逻辑会导致永远表现为无法开启)
### 2.4 客户端与后端接口调用
- 新增 `client/src/services/pushApi.ts`(参考现有 `legalApi.ts` / `recoApi.ts` 的风格),并使用统一 HTTP 封装 `client/src/utils/http.ts`
- 接口调用点:
- register拿到 token 后
- preferences用户完成选择或在个人主页修改后
---
## 3. 后端设计
### 3.1 数据模型(最小可用)
1) `push_tokens`
- 唯一键:`env + app_id + push_token`
- 字段:
- `client_user_id`UUID string
- `platform`ios/android
- `push_token`
- `app_id`
- `env`
- `is_active`
- `last_seen_at`
2) `push_preferences`
- 主键:`client_user_id`(或加 `env + app_id` 做隔离)
- 字段:
- `enabled`
- `times_per_day`05
- `timezone`IANA来自客户端
- `locale`(用于文案选择)
- `updated_at`
3) `push_send_log`(幂等防重复)
- 目的:保证“同一用户同一天第 k 条只发一次”
- 唯一键:
- `client_user_id + local_date + slot_index`slot_index=1..times_per_day
- 字段:
- `scheduled_at`(用户时区的时间点)
- `sent_at`
- `status`scheduled/sent/failed
- `error`(可选)
> 注:不直接对数据库执行破坏性操作;通过迁移脚本落地表结构,执行迁移需你显式允许后才进行。
### 3.2 API
`spec_kit/App Push/spec.md`
- `POST /v1/push/register`
- `PUT /v1/push/preferences`
- `GET /v1/push/preferences`
- `POST /v1/push/test`(仅 dev
### 3.3 推送发送器Expo
- 使用 `https://exp.host/--/api/v2/push/send`
- 最小 payload
- `to`Expo Push Token
- `title/body`:来自个性化推荐模板
- `data`:深链路参数(可选)
- 失败处理:
- 对 “DeviceNotRegistered”等不可恢复错误将 token 标记为 inactive
- 记录失败原因到 `push_send_log`
### 3.4 定时任务(每日 924随机抖动
策略:**每日“生成当天计划 + 分发 ETA 任务”**(推荐)
- 每日固定时刻(例如 UTC 00:10 或服务器本地时间某个点)执行一次“生成计划任务”:
- 对每个 `enabled && times_per_day>0` 的用户:
- 将用户时区当天的 9:0024:00 转换为 UTC
- 随机生成 \(N\) 个时间点:
- 均匀切分窗口为 \(N\) 个区间
- 每个区间内随机选一个时间(抖动)
- 对每个 slot 写入 `push_send_log`(唯一键保证幂等)
- 再投递 Celery ETA 任务,到点调用“发送 push”
这样可以避免 worker 长时间 sleep也便于观察“今天会推哪些”。
---
## 4. 个性化推荐模板(后端 @overview.md
推送文案生成使用后端推荐模块中 “Push 场景模板/策略”:
- 输入:`client_user_id`、(可选)用户画像/行为统计
- 输出:`title/body`(多语言)
- 风险控制Push 场景默认“降个性化/降风险”,避免敏感/医疗类文案(遵守后端已定义的安全规则)
> plan 阶段仅明确“调用点与输入输出”,具体实现复用后端推荐模块现有能力,避免重复逻辑。
---
## 5. 测试与验收(落地检查清单)
- 客户端:
- 首次进入可选择次数/跳过
- 权限 granted 时能拿到 Expo token 并上报
- 设置页改次数/关闭会调用 preferences 接口并持久化
- 后端:
- register/preferences 接口幂等可重试
- test 接口可在 dev 环境立刻推送到真机
- 每日计划生成不会重复排程(`push_send_log` 唯一键)
- token 失效可自动停用