优化分配功能
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user