From eded5896cd770ade375617ed3e09e7852165958f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=BF=E4=BA=91=E7=A7=8B=E6=9C=88?= <15273589815@163.com> Date: Mon, 14 Sep 2026 01:35:34 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(admin):=20=E5=9B=9E=E5=A4=8D=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E7=9A=84=20scene=20=E5=8A=A0=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=EF=BC=8C=E9=9D=9E=E6=B3=95=E5=80=BC=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=86=92=E6=88=90=20500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 `agent_reply_template.scene` 有数据库 CHECK 约束 `chk_template_scene`,只允许 `disclaimer` / `low_confidence` / `compliance_block` / `transfer` / `model_failure` / `system_busy` / `clarification` 七个值。 而 `ReplyPayload.scene` 只校验长度(`min_length=1, max_length=32`)—— 传一个不在列内的场景会**穿过接口校验、撞上数据库约束**,最终以 `500` 冒出: (3819, "Check constraint 'chk_template_scene' is violated.") 那本该是一次 `422` 参数校验失败。**500 与 422 的差别不只是状态码**: 前者会让调用方以为服务端故障、触发重试与告警,而实际是自己参数错了。 ## 改动 `ReplyPayload.scene` 改为 `Literal[...]`(新增 `ReplyScene` 类型别名), 取值与 `chk_template_scene` **逐字对齐**,并在注释里写明这个对齐关系与本次事故。 实测:非法 scene → `422 AGENT_INPUT_INVALID`(不再是 500);合法 scene → `201`。 ## 发现方式 这一处是**按接口逐条调用、逐个核对返回**时暴露的 —— 只看代码很难注意到 "schema 的宽松校验"与"数据库的严格约束"之间那道缝。同类风险仍存在: 凡是**表上有 CHECK 而 schema 只做长度校验**的字段,都有同样的 500 风险。 验证:unit+contract 1397 passed;integration 110 passed;ruff 通过; mypy 251 文件 0 错;e2e 冒烟 40/40。 --- app/api/schemas/admin.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/api/schemas/admin.py b/app/api/schemas/admin.py index 467ba5b..f1f602b 100644 --- a/app/api/schemas/admin.py +++ b/app/api/schemas/admin.py @@ -80,9 +80,25 @@ class IntentPayload(StrictPayload): version: int = Field(default=1, ge=1) +#: `agent_reply_template.chk_template_scene` 允许的取值。**必须逐字对齐**: +#: 此前 `ReplyPayload.scene` 只校验长度(`min_length=1, max_length=32`), +#: 于是传一个不在列内的场景会**穿过接口校验、撞上数据库 CHECK 约束**, +#: 最终以 `500` 冒出(`(3819, "Check constraint 'chk_template_scene' is violated.")`)—— +#: 那本该是一次 `422` 参数校验失败。2026-09-13 由接口逐条测试发现。 +ReplyScene = Literal[ + "disclaimer", + "low_confidence", + "compliance_block", + "transfer", + "model_failure", + "system_busy", + "clarification", +] + + class ReplyPayload(StrictPayload): template_code: str = Field(min_length=1, max_length=64) - scene: str = Field(min_length=1, max_length=32) + scene: ReplyScene title: str = Field(min_length=1, max_length=128) content_text: str = Field(min_length=1, max_length=32000) variables: list[str] = Field(default_factory=list) From 3cbfe10d626461c9105b80e1b937f47dc108f0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=BF=E4=BA=91=E7=A7=8B=E6=9C=88?= <15273589815@163.com> Date: Mon, 14 Sep 2026 01:42:21 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(portal):=20=E8=A1=A5=E9=BD=90=E8=BF=90?= =?UTF-8?q?=E8=90=A5=E4=B8=89=E4=B8=AA=E6=96=B0=E9=A1=B5=E9=9D=A2=E7=9A=84?= =?UTF-8?q?=E5=90=8C=E5=90=8D=20css=20=E5=85=A5=E5=8F=A3=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=90=88=E5=B9=B6=E8=BF=9B=E6=9D=A5=E7=9A=84=E7=BA=A2?= =?UTF-8?q?=E7=81=AF=E5=A5=91=E7=BA=A6=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并组员的运营工作台提交后,`test_every_portal_page_has_local_js_and_css_entry` 红了:门户每个页面都要有与目录同名的 `js`/`css` 入口,而新加的 `nl2sql` / `offsite` / `promotion` 三个页面**只有 js**,样式统一引 `operator-workspace.css`。 ## 改动 给三个页面补上同名样式入口,并在各自 `index.html` 里引用(放在共用的 `operator-workspace.css` 之后,便于页面覆盖): - `employee-operations/nl2sql/nl2sql.css` - `employee-operations/offsite/offsite.css` - `employee-operations/promotion/promotion.css` 三个文件当前**没有规则**,只写了用途说明 —— 它们的价值是把"页面专属样式"的位置 **确定下来**:这个约定的意义正在于此,否则将来只会继续往共用文件里堆。 ⚠️ 只在每个 `index.html` 的 `
` 里**加了一行 link**,未改动组员的其它内容。 验证:`tests/unit/api/test_portal_frontend.py` 37 passed; unit+contract **1398 passed**;integration **110 passed**;ruff 通过; mypy 251 文件 0 错;三个页面与三个 css 均 200;e2e 冒烟 **40/40**。 --- app/static/portal/employee-operations/nl2sql/index.html | 1 + app/static/portal/employee-operations/nl2sql/nl2sql.css | 8 ++++++++ app/static/portal/employee-operations/offsite/index.html | 1 + app/static/portal/employee-operations/offsite/offsite.css | 8 ++++++++ .../portal/employee-operations/promotion/index.html | 1 + .../portal/employee-operations/promotion/promotion.css | 8 ++++++++ 6 files changed, 27 insertions(+) create mode 100644 app/static/portal/employee-operations/nl2sql/nl2sql.css create mode 100644 app/static/portal/employee-operations/offsite/offsite.css create mode 100644 app/static/portal/employee-operations/promotion/promotion.css diff --git a/app/static/portal/employee-operations/nl2sql/index.html b/app/static/portal/employee-operations/nl2sql/index.html index 886feef..70169cb 100644 --- a/app/static/portal/employee-operations/nl2sql/index.html +++ b/app/static/portal/employee-operations/nl2sql/index.html @@ -7,6 +7,7 @@ +