fix:小组件- PUSH
This commit is contained in:
169
spec_kit/App Push/plan.md
Normal file
169
spec_kit/App Push/plan.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# App Push|每日提醒推送(客户端 + 后端)|Plan
|
||||
|
||||
> 阶段:技术计划(plan)
|
||||
>
|
||||
> 依据:`spec_kit/App Push/spec.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. 总体思路
|
||||
|
||||
本期以 **Expo Push(Expo Push Token + Expo Push Service)** 为推送通道,实现“每日提醒”闭环:
|
||||
|
||||
- **客户端**:负责
|
||||
- 生成/持久化 `client_user_id`(UUID)
|
||||
- 引导用户设置每日次数(0~5,可跳过)
|
||||
- 申请通知权限、获取 Expo Push Token、并上报后端
|
||||
- 在个人主页的“每日提醒”弹窗修改次数或关闭,并同步到后端
|
||||
- 上报 `timezone`(IANA)与 `locale`(用于文案语言)
|
||||
- **后端**:负责
|
||||
- 保存 token 绑定与用户推送偏好
|
||||
- 每日按用户时区在 9:00~24:00 窗口内生成 \(N\in[0,5]\) 个随机抖动时间点
|
||||
- 在这些时间点向用户发送推送(使用后端 `@overview.md` 约定的个性化推荐模板/文案策略)
|
||||
- 幂等防重复(同一用户同一天同一序号只发一次)
|
||||
|
||||
---
|
||||
|
||||
## 2. 客户端设计
|
||||
|
||||
### 2.1 UUID(client_user_id)
|
||||
|
||||
- 复用 `spec_kit/Client User Identity/spec.md` 的结论:
|
||||
- 默认 UUID v4
|
||||
- 本地稳定持久化
|
||||
- 获取时机:
|
||||
- App 启动即确保已生成(便于后续任何上报都可带上)
|
||||
|
||||
### 2.2 Push 权限与 token 获取策略
|
||||
|
||||
- 在 Onboarding 的“每日提醒”设置页(或 push 引导页)进行权限申请:
|
||||
- 先说明 → 再弹系统权限框
|
||||
- token 获取与上报触发:
|
||||
- 权限 granted 后立即获取 token 并上报
|
||||
- 后续冷启动时可“补偿上报”(避免首轮失败导致后端缺 token)
|
||||
|
||||
### 2.3 每日提醒设置(0~5 次)
|
||||
|
||||
#### Onboarding
|
||||
|
||||
- 新增或复用一个 Onboarding 步骤:
|
||||
- 0~5 次选择
|
||||
- “跳过”按钮(跳过不阻塞)
|
||||
- 当用户选择 `times_per_day > 0`:
|
||||
- 引导用户开启系统通知权限
|
||||
- 成功与否都进入主功能
|
||||
|
||||
#### 个人主页弹窗(Daily Reminder)
|
||||
|
||||
- 现有弹窗:
|
||||
- 次数 +/-(限制 0~5)
|
||||
- 开关(关闭等价 `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`(0~5)
|
||||
- `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 定时任务(每日 9~24,随机抖动)
|
||||
|
||||
策略:**每日“生成当天计划 + 分发 ETA 任务”**(推荐)
|
||||
|
||||
- 每日固定时刻(例如 UTC 00:10 或服务器本地时间某个点)执行一次“生成计划任务”:
|
||||
- 对每个 `enabled && times_per_day>0` 的用户:
|
||||
- 将用户时区当天的 9:00~24: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 失效可自动停用
|
||||
|
||||
220
spec_kit/App Push/spec.md
Normal file
220
spec_kit/App Push/spec.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# 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 不推)?
|
||||
|
||||
127
spec_kit/App Push/tasks.md
Normal file
127
spec_kit/App Push/tasks.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# App Push|每日提醒推送(客户端 + 后端)|Tasks
|
||||
|
||||
> 阶段:任务清单(tasks)
|
||||
>
|
||||
> 依赖:`spec_kit/App Push/spec.md`、`spec_kit/App Push/plan.md`
|
||||
|
||||
---
|
||||
|
||||
## 0. 约束与共识(本期已确认)
|
||||
|
||||
- 每日次数:**0~5 条/天**
|
||||
- 发送窗口:**9:00~24:00(按客户端上报时区)**
|
||||
- 时间点:窗口内 **随机抖动**
|
||||
- 勿扰:**不做**
|
||||
- 文案:使用后端推荐模块的 **Push 场景个性化模板(降风险)**
|
||||
|
||||
---
|
||||
|
||||
## 1. 客户端(Expo RN)
|
||||
|
||||
### 1.1 UUID(client_user_id)
|
||||
|
||||
- [ ] 在 `client/src/storage/appStorage.ts`(或新模块)实现:
|
||||
- `getOrCreateClientUserId(): Promise<string>`
|
||||
- 首次生成 UUID 并持久化,后续复用
|
||||
|
||||
### 1.2 Push 权限与 token
|
||||
|
||||
- [ ] 接入获取 Expo Push Token 的封装(例如 `src/features/push/`):
|
||||
- 获取权限状态
|
||||
- 请求权限
|
||||
- 获取 Expo Push Token
|
||||
- [ ] 在 Onboarding / push 引导完成后:
|
||||
- granted 时:获取 token + 调 `register`
|
||||
- 未 granted:仅保存本地设置,不阻塞进入主功能
|
||||
|
||||
### 1.3 “每日提醒次数”入口
|
||||
|
||||
- [ ] Onboarding:新增/复用一个步骤页面
|
||||
- 选择 0~5 次(0 表示关闭)
|
||||
- 支持跳过
|
||||
- 完成后写本地存储,并调用后端 `preferences`
|
||||
- [ ] 个人主页弹窗:
|
||||
- 修复“测试模式强制无权限”的逻辑
|
||||
- 次数限制 0~5;关闭等价 0
|
||||
- denied 时提示去系统设置
|
||||
- 修改后写本地存储,并调用后端 `preferences`
|
||||
|
||||
### 1.4 接口封装
|
||||
|
||||
- [ ] 新增 `client/src/services/pushApi.ts`
|
||||
- `registerPushToken(...)`
|
||||
- `setPushPreferences(...)`
|
||||
- `getPushPreferences(...)`(可选)
|
||||
- [ ] 统一使用 `client/src/utils/http.ts` 的请求封装
|
||||
|
||||
### 1.5 联调开关与日志
|
||||
|
||||
- [ ] 开发环境输出必要日志(不打印敏感信息):
|
||||
- client_user_id 生成结果
|
||||
- 权限状态与 token 是否获取成功
|
||||
- register/preferences 的请求是否成功与错误原因
|
||||
|
||||
---
|
||||
|
||||
## 2. 后端(FastAPI)
|
||||
|
||||
### 2.1 数据模型与迁移
|
||||
|
||||
- [ ] 新增表(或等价模型):
|
||||
- `push_tokens`
|
||||
- `push_preferences`
|
||||
- `push_send_log`(幂等防重复)
|
||||
- [ ] 增加迁移脚本(Alembic 或项目现有迁移机制)
|
||||
|
||||
> 注意:不执行任何破坏性数据库操作;运行迁移前若涉及真实库,需要你明确回复“允许操作数据库”。
|
||||
|
||||
### 2.2 API
|
||||
|
||||
- [ ] `POST /v1/push/register`
|
||||
- 幂等:`env + app_id + push_token` 唯一
|
||||
- 更新 `client_user_id` 归属与 `last_seen_at`
|
||||
- [ ] `PUT /v1/push/preferences`
|
||||
- 校验 `times_per_day` ∈ [0,5]
|
||||
- `enabled=false` 或 `times_per_day=0` 时停止后续推送
|
||||
- [ ] `GET /v1/push/preferences`
|
||||
- 返回当前偏好与更新时间
|
||||
- [ ] `POST /v1/push/test`(仅 dev)
|
||||
- 立即向该用户发送一条测试推送(用于真机联调)
|
||||
|
||||
### 2.3 Expo 推送发送器
|
||||
|
||||
- [ ] 实现 `send_expo_push(token, title, body, data?)`
|
||||
- 处理 Expo 返回错误并对不可恢复错误停用 token
|
||||
- 记录发送结果到 `push_send_log`
|
||||
|
||||
### 2.4 推送文案(推荐模板)
|
||||
|
||||
- [ ] 在推送任务中调用后端推荐模块的 Push 场景模板:
|
||||
- 输入:`client_user_id`、语言/时区(可选)
|
||||
- 输出:`title/body`
|
||||
- 默认“降个性化/降风险”
|
||||
|
||||
---
|
||||
|
||||
## 3. 定时任务(每日计划 + ETA 发送)
|
||||
|
||||
- [ ] 每日“计划生成任务”
|
||||
- 扫描 `enabled && times_per_day>0` 用户
|
||||
- 按用户时区在 9:00~24:00 生成 N 个随机抖动时间点
|
||||
- 写入 `push_send_log`(唯一键保证幂等)
|
||||
- 投递 ETA 发送任务(或按项目现有任务系统实现)
|
||||
- [ ] ETA “发送任务”
|
||||
- 拉取当次发送所需 token/偏好
|
||||
- 生成文案(推荐模板)
|
||||
- 调用 Expo push 发送
|
||||
- 更新 `push_send_log` 状态
|
||||
|
||||
---
|
||||
|
||||
## 4. 验收与回归
|
||||
|
||||
- [ ] iOS 真机:权限申请、token 获取、test 推送可达
|
||||
- [ ] Android 真机:权限申请、token 获取、test 推送可达
|
||||
- [ ] 修改次数:后端计划生成正确(一天内不超发)
|
||||
- [ ] 关闭:后端停止后续推送(不再生成计划/不再发送)
|
||||
|
||||
Reference in New Issue
Block a user