支持账号按筛选全量批量操作

This commit is contained in:
yml2213
2026-07-25 10:50:56 +08:00
parent 09efb23f58
commit 10f298b4e1
8 changed files with 490 additions and 112 deletions
+153 -23
View File
@@ -23,10 +23,22 @@ from core.sms_provider import parse_sms_lines
from ..database import SessionLocal, get_db
from ..deps import authenticate_websocket, get_current_user, require_permission
from ..models import HuyaAccount, HuyaConfig, HuyaGoodsSnapshot, HuyaRechargeGoodsSnapshot, HuyaTask, ProxyConfig, User
from ..models import (
HuyaAccount,
HuyaConfig,
HuyaGoodsSnapshot,
HuyaRechargeGoodsSnapshot,
HuyaRegisterItem,
HuyaRegisterSuccessLog,
HuyaTask,
ProxyConfig,
User,
)
from ..permissions import user_has_permission
from ..schemas import (
AccountAssign,
AccountBulkSelection,
AccountBulkTag,
AccountTag,
BatchAssign,
HuyaAccountOut,
@@ -103,6 +115,89 @@ def _visible_huya_accounts_query(db: Session, current: User):
raise HTTPException(status_code=403, detail="无权查看虎牙账号")
def _filter_huya_accounts_query(
query,
current: User,
*,
assigned_only: bool = False,
tag: str | None = None,
has_cookie: bool = False,
search: str = "",
):
"""复用虎牙账号列表筛选条件,供分页和批量操作保持一致。"""
if assigned_only and _can_view_huya_all(current):
query = query.filter(HuyaAccount.assigned_to.isnot(None))
if tag:
query = query.filter(HuyaAccount.tag == tag)
if has_cookie:
query = query.filter(HuyaAccount.cookie != "")
search_text = (search or "").strip()
if search_text:
pattern = f"%{search_text}%"
query = query.filter(or_(
HuyaAccount.uid.ilike(pattern),
HuyaAccount.yyuid.ilike(pattern),
HuyaAccount.username.ilike(pattern),
HuyaAccount.nickname.ilike(pattern),
HuyaAccount.tag.ilike(pattern),
HuyaAccount.game_name.ilike(pattern),
HuyaAccount.game_phone.ilike(pattern),
))
return query
def _selected_huya_account_ids(db: Session, current: User, req: AccountBulkSelection) -> list[int]:
"""解析虎牙批量操作目标:当前筛选全部或显式选择的 ID。"""
base_query = _visible_huya_accounts_query(db, current)
if req.all_matching:
rows = (
_filter_huya_accounts_query(
base_query,
current,
assigned_only=req.assigned_only,
tag=req.tag,
has_cookie=req.has_cookie,
search=req.search,
)
.with_entities(HuyaAccount.id)
.order_by(None)
.all()
)
return [account_id for account_id, in rows]
seen = set()
requested_ids = []
for account_id in req.account_ids:
if account_id not in seen:
seen.add(account_id)
requested_ids.append(account_id)
if not requested_ids:
return []
rows = (
base_query
.filter(HuyaAccount.id.in_(requested_ids))
.with_entities(HuyaAccount.id)
.order_by(None)
.all()
)
allowed = {account_id for account_id, in rows}
return [account_id for account_id in requested_ids if account_id in allowed]
def _clear_huya_account_references(db: Session, account_ids: list[int]) -> None:
"""删除账号前保留注册历史,把历史流水中的账号引用置空。"""
if not account_ids:
return
db.query(HuyaRegisterItem).filter(HuyaRegisterItem.account_id.in_(account_ids)).update(
{HuyaRegisterItem.account_id: None},
synchronize_session=False,
)
db.query(HuyaRegisterSuccessLog).filter(HuyaRegisterSuccessLog.account_id.in_(account_ids)).update(
{HuyaRegisterSuccessLog.account_id: None},
synchronize_session=False,
)
def _visible_huya_tasks_query(db: Session, current: User):
"""返回当前用户可查看的虎牙任务查询。"""
query = db.query(HuyaTask).options(joinedload(HuyaTask.account))
@@ -305,25 +400,14 @@ def list_accounts(
current: User = Depends(get_current_user),
):
"""查看虎牙 CK 账号。"""
query = _visible_huya_accounts_query(db, current)
if assigned_only and _can_view_huya_all(current):
query = query.filter(HuyaAccount.assigned_to.isnot(None))
if tag:
query = query.filter(HuyaAccount.tag == tag)
if has_cookie:
query = query.filter(HuyaAccount.cookie != "")
search_text = (search or "").strip()
if search_text:
pattern = f"%{search_text}%"
query = query.filter(or_(
HuyaAccount.uid.ilike(pattern),
HuyaAccount.yyuid.ilike(pattern),
HuyaAccount.username.ilike(pattern),
HuyaAccount.nickname.ilike(pattern),
HuyaAccount.tag.ilike(pattern),
HuyaAccount.game_name.ilike(pattern),
HuyaAccount.game_phone.ilike(pattern),
))
query = _filter_huya_accounts_query(
_visible_huya_accounts_query(db, current),
current,
assigned_only=assigned_only,
tag=tag,
has_cookie=has_cookie,
search=search,
)
total = None
if page is not None:
@@ -694,12 +778,13 @@ def password_login_selected_accounts(
):
"""对已导入的虎牙账号执行密码登录。"""
_require_huya_perm(current, "huya:import")
if not req.account_ids:
selected_ids = _selected_huya_account_ids(db, current, req)
if not selected_ids:
raise HTTPException(status_code=400, detail="请选择虎牙账号")
accounts = (
_visible_huya_accounts_query(db, current)
.filter(HuyaAccount.id.in_(req.account_ids))
.filter(HuyaAccount.id.in_(selected_ids))
.all()
)
account_map = {account.id: account for account in accounts}
@@ -708,7 +793,7 @@ def password_login_selected_accounts(
failed_count = 0
include_cookie = _can_view_huya_cookie(current)
for account_id in req.account_ids:
for account_id in selected_ids:
account = account_map.get(account_id)
if account is None:
failed_count += 1
@@ -827,12 +912,35 @@ def delete_accounts_batch(
ids = [int(x) for x in account_ids.split(",") if x.strip().isdigit()]
if not ids:
raise HTTPException(status_code=400, detail="无效的账号ID")
ids = _selected_huya_account_ids(db, current, AccountBulkSelection(account_ids=ids))
if not ids:
raise HTTPException(status_code=400, detail="请选择虎牙账号")
db.query(HuyaTask).filter(HuyaTask.account_id.in_(ids)).delete(synchronize_session=False)
_clear_huya_account_references(db, ids)
deleted = db.query(HuyaAccount).filter(HuyaAccount.id.in_(ids)).delete(synchronize_session=False)
db.commit()
return {"message": f"已删除 {deleted} 个虎牙账号", "deleted": deleted, "success": True}
@router.post("/accounts/batch-delete")
def delete_accounts_batch_selection(
req: AccountBulkSelection,
db: Session = Depends(get_db),
current: User = Depends(get_current_user),
):
"""按显式选择或当前筛选结果批量删除虎牙账号及任务记录。"""
_require_huya_perm(current, "huya:delete")
ids = _selected_huya_account_ids(db, current, req)
if not ids:
raise HTTPException(status_code=400, detail="请选择虎牙账号")
db.query(HuyaTask).filter(HuyaTask.account_id.in_(ids)).delete(synchronize_session=False)
_clear_huya_account_references(db, ids)
deleted = db.query(HuyaAccount).filter(HuyaAccount.id.in_(ids)).delete(synchronize_session=False)
db.commit()
scope = "当前筛选下" if req.all_matching else "选中的"
return {"message": f"已删除{scope} {deleted} 个虎牙账号", "deleted": deleted, "success": True}
@router.delete("/accounts/{account_id}")
def delete_account(
account_id: int,
@@ -845,6 +953,7 @@ def delete_account(
if not account:
raise HTTPException(status_code=404, detail="账号不存在")
db.query(HuyaTask).filter(HuyaTask.account_id == account_id).delete(synchronize_session=False)
_clear_huya_account_references(db, [account_id])
db.delete(account)
db.commit()
return {"message": "已删除", "success": True}
@@ -964,6 +1073,27 @@ def batch_tag(
return {"message": f"已为 {count} 个虎牙账号设置标签", "success": True, "count": count}
@router.put("/accounts/batch-tag-selection")
def batch_tag_selection(
req: AccountBulkTag,
db: Session = Depends(get_db),
current: User = Depends(get_current_user),
):
"""按显式选择或当前筛选结果批量设置虎牙账号标签。"""
_require_huya_perm(current, "huya:import")
ids = _selected_huya_account_ids(db, current, req)
if not ids:
raise HTTPException(status_code=400, detail="请选择虎牙账号")
tag = (req.tag_value or "").strip()
count = db.query(HuyaAccount).filter(HuyaAccount.id.in_(ids)).update(
{HuyaAccount.tag: tag},
synchronize_session=False,
)
db.commit()
scope = "当前筛选下" if req.all_matching else "选中的"
return {"message": f"已为{scope} {count} 个虎牙账号设置标签", "success": True, "count": count}
@router.get("/accounts/tags/list")
def list_tags(
db: Session = Depends(get_db),