功能: 按用户同步斗鱼工作台

This commit is contained in:
yml2213
2026-08-13 15:47:07 +08:00
parent 090698e6b2
commit cf3fe68de3
14 changed files with 503 additions and 44 deletions
+4 -1
View File
@@ -5,7 +5,7 @@ from sqlalchemy import func, or_
from sqlalchemy.orm import Session, defer, joinedload
from ..database import get_db
from ..models import User, Account, AuditLog, LoginTask, DouyuTask
from ..models import User, Account, AuditLog, LoginTask, DouyuTask, DouyuWorkbenchAccount
from ..schemas import AccountBulkSelection, AccountBulkTag, AccountImport, AccountAssign, AccountTag, AccountOut, BatchAssign
from ..deps import get_current_user, require_permission
from ..permissions import user_has_permission
@@ -405,6 +405,7 @@ def batch_delete_accounts(
# 先删除关联的登录任务
db.query(LoginTask).filter(LoginTask.account_id.in_(ids)).delete(synchronize_session=False)
db.query(DouyuTask).filter(DouyuTask.account_id.in_(ids)).delete(synchronize_session=False)
db.query(DouyuWorkbenchAccount).filter(DouyuWorkbenchAccount.account_id.in_(ids)).delete(synchronize_session=False)
# 删除账号
deleted = db.query(Account).filter(Account.id.in_(ids)).delete(synchronize_session=False)
@@ -430,6 +431,7 @@ def batch_delete_accounts_selection(
db.query(LoginTask).filter(LoginTask.account_id.in_(ids)).delete(synchronize_session=False)
db.query(DouyuTask).filter(DouyuTask.account_id.in_(ids)).delete(synchronize_session=False)
db.query(DouyuWorkbenchAccount).filter(DouyuWorkbenchAccount.account_id.in_(ids)).delete(synchronize_session=False)
deleted = (
_visible_accounts_query(db, current)
.filter(Account.id.in_(ids))
@@ -457,6 +459,7 @@ def delete_account(
# 先删除关联的登录任务,避免外键约束失败
db.query(LoginTask).filter(LoginTask.account_id == account_id).delete(synchronize_session=False)
db.query(DouyuTask).filter(DouyuTask.account_id == account_id).delete(synchronize_session=False)
db.query(DouyuWorkbenchAccount).filter(DouyuWorkbenchAccount.account_id == account_id).delete(synchronize_session=False)
db.add(AuditLog(user_id=current.id, username=current.username,
action="account:delete", target=acc.username))
+73 -1
View File
@@ -12,7 +12,7 @@ from sqlalchemy.orm import Session, joinedload
from ..database import SessionLocal, get_db
from ..deps import authenticate_websocket, get_current_user, require_permission
from ..models import Account, DouyuConfig, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask, DouyuXpdGoodsSnapshot, User
from ..models import Account, DouyuConfig, DouyuEsportsGoodsSnapshot, DouyuGoodsSnapshot, DouyuTask, DouyuWorkbench, DouyuWorkbenchAccount, DouyuXpdGoodsSnapshot, User
from ..permissions import user_has_permission
from ..schemas import (
DouyuConfigOut,
@@ -21,11 +21,13 @@ from ..schemas import (
DouyuTaskAccountOut,
DouyuTaskBatchRequest,
DouyuTaskOut,
DouyuWorkbenchAccountsUpdate,
DouyuXpdGoodsOut,
)
from ..services.douyu_runner import DouyuBatchRunner, douyu_batch_registry
from ..services.douyu_service import (
DOUYU_CONFIG_FIELDS,
DOUYU_HANDBOOK_TASK_TYPES,
SUPPORTED_DOUYU_TASK_TYPES,
apply_douyu_config_defaults,
cleanup_orphan_douyu_tasks,
@@ -242,6 +244,67 @@ def list_task_account_ids(
return {"account_ids": account_ids, "total": len(account_ids)}
@router.get("/workbench-accounts")
def list_workbench_accounts(
handbook_scope: str = Query(..., pattern="^(elite|esports|peace)$"),
db: Session = Depends(get_db),
current: User = Depends(get_current_user),
):
"""返回当前用户在指定工作台启用的账号,供不同浏览器同步。"""
rows = (
db.query(DouyuWorkbenchAccount.account_id)
.join(Account, Account.id == DouyuWorkbenchAccount.account_id)
.filter(
DouyuWorkbenchAccount.user_id == current.id,
DouyuWorkbenchAccount.handbook_scope == handbook_scope,
)
)
if not _can_view_all(current):
rows = rows.filter(Account.assigned_to == current.id)
account_ids = [account_id for account_id, in rows.order_by(DouyuWorkbenchAccount.id.asc()).all()]
configured = db.query(DouyuWorkbench.id).filter(
DouyuWorkbench.user_id == current.id,
DouyuWorkbench.handbook_scope == handbook_scope,
).first() is not None
return {"account_ids": account_ids, "configured": configured}
@router.put("/workbench-accounts")
def update_workbench_accounts(
req: DouyuWorkbenchAccountsUpdate,
db: Session = Depends(get_db),
current: User = Depends(require_permission("douyu:task")),
):
"""用当前完整账号集合覆盖一个工作台,作为跨浏览器的同步状态。"""
account_ids = sorted(set(req.account_ids))
if any(account_id < 1 for account_id in account_ids):
raise HTTPException(status_code=400, detail="无效的账号ID")
if account_ids:
_require_task_account_access(db, current, account_ids)
workbench = db.query(DouyuWorkbench).filter(
DouyuWorkbench.user_id == current.id,
DouyuWorkbench.handbook_scope == req.handbook_scope,
).first()
if workbench is None:
db.add(DouyuWorkbench(user_id=current.id, handbook_scope=req.handbook_scope))
else:
workbench.updated_at = datetime.now(timezone.utc)
db.query(DouyuWorkbenchAccount).filter(
DouyuWorkbenchAccount.user_id == current.id,
DouyuWorkbenchAccount.handbook_scope == req.handbook_scope,
).delete(synchronize_session=False)
db.add_all([
DouyuWorkbenchAccount(
user_id=current.id,
handbook_scope=req.handbook_scope,
account_id=account_id,
)
for account_id in account_ids
])
db.commit()
return {"account_ids": account_ids, "success": True}
@router.get("/config", response_model=DouyuConfigOut)
def get_config(
db: Session = Depends(get_db),
@@ -324,6 +387,7 @@ async def create_task_batch(
db,
req.account_ids,
req.task_type,
req.handbook_scope,
current.id,
req.payload,
)
@@ -355,6 +419,7 @@ async def create_task_batch(
@router.get("/tasks")
def list_tasks(
batch_id: str | None = None,
handbook_scope: str | None = Query(None, pattern="^(elite|esports|peace)$"),
include_detail: bool = Query(False, description="是否返回完整任务结果(默认否,轮询请保持 false)"),
page: int | None = Query(None, ge=1),
page_size: int = Query(100, ge=1, le=500),
@@ -365,6 +430,13 @@ def list_tasks(
query = _visible_tasks_query(db, current)
if batch_id:
query = query.filter(DouyuTask.batch_id == batch_id)
if handbook_scope:
# 旧任务没有归属字段,按历史任务类型继续展示,但绝不会触发自动二维码弹窗。
query = query.filter(or_(
DouyuTask.handbook_scope == handbook_scope,
(DouyuTask.handbook_scope == "legacy")
& DouyuTask.task_type.in_(DOUYU_HANDBOOK_TASK_TYPES[handbook_scope]),
))
total = None
if page is not None:
total = query.enable_eagerloads(False).order_by(None).count()