From 11b249f1fbb9954195f812a5abef4ebdea519463 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 1 Aug 2026 12:24:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E5=AF=BC=E5=85=A5--=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E7=9B=B4=E6=8E=A5=E6=89=93=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/backend/routers/accounts.py | 7 +++++-- web/backend/schemas.py | 7 ++++++- web/backend/services/account_service.py | 12 ++++++++++-- web/frontend/src/api/accounts.ts | 3 ++- web/frontend/src/pages/AccountsPage.tsx | 17 +++++++++++++++-- web/frontend/src/pages/DouyuTasksPage.tsx | 6 +++++- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/web/backend/routers/accounts.py b/web/backend/routers/accounts.py index 534e0fb..be91e59 100644 --- a/web/backend/routers/accounts.py +++ b/web/backend/routers/accounts.py @@ -186,8 +186,11 @@ def import_accounts( db: Session = Depends(get_db), current: User = Depends(require_permission("account:import")), ): - """批量导入账号。格式:用户名|密码|邮箱|邮箱密码|标签(可选)""" - accounts, skipped = parse_and_build_accounts(req.text) + """批量导入账号。格式:用户名|密码|邮箱|邮箱密码|标签(可选) + + req.tag 为统一标签兜底:某行未单独写标签时使用该值,行内标签优先。 + """ + accounts, skipped = parse_and_build_accounts(req.text, req.tag) if accounts: db.add_all(accounts) diff --git a/web/backend/schemas.py b/web/backend/schemas.py index 0dd6710..c9f3b38 100644 --- a/web/backend/schemas.py +++ b/web/backend/schemas.py @@ -80,8 +80,13 @@ class UserUpdate(BaseModel): # ---- 账号 ---- class AccountImport(BaseModel): - """批量导入,文本格式:用户名|密码|邮箱|邮箱密码""" + """批量导入,文本格式:用户名|密码|邮箱|邮箱密码|标签(可选) + + tag 为本次导入的统一标签兜底:某行未单独写标签时使用该值, + 行内第5列标签优先。 + """ text: str + tag: str = "" class AccountAssign(BaseModel): diff --git a/web/backend/services/account_service.py b/web/backend/services/account_service.py index cdc1719..107c454 100644 --- a/web/backend/services/account_service.py +++ b/web/backend/services/account_service.py @@ -31,15 +31,21 @@ def cookie_account_ids_query(db: Session): ).distinct() -def parse_and_build_accounts(text: str) -> tuple[list[Account], int]: +def parse_and_build_accounts(text: str, default_tag: str = "") -> tuple[list[Account], int]: """ 解析批量导入文本,构建 Account ORM 对象列表。 + Args: + text: 批量导入文本,每行格式:用户名|密码|邮箱|邮箱密码|标签(可选) + default_tag: 统一标签兜底。某行未单独写标签时使用该值, + 行内第5列标签优先。 + Returns: (accounts, skipped_count) """ from core.douyu.email_verifier import get_email_config_for_account + default_tag = (default_tag or "").strip() accounts = [] skipped = 0 for line in text.strip().split('\n'): @@ -52,7 +58,9 @@ def parse_and_build_accounts(text: str) -> tuple[list[Account], int]: continue username, password, email, email_password = [p.strip() for p in parts[:4]] - tag = parts[4].strip() if len(parts) > 4 else "" + line_tag = parts[4].strip() if len(parts) > 4 else "" + # 行内标签优先,未写则用统一兜底标签 + tag = line_tag or default_tag if not all([username, password, email, email_password]): skipped += 1 continue diff --git a/web/frontend/src/api/accounts.ts b/web/frontend/src/api/accounts.ts index 93c52cb..173fa0f 100644 --- a/web/frontend/src/api/accounts.ts +++ b/web/frontend/src/api/accounts.ts @@ -18,7 +18,8 @@ export const accountApi = { listPaged: (params: PageParams & { assigned_only?: boolean; tag?: string; has_cookie?: boolean; include_sensitive?: boolean }) => api.get, PaginatedResponse>('/accounts', { params }), summary: () => api.get('/accounts/summary'), - import: (text: string) => api.post('/accounts/import', { text }), + import: (text: string, tag?: string) => + api.post('/accounts/import', { text, tag: tag || '' }), assign: (id: number, assigned_to: number | null) => api.put(`/accounts/${id}/assign`, { assigned_to }), batchAssign: (account_ids: number[], assigned_to: number | null) => diff --git a/web/frontend/src/pages/AccountsPage.tsx b/web/frontend/src/pages/AccountsPage.tsx index 28f3c71..2c39b14 100644 --- a/web/frontend/src/pages/AccountsPage.tsx +++ b/web/frontend/src/pages/AccountsPage.tsx @@ -25,6 +25,7 @@ export default function AccountsPage() { const [loading, setLoading] = useState(false); const [importOpen, setImportOpen] = useState(false); const [importText, setImportText] = useState(''); + const [importTag, setImportTag] = useState(''); const [importing, setImporting] = useState(false); const [tagFilter, setTagFilter] = useState(''); const [searchText, setSearchText] = useState(''); @@ -137,10 +138,11 @@ export default function AccountsPage() { } setImporting(true); try { - const result = await accountApi.import(importText); + const result = await accountApi.import(importText, importTag); message.success(result.message); setImportOpen(false); setImportText(''); + setImportTag(''); loadAccounts(); loadTags(); loadSummary(); @@ -375,7 +377,7 @@ export default function AccountsPage() { )} {canImport && ( - )} @@ -480,6 +482,17 @@ export default function AccountsPage() { placeholder={`用户名|密码|邮箱|邮箱密码|标签\n用户名|密码|邮箱|邮箱密码|标签`} style={{ marginTop: 8 }} /> +
+ 统一标签(可选):未在行内写标签的账号将使用此标签 +