docs: 修正 spec 成功率口径 + 派生逻辑落为共享模块

- 两页成功率分母其实都是 total-cancelled(概览 queries.py:532、大盘 stats.py:290),
  一致、本次不动;真正的既有差异在分子(概览含 below_minimum、大盘不含),
  本次统一为 original∈S,顺带对齐两页口径
- 派生逻辑落为独立模块 comparison_outcome.py,供 queries/stats 共用避免循环 import

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
guke
2026-08-04 15:13:17 +08:00
parent a0cd4ff4e9
commit 28820fe995
@@ -3,7 +3,7 @@
- **日期**2026-08-04
- **范围**:后端 `shaguabijia-app-server`admin 层,**方案 A**:无 schema 变更、无迁移、不碰 #209 落库与 C 端)+ 前端 `shaguabijia-admin-web`
- **涉及文件**
- 后端:`app/admin/repositories/queries.py`(列表派生 + 概览口径)、`app/admin/schemas/comparison.py`(新增两字段)、`app/admin/repositories/stats.py`(大盘成功率分子口径)
- 后端:`app/admin/repositories/comparison_outcome.py`**新建**:共享常量 + Python 派生 + SQL 判定)、`app/admin/repositories/queries.py`(列表派生 + 概览口径)、`app/admin/schemas/comparison.py`(新增两字段)、`app/admin/repositories/stats.py`(大盘成功率分子口径)
- 前端:`src/app/(main)/comparison-records/page.tsx``src/lib/types.ts`
- 文档:`shaguabijia-app-server/docs/guides/比价结果卡片-状态口径与交互参考.md`(补 admin 口径说明)
@@ -56,29 +56,46 @@ outcome_hint: str | None = None # 缺失提示文案;None = 无缺失
## 后端实现(方案 A
### 1. 常量 + 派生(`queries.py`,紧邻既有 `_COMPARISON_STATUS_ALIASES`
### 1. 新模块 `app/admin/repositories/comparison_outcome.py`(供 `queries.py` 与 `stats.py` 共用,避免循环 import
```python
_ADMIN_SUCCESS_OUTCOMES = frozenset({
from sqlalchemy import func
from app.models.comparison import ComparisonRecord
ADMIN_SUCCESS_OUTCOMES = frozenset({
"success", "below_minimum", "store_closed",
"store_not_found", "items_not_found", "no_delivery", "unsupported",
})
_OUTCOME_HINTS = {
OUTCOME_HINTS = {
"below_minimum": "未满起送", "store_closed": "门店打烊",
"store_not_found": "未找到店", "items_not_found": "未找到菜",
"no_delivery": "单点不配送", "unsupported": "平台·场景不支持",
}
def _derive_admin_outcome(raw_payload: dict | None, status: str) -> tuple[str, str | None]:
def derive_admin_outcome(raw_payload: dict | None, status: str) -> tuple[str, str | None]:
"""Python 层派生(列表用;raw_payload 已随 ORM 加载,零额外查询)。"""
if status in ("cancelled", "running"):
return status, None
raw = raw_payload or {}
original = raw.get("record_status") or raw.get("status")
if original is None: # 极老记录兜底
return ("success" if status == "success" else "failed"), None
if original in _ADMIN_SUCCESS_OUTCOMES:
return "success", _OUTCOME_HINTS.get(original)
if original in ADMIN_SUCCESS_OUTCOMES:
return "success", OUTCOME_HINTS.get(original)
return "failed", None
def _original_expr():
return func.coalesce(
ComparisonRecord.raw_payload["record_status"].as_string(),
ComparisonRecord.raw_payload["status"].as_string(),
)
def admin_success_sql():
"""SQL 层 admin 成功判定(概览/大盘的 case/where 共用)。"""
original = _original_expr()
return original.in_(tuple(ADMIN_SUCCESS_OUTCOMES)) | (
original.is_(None) & (ComparisonRecord.status == "success")
)
```
### 2. 列表 `list_comparison_records`
@@ -101,17 +118,17 @@ _original_expr = func.coalesce(
ComparisonRecord.raw_payload["status"].as_string(),
)
# admin 成功 = original ∈ S OR (original IS NULL AND status == 'success')
# completed = admin 成功 + 纯 failedadmin 口径失败
# success_rate = admin 成功 / completed
# completed = admin 成功 + 纯 failed展示字段;admin 口径)
# success_rate = admin 成功 / (started - cancelled) # 分母不变,见 queries.py:532
```
**耗时分位**avg / p50 / p95`_comparison_duration_aggregate*`):统计集从「`status == "success"`」改为「admin 成功集」——用户已确认**统一口径纳入**这 6 类。改动点:`_comparison_status_condition` 系的耗时过滤条件改用 `_original_expr ∈ S`(或复用一个 `_admin_success_condition()` 表达式,列表/概览/大盘共用)。
### 4. 大盘 `stats.py` `dashboard_overview`
`period_comparison_success`分子[stats.py:288](../../../shaguabijia-app-server/app/admin/repositories/stats.py) 的聚合 case)改用 `_original_expr ∈ S` 口径,与概览一致。
`period_comparison_stats`success 分子[stats.py:278-281](../../../shaguabijia-app-server/app/admin/repositories/stats.py) 的 `case(status=='success')`)现在**不含 below_minimum**#209 只改了概览、没同步大盘)。改用 `admin_success_sql()``original ∈ S`口径,与概览一致。
**范围界定(重要)**大盘成功率分母是 `total - cancelled`[stats.py:290](../../../shaguabijia-app-server/app/admin/repositories/stats.py)),与比价记录页概览的 `completed`success+failed)分母**本就不同**running 的处理差异)。**此既有差异不在本次范围**——本次只统一「6 类算成功」这个**分子**口径;两页分母口径保持现状,避免波及大盘既有数字
**范围界定**两页成功率**分母`total - cancelled`**(概览 [queries.py:532](../../src/../../../shaguabijia-app-server/app/admin/repositories/queries.py)、大盘 [stats.py:290](../../../shaguabijia-app-server/app/admin/repositories/stats.py)),口径一致、本次不动。真正的既有差异在**分子**:概览 success 含 `below_minimum`、大盘不含。本次把两处分子都统一为 `original ∈ S`,改完两页口径**完全一致**。副作用:大盘成功率因补上 `below_minimum` + 5 类而上升,概览因补上 5 类上升——均属口径调整、非数据异常
## 前端实现