Dify 配置外置并切至内网实例
接口地址与各应用 API Key 原先硬编码在 AiController 与 KnowledgeBaseController 中,换环境要改代码、密钥还会散落在多处。 新增 DifyProperties 统一收敛到 dify 配置节点,并补上文档中缺失的 两个应用与知识库管理接口。 - 新增 DifyProperties,外置 base-url / 8 个应用 Key / 知识库密钥, 默认值保持公网实例,本地开发不受影响 - 生产 profile 覆盖为内网 10.100.158.15,含全部 8 个应用 Key 与 两个知识库的独立密钥 - 新增申报书生成、智能文档处理两个工作流接口(含文件上传版本), 接入既有的 WebSocket 流式推送与停止机制 - KnowledgeBaseController 补齐知识库增删改查、文档管理与索引进度, 并对文档状态动作加白名单,避免路径参数被任意拼接 - 修正 AI 内容创作的 number 变量名:Dify 侧为 Number, 变量名大小写敏感,此前一直发送小写导致该功能缺参 - 生产下显式覆盖公网知识库 ID 的密钥:YAML 的 map 按键合并而非整体 替换,不覆盖会残留指向公网实例的死密钥 工作流变量名取自内网实例实测(probe-dify.sh),其中申报书生成与 文档处理两组变量与公网命名习惯不一致,故以配置映射承载,改名只需 改 yml 而非改代码。 新增 18 项测试,覆盖配置绑定、profile 覆盖完整性(逐项断言 8 个 Key 均已覆盖且不残留公网 Key)与入参到 Dify inputs 的映射。
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
package com.gxwebsoft.ai.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* 校验生产 profile 的 Dify 覆盖是否完整。
|
||||
*
|
||||
* <p>背景:Dify 内网实例与公网实例的应用 Key、知识库 ID、密钥<b>完全不通用</b>。
|
||||
* 切换时若漏覆盖某一项,运行期会表现为该功能单独报 401/参数错误,
|
||||
* 而 Spring 的松散绑定不会给出任何警告 —— 这类"漏一项"极难在测试环境发现。
|
||||
* 因此这里逐项断言 prod 覆盖的完整性。
|
||||
*
|
||||
* <p>本测试不启动容器,只做 application.yml + application-prod.yml 的绑定,
|
||||
* 不依赖数据库与 Redis。
|
||||
*/
|
||||
class DifyProdProfileBindingTest {
|
||||
|
||||
/**
|
||||
* 模拟 Spring 的 profile 加载顺序。
|
||||
*
|
||||
* <p>PropertySource 是<b>先匹配先生效</b>:列表靠前的优先级更高。
|
||||
* 因此必须把 application-prod.yml 放到 application.yml <b>之前</b>,
|
||||
* 才能复现"profile 覆盖默认值"的真实行为。
|
||||
*
|
||||
* <p>第一版误用了 addLast 放 profile,导致默认值反而胜出、测试假失败,
|
||||
* 排查花了些时间 —— 这里保留说明,避免以后再踩。
|
||||
*/
|
||||
private DifyProperties loadProd() throws Exception {
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
// 默认值先设进 SystemProperties 之后(最低),再由 profile 覆盖
|
||||
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
|
||||
for (PropertySource<?> source : loader.load("application", new ClassPathResource("application.yml"))) {
|
||||
environment.getPropertySources().addLast(source);
|
||||
}
|
||||
// addFirst:profile 优先级高于 application.yml
|
||||
for (PropertySource<?> source : loader.load("application-prod",
|
||||
new ClassPathResource("application-prod.yml"))) {
|
||||
environment.getPropertySources().addFirst(source);
|
||||
}
|
||||
return Binder.get(environment).bind("dify", DifyProperties.class).orElse(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void 生产环境指向内网实例() throws Exception {
|
||||
DifyProperties properties = loadProd();
|
||||
assertNotNull(properties);
|
||||
assertEquals("http://10.100.158.15/v1", properties.getBaseUrl(),
|
||||
"生产必须指向内网实例");
|
||||
}
|
||||
|
||||
@Test
|
||||
void 生产环境八个应用Key全部覆盖为内网Key() throws Exception {
|
||||
DifyProperties.AppKeys apps = loadProd().getApps();
|
||||
assertNotNull(apps);
|
||||
|
||||
// 逐个断言:任一为 null 或仍是公网 Key 都说明覆盖漏了
|
||||
assertEquals("app-ZLUUbLkAX3FVzDubY06puxAo", apps.getChat(), "chat");
|
||||
assertEquals("app-4oBWB3HrdNlANdPFG8ZvVTkY", apps.getActivityOutline(), "activity-outline");
|
||||
assertEquals("app-ohdIhk0lVfUwoy10xXRebmjD", apps.getDocumentProcess(), "document-process");
|
||||
assertEquals("app-fhhz4fXMkEdVivAEiuWpycn6", apps.getManuscript(), "manuscript");
|
||||
assertEquals("app-lAbpxMggNbIrByiDRDfckSSl", apps.getCreativeAssistant(), "creative-assistant");
|
||||
assertEquals("app-J6h5I6Bo4oeBif2EofBGVrGs", apps.getProjectQualityReview(), "project-quality-review");
|
||||
assertEquals("app-k20aDmiQYh3LxqZSLuX8wjkU", apps.getDeclaration(), "declaration");
|
||||
assertEquals("app-UzVLKnGwg69uAUdbgFx6XNmc", apps.getCompetitionHistoryPreprocess(),
|
||||
"competition-history-preprocess");
|
||||
}
|
||||
|
||||
@Test
|
||||
void 生产环境不残留任何公网Key() throws Exception {
|
||||
DifyProperties.AppKeys apps = loadProd().getApps();
|
||||
|
||||
// 公网实例的 6 个存量 Key,生产环境里一个都不该出现
|
||||
List<String> publicKeys = new ArrayList<>();
|
||||
publicKeys.add("app-ou0qPesouiopaPpmxcFV2gFG");
|
||||
publicKeys.add("app-hp4PJknbejImmdDuMfziVcnN");
|
||||
publicKeys.add("app-hCzkdd6OsnMnwI2dQByLyNIN");
|
||||
publicKeys.add("app-l3accBEJLDAh9Pn8fLW8nNJ0");
|
||||
publicKeys.add("app-HiI2FrNjcwL0A7QOOf7GIP50");
|
||||
publicKeys.add("app-bhyQLMAw2JEDXMTs2mzNRiIU");
|
||||
|
||||
List<String> actual = new ArrayList<>();
|
||||
actual.add(apps.getChat());
|
||||
actual.add(apps.getActivityOutline());
|
||||
actual.add(apps.getManuscript());
|
||||
actual.add(apps.getCreativeAssistant());
|
||||
actual.add(apps.getProjectQualityReview());
|
||||
actual.add(apps.getCompetitionHistoryPreprocess());
|
||||
actual.add(apps.getDocumentProcess());
|
||||
actual.add(apps.getDeclaration());
|
||||
|
||||
for (String pub : publicKeys) {
|
||||
assertTrue(!actual.contains(pub),
|
||||
"生产环境残留公网 Key: " + pub + ",会导致该功能 401");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void 生产环境知识库密钥指向内网() throws Exception {
|
||||
DifyProperties.Dataset dataset = loadProd().getDataset();
|
||||
|
||||
assertEquals("dataset-jaqtnAQvYcQTEjECzxhQfNCv", dataset.getApiKey());
|
||||
|
||||
// 内网两个库各自的独立密钥
|
||||
assertEquals("dataset-jaqtnAQvYcQTEjECzxhQfNCv",
|
||||
dataset.keyOf("181d58e3-78c2-4b7d-939c-57e780a8f7d6"));
|
||||
assertEquals("dataset-f2a0Hjxix7lBcPNZtNMyKDTQ",
|
||||
dataset.keyOf("3e0ac108-3a0d-41e2-8447-aa1c1044d3ac"));
|
||||
|
||||
// 公网知识库 ID 仍登记,但生产下必须也指到内网密钥,不能残留公网密钥
|
||||
assertEquals("dataset-jaqtnAQvYcQTEjECzxhQfNCv",
|
||||
dataset.keyOf("b342f096-98b1-4a83-a296-f4ab60a6db0e"),
|
||||
"公网库 ID 在生产残留了公网密钥,属死密钥,应显式覆盖为内网密钥");
|
||||
|
||||
// 明确断言:生产下任何知识库 ID 都不得解析出公网密钥
|
||||
for (String id : dataset.getKeys().keySet()) {
|
||||
assertTrue(!"dataset-1n6MECRtQvEVGOL1o7SrSGfe".equals(dataset.keyOf(id)),
|
||||
"生产环境知识库 " + id + " 残留公网密钥 dataset-1n6MECRt...");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void 生产环境知识库ID登记了内网两个库() throws Exception {
|
||||
DifyProperties.Dataset dataset = loadProd().getDataset();
|
||||
|
||||
assertEquals("181d58e3-78c2-4b7d-939c-57e780a8f7d6", dataset.idOf("competition-cases"));
|
||||
assertEquals("3e0ac108-3a0d-41e2-8447-aa1c1044d3ac",
|
||||
dataset.idOf("gxmu-constitution-internal"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 生产环境保留工作流变量映射() throws Exception {
|
||||
// profile 覆盖了 base-url 与密钥,但不应把 workflows 映射冲掉
|
||||
DifyProperties.WorkflowVariables workflows = loadProd().getWorkflows();
|
||||
assertNotNull(workflows);
|
||||
|
||||
assertEquals("core_idea", workflows.getDeclaration().variableOf("core-idea"));
|
||||
assertEquals("pdf_file", workflows.getDocumentProcess().variableOf("pdf-file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 本地默认仍指向公网实例() throws Exception {
|
||||
// application.yml 默认值不得被改成内网,否则本地开发连不上
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
for (PropertySource<?> source : new YamlPropertySourceLoader()
|
||||
.load("application", new ClassPathResource("application.yml"))) {
|
||||
environment.getPropertySources().addLast(source);
|
||||
}
|
||||
DifyProperties properties = Binder.get(environment).bind("dify", DifyProperties.class).orElse(null);
|
||||
assertNotNull(properties);
|
||||
assertEquals("http://82.156.98.71/v1", properties.getBaseUrl(),
|
||||
"application.yml 默认值应保持公网,避免本地开发被切到内网");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.gxwebsoft.ai.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
/**
|
||||
* 校验 {@code application.yml} 中的 dify 配置能正确绑定到 {@link DifyProperties}。
|
||||
*
|
||||
* <p>纯绑定测试,不启动 Spring 容器,因此不依赖数据库与 Redis,
|
||||
* 可以在本地直接跑。主要用于防止 yml 里 kebab-case 与 Java 驼峰对不上、
|
||||
* 或缩进写错导致配置静默为 null。
|
||||
*/
|
||||
class DifyPropertiesBindingTest {
|
||||
|
||||
private DifyProperties load() throws Exception {
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
for (PropertySource<?> source : new YamlPropertySourceLoader()
|
||||
.load("application", new ClassPathResource("application.yml"))) {
|
||||
environment.getPropertySources().addLast(source);
|
||||
}
|
||||
return Binder.get(environment).bind("dify", DifyProperties.class).orElse(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void 配置能绑定且关键字段不为空() throws Exception {
|
||||
DifyProperties properties = load();
|
||||
assertNotNull(properties, "dify 配置未绑定,请检查 application.yml 缩进");
|
||||
|
||||
assertEquals("http://82.156.98.71/v1", properties.getBaseUrl());
|
||||
assertEquals(600000, properties.getTimeoutMs());
|
||||
|
||||
DifyProperties.AppKeys apps = properties.getApps();
|
||||
assertNotNull(apps);
|
||||
assertEquals("app-ou0qPesouiopaPpmxcFV2gFG", apps.getChat());
|
||||
assertEquals("app-hp4PJknbejImmdDuMfziVcnN", apps.getActivityOutline());
|
||||
assertEquals("app-hCzkdd6OsnMnwI2dQByLyNIN", apps.getManuscript());
|
||||
assertEquals("app-l3accBEJLDAh9Pn8fLW8nNJ0", apps.getCreativeAssistant());
|
||||
assertEquals("app-HiI2FrNjcwL0A7QOOf7GIP50", apps.getProjectQualityReview());
|
||||
assertEquals("app-bhyQLMAw2JEDXMTs2mzNRiIU", apps.getCompetitionHistoryPreprocess());
|
||||
// 新增的两个应用在 yml 里用了环境变量占位,需确认占位符被解析
|
||||
assertEquals("app-ohdIhk0lVfUwoy10xXRebmjD", apps.getDocumentProcess());
|
||||
assertEquals("app-k20aDmiQYh3LxqZSLuX8wjkU", apps.getDeclaration());
|
||||
}
|
||||
|
||||
@Test
|
||||
void 按代号取密钥可用() throws Exception {
|
||||
DifyProperties.AppKeys apps = load().getApps();
|
||||
|
||||
assertEquals("app-ou0qPesouiopaPpmxcFV2gFG", apps.byCode("chat"));
|
||||
assertEquals("app-k20aDmiQYh3LxqZSLuX8wjkU", apps.byCode("declaration"));
|
||||
assertEquals("app-ohdIhk0lVfUwoy10xXRebmjD", apps.byCode("documentProcess"));
|
||||
// 未登记的代号返回 null,而不是静默回退到某个应用
|
||||
assertNull(apps.byCode("not-exist"));
|
||||
assertNull(apps.byCode(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 知识库配置绑定正确() throws Exception {
|
||||
DifyProperties.Dataset dataset = load().getDataset();
|
||||
|
||||
assertEquals("dataset-1n6MECRtQvEVGOL1o7SrSGfe", dataset.getApiKey());
|
||||
assertEquals("b342f096-98b1-4a83-a296-f4ab60a6db0e", dataset.idOf("challenge-cup"));
|
||||
assertEquals("aa72c649-71a0-46e7-ac55-47f44b397a4e", dataset.idOf("gxmu-constitution"));
|
||||
// 已登记的库返回自己的密钥
|
||||
assertEquals("dataset-1n6MECRtQvEVGOL1o7SrSGfe",
|
||||
dataset.keyOf("b342f096-98b1-4a83-a296-f4ab60a6db0e"));
|
||||
// 未登记的库回退到默认密钥
|
||||
assertEquals("dataset-1n6MECRtQvEVGOL1o7SrSGfe", dataset.keyOf("unknown-id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 工作流变量映射绑定正确() throws Exception {
|
||||
DifyProperties.WorkflowVariables workflows = load().getWorkflows();
|
||||
assertNotNull(workflows);
|
||||
|
||||
DifyProperties.VariableMapping declaration = workflows.getDeclaration();
|
||||
assertEquals("core_idea", declaration.variableOf("core-idea"));
|
||||
assertEquals("tech_route", declaration.variableOf("tech-route"));
|
||||
assertEquals("team_intro", declaration.variableOf("team-intro"));
|
||||
assertEquals("upload_file", declaration.variableOf("upload-file"));
|
||||
assertEquals("parse_value", declaration.variableOf("parse-value"));
|
||||
// 未登记的字段按原名透传
|
||||
assertEquals("unmapped", declaration.variableOf("unmapped"));
|
||||
|
||||
DifyProperties.VariableMapping documentProcess = workflows.getDocumentProcess();
|
||||
assertEquals("query", documentProcess.variableOf("query"));
|
||||
assertEquals("pdf_url", documentProcess.variableOf("pdf-url"));
|
||||
assertEquals("pdf_file", documentProcess.variableOf("pdf-file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 知识库登记了公网与内网两套() throws Exception {
|
||||
DifyProperties.Dataset dataset = load().getDataset();
|
||||
List<String> ids = new java.util.ArrayList<>(dataset.getIds().keySet());
|
||||
assertEquals(4, ids.size(), "dify.dataset.ids 应登记 4 个知识库,实际: " + ids);
|
||||
|
||||
// 内网实例的两个库各自有独立密钥,不能都回退到默认密钥
|
||||
assertEquals("dataset-jaqtnAQvYcQTEjECzxhQfNCv",
|
||||
dataset.keyOf("181d58e3-78c2-4b7d-939c-57e780a8f7d6"));
|
||||
assertEquals("dataset-f2a0Hjxix7lBcPNZtNMyKDTQ",
|
||||
dataset.keyOf("3e0ac108-3a0d-41e2-8447-aa1c1044d3ac"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.gxwebsoft.ai.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.gxwebsoft.ai.config.DifyProperties;
|
||||
import com.gxwebsoft.ai.entity.DeclarationInputs;
|
||||
import com.gxwebsoft.ai.entity.DocumentProcessInputs;
|
||||
import com.gxwebsoft.common.core.utils.JSONUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* 校验「申报书生成」「智能文档处理」的入参到 Dify inputs 的映射。
|
||||
*
|
||||
* <p>变量名以 2026-09-19 在内网实例 10.100.158.15 上跑 probe-dify.sh 的实测输出为准:
|
||||
* <pre>
|
||||
* document-process : query, pdf_url, pdf_file(文件类)
|
||||
* declaration : core_idea, tech_route, team_intro, upload_file(文件类), parse_value
|
||||
* </pre>
|
||||
* 这两组名字与公网实例的命名习惯并不一致,因此这里既固定映射行为,
|
||||
* 也直接从 application.yml 读配置来断言,防止后人"顺手改成看起来更合理"的名字。
|
||||
*/
|
||||
class AiInputMappingTest {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 复刻 AiController#objectToMap 的实现(该方法为 private,此处等价重写)
|
||||
*/
|
||||
private Map<String, Object> objectToMap(Object source) throws Exception {
|
||||
String json = objectMapper.writeValueAsString(source);
|
||||
Map<String, Object> map = JSONUtil.parseObject(json, Map.class);
|
||||
return map == null ? new LinkedHashMap<>() : map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复刻 AiController#remapInputs 的实现
|
||||
*/
|
||||
private Map<String, Object> remap(Map<String, Object> raw, DifyProperties.VariableMapping mapping) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : raw.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
if (value instanceof String && ((String) value).trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String variable = mapping.variableOf(entry.getKey());
|
||||
if (variable == null || variable.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
result.put(variable, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 application.yml 读取真实的映射配置
|
||||
*/
|
||||
private DifyProperties loadProperties() throws Exception {
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
for (PropertySource<?> source : new YamlPropertySourceLoader()
|
||||
.load("application", new ClassPathResource("application.yml"))) {
|
||||
environment.getPropertySources().addLast(source);
|
||||
}
|
||||
return Binder.get(environment).bind("dify", DifyProperties.class).orElse(null);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 申报书生成
|
||||
|
||||
@Test
|
||||
void 申报书生成_映射为内网实例的真实变量名() throws Exception {
|
||||
DifyProperties properties = loadProperties();
|
||||
assertNotNull(properties);
|
||||
DifyProperties.VariableMapping mapping = properties.getWorkflows().getDeclaration();
|
||||
|
||||
DeclarationInputs inputs = new DeclarationInputs();
|
||||
inputs.setCoreIdea("做一个校园二手书平台");
|
||||
inputs.setTechRoute("Spring Boot + 小程序");
|
||||
inputs.setTeamIntro("3 人,均为计算机专业");
|
||||
inputs.setParseValue("true");
|
||||
|
||||
Map<String, Object> result = remap(objectToMap(inputs), mapping);
|
||||
|
||||
assertEquals("做一个校园二手书平台", result.get("core_idea"));
|
||||
assertEquals("Spring Boot + 小程序", result.get("tech_route"));
|
||||
assertEquals("3 人,均为计算机专业", result.get("team_intro"));
|
||||
assertEquals("true", result.get("parse_value"));
|
||||
// 不能残留业务字段名
|
||||
assertFalse(result.containsKey("coreIdea"));
|
||||
assertFalse(result.containsKey("teamIntro"));
|
||||
assertFalse(result.containsKey("parseValue"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 申报书生成_上传文件映射为upload_file() throws Exception {
|
||||
DifyProperties.VariableMapping mapping = loadProperties().getWorkflows().getDeclaration();
|
||||
|
||||
DeclarationInputs inputs = new DeclarationInputs();
|
||||
inputs.setCoreIdea("创意");
|
||||
Map<String, Object> fileValue = new LinkedHashMap<>();
|
||||
fileValue.put("transfer_method", "local_file");
|
||||
fileValue.put("upload_file_id", "abc-123");
|
||||
inputs.setUploadFile(fileValue);
|
||||
|
||||
Map<String, Object> result = remap(objectToMap(inputs), mapping);
|
||||
|
||||
assertEquals(fileValue, result.get("upload_file"));
|
||||
assertFalse(result.containsKey("uploadFile"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- 智能文档处理
|
||||
|
||||
@Test
|
||||
void 文档处理_映射为内网实例的真实变量名() throws Exception {
|
||||
DifyProperties.VariableMapping mapping = loadProperties().getWorkflows().getDocumentProcess();
|
||||
|
||||
DocumentProcessInputs inputs = new DocumentProcessInputs();
|
||||
inputs.setQuery("请解析并入库");
|
||||
inputs.setPdfUrl("http://example.com/a.pdf");
|
||||
Map<String, Object> fileValue = new LinkedHashMap<>();
|
||||
fileValue.put("transfer_method", "local_file");
|
||||
fileValue.put("upload_file_id", "pdf-1");
|
||||
inputs.setPdfFile(fileValue);
|
||||
|
||||
Map<String, Object> result = remap(objectToMap(inputs), mapping);
|
||||
|
||||
assertEquals("请解析并入库", result.get("query"));
|
||||
assertEquals("http://example.com/a.pdf", result.get("pdf_url"));
|
||||
assertEquals(fileValue, result.get("pdf_file"));
|
||||
assertFalse(result.containsKey("pdfUrl"));
|
||||
assertFalse(result.containsKey("pdfFile"));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 通用行为
|
||||
|
||||
@Test
|
||||
void 空字段不下发() throws Exception {
|
||||
DifyProperties.VariableMapping mapping = loadProperties().getWorkflows().getDeclaration();
|
||||
|
||||
DeclarationInputs inputs = new DeclarationInputs();
|
||||
inputs.setCoreIdea("创意");
|
||||
inputs.setTechRoute(" "); // 纯空白应被丢弃
|
||||
inputs.setTeamIntro(null);
|
||||
|
||||
Map<String, Object> result = remap(objectToMap(inputs), mapping);
|
||||
|
||||
assertEquals(1, result.size(), "只应保留 core_idea,实际: " + result.keySet());
|
||||
assertTrue(result.containsKey("core_idea"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 未登记字段按原名透传() throws Exception {
|
||||
DifyProperties.VariableMapping mapping = loadProperties().getWorkflows().getDeclaration();
|
||||
|
||||
DeclarationInputs inputs = new DeclarationInputs();
|
||||
inputs.setCoreIdea("创意");
|
||||
|
||||
Map<String, Object> raw = objectToMap(inputs);
|
||||
raw.put("extraField", "额外字段");
|
||||
|
||||
Map<String, Object> result = remap(raw, mapping);
|
||||
|
||||
assertEquals("额外字段", result.get("extraField"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void 映射配置覆盖了全部真实变量() throws Exception {
|
||||
DifyProperties properties = loadProperties();
|
||||
|
||||
// 内网探针实测出的变量全集,少一个就说明 yml 里漏配
|
||||
Map<String, String> declaration = properties.getWorkflows().getDeclaration().getFields();
|
||||
assertTrue(declaration.containsValue("core_idea"), "缺少 core_idea");
|
||||
assertTrue(declaration.containsValue("tech_route"), "缺少 tech_route");
|
||||
assertTrue(declaration.containsValue("team_intro"), "缺少 team_intro");
|
||||
assertTrue(declaration.containsValue("upload_file"), "缺少 upload_file");
|
||||
assertTrue(declaration.containsValue("parse_value"), "缺少 parse_value");
|
||||
|
||||
Map<String, String> document = properties.getWorkflows().getDocumentProcess().getFields();
|
||||
assertTrue(document.containsValue("query"), "缺少 query");
|
||||
assertTrue(document.containsValue("pdf_url"), "缺少 pdf_url");
|
||||
assertTrue(document.containsValue("pdf_file"), "缺少 pdf_file");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user