From ca2832b6982cbc5350fb1e0e5b35bf78ac93e671 Mon Sep 17 00:00:00 2001 From: Hermes CI Fix Date: Sat, 12 Sep 2026 10:46:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(bot-bridge):=20subjects=20=E5=86=99?= =?UTF-8?q?=E5=85=A5=E7=AB=AF=E7=82=B9=E7=BC=96=E7=A0=81=E9=95=BF=E5=BA=A6?= =?UTF-8?q?=E6=94=B6=E6=95=9B=E5=88=B0=20DB=20=E5=88=97=E5=AE=BD20=20?= =?UTF-8?q?=E2=80=94=2021~32=E5=AD=97=E7=AC=A6=E7=BC=96=E7=A0=81=E5=8E=9F?= =?UTF-8?q?=E7=A9=BF=E8=BF=87=E6=A0=A1=E9=AA=8C=E8=A7=A6=E5=8F=91=20MySQL?= =?UTF-8?q?=201406=20=E6=95=B4=E6=89=B9500,=20=E6=94=B9=E4=B8=BA400?= =?UTF-8?q?=E6=8B=92=E7=BB=9D=20(dama-cma-subjects-write-endpoint-20260912?= =?UTF-8?q?=20=E5=A4=8D=E6=A0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/bot_bridge.py | 7 +++++-- backend/tests/test_bot_bridge.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/backend/app/api/bot_bridge.py b/backend/app/api/bot_bridge.py index fb97b9a9..8ef38c3a 100644 --- a/backend/app/api/bot_bridge.py +++ b/backend/app/api/bot_bridge.py @@ -987,7 +987,10 @@ _SUBJ_FIELDS = ("subject_code", "subject_name", "parent_code", "level", "categor _SUBJ_CATEGORIES = ("asset", "liability", "equity", "cost", "income", "expense", "detail", "other") _SUBJ_LEVELS = (1, 2, 3) _SUBJ_MAX_ROWS = 2000 -_SUBJ_CODE_MAXLEN = 32 +# subject_code / parent_code 长度上限:任务规格为 ≤32,但 DB 列 subjects.subject_code / +# parent_code 实为 varchar(20) —— 取交集 20。若按 32 放行,21~32 字符的编码会穿过校验直达 +# MySQL,报 (1406, Data too long) → 整批 500(2026-09-12 生产实测),故此处收敛到 DB 列宽。 +_SUBJ_CODE_MAXLEN = 20 _SUBJ_NAME_MAXLEN = 200 @@ -1006,7 +1009,7 @@ def _validate_subject_rows(rows, db): """行级全量校验(任一错误 → 400 且零落库),返回标准化行 [{字段}] 规则(任务 dama-cma-subjects-write-endpoint-20260912): - subject_code 非空且 ≤32 字符 + 批次内去重;subject_name 非空(DB NOT NULL)且 ≤200; + subject_code 非空且 ≤_SUBJ_CODE_MAXLEN(=20,DB varchar(20) 列宽)+ 批次内去重;subject_name 非空(DB NOT NULL)且 ≤200; level ∈ {1,2,3}(缺省 1);parent_code 为空或存在于本表(或本批次内,支持层级同批导入); category 限枚举;is_active ∈ {0,1}(缺省 1)。 """ diff --git a/backend/tests/test_bot_bridge.py b/backend/tests/test_bot_bridge.py index ced2d6bc..bfbc560e 100644 --- a/backend/tests/test_bot_bridge.py +++ b/backend/tests/test_bot_bridge.py @@ -493,6 +493,24 @@ class TestSubjectsWrite: assert resp.status_code == 400 assert "subject_name" in str(resp.json()) + def test_code_too_long_400(self, client: TestClient, db: Session): + """编码长度上限 = min(任务规格 32, DB varchar(20)) = 20 + + 生产实测(2026-09-12):按 32 放行时 21 字符编码穿过校验 → MySQL 1406 Data too long + → 整批 500;收敛到 DB 列宽后应 400 拒绝且零落库。 + """ + resp = client.post(self.URL, json={"subjects": [ + self._row("9" * 21, "验证科目")]}, headers=BOT_KEY) + assert resp.status_code == 400, resp.json() + assert "超长" in str(resp.json()) + assert db.query(Subject).filter(Subject.subject_code == "9" * 21).count() == 0 + + def test_parent_code_too_long_400(self, client: TestClient): + resp = client.post(self.URL, json={"subjects": [ + self._row("9402", "验证子科目", parent_code="9" * 21, level=2)]}, headers=BOT_KEY) + assert resp.status_code == 400, resp.json() + assert "超长" in str(resp.json()) + # ── 落库语义 ── def test_dry_run_zero_write(self, client: TestClient, db: Session): before = db.query(Subject).count()