Преглед на файлове

fix: ai_clean_field 列名与 field_name_en 对齐并收紧到 varchar(5)

- AI 动态表物理列名固定 c0/c1/…(保证长度 ≤5),ai_clean_field.column_name
  与 field_name_en 写入完全一致的值;AI 语义英文名不再进字段元数据
- ai_clean_field 的 column_name / field_name_en DDL 从 varchar(200) 改为 varchar(5)
- 新增 sql/ai_clean_field_short_names.sql:存量库回填 field_name_en=column_name
  并收窄两列列宽(幂等)
cc преди 2 дни
родител
ревизия
c646be3d49
променени са 3 файла, в които са добавени 29 реда и са изтрити 10 реда
  1. 11 8
      ai-server/src/main/java/com/zsjz/ai/module/aiclean/rule/AiRulePlan.java
  2. 2 2
      sql/ai_clean.sql
  3. 16 0
      sql/ai_clean_field_short_names.sql

+ 11 - 8
ai-server/src/main/java/com/zsjz/ai/module/aiclean/rule/AiRulePlan.java

@@ -7,7 +7,6 @@ import com.zsjz.ai.common.model.plat.entity.TableField;
 import com.zsjz.ai.module.aiclean.entity.AiCleanField;
 import com.zsjz.ai.module.aiclean.model.RuleBind;
 import com.zsjz.ai.module.aiclean.model.TemplateCandidate;
-import com.zsjz.ai.module.aiclean.support.AiColumnNames;
 
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -80,6 +79,11 @@ public final class AiRulePlan {
      * <p><b>动态表要能还原整张 sheet</b>:AI 通常只绑上部分列(如车辆表只认出号牌/号牌种类/所有人),
      * 其余列按原表头顺序、原值保留为 TEXT。否则 {@code ai_clean_field} 与治理树只会显示模型
      * 命中的那几个字段,表就像被截断了一样(2026-10-18 需求)。
+     *
+     * <p><b>列名口径(2026-10-18 二次需求)</b>:物理列名固定用 {@code c0/c1/…},
+     * 且 {@code field_name_en} 与 {@code column_name} 完全一致 ——
+     * {@code ai_clean_field.column_name / field_name_en} 的列宽都只有 {@code varchar(5)}。
+     * AI 的语义英文名只留在 job 草稿/规则提示里,不再写进字段元数据。
      */
     public static Plan forAi(List<RuleBind> binds, List<String> headerNames) {
         Map<Integer, RuleBind> bindByIndex = new LinkedHashMap<>();
@@ -104,22 +108,21 @@ public final class AiRulePlan {
                 names.add(colName(headerNames, idx));
             }
         }
-        List<String> safe = AiColumnNames.sanitizeAll(names);
         List<CompiledColumn> columns = new ArrayList<>();
         List<AiCleanField> fields = new ArrayList<>();
         for (int pos = 0; pos < indexes.size(); pos++) {
             int fileColIndex = indexes.get(pos);
             RuleBind bind = bindByIndex.get(fileColIndex);
             String nameCn = names.get(pos);
-            String fieldNameEn = bind == null || StrUtil.isBlank(bind.getFieldNameEn())
-                    ? nameCn
-                    : bind.getFieldNameEn();
+            // 列名固定 c{位置}:既保证长度 ≤5(ai_clean_field 两列都是 varchar(5)),
+            // 也保证 field_name_en 与 column_name 完全一致
+            String columnName = "c" + pos;
             String type = bind == null ? "TEXT" : typeOf(bind);
             boolean required = bind != null && Boolean.TRUE.equals(bind.getRequired());
             AiCleanField f = new AiCleanField();
-            f.setColumnName(safe.get(pos));
+            f.setColumnName(columnName);
             f.setFieldNameCn(nameCn);
-            f.setFieldNameEn(fieldNameEn);
+            f.setFieldNameEn(columnName);
             f.setFieldType(type);
             f.setRequired(required ? 1 : 0);
             f.setFieldLen(0);
@@ -127,7 +130,7 @@ public final class AiRulePlan {
             f.setColIndex(fileColIndex);
             f.setMatched(1);
             fields.add(f);
-            columns.add(CompiledColumn.of(fileColIndex, safe.get(pos), fieldNameEn, type, required, 0,
+            columns.add(CompiledColumn.of(fileColIndex, columnName, columnName, type, required, 0,
                     bind == null ? List.of() : AiRuleCompiler.compileFuns(bind)));
         }
         return new Plan(columns, fields);

+ 2 - 2
sql/ai_clean.sql

@@ -55,9 +55,9 @@ create table if not exists ai_clean_field
 (
     id             bigint primary key,
     ai_template_id bigint      not null,
-    column_name    varchar(200) not null,                 -- 动态表里的实际列名(已消毒,仅 [a-z0-9_])
+    column_name    varchar(5)  not null,                  -- 动态表里的实际列名,固定 c0/c1/…(长度 ≤5)
     field_name_cn  varchar(200),                          -- 沿用文件原列头,前端展示用
-    field_name_en  varchar(200),                          -- 语义英文名,模型绑定目标
+    field_name_en  varchar(5),                            -- 与 column_name 完全一致(2026-10-18 需求)
     field_type     varchar(50),                           -- FieldTypeEnum#name()
     required       integer   default 0,
     field_len      integer   default 0,

+ 16 - 0
sql/ai_clean_field_short_names.sql

@@ -0,0 +1,16 @@
+-- ============================================================================
+-- ai_clean_field:column_name / field_name_en 统一为物理列名 c0/c1/…(2026-10-18)
+--
+-- 规则变更为:动态表列名固定 c{位置},field_name_en 与 column_name 完全一致;
+-- 两列 DDL 宽度也一并收紧到 varchar(5)(c0..c9999 最长 5 字符)。
+-- 本脚本用于**已存在的** ai_clean_field 表;新环境直接按 ai_clean.sql 建表即可。
+-- ============================================================================
+
+-- 1) 历史数据回填:field_name_en 改成与物理列名一致(如 plateNo / 中文表头 → c5)
+update ai_clean_field
+set field_name_en = column_name
+where field_name_en is distinct from column_name;
+
+-- 2) 收窄列宽;回填后 column_name 都是 c{位置},长度不会超过 5
+alter table ai_clean_field alter column column_name type varchar(5);
+alter table ai_clean_field alter column field_name_en type varchar(5);