251 lines
8.2 KiB
Python
251 lines
8.2 KiB
Python
"""虎牙基础管理路由"""
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, WebSocket
|
|
from sqlalchemy.orm import Session, joinedload
|
|
|
|
from ..database import get_db
|
|
from ..deps import authenticate_websocket, require_permission
|
|
from ..models import HuyaAccount, HuyaConfig, HuyaGoodsSnapshot, HuyaTask, User
|
|
from ..schemas import (
|
|
HuyaAccountOut,
|
|
HuyaConfigOut,
|
|
HuyaConfigUpdate,
|
|
HuyaCookieImport,
|
|
HuyaGoodsOut,
|
|
HuyaTaskBatchRequest,
|
|
HuyaTaskOut,
|
|
)
|
|
from ..services.huya_service import (
|
|
SUPPORTED_TASK_TYPES,
|
|
create_planned_tasks,
|
|
ensure_huya_config,
|
|
import_huya_cookies,
|
|
)
|
|
|
|
|
|
router = APIRouter(prefix="/api/huya", tags=["虎牙"])
|
|
|
|
|
|
def _fmt_cookie_preview(cookie: str) -> str:
|
|
if not cookie:
|
|
return ""
|
|
return cookie[:50] + "..." if len(cookie) > 50 else cookie
|
|
|
|
|
|
def _account_out(account: HuyaAccount) -> HuyaAccountOut:
|
|
cookie = account.cookie or ""
|
|
return HuyaAccountOut(
|
|
id=account.id,
|
|
uid=account.uid or "",
|
|
yyuid=account.yyuid or "",
|
|
username=account.username or "",
|
|
nickname=account.nickname or "",
|
|
cookie=cookie,
|
|
cookie_preview=_fmt_cookie_preview(cookie),
|
|
tag=account.tag or "",
|
|
remark=account.remark or "",
|
|
status=account.status or "",
|
|
points=account.points,
|
|
game_name=account.game_name or "",
|
|
game_channel=account.game_channel or "",
|
|
game_phone=account.game_phone or "",
|
|
assigned_to=account.assigned_to,
|
|
assigned_username=account.assigned_user.username if account.assigned_user else None,
|
|
created_at=account.created_at,
|
|
updated_at=account.updated_at,
|
|
)
|
|
|
|
|
|
def _task_out(task: HuyaTask) -> HuyaTaskOut:
|
|
account = task.account
|
|
return HuyaTaskOut(
|
|
id=task.id,
|
|
batch_id=task.batch_id,
|
|
account_id=task.account_id,
|
|
account_uid=account.uid if account else "",
|
|
account_nickname=account.nickname if account else "",
|
|
task_type=task.task_type,
|
|
status=task.status or "",
|
|
message=task.message or "",
|
|
result=task.result,
|
|
created_by=task.created_by,
|
|
created_at=task.created_at,
|
|
finished_at=task.finished_at,
|
|
)
|
|
|
|
|
|
@router.get("/task-types")
|
|
def task_types(current: User = Depends(require_permission("huya:task"))):
|
|
"""返回当前规划的虎牙任务类型。"""
|
|
return SUPPORTED_TASK_TYPES
|
|
|
|
|
|
@router.get("/accounts", response_model=list[HuyaAccountOut])
|
|
def list_accounts(
|
|
tag: str | None = Query(None),
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:account")),
|
|
):
|
|
"""查看虎牙 CK 账号。"""
|
|
query = db.query(HuyaAccount).options(joinedload(HuyaAccount.assigned_user))
|
|
if tag:
|
|
query = query.filter(HuyaAccount.tag == tag)
|
|
accounts = query.order_by(HuyaAccount.id.desc()).all()
|
|
return [_account_out(account) for account in accounts]
|
|
|
|
|
|
@router.post("/accounts/import-cookies")
|
|
def import_cookies(
|
|
req: HuyaCookieImport,
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:account")),
|
|
):
|
|
"""粘贴并导入虎牙 Cookie。"""
|
|
count, skipped = import_huya_cookies(db, req.text, req.tag)
|
|
return {
|
|
"message": f"导入/更新 {count} 条,跳过 {skipped} 条",
|
|
"success": True,
|
|
"count": count,
|
|
"skipped": skipped,
|
|
}
|
|
|
|
|
|
@router.delete("/accounts/batch")
|
|
def delete_accounts_batch(
|
|
account_ids: str = Query(..., description="逗号分隔的虎牙账号ID"),
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:account")),
|
|
):
|
|
"""批量删除虎牙 CK 账号及任务记录。"""
|
|
ids = [int(x) for x in account_ids.split(",") if x.strip().isdigit()]
|
|
if not ids:
|
|
raise HTTPException(status_code=400, detail="无效的账号ID")
|
|
db.query(HuyaTask).filter(HuyaTask.account_id.in_(ids)).delete(synchronize_session=False)
|
|
deleted = db.query(HuyaAccount).filter(HuyaAccount.id.in_(ids)).delete(synchronize_session=False)
|
|
db.commit()
|
|
return {"message": f"已删除 {deleted} 个虎牙账号", "deleted": deleted, "success": True}
|
|
|
|
|
|
@router.delete("/accounts/{account_id}")
|
|
def delete_account(
|
|
account_id: int,
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:account")),
|
|
):
|
|
"""删除单个虎牙 CK 账号。"""
|
|
account = db.query(HuyaAccount).filter(HuyaAccount.id == account_id).first()
|
|
if not account:
|
|
raise HTTPException(status_code=404, detail="账号不存在")
|
|
db.query(HuyaTask).filter(HuyaTask.account_id == account_id).delete(synchronize_session=False)
|
|
db.delete(account)
|
|
db.commit()
|
|
return {"message": "已删除", "success": True}
|
|
|
|
|
|
@router.get("/config", response_model=HuyaConfigOut)
|
|
def get_config(
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:config")),
|
|
):
|
|
"""获取虎牙配置。"""
|
|
config = ensure_huya_config(db)
|
|
return HuyaConfigOut(
|
|
room_pid=config.room_pid or "",
|
|
sid=config.sid or "",
|
|
outer_act_id=config.outer_act_id or "9504",
|
|
bind_act_id=config.bind_act_id or "17096",
|
|
pay_channel=config.pay_channel or "Zfb",
|
|
updated_at=config.updated_at,
|
|
)
|
|
|
|
|
|
@router.put("/config", response_model=HuyaConfigOut)
|
|
def update_config(
|
|
req: HuyaConfigUpdate,
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:config")),
|
|
):
|
|
"""更新虎牙配置。"""
|
|
config = ensure_huya_config(db)
|
|
for field in ("room_pid", "sid", "outer_act_id", "bind_act_id", "pay_channel"):
|
|
value = getattr(req, field)
|
|
if value is not None:
|
|
setattr(config, field, value.strip())
|
|
config.updated_at = datetime.now(timezone.utc)
|
|
db.commit()
|
|
db.refresh(config)
|
|
return HuyaConfigOut(
|
|
room_pid=config.room_pid or "",
|
|
sid=config.sid or "",
|
|
outer_act_id=config.outer_act_id or "9504",
|
|
bind_act_id=config.bind_act_id or "17096",
|
|
pay_channel=config.pay_channel or "Zfb",
|
|
updated_at=config.updated_at,
|
|
)
|
|
|
|
|
|
@router.get("/goods", response_model=list[HuyaGoodsOut])
|
|
def list_goods(
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:task")),
|
|
):
|
|
"""查看已缓存的虎牙商品快照。"""
|
|
rows = db.query(HuyaGoodsSnapshot).order_by(HuyaGoodsSnapshot.updated_at.desc()).all()
|
|
return rows
|
|
|
|
|
|
@router.post("/tasks/batch")
|
|
def create_task_batch(
|
|
req: HuyaTaskBatchRequest,
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:task")),
|
|
):
|
|
"""创建虎牙任务记录,真实执行器后续接入。"""
|
|
if not req.account_ids:
|
|
raise HTTPException(status_code=400, detail="请选择虎牙账号")
|
|
try:
|
|
batch_id, count = create_planned_tasks(
|
|
db,
|
|
req.account_ids,
|
|
req.task_type,
|
|
current.id,
|
|
req.payload,
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
if count == 0:
|
|
raise HTTPException(status_code=400, detail="没有有效的虎牙账号")
|
|
return {"batch_id": batch_id, "count": count, "success": True}
|
|
|
|
|
|
@router.get("/tasks", response_model=list[HuyaTaskOut])
|
|
def list_tasks(
|
|
batch_id: str | None = None,
|
|
db: Session = Depends(get_db),
|
|
current: User = Depends(require_permission("huya:task")),
|
|
):
|
|
"""查看虎牙任务记录。"""
|
|
query = db.query(HuyaTask).options(joinedload(HuyaTask.account))
|
|
if batch_id:
|
|
query = query.filter(HuyaTask.batch_id == batch_id)
|
|
tasks = query.order_by(HuyaTask.id.desc()).limit(300).all()
|
|
return [_task_out(task) for task in tasks]
|
|
|
|
|
|
@router.websocket("/ws/{batch_id}")
|
|
async def ws_huya_logs(websocket: WebSocket, batch_id: str):
|
|
"""虎牙实时日志占位通道。"""
|
|
user = authenticate_websocket(websocket)
|
|
if not user:
|
|
await websocket.close(code=1008, reason="未授权")
|
|
return
|
|
await websocket.accept()
|
|
await websocket.send_json({
|
|
"level": "warning",
|
|
"message": f"虎牙批次 {batch_id} 已创建,真实执行器尚未接入",
|
|
})
|
|
await websocket.send_json({"level": "result", "message": ""})
|
|
await websocket.close()
|