完善客服操作台权限与绑定二维码

This commit is contained in:
yml2213
2026-07-25 09:45:49 +08:00
parent 9471a5b4ff
commit df4297bee5
7 changed files with 115 additions and 52 deletions
+51 -7
View File
@@ -57,6 +57,42 @@ def _visible_task_accounts_query(db: Session, current: User):
raise HTTPException(status_code=403, detail="无权查看斗鱼账号")
def _visible_tasks_query(db: Session, current: User):
"""返回当前用户可查看的斗鱼任务查询。"""
query = db.query(DouyuTask).options(joinedload(DouyuTask.account))
if _can_view_all(current):
return query
if user_has_permission(current, "account:view_assigned"):
return query.join(DouyuTask.account).filter(Account.assigned_to == current.id)
raise HTTPException(status_code=403, detail="无权查看斗鱼任务")
def _require_task_account_access(db: Session, current: User, account_ids: list[int]) -> None:
"""确保任务只会提交到当前用户可操作的账号。"""
requested_ids = set(account_ids)
query = db.query(Account.id).filter(Account.id.in_(requested_ids))
if not _can_view_all(current):
if not user_has_permission(current, "account:view_assigned"):
raise HTTPException(status_code=403, detail="无权操作斗鱼账号")
query = query.filter(Account.assigned_to == current.id)
allowed_ids = {account_id for account_id, in query.all()}
if allowed_ids != requested_ids:
raise HTTPException(status_code=403, detail="包含无权操作的斗鱼账号")
def _require_batch_owner(db: Session, current: User, batch_id: str) -> None:
"""客服只能停止或订阅自己创建的任务批次。"""
if _can_view_all(current):
return
exists = (
db.query(DouyuTask.id)
.filter(DouyuTask.batch_id == batch_id, DouyuTask.created_by == current.id)
.first()
)
if not exists:
raise HTTPException(status_code=403, detail="无权操作该斗鱼任务批次")
def _account_out(account: Account) -> DouyuTaskAccountOut:
return DouyuTaskAccountOut(
id=account.id,
@@ -187,6 +223,7 @@ async def create_task_batch(
"""创建斗鱼任务记录并启动后台执行器。"""
if not req.account_ids:
raise HTTPException(status_code=400, detail="请选择斗鱼账号")
_require_task_account_access(db, current, req.account_ids)
cleanup_orphan_douyu_tasks(
db,
@@ -241,7 +278,7 @@ def list_tasks(
statuses=("pending", "running"),
message="任务已中断(无执行器接管)",
)
query = db.query(DouyuTask).options(joinedload(DouyuTask.account))
query = _visible_tasks_query(db, current)
if batch_id:
query = query.filter(DouyuTask.batch_id == batch_id)
rows = query.order_by(DouyuTask.id.desc()).limit(300).all()
@@ -255,12 +292,7 @@ def get_task(
current: User = Depends(require_permission("douyu:task")),
):
"""获取单条斗鱼任务详情。"""
task = (
db.query(DouyuTask)
.options(joinedload(DouyuTask.account))
.filter(DouyuTask.id == task_id)
.first()
)
task = _visible_tasks_query(db, current).filter(DouyuTask.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="任务不存在")
return _task_out(task)
@@ -273,6 +305,7 @@ def stop_batch(
current: User = Depends(require_permission("douyu:task")),
):
"""停止正在运行的斗鱼批次。"""
_require_batch_owner(db, current, batch_id)
batch = douyu_batch_registry.get(batch_id)
if batch:
if batch.get("finished"):
@@ -297,6 +330,17 @@ async def ws_douyu_logs(websocket: WebSocket, batch_id: str):
if not user:
await websocket.close(code=1008, reason="未授权")
return
if not user_has_permission(user, "douyu:task"):
await websocket.close(code=1008, reason="无权限")
return
db = SessionLocal()
try:
_require_batch_owner(db, user, batch_id)
except HTTPException:
await websocket.close(code=1008, reason="无权访问该任务批次")
return
finally:
db.close()
await websocket.accept()
batch = douyu_batch_registry.get(batch_id)
+51 -7
View File
@@ -103,6 +103,42 @@ def _visible_huya_accounts_query(db: Session, current: User):
raise HTTPException(status_code=403, detail="无权查看虎牙账号")
def _visible_huya_tasks_query(db: Session, current: User):
"""返回当前用户可查看的虎牙任务查询。"""
query = db.query(HuyaTask).options(joinedload(HuyaTask.account))
if _can_view_huya_all(current):
return query
if _can_view_huya_assigned(current):
return query.join(HuyaTask.account).filter(HuyaAccount.assigned_to == current.id)
raise HTTPException(status_code=403, detail="无权查看虎牙任务")
def _require_huya_task_account_access(db: Session, current: User, account_ids: list[int]) -> None:
"""确保任务只会提交到当前用户可操作的虎牙账号。"""
requested_ids = set(account_ids)
query = db.query(HuyaAccount.id).filter(HuyaAccount.id.in_(requested_ids))
if not _can_view_huya_all(current):
if not _can_view_huya_assigned(current):
raise HTTPException(status_code=403, detail="无权操作虎牙账号")
query = query.filter(HuyaAccount.assigned_to == current.id)
allowed_ids = {account_id for account_id, in query.all()}
if allowed_ids != requested_ids:
raise HTTPException(status_code=403, detail="包含无权操作的虎牙账号")
def _require_huya_batch_owner(db: Session, current: User, batch_id: str) -> None:
"""客服只能停止或订阅自己创建的任务批次。"""
if _can_view_huya_all(current):
return
exists = (
db.query(HuyaTask.id)
.filter(HuyaTask.batch_id == batch_id, HuyaTask.created_by == current.id)
.first()
)
if not exists:
raise HTTPException(status_code=403, detail="无权操作该虎牙任务批次")
def _fmt_cookie_preview(cookie: str) -> str:
if not cookie:
return ""
@@ -1148,6 +1184,7 @@ async def create_task_batch(
"""创建虎牙任务记录并启动后台执行器。"""
if not req.account_ids:
raise HTTPException(status_code=400, detail="请选择虎牙账号")
_require_huya_task_account_access(db, current, req.account_ids)
# 先清掉没有执行器的历史 running/pending,避免前端被假活跃批次锁死。
# 不清理 planned:避免与刚创建的新任务产生竞态。
@@ -1205,7 +1242,7 @@ def list_tasks(
statuses=("pending", "running"),
message="任务已中断(无执行器接管)",
)
query = db.query(HuyaTask).options(joinedload(HuyaTask.account))
query = _visible_huya_tasks_query(db, current)
if batch_id:
query = query.filter(HuyaTask.batch_id == batch_id)
tasks = query.order_by(HuyaTask.id.desc()).limit(300).all()
@@ -1219,12 +1256,7 @@ def get_task(
current: User = Depends(require_permission("huya:task")),
):
"""获取单条虎牙任务详情(含二维码图片)。"""
task = (
db.query(HuyaTask)
.options(joinedload(HuyaTask.account))
.filter(HuyaTask.id == task_id)
.first()
)
task = _visible_huya_tasks_query(db, current).filter(HuyaTask.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="任务不存在")
return _task_out(task, include_images=True)
@@ -1237,6 +1269,7 @@ def stop_batch(
current: User = Depends(require_permission("huya:task")),
):
"""停止正在运行的虎牙批次。"""
_require_huya_batch_owner(db, current, batch_id)
batch = huya_batch_registry.get(batch_id)
if batch:
if batch.get("finished"):
@@ -1269,6 +1302,17 @@ async def ws_huya_logs(websocket: WebSocket, batch_id: str):
if not user:
await websocket.close(code=1008, reason="未授权")
return
if not _has_huya_perm(user, "huya:task"):
await websocket.close(code=1008, reason="无权限")
return
db = SessionLocal()
try:
_require_huya_batch_owner(db, user, batch_id)
except HTTPException:
await websocket.close(code=1008, reason="无权访问该任务批次")
return
finally:
db.close()
await websocket.accept()
batch = huya_batch_registry.get(batch_id)
+1 -1
View File
@@ -85,7 +85,7 @@ def update_user(
user.is_active = req.is_active
if req.remark is not None:
user.remark = req.remark
if req.custom_permissions is not None:
if "custom_permissions" in req.model_fields_set:
user.custom_permissions = req.custom_permissions
db.commit()