优化分配功能

This commit is contained in:
yml2213
2026-06-22 16:23:34 +08:00
parent f94b323ad8
commit 48b6a1623b
8 changed files with 530 additions and 4 deletions
+103 -2
View File
@@ -6,10 +6,11 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from ..database import get_db
from ..models import User, Account, AuditLog
from ..schemas import AccountImport, AccountAssign, AccountTag, AccountOut
from ..models import User, Account, AuditLog, LoginTask
from ..schemas import AccountImport, AccountAssign, AccountTag, AccountOut, BatchAssign
from ..deps import get_current_user, require_permission
from ..permissions import has_permission
from sqlalchemy import func
router = APIRouter(prefix="/api/accounts", tags=["账号管理"])
@@ -26,16 +27,30 @@ def _split_account_line(line: str) -> list[str]:
return line.split()
def _cookie_account_ids_query(db: Session):
"""返回有成功登录记录(cookie非空)的账号ID子查询。"""
return db.query(LoginTask.account_id).filter(
LoginTask.status == 'success',
LoginTask.cookie != '',
LoginTask.cookie.isnot(None),
).distinct()
@router.get("", response_model=list[AccountOut])
def list_accounts(
assigned_only: bool = Query(False),
tag: str = Query(None),
has_cookie: bool = Query(False),
db: Session = Depends(get_db),
current: User = Depends(get_current_user),
):
"""列表:按角色返回不同字段和范围。"""
query = db.query(Account)
# 只展示已成功登录过的账号
if has_cookie:
query = query.filter(Account.id.in_(_cookie_account_ids_query(db)))
# 权限控制:客服只能看分配给自己的
if not has_permission(current.role, "account:view_all"):
if has_permission(current.role, "account:view_assigned"):
@@ -135,6 +150,15 @@ def assign_account(
if target.role != "support":
raise HTTPException(status_code=400, detail="只能分配给客服角色")
# 只能分配已成功登录过的账号(有cookie)
has_success = db.query(LoginTask).filter(
LoginTask.account_id == account_id,
LoginTask.status == 'success',
LoginTask.cookie != '',
).first()
if not has_success:
raise HTTPException(status_code=400, detail="该账号尚未成功登录,无法分配")
acc.assigned_to = req.assigned_to
db.add(AuditLog(user_id=current.id, username=current.username,
action="account:assign", target=acc.username))
@@ -142,6 +166,83 @@ def assign_account(
return {"message": "已分配", "success": True}
@router.post("/batch-assign")
def batch_assign_accounts(
req: BatchAssign,
db: Session = Depends(get_db),
current: User = Depends(require_permission("account:assign")),
):
"""批量分配/取消分配账号给客服。"""
if not req.account_ids:
raise HTTPException(status_code=400, detail="请选择账号")
# 验证目标用户
if req.assigned_to is not None:
target = db.query(User).filter(User.id == req.assigned_to).first()
if not target:
raise HTTPException(status_code=404, detail="目标用户不存在")
if target.role != "support":
raise HTTPException(status_code=400, detail="只能分配给客服角色")
# 只能分配已成功登录过的账号(有cookie)
cookie_ids_query = _cookie_account_ids_query(db).subquery()
invalid_ids = db.query(Account.id).filter(
Account.id.in_(req.account_ids),
Account.id.notin_(cookie_ids_query),
).all()
if invalid_ids:
names = db.query(Account.username).filter(Account.id.in_([i[0] for i in invalid_ids])).all()
name_list = ', '.join([n[0] for n in names[:5]])
suffix = '...' if len(invalid_ids) > 5 else ''
raise HTTPException(
status_code=400,
detail=f"以下账号尚未成功登录,无法分配:{name_list}{suffix}",
)
count = db.query(Account).filter(Account.id.in_(req.account_ids)).update(
{Account.assigned_to: req.assigned_to}, synchronize_session=False
)
db.add(AuditLog(
user_id=current.id, username=current.username,
action="account:assign",
target=f"批量{'分配' if req.assigned_to else '取消分配'}{count}个账号"
))
db.commit()
action = "分配" if req.assigned_to else "取消分配"
return {"message": f"已批量{action} {count} 个账号", "success": True, "count": count}
@router.get("/assignments/summary")
def assignments_summary(
db: Session = Depends(get_db),
current: User = Depends(require_permission("account:assign")),
):
"""分配概览:每个客服分配了多少账号(仅统计已成功登录的账号)。"""
cookie_subq = _cookie_account_ids_query(db).subquery()
cookie_accounts = db.query(Account).filter(Account.id.in_(cookie_subq)).subquery()
results = (
db.query(User.id, User.username, func.count(cookie_accounts.c.id).label("count"))
.outerjoin(cookie_accounts, cookie_accounts.c.assigned_to == User.id)
.filter(User.role == "support")
.group_by(User.id, User.username)
.order_by(func.count(cookie_accounts.c.id).desc())
.all()
)
total_unassigned = (
db.query(func.count(cookie_accounts.c.id))
.filter(cookie_accounts.c.assigned_to.is_(None))
.scalar()
) or 0
return {
"support_users": [
{"id": uid, "username": uname, "assigned_count": cnt}
for uid, uname, cnt in results
],
"unassigned_count": total_unassigned,
}
@router.put("/{account_id}/tag")
def set_account_tag(
account_id: int,