Compare commits

..

2 Commits

Author SHA1 Message Date
linkeyu 5bec909dbd fix: 对齐领券成功率分母口径 (#101)
## 改动说明
- 对齐后端领券成功率新口径
- 卡片提示展示成功次数、发起数、中途退出数和实际分母
- 中途退出已有结果显示真实单券成功率
- 完全无逐券事件显示 0.0%(退出前无结果)
- 只有 skipped 时显示 0.0%(无有效结果),并保留可点击的逐券明细入口
- 完成/失败记录缺埋点仍显示 -,避免误报为 0%
- 点位成功率维持原有独立口径

## 验证
- TypeScript 类型检查通过
- 生产构建通过
- 已覆盖部分成功、无事件、只有 skipped、非退出埋点缺失组合

依赖后端:WonderableAI/shaguabijia-app-server#213,请同步发布。

---------

Co-authored-by: linkeyu <798648091@qq.com>
Reviewed-on: #101
Co-authored-by: linkeyu <linkeyu@wonderable.ai>
Co-committed-by: linkeyu <linkeyu@wonderable.ai>
2026-08-01 23:26:27 +08:00
linkeyu 5d2841fc85 修复:补齐比价记录状态枚举映射 (#100)
## 变更说明

- 补齐主状态 running 及历史业务状态的中文映射
- 补齐 source/ok/store_closed/no_delivery 等逐平台状态
- 修复 platform_results 实际为对象时无法渲染的问题
- 无 platform_results 时兼容回退 platforms 数组
- 增加进行中状态筛选

## 验证

- Next.js 生产构建:通过
- TypeScript:通过

---------

Co-authored-by: linkeyu <798648091@qq.com>
Reviewed-on: #100
Co-authored-by: linkeyu <linkeyu@wonderable.ai>
Co-committed-by: linkeyu <linkeyu@wonderable.ai>
2026-08-01 23:25:37 +08:00
3 changed files with 29 additions and 8 deletions
+21 -5
View File
@@ -76,6 +76,7 @@ interface CouponDataRow {
claimed_count: number | null;
point_success_count: number | null;
point_total_count: number | null;
point_event_count?: number;
// 兼容旧后端的内嵌明细;新后端不再返回,改为点击后按 trace 加载。
point_details?: CouponPointDetail[];
trace_url: string | null;
@@ -145,15 +146,22 @@ function PointScorePopover({ row }: { row: CouponDataRow }) {
const [loading, setLoading] = useState(false);
const [loadError, setLoadError] = useState<string | null>(null);
if (row.point_success_count == null || row.point_total_count == null || row.point_total_count <= 0) {
const abandonedWithoutScoredResult =
row.status === 'abandoned' && row.point_success_count === 0 && row.point_total_count === 0;
if (
row.point_success_count == null ||
row.point_total_count == null ||
(row.point_total_count <= 0 && !abandonedWithoutScoredResult)
) {
return <Typography.Text type="secondary">-</Typography.Text>;
}
const score = `${row.point_success_count}/${row.point_total_count}`;
const scoreWithRate = `${score}${(
row.point_success_count / row.point_total_count * 100
).toFixed(1)}%`;
const hasPointDetails = (row.point_event_count ?? 0) > 0;
const scoreWithRate = abandonedWithoutScoredResult
? `0.0%${hasPointDetails ? '无有效结果' : '退出前无结果'}`
: `${score}${(row.point_success_count / row.point_total_count * 100).toFixed(1)}%`;
const scoreColor =
row.point_success_count === row.point_total_count
row.point_total_count > 0 && row.point_success_count === row.point_total_count
? STATUS_TAG.completed.color
: STATUS_TAG.abandoned.color;
const loadDetails = async () => {
@@ -174,6 +182,14 @@ function PointScorePopover({ row }: { row: CouponDataRow }) {
}
};
if (abandonedWithoutScoredResult && !hasPointDetails) {
return (
<Tooltip title="本次在第一张券产生终态前中途退出,没有可计入成功/尝试的单券结果;按运营展示口径记为 0.0%,不虚构失败券数量。">
<Typography.Text type="warning">{scoreWithRate}</Typography.Text>
</Tooltip>
);
}
return (
<Popover
trigger="click"
+6 -3
View File
@@ -550,6 +550,7 @@ export default function DashboardPage() {
const couponPeriod = periodData?.coupon ?? null;
const previousCouponPeriod = previousPeriodData?.coupon ?? null;
const couponStartedDelta = percentDelta(couponPeriod?.started, previousCouponPeriod?.started);
const couponSuccessDenominator = couponPeriod?.success_denominator ?? null;
const couponSuccessRateValue =
couponPeriod?.success_rate == null ? '--' : (couponPeriod.success_rate * 100).toFixed(1);
const couponSuccessRateDelta = pointDelta(couponPeriod?.success_rate, previousCouponPeriod?.success_rate);
@@ -875,9 +876,11 @@ export default function DashboardPage() {
unit="%"
delta={couponSuccessRateDelta.value}
deltaTone={couponSuccessRateDelta.tone}
hint={`全部点位领成功的次数 / 领券发起数;本期 ${fmtInt(couponPeriod?.all_success)} / ${fmtInt(
couponPeriod?.started,
)} 次。点位成功口径含「今日已领过」。`}
hint={`全部点位领成功的次数 ÷(领券发起数-中途退出数);本期 ${fmtInt(
couponPeriod?.all_success,
)} ÷(${fmtInt(couponPeriod?.started)}${fmtInt(couponPeriod?.abandoned)}),分母为 ${fmtInt(
couponSuccessDenominator,
)}。中途退出不计入分母,点位成功口径含「今日已领过」。`}
/>
<StatCard
title="点位成功率"
+2
View File
@@ -707,6 +707,8 @@ export interface DashboardOverview {
// 点位=一张券;成功口径 success+already_claimed;点位成功率分母=发起数×应领点位数(未跑到视为失败)。
coupon?: {
started: number;
abandoned: number;
success_denominator: number;
all_success: number; // 全部点位领成功的完成场次数
success_rate: number | null;
point_success: number;