更新
This commit is contained in:
+135
-37
@@ -20,7 +20,7 @@ from settings import CORS_ORIGINS, SERVE_WEB, STATIC_DIR
|
||||
from help_pages import serve_credential_tool
|
||||
from web_static import mount_frontend
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import select, update, delete, text, func, case
|
||||
from sqlalchemy import select, update, delete, text, func, case, or_, cast, String
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from models.database import engine, Base, get_db, AsyncSessionLocal
|
||||
@@ -1080,42 +1080,91 @@ async def get_accounts(
|
||||
支持 q(昵称/抖音ID/手机号/账号ID 搜索)与 status 筛选。
|
||||
Cookie 解析等重逻辑只对当前页执行。
|
||||
"""
|
||||
result = await db.execute(accounts_for_user(user))
|
||||
accounts = result.scalars().all()
|
||||
# 更新内存中的运行状态与数据库同步,以防异常断开
|
||||
for acc in accounts:
|
||||
if page is None:
|
||||
result = await db.execute(accounts_for_user(user))
|
||||
accounts = result.scalars().all()
|
||||
# 兼容旧的全量接口行为。
|
||||
for acc in accounts:
|
||||
is_running = manager.is_running(acc.id)
|
||||
if is_running and acc.status == "offline":
|
||||
acc.status = "online"
|
||||
elif not is_running and acc.status in ("online", "logging_in", "starting"):
|
||||
acc.status = "offline"
|
||||
return [_build_account_response(acc) for acc in accounts]
|
||||
|
||||
keyword = (q or "").strip().lower()
|
||||
status_filter = (status or "").strip()
|
||||
stmt = accounts_for_user(user)
|
||||
running_account_ids = [
|
||||
int(account_id)
|
||||
for account_id, worker in list(manager.workers.items())
|
||||
if worker and worker.is_running
|
||||
]
|
||||
runtime_running = Account.id.in_(running_account_ids)
|
||||
effective_status = case(
|
||||
(
|
||||
runtime_running & (Account.status == "offline"),
|
||||
"online",
|
||||
),
|
||||
(
|
||||
(~runtime_running)
|
||||
& Account.status.in_(("online", "logging_in", "starting")),
|
||||
"offline",
|
||||
),
|
||||
else_=Account.status,
|
||||
)
|
||||
|
||||
if keyword:
|
||||
like_value = f"%{keyword}%"
|
||||
search_conditions = [
|
||||
func.lower(Account.username).like(like_value),
|
||||
func.lower(Account.douyin_uid).like(like_value),
|
||||
func.lower(Account.phone).like(like_value),
|
||||
func.lower(Account.user_agent).like(like_value),
|
||||
cast(Account.id, String).like(like_value),
|
||||
]
|
||||
matching_profiles = [
|
||||
profile
|
||||
for profile in list_device_profiles()
|
||||
if keyword in str(profile.get("label") or "").lower()
|
||||
or keyword in str(profile.get("platform") or "").lower()
|
||||
]
|
||||
if matching_profiles:
|
||||
matching_uas = [profile["user_agent"] for profile in matching_profiles]
|
||||
search_conditions.append(Account.user_agent.in_(matching_uas))
|
||||
if any(profile.get("id") == "chrome_win120" for profile in matching_profiles):
|
||||
search_conditions.append(Account.user_agent.is_(None))
|
||||
stmt = stmt.where(or_(*search_conditions))
|
||||
|
||||
if status_filter and status_filter != "all":
|
||||
if status_filter == "quota_disabled":
|
||||
stmt = stmt.where(Account.quota_disabled.is_(True))
|
||||
else:
|
||||
stmt = stmt.where(
|
||||
Account.quota_disabled.is_not(True),
|
||||
effective_status == status_filter,
|
||||
)
|
||||
|
||||
total_result = await db.execute(
|
||||
stmt.with_only_columns(func.count(Account.id)).order_by(None)
|
||||
)
|
||||
total = int(total_result.scalar_one() or 0)
|
||||
result = await db.execute(
|
||||
stmt.order_by(Account.id.asc())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
)
|
||||
items = result.scalars().all()
|
||||
|
||||
# Only reconcile the current page. The old implementation hydrated every
|
||||
# account including large Cookie/IM blobs on every refresh, which became
|
||||
# visibly slow beyond a few hundred accounts.
|
||||
for acc in items:
|
||||
is_running = manager.is_running(acc.id)
|
||||
if is_running and acc.status == "offline":
|
||||
acc.status = "online"
|
||||
elif not is_running and acc.status in ("online", "logging_in", "starting"):
|
||||
acc.status = "offline"
|
||||
|
||||
if page is None:
|
||||
return [_build_account_response(acc) for acc in accounts]
|
||||
|
||||
keyword = (q or "").strip().lower()
|
||||
status_filter = (status or "").strip()
|
||||
filtered = [
|
||||
acc
|
||||
for acc in accounts
|
||||
if _account_matches_keyword(acc, keyword)
|
||||
and (
|
||||
not status_filter
|
||||
or status_filter == "all"
|
||||
or (
|
||||
status_filter == "quota_disabled"
|
||||
and bool(acc.quota_disabled)
|
||||
)
|
||||
or (
|
||||
status_filter != "quota_disabled"
|
||||
and not acc.quota_disabled
|
||||
and acc.status == status_filter
|
||||
)
|
||||
)
|
||||
]
|
||||
total = len(filtered)
|
||||
start = (page - 1) * page_size
|
||||
items = filtered[start : start + page_size]
|
||||
return {
|
||||
"items": [_build_account_response(acc) for acc in items],
|
||||
"total": total,
|
||||
@@ -1330,14 +1379,55 @@ async def update_account(
|
||||
|
||||
@app.get("/api/reply-queues", response_model=ReplyQueueSummaryResponse)
|
||||
async def get_reply_queue_summaries(
|
||||
account_ids: Optional[str] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(get_current_user),
|
||||
):
|
||||
"""聚合当前用户可见账号的排队数量,供账号页低频轮询。"""
|
||||
allowed_ids = await owned_account_ids(db, user)
|
||||
"""聚合可见账号的排队数量;账号页可限定为当前页账号。"""
|
||||
requested_ids: list[int] | None = None
|
||||
if account_ids is not None:
|
||||
requested_ids = []
|
||||
seen_ids: set[int] = set()
|
||||
for raw in str(account_ids).split(","):
|
||||
token = raw.strip()
|
||||
if not token:
|
||||
continue
|
||||
try:
|
||||
account_id = int(token)
|
||||
except (TypeError, ValueError):
|
||||
raise HTTPException(status_code=400, detail="账号编号格式错误")
|
||||
if account_id > 0 and account_id not in seen_ids:
|
||||
seen_ids.add(account_id)
|
||||
requested_ids.append(account_id)
|
||||
if len(requested_ids) > 100:
|
||||
raise HTTPException(status_code=400, detail="单次最多查询 100 个账号的回复队列")
|
||||
|
||||
allowed_ids: set[int] | None
|
||||
if requested_ids is None:
|
||||
allowed_ids = await owned_account_ids(db, user)
|
||||
worker_entries = list(manager.workers.items())
|
||||
else:
|
||||
if is_admin(user.role):
|
||||
allowed_ids = set(requested_ids)
|
||||
elif requested_ids:
|
||||
owned_result = await db.execute(
|
||||
select(Account.id).where(
|
||||
Account.owner_id == user.id,
|
||||
Account.id.in_(requested_ids),
|
||||
)
|
||||
)
|
||||
allowed_ids = {int(row[0]) for row in owned_result.all()}
|
||||
else:
|
||||
allowed_ids = set()
|
||||
worker_entries = [
|
||||
(account_id, manager.workers.get(account_id))
|
||||
for account_id in requested_ids
|
||||
if account_id in allowed_ids and account_id in manager.workers
|
||||
]
|
||||
|
||||
summaries: list[ReplyQueueSummaryItem] = []
|
||||
total_pending = 0
|
||||
for account_id, worker in list(manager.workers.items()):
|
||||
for account_id, worker in worker_entries:
|
||||
if allowed_ids is not None and account_id not in allowed_ids:
|
||||
continue
|
||||
service = worker._im_service if worker else None
|
||||
@@ -1688,7 +1778,11 @@ async def validate_account_credential(
|
||||
account = await get_owned_account(db, user, account_id)
|
||||
|
||||
cookie_data = _get_account_cookie_data(account)
|
||||
assessment = await assess_account_credential(cookie_data, account.im_session_data)
|
||||
assessment = await assess_account_credential(
|
||||
cookie_data,
|
||||
account.im_session_data,
|
||||
startup_priority=True,
|
||||
)
|
||||
return CredentialValidateResponse(**assessment)
|
||||
|
||||
|
||||
@@ -1718,7 +1812,11 @@ async def _start_account_rpa_impl(
|
||||
return {"status": "running", "message": "RPA worker is already running."}
|
||||
|
||||
cookie_data = _get_account_cookie_data(account)
|
||||
assessment = await assess_account_credential(cookie_data, account.im_session_data)
|
||||
assessment = await assess_account_credential(
|
||||
cookie_data,
|
||||
account.im_session_data,
|
||||
startup_priority=True,
|
||||
)
|
||||
login_mode = requested_login_mode or assessment["login_mode"]
|
||||
reset_performed = False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user