fix(overview): 锁定核心高价值套装并修复未查询空值误判
- 高价值套装精确匹配「套装-兔兔白日梦」与「套装-狂战萌兔」,排除头盔/背包/手榴弹散件干扰 - 增加 is_suit_queried_expr,未查询或 NULL 账号严禁误判为双套均可兑换 - 修复前后端序列化空值保持,完善列表与详情页准入状态和判定依据展示 - 新增头盔排除与 NULL 统计防错回归单测,全量通过 689 个用例
This commit is contained in:
@@ -62,6 +62,7 @@ from ..services.account_value import (
|
||||
SUIT_BAIRIMENG,
|
||||
SUIT_MENGTU,
|
||||
is_high_value_expr,
|
||||
is_suit_queried_expr,
|
||||
suit_owned_expr,
|
||||
suit_sort_rank_expr,
|
||||
)
|
||||
@@ -159,22 +160,23 @@ def _filter_accounts_query(
|
||||
# 高价值套装筛选
|
||||
hv = _query_str(high_value_filter)
|
||||
if hv:
|
||||
suit_queried = is_suit_queried_expr()
|
||||
bairimeng_expr = suit_owned_expr(Account.high_value_owned, SUIT_BAIRIMENG)
|
||||
mengtu_expr = suit_owned_expr(Account.high_value_owned, SUIT_MENGTU)
|
||||
if hv == "both_available":
|
||||
query = query.filter(
|
||||
Account.overview_queried_at.isnot(None),
|
||||
suit_queried,
|
||||
~bairimeng_expr,
|
||||
~mengtu_expr,
|
||||
)
|
||||
elif hv == "can_bairimeng":
|
||||
query = query.filter(
|
||||
Account.overview_queried_at.isnot(None),
|
||||
suit_queried,
|
||||
~bairimeng_expr,
|
||||
)
|
||||
elif hv == "can_mengtu":
|
||||
query = query.filter(
|
||||
Account.overview_queried_at.isnot(None),
|
||||
suit_queried,
|
||||
~mengtu_expr,
|
||||
)
|
||||
elif hv == "owned_bairimeng":
|
||||
@@ -576,7 +578,7 @@ def list_accounts(
|
||||
# 一键查询得到的「已兑换过的高价值套装」(不是整份快照 ——
|
||||
# 列表带不起 1~2KB/账号 的 JSON)。兑换条数不在这里:
|
||||
# 上面的 exchange_count 已经是**官方事件**聚合的结果。
|
||||
high_value_owned=acc.high_value_owned or [],
|
||||
high_value_owned=acc.high_value_owned,
|
||||
# 「有没有查过」靠它判断 —— 否则"查过但没兑换"和"没查过"都是空,分不开
|
||||
overview_queried_at=acc.overview_queried_at,
|
||||
)
|
||||
@@ -640,32 +642,16 @@ def accounts_summary(
|
||||
Account.ck_check_status.in_(["invalid", "error"]),
|
||||
)
|
||||
|
||||
suit_queried = is_suit_queried_expr()
|
||||
hv_row = visible.with_entities(
|
||||
func.count(
|
||||
func.nullif(
|
||||
Account.overview_queried_at.isnot(None)
|
||||
& ~bairimeng_expr
|
||||
& ~mengtu_expr,
|
||||
False,
|
||||
)
|
||||
),
|
||||
func.count(
|
||||
func.nullif(
|
||||
Account.overview_queried_at.isnot(None) & ~bairimeng_expr,
|
||||
False,
|
||||
)
|
||||
),
|
||||
func.count(
|
||||
func.nullif(
|
||||
Account.overview_queried_at.isnot(None) & ~mengtu_expr,
|
||||
False,
|
||||
)
|
||||
),
|
||||
func.count(func.nullif(Account.overview_queried_at.is_(None), False)),
|
||||
func.count(func.nullif(Account.points >= 300, False)),
|
||||
func.count(func.nullif(Account.gold_balance > 0, False)),
|
||||
func.count(func.nullif(Account.elite_opened.is_(True), False)),
|
||||
func.count(func.nullif(high_value_broken_expr, False)),
|
||||
func.count(case((and_(suit_queried, ~bairimeng_expr, ~mengtu_expr), 1))),
|
||||
func.count(case((and_(suit_queried, ~bairimeng_expr), 1))),
|
||||
func.count(case((and_(suit_queried, ~mengtu_expr), 1))),
|
||||
func.count(case((~suit_queried, 1))),
|
||||
func.count(case((Account.points >= 300, 1))),
|
||||
func.count(case((Account.gold_balance > 0, 1))),
|
||||
func.count(case((Account.elite_opened.is_(True), 1))),
|
||||
func.count(case((high_value_broken_expr, 1))),
|
||||
).first()
|
||||
high_value_summary = {
|
||||
"both_available": int(hv_row[0] or 0) if hv_row else 0,
|
||||
@@ -1392,7 +1378,7 @@ def account_overview(
|
||||
"points_expire_at": acc.points_expire_at,
|
||||
"overview_snapshot": acc.overview_snapshot,
|
||||
"overview_queried_at": acc.overview_queried_at,
|
||||
"high_value_owned": acc.high_value_owned or [],
|
||||
"high_value_owned": acc.high_value_owned,
|
||||
"elite_opened": acc.elite_opened,
|
||||
"elite_expire_time": acc.elite_expire_time,
|
||||
"esports_expire_time": acc.esports_expire_time,
|
||||
|
||||
Reference in New Issue
Block a user