From fec84ce8822492f4fde92dfbd520975073c4da5b Mon Sep 17 00:00:00 2001 From: zichun <26684461+reporkey@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:26:21 +0800 Subject: [PATCH] fix(form): fuzzy prefill alignment key<->label (e2e-found: model says 客户, field is 客户名称 — exact match silently dropped the prefill); deterministic tie-break prefers unique fkselect/名称 field, ambiguous cases skip --- src/main/java/com/xly/tool/FormCollectTool.java | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/test/java/com/xly/tool/FormCollectDimensionTest.java | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/xly/tool/FormCollectTool.java b/src/main/java/com/xly/tool/FormCollectTool.java index 2ffe2b0..29e3dfe 100644 --- a/src/main/java/com/xly/tool/FormCollectTool.java +++ b/src/main/java/com/xly/tool/FormCollectTool.java @@ -114,10 +114,6 @@ public class FormCollectTool { if (hint != null && !String.valueOf(hint).isBlank()) { f.put("hint", String.valueOf(hint)); } - String pre = known.get(normLabel(String.valueOf(f.get("label")))); - if (pre != null && !pre.isBlank()) { - f.put("default", pre); // 预填用户已说的值 - } fields.add(f); } @@ -157,6 +153,7 @@ public class FormCollectTool { if (fields.isEmpty()) { return err("该表单没有可填写的业务字段。"); } + applyKnownDefaults(known, fields); Map out = new LinkedHashMap<>(); out.put("type", "form_collect"); @@ -258,6 +255,59 @@ public class FormCollectTool { } /** + * 把用户已说的值预填进表单字段。键与字段标签先按归一化全等匹配;对不上再做**宽松对齐** + * (「客户」→「客户名称」这类互含关系),多个候选字段时用确定性决胜:唯一的外键下拉字段 + * 或唯一以「名称」结尾的字段,仍二义则放弃预填(宁缺勿错,用户在表单里自己填)。 + */ + static void applyKnownDefaults(Map known, List> fields) { + List normLabels = new ArrayList<>(); + for (Map f : fields) { + normLabels.add(normLabel(String.valueOf(f.get("label")))); + } + Map aligned = new HashMap<>(); + for (Map.Entry e : known.entrySet()) { + String key = e.getKey(); + if (key.isEmpty() || e.getValue() == null || e.getValue().isBlank()) { + continue; + } + if (normLabels.contains(key)) { + aligned.put(key, e.getValue()); + continue; + } + List hits = new ArrayList<>(); + for (int i = 0; i < normLabels.size(); i++) { + String l = normLabels.get(i); + if (!l.isEmpty() && (l.contains(key) || key.contains(l))) { + hits.add(i); + } + } + Integer pick = null; + if (hits.size() == 1) { + pick = hits.get(0); + } else if (hits.size() > 1) { + List fk = hits.stream() + .filter(i -> "fkselect".equals(fields.get(i).get("type"))).toList(); + List named = hits.stream() + .filter(i -> normLabels.get(i).endsWith("名称")).toList(); + if (fk.size() == 1) { + pick = fk.get(0); + } else if (named.size() == 1) { + pick = named.get(0); + } + } + if (pick != null) { + aligned.putIfAbsent(normLabels.get(pick), e.getValue()); + } + } + for (int i = 0; i < fields.size(); i++) { + String pre = aligned.get(normLabels.get(i)); + if (pre != null && !pre.isBlank()) { + fields.get(i).put("default", pre); // 用户说过的值优先于表单默认值 + } + } + } + + /** * 预填值里的尺寸表达(「50*30*5」「长和宽50,高5cm」)确定性拆成 长/宽/高 三个键 * (模型拆不对是实测结论——原 SlotFillService 的正则迁于此)。已有的 长/宽/高 预填不覆盖。 */ diff --git a/src/test/java/com/xly/tool/FormCollectDimensionTest.java b/src/test/java/com/xly/tool/FormCollectDimensionTest.java index f9a8abe..79fab62 100644 --- a/src/test/java/com/xly/tool/FormCollectDimensionTest.java +++ b/src/test/java/com/xly/tool/FormCollectDimensionTest.java @@ -65,4 +65,46 @@ class FormCollectDimensionTest { assertFalse(m.containsKey("长"), "普通数值/名称/电话不产生尺寸"); assertEquals("大16开画册", m.get("产品名称"), "值逐字保留"); } + + // ---- 预填键 ↔ 字段标签的宽松对齐(e2e 实测缺陷:模型给「客户」,字段叫「客户名称」,全等匹配丢预填)---- + + private static java.util.List> fields(String... labelTypePairs) { + java.util.List> out = new java.util.ArrayList<>(); + for (int i = 0; i < labelTypePairs.length; i += 2) { + Map f = new LinkedHashMap<>(); + f.put("label", labelTypePairs[i]); + f.put("type", labelTypePairs[i + 1]); + out.add(f); + } + return out; + } + + @Test + void fuzzyKeyPrefillsContainingLabel() { + var fs = fields("客户名称", "fkselect", "产品名称", "fkselect", "数量", "number"); + FormCollectTool.applyKnownDefaults(known("客户", "必胜客", "数量", "5000"), fs); + assertEquals("必胜客", fs.get(0).get("default"), "「客户」对齐到「客户名称」"); + assertEquals("5000", fs.get(2).get("default")); + } + + @Test + void ambiguousKeyPrefersFkOrNamedField() { + var fs = fields("客户代码", "text", "客户名称", "fkselect"); + FormCollectTool.applyKnownDefaults(known("客户", "必胜客"), fs); + assertEquals("必胜客", fs.get(1).get("default"), "多候选时唯一外键下拉字段胜出"); + assertFalse(fs.get(0).containsKey("default")); + + var fs2 = fields("客户代码", "text", "客户简称", "text"); + FormCollectTool.applyKnownDefaults(known("客户", "必胜客"), fs2); + assertFalse(fs2.get(0).containsKey("default"), "无法决胜的二义 → 宁缺勿错"); + assertFalse(fs2.get(1).containsKey("default")); + } + + @Test + void exactMatchAndParenLabelStillWork() { + var fs = fields("长(L)", "number", "宽(W)", "number"); + FormCollectTool.applyKnownDefaults(known("长", "50", "宽", "30"), fs); + assertEquals("50", fs.get(0).get("default"), "括号标签归一化后全等命中"); + assertEquals("30", fs.get(1).get("default")); + } } -- libgit2 0.22.2