Files
shaguabijia-admin-web/docs/superpowers/specs/2026-07-11-coupon-point-rate-as-coupon-mean-design.md
T
2026-07-11 18:45:44 +08:00

4.7 KiB
Raw Blame History

点位成功率改为「按券成功率的算术平均」设计

  • 日期2026-07-11
  • 范围:纯前端 shaguabijia-admin-web后端零改动、零 schema 改动、零迁移
  • 涉及文件src/app/(main)/coupon-data/page.tsx(单文件)

目标

「领券数据」看板汇总卡里的 分平台点位成功率(美团/淘宝/京东)与 点位成功率(合计),改为 该平台(/全部)几张券成功率的算术平均,让平台数字与下方「按券明细」自洽,便于快速揪出坏券。

背景

现状:分平台 / 合计点位成功率来自后端 _success_rates会话口径,数据源 coupon_session.platform_success), 与页面下方「按券成功率」(数据源 coupon_claim_record)口径不同,导致同一平台的平台数(如淘宝 71.4%) 与其券明细(100%/80%/80%)对不上,甚至出现「合计低于每个平台」的反直觉现象。

关键点:本页已经把全量按券数据拉到前端——couponSlots(来自 /admin/api/coupon-data/coupons 后端 coupon_slot_report 不分页、返回全量),每行含 platformsuccess_rate。因此直接在前端 对其求算术平均即可,无需后端改动

口径(改动后)

  • 分平台点位成功率 = 该平台所有券 success_rate 的算术平均(每张券等权;success_rate 为空的券跳过;无券 → 显示 -)。
  • 点位成功率(合计) = couponSlots 全部券 success_rate 的算术平均。
  • 每张券成功率(按券明细,不变):成功(success + already_claimed) ÷ 尝试(+ failed)skipped 排除(后端 coupon_slot_report)。

例:淘宝三张券 100% / 80% / 80% → 淘宝点位 = (100+80+80)/3 = 86.7%

不改(其他保持不变)

  • 后端coupon_slot_report / _success_rates / CouponDataSummary schema 全不动。 summary.per_platformsummary.point_success_rate 仍会返回(会话口径),只是前端这两处不再引用它们(留着不删,接口稳定)。
  • 整单成功率:会话口径不变。
  • 按券明细表:排序、列等不动。
  • 筛选联动:整单 / 发起 / 完成 / 耗时仍随「用户 / 状态 / 日期 / 环境」全部筛选; 点位随 couponSlots 走(= 日期 + 环境,不随「用户 / 状态」搜索变——与按券表现状一致)。

实现(单文件 page.tsx

1. 加一个求均值 helper(放 fmtPct 附近)

// 一组按券行的 success_rate 算术平均;无有效券 → null(显示 -)
const slotRateMean = (rows: CouponSlotRow[]): number | null => {
  const rates = rows
    .map((r) => r.success_rate)
    .filter((v): v is number => v != null);
  return rates.length ? rates.reduce((a, b) => a + b, 0) / rates.length : null;
};

2. 合计:改 value

value={fmtPct(summary.point_success_rate)}

改成

value={fmtPct(slotRateMean(couponSlots))}

3. 分平台卡:改 rate 来源

把每个平台卡里的

const rate = summary.per_platform?.[pid];

改成

const rate = slotRateMean(couponSlots.filter((r) => r.platform === pid));

4. 更新口径 tooltip 文案

POINT_RATE_HINT 改成(去掉旧的会话口径描述):

const POINT_RATE_HINT =
  '点位成功率(合计):当前区间内全部券成功率的算术平均(每张券等权);' +
  '单券成功率=成功(含已领)÷尝试(不含跳过),源自领券每券记录。';

分平台卡标题(${SLOT_PLATFORM[pid]}点位成功率)如需补口径,注明「该平台各券成功率的算术平均」。

边角 / 注意

  • 精度success_rate 后端已 round 到 4 位,前端再平均,展示只到 1 位小数(fmtPct),误差可忽略。
  • 依赖按券请求:点位现在依赖 couponSlots(独立请求,失败时 setCouponSlots([]))。若该请求失败,点位卡显示 -(优雅降级)。此前它来自主 summary 请求。
  • platform=null 的券:coupon_id 前缀无法识别的券(实践中 pricebot 都是 mt_/tb_/ele_/elm_/jd_,基本不出现)会计入 合计、但不在任何分平台卡里。可接受。

验证

  • 手动:选一天,展开某平台「查看明细」,核对该平台卡数字 = 明细表各券成功率的算术平均;合计 = 全部券的算术平均。
  • npx tsc --noEmit 通过;npm run lint 无新增 error。

不做(YAGNI

  • 最小尝试数阈值 / 低量券滤噪 / 坏券标红。
  • 按券表默认升序(坏券置顶)。
  • 「数据大盘」overview 的领券点位成功率(另一页、另一口径)。
  • 删除后端已不再被前端使用的 per_platform / point_success_rate 字段。