From 27e9107e3814a680271849d069587d6d784960d6 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 5 Oct 2026 11:36:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(accounts):=20=E4=BF=AE=E5=A4=8D=E9=B1=BC?= =?UTF-8?q?=E7=BF=85=E7=AD=9B=E9=80=89=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=BA=E6=B5=AE=E7=82=B9=E6=95=B0=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 后端将 min_gold 与 max_gold 参数及 Schema 类型从 int 改为 float | None,支持小数鱼翅(如 >0 快捷筛选的 0.01); 2. 修复前端 axios 拦截器在遇到 FastAPI 422 验证错误时未展开 detail 导致弹出 [object Object] 的问题; 3. 单元测试补充验证浮点数鱼翅过滤场景。 --- tests/test_account_overview.py | 5 +++++ web/backend/routers/accounts.py | 12 ++++++------ web/backend/schemas.py | 4 ++-- web/frontend/src/api/client.ts | 10 +++++++++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/test_account_overview.py b/tests/test_account_overview.py index 8146e81..1aff10d 100644 --- a/tests/test_account_overview.py +++ b/tests/test_account_overview.py @@ -1766,6 +1766,11 @@ class TestHighValueAccountFilters: names = [it.username for it in items] assert names == ["acc_crown"] + # 浮点数鱼翅过滤(如前端「有鱼翅」快捷筛选传 0.01) + items_float = self._query(min_gold=0.01) + names_float = [it.username for it in items_float] + assert "acc_crown" in names_float + # 宝典已开通 items = self._query(elite_opened=True) names = [it.username for it in items] diff --git a/web/backend/routers/accounts.py b/web/backend/routers/accounts.py index bc574e8..b0fec5d 100644 --- a/web/backend/routers/accounts.py +++ b/web/backend/routers/accounts.py @@ -105,8 +105,8 @@ def _filter_accounts_query( high_value_filter: str = "", min_points: int | None = None, max_points: int | None = None, - min_gold: int | None = None, - max_gold: int | None = None, + min_gold: float | None = None, + max_gold: float | None = None, elite_opened: bool | None = None, esports_opened: bool | None = None, role_bind_status: str = "", @@ -192,9 +192,9 @@ def _filter_accounts_query( query = query.filter(Account.points >= min_points) if isinstance(max_points, int): query = query.filter(Account.points <= max_points) - if isinstance(min_gold, int): + if isinstance(min_gold, (int, float)): query = query.filter(Account.gold_balance >= min_gold) - if isinstance(max_gold, int): + if isinstance(max_gold, (int, float)): query = query.filter(Account.gold_balance <= max_gold) # 开通状态过滤 @@ -295,8 +295,8 @@ def list_accounts( ), min_points: int | None = Query(None, description="最低积分"), max_points: int | None = Query(None, description="最高积分"), - min_gold: int | None = Query(None, description="最低鱼翅"), - max_gold: int | None = Query(None, description="最高鱼翅"), + min_gold: float | None = Query(None, description="最低鱼翅"), + max_gold: float | None = Query(None, description="最高鱼翅"), elite_opened: bool | None = Query(None, description="是否开通精英宝典"), esports_opened: bool | None = Query(None, description="是否开通电竞手册"), role_bind_status: str = Query( diff --git a/web/backend/schemas.py b/web/backend/schemas.py index 466c020..78fcb2a 100644 --- a/web/backend/schemas.py +++ b/web/backend/schemas.py @@ -143,8 +143,8 @@ class AccountBulkSelection(BaseModel): high_value_filter: str = "" min_points: int | None = None max_points: int | None = None - min_gold: int | None = None - max_gold: int | None = None + min_gold: float | None = None + max_gold: float | None = None elite_opened: bool | None = None esports_opened: bool | None = None role_bind_status: str = "" diff --git a/web/frontend/src/api/client.ts b/web/frontend/src/api/client.ts index 119b5bc..be69ad6 100644 --- a/web/frontend/src/api/client.ts +++ b/web/frontend/src/api/client.ts @@ -16,7 +16,15 @@ api.interceptors.response.use( localStorage.removeItem('user'); window.location.href = '/login'; } - const msg = error.response?.data?.detail || error.message || '请求失败'; + const detail = error.response?.data?.detail; + let msg = error.message || '请求失败'; + if (typeof detail === 'string') { + msg = detail; + } else if (Array.isArray(detail) && detail.length > 0) { + msg = detail.map((d: any) => d?.msg || (typeof d === 'string' ? d : JSON.stringify(d))).join('; '); + } else if (detail && typeof detail === 'object') { + msg = JSON.stringify(detail); + } return Promise.reject(new Error(msg)); } );