From e6886ee977d687692c09a467c5538cacb5d8891e Mon Sep 17 00:00:00 2001
From: zichun <26684461+reporkey@users.noreply.github.com>
Date: Wed, 29 Jul 2026 11:45:07 +0800
Subject: [PATCH] audit HIGH fixes: FK resolve — candidates + exact-name priority + ambiguity error + shown=resolved record name (WYSIWYG); phantom-column cross-check vs information_schema (resolveColumn/businessFields/skeleton/normalize hard gate); ledger cold-read always warms full window (partial-window rebuild permanently truncated history); CORS origin allowlist (configurable, default localhost) replacing wildcard+credentials; stale 'confirmed' docs
---
docs/agent-architecture.md | 3 ++-
src/main/java/com/xly/config/CorsConfig.java | 65 +++++++++++++++++++----------------------------------------------
src/main/java/com/xly/service/FormRenderService.java | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
src/main/java/com/xly/service/FormResolverService.java | 47 +++++++++++++++++++++++++++++++++++------------
src/main/java/com/xly/service/LedgerService.java | 13 ++++++++-----
src/main/java/com/xly/web/AgentChatController.java | 2 +-
src/test/java/com/xly/service/FormRenderNormalizeTest.java | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 213 insertions(+), 86 deletions(-)
create mode 100644 src/test/java/com/xly/service/FormRenderNormalizeTest.java
diff --git a/docs/agent-architecture.md b/docs/agent-architecture.md
index 1dc4e66..cf38098 100644
--- a/docs/agent-architecture.md
+++ b/docs/agent-architecture.md
@@ -516,7 +516,8 @@ ai_op_queue(
LLM 窗口截断的孤儿 tool_result 丢弃。校准回路:`prompt_eval_count` vs 估算+定额,超出即告警
(TracingChatModelListener)。
- 流程卡新语义:previewChange 预览行("尚未写入");`queued` 后技能线摘下,待办行**只读**展示
- `ai_op_queue.sStatus` 进度(confirmed/executing/executed/failed),直到下一个流程事件替换。
+ `ai_op_queue.sStatus+iStatusCode` 进度(pending 100/101、success 200/201、fail 4xx/5xx),
+ 直到下一个流程事件替换。
### 21.3 技能:ai_skill 表存在开关
diff --git a/src/main/java/com/xly/config/CorsConfig.java b/src/main/java/com/xly/config/CorsConfig.java
index cce455e..193465c 100644
--- a/src/main/java/com/xly/config/CorsConfig.java
+++ b/src/main/java/com/xly/config/CorsConfig.java
@@ -1,70 +1,43 @@
package com.xly.config;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-/***
- * @Author 钱豹
- * @Date 22:40 2026/2/3
- * @Param
- * @return
- * @Description 跨域配置
- **/
+/**
+ * CORS:**origin 白名单**(可配置 {@code xly.cors.allowed-origins},逗号分隔,支持通配段)。
+ *
+ *
不再对所有域名放行——通配 + allowCredentials 意味着浏览器里任意网页都能跨域驱动本服务
+ * (本地 dev-login 开启时无凭证即可入队 ERP 写操作)。生产走 nginx 同源代理本就不需要 CORS;
+ * 出现合法跨域消费方时把它的 origin 加进配置即可。
+ */
@Configuration
public class CorsConfig {
- /**
- * 允许所有跨域请求 - CorsFilter方式
- */
+ /** 默认只放行本机开发场景的 origin。 */
+ @Value("${xly.cors.allowed-origins:http://localhost:*,http://127.0.0.1:*,http://host.docker.internal:*}")
+ private String allowedOrigins;
+
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
-
- // 允许所有域名
- config.addAllowedOriginPattern("*");
-
- // 允许所有请求方法
+ for (String o : allowedOrigins.split(",")) {
+ String t = o.trim();
+ if (!t.isEmpty()) {
+ config.addAllowedOriginPattern(t);
+ }
+ }
config.addAllowedMethod("*");
-
- // 允许所有请求头
config.addAllowedHeader("*");
-
- // 允许携带凭证(如cookies)
config.setAllowCredentials(true);
-
- // 暴露所有响应头
config.addExposedHeader("*");
-
- // 预检请求缓存时间
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
-
return new CorsFilter(source);
}
-
- /**
- * 允许所有跨域请求 - WebMvcConfigurer方式
- */
- @Bean
- public WebMvcConfigurer corsConfigurer() {
- return new WebMvcConfigurer() {
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOriginPatterns("*") // 使用 allowedOriginPatterns 代替 allowedOrigins
- .allowedMethods("*")
- .allowedHeaders("*")
- .exposedHeaders("*")
- .allowCredentials(true)
- .maxAge(3600);
- }
- };
- }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/xly/service/FormRenderService.java b/src/main/java/com/xly/service/FormRenderService.java
index 81d5d17..a6d6543 100644
--- a/src/main/java/com/xly/service/FormRenderService.java
+++ b/src/main/java/com/xly/service/FormRenderService.java
@@ -93,7 +93,8 @@ public class FormRenderService {
"AND IFNULL(bReadonly,0)=0 ORDER BY iOrder LIMIT " + MAX_FIELDS, formId);
for (Map c : cols) {
String name = str(c.get("sName"));
- if (name == null || name.isBlank() || resolver.isSystemColumn(name)) {
+ if (name == null || name.isBlank() || resolver.isSystemColumn(name)
+ || !types.containsKey(name)) { // 界面元数据同样含幻影列
continue;
}
Map f = new LinkedHashMap<>();
@@ -214,23 +215,30 @@ public class FormRenderService {
// ---------------------------------------------------------------- 字段解析与规范化
- /** 字段中文名 → {col, fk}(先精确合并模糊,取使用度最高的一列)。找不到返回 null。 */
+ /**
+ * 字段中文名 → {col, fk}(先精确合并模糊,按使用度排序)。字段字典由表单控件名构建,
+ * 有大量**幻影列**(不存在于物理表)——只返回物理存在的候选,找不到返回 null。
+ */
public Map resolveColumn(String table, String zh) {
try {
+ Map types = resolver.columnTypes(table);
List