增加兑换
This commit is contained in:
@@ -50,6 +50,8 @@ class DouyuActivityClient:
|
|||||||
ESPORTS_AGREEMENT_API = "https://www.douyu.com/wgapi/ordnc/activity/peacematch/agreement"
|
ESPORTS_AGREEMENT_API = "https://www.douyu.com/wgapi/ordnc/activity/peacematch/agreement"
|
||||||
ESPORTS_ACT_BIND_API = "https://www.douyu.com/wgapi/ordnc/live/memberthird/tencent/actBind"
|
ESPORTS_ACT_BIND_API = "https://www.douyu.com/wgapi/ordnc/live/memberthird/tencent/actBind"
|
||||||
ESPORTS_USER_INFO_API = "https://www.douyu.com/wgapi/ordnc/activity/peacematch/getUserInfo"
|
ESPORTS_USER_INFO_API = "https://www.douyu.com/wgapi/ordnc/activity/peacematch/getUserInfo"
|
||||||
|
ESPORTS_GOODS_API = "https://www.douyu.com/wgapi/ordnc/activity/peacematch/storehome"
|
||||||
|
ESPORTS_EXCHANGE_API = "https://www.douyu.com/wgapi/ordnc/activity/peacematch/exchange"
|
||||||
GOLD_QR_API = "https://cz.douyu.com/m/gold/getQrCode"
|
GOLD_QR_API = "https://cz.douyu.com/m/gold/getQrCode"
|
||||||
GOLD_ACCOUNT_API = "https://cz.douyu.com/item/gold/account"
|
GOLD_ACCOUNT_API = "https://cz.douyu.com/item/gold/account"
|
||||||
EXCHANGE_BALANCE_API = "https://www.douyu.com/wjapi/nc/exchange/fim"
|
EXCHANGE_BALANCE_API = "https://www.douyu.com/wjapi/nc/exchange/fim"
|
||||||
@@ -507,6 +509,71 @@ class DouyuActivityClient:
|
|||||||
"raw": payload,
|
"raw": payload,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _esports_shop_referer(rid: str) -> str:
|
||||||
|
return (
|
||||||
|
"https://www.douyu.com/pages/live-peace-pel/web/shop"
|
||||||
|
f"?roomId={rid or '0'}&isAnchorSide=0"
|
||||||
|
)
|
||||||
|
|
||||||
|
def list_esports_goods(self, *, manual_id: str, rid: str) -> dict[str, Any]:
|
||||||
|
"""查询电竞手册皮肤商城。"""
|
||||||
|
payload = self._request_json(
|
||||||
|
"get",
|
||||||
|
self.ESPORTS_GOODS_API,
|
||||||
|
"刷新电竞皮肤商城",
|
||||||
|
params={"manualID": manual_id, "rid": rid},
|
||||||
|
headers={
|
||||||
|
"Origin": "",
|
||||||
|
"Referer": self._esports_shop_referer(rid),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
goods = (payload.get("data") or {}).get("list") or []
|
||||||
|
return {
|
||||||
|
"goods": goods,
|
||||||
|
"score": (payload.get("data") or {}).get("score"),
|
||||||
|
"raw": payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
def exchange_esports_goods(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
manual_id: str,
|
||||||
|
rid: str,
|
||||||
|
commodity_id: str,
|
||||||
|
quantity: int = 1,
|
||||||
|
ctn: str | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""兑换电竞手册皮肤商城商品。"""
|
||||||
|
referer = self._esports_shop_referer(rid)
|
||||||
|
ctn_value = ctn or self.esports_ctn(referer=referer)
|
||||||
|
self._set_cookie_value("acf_ccn", ctn_value)
|
||||||
|
payload = self._request_json(
|
||||||
|
"post",
|
||||||
|
self.ESPORTS_EXCHANGE_API,
|
||||||
|
"兑换电竞皮肤",
|
||||||
|
data={
|
||||||
|
"manualID": manual_id,
|
||||||
|
"commodityID": commodity_id,
|
||||||
|
"rid": rid or "0",
|
||||||
|
"num": str(max(1, quantity)),
|
||||||
|
"ctn": ctn_value,
|
||||||
|
},
|
||||||
|
headers={
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
"Origin": "",
|
||||||
|
"Referer": referer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if payload.get("error") not in (0, "0"):
|
||||||
|
raise DouyuActivityError(payload.get("msg") or "兑换电竞皮肤失败")
|
||||||
|
return {
|
||||||
|
"commodity_id": commodity_id,
|
||||||
|
"quantity": max(1, quantity),
|
||||||
|
"ctn": ctn_value,
|
||||||
|
"raw": payload,
|
||||||
|
}
|
||||||
|
|
||||||
def create_gold_qr(self, *, amount: int, pay_type: int = 1, product_id: str = "DYTV_Product_Gold_1") -> dict[str, Any]:
|
def create_gold_qr(self, *, amount: int, pay_type: int = 1, product_id: str = "DYTV_Product_Gold_1") -> dict[str, Any]:
|
||||||
"""生成鱼翅充值支付二维码。"""
|
"""生成鱼翅充值支付二维码。"""
|
||||||
payload = self._request_json(
|
payload = self._request_json(
|
||||||
@@ -533,19 +600,20 @@ class DouyuActivityClient:
|
|||||||
raise DouyuActivityError(payload.get("msg") or "生成鱼翅充值码失败")
|
raise DouyuActivityError(payload.get("msg") or "生成鱼翅充值码失败")
|
||||||
return {"pay_url": pay_url, "token": token, "raw": payload}
|
return {"pay_url": pay_url, "token": token, "raw": payload}
|
||||||
|
|
||||||
def donate_elite_gift(
|
def _donate_gift(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
gift_count: int,
|
gift_count: int,
|
||||||
room_id: str,
|
room_id: str,
|
||||||
gift_id: str,
|
gift_id: str,
|
||||||
skin_id: str,
|
skin_id: str,
|
||||||
|
source: str,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""赠送精英令礼物。"""
|
"""赠送直播间礼物。"""
|
||||||
payload = self._request_json(
|
payload = self._request_json(
|
||||||
"post",
|
"post",
|
||||||
self.DONATE_API,
|
self.DONATE_API,
|
||||||
"赠送精英令",
|
source,
|
||||||
data={
|
data={
|
||||||
"giftId": gift_id,
|
"giftId": gift_id,
|
||||||
"giftCount": str(gift_count),
|
"giftCount": str(gift_count),
|
||||||
@@ -559,9 +627,44 @@ class DouyuActivityClient:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if payload.get("error") not in (0, "0") and payload.get("msg") != "success":
|
if payload.get("error") not in (0, "0") and payload.get("msg") != "success":
|
||||||
raise DouyuActivityError(payload.get("msg") or "赠送精英令失败")
|
raise DouyuActivityError(payload.get("msg") or f"{source}失败")
|
||||||
return {"raw": payload}
|
return {"raw": payload}
|
||||||
|
|
||||||
|
def donate_elite_gift(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
gift_count: int,
|
||||||
|
room_id: str,
|
||||||
|
gift_id: str,
|
||||||
|
skin_id: str,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""赠送精英令礼物。"""
|
||||||
|
return self._donate_gift(
|
||||||
|
gift_count=gift_count,
|
||||||
|
room_id=room_id,
|
||||||
|
gift_id=gift_id,
|
||||||
|
skin_id=skin_id,
|
||||||
|
source="赠送精英令",
|
||||||
|
)
|
||||||
|
|
||||||
|
def donate_esports_gift(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
gift_name: str,
|
||||||
|
gift_count: int,
|
||||||
|
room_id: str,
|
||||||
|
gift_id: str,
|
||||||
|
skin_id: str,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""赠送电竞手册任务指定礼物。"""
|
||||||
|
return self._donate_gift(
|
||||||
|
gift_count=gift_count,
|
||||||
|
room_id=room_id,
|
||||||
|
gift_id=gift_id,
|
||||||
|
skin_id=skin_id,
|
||||||
|
source=f"赠送{gift_name}",
|
||||||
|
)
|
||||||
|
|
||||||
def query_points(self, *, uid: str, ctn: str) -> dict[str, Any]:
|
def query_points(self, *, uid: str, ctn: str) -> dict[str, Any]:
|
||||||
"""查询手册积分余额。"""
|
"""查询手册积分余额。"""
|
||||||
payload = self._request_json(
|
payload = self._request_json(
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
"""新增电竞手册送礼配置
|
||||||
|
|
||||||
|
Revision ID: 20260728_0013
|
||||||
|
Revises: 20260728_0012
|
||||||
|
Create Date: 2026-07-28
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
revision: str = "20260728_0013"
|
||||||
|
down_revision: Union[str, None] = "20260728_0012"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def _columns(bind, table_name: str) -> set[str]:
|
||||||
|
if not sa.inspect(bind).has_table(table_name):
|
||||||
|
return set()
|
||||||
|
return {column["name"] for column in sa.inspect(bind).get_columns(table_name)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
bind = op.get_bind()
|
||||||
|
columns = _columns(bind, "douyu_config")
|
||||||
|
for column in [
|
||||||
|
sa.Column("esports_chicken_gift_id", sa.String(length=64), nullable=True),
|
||||||
|
sa.Column("esports_chicken_skin_id", sa.String(length=64), nullable=True),
|
||||||
|
sa.Column("esports_firework_gift_id", sa.String(length=64), nullable=True),
|
||||||
|
sa.Column("esports_firework_skin_id", sa.String(length=64), nullable=True),
|
||||||
|
]:
|
||||||
|
if column.name not in columns:
|
||||||
|
op.add_column("douyu_config", column)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
bind = op.get_bind()
|
||||||
|
columns = _columns(bind, "douyu_config")
|
||||||
|
for column_name in [
|
||||||
|
"esports_firework_skin_id",
|
||||||
|
"esports_firework_gift_id",
|
||||||
|
"esports_chicken_skin_id",
|
||||||
|
"esports_chicken_gift_id",
|
||||||
|
]:
|
||||||
|
if column_name in columns:
|
||||||
|
op.drop_column("douyu_config", column_name)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"""新增电竞手册皮肤商城快照
|
||||||
|
|
||||||
|
Revision ID: 20260728_0014
|
||||||
|
Revises: 20260728_0013
|
||||||
|
Create Date: 2026-07-28
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
revision: str = "20260728_0014"
|
||||||
|
down_revision: Union[str, None] = "20260728_0013"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
bind = op.get_bind()
|
||||||
|
if sa.inspect(bind).has_table("douyu_esports_goods_snapshot"):
|
||||||
|
return
|
||||||
|
op.create_table(
|
||||||
|
"douyu_esports_goods_snapshot",
|
||||||
|
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
||||||
|
sa.Column("commodity_id", sa.String(length=64), nullable=False),
|
||||||
|
sa.Column("name", sa.String(length=256), nullable=True),
|
||||||
|
sa.Column("score", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("status", sa.String(length=32), nullable=True),
|
||||||
|
sa.Column("raw", sa.JSON(), nullable=True),
|
||||||
|
sa.Column("updated_at", sa.DateTime(), nullable=True),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_douyu_esports_goods_snapshot_commodity_id",
|
||||||
|
"douyu_esports_goods_snapshot",
|
||||||
|
["commodity_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
bind = op.get_bind()
|
||||||
|
if sa.inspect(bind).has_table("douyu_esports_goods_snapshot"):
|
||||||
|
op.drop_table("douyu_esports_goods_snapshot")
|
||||||
@@ -128,6 +128,10 @@ class DouyuConfig(Base):
|
|||||||
esports_manual_id = Column(String(64), default="1W0yGkOdDbAXb")
|
esports_manual_id = Column(String(64), default="1W0yGkOdDbAXb")
|
||||||
esports_act_alias = Column(String(64), default="20260226ZMGDG")
|
esports_act_alias = Column(String(64), default="20260226ZMGDG")
|
||||||
esports_amount = Column(Integer, default=3000)
|
esports_amount = Column(Integer, default=3000)
|
||||||
|
esports_chicken_gift_id = Column(String(64), default="24766")
|
||||||
|
esports_chicken_skin_id = Column(String(64), default="0")
|
||||||
|
esports_firework_gift_id = Column(String(64), default="24767")
|
||||||
|
esports_firework_skin_id = Column(String(64), default="3850")
|
||||||
gold_pay_type = Column(Integer, default=1)
|
gold_pay_type = Column(Integer, default=1)
|
||||||
gift_id = Column(String(64), default="23643")
|
gift_id = Column(String(64), default="23643")
|
||||||
skin_id = Column(String(64), default="2942")
|
skin_id = Column(String(64), default="2942")
|
||||||
@@ -147,6 +151,19 @@ class DouyuGoodsSnapshot(Base):
|
|||||||
updated_at = Column(DateTime, default=_utcnow, onupdate=_utcnow)
|
updated_at = Column(DateTime, default=_utcnow, onupdate=_utcnow)
|
||||||
|
|
||||||
|
|
||||||
|
class DouyuEsportsGoodsSnapshot(Base):
|
||||||
|
"""斗鱼电竞手册皮肤商城商品快照"""
|
||||||
|
__tablename__ = "douyu_esports_goods_snapshot"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
commodity_id = Column(String(64), nullable=False, index=True)
|
||||||
|
name = Column(String(256), default="")
|
||||||
|
score = Column(Integer, nullable=True)
|
||||||
|
status = Column(String(32), default="")
|
||||||
|
raw = Column(JSON, nullable=True)
|
||||||
|
updated_at = Column(DateTime, default=_utcnow, onupdate=_utcnow)
|
||||||
|
|
||||||
|
|
||||||
class HuyaAccount(Base):
|
class HuyaAccount(Base):
|
||||||
"""虎牙账号"""
|
"""虎牙账号"""
|
||||||
__tablename__ = "huya_accounts"
|
__tablename__ = "huya_accounts"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from sqlalchemy.orm import Session, joinedload
|
|||||||
|
|
||||||
from ..database import SessionLocal, get_db
|
from ..database import SessionLocal, get_db
|
||||||
from ..deps import authenticate_websocket, get_current_user, require_permission
|
from ..deps import authenticate_websocket, get_current_user, require_permission
|
||||||
from ..models import Account, DouyuConfig, DouyuGoodsSnapshot, DouyuTask, User
|
from ..models import Account, DouyuConfig, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask, User
|
||||||
from ..permissions import user_has_permission
|
from ..permissions import user_has_permission
|
||||||
from ..schemas import (
|
from ..schemas import (
|
||||||
DouyuConfigOut,
|
DouyuConfigOut,
|
||||||
@@ -149,6 +149,10 @@ def _config_out(config: DouyuConfig) -> DouyuConfigOut:
|
|||||||
esports_manual_id=douyu_config_value("esports_manual_id", config.esports_manual_id),
|
esports_manual_id=douyu_config_value("esports_manual_id", config.esports_manual_id),
|
||||||
esports_act_alias=douyu_config_value("esports_act_alias", config.esports_act_alias),
|
esports_act_alias=douyu_config_value("esports_act_alias", config.esports_act_alias),
|
||||||
esports_amount=douyu_config_value("esports_amount", config.esports_amount),
|
esports_amount=douyu_config_value("esports_amount", config.esports_amount),
|
||||||
|
esports_chicken_gift_id=douyu_config_value("esports_chicken_gift_id", config.esports_chicken_gift_id),
|
||||||
|
esports_chicken_skin_id=douyu_config_value("esports_chicken_skin_id", config.esports_chicken_skin_id),
|
||||||
|
esports_firework_gift_id=douyu_config_value("esports_firework_gift_id", config.esports_firework_gift_id),
|
||||||
|
esports_firework_skin_id=douyu_config_value("esports_firework_skin_id", config.esports_firework_skin_id),
|
||||||
gold_pay_type=douyu_config_value("gold_pay_type", config.gold_pay_type),
|
gold_pay_type=douyu_config_value("gold_pay_type", config.gold_pay_type),
|
||||||
gift_id=douyu_config_value("gift_id", config.gift_id),
|
gift_id=douyu_config_value("gift_id", config.gift_id),
|
||||||
skin_id=douyu_config_value("skin_id", config.skin_id),
|
skin_id=douyu_config_value("skin_id", config.skin_id),
|
||||||
@@ -224,6 +228,16 @@ def list_goods(
|
|||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/esports-goods", response_model=list[DouyuGoodsOut])
|
||||||
|
def list_esports_goods(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current: User = Depends(require_permission("douyu:task")),
|
||||||
|
):
|
||||||
|
"""查看已缓存的电竞手册皮肤商城商品。"""
|
||||||
|
rows = db.query(DouyuEsportsGoodsSnapshot).order_by(DouyuEsportsGoodsSnapshot.id.asc()).all()
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
@router.post("/tasks/batch")
|
@router.post("/tasks/batch")
|
||||||
async def create_task_batch(
|
async def create_task_batch(
|
||||||
req: DouyuTaskBatchRequest,
|
req: DouyuTaskBatchRequest,
|
||||||
|
|||||||
@@ -545,6 +545,10 @@ class DouyuConfigOut(BaseModel):
|
|||||||
esports_manual_id: str = "1W0yGkOdDbAXb"
|
esports_manual_id: str = "1W0yGkOdDbAXb"
|
||||||
esports_act_alias: str = "20260226ZMGDG"
|
esports_act_alias: str = "20260226ZMGDG"
|
||||||
esports_amount: int = 3000
|
esports_amount: int = 3000
|
||||||
|
esports_chicken_gift_id: str = "24766"
|
||||||
|
esports_chicken_skin_id: str = "0"
|
||||||
|
esports_firework_gift_id: str = "24767"
|
||||||
|
esports_firework_skin_id: str = "3850"
|
||||||
gold_pay_type: int = 1
|
gold_pay_type: int = 1
|
||||||
gift_id: str = "23643"
|
gift_id: str = "23643"
|
||||||
skin_id: str = "2942"
|
skin_id: str = "2942"
|
||||||
@@ -563,6 +567,10 @@ class DouyuConfigOut(BaseModel):
|
|||||||
"esports_manual_id": self.esports_manual_id,
|
"esports_manual_id": self.esports_manual_id,
|
||||||
"esports_act_alias": self.esports_act_alias,
|
"esports_act_alias": self.esports_act_alias,
|
||||||
"esports_amount": self.esports_amount,
|
"esports_amount": self.esports_amount,
|
||||||
|
"esports_chicken_gift_id": self.esports_chicken_gift_id,
|
||||||
|
"esports_chicken_skin_id": self.esports_chicken_skin_id,
|
||||||
|
"esports_firework_gift_id": self.esports_firework_gift_id,
|
||||||
|
"esports_firework_skin_id": self.esports_firework_skin_id,
|
||||||
"gold_pay_type": self.gold_pay_type,
|
"gold_pay_type": self.gold_pay_type,
|
||||||
"gift_id": self.gift_id,
|
"gift_id": self.gift_id,
|
||||||
"skin_id": self.skin_id,
|
"skin_id": self.skin_id,
|
||||||
@@ -581,6 +589,10 @@ class DouyuConfigUpdate(BaseModel):
|
|||||||
esports_manual_id: Optional[str] = None
|
esports_manual_id: Optional[str] = None
|
||||||
esports_act_alias: Optional[str] = None
|
esports_act_alias: Optional[str] = None
|
||||||
esports_amount: Optional[int] = Field(None, ge=1)
|
esports_amount: Optional[int] = Field(None, ge=1)
|
||||||
|
esports_chicken_gift_id: Optional[str] = None
|
||||||
|
esports_chicken_skin_id: Optional[str] = None
|
||||||
|
esports_firework_gift_id: Optional[str] = None
|
||||||
|
esports_firework_skin_id: Optional[str] = None
|
||||||
gold_pay_type: Optional[int] = Field(None, ge=1, le=9)
|
gold_pay_type: Optional[int] = Field(None, ge=1, le=9)
|
||||||
gift_id: Optional[str] = None
|
gift_id: Optional[str] = None
|
||||||
skin_id: Optional[str] = None
|
skin_id: Optional[str] = None
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from sqlalchemy.orm import Session, joinedload
|
|||||||
from core.douyu import DouyuActivityClient, DouyuActivityError
|
from core.douyu import DouyuActivityClient, DouyuActivityError
|
||||||
|
|
||||||
from ..database import SessionLocal
|
from ..database import SessionLocal
|
||||||
from ..models import Account, DouyuGoodsSnapshot, DouyuTask
|
from ..models import Account, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask
|
||||||
from .douyu_service import (
|
from .douyu_service import (
|
||||||
DOUYU_CONFIG_FIELDS,
|
DOUYU_CONFIG_FIELDS,
|
||||||
account_uid,
|
account_uid,
|
||||||
@@ -382,6 +382,27 @@ class DouyuBatchRunner:
|
|||||||
row.updated_at = now
|
row.updated_at = now
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
def _upsert_esports_goods(self, db: Session, goods: list[dict]) -> None:
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
for raw in goods:
|
||||||
|
commodity_id = str(raw.get("commodityId") or raw.get("commodity_id") or "")
|
||||||
|
if not commodity_id:
|
||||||
|
continue
|
||||||
|
row = (
|
||||||
|
db.query(DouyuEsportsGoodsSnapshot)
|
||||||
|
.filter(DouyuEsportsGoodsSnapshot.commodity_id == commodity_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if row is None:
|
||||||
|
row = DouyuEsportsGoodsSnapshot(commodity_id=commodity_id)
|
||||||
|
db.add(row)
|
||||||
|
row.name = str(raw.get("commodityName") or raw.get("name") or "")
|
||||||
|
row.score = self._to_int(raw.get("score"))
|
||||||
|
row.status = str(raw.get("status") or "")
|
||||||
|
row.raw = raw
|
||||||
|
row.updated_at = now
|
||||||
|
db.commit()
|
||||||
|
|
||||||
def _config_info(self, db: Session) -> dict:
|
def _config_info(self, db: Session) -> dict:
|
||||||
config = ensure_douyu_config(db)
|
config = ensure_douyu_config(db)
|
||||||
return {field: douyu_config_value(field, getattr(config, field, None)) for field in DOUYU_CONFIG_FIELDS}
|
return {field: douyu_config_value(field, getattr(config, field, None)) for field in DOUYU_CONFIG_FIELDS}
|
||||||
@@ -921,6 +942,32 @@ class DouyuBatchRunner:
|
|||||||
account.updated_at = datetime.now(timezone.utc)
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
self._mark_task(db, task, "success", f"已刷新商品 {len(goods)} 个", {"goods_count": len(goods), "goods": goods})
|
self._mark_task(db, task, "success", f"已刷新商品 {len(goods)} 个", {"goods_count": len(goods), "goods": goods})
|
||||||
|
|
||||||
|
def _execute_refresh_esports_goods(
|
||||||
|
self,
|
||||||
|
db: Session,
|
||||||
|
task: DouyuTask,
|
||||||
|
account: Account,
|
||||||
|
cookie: str,
|
||||||
|
config: dict,
|
||||||
|
):
|
||||||
|
"""刷新电竞手册皮肤商城快照。"""
|
||||||
|
client = self._client(cookie)
|
||||||
|
result = client.list_esports_goods(
|
||||||
|
manual_id=str(config["esports_manual_id"]),
|
||||||
|
rid=str(config["room_id"]),
|
||||||
|
)
|
||||||
|
goods = result["goods"]
|
||||||
|
self._upsert_esports_goods(db, goods)
|
||||||
|
account.esports_bind_status = "esports_goods_refreshed"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
|
self._mark_task(
|
||||||
|
db,
|
||||||
|
task,
|
||||||
|
"success",
|
||||||
|
f"已刷新电竞皮肤 {len(goods)} 个",
|
||||||
|
{"goods_count": len(goods), "esports_store_score": result["score"], "goods": goods},
|
||||||
|
)
|
||||||
|
|
||||||
def _execute_get_bind_qr(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
def _execute_get_bind_qr(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||||
client = self._client(cookie)
|
client = self._client(cookie)
|
||||||
qr_act_alias = self._bind_qr_act_alias(config)
|
qr_act_alias = self._bind_qr_act_alias(config)
|
||||||
@@ -1492,6 +1539,147 @@ class DouyuBatchRunner:
|
|||||||
result,
|
result,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _execute_query_esports_points(
|
||||||
|
self,
|
||||||
|
db: Session,
|
||||||
|
task: DouyuTask,
|
||||||
|
account: Account,
|
||||||
|
cookie: str,
|
||||||
|
config: dict,
|
||||||
|
):
|
||||||
|
"""查询电竞手册积分。"""
|
||||||
|
manual_id = str(config.get("esports_manual_id") or "").strip()
|
||||||
|
if not manual_id:
|
||||||
|
self._mark_task(db, task, "failed", "请先配置电竞手册 manualID")
|
||||||
|
return
|
||||||
|
client = self._client(cookie)
|
||||||
|
result = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||||
|
account.esports_bind_status = "esports_points_queried"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
|
points = result["esports_points"]
|
||||||
|
self._mark_task(db, task, "success", f"电竞积分: {points if points is not None else '-'}", result)
|
||||||
|
|
||||||
|
def _execute_donate_esports_gift(
|
||||||
|
self,
|
||||||
|
db: Session,
|
||||||
|
task: DouyuTask,
|
||||||
|
account: Account,
|
||||||
|
cookie: str,
|
||||||
|
config: dict,
|
||||||
|
*,
|
||||||
|
gift_name: str,
|
||||||
|
config_gift_id_key: str,
|
||||||
|
config_skin_id_key: str,
|
||||||
|
):
|
||||||
|
"""赠送电竞手册任务礼物并刷新独立积分。"""
|
||||||
|
payload = self._task_payload(task)
|
||||||
|
try:
|
||||||
|
gift_count = max(1, int(payload.get("gift_count") or payload.get("count") or 1))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self._mark_task(db, task, "failed", "赠送数量必须是正整数")
|
||||||
|
return
|
||||||
|
|
||||||
|
manual_id = str(config.get("esports_manual_id") or "").strip()
|
||||||
|
gift_id = str(payload.get("gift_id") or config.get(config_gift_id_key) or "").strip()
|
||||||
|
skin_id = str(payload.get("skin_id") or config.get(config_skin_id_key) or "").strip()
|
||||||
|
room_id = str(payload.get("room_id") or config.get("room_id") or "").strip()
|
||||||
|
if not manual_id or not gift_id or not skin_id or not room_id:
|
||||||
|
self._mark_task(db, task, "failed", "请先完整配置电竞手册、房间和礼物参数")
|
||||||
|
return
|
||||||
|
|
||||||
|
client = self._client(cookie)
|
||||||
|
baseline_points = account.esports_points
|
||||||
|
try:
|
||||||
|
baseline = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||||
|
baseline_points = baseline["esports_points"]
|
||||||
|
db.commit()
|
||||||
|
except Exception as exc:
|
||||||
|
self._push_log("warning", f"赠送{gift_name}前刷新电竞积分失败: {exc}")
|
||||||
|
|
||||||
|
result = client.donate_esports_gift(
|
||||||
|
gift_name=gift_name,
|
||||||
|
gift_count=gift_count,
|
||||||
|
room_id=room_id,
|
||||||
|
gift_id=gift_id,
|
||||||
|
skin_id=skin_id,
|
||||||
|
)
|
||||||
|
result.update(
|
||||||
|
{
|
||||||
|
"gift_name": gift_name,
|
||||||
|
"gift_id": gift_id,
|
||||||
|
"skin_id": skin_id,
|
||||||
|
"gift_count": gift_count,
|
||||||
|
"esports_points_baseline": baseline_points,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
refresh_errors = []
|
||||||
|
try:
|
||||||
|
result.update(self._refresh_account_gold_balance(client, account))
|
||||||
|
except Exception as exc:
|
||||||
|
refresh_errors.append(f"鱼翅余额: {exc}")
|
||||||
|
try:
|
||||||
|
points_result = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||||
|
result.update(points_result)
|
||||||
|
result["esports_points_after_gift"] = points_result["esports_points"]
|
||||||
|
result["esports_points_changed"] = (
|
||||||
|
baseline_points is not None
|
||||||
|
and points_result["esports_points"] is not None
|
||||||
|
and points_result["esports_points"] != baseline_points
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
refresh_errors.append(f"电竞积分: {exc}")
|
||||||
|
if refresh_errors:
|
||||||
|
result["refresh_errors"] = refresh_errors
|
||||||
|
|
||||||
|
account.esports_bind_status = "esports_gift_donated"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
|
message = f"赠送{gift_name}成功: {gift_count}"
|
||||||
|
if account.gold_balance is not None:
|
||||||
|
message += f",鱼翅余额: {account.gold_balance}"
|
||||||
|
if account.esports_points is not None:
|
||||||
|
message += f",电竞积分: {account.esports_points}"
|
||||||
|
self._mark_task(db, task, "success", message, result)
|
||||||
|
|
||||||
|
def _execute_donate_esports_chicken_gift(
|
||||||
|
self,
|
||||||
|
db: Session,
|
||||||
|
task: DouyuTask,
|
||||||
|
account: Account,
|
||||||
|
cookie: str,
|
||||||
|
config: dict,
|
||||||
|
):
|
||||||
|
"""赠送冠军鸡腿。"""
|
||||||
|
self._execute_donate_esports_gift(
|
||||||
|
db,
|
||||||
|
task,
|
||||||
|
account,
|
||||||
|
cookie,
|
||||||
|
config,
|
||||||
|
gift_name="冠军鸡腿",
|
||||||
|
config_gift_id_key="esports_chicken_gift_id",
|
||||||
|
config_skin_id_key="esports_chicken_skin_id",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _execute_donate_esports_firework_gift(
|
||||||
|
self,
|
||||||
|
db: Session,
|
||||||
|
task: DouyuTask,
|
||||||
|
account: Account,
|
||||||
|
cookie: str,
|
||||||
|
config: dict,
|
||||||
|
):
|
||||||
|
"""赠送冠军烟花。"""
|
||||||
|
self._execute_donate_esports_gift(
|
||||||
|
db,
|
||||||
|
task,
|
||||||
|
account,
|
||||||
|
cookie,
|
||||||
|
config,
|
||||||
|
gift_name="冠军烟花",
|
||||||
|
config_gift_id_key="esports_firework_gift_id",
|
||||||
|
config_skin_id_key="esports_firework_skin_id",
|
||||||
|
)
|
||||||
|
|
||||||
def _execute_create_gold_qr(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
def _execute_create_gold_qr(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||||
payload = self._task_payload(task)
|
payload = self._task_payload(task)
|
||||||
amount = int(payload.get("amount") or payload.get("gold_amount") or 1)
|
amount = int(payload.get("amount") or payload.get("gold_amount") or 1)
|
||||||
@@ -1650,6 +1838,71 @@ class DouyuBatchRunner:
|
|||||||
{"goods": goods.raw if goods else None, **result},
|
{"goods": goods.raw if goods else None, **result},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _execute_exchange_esports_goods(
|
||||||
|
self,
|
||||||
|
db: Session,
|
||||||
|
task: DouyuTask,
|
||||||
|
account: Account,
|
||||||
|
cookie: str,
|
||||||
|
config: dict,
|
||||||
|
):
|
||||||
|
"""兑换电竞手册皮肤,并同步电竞积分。"""
|
||||||
|
payload = self._task_payload(task)
|
||||||
|
commodity_id = str(payload.get("commodity_id") or payload.get("commodityId") or "").strip()
|
||||||
|
if not commodity_id:
|
||||||
|
self._mark_task(db, task, "failed", "请选择电竞皮肤")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
quantity = max(1, int(payload.get("quantity") or payload.get("num") or 1))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self._mark_task(db, task, "failed", "兑换数量必须是正整数")
|
||||||
|
return
|
||||||
|
|
||||||
|
manual_id = str(config.get("esports_manual_id") or "").strip()
|
||||||
|
room_id = str(config.get("room_id") or "").strip()
|
||||||
|
if not manual_id or not room_id:
|
||||||
|
self._mark_task(db, task, "failed", "请先配置电竞手册 manualID 和房间 ID")
|
||||||
|
return
|
||||||
|
|
||||||
|
client = self._client(cookie)
|
||||||
|
baseline_points = account.esports_points
|
||||||
|
try:
|
||||||
|
baseline = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||||
|
baseline_points = baseline["esports_points"]
|
||||||
|
db.commit()
|
||||||
|
except Exception as exc:
|
||||||
|
self._push_log("warning", f"兑换电竞皮肤前刷新积分失败: {exc}")
|
||||||
|
|
||||||
|
result = client.exchange_esports_goods(
|
||||||
|
manual_id=manual_id,
|
||||||
|
rid=room_id,
|
||||||
|
commodity_id=commodity_id,
|
||||||
|
quantity=quantity,
|
||||||
|
)
|
||||||
|
goods = (
|
||||||
|
db.query(DouyuEsportsGoodsSnapshot)
|
||||||
|
.filter(DouyuEsportsGoodsSnapshot.commodity_id == commodity_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
result["goods"] = goods.raw if goods else None
|
||||||
|
result["esports_points_baseline"] = baseline_points
|
||||||
|
try:
|
||||||
|
points_result = self._refresh_esports_handbook(client, account, manual_id=manual_id)
|
||||||
|
result.update(points_result)
|
||||||
|
result["esports_points_after_exchange"] = points_result["esports_points"]
|
||||||
|
except Exception as exc:
|
||||||
|
result["esports_points_refresh_error"] = str(exc)
|
||||||
|
|
||||||
|
account.esports_bind_status = "esports_goods_exchanged"
|
||||||
|
account.updated_at = datetime.now(timezone.utc)
|
||||||
|
name = (goods.name if goods else "") or commodity_id
|
||||||
|
message = f"兑换电竞皮肤成功: {name}"
|
||||||
|
if quantity > 1:
|
||||||
|
message += f" x{quantity}"
|
||||||
|
if account.esports_points is not None:
|
||||||
|
message += f",电竞积分: {account.esports_points}"
|
||||||
|
self._mark_task(db, task, "success", message, result)
|
||||||
|
|
||||||
def _execute_query_game_name(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
def _execute_query_game_name(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):
|
||||||
client = self._client(cookie)
|
client = self._client(cookie)
|
||||||
query_aliases = self._query_bind_act_aliases(config)
|
query_aliases = self._query_bind_act_aliases(config)
|
||||||
@@ -1870,6 +2123,7 @@ class DouyuBatchRunner:
|
|||||||
|
|
||||||
handler = {
|
handler = {
|
||||||
"refresh_goods": self._execute_refresh_goods,
|
"refresh_goods": self._execute_refresh_goods,
|
||||||
|
"refresh_esports_goods": self._execute_refresh_esports_goods,
|
||||||
"get_bind_qr": self._execute_get_bind_qr,
|
"get_bind_qr": self._execute_get_bind_qr,
|
||||||
"confirm_bind": self._execute_confirm_bind,
|
"confirm_bind": self._execute_confirm_bind,
|
||||||
"create_elite_qr": self._execute_create_elite_qr,
|
"create_elite_qr": self._execute_create_elite_qr,
|
||||||
@@ -1878,10 +2132,14 @@ class DouyuBatchRunner:
|
|||||||
"query_esports_game_name": self._execute_query_esports_game_name,
|
"query_esports_game_name": self._execute_query_esports_game_name,
|
||||||
"confirm_esports_bind": self._execute_confirm_esports_bind,
|
"confirm_esports_bind": self._execute_confirm_esports_bind,
|
||||||
"create_esports_qr": self._execute_create_esports_qr,
|
"create_esports_qr": self._execute_create_esports_qr,
|
||||||
|
"query_esports_points": self._execute_query_esports_points,
|
||||||
|
"donate_esports_chicken_gift": self._execute_donate_esports_chicken_gift,
|
||||||
|
"donate_esports_firework_gift": self._execute_donate_esports_firework_gift,
|
||||||
"create_gold_qr": self._execute_create_gold_qr,
|
"create_gold_qr": self._execute_create_gold_qr,
|
||||||
"donate_elite_gift": self._execute_donate_elite_gift,
|
"donate_elite_gift": self._execute_donate_elite_gift,
|
||||||
"query_points": self._execute_query_points,
|
"query_points": self._execute_query_points,
|
||||||
"exchange_goods": self._execute_exchange_goods,
|
"exchange_goods": self._execute_exchange_goods,
|
||||||
|
"exchange_esports_goods": self._execute_exchange_esports_goods,
|
||||||
"query_game_name": self._execute_query_game_name,
|
"query_game_name": self._execute_query_game_name,
|
||||||
"query_change_bind_time": self._execute_query_change_bind_time,
|
"query_change_bind_time": self._execute_query_change_bind_time,
|
||||||
"query_limited_goods": self._execute_query_limited_goods,
|
"query_limited_goods": self._execute_query_limited_goods,
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ SUPPORTED_DOUYU_TASK_TYPES = {
|
|||||||
"query_esports_game_name": "查询最新游戏角色",
|
"query_esports_game_name": "查询最新游戏角色",
|
||||||
"confirm_esports_bind": "确认电竞手册绑定",
|
"confirm_esports_bind": "确认电竞手册绑定",
|
||||||
"create_esports_qr": "开通电竞手册30",
|
"create_esports_qr": "开通电竞手册30",
|
||||||
|
"query_esports_points": "查询电竞积分",
|
||||||
|
"donate_esports_chicken_gift": "赠送冠军鸡腿",
|
||||||
|
"donate_esports_firework_gift": "赠送冠军烟花",
|
||||||
|
"refresh_esports_goods": "刷新电竞皮肤商城",
|
||||||
|
"exchange_esports_goods": "兑换电竞皮肤",
|
||||||
"create_gold_qr": "充值鱼翅",
|
"create_gold_qr": "充值鱼翅",
|
||||||
"donate_elite_gift": "赠送精英令",
|
"donate_elite_gift": "赠送精英令",
|
||||||
"query_points": "一键查询积分",
|
"query_points": "一键查询积分",
|
||||||
@@ -47,6 +52,10 @@ DOUYU_CONFIG_DEFAULTS = {
|
|||||||
"esports_manual_id": "1W0yGkOdDbAXb",
|
"esports_manual_id": "1W0yGkOdDbAXb",
|
||||||
"esports_act_alias": "20260226ZMGDG",
|
"esports_act_alias": "20260226ZMGDG",
|
||||||
"esports_amount": 3000,
|
"esports_amount": 3000,
|
||||||
|
"esports_chicken_gift_id": "24766",
|
||||||
|
"esports_chicken_skin_id": "0",
|
||||||
|
"esports_firework_gift_id": "24767",
|
||||||
|
"esports_firework_skin_id": "3850",
|
||||||
"gold_pay_type": 1,
|
"gold_pay_type": 1,
|
||||||
"gift_id": "23643",
|
"gift_id": "23643",
|
||||||
"skin_id": "2942",
|
"skin_id": "2942",
|
||||||
@@ -160,7 +169,7 @@ def create_douyu_planned_tasks(
|
|||||||
raise ValueError("不支持的任务类型")
|
raise ValueError("不支持的任务类型")
|
||||||
|
|
||||||
accounts = visible_douyu_task_accounts(db, account_ids)
|
accounts = visible_douyu_task_accounts(db, account_ids)
|
||||||
if task_type == "refresh_goods" and accounts:
|
if task_type in {"refresh_goods", "refresh_esports_goods"} and accounts:
|
||||||
# 商品快照是全局数据,一个可用 CK 足够;没有 CK 时前端无法选账号创建任务。
|
# 商品快照是全局数据,一个可用 CK 足够;没有 CK 时前端无法选账号创建任务。
|
||||||
accounts = accounts[:1]
|
accounts = accounts[:1]
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const douyuApi = {
|
|||||||
getConfig: () => api.get<DouyuConfig, DouyuConfig>('/douyu/config'),
|
getConfig: () => api.get<DouyuConfig, DouyuConfig>('/douyu/config'),
|
||||||
updateConfig: (data: Partial<DouyuConfig>) => api.put<DouyuConfig, DouyuConfig>('/douyu/config', data),
|
updateConfig: (data: Partial<DouyuConfig>) => api.put<DouyuConfig, DouyuConfig>('/douyu/config', data),
|
||||||
listGoods: () => api.get<DouyuGoodsItem[], DouyuGoodsItem[]>('/douyu/goods'),
|
listGoods: () => api.get<DouyuGoodsItem[], DouyuGoodsItem[]>('/douyu/goods'),
|
||||||
|
listEsportsGoods: () => api.get<DouyuGoodsItem[], DouyuGoodsItem[]>('/douyu/esports-goods'),
|
||||||
createTasks: (data: DouyuTaskBatchRequest) =>
|
createTasks: (data: DouyuTaskBatchRequest) =>
|
||||||
api.post<DouyuTaskBatchResult, DouyuTaskBatchResult>('/douyu/tasks/batch', data),
|
api.post<DouyuTaskBatchResult, DouyuTaskBatchResult>('/douyu/tasks/batch', data),
|
||||||
listTasks: (batchId?: string) =>
|
listTasks: (batchId?: string) =>
|
||||||
|
|||||||
@@ -217,6 +217,10 @@ export interface DouyuConfig {
|
|||||||
esports_manual_id: string;
|
esports_manual_id: string;
|
||||||
esports_act_alias: string;
|
esports_act_alias: string;
|
||||||
esports_amount: number;
|
esports_amount: number;
|
||||||
|
esports_chicken_gift_id: string;
|
||||||
|
esports_chicken_skin_id: string;
|
||||||
|
esports_firework_gift_id: string;
|
||||||
|
esports_firework_skin_id: string;
|
||||||
gold_pay_type: number;
|
gold_pay_type: number;
|
||||||
gift_id: string;
|
gift_id: string;
|
||||||
skin_id: string;
|
skin_id: string;
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ const ESPORTS_QUICK_ACTIONS = [
|
|||||||
{ key: 'prepare_esports_bind', icon: <QrcodeOutlined /> },
|
{ key: 'prepare_esports_bind', icon: <QrcodeOutlined /> },
|
||||||
{ key: 'query_esports_game_name', icon: <SearchOutlined /> },
|
{ key: 'query_esports_game_name', icon: <SearchOutlined /> },
|
||||||
{ key: 'create_esports_qr', icon: <CreditCardOutlined /> },
|
{ key: 'create_esports_qr', icon: <CreditCardOutlined /> },
|
||||||
|
{ key: 'query_esports_points', icon: <SearchOutlined /> },
|
||||||
|
{ key: 'query_gold_balance', icon: <SearchOutlined /> },
|
||||||
|
{ key: 'refresh_esports_goods', icon: <ReloadOutlined /> },
|
||||||
|
{ key: 'exchange_esports_goods', icon: <ShoppingOutlined /> },
|
||||||
|
{ key: 'create_gold_qr', icon: <CreditCardOutlined /> },
|
||||||
|
{ key: 'donate_esports_chicken_gift', icon: <GiftOutlined /> },
|
||||||
|
{ key: 'donate_esports_firework_gift', icon: <GiftOutlined /> },
|
||||||
];
|
];
|
||||||
|
|
||||||
const ELITE_BIND_ACTION_KEYS = ['get_bind_qr', 'confirm_bind', 'query_game_name', 'query_change_bind_time'];
|
const ELITE_BIND_ACTION_KEYS = ['get_bind_qr', 'confirm_bind', 'query_game_name', 'query_change_bind_time'];
|
||||||
@@ -70,6 +77,13 @@ const ESPORTS_TASK_TYPES = new Set([
|
|||||||
'query_esports_game_name',
|
'query_esports_game_name',
|
||||||
'confirm_esports_bind',
|
'confirm_esports_bind',
|
||||||
'create_esports_qr',
|
'create_esports_qr',
|
||||||
|
'query_esports_points',
|
||||||
|
'query_gold_balance',
|
||||||
|
'refresh_esports_goods',
|
||||||
|
'exchange_esports_goods',
|
||||||
|
'create_gold_qr',
|
||||||
|
'donate_esports_chicken_gift',
|
||||||
|
'donate_esports_firework_gift',
|
||||||
]);
|
]);
|
||||||
const DOUYU_GOLD_AMOUNT_STORAGE_KEY = 'douyu_task_gold_amount';
|
const DOUYU_GOLD_AMOUNT_STORAGE_KEY = 'douyu_task_gold_amount';
|
||||||
const DOUYU_GIFT_COUNT_STORAGE_KEY = 'douyu_task_gift_count';
|
const DOUYU_GIFT_COUNT_STORAGE_KEY = 'douyu_task_gift_count';
|
||||||
@@ -97,6 +111,10 @@ const BIND_STATUS_LABELS: Record<string, string> = {
|
|||||||
esports_bound: '电竞已绑定',
|
esports_bound: '电竞已绑定',
|
||||||
esports_qr_created: '电竞待开通',
|
esports_qr_created: '电竞待开通',
|
||||||
esports_opened: '电竞已开通',
|
esports_opened: '电竞已开通',
|
||||||
|
esports_points_queried: '已查电竞积分',
|
||||||
|
esports_gift_donated: '已赠电竞礼物',
|
||||||
|
esports_goods_refreshed: '已刷电竞皮肤',
|
||||||
|
esports_goods_exchanged: '已兑电竞皮肤',
|
||||||
change_time_queried: '已查换绑',
|
change_time_queried: '已查换绑',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -309,7 +327,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
try {
|
try {
|
||||||
const [accResult, goodsResult, taskResult, cfgResult, typeResult] = await Promise.allSettled([
|
const [accResult, goodsResult, taskResult, cfgResult, typeResult] = await Promise.allSettled([
|
||||||
douyuApi.listAccounts(),
|
douyuApi.listAccounts(),
|
||||||
douyuApi.listGoods(),
|
isEsportsHandbook ? douyuApi.listEsportsGoods() : douyuApi.listGoods(),
|
||||||
douyuApi.listTasks(),
|
douyuApi.listTasks(),
|
||||||
canConfig ? douyuApi.getConfig() : Promise.resolve(null),
|
canConfig ? douyuApi.getConfig() : Promise.resolve(null),
|
||||||
douyuApi.taskTypes(),
|
douyuApi.taskTypes(),
|
||||||
@@ -475,14 +493,16 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
message.warning('当前已有批次在运行,请先停止或等待结束');
|
message.warning('当前已有批次在运行,请先停止或等待结束');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (taskType === 'exchange_goods' && !selectedGoodsId) {
|
if (['exchange_goods', 'exchange_esports_goods'].includes(taskType) && !selectedGoodsId) {
|
||||||
message.warning('请先选择兑换商品');
|
message.warning('请先选择兑换商品');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const payload: Record<string, unknown> = { ...extraPayload };
|
const payload: Record<string, unknown> = { ...extraPayload };
|
||||||
if (taskType === 'exchange_goods') payload.commodity_id = selectedGoodsId;
|
if (['exchange_goods', 'exchange_esports_goods'].includes(taskType)) payload.commodity_id = selectedGoodsId;
|
||||||
if (taskType === 'create_gold_qr') payload.amount = goldAmount;
|
if (taskType === 'create_gold_qr') payload.amount = goldAmount;
|
||||||
if (taskType === 'donate_elite_gift') payload.gift_count = giftCount;
|
if (['donate_elite_gift', 'donate_esports_chicken_gift', 'donate_esports_firework_gift'].includes(taskType)) {
|
||||||
|
payload.gift_count = giftCount;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = await douyuApi.createTasks({
|
const result = await douyuApi.createTasks({
|
||||||
account_ids: ids,
|
account_ids: ids,
|
||||||
@@ -798,10 +818,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
width: 80,
|
width: 80,
|
||||||
render: (v) => v ?? <Text type="secondary">-</Text>,
|
render: (v) => v ?? <Text type="secondary">-</Text>,
|
||||||
},
|
},
|
||||||
...(isEsportsHandbook ? [] : [{
|
{
|
||||||
title: '鱼翅', dataIndex: 'gold_balance', width: 70,
|
title: '鱼翅', dataIndex: 'gold_balance', width: 70,
|
||||||
render: (v: number | null) => v ?? <Text type="secondary">-</Text>,
|
render: (v: number | null) => v ?? <Text type="secondary">-</Text>,
|
||||||
}]),
|
},
|
||||||
{
|
{
|
||||||
title: '换绑时间', dataIndex: 'change_role_wait_time', width: 130,
|
title: '换绑时间', dataIndex: 'change_role_wait_time', width: 130,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
@@ -1001,7 +1021,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
const actionByKey = useMemo(() => new Map(quickActions.map((item) => [item.key, item])), [quickActions]);
|
const actionByKey = useMemo(() => new Map(quickActions.map((item) => [item.key, item])), [quickActions]);
|
||||||
const actionDisabled = (taskType: string) => {
|
const actionDisabled = (taskType: string) => {
|
||||||
if (batchBusy || selectedIds.length === 0) return true;
|
if (batchBusy || selectedIds.length === 0) return true;
|
||||||
if (taskType === 'exchange_goods') return !selectedGoodsId;
|
if (['exchange_goods', 'exchange_esports_goods'].includes(taskType)) return !selectedGoodsId;
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const renderActionButton = (taskType: string, type: 'default' | 'primary' = 'default') => {
|
const renderActionButton = (taskType: string, type: 'default' | 'primary' = 'default') => {
|
||||||
@@ -1166,6 +1186,75 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
</div>
|
</div>
|
||||||
{renderActionButton('create_esports_qr', 'primary')}
|
{renderActionButton('create_esports_qr', 'primary')}
|
||||||
</div>
|
</div>
|
||||||
|
<div style={sectionStyle}>
|
||||||
|
<div style={sectionTitleStyle}>
|
||||||
|
<SearchOutlined />
|
||||||
|
<span>查询</span>
|
||||||
|
</div>
|
||||||
|
<div style={actionGridStyle}>
|
||||||
|
{renderActionButton('query_esports_points')}
|
||||||
|
{renderActionButton('query_gold_balance')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={sectionStyle}>
|
||||||
|
<div style={sectionTitleStyle}>
|
||||||
|
<ShoppingOutlined />
|
||||||
|
<span>皮肤兑换</span>
|
||||||
|
</div>
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }} size={6}>
|
||||||
|
<Select
|
||||||
|
size="small"
|
||||||
|
value={selectedGoodsId || undefined}
|
||||||
|
onChange={setSelectedGoodsId}
|
||||||
|
options={goods.map((item) => ({ value: item.commodity_id, label: goodsLabel(item) }))}
|
||||||
|
placeholder="选择电竞皮肤"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
<div style={actionGridStyle}>
|
||||||
|
{renderActionButton('refresh_esports_goods')}
|
||||||
|
{renderActionButton('exchange_esports_goods', 'primary')}
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
<div style={sectionStyle}>
|
||||||
|
<div style={sectionTitleStyle}>
|
||||||
|
<CreditCardOutlined />
|
||||||
|
<span>充值与送礼</span>
|
||||||
|
</div>
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }} size={6}>
|
||||||
|
<Space.Compact style={{ width: '100%' }}>
|
||||||
|
<InputNumber
|
||||||
|
size="small"
|
||||||
|
min={1}
|
||||||
|
value={goldAmount}
|
||||||
|
onChange={(v) => setGoldAmount(v || 1)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<CreditCardOutlined />}
|
||||||
|
onClick={() => startTask('create_gold_qr')}
|
||||||
|
disabled={actionDisabled('create_gold_qr')}
|
||||||
|
>
|
||||||
|
充值鱼翅
|
||||||
|
</Button>
|
||||||
|
</Space.Compact>
|
||||||
|
<InputNumber
|
||||||
|
size="small"
|
||||||
|
min={1}
|
||||||
|
value={giftCount}
|
||||||
|
onChange={(v) => setGiftCount(v || 1)}
|
||||||
|
addonAfter="赠送数量"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
<div style={actionGridStyle}>
|
||||||
|
{renderActionButton('donate_esports_chicken_gift', 'primary')}
|
||||||
|
{renderActionButton('donate_esports_firework_gift', 'primary')}
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -1354,6 +1443,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
|||||||
{ label: '电竞手册 manualID', key: 'esports_manual_id' },
|
{ label: '电竞手册 manualID', key: 'esports_manual_id' },
|
||||||
{ label: '电竞手册活动', key: 'esports_act_alias' },
|
{ label: '电竞手册活动', key: 'esports_act_alias' },
|
||||||
{ label: '房间 ID', key: 'room_id' },
|
{ label: '房间 ID', key: 'room_id' },
|
||||||
|
{ label: '冠军鸡腿礼物 ID', key: 'esports_chicken_gift_id' },
|
||||||
|
{ label: '冠军鸡腿皮肤 ID', key: 'esports_chicken_skin_id' },
|
||||||
|
{ label: '冠军烟花礼物 ID', key: 'esports_firework_gift_id' },
|
||||||
|
{ label: '冠军烟花皮肤 ID', key: 'esports_firework_skin_id' },
|
||||||
] : [
|
] : [
|
||||||
{ label: 'manualID', key: 'manual_id' },
|
{ label: 'manualID', key: 'manual_id' },
|
||||||
{ label: 'RID', key: 'rid' },
|
{ label: 'RID', key: 'rid' },
|
||||||
|
|||||||
Reference in New Issue
Block a user