fix(comparison): best 兜底候选含源,源最便宜时回落源避免负省额

无 is_best 兜底原本排除源、强选最低目标;当源本身最便宜时(全平台
dish-diff 相似替换),会误选更贵目标 → saved 变负,倒扣 get_stats 的
「累计发现可省」(该聚合按 status=success 求和、不带 >0 过滤)。

改为在含源的有价行里取最低价,与老派生函数 _derive「全目标缺菜回落源、
不虚报省」同一语义:源最便宜 → best=源、saved=0、is_source_best=True。
补回归测试:源最便宜场景(旧逻辑 best 误选更贵目标、saved 为负)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
guke
2026-07-30 10:52:33 +08:00
parent c61eab610b
commit e6dcad7c7d
2 changed files with 43 additions and 9 deletions
+9 -9
View File
@@ -376,15 +376,15 @@ def _derive_from_platforms(
src = next((p for p in rows if p.get("role") == "source"), None)
best = next((p for p in rows if p.get("is_best")), None)
if best is None:
# pricebot 没标 is_best(如全平台 has_dish_diff「相似替换/价格仅供参考」→ 不认定权威
# 最低价)但仍有有价目标 → 兜底取有价目标里最低价当参考 best,避免记录级 best_*/saved
# 整条落 NULL(否则首页价 0.00 / 记录页无最低红框 / 省额丢失 /「累计发现可省」漏计)。
priced_targets = [
p for p in rows
if p.get("role") != "source" and p.get("price") is not None
]
if priced_targets:
best = min(priced_targets, key=lambda p: (p["price"], p.get("display_order") or 0))
# pricebot 没标 is_best(如全平台 has_dish_diff「相似替换/仅供参考」→ 不认定权威最低价)
# 但仍有有价 → 兜底取有价里最低价当参考 best,避免记录级 best_*/saved 整条落 NULL
# (否则首页价 0.00 / 记录页无最低红框 / 省额丢失 /「累计发现可省」漏计)。**候选含源**:
# 源常年全菜、价可信,源本身最便宜时 best 回落源(saved=0、is_source_best=True),与 _derive
# 「全目标缺菜回落源、不虚报省」同一语义;若排除源强选更贵目标,saved 会变负、倒扣「累计
# 发现可省」(get_stats 对 saved_amount_cents 求和不带 >0 过滤)。
priced = [p for p in rows if p.get("price") is not None]
if priced:
best = min(priced, key=lambda p: (p["price"], p.get("display_order") or 0))
source_price_cents = _yuan_to_cents(src.get("price")) if src else None
best_price_cents = _yuan_to_cents(best.get("price")) if best else None
saved_amount_cents = None
+34
View File
@@ -175,6 +175,40 @@ def test_harvest_done_platforms_no_isbest_falls_back_to_cheapest_target(client)
assert rec.is_source_best is False # 兜底选的是目标,非源
def _done_params_no_isbest_source_cheapest() -> dict:
"""无 is_best 且源本身最便宜:源淘宝闪购 50.0 < 全部 dish-diff 目标(美团 57.8 / 京东 74.9)。
此时不能强选更贵的目标当 best(否则 saved 变负、污染「累计发现可省」),应回落源。"""
return {
"platforms": [
{"role": "source", "platform_id": "eleme", "platform_name": "淘宝闪购",
"package": "me.ele", "price": 50.0, "is_best": False, "has_dish_diff": False,
"store_name": "窑鸡王", "items": [{"name": "招牌窑鸡 整只-香辣", "qty": 1}]},
{"role": "target", "platform_id": "meituan_waimai", "platform_name": "美团外卖",
"package": "com.sankuai.meituan.takeoutnew", "price": 57.8, "is_best": False,
"has_dish_diff": True},
{"role": "target", "platform_id": "jd_waimai_standalone", "platform_name": "京东外卖",
"package": "com.jd.waimai", "price": 74.9, "is_best": False, "has_dish_diff": True},
],
"information": "源平台已是最低(其余为相似替换)",
}
def test_harvest_done_no_isbest_source_cheapest_falls_back_to_source(client) -> None:
"""回归:无 is_best 且源最便宜 → best 回落源(与 _derive「全目标缺菜回落源、不虚报省」同语义),
saved=0、is_source_best=True,绝不因强选更贵目标而让 saved 变负、倒扣「累计发现可省」。"""
tid = _tid()
with SessionLocal() as db:
crud.harvest_running(db, trace_id=tid, user_id=None)
rec, _ = crud.harvest_done(db, trace_id=tid, user_id=None,
done_params=_done_params_no_isbest_source_cheapest())
assert rec.status == "success"
assert rec.best_platform_id == "eleme" # 回落到源(源最便宜)
assert rec.best_price_cents == 5000 # 源 50.0
assert rec.source_price_cents == 5000
assert rec.saved_amount_cents == 0 # 没省到,绝不为负
assert rec.is_source_best is True # 源就是最便宜
def test_harvest_abort_cancels_running(client) -> None:
tid = _tid()
with SessionLocal() as db: