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

5.5 KiB
Raw Permalink Blame History

Daily Widget Reco任务清单

对应计划:spec_kit/Daily Widget Reco/plan.md

标记规则:

  • 执行完成后将对应项从 - [ ] 改为 - [x],并把“状态”改为 已完成
  • 进行中改为 进行中
  • 阻塞写明原因与解除条件

0. 清单状态说明

  • 状态:未开始 / 进行中 / 已完成 / 阻塞
  • 阻塞:必须写明阻塞点与解除条件

1. 客户端:补齐 Widget 场景 APIRN

  • 新增 fetchRecoWidgetPOST /v1/reco/widget
    • 状态:未开始
    • 文件client/src/services/recoApi.ts
    • 要求
      • Header Accept-Language 逻辑沿用 fetchRecoFeed
      • body 结构沿用 RecoRequest
      • timeout 建议 12s
    • 验收:在 App 内调用可拿到 items[0].text(允许为空但不报错)

2. iOS 原生能力App Group 共享读写suiteName 已确认)

suiteNamegroup.com.damer.mindfulness

  • 新增 App Group 共享存储原生模块RN Bridge

    • 状态:未开始
    • 目标RN 可写/读共享 UserDefaults(suiteName:)
    • 要求
      • 支持 setString(key, value) / getString(key)(最小闭环)
      • 以 JSON 字符串形式存储(上层 JS 自行 JSON.stringify/parse
    • 验收
      • RN 写入后Widget 侧能读到同一 key
      • 解析失败不 crash返回 null/空字符串均可,按上层回退)
  • 新增触发 Widget 刷新的原生方法(可选但建议)

    • 状态:未开始
    • 目标App 写入缓存后触发 WidgetCenter.reloadAllTimelines()
    • 验收App 调用后Widget 在合理时间内刷新显示新内容(允许系统延迟)

3. 客户端:写入共享配置/画像/每日缓存App ↔ Widget 共通)

  • 写入共享配置 widget.config.v1(包含 apiBaseUrl

    • 状态:未开始
    • 要求
      • App 启动后(或 baseURL 计算完成后)写入
      • JSON 字段最少包含:schema_versionapiBaseUrlsaved_at
    • 验收Widget 侧可读到正确 baseURLdev/pro 随 App 切换)
  • 写入共享画像 widget.userProfile.v1_2Onboarding 完成后)

    • 状态:未开始
    • 依赖:客户端已能获取/持久化用户画像(本地存储已有 user.profileScoring
    • 要求
      • 结构以现有 UserProfileV1_2 输出为准
      • JSON 字段建议包含:schema_versionsaved_atuser_profile
    • 验收Widget 侧可读到 user_profile,且可用于请求后端
  • 实现 App 前台“可选主动拉取”并写入 widget.dailyReco.v1

    • 状态:未开始
    • 要求
      • 判断 day_key(用户时区 YYYY-MM-DD)不是今天时才拉取
      • 调用 fetchRecoWidget(k=1),成功写入缓存
      • 失败不影响 UI不阻塞主流程
    • 验收App 打开后能把今日推荐写入共享缓存Widget reload 后展示一致

4. iOS Widget读取共享缓存并展示先不接网络也能跑通

  • Widget 读取 widget.dailyReco.v1 并展示
    • 状态:未开始
    • 要求
      • JSON 解析失败容错:视为无缓存
      • 展示优先级:今日缓存 → 最近缓存 → 兜底文案
    • 验收手动写入共享缓存后Widget 立即能展示对应文案(或在下一次 reload 展示)

5. iOS Widget网络拉取每日推荐 + Timeline 每日刷新(核心)

  • Widget 在缓存过期时请求 POST /v1/reco/widget 并写回缓存

    • 状态:未开始
    • 依赖:可读到 widget.config.v1apiBaseUrlwidget.userProfile.v1_2user_profile
    • 要求
      • 无鉴权
      • 超时建议 812s
      • Accept-Language 与 App 保持一致(en/tc
      • items=[] 视为失败,走回退
    • 验收清空今日缓存后Widget 能在一次 timeline 请求内拉取并展示今日推荐(允许先兜底再更新)
  • 实现每日刷新调度TimelinePolicy

    • 状态:未开始
    • 要求
      • nextRefreshDate 设置为“下一天本地时间 00:1001:00 随机”之一
      • 不追求严格准点,但确保每日范围内能更新
    • 验收:通过修改系统日期/模拟 day_key 变化,可观察到会进入“过期→重新拉取”逻辑

6. 联调与验收验证(必须)

  • 联调:添加小组件后,未打开 App 也能每日更新(尽力而为)

    • 状态:未开始
    • 步骤
      • 安装带 Widget 的开发包
      • 在桌面添加 Widget
      • 清空今日缓存并等待 Widget 刷新(或手动触发 reload
    • 验收Widget 能从后端拉取并展示;隔天能更新到新内容(允许系统延迟)
  • 回退验证:断网/后端不可用/返回异常

    • 状态:未开始
    • 验收
      • Widget 不崩溃
      • 展示最近缓存或兜底文案
  • 一致性验证App 与 Widget 当天展示一致

    • 状态:未开始
    • 验收App 主动拉取并写入缓存后Widget reload 后展示同一条 content_id/text

7. 文档与总览更新

  • 补充实现说明(可放入客户端 README 或本需求 overflow

    • 状态:未开始
    • 要求:记录 suiteName、共享 key、如何本地验证
  • 更新 spec_kit/overview.md

    • 状态:未开始
    • 要求在“Spec Kit Overview”中新增本需求摘要目标/产物/已完成编码变更)