fix(huya): 一键查询统一最新 CK 探针与 HTTP 快速通道
This commit is contained in:
@@ -230,9 +230,7 @@ def _runner(monkeypatch, shop_client, activity_client=None):
|
||||
runner = HuyaBatchRunner(db=None, batch_id="test-batch", task_type="query_all")
|
||||
marks: list[tuple[str, str, dict | None]] = []
|
||||
logs: list[tuple[str, str]] = []
|
||||
monkeypatch.setattr(
|
||||
runner, "_shop_client", lambda uid, cookie: shop_client, raising=False
|
||||
)
|
||||
monkeypatch.setattr(runner, "_http_client", lambda: shop_client, raising=False)
|
||||
if activity_client is not None:
|
||||
monkeypatch.setattr(
|
||||
runner,
|
||||
@@ -324,7 +322,10 @@ def test_query_all_fills_expire_time_from_season(monkeypatch):
|
||||
)
|
||||
runner, marks, _logs = _runner(monkeypatch, shop_client, activity_client=object())
|
||||
monkeypatch.setattr(
|
||||
runner, "_resolve_bind_status", lambda **kwargs: (None, {}), raising=False
|
||||
runner,
|
||||
"_probe_bind_status",
|
||||
lambda **kwargs: (_FakeBindStatus(status=601, msg="活动已下线"), {}),
|
||||
raising=False,
|
||||
)
|
||||
account = HuyaAccount(uid=str(UID), cookie=COOKIE)
|
||||
|
||||
@@ -606,7 +607,7 @@ def test_query_all_combines_points_elite_and_role(monkeypatch):
|
||||
runner, marks, _logs = _runner(monkeypatch, shop_client, activity_client=object())
|
||||
monkeypatch.setattr(
|
||||
runner,
|
||||
"_resolve_bind_status",
|
||||
"_probe_bind_status",
|
||||
lambda **kwargs: (_FakeBindStatus(), {"bind_status_source": "outer"}),
|
||||
raising=False,
|
||||
)
|
||||
@@ -660,7 +661,10 @@ def test_query_all_reports_not_opened_without_order_lookup(monkeypatch):
|
||||
shop_client = _StubClient(redeem=_redeem(REDEEM_NOT_OPENED_HEX))
|
||||
runner, marks, _logs = _runner(monkeypatch, shop_client, activity_client=object())
|
||||
monkeypatch.setattr(
|
||||
runner, "_resolve_bind_status", lambda **kwargs: (None, {}), raising=False
|
||||
runner,
|
||||
"_probe_bind_status",
|
||||
lambda **kwargs: (_FakeBindStatus(status=601, msg="活动已下线"), {}),
|
||||
raising=False,
|
||||
)
|
||||
account = HuyaAccount(uid=str(UID), cookie=COOKIE)
|
||||
|
||||
@@ -669,13 +673,13 @@ def test_query_all_reports_not_opened_without_order_lookup(monkeypatch):
|
||||
)
|
||||
|
||||
status, message, result = marks[-1]
|
||||
# 角色接口挂了不影响宝典/积分结论
|
||||
# 角色接口业务失败不影响宝典/积分结论
|
||||
assert status == "success"
|
||||
assert result["elite_opened"] is False
|
||||
assert account.elite_opened is False
|
||||
assert account.elite_open_time is None
|
||||
assert "未开通宝典" in message
|
||||
assert "角色信息接口无响应" in message
|
||||
assert "活动已下线" in message
|
||||
# 未开通不查订单(省一次请求)
|
||||
assert [name for name, _kwargs in shop_client.calls] == ["get_redeem_need_goods"]
|
||||
|
||||
@@ -684,9 +688,12 @@ def test_query_all_fails_when_points_center_unreachable(monkeypatch):
|
||||
shop_client = _StubClient(redeem=None)
|
||||
runner, marks, _logs = _runner(monkeypatch, shop_client, activity_client=object())
|
||||
monkeypatch.setattr(
|
||||
runner, "_resolve_bind_status", lambda **kwargs: (None, {}), raising=False
|
||||
runner,
|
||||
"_probe_bind_status",
|
||||
lambda **kwargs: (_FakeBindStatus(status=601, msg="活动已下线"), {}),
|
||||
raising=False,
|
||||
)
|
||||
account = HuyaAccount(uid=str(UID), cookie=COOKIE)
|
||||
account = HuyaAccount(uid=str(UID), cookie=COOKIE, status="imported")
|
||||
|
||||
runner._execute_query_all(
|
||||
None, None, account, {"uid": UID, "cookie": COOKIE}, {"bind_act_id": "9271"}
|
||||
@@ -695,9 +702,9 @@ def test_query_all_fails_when_points_center_unreachable(monkeypatch):
|
||||
status, message, result = marks[-1]
|
||||
assert status == "failed"
|
||||
assert "积分中心接口无响应" in message
|
||||
# 两条通道都没回包:CK 失效标记跟着失败结果一起落库(前端整行标红)
|
||||
assert result["ck_expired"] is True
|
||||
assert account.status == "ck_expired"
|
||||
assert result.get("ck_expired") is None
|
||||
# 没有查到任何东西时不能把账号标成 all_queried
|
||||
assert account.status == "imported"
|
||||
|
||||
|
||||
def test_query_all_flags_ck_expired_when_role_lookup_has_no_response(monkeypatch):
|
||||
@@ -705,13 +712,16 @@ def test_query_all_flags_ck_expired_when_role_lookup_has_no_response(monkeypatch
|
||||
|
||||
真机日志(logs/app-2026-10-15.log,hy_297838517)里
|
||||
``webActUI.checkUserBindGameAccount`` 的响应只有 ``newdata keys: ['']``(没有 tRsp),
|
||||
``_resolve_bind_status`` 因此返回 None;积分中心走另一条通道却还能返回 200,
|
||||
``_probe_bind_status`` 因此返回 None;积分中心走另一条通道却还能返回 200,
|
||||
所以不能凭「积分查到了」就当成功,必须打 ck_expired 让页面标红。
|
||||
|
||||
2026-10-17 起一键查询也改成先验 CK:判定失效后立即停止,
|
||||
不再白跑积分中心和宝典接口。
|
||||
"""
|
||||
shop_client = _StubClient(redeem=_redeem(REDEEM_NOT_OPENED_HEX))
|
||||
runner, marks, _logs = _runner(monkeypatch, shop_client, activity_client=object())
|
||||
monkeypatch.setattr(
|
||||
runner, "_resolve_bind_status", lambda **kwargs: (None, {}), raising=False
|
||||
runner, "_probe_bind_status", lambda **kwargs: (None, {}), raising=False
|
||||
)
|
||||
account = HuyaAccount(uid=str(UID), cookie=COOKIE)
|
||||
|
||||
@@ -720,13 +730,12 @@ def test_query_all_flags_ck_expired_when_role_lookup_has_no_response(monkeypatch
|
||||
)
|
||||
|
||||
status, message, result = marks[-1]
|
||||
# 宝典/积分查到了,任务本身不算失败(结果照样展示),但 CK 失效标记必须带上
|
||||
assert status == "success"
|
||||
assert status == "error"
|
||||
assert result["ck_expired"] is True
|
||||
assert result["ck_expired_reason"] == "角色信息接口无响应"
|
||||
assert account.status == "ck_expired"
|
||||
assert "角色信息接口无响应" in message
|
||||
assert "疑似 CK 已失效" in message
|
||||
assert "CK 已失效" in message
|
||||
assert shop_client.calls == []
|
||||
|
||||
|
||||
def test_query_all_keeps_business_error_out_of_ck_expired(monkeypatch):
|
||||
@@ -735,7 +744,7 @@ def test_query_all_keeps_business_error_out_of_ck_expired(monkeypatch):
|
||||
runner, marks, _logs = _runner(monkeypatch, shop_client, activity_client=object())
|
||||
monkeypatch.setattr(
|
||||
runner,
|
||||
"_resolve_bind_status",
|
||||
"_probe_bind_status",
|
||||
lambda **kwargs: (_FakeBindStatus(status=601, msg="活动已下线"), {}),
|
||||
raising=False,
|
||||
)
|
||||
|
||||
@@ -263,7 +263,12 @@ class EliteMixin:
|
||||
account_info: dict,
|
||||
config_info: dict,
|
||||
):
|
||||
"""一键查询:角色绑定 + 精英宝典开通 + 积分(勾选账号批量跑)。"""
|
||||
"""一键查询:先验 CK,再查角色绑定 + 精英宝典开通 + 积分。
|
||||
|
||||
与「查询角色 / 查询积分」保持同一套最新链路:
|
||||
- CK 走 HTTP 探针 + outer_act_id + 15 秒;无回包立即判失效并返回;
|
||||
- 积分/宝典直接走 HTTP,不再为一次查询建 WSS 商城会话。
|
||||
"""
|
||||
uid = self._resolve_uid(account_info)
|
||||
if not uid:
|
||||
self._mark_task(worker_db, task, "failed", "无法从账号或 Cookie 解析 yyuid")
|
||||
@@ -277,14 +282,86 @@ class EliteMixin:
|
||||
result: dict[str, Any] = {"uid": uid}
|
||||
failures: list[str] = []
|
||||
message_parts: list[str] = []
|
||||
role_name = ""
|
||||
|
||||
# ---- 积分 + 宝典开通(积分中心同一个接口) ----
|
||||
shop_client: Any = self._shop_client(uid, cookie)
|
||||
# ---- 角色绑定 / CK 有效性(先做,失效就不空跑后面两个接口) ----
|
||||
b_act_id_int = self._to_int(
|
||||
self.payload.get("bind_act_id") or config_info.get("bind_act_id")
|
||||
)
|
||||
if not b_act_id_int:
|
||||
failures.append("未配置绑定 bActId")
|
||||
result["ck_valid"] = False
|
||||
else:
|
||||
outer_act_id = str(
|
||||
self.payload.get("outer_act_id")
|
||||
or config_info.get("outer_act_id")
|
||||
or ""
|
||||
).strip()
|
||||
bind_status, bind_query_result = self._probe_bind_status(
|
||||
uid=uid,
|
||||
cookie=cookie,
|
||||
b_act_id_int=b_act_id_int,
|
||||
outer_act_id=outer_act_id,
|
||||
)
|
||||
if bind_status is None:
|
||||
account.status = "ck_expired"
|
||||
account.updated_at = datetime.now(UTC)
|
||||
result.update(
|
||||
{
|
||||
"bind_act_id": b_act_id_int,
|
||||
"failures": failures + ["角色信息接口无响应"],
|
||||
"ck_expired": True,
|
||||
"ck_expired_reason": "角色信息接口无响应",
|
||||
}
|
||||
)
|
||||
self._mark_task(
|
||||
worker_db,
|
||||
task,
|
||||
"error",
|
||||
"虎牙角色信息接口无响应(CK 已失效,请重新登录或重新导入 Cookie)",
|
||||
result,
|
||||
)
|
||||
return
|
||||
result.update(
|
||||
{
|
||||
"bind_act_id": b_act_id_int,
|
||||
"ck_valid": int(getattr(bind_status, "status", 0) or 0) == 200,
|
||||
}
|
||||
)
|
||||
if bind_status.status != 200:
|
||||
failures.append(
|
||||
bind_status.msg or f"角色信息查询失败: {bind_status.status}"
|
||||
)
|
||||
else:
|
||||
role_info = self._bind_role_result(bind_status)
|
||||
role_name = role_info.get("role_name") or ""
|
||||
result.update(
|
||||
{
|
||||
**role_info,
|
||||
**self._bind_change_state(bind_status),
|
||||
**bind_query_result,
|
||||
}
|
||||
)
|
||||
if role_name:
|
||||
account.game_name = role_name
|
||||
account.game_channel = (
|
||||
self._role_channel(bind_status) or account.game_channel
|
||||
)
|
||||
message_parts.append(f"角色 {role_name}")
|
||||
else:
|
||||
message_parts.append("未绑定角色")
|
||||
|
||||
# ---- 积分 + 宝典开通(积分中心同一个接口,直接 HTTP) ----
|
||||
shop_client: Any = self._http_client()
|
||||
goods_num = (
|
||||
self._to_int(self.payload.get("goods_num")) or POINTS_REDEEM_GOODS_NUM
|
||||
)
|
||||
redeem = shop_client.get_redeem_need_goods(
|
||||
uid=uid, guid="", cookie=cookie, goods_num=goods_num
|
||||
uid=uid,
|
||||
guid="",
|
||||
cookie=cookie,
|
||||
goods_num=goods_num,
|
||||
timeout=10.0,
|
||||
)
|
||||
points: int | None = None
|
||||
opened: bool | None = None
|
||||
@@ -338,68 +415,8 @@ class EliteMixin:
|
||||
opened_text = "未开通宝典"
|
||||
message_parts.append(opened_text)
|
||||
|
||||
# ---- 角色绑定(活动通道) ----
|
||||
b_act_id_int = self._to_int(
|
||||
self.payload.get("bind_act_id") or config_info.get("bind_act_id")
|
||||
)
|
||||
role_name = ""
|
||||
# 角色查不到 = 这个 CK 已经不认了(模块头里有真机依据),整行要标红提醒
|
||||
ck_expired = False
|
||||
ck_expired_reason = ""
|
||||
if not b_act_id_int:
|
||||
failures.append("未配置绑定 bActId")
|
||||
else:
|
||||
outer_act_id = str(
|
||||
self.payload.get("outer_act_id")
|
||||
or config_info.get("outer_act_id")
|
||||
or ""
|
||||
).strip()
|
||||
bind_client: Any = self._http_client()
|
||||
bind_status, bind_query_result = self._resolve_bind_status(
|
||||
client=bind_client,
|
||||
uid=uid,
|
||||
cookie=cookie,
|
||||
b_act_id_int=b_act_id_int,
|
||||
outer_act_id=outer_act_id,
|
||||
timeout=15.0,
|
||||
)
|
||||
if bind_status is None:
|
||||
ck_expired = True
|
||||
ck_expired_reason = "角色信息接口无响应"
|
||||
failures.append(ck_expired_reason)
|
||||
elif bind_status.status != 200:
|
||||
failures.append(
|
||||
bind_status.msg or f"角色信息查询失败: {bind_status.status}"
|
||||
)
|
||||
else:
|
||||
role_info = self._bind_role_result(bind_status)
|
||||
role_name = role_info.get("role_name") or ""
|
||||
result.update(
|
||||
{
|
||||
"bind_act_id": b_act_id_int,
|
||||
**role_info,
|
||||
**self._bind_change_state(bind_status),
|
||||
**bind_query_result,
|
||||
}
|
||||
)
|
||||
if role_name:
|
||||
account.game_name = role_name
|
||||
account.game_channel = (
|
||||
self._role_channel(bind_status) or account.game_channel
|
||||
)
|
||||
message_parts.append(f"角色 {role_name}")
|
||||
else:
|
||||
message_parts.append("未绑定角色")
|
||||
|
||||
account.points = points if points is not None else account.points
|
||||
# CK 失效时账号状态单独标出来(前端「状态」列据此显示红色「CK 失效」)
|
||||
account.status = "ck_expired" if ck_expired else "all_queried"
|
||||
account.updated_at = datetime.now(UTC)
|
||||
result["failures"] = failures
|
||||
if ck_expired:
|
||||
result["ck_expired"] = True
|
||||
result["ck_expired_reason"] = ck_expired_reason
|
||||
ck_hint = ";疑似 CK 已失效,请重新登录或重新导入 Cookie" if ck_expired else ""
|
||||
|
||||
fetched_anything = points is not None or role_name or opened is not None
|
||||
if not fetched_anything:
|
||||
@@ -407,12 +424,14 @@ class EliteMixin:
|
||||
worker_db,
|
||||
task,
|
||||
"failed",
|
||||
(";".join(failures) or "一键查询全部失败") + ck_hint,
|
||||
";".join(failures) or "一键查询全部失败",
|
||||
result,
|
||||
)
|
||||
return
|
||||
|
||||
account.status = "all_queried"
|
||||
account.updated_at = datetime.now(UTC)
|
||||
message = "|".join(message_parts) or "一键查询完成"
|
||||
if failures:
|
||||
message += f";部分失败: {';'.join(failures)}"
|
||||
self._mark_task(worker_db, task, "success", message + ck_hint, result)
|
||||
self._mark_task(worker_db, task, "success", message, result)
|
||||
|
||||
Reference in New Issue
Block a user