diff --git a/backend/app/data/catalog_transfer.py b/backend/app/data/catalog_transfer.py index b0badbdf..39bab51d 100644 --- a/backend/app/data/catalog_transfer.py +++ b/backend/app/data/catalog_transfer.py @@ -396,6 +396,9 @@ def plan_table(table_name: str, target: str, file_notes: Mapping[str, Any], targ if why is not None: plan.skipped.append({"path": path, "why": why}) continue + if isinstance(item, dict) and item.get("status") not in _LOCKED and "reason" in item: + # 推断、有确证的项不带确认时的依据:在校验之前去掉,手改的文件里依据不合规也不把整项跳过 + item = {k: v for k, v in item.items() if k != "reason"} problem = _item_problem(mapped or key, item) if problem is not None or mapped is None: plan.skipped.append({"path": path, "why": problem or "无法识别这一项"}) @@ -405,8 +408,6 @@ def plan_table(table_name: str, target: str, file_notes: Mapping[str, Any], targ continue seen.add(mapped) status = item.get("status") - if status not in _LOCKED: - item = {k: v for k, v in item.items() if k != "reason"} # 推断、有确证的项不带确认时的依据 have = existing.get(mapped) if have is None: plan.add += 1 diff --git a/backend/tests/test_catalog_transfer.py b/backend/tests/test_catalog_transfer.py index ba861de3..61a210cb 100644 --- a/backend/tests/test_catalog_transfer.py +++ b/backend/tests/test_catalog_transfer.py @@ -411,3 +411,22 @@ async def test_round_trip_into_an_empty_copy_reproduces_the_catalog(client): expected["relations"] = [r for r in mine.notes["relations"] if r["to_table"] != "stores"] del expected["columns"]["gift_flag"] # 表结构里没有这一列 assert theirs.notes == expected + + +async def test_a_bad_reason_on_an_inferred_item_is_dropped_not_the_item(client): + """手改过的文件里,推断的项带着不合规的确认时的依据(推断项本来就不该有依据):去掉依据、照常填空缺,不把整项跳过。 + 已确认的项依据不合规照旧跳过(依据是它的一部分,照带就得合规)。""" + local, remote = await _pair() + content = await _export(client, local) + notes = content["tables"]["orders"]["notes"] + notes["grain"] = {"value": "每笔订单一行", "source": "llm", "status": "proposed", "reason": "依" * 600} + notes["description"] = {"value": "订单头", "source": "human", "status": "confirmed", "reason": 42} + report = (await client.post(f"{_base(remote)}/import/preview", json={"content": content})).json() + skipped = {s["path"]: s["why"] for s in _by(report)["ORDERS"]["skipped"]} + assert "grain" not in skipped + assert "description" in skipped and "依据" in skipped["description"] + r = await client.post(f"{_base(remote)}/import", json={"content": content}) + assert r.status_code == 200, r.text + assert "grain" not in {s["path"] for s in _by(r.json())["ORDERS"]["skipped"]} + got = (await client.get(f"{_base(remote)}/ORDERS")).json()["notes"] + assert "reason" not in got["grain"] # 目标原有的推断粒度保留(推断只填空缺),也没被挂上依据