fix(huya): 查询积分先验 CK 并改用 HTTP 快速通道

This commit is contained in:
yml2213
2026-10-17 16:22:54 +08:00
parent 2620023db4
commit ef6663beae
2 changed files with 164 additions and 102 deletions
+73 -64
View File
@@ -10,8 +10,12 @@ from sqlalchemy.orm import Session
from core.huya.wss_client import POINTS_REDEEM_GOODS_NUM
from ..huya_defaults import HUYA_DEFAULT_OUTER_ACT_ID
from ..models import HuyaAccount, HuyaGoodsSnapshot, HuyaTask
# 积分查询超时:HTTP 通道正常几百毫秒,10 秒足够;失败时别让整批任务长时间挂住。
HUYA_POINTS_QUERY_TIMEOUT_SECONDS = 10.0
class GoodsMixin:
"""积分与商城域:积分/兑换记录/商品刷新/兑换。"""
@@ -29,6 +33,10 @@ class GoodsMixin:
def _push_log(self, level: str, message: str) -> None: ...
def _activity_client(self, uid: int, cookie: str): ...
def _shop_client(self, uid: int, cookie: str): ...
def _http_client(self): ...
def _probe_bind_status(
self, uid: int, cookie: str, b_act_id_int: int, outer_act_id: str = ""
): ...
def _mark_task(self, *args: Any, **kwargs: Any) -> None: ...
def _wait_until(self, when: datetime, uid: int) -> bool: ...
def _parse_scheduled_time(self, value: Any) -> datetime | None: ...
@@ -93,12 +101,15 @@ class GoodsMixin:
account_info: dict,
config_info: dict,
):
"""查询积分:主源是积分中心的 shopMiddleUI.getRedeemNeedGoods。
"""查询积分:先验 CK,再走积分中心 HTTP 接口。
2026-10-15 真机实测:旧活动接口 webActUI.getUserScore 对任何 sid 都返回
603「活动已结束」(该接口已下线,与配置无关);积分中心接口返回
``tag2``=当前积分、``tag3``=兑换门槛积分,另可顺手取 GameBizUI
的成长值/成长等级。
2026-10-15 真机实测:旧活动接口 ``webActUI.getUserScore`` 对任何 sid 都返回
603「活动已结束」(该接口已下线,与配置无关),不再作为回退——否则积分中心
超时后还要再等一次 15 秒。积分中心接口返回 ``tag2``=当前积分、
``tag3``=兑换门槛积分,另可顺手取 GameBizUI 的成长值/成长等级。
CK 判定复用外部单协作的新探针(HTTP + outer_act_id + 15 秒):积分中心对
失效 CK 仍可能返回 200/0 分,不能拿积分结果判断凭据是否有效。
"""
uid = self._resolve_uid(account_info)
if not uid:
@@ -110,16 +121,63 @@ class GoodsMixin:
self._mark_task(worker_db, task, "failed", "账号 Cookie 为空")
return
# ---- 先验 CK:没有回包就立即判失效,不再空跑积分与旧接口 ----
ck_valid = False
b_act_id_int = self._to_int(
self.payload.get("bind_act_id") or config_info.get("bind_act_id")
)
if b_act_id_int:
outer_act_id = str(
self.payload.get("outer_act_id")
or config_info.get("outer_act_id")
or HUYA_DEFAULT_OUTER_ACT_ID
).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)
self._mark_task(
worker_db,
task,
"error",
"虎牙角色信息接口无响应(CK 已失效,请重新登录或重新导入 Cookie)",
{
"ck_expired": True,
"ck_expired_reason": "角色信息接口无响应",
},
)
return
ck_valid = int(getattr(bind_status, "status", 0) or 0) == 200
goods_num = (
self._to_int(self.payload.get("goods_num")) or POINTS_REDEEM_GOODS_NUM
)
client: Any = self._shop_client(uid, cookie)
# 直接走 HTTP:积分中心不需要 WSS 会话,省掉每个账号建连/初始化的几秒等待。
client: Any = self._http_client()
redeem = client.get_redeem_need_goods(
uid=uid, guid="", cookie=cookie, goods_num=goods_num
uid=uid,
guid="",
cookie=cookie,
goods_num=goods_num,
timeout=HUYA_POINTS_QUERY_TIMEOUT_SECONDS,
)
if redeem is None:
self._query_points_legacy(
worker_db, task, account, uid, cookie, config_info
offline_result: dict[str, Any] = {"goods_num": goods_num}
if ck_valid:
offline_result["ck_valid"] = True
self._mark_task(
worker_db,
task,
"error",
"虎牙积分中心接口无响应(CK 探针正常,请稍后重试)"
if ck_valid
else "虎牙积分中心接口无响应",
offline_result,
)
return
@@ -138,7 +196,12 @@ class GoodsMixin:
points = self._to_int(redeem.value("tag2"))
result["points"] = points
result["redeem_threshold"] = self._to_int(redeem.value("tag3"))
growth = client.get_grow_level_user_info(uid=uid, guid="", cookie=cookie)
growth = client.get_grow_level_user_info(
uid=uid,
guid="",
cookie=cookie,
timeout=HUYA_POINTS_QUERY_TIMEOUT_SECONDS,
)
if growth is not None and growth.code == 0:
result["grow_value"] = self._to_int(growth.value("tag1", "tag0"))
result["grow_level"] = self._to_int(growth.value("tag1", "tag1"))
@@ -149,60 +212,6 @@ class GoodsMixin:
account.updated_at = datetime.now(UTC)
self._mark_task(worker_db, task, "success", f"积分: {points}", result)
def _query_points_legacy(
self,
worker_db: Session,
task: HuyaTask,
account: HuyaAccount,
uid: int,
cookie: str,
config_info: dict,
):
"""积分中心接口没有响应时回退旧活动接口,并如实报告旧接口的返回。"""
sid = str(self.payload.get("sid") or config_info.get("sid") or "").strip()
if not sid:
self._mark_task(
worker_db, task, "error", "积分中心接口无响应,且未配置可回退的活动 SID"
)
return
sid_int = self._to_int(sid)
if not sid_int:
self._mark_task(worker_db, task, "failed", f"虎牙活动 SID 无效: {sid}")
return
client: Any = self._activity_client(uid, cookie)
response = client.query_user_score(uid=uid, cookie=cookie, sid=sid_int)
if response is None:
self._mark_task(
worker_db,
task,
"error",
"虎牙积分接口无响应(积分中心与旧活动接口均失败)",
)
return
result = response.to_dict()
result["sid"] = sid_int
if response.status != 200:
self._mark_task(
worker_db,
task,
"failed",
f"积分中心接口无响应;旧活动接口(sid={sid_int})返回 "
f"{response.status} {response.msg}".strip(),
result,
)
return
points = response.available_score
account.points = points
account.status = "points_queried"
account.updated_at = datetime.now(UTC)
self._mark_task(
worker_db, task, "success", f"积分: {points}(旧活动接口)", result
)
def _execute_query_exchange_records(
self,
worker_db: Session,