From ecdb9cb471d187aa7dce732ae3eb6fb5952d0148 Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Fri, 24 Apr 2026 19:09:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20v0.19.3=20admin=20settings=20GET=20?= =?UTF-8?q?=E8=A1=A5=E8=BF=94=E5=9B=9E=201080P=20=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E5=8D=95=E4=BB=B7=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.19.0 做 1080P 时 QuotaConfig 加了 base_token_price_1080p 和 base_token_price_1080p_video 字段, serializer (PUT) 和计费逻辑 (_get_token_price) 都处理了, 但 _settings_dict (GET) 漏了两行, 导致管理后台设置页两个 1080P 单价输入框显示空白。 实际影响 - DB 值对 (51 / 31), 计费走 _get_token_price 直接读 DB, 计费一直正确 - 前端 SettingsPage fetchSettings 用 setSettings(data) 覆盖, GET 返回缺字段 -> state 变 undefined -> 输入框显示空 - 管理员点保存: undefined 被 JSON.stringify 省略 -> PUT body 不含 这两字段 -> serializer validated_data 里没有 -> DB 未改 - 所以目前"巧合安全", 但风险: 管理员在空输入框填数字后清空, Number("") = 0 会覆盖 DB, 把单价刷成 0 修复 - backend/apps/generation/views.py _settings_dict() 加两行返回 base_token_price_1080p / base_token_price_1080p_video - 前端 GET 后 state 直接拿到 51 / 31, 输入框自动显示, 不依赖"巧合" 回归测试 (backend/tests/test_1080p_api.py) - 新增 TestAdminSettingsResponse.test_get_returns_all_token_price_fields 断言 GET /admin/settings 返回 6 个 token_price 字段全齐 - 失败消息明示: "缺字段会导致前端输入框显示空" 以防以后再漏 Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/apps/generation/views.py | 2 ++ backend/tests/test_1080p_api.py | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/backend/apps/generation/views.py b/backend/apps/generation/views.py index ebec6a7..a4b014b 100644 --- a/backend/apps/generation/views.py +++ b/backend/apps/generation/views.py @@ -1907,6 +1907,8 @@ def _settings_dict(config): 'base_token_price_video': float(config.base_token_price_video), 'base_token_price_fast': float(config.base_token_price_fast), 'base_token_price_fast_video': float(config.base_token_price_fast_video), + 'base_token_price_1080p': float(config.base_token_price_1080p), + 'base_token_price_1080p_video': float(config.base_token_price_1080p_video), 'announcement': config.announcement, 'announcement_enabled': config.announcement_enabled, 'max_desktop_sessions': config.max_desktop_sessions, diff --git a/backend/tests/test_1080p_api.py b/backend/tests/test_1080p_api.py index 72e348a..3b8398b 100644 --- a/backend/tests/test_1080p_api.py +++ b/backend/tests/test_1080p_api.py @@ -127,5 +127,38 @@ class TestVideoGenerateResolution(TestCase): self.assertNotEqual(body.get('error'), 'invalid_resolution') +class TestAdminSettingsResponse(TestCase): + """GET /api/v1/admin/settings 必须返回所有 token_price 字段, + 以防 v0.19.0 那种"字段在 serializer 里加了、但 _settings_dict 漏了"的回归。""" + + def setUp(self): + QuotaConfig.objects.get_or_create(pk=1) + self.admin = User.objects.create_user( + username='test_admin_settings', + email='test_admin_settings@example.com', + password='testpass123', + is_staff=True, + is_superuser=True, + ) + self.client = APIClient() + self.client.force_authenticate(user=self.admin) + + def test_get_returns_all_token_price_fields(self): + """GET 返回 4 档单价(全部分辨率 + 是否含视频),缺一不可 — 缺字段会导致前端输入框显示空。""" + resp = self.client.get('/api/v1/admin/settings') + self.assertEqual(resp.status_code, 200) + body = resp.json() + for field in ( + 'base_token_price', + 'base_token_price_video', + 'base_token_price_fast', + 'base_token_price_fast_video', + 'base_token_price_1080p', + 'base_token_price_1080p_video', + ): + self.assertIn(field, body, f'GET /admin/settings response missing {field!r} — 前端这个输入框会显示空') + self.assertIsInstance(body[field], (int, float), f'{field} 应该是数字类型') + + if __name__ == '__main__': unittest.main(verbosity=2)