|
|
@@ -39,6 +39,7 @@ import com.zsjz.ai.module.aiclean.probe.SheetProbe;
|
|
|
import com.zsjz.ai.module.aiclean.rule.AiRulePlan;
|
|
|
import com.zsjz.ai.module.aiclean.rule.CompiledColumn;
|
|
|
import com.zsjz.ai.module.aiclean.store.AiCleanStore;
|
|
|
+import com.zsjz.ai.module.aiclean.support.HeaderFingerprint;
|
|
|
import com.zsjz.ai.module.aiclean.verify.AiRuleVerifier;
|
|
|
import com.zsjz.ai.module.dm.mapper.FileInfoMapper;
|
|
|
import com.zsjz.ai.module.agent.llm.LlmResult;
|
|
|
@@ -292,28 +293,34 @@ public class AiCleanService {
|
|
|
+ ",需人工确认后再清洗");
|
|
|
}
|
|
|
if (tpl != null) {
|
|
|
- List<RuleBind> stored = store.loadBinds(tpl.getId());
|
|
|
- List<AiCleanField> storedFields = stored.isEmpty() ? List.of()
|
|
|
- : fieldMapper.selectList(Wrappers.<AiCleanField>lambdaQuery()
|
|
|
- .eq(AiCleanField::getAiTemplateId, tpl.getId())
|
|
|
- .orderByAsc(AiCleanField::getFieldSort));
|
|
|
- if (!stored.isEmpty() && !storedFields.isEmpty()) {
|
|
|
- // 复用路径也要抽样校验:存档类型/必填可能已经不适配当前文件,
|
|
|
- // 不校验就直接写,会把错列/错类型的数据静默落库。
|
|
|
- mark(job, AiCleanStatusEnum.VERIFYING);
|
|
|
- List<CompiledColumn> columns = AiRulePlan.forStoredTemplate(storedFields, stored).columns();
|
|
|
- List<Map<Integer, String>> sample = loadSample(root, sheet, ev, job);
|
|
|
- VerifyReport report = verify(sample, columns, outcome.candidate(), outcome, ev);
|
|
|
- job.setConfidence(java.math.BigDecimal.valueOf(report.confidence().overall()));
|
|
|
- job.setDetail(detailJson(report));
|
|
|
- if (requiredRejected(columns, report)) {
|
|
|
- return new AiJudgeResult.NeedReview("复用模板的必填列抽样错误率过高,需人工确认");
|
|
|
+ // 复用存档绑定的前提是「同序同表头」:打分腿是集合相似度,列序变了仍可能高分,
|
|
|
+ // 直接套旧 col_index 会把整列数据写错位置。
|
|
|
+ if (!HeaderFingerprint.sameOrderedHeaders(headers, outcome.candidate().headers())) {
|
|
|
+ log.info("AI 模板表头顺序/内容与当前文件不一致,不复用存档绑定: templateId={}", tpl.getId());
|
|
|
+ } else {
|
|
|
+ List<RuleBind> stored = store.loadBinds(tpl.getId());
|
|
|
+ List<AiCleanField> storedFields = stored.isEmpty() ? List.of()
|
|
|
+ : fieldMapper.selectList(Wrappers.<AiCleanField>lambdaQuery()
|
|
|
+ .eq(AiCleanField::getAiTemplateId, tpl.getId())
|
|
|
+ .orderByAsc(AiCleanField::getFieldSort));
|
|
|
+ if (!stored.isEmpty() && !storedFields.isEmpty()) {
|
|
|
+ // 复用路径也要抽样校验:存档类型/必填可能已经不适配当前文件,
|
|
|
+ // 不校验就直接写,会把错列/错类型的数据静默落库。
|
|
|
+ mark(job, AiCleanStatusEnum.VERIFYING);
|
|
|
+ List<CompiledColumn> columns = AiRulePlan.forStoredTemplate(storedFields, stored).columns();
|
|
|
+ List<Map<Integer, String>> sample = loadSample(root, sheet, ev, job);
|
|
|
+ VerifyReport report = verify(sample, columns, outcome.candidate(), outcome, ev);
|
|
|
+ job.setConfidence(java.math.BigDecimal.valueOf(report.confidence().overall()));
|
|
|
+ job.setDetail(detailJson(report));
|
|
|
+ if (requiredRejected(columns, report)) {
|
|
|
+ return new AiJudgeResult.NeedReview("复用模板的必填列抽样错误率过高,需人工确认");
|
|
|
+ }
|
|
|
+ if (!report.confidence().pass(AiCleanExitEnum.B)) {
|
|
|
+ return new AiJudgeResult.NeedReview(StrUtil.format(
|
|
|
+ "复用模板置信度未达出口 B 的放行线: {}", report.confidence()));
|
|
|
+ }
|
|
|
+ return new AiJudgeResult.AiPlan(ev, stored, outcome.candidate(), null, headers);
|
|
|
}
|
|
|
- if (!report.confidence().pass(AiCleanExitEnum.B)) {
|
|
|
- return new AiJudgeResult.NeedReview(StrUtil.format(
|
|
|
- "复用模板置信度未达出口 B 的放行线: {}", report.confidence()));
|
|
|
- }
|
|
|
- return new AiJudgeResult.AiPlan(ev, stored, outcome.candidate(), null, headers);
|
|
|
}
|
|
|
}
|
|
|
// 存档规则/字段缺失(历史异常数据)→ 落回原路:按本次表头问模型补绑定
|
|
|
@@ -441,7 +448,10 @@ public class AiCleanService {
|
|
|
boolean reuse = plan.candidate() != null && plan.candidate().aiTemplateId() != null;
|
|
|
if (reuse) {
|
|
|
tpl = templateMapper.selectById(plan.candidate().aiTemplateId());
|
|
|
- reuse = tpl != null && "ONLINE".equals(tpl.getStatus());
|
|
|
+ // 必须「同序同表头」才复用:打分腿是集合相似度,列序变了仍可能命中;
|
|
|
+ // 复用存档 col_index 会把数据写错列,宁可新建模板也不冒这个险。
|
|
|
+ reuse = tpl != null && "ONLINE".equals(tpl.getStatus())
|
|
|
+ && HeaderFingerprint.sameOrderedHeaders(headers, plan.candidate().headers());
|
|
|
if (!reuse) {
|
|
|
tpl = null;
|
|
|
}
|