feat(huya): 统一积分兑换并完善兑换记录

This commit is contained in:
yml
2026-11-13 19:03:55 +08:00
parent b89ee4fd18
commit 711be0e2b6
17 changed files with 2064 additions and 193 deletions
+69
View File
@@ -1142,6 +1142,75 @@ class HuyaHttpClient:
self.logger(" createOrderV5: 无响应")
return result
def create_order_v6(
self,
uid,
guid,
cookie,
pid,
items,
source_id="yellowcarlist",
game_id="0",
biz_params=JIFENCENTER_BIZ_PARAMS,
):
"""积分兑换多商品下单(``shopMiddleUI.createOrderV6``)。
2026-11-13 HAR:积分足够时官方只发这一单,不再调用
``scoreExchangePrize`` 或 ``payOrderSubmitV5``。
"""
from .shop_structs import (
CreateOrderAccountParam,
CreateOrderExtraParam,
CreateOrderItemReqV6,
CreateOrderPromotionParam,
CreateOrderReqV6,
CreateOrderRspV6,
)
req = CreateOrderReqV6()
req.userId = self._build_shop_user(uid, cookie)
req.shopAppInfo = self._build_shop_app(source_id, SHOP_APP_SCENE)
req.items = []
for item in items:
row = CreateOrderItemReqV6()
row.spuId = str(item.get("spu_id") or "")
row.skuId = int(item.get("sku_id") or 0)
row.itemCount = int(item.get("item_count") or 1)
row.goodsType = int(item.get("goods_type") or 0)
row.virtualType = int(item.get("virtual_type") or 0)
req.items.append(row)
req.pid = int(pid or 0)
req.gameId = str(game_id or "0")
req.src = 2
req.orderType = 6
extra = CreateOrderExtraParam()
extra.bizParams = biz_params
req.extraParam = extra
req.sourceId = source_id
req.env = self._build_order_env()
req.orderScene = 3
req.promotionParam = CreateOrderPromotionParam()
req.accountParam = CreateOrderAccountParam()
result: Any = self.call_rpc(
"shopMiddleUI",
"createOrderV6",
req,
CreateOrderRspV6,
uid,
guid,
cookie,
timeout=15.0,
)
if result is not None:
self.logger(
f" createOrderV6: code={result.code} orderId={result.orderId} "
f"msg={result.message}"
)
else:
self.logger(" createOrderV6: 无响应")
return result
@staticmethod
def _normalize_pay_channel(pay_type) -> str:
text = str(pay_type or "").strip()
+154
View File
@@ -1055,6 +1055,138 @@ class CreateOrderRsp(TafStruct):
pass
class CreateOrderItemReqV6(TafStruct):
"""``createOrderV6`` 的单个商品项(2026-11-13 积分兑换 HAR)。"""
def __init__(self):
self.spuId: str = "" # tag 0
self.skuId: int = 0 # tag 1
self.itemCount: int = 0 # tag 2
self.groupItemList: list = [] # tag 3 Vector<CreateOrderItemReqV6>
self.goodsType: int = 0 # tag 4
self.virtualType: int = 0 # tag 5
def write_to(self, os: TafOutputStream):
os.write_string(0, self.spuId)
os.write_int64(1, self.skuId)
os.write_int32(2, self.itemCount)
os.write_list(3, self.groupItemList)
os.write_int32(4, self.goodsType)
os.write_int32(5, self.virtualType)
def read_from(self, ins: TafInputStream):
self.spuId = ins.read_string(0, default=self.spuId)
self.skuId = ins.read_int64(1, default=self.skuId)
self.itemCount = ins.read_int32(2, default=self.itemCount)
self.groupItemList = ins.read_list(3, item_reader=CreateOrderItemReqV6.read)
self.goodsType = ins.read_int32(4, default=self.goodsType)
self.virtualType = ins.read_int32(5, default=self.virtualType)
@staticmethod
def read(ins: TafInputStream) -> "CreateOrderItemReqV6":
item = CreateOrderItemReqV6()
item.read_from(ins)
return item
class CreateOrderReqV6(TafStruct):
"""积分兑换多商品下单请求 (shopMiddleUI.createOrderV6)。"""
def __init__(self):
self.userId = UserId() # tag 0
self.shopAppInfo = ShopAppInfo() # tag 1
self.receiveId: int = 0 # tag 2
self.items: list[CreateOrderItemReqV6] = [] # tag 3
self.pid: int = 0 # tag 4
self.remark: str = "" # tag 5
self.gameId: str = "" # tag 6
self.orderId: int = 0 # tag 7
self.src: int = 0 # tag 8
self.couponUserIds: list[int] = [] # tag 9
self.orderType: int = 0 # tag 10
self.extraParam: CreateOrderExtraParam | None = None # tag 11
self.scene: int = 0 # tag 12
self.promotionItems: list = [] # tag 13
self.sourceId: str = "" # tag 14
self.env: dict[str, str] = {} # tag 15
self.orderScene: int = 0 # tag 16
self.watchWord: str = "" # tag 17
self.marketingChannel: str = "" # tag 18
self.promotionParam: CreateOrderPromotionParam | None = None # tag 19
self.externalTraceKey: str = "" # tag 20
self.kefuUid: int = 0 # tag 21
self.accountParam: CreateOrderAccountParam | None = None # tag 22
self.parentOrderId: int = 0 # tag 23
self.vendorAccountType: str = "" # tag 24
self.vendorAccountVal: str = "" # tag 25
self.vendorSubAccountVal: str = "" # tag 26
self.vendorSubAccountType: str = "" # tag 27
def write_to(self, os: TafOutputStream):
os.write_struct(0, self.userId)
os.write_struct(1, self.shopAppInfo)
os.write_int64(2, self.receiveId)
os.write_list(3, self.items)
os.write_int64(4, self.pid)
os.write_string(5, self.remark)
os.write_string(6, self.gameId)
os.write_int64(7, self.orderId)
os.write_int32(8, self.src)
os.write_list(9, self.couponUserIds)
os.write_int32(10, self.orderType)
os.write_struct(11, self.extraParam or CreateOrderExtraParam())
os.write_int32(12, self.scene)
os.write_list(13, self.promotionItems)
os.write_string(14, self.sourceId)
os.write_map(15, self.env)
os.write_int32(16, self.orderScene)
os.write_string(17, self.watchWord)
os.write_string(18, self.marketingChannel)
os.write_struct(19, self.promotionParam or CreateOrderPromotionParam())
os.write_string(20, self.externalTraceKey)
os.write_int64(21, self.kefuUid)
os.write_struct(22, self.accountParam or CreateOrderAccountParam())
os.write_int64(23, self.parentOrderId)
os.write_string(24, self.vendorAccountType)
os.write_string(25, self.vendorAccountVal)
os.write_string(26, self.vendorSubAccountVal)
os.write_string(27, self.vendorSubAccountType)
def read_from(self, ins: TafInputStream):
pass
class CreateOrderRspV6(TafStruct):
"""积分兑换多商品下单响应 (shopMiddleUI.createOrderV6)。"""
def __init__(self):
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.orderId: int = 0 # tag 2
self.subOrderId: int = 0 # tag 3
self.orderStatus: int = 0 # tag 4
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
self.message = ins.read_string(1, default=self.message)
self.orderId = ins.read_int64(2, default=self.orderId)
self.subOrderId = ins.read_int64(3, default=self.subOrderId)
self.orderStatus = ins.read_int32(4, default=self.orderStatus)
_skip_to_struct_end(ins)
def write_to(self, os: TafOutputStream):
pass
def to_dict(self) -> dict:
return {
"code": self.code,
"message": self.message,
"order_id": self.orderId,
"sub_order_id": self.subOrderId,
"order_status": self.orderStatus,
}
# ============================================================
# 支付(payOrderSubmitV5)
# ============================================================
@@ -1314,6 +1446,28 @@ def extract_points_center_tabs(payload: dict | None) -> list[dict]:
return result
def extract_points_center_sid(payload: dict | None) -> int | None:
"""从 ``queryAllocation`` 的 ScoreCenterConfig 提取当前兑换活动 SID。
2026-11-13 HAR:配置位于
``payload.tag2[*].tag2_2.tag0.tag7``,当前值为 ``2277``;这个值正是
``webActUI.getUserPrizeRecords`` 的 ``tag1``。
"""
if not isinstance(payload, dict):
return None
for section in payload.get("tag2") or []:
if not isinstance(section, dict):
continue
config_box = section.get("tag2_2")
config = config_box.get("tag0") if isinstance(config_box, dict) else None
if not isinstance(config, dict):
continue
sid = _as_int(config.get("tag7"))
if sid and sid > 0:
return sid
return None
class PlayMallAppInfo(TafStruct):
"""PlayMallUI 系接口的应用信息。
+54 -1
View File
@@ -105,7 +105,7 @@ SEQ_ACTIVITY_BIZ = 0x1D000109
SEQ_POINTS_BIZ = 0x1D000107
SEQ_POINTS_ORDER = 0x1D000108
# 积分中心通道里用 0x1d000108 的两帧
POINTS_ORDER_METHODS = frozenset({"createOrderV5", "payOrderSubmitV5"})
POINTS_ORDER_METHODS = frozenset({"createOrderV5", "createOrderV6", "payOrderSubmitV5"})
class WssCommand:
@@ -1309,6 +1309,59 @@ class HuyaWssClient:
"shopMiddleUI", "createOrderV5", req, CreateOrderRsp, timeout=15.0
)
async def create_order_v6(
self,
uid: int,
guid: str,
cookie: str,
pid: int,
items,
source_id: str = "yellowcarlist",
game_id: str = "0",
biz_params: str = JIFENCENTER_BIZ_PARAMS,
):
"""积分兑换多商品下单(``shopMiddleUI.createOrderV6``)。"""
from .shop_structs import (
CreateOrderAccountParam,
CreateOrderExtraParam,
CreateOrderItemReqV6,
CreateOrderPromotionParam,
CreateOrderReqV6,
CreateOrderRspV6,
)
req = CreateOrderReqV6()
req.userId = self._build_biz_user(uid, cookie)
req.shopAppInfo = self._build_shop_app(source_id, SHOP_APP_SCENE)
req.items = []
for item in items:
row = CreateOrderItemReqV6()
row.spuId = str(item.get("spu_id") or "")
row.skuId = int(item.get("sku_id") or 0)
row.itemCount = int(item.get("item_count") or 1)
row.goodsType = int(item.get("goods_type") or 0)
row.virtualType = int(item.get("virtual_type") or 0)
req.items.append(row)
req.pid = int(pid or 0)
req.gameId = str(game_id or "0")
req.src = 2
req.orderType = 6
extra = CreateOrderExtraParam()
extra.bizParams = biz_params
req.extraParam = extra
req.sourceId = source_id
req.env = self._build_order_env()
req.orderScene = 3
req.promotionParam = CreateOrderPromotionParam()
req.accountParam = CreateOrderAccountParam()
return await self.call_rpc(
"shopMiddleUI",
"createOrderV6",
req,
CreateOrderRspV6,
timeout=15.0,
)
async def pay_order_submit(
self,
uid: int,