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

5.6 KiB
Raw Blame History

App Push每日提醒推送客户端 + 后端Plan

阶段技术计划plan

依据:spec_kit/App Push/spec.md


1. 总体思路

本期以 Expo PushExpo Push Token + Expo Push Service 为推送通道,实现“每日提醒”闭环:

  • 客户端:负责
    • 生成/持久化 client_user_idUUID
    • 引导用户设置每日次数05可跳过
    • 申请通知权限、获取 Expo Push Token、并上报后端
    • 在个人主页的“每日提醒”弹窗修改次数或关闭,并同步到后端
    • 上报 timezoneIANAlocale(用于文案语言)
  • 后端:负责
    • 保存 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_idUUID string
    • platformios/android
    • push_token
    • app_id
    • env
    • is_active
    • last_seen_at
  1. push_preferences
  • 主键:client_user_id(或加 env + app_id 做隔离)
  • 字段:
    • enabled
    • times_per_day05
    • timezoneIANA来自客户端
    • locale(用于文案选择)
    • updated_at
  1. push_send_log(幂等防重复)
  • 目的:保证“同一用户同一天第 k 条只发一次”
  • 唯一键:
    • client_user_id + local_date + slot_indexslot_index=1..times_per_day
  • 字段:
    • scheduled_at(用户时区的时间点)
    • sent_at
    • statusscheduled/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
    • toExpo 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 失效可自动停用