f984b07a0c
- coupon_session.platform_success:整单成功率②/点位成功率③ + 分平台点位成功率 - coupon_claim_record.app_env:按 coupon_id「按券成功率」聚合 + 子端点 /admin/api/coupon-data/coupons - /step 复用 record_claims 同一 SessionLocal 写入(fire-and-forget),pricebot 转发/负载不变 - 迁移 coupon_session_platform_success + coupon_claim_app_env;新增 14 测试 - 设计见 docs/guides/领券成功率指标-设计与埋点.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
113 lines
5.3 KiB
Python
113 lines
5.3 KiB
Python
"""admin「领券数据」看板 schemas:汇总卡 + 按天/小时趋势 + 逐条领券明细。
|
||
|
||
数据源 coupon_session(一次领券一行)。耗时单位 ms(前端按需折秒);均值/分位只统计 completed。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class CouponDataSummary(BaseModel):
|
||
"""汇总卡:发起/完成数 + 耗时均值与分位(P5/P50/P95/P99,基于 completed 的 elapsed_ms)。"""
|
||
|
||
started_count: int = Field(..., description="发起数(区间内所有领券 session)")
|
||
completed_count: int = Field(..., description="完成数(status=completed)")
|
||
avg_elapsed_ms: int | None = Field(None, description="平均耗时(ms,仅 completed;无数据为空)")
|
||
p5_ms: int | None = Field(None, description="耗时 5 分位(ms)")
|
||
p50_ms: int | None = Field(None, description="耗时 50 分位(ms,中位数)")
|
||
p95_ms: int | None = Field(None, description="耗时 95 分位(ms)")
|
||
p99_ms: int | None = Field(None, description="耗时 99 分位(ms)")
|
||
# 平台粒度成功率(见 docs/guides/领券成功率指标-设计与埋点.md):基数含全部 session。
|
||
full_success_count: int = Field(0, description="整单成功数(勾选平台全部领到的 session 数)")
|
||
full_success_rate: float | None = Field(None, description="整单成功率②=整单成功数/发起数;无数据为空")
|
||
point_success_count: int = Field(0, description="成功平台点位数(Σ 每次成功的平台数)")
|
||
point_total_count: int = Field(0, description="总平台点位数(Σ 每次勾选平台数;空勾选=全领三档)")
|
||
point_success_rate: float | None = Field(None, description="点位成功率③=成功点位/总点位;无数据为空")
|
||
per_platform: dict[str, float | None] = Field(
|
||
default_factory=dict,
|
||
description="分平台点位成功率 {平台id: rate|None};恒含美团/淘宝/京东三档,区间内无人勾选的平台为 None",
|
||
)
|
||
|
||
|
||
class CouponDataDaily(BaseModel):
|
||
"""按天趋势(全量,不受分页影响):柱=发起/完成数,线=平均耗时。"""
|
||
|
||
date: str = Field(..., description="北京时间 YYYY-MM-DD")
|
||
started_count: int
|
||
completed_count: int
|
||
avg_elapsed_ms: int | None = Field(None, description="当天平均耗时(ms,仅 completed)")
|
||
|
||
|
||
class CouponDataHourly(BaseModel):
|
||
"""按北京小时(0–23)趋势(单日 granularity=hour 时非空)。"""
|
||
|
||
hour: int = Field(..., description="北京时间小时 0–23")
|
||
started_count: int
|
||
completed_count: int
|
||
avg_elapsed_ms: int | None = None
|
||
|
||
|
||
class CouponDataRow(BaseModel):
|
||
"""一条领券明细(一次领券任务)。"""
|
||
|
||
id: int = Field(..., description="coupon_session 主键(抽屉 rowKey 用)")
|
||
trace_id: str
|
||
user_id: int | None = None
|
||
user_phone: str | None = Field(None, description="手机号(admin 展示;匿名领券/查不到为空)")
|
||
user_nickname: str | None = Field(None, description="昵称")
|
||
status: str = Field(..., description="started / completed / failed / abandoned")
|
||
platforms: list[str] | None = Field(None, description="发起勾选平台")
|
||
origin_package: str | None = Field(None, description="发起来源 App 包名;null=App 内(傻瓜比价首页)发起")
|
||
elapsed_ms: int | None = Field(None, description="全程耗时(ms)")
|
||
platform_elapsed: dict[str, int] | None = Field(
|
||
None, description="各平台耗时 {meituan-waimai/taobao-shanguang/jd-waimai: ms}"
|
||
)
|
||
device_model: str | None = None
|
||
rom: str | None = None
|
||
app_env: str | None = None
|
||
started_at: datetime = Field(..., description="发起时刻(明细「时间」列)")
|
||
claimed_count: int | None = None
|
||
trace_url: str | None = Field(None, description="pricebot 公网 trace 链接(仅 completed 有);admin 渲染可点链接,无则显示可复制 trace_id")
|
||
|
||
|
||
class CouponDataOut(BaseModel):
|
||
"""领券数据看板响应:汇总卡 + 趋势 + 明细分页。"""
|
||
|
||
date_from: str
|
||
date_to: str
|
||
summary: CouponDataSummary
|
||
daily: list[CouponDataDaily] = Field(default_factory=list, description="按天趋势(全量)")
|
||
hourly: list[CouponDataHourly] = Field(
|
||
default_factory=list, description="按小时趋势(单日 hour 粒度时非空)"
|
||
)
|
||
total: int = Field(..., description="明细总条数(全量,不受分页)")
|
||
items: list[CouponDataRow] = Field(..., description="逐条领券明细(当前页)")
|
||
|
||
|
||
class CouponUserRecordsOut(BaseModel):
|
||
"""某用户全部领券记录(点手机号抽屉用):total=该用户领券总次数,items=记录列表(UserRecordsDrawer 渲染)。"""
|
||
|
||
items: list[CouponDataRow]
|
||
total: int
|
||
|
||
|
||
class CouponSlotRow(BaseModel):
|
||
"""按券成功率一行(§13):粒度=设备-天;成功率=成功/(成功+失败),skipped 排除。"""
|
||
|
||
coupon_id: str
|
||
coupon_name: str | None = None
|
||
platform: str | None = Field(None, description="美团/淘宝/京东 平台 id;无法识别为空")
|
||
tried: int = Field(..., description="尝试数(success+already_claimed+failed 的设备-天数)")
|
||
succeeded: int = Field(..., description="成功数(success+already_claimed)")
|
||
success_rate: float | None = Field(None, description="成功率=成功/尝试")
|
||
|
||
|
||
class CouponSlotsOut(BaseModel):
|
||
"""按券成功率表响应(§13)。"""
|
||
|
||
date_from: str
|
||
date_to: str
|
||
items: list[CouponSlotRow]
|