feat(overview): 关掉和平小店查询 + 修电竞「已开通」不落库

## ① 和平小店总开关(默认关)

小店目前是**关闭状态**,但不跳过的话一键查询仍会对每个账号发小店请求,
结果 100% 失败 —— 实测 200 个账号里 `exchange_records_peace` 失败 **195 次(100%)**:

    Livelink登录态校验失败

100% 意味着不是账号问题。这既淹掉真错误、又让人以为出故障。

新增 `douyu_config.peace_enabled`(迁移 `0050`,**默认 False**)。
关着时**完全跳过**小店的所有请求(绑定 + 兑换记录),并且**不记 errors** ——
「配置成关的」和「查失败」是两回事,混在一起会让 errors 失去意义。

将来小店恢复,在后台打开这个开关即可;打开后小店失败仍会**如实暴露**
(有用例专门钉住这一点,别把开关当成遮羞布)。

## ② 修:电竞手册「已开通」不会被一键查询更新

用户问"电竞到期时间为什么拿不到",查下去发现**两层**:

**第一层不是 bug**:那 200 个账号 `esports_opened` 全是 0(没开通电竞手册),
所以 `expireTime="0"`、没有到期时间可存。注意
**"电竞已绑定角色"≠"电竞手册已开通"**。实测 `opened_but_no_expire = 0`,没有异常。

**第二层是真 bug(不对称)**:

    _refresh_elite_handbook   → 写 account.elite_opened  ✅
    _refresh_esports_handbook → 不写 account.esports_opened ❌

`esports_opened` 只在"开通"任务里被设置,所以**详情页电竞的「已开通/未开通」
根本不会被一键查询更新**。已改成与精英对称(口径同 create_esports_qr:
`manual_type >= 1` 即已开通)。

## 测试(+2)

- 小店关着时:**不发**小店请求、**不查**小店绑定、**不记** peace 相关的 errors,
  且结果里标明 `peace_skipped`
- 小店打开时:失败**仍然如实暴露**(反向用例,防止开关变成遮羞布)

## 验证(本地)

TZ=Asia/Shanghai **620 passed / 6 skipped**;ruff 全绿。
This commit is contained in:
yml2213
2026-10-04 20:59:17 +08:00
parent 631e33a4f6
commit ccd05cba9c
7 changed files with 209 additions and 1 deletions
@@ -203,6 +203,12 @@ class ManualMixin:
manual_type = self._to_int(result.get("manual_type"))
manual_score = self._to_int(result.get("manual_score"))
account.esports_points = manual_score
# 开通状态也要落库 —— 与精英对称。
# 之前只有 elite 那支写 `account.elite_opened`,电竞的 `esports_opened`
# **只在"开通"任务里被设置**,于是详情页的电竞「已开通/未开通」
# 根本不会被一键查询更新(用户报"电竞到期时间拿不到"时顺带查出来的)。
# 判定口径与 create_esports_qr 一致:manual_type >= 1 即已开通。
account.esports_opened = manual_type is not None and manual_type >= 1
# 到期时间也落库 —— 否则界面只能显示"已开通",看不到什么时候到期
expire_raw = result.get("expire_time")
expire_int = (
@@ -439,12 +439,25 @@ class OverviewMixin:
# `bind_status` 能推的推、推不出的留 `unknown`,于是界面上 97% 显示"未知"。
# 实测校准:开发库 4 个账号里 3 个其实**已绑定**(`is_bound_role=True` 且有角色名),
# 全被 unknown 盖住了。
# 和平小店总开关:关着时**完全跳过**它的所有请求。
# 小店目前是关闭状态,不跳过的话每个账号都会产生一条
# "Livelink登录态校验失败"(实测 200 个里失败 195 次,100%),
# 既淹掉真错误、又让人以为出故障。跳过时**不记 errors** ——
# 这是配置成关的,不是失败。
peace_on = bool(config.get("peace_enabled"))
if not peace_on:
overview["peace_skipped"] = (
"和平小店已关闭(douyu_config.peace_enabled=False)"
)
overview["bind"] = {}
for scope, label, alias_key in (
("elite", "精英宝典", "bind_act_alias"),
("esports", "电竞手册", "esports_act_alias"),
("peace", "和平小店", "xpd_act_alias"),
):
if scope == "peace" and not peace_on:
continue
alias = str(config.get(alias_key) or "").strip()
if not alias:
errors[f"bind_{scope}"] = f"未配置{label}活动别名"
@@ -480,6 +493,8 @@ class OverviewMixin:
("esports", "电竞手册", self._fetch_esports_records),
("peace", "和平小店", self._fetch_xpd_records),
):
if scope == "peace" and not peace_on:
continue
key = f"exchange_records_{scope}"
if not self._scope_configured(scope, manual_id, esports_manual_id, config):
errors[key] = f"未配置{label}活动参数,跳过"
+2
View File
@@ -129,6 +129,8 @@ DOUYU_CONFIG_DEFAULTS = {
"gold_api_account_template_name": "斗鱼昵称",
"gift_id": "23643",
"skin_id": "2942",
# 和平小店总开关,默认关(小店当前关闭;关着时一键查询完全跳过它的请求)
"peace_enabled": False,
"xpd_act_alias": "20260623KDQFH",
"xpd_act_id": "46195",
"xpd_rid": "9263298",