fix: 领券成功率剔除中途退出 #213

Merged
guke merged 4 commits from codex/coupon-rate-exclude-abandoned into main 2026-08-01 23:24:56 +08:00
Member

改动说明

  • 领券成功率分母改为:发起数 - 中途退出数
  • failed 与 started 仍保留在分母
  • 接口新增 abandoned 和 success_denominator 字段
  • 中途退出已有单券结果时返回真实成功/尝试数
  • 中途退出且没有逐券终态时明确返回 0/0
  • 新增 point_event_count,区分「只有 skipped、无有效计分结果」和「完全无逐券事件」
  • 用户领券记录接口同步聚合逐券结果

验证

  • 相关后端测试:21 passed
  • Ruff:通过
  • 线上数据库只读核对:空白记录确实没有 coupon_claim_event

配套前端:WonderableAI/shaguabijia-admin-web#101

## 改动说明 - 领券成功率分母改为:发起数 - 中途退出数 - failed 与 started 仍保留在分母 - 接口新增 abandoned 和 success_denominator 字段 - 中途退出已有单券结果时返回真实成功/尝试数 - 中途退出且没有逐券终态时明确返回 0/0 - 新增 point_event_count,区分「只有 skipped、无有效计分结果」和「完全无逐券事件」 - 用户领券记录接口同步聚合逐券结果 ## 验证 - 相关后端测试:21 passed - Ruff:通过 - 线上数据库只读核对:空白记录确实没有 coupon_claim_event 配套前端:WonderableAI/shaguabijia-admin-web#101
linkeyu added 1 commit 2026-08-01 13:57:01 +08:00
Member

🤖 review-pr 深审结论

🟡 需作者/产品确认 · 0.8 — 领券成功率分母改为「发起数 − 中途退出数」,改动本身正确、有测试、无回归;但大盘总览领券数据页两处「领券成功率」的分母口径现在不一致,值得确认是否有意为之。

需确认

  • 🟡 跨页口径不一致 app/admin/repositories/stats.py:569 vs app/admin/repositories/coupon_data.py:128,150
    • 本 PR 让大盘总览 coupon.success_rate 分母 = started − abandoned剔除中途退出)。
    • 领券数据页 coupon_data._success_rates.full_success_rate 分母仍 = started abandoned;该函数注释明确写「基数含全部 session…与发起数同基数」,用于流失分析)。
    • 结果:admin 两个页面对「领券成功率」给出不同分母的数字。若刻意(总览看真实成功率、数据页看流失基数)则 OK;否则建议 coupon_data 也同口径剔除 abandoned。

构建 / 测试(已实跑,诚实)

  • 合并最新 main(PR head 已含最新 main)后隔离 worktree 实跑:
    • tests/test_admin_read.py(含新增用例)→ 25 passed
    • tests/test_coupon_platform_success.py(coupon_data 侧回归)→ 13 passed,无回归。

做得对

  • 分母零值边界 if coupon_success_denominator else None,避免除零;abandoned ⊆ started 保证分母非负。
  • 加性 schema 字段(abandoned / success_denominator 带默认值),向后兼容;配套前端 admin-web#101。
  • 新用例覆盖正常(2/3)+ 纯 abandoned 零分母(rate=None)两条路径。

建议

  • 确认 coupon_data 页 full_success_rate 是否也应剔除 abandoned(跨页一致);若维持不同口径,建议在两处 UI / 字段注释里点明差异,免得对数对不上。

🤖 自动深审 · 全程只读 · 合并 base 后隔离 worktree 实跑 pytest

## 🤖 review-pr 深审结论 🟡 **需作者/产品确认 · 0.8** — 领券成功率分母改为「发起数 − 中途退出数」,改动本身正确、有测试、无回归;但**大盘总览**与**领券数据页**两处「领券成功率」的分母口径现在不一致,值得确认是否有意为之。 ### 需确认 - 🟡 **跨页口径不一致** `app/admin/repositories/stats.py:569` vs `app/admin/repositories/coupon_data.py:128,150`: - 本 PR 让**大盘总览** `coupon.success_rate` 分母 = `started − abandoned`(**剔除**中途退出)。 - 但**领券数据页** `coupon_data._success_rates.full_success_rate` 分母仍 = `started`(**含** abandoned;该函数注释明确写「基数含全部 session…与发起数同基数」,用于流失分析)。 - 结果:admin 两个页面对「领券成功率」给出**不同分母**的数字。若刻意(总览看真实成功率、数据页看流失基数)则 OK;否则建议 coupon_data 也同口径剔除 abandoned。 ### 构建 / 测试(已实跑,诚实) - 合并最新 `main`(PR head 已含最新 main)后隔离 worktree 实跑: - `tests/test_admin_read.py`(含新增用例)→ **25 passed** - `tests/test_coupon_platform_success.py`(coupon_data 侧回归)→ **13 passed**,无回归。 ### 做得对 - 分母零值边界 `if coupon_success_denominator else None`,避免除零;`abandoned ⊆ started` 保证分母非负。 - 加性 schema 字段(`abandoned` / `success_denominator` 带默认值),向后兼容;配套前端 admin-web#101。 - 新用例覆盖正常(2/3)+ 纯 abandoned 零分母(rate=None)两条路径。 ### 建议 - 确认 coupon_data 页 `full_success_rate` 是否也应剔除 abandoned(跨页一致);若维持不同口径,建议在两处 UI / 字段注释里点明差异,免得对数对不上。 <sub>🤖 自动深审 · 全程只读 · 合并 base 后隔离 worktree 实跑 pytest</sub>
linkeyu added 1 commit 2026-08-01 14:06:57 +08:00
linkeyu added 1 commit 2026-08-01 14:12:39 +08:00
Member

🤖 review-pr 深审结论(增量复审 @8fc2cd41)

🟢 可合并 · 0.85(上轮 🟡 的跨页口径疑点已确认系有意为之)— 本次新提交「区分跳过事件与无逐券结果」是对逐券展示的正交精修,加性、有测试、无回归。

本次增量(37fba3c → 8fc2cd41,coupon_data 3 文件 +148/-15)

  • coupon_data._point_scores_by_trace:改为一次聚合出 succeeded(_SLOT_OK) / tried(_SLOT_TRIED) / events(全部),去掉原 WHERE status IN _SLOT_TRIED 过滤 → 只有 skipped 事件的 trace 也能返回(tried=0, events>0),从而区分「跳过」与「无事件」。
  • _session_to_row:逐券计数语义更清晰 —— point_stats 有则用实际值;abandoned 且无事件 → 0/0(退出前无结果);其它无事件 → None;新增 point_event_count
  • coupon_user_records:抽屉行也补算 point_stats(原来传 None,现能显示逐券分)。
  • schema CouponDataRow + point_event_count: int = 0(加性、带默认)。

关键核对:不影响聚合口径

  • 逐券 _point_scores_by_trace 只喂逐行展示;聚合 full_success_rate / point_success_rate 走的是会话级 platform_success 独立算法(_success_rates),不受本次重构影响(已核对 + 43 passed)。故上轮我提的「大盘剔除 abandoned vs coupon_data 含 abandoned」依旧存在,但经 admin-web#101 body 明确「点位成功率维持原有独立口径」+ 本次改动方向,确认系有意的双口径(大盘看真实成功率、数据页看流失基数),非缺陷。

构建 / 测试(已实跑,诚实)

  • 合并最新 main 后隔离 worktree 实跑:test_coupon_point_score + test_coupon_platform_success + test_admin_read43 passed,无回归。

建议

  • 无阻塞。若两处口径差异易让运营对不上数,可选在 UI 注一句「数据页含中途退出、大盘不含」。配套前端 admin-web#101。

🤖 自动深审 · 增量复审 · 合并 base 后隔离 worktree 实跑 pytest

## 🤖 review-pr 深审结论(增量复审 @8fc2cd41) 🟢 **可合并 · 0.85**(上轮 🟡 的跨页口径疑点已确认**系有意为之**)— 本次新提交「区分跳过事件与无逐券结果」是对逐券展示的**正交精修**,加性、有测试、无回归。 ### 本次增量(`37fba3c → 8fc2cd41`,coupon_data 3 文件 +148/-15) - `coupon_data._point_scores_by_trace`:改为一次聚合出 `succeeded`(_SLOT_OK) / `tried`(_SLOT_TRIED) / `events`(全部),去掉原 `WHERE status IN _SLOT_TRIED` 过滤 → **只有 skipped 事件的 trace 也能返回**(tried=0, events>0),从而区分「跳过」与「无事件」。 - `_session_to_row`:逐券计数语义更清晰 —— `point_stats` 有则用实际值;`abandoned` 且无事件 → **0/0**(退出前无结果);其它无事件 → `None`;新增 `point_event_count`。 - `coupon_user_records`:抽屉行也补算 `point_stats`(原来传 None,现能显示逐券分)。 - schema `CouponDataRow` + `point_event_count: int = 0`(加性、带默认)。 ### 关键核对:不影响聚合口径 - 逐券 `_point_scores_by_trace` 只喂**逐行**展示;聚合 `full_success_rate` / `point_success_rate` 走的是**会话级** `platform_success` 独立算法(`_success_rates`),**不受本次重构影响**(已核对 + 43 passed)。故上轮我提的「大盘剔除 abandoned vs coupon_data 含 abandoned」**依旧存在,但经 admin-web#101 body 明确「点位成功率维持原有独立口径」+ 本次改动方向,确认系有意的双口径**(大盘看真实成功率、数据页看流失基数),非缺陷。 ### 构建 / 测试(已实跑,诚实) - 合并最新 `main` 后隔离 worktree 实跑:`test_coupon_point_score` + `test_coupon_platform_success` + `test_admin_read` → **43 passed**,无回归。 ### 建议 - 无阻塞。若两处口径差异易让运营对不上数,可选在 UI 注一句「数据页含中途退出、大盘不含」。配套前端 admin-web#101。 <sub>🤖 自动深审 · 增量复审 · 合并 base 后隔离 worktree 实跑 pytest</sub>
guke added 1 commit 2026-08-01 23:06:54 +08:00
guke merged commit 67ac2dcbbb into main 2026-08-01 23:24:56 +08:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: WonderableAI/shaguabijia-app-server#213