新增虎牙账号和任务基础功能

This commit is contained in:
yml2213
2026-07-04 16:31:12 +08:00
parent 3df247e4e5
commit e1d47a85be
26 changed files with 4513 additions and 3 deletions
+250
View File
@@ -0,0 +1,250 @@
"""虎牙基础管理路由"""
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()