fix(overview): 锁定核心高价值套装并修复未查询空值误判
- 高价值套装精确匹配「套装-兔兔白日梦」与「套装-狂战萌兔」,排除头盔/背包/手榴弹散件干扰 - 增加 is_suit_queried_expr,未查询或 NULL 账号严禁误判为双套均可兑换 - 修复前后端序列化空值保持,完善列表与详情页准入状态和判定依据展示 - 新增头盔排除与 NULL 统计防错回归单测,全量通过 689 个用例
This commit is contained in:
@@ -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
@@ -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
@@ -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)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user