5.6 KiB
5.6 KiB
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 数据模型(最小可用)
push_tokens
- 唯一键:
env + app_id + push_token - 字段:
client_user_id(UUID string)platform(ios/android)push_tokenapp_idenvis_activelast_seen_at
push_preferences
- 主键:
client_user_id(或加env + app_id做隔离) - 字段:
enabledtimes_per_day(0~5)timezone(IANA,来自客户端)locale(用于文案选择)updated_at
push_send_log(幂等防重复)
- 目的:保证“同一用户同一天第 k 条只发一次”
- 唯一键:
client_user_id + local_date + slot_index(slot_index=1..times_per_day)
- 字段:
scheduled_at(用户时区的时间点)sent_atstatus(scheduled/sent/failed)error(可选)
注:不直接对数据库执行破坏性操作;通过迁移脚本落地表结构,执行迁移需你显式允许后才进行。
3.2 API
按 spec_kit/App Push/spec.md:
POST /v1/push/registerPUT /v1/push/preferencesGET /v1/push/preferencesPOST /v1/push/test(仅 dev)
3.3 推送发送器(Expo)
- 使用
https://exp.host/--/api/v2/push/send - 最小 payload:
to:Expo Push Tokentitle/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 失效可自动停用