- TaskBonusPanel 重写:每日任务/新手任务分组、五态按钮、真实「去完成」跳转(first_report→诊断落地页;report_feedback/share_report→报告结果页)、40311/40310 文案、panel_view/task_tap 埋点 - ReportPdfPage 分享成功→静默 claim share_report;ReportResultPage SUCCEEDED 展示→静默 claim first_report(后端 40910 幂等兜底,老用户首进补发一次属预期回填) - ChatPage/ReportResultPage 修 bonus_granted 类型误写(boolean→number) - docs:T2b 任务书(验收 11 条)+ 产品方案 §3.2 任务目录定稿(签到对话+10/日、报告反馈发型+2/日、分享换装+3/日、首测重生成+1/终身;邀请好友砍) 偏差说明:share_report 落点为报告结果页而非 PDF 预览页(该页强依赖本地 pdfPath,直达需下迭代加持久化)。
14 KiB
T2b · 任务换额度:端侧闭环与完成判定
对应任务:
T-P1-26收尾件之二(T2 的后续迭代)| 需求:FR-BILL-03、FR-BILL-04、FR-BILL-07| 跨端任务:后端(meida_backend)+ 端侧(app/) 先读《README.md(协作规程)》与《README-FE批次.md》,遵守全部纪律。 产品定稿依据:产品方案 §3.2「任务奖励额度的获取方式」(2026-09-11 定稿表与决策理由)。
1. 目标
把「做任务换额度」从「只有签到能真领」补齐为完整闭环:
- 任务目录按产品定稿落地(4 项上线 +
invite_friend下架),奖励数值由占位值 1 改为定稿值; - 有服务端可信信号的任务(
report_feedback/first_report)在 claim 时做完成校验,无凭据拒绝; - 端侧面板从「toast 占位」升级为真实「去完成」跳转 + 完成点自动 claim;
- 全程遵守
FR-BILL-07:文案不出现价格/购买/会员/升级字样。
2. 现状事实(2026-09-11 读码核实)
后端(meida_backend)
- ✅
POST /quota/tasks/{task_key}/claim已实现(T2):幂等(bl_ledger.idempotency_key快捷路径 + 唯一约束并发兜底)、封顶(grant_bonus对task_bonus_max)、配置化(TASK_BONUS_RULES_JSON)、错误码 40410/40910/40310 文案合规。见apps/api/routers/quota.py:65、packages/billing/service.py:306、packages/domain/billing/quota.py:146。 - ✅ 配额种子与产品方案 §3.2 对齐(
migrations/versions/0003_align_quota_with_product_ssot.py):agent_chat10/10/40、tryon3/3/8、hairstyle3/2/6、hd_export1/0/3、report_basic终身 1、report_regen月 1/1/2。 - ❌ claim 不做任何完成凭据校验:任何登录用户对任何 task_key 直接 POST 即领。
report_feedback(rp_feedback表)与first_report(rp_report表)明明有可信信号却没用上。 - ❌
task_bonus_rules默认值(packages/shared/config.py:136)amount 全是占位值 1,且映射与 §3.2 总量对不上:换装最多 +2/日(表定 +3)、发型 0(表定 +2)、对话 +2(表定 +10)。 - ❌ 无任务目录下发接口——V1 决定端侧静态表,不做。
- 事实备注:
rp_share表已建结构但无路由(models.py 注释「V1 只建结构」);POST /events/batch无事件白名单(事件字典待 TD-23)。
端侧(app/entry/src/main/ets/)
- ✅
components/f1/TaskBonusPanel.ets:五项静态任务、已领态走core/TaskBonusState.ets(preferencesmeida_task_bonus,跨重启)、40910 同步已领态、文案合规;已挂载 5 处(ProfileContent/ChatPage/FittingRoomPage/HairDesignPage/HairColorPage)。 - ✅ 真实闭环已有两条:
sign_in点击即 claim(面板内);report_feedback在报告页反馈提交成功后静默自动 claim(ReportResultPage.ets:435)。 - ✅
DailyLimiter.setBonus已把bonus_granted计入试穿/试戴当日上限;ChatPage对话额度服务端口径(remaining 含 bonus)。 - ❌ 其余 3 项任务是 toast 占位(「即将上线」),无「去完成」跳转。
- ❌
share_report无联动:ReportPdfPage.ets:90已有 Share KitsystemShare分享 PDF,但分享动作与 claim 之间没有凭据、没有联动。 - ❌ 报告生成成功处无
first_reportclaim 联动。 - ❌ 面板不展示每项的奖励数值与周期(任务表纪律是"数值不硬编码",定稿后可写入展示文案,到账仍以 claim 响应为准)。
- 🐞
ChatPage.ets:36QuotaWireResp.bonus_granted类型误写为boolean(后端返回 number;该字段暂未消费,本迭代顺手修正)。
判定与埋点
- ✅ 埋点链路通:
Tracker→POST /events/batch→ob_event(整批 fail-close 校验)。 - ❌ 任务完成事件目前只有
task_bonus.claim_result;无面板曝光、任务点击、分享调起事件。 - 产品决策(已写进 §3.2):埋点只作观测,不作发放凭据——埋点本身可伪造且有 20 条/30s 节流延迟,拿它做校验是自欺。
3. V1 任务目录(产品定稿,2026-09-11)
| task_key | 名称 | quota_key | amount | claim_period | 完成判定 | 状态 |
|---|---|---|---|---|---|---|
sign_in |
每日签到 | agent_chat |
10 | day |
点击签到即完成,无额外校验 | 上 |
report_feedback |
给报告结论打反馈 | hairstyle |
2 | day |
服务端校验当日 rp_feedback 存在 |
上 |
share_report |
分享我的报告 | tryon |
3 | day |
端侧自报,无服务端校验(内测水位) | 上 |
first_report |
完成首次形象速测 | report_regen |
1 | lifetime |
服务端校验 rp_report 存在已完成报告 |
上 |
invite_friend |
邀请好友 | — | — | — | 无可信信号 | 下架(env 不下发该键) |
变更点:删除 profile_complete(资料维护 FR-ACC-06 V1 未实现,无可信信号),新增 first_report;report_feedback 由 report_regen 改挂 hairstyle(理由见产品方案 §3.2 决策理由第 3 条)。
4. 实现要点
后端(worktree 纪律见《README.md》)
允许改动:packages/billing/service.py、packages/billing/repo.py(如需)、packages/billing/schemas.py(如需)、测试新文件。
- B1 · 定稿数值走 env:部署/本地
.env的TASK_BONUS_RULES_JSON改为:不含{ "sign_in": {"quota_key": "agent_chat", "amount": 10, "claim_period": "day"}, "report_feedback": {"quota_key": "hairstyle", "amount": 2, "claim_period": "day"}, "share_report": {"quota_key": "tryon", "amount": 3, "claim_period": "day"}, "first_report": {"quota_key": "report_regen", "amount": 1, "claim_period": "lifetime"} }invite_friend——不下发即下架(claim 返回 40410「任务不存在或暂未开放」),这是配置化设计的红利,不要为它写分支代码。 ⚠️config.py的默认值是占位且禁改;在提交信息里写明「默认值与定稿不一致,建议协调提交同步默认值与.env.example」,由协调者统一处理。 - B2 · claim 完成校验钩子:
BillingService.claim_task_bonus在解析规则后、幂等快捷路径之前,按 task_key 执行完成校验(映射写死在 service 内即可,不做配置化):report_feedback:rp_feedback存在user_id匹配且created_at落在当日(UTC+8,与period_key同时区口径)的行;first_report:rp_report存在user_id匹配且状态为已完成的行;sign_in/share_report:不校验,写注释说明产品决策(签到行为本身即任务;分享内测期自报,hard_cap兜底,见 §3.2 决策理由 4)。- 校验不过 →
AppError(ErrorCode.TASK_BONUS_NOT_ELIGIBLE)。该错误码尚不存在(建议 40311,meta「先完成任务,再来领奖励」)——errors.py禁改,在提交信息中列出需求,由协调提交统一加;本地开发可先在 service 里raise AppError(...)引用占位并在提交信息标注。
- B3 · 幂等与封顶语义不变:40910/40310 路径、
delta=0流水口径不动;校验逻辑不得影响既有测试。
端侧(纯 Windows,构建门禁见《README-FE批次.md》§1)
允许改动:components/f1/TaskBonusPanel.ets、pages/ReportResultPage.ets、pages/ReportPdfPage.ets、pages/ChatPage.ets(仅 B 端 wire 类型修正)。其余页面如需挂回调点,在提交信息里说明。
- E1 · 面板任务表更新为定稿 4 项:删
invite_friend;profile_complete替换为first_report(名称「完成首次形象速测」);每项 desc 写清奖励内容(如「领 10 轮 AI 对话额度」「领 2 次发型试戴额度」)并标注周期(「每日」/「一次性」)。到账数值仍以 claim 响应amount为准,desc 只是展示。 - E2 · 「去完成」真实跳转(
router.pushUrl,禁止 import 页面):first_report→pages/DiagnosisLandingPage;进入面板时若已可领(服务端已有报告且未领过)则按钮直接为「领额度」走 claim;report_feedback→ 报告结果页(带reportId参数);无报告时 toast「先生成你的形象速测,再来反馈」;share_report→pages/ReportPdfPage(分享入口所在页);无报告时同上引导;sign_in保持点击即领。
- E3 · 完成点自动 claim(与既有
claimReportFeedback同款静默闭环:成功 toast 到账、40910 静默同步已领态、其他失败静默):ReportPdfPage.share()调起系统分享面板成功后:静默 claimshare_report+TaskBonusState.markClaimed('share_report', PERIOD_DAY)+Tracker.track('report.share_invoke', ...);- 报告生成任务成功(
ReportResultPage展示 SUCCEEDED 结果的入口路径):静默 claimfirst_report+markClaimed。
- E4 · 状态与文案:见 §5,逐字遵守。
- E5 · 埋点补齐(事件名字面量、props 只放低维枚举/数值):
task_bonus.panel_view、task_bonus.task_tap(props:task_key)、report.share_invoke、task_bonus.claim_result(已有,保留)。 - E6 · 修正
ChatPage.ets:36bonus_granted: boolean→number。
5. UX 与文案规格(FR-BILL-07,逐字遵守)
面板信息架构:标题「做任务换额度」;分组一「每日任务」(签到 / 打反馈 / 分享报告);分组二「新手任务」(完成首次形象速测);底部说明保留「奖励以实际到账为准,额度每日重置」。
按钮五态:
| 状态 | 文案 |
|---|---|
| 可领取(签到 / 已满足条件) | 「领额度」 |
| 需跳转完成 | 「去完成」 |
| 领取中 | 「领取中…」 |
| 已领(日周期) | 「今日已领」 |
| 已领(终身) | 「已领取」 |
toast:
| 场景 | 文案 |
|---|---|
| 领取成功 | 「签到成功,额度 +{amount}」/「感谢反馈,额度 +{amount}」(amount 取响应值,不硬编码) |
| 40910 本周期已领 | 「今天已经领过啦」 |
| 40310 奖励封顶 | 「今日任务奖励已达上限,明天再来」 |
| 40311 未完成 | 「先完成任务,再来领奖励」 |
| 无报告时去做反馈/分享 | 「先生成你的形象速测,再来反馈」 |
红线:全部新增文案不得出现价格、购买、会员、升级、充值等字样(FR-BILL-07);额度耗尽入口文案维持「今日额度已用完,明天再来,或做个任务多换几次」口径。
6. 测试要求
后端
- 单测:校验钩子纯逻辑(若抽纯函数)覆盖有/无凭据两分支。
- 集成测试新文件
tests/integration/test_task_bonus_eligibility.py(允许跑,MEIDA_RUN_INTEGRATION=1):- 无报告用户 claim
first_report→ 40311;创建报告完成后 → 200 且bonus_granted=1; - 无反馈 claim
report_feedback→ 40311;提交一条反馈后 → 200 且发型bonus_granted=2; sign_inclaim → 200,agent_chatbonus_granted=10;当日再 claim → 40910;invite_friend(不在 env)→ 40410;- 40311 与 40310 路径均不写流水(查
bl_ledger无新行)。
- 无报告用户 claim
- 提交前全绿:
ruff check/ruff format --check/mypy(范围同 T2)/lint-imports/pytest -q。
端侧
- 构建门禁(唯一验收命令,见《README-FE批次.md》§1)
BUILD SUCCESSFUL; - ArkTS 严格模式铁律(README-FE批次 §3)逐条遵守:显式泛型、具名 wire interface、throw 只抛 ApiError。
7. 禁改清单(通用禁改之外)
- 后端:
packages/shared/config.py、packages/shared/errors.py、packages/shared/models.py、migrations/**、apps/api/routers/events.py、packages/analytics/**。新错误码与默认值同步只写进提交信息,由协调者处理。 - 端侧:
core/**(含TaskBonusState.ets、Tracker.ets、DailyLimiter.ets——本迭代需求已够,不要顺手扩展)、module.json5、main_pages.json、resources、oh-package*.json5。 - 不改配额种子数值、不动
grant_bonus语义、不改既有测试断言语义。
8. 验收标准(按组,每条可验收)
后端
- 四个 task_key 按 §3 定稿数值 claim 成功,响应
amount与定稿一致;invite_friend返回 40410。 - 无凭据 claim
report_feedback/first_report返回 40311 且无流水;有凭据后 claim 成功。 - 重复 claim 40910、封顶 40310 行为与 T2 验收一致,无回归(既有
test_task_bonus.py全绿)。 - 后端检查全绿(§6)。
端侧
- 面板只展示 4 项定稿任务,奖励文案、周期标注、五态按钮与 §5 一致。
- 三项「去完成」跳转可达目标页;无报告场景给引导 toast 而非报错。
- 分享调起成功、报告生成成功两处自动 claim 生效,面板已领态同步(含杀进程重启后)。
ChatPagewire 类型修正后构建绿;埋点新增事件在ob_event可见。
联调
- 全链路走查(真机或模拟器连本地后端):签到 → 对话额度卡 +10;报告页打反馈 → 发型当日上限 +2(
DailyLimiter生效);PDF 页分享 → 换装当日上限 +3;完成首次速测 → 重生成额度 +1(个人中心额度卡可见)。 - 异常路径走查:同日重复领取 →「今天已经领过啦」;未打反馈直接在面板点领取(若 UI 可达)→「先完成任务,再来领奖励」。
- 文案红线检查:任务面板、toast、额度耗尽提示全文检索无「价格/购买/会员/升级/充值/¥」字样。
9. 提交信息
后端:
feat(billing): task bonus final rules + eligibility check (T2b)
TASK_BONUS_RULES_JSON 定稿(签到+10对话/反馈+2发型/分享+3换装/首测+1重生成,
invite_friend 下架);report_feedback/first_report claim 前完成校验(rp_feedback/
rp_report 可信信号),sign_in/share_report 不校验(产品决策见产品方案 §3.2)。
偏差说明:TASK_BONUS_NOT_ELIGIBLE(40311) 错误码与 config.py 默认值同步需协调提交;
<其余偏差写这里,无则写"无">
端侧:
feat(f1): task bonus panel final catalog + auto-claim hooks (T2b)
面板 4 项定稿任务与「去完成」真实跳转;分享调起/报告生成成功自动 claim;
新增 panel_view/task_tap/share_invoke 埋点;修正 ChatPage bonus_granted 类型。
偏差说明:<如有写在这里,无则写"无">