fix(overview): 锁定核心高价值套装并修复未查询空值误判

- 高价值套装精确匹配「套装-兔兔白日梦」与「套装-狂战萌兔」,排除头盔/背包/手榴弹散件干扰
- 增加 is_suit_queried_expr,未查询或 NULL 账号严禁误判为双套均可兑换
- 修复前后端序列化空值保持,完善列表与详情页准入状态和判定依据展示
- 新增头盔排除与 NULL 统计防错回归单测,全量通过 689 个用例
This commit is contained in:
yml2213
2026-10-07 15:27:30 +08:00
parent 5405d4b1d1
commit 44b762ea8c
11 changed files with 202 additions and 112 deletions
+6 -4
View File
@@ -36,8 +36,8 @@ LoginTask.cookie.isnot(None) & (LoginTask.cookie != "")
而列表接口序列化时:
```python
status=attempt_status or t.status # 显示的是「最近一次尝试」
message=(attempt_message if attempt_status else "") or t.message
status = attempt_status or t.status # 显示的是「最近一次尝试」
message = (attempt_message if attempt_status else "") or t.message
```
两者不是一回事。生产实测当时有 **752 行**处于「显示 failed、但 cookie 非空」的状态
@@ -46,8 +46,10 @@ message=(attempt_message if attempt_status else "") or t.message
新加的「清空失败」按**显示状态**筛:
```python
or_(and_(attempt_status != "", attempt_status == "failed"),
and_(attempt_status == "", status == "failed"))
or_(
and_(attempt_status != "", attempt_status == "failed"),
and_(attempt_status == "", status == "failed"),
)
```
于是这 752 行连同其它 success 行一起被选中删除。**这不是单纯的边界疏漏,是把两个
+17 -9
View File
@@ -131,13 +131,19 @@ cookie_updated_at: Mapped[datetime | None] = mapped_column(UTCDateTime, nullable
# ── CK 健康状态(原 login_tasks.ck_check_*)──
ck_check_status: Mapped[str] = mapped_column(String(16), default="", nullable=False)
ck_check_result: Mapped[dict | None] = mapped_column(JSON, nullable=True)
ck_checked_at: Mapped[datetime | None] = mapped_column(UTCDateTime, nullable=True, index=True)
ck_checked_at: Mapped[datetime | None] = mapped_column(
UTCDateTime, nullable=True, index=True
)
# ── 重登调度(原 login_tasks.relogin_*)──
relogin_attempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
relogin_next_at: Mapped[datetime | None] = mapped_column(UTCDateTime, nullable=True, index=True)
relogin_next_at: Mapped[datetime | None] = mapped_column(
UTCDateTime, nullable=True, index=True
)
relogin_last_error: Mapped[str] = mapped_column(String(512), default="", nullable=False)
relogin_failure_kind: Mapped[str] = mapped_column(String(16), default="", nullable=False)
relogin_failure_kind: Mapped[str] = mapped_column(
String(16), default="", nullable=False
)
```
`login_tasks` 的对应列**本次不动**(既不读写、也不 drop),留到下一次发版再清理 —— 这是回退后路,见 §6、§7。
@@ -366,12 +372,14 @@ db.query(Account)
**直接消失**(表现为「账号突然没有 CK」,且无从察觉是漏回填)。所以迁移里必须:
```python
expected = db.execute(text(
"SELECT COUNT(DISTINCT account_id) FROM login_tasks WHERE cookie IS NOT NULL AND cookie <> ''"
)).scalar()
actual = db.execute(text(
"SELECT COUNT(*) FROM accounts WHERE cookie IS NOT NULL AND cookie <> ''"
)).scalar()
expected = db.execute(
text(
"SELECT COUNT(DISTINCT account_id) FROM login_tasks WHERE cookie IS NOT NULL AND cookie <> ''"
)
).scalar()
actual = db.execute(
text("SELECT COUNT(*) FROM accounts WHERE cookie IS NOT NULL AND cookie <> ''")
).scalar()
if actual < expected:
raise RuntimeError(f"回填不完整:期望 {expected},实际 {actual},中止迁移")
```
+13 -5
View File
@@ -71,11 +71,19 @@ class AccountEvent(Base):
account_id: Mapped[int] = mapped_column(ForeignKey("accounts.id"), index=True)
event_type: Mapped[str] = mapped_column(String(32), index=True)
occurred_at: Mapped[datetime] = mapped_column(UTCDateTime, index=True)
title: Mapped[str] = mapped_column(String(256), default="") # 直接可展示
amount: Mapped[int | None] = mapped_column(Integer, nullable=True) # 充值额/消耗积分(有符号)
balance_after: Mapped[int | None] = mapped_column(Integer, nullable=True) # 变更后余额
detail: Mapped[dict | None] = mapped_column(JSON, nullable=True) # 精简后的细节(非原始大 JSON)
source: Mapped[str] = mapped_column(String(24), default="") # login/task/audit/manual
title: Mapped[str] = mapped_column(String(256), default="") # 直接可展示
amount: Mapped[int | None] = mapped_column(
Integer, nullable=True
) # 充值额/消耗积分(有符号)
balance_after: Mapped[int | None] = mapped_column(
Integer, nullable=True
) # 变更后余额
detail: Mapped[dict | None] = mapped_column(
JSON, nullable=True
) # 精简后的细节(非原始大 JSON)
source: Mapped[str] = mapped_column(
String(24), default=""
) # login/task/audit/manual
source_ref: Mapped[str] = mapped_column(String(96), unique=True) # 幂等键
created_at: Mapped[datetime] = mapped_column(UTCDateTime, default=_utcnow)
```