diff --git a/tools/mms-mapping/API-buildIndexDebug.md b/tools/mms-mapping/API-buildIndexDebug.md deleted file mode 100644 index 6978956..0000000 --- a/tools/mms-mapping/API-buildIndexDebug.md +++ /dev/null @@ -1,308 +0,0 @@ -# buildIndexConfirmData / buildIndexSelection 标准 API 调试文档 - -## 1. 文档范围 - -本文档用于说明 `mms-mapping` 模块中 `buildIndexConfirmData`、`buildIndexSelection` 两个调试接口的标准调用方式、请求结构、响应规则和联调注意事项。 - -本文档内容以当前源码为准,主要对照以下实现: - -- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java` -- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexSelectionBuildService.java` -- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateRequest.java` -- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/BuildIndexSelectionRequest.java` -- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmGroupResponse.java` -- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexSelectionGroupResponse.java` - -说明: - -- 两个接口当前统一返回 `HttpResult` 标准包装。 -- 业务异常和参数异常仍由全局异常处理器统一包装。 -- 本次仅整理接口标准出口和调试文档,未改动索引组装算法。 -- 本次未执行 `mvn` 编译、打包或真实接口联调。 - -## 2. 接口一:buildIndexConfirmData - -### 2.1 基本信息 - -| 项 | 说明 | -| --- | --- | -| 接口名称 | `buildIndexConfirmData` | -| 请求方法 | `POST` | -| 请求路径 | `/api/mms-mapping/build-index-confirm-data` | -| Content-Type | `application/json` | -| 控制器入口 | `MappingController#buildIndexConfirmData` | -| 成功响应体 | `HttpResult>` | - -### 2.2 接口职责 - -该接口用于把 `getIcdMmsJson` 或 `getICD` 返回的 `indexCandidates` 转换成前端可直接展示和确认的弹窗模型。 - -转换后的确认模型主要补充以下信息: - -- 每个分组下去重后的标签列表 -- 每个标签可配置的目标报告 -- 每个标签多个目标报告的 `lnInst` 共同候选值 -- 当共同候选值只有一个时,自动给出 `defaultLnInst` - -### 2.3 请求体 - -请求体为 `IndexCandidateRequest[]`,也就是候选分组数组。 - -示例: - -```json -[ - { - "groupKey": "统计数据__DSSTATISTICDATA", - "groupDesc": "统计数据", - "reportCount": 2, - "templateLabels": [ - "A相", - "B相", - "C相", - "间谐波1" - ], - "reports": [ - { - "reportName": "brcbStatistic", - "dataSetName": "dsStatisticData", - "reportDesc": "统计数据报告", - "availableLnInstValues": [ - "1", - "2", - "3", - "11", - "12", - "13" - ] - }, - { - "reportName": "brcbStatisticInter", - "dataSetName": "dsStIHarm", - "reportDesc": "间谐波统计报告", - "availableLnInstValues": [ - "11", - "12", - "13" - ] - } - ] - } -] -``` - -### 2.4 成功响应示例 - -```json -{ - "code": "200", - "msg": "buildIndexConfirmData,成功", - "data": [ - { - "groupKey": "统计数据__DSSTATISTICDATA", - "groupDesc": "统计数据", - "labelItems": [ - { - "label": "A相", - "required": false, - "configurableOnce": true, - "commonLnInstValues": [ - "1", - "2", - "3" - ], - "targets": [ - { - "reportName": "brcbStatistic", - "dataSetName": "dsStatisticData", - "reportDesc": "统计数据报告", - "availableLnInstValues": [ - "1", - "2", - "3" - ] - } - ] - } - ] - } - ] -} -``` - -说明: - -- `code`、`msg`、`data` 为标准 `HttpResult` 包装字段。 -- `data` 中的每个元素对应一个确认分组。 -- 若请求体为空数组,当前实现会返回成功响应,`data` 为空数组。 - -## 3. 接口二:buildIndexSelection - -### 3.1 基本信息 - -| 项 | 说明 | -| --- | --- | -| 接口名称 | `buildIndexSelection` | -| 请求方法 | `POST` | -| 请求路径 | `/api/mms-mapping/build-index-selection` | -| Content-Type | `application/json` | -| 控制器入口 | `MappingController#buildIndexSelection` | -| 成功响应体 | `HttpResult>` | - -### 3.2 接口职责 - -该接口用于根据前端确认模型 `confirmData` 和用户最终选择结果 `confirmedData`,展开生成正式的 `indexSelection` 结构,供 `getMmsJson` 或 `getIcdMmsJson` 第二次提交时直接复用。 - -该接口会在生成前做以下校验: - -- `request` 不能为空 -- `confirmData` 不能为空 -- `confirmedData` 中不能出现未知分组 -- 每个分组下不能出现未知标签 -- 已启用标签必须选择合法的 `lnInst` -- `lnInst` 必须同时落在该标签所有目标报告的允许范围内 - -### 3.3 请求体 - -请求体为 `BuildIndexSelectionRequest`,包含两部分: - -- `confirmData`:接口一返回并由前端回传的确认模型 -- `confirmedData`:用户在界面上实际确认后的启用状态和 `lnInst` - -示例: - -```json -{ - "confirmData": [ - { - "groupKey": "harm", - "groupDesc": "谐波数据", - "labelItems": [ - { - "label": "A相", - "required": false, - "configurableOnce": true, - "defaultLnInst": "1", - "commonLnInstValues": [ - "1", - "2", - "3" - ], - "targets": [ - { - "reportName": "brcbStHarm", - "dataSetName": "dsStHarm", - "reportDesc": "谐波报告", - "availableLnInstValues": [ - "1", - "2", - "3" - ] - } - ] - } - ] - } - ], - "confirmedData": [ - { - "groupKey": "harm", - "labelItems": [ - { - "label": "A相", - "enabled": true, - "lnInst": "1" - } - ] - } - ] -} -``` - -### 3.4 成功响应示例 - -```json -{ - "code": "200", - "msg": "buildIndexSelection,成功", - "data": [ - { - "groupKey": "harm", - "groupDesc": "谐波数据", - "bindings": [ - { - "reportName": "brcbStHarm", - "dataSetName": "dsStHarm", - "label": "A相", - "lnInst": "1" - } - ] - } - ] -} -``` - -说明: - -- 返回结果中的 `data` 就是可直接提交给 `getMmsJson` / `getIcdMmsJson` 的 `indexSelection`。 -- 只有启用且校验通过的标签才会被展开进最终 `bindings`。 - -### 3.5 失败响应说明 - -如果请求结构不合法或业务校验失败,会进入全局异常处理,返回统一 `HttpResult`。常见场景包括: - -- `request` 为空 -- `confirmData` 为空 -- `confirmedData` 中存在未知分组 -- 某个标签未选择 `lnInst` -- 选择的 `lnInst` 不在共同候选范围内 -- 选择的 `lnInst` 不在目标报告允许范围内 - -示例: - -```json -{ - "code": "500", - "msg": "buildIndexSelection,参数异常", - "data": "分组【谐波数据】的标签【A相】未选择 lnInst" -} -``` - -说明: - -- 这里的 `code`、`msg` 以公共异常包装实现为准,不保证与示例完全一致。 -- 具体错误文案由 `IndexSelectionBuildService` 抛出的 `IllegalArgumentException` 决定。 - -## 4. 标准调试顺序 - -推荐按以下顺序联调: - -1. 先调用 `getIcdMmsJson` 或 `getICD` 获取 `indexCandidates` -2. 把 `indexCandidates` 原样提交给 `buildIndexConfirmData` -3. 前端根据返回的确认模型组装 `confirmedData` -4. 调用 `buildIndexSelection` 生成正式 `indexSelection` -5. 把 `indexSelection` 提交给 `getMmsJson` 或 `getIcdMmsJson` 生成正式 `mappingJson` - -## 5. curl 调试示例 - -### 5.1 buildIndexConfirmData - -```powershell -curl.exe -X POST "http://localhost:8080/api/mms-mapping/build-index-confirm-data" ` - -H "Content-Type: application/json" ` - -d "[{\"groupKey\":\"harm\",\"groupDesc\":\"谐波数据\",\"reportCount\":1,\"templateLabels\":[\"A相\",\"B相\",\"C相\"],\"reports\":[{\"reportName\":\"brcbStHarm\",\"dataSetName\":\"dsStHarm\",\"reportDesc\":\"谐波报告\",\"availableLnInstValues\":[\"1\",\"2\",\"3\"]}]}]" -``` - -### 5.2 buildIndexSelection - -```powershell -curl.exe -X POST "http://localhost:8080/api/mms-mapping/build-index-selection" ` - -H "Content-Type: application/json" ` - -d "{\"confirmData\":[{\"groupKey\":\"harm\",\"groupDesc\":\"谐波数据\",\"labelItems\":[{\"label\":\"A相\",\"required\":false,\"configurableOnce\":true,\"defaultLnInst\":\"1\",\"commonLnInstValues\":[\"1\",\"2\",\"3\"],\"targets\":[{\"reportName\":\"brcbStHarm\",\"dataSetName\":\"dsStHarm\",\"reportDesc\":\"谐波报告\",\"availableLnInstValues\":[\"1\",\"2\",\"3\"]}]}]}],\"confirmedData\":[{\"groupKey\":\"harm\",\"labelItems\":[{\"label\":\"A相\",\"enabled\":true,\"lnInst\":\"1\"}]}]}" -``` - -## 6. 当前边界 - -- 本文档只覆盖 `buildIndexConfirmData` 和 `buildIndexSelection` -- `getIcdMmsJson` 独立调试方式仍以 `API-getIcdMmsJson.md` 为准 -- 示例中的 `HttpResult` 字段和值为结构化示意,实际成功码和提示文案以公共响应实现为准 diff --git a/tools/mms-mapping/README.md b/tools/mms-mapping/README.md index 5f70f25..e263d6e 100644 --- a/tools/mms-mapping/README.md +++ b/tools/mms-mapping/README.md @@ -5,6 +5,7 @@ ## 0. 调试文档 - `getIcdMmsJson`:`API-getIcdMmsJson.md` +- `getXmlFromJson`:`API-getXmlFromJson.md` - `buildIndexConfirmData` / `buildIndexSelection`:`API-buildIndexDebug.md` ## 1. 接口信息 diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdToXmlResponseConverter.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdToXmlResponseConverter.java index c0140b7..d30fad9 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdToXmlResponseConverter.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdToXmlResponseConverter.java @@ -7,18 +7,26 @@ import com.njcn.gather.icd.mapping.pojo.vo.IcdToXmlResponse; import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse; import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse; import com.njcn.gather.icd.mapping.pojo.vo.MappingDocumentResponse; +import com.njcn.gather.icd.mapping.pojo.vo.XmlFileResponse; import org.springframework.stereotype.Component; @Component public class IcdToXmlResponseConverter { + /** 默认 XML 展示文件名。 */ + private static final String DEFAULT_XML_FILE_NAME = "mapping.xml"; + /** XML 内容类型。 */ + private static final String XML_CONTENT_TYPE = "application/xml"; + /** XML 文件编码。 */ + private static final String XML_ENCODING = "UTF-8"; + public IcdToXmlResponse fromResult(IcdToXmlGenerateResult result) { IcdToXmlResponse response = new IcdToXmlResponse(); response.setStatus(result.getStatus()); response.setMessage(result.getMessage()); response.setIedName(result.getIedName()); response.setLdInst(result.getLdInst()); - response.setSavedPath(result.getSavedPath()); + response.setXmlFile(buildXmlFile(result)); response.getProblems().addAll(result.getProblems()); if (result.getIndexAnalysis() != null && result.getIndexAnalysis().getCandidates() != null) { @@ -59,4 +67,36 @@ public class IcdToXmlResponseConverter { return response; } -} \ No newline at end of file + + /** + * 将 XML 内容包装为前端展示用的标准文件容器。 + */ + private XmlFileResponse buildXmlFile(IcdToXmlGenerateResult result) { + if (result.getXmlContent() == null || result.getXmlContent().isEmpty()) { + return null; + } + + XmlFileResponse xmlFile = new XmlFileResponse(); + xmlFile.setFileName(resolveXmlFileName(result)); + xmlFile.setContentType(XML_CONTENT_TYPE); + xmlFile.setEncoding(XML_ENCODING); + xmlFile.setContent(result.getXmlContent()); + return xmlFile; + } + + /** + * 优先使用 IED 名称构造文件名,缺失时回退默认名。 + */ + private String resolveXmlFileName(IcdToXmlGenerateResult result) { + String iedName = result.getIedName(); + if (iedName == null || iedName.trim().isEmpty()) { + return DEFAULT_XML_FILE_NAME; + } + + String safeName = iedName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim(); + if (safeName.isEmpty()) { + return DEFAULT_XML_FILE_NAME; + } + return safeName + ".xml"; + } +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/JsonToXmlConversionService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/JsonToXmlConversionService.java index 8395005..8109d0c 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/JsonToXmlConversionService.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/JsonToXmlConversionService.java @@ -54,14 +54,7 @@ public class JsonToXmlConversionService { InputStream templateStream, List ruleStreams, IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { - - // 1. 反序列化JSON为MappingDocument对象 - MappingDocument mappingDocument = objectMapper.readValue(mappingJson, MappingDocument.class); - - // 2. 使用现有的规则引擎生成XML - // 注意:这里复用RuleBasedXmlMappingService的核心逻辑 - // 但数据来源从ICD直接解析改为从MappingDocument提取 - String xmlContent = buildXmlFromMapping(mappingDocument, templateStream, ruleStreams, indexMapping); + String xmlContent = buildXmlContentFromJson(mappingJson, templateStream, ruleStreams, indexMapping); // 3. 保存为临时文件 Path tempPath = Files.createTempFile("converted_", ".xml"); @@ -69,6 +62,33 @@ public class JsonToXmlConversionService { return tempPath.toString(); } + + /** + * 从 JSON 字符串转换为 XML 内容,供前端直接展示。 + * + * @param mappingJson JSON 格式的映射文档 + * @param templateStream XML 模板流 + * @param ruleStreams 规则文件流列表 + * @param indexMapping 索引映射配置 + * @return 生成后的 XML 内容 + * @throws Exception 转换异常 + */ + public String buildXmlContentFromJson(String mappingJson, + InputStream templateStream, + List ruleStreams, + IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { + return buildXmlContentFromJson(mappingJson, templateStream, ruleStreams, indexMapping, null); + } + + public String buildXmlContentFromJson(String mappingJson, + InputStream templateStream, + List ruleStreams, + IcdToXmlMappingService.IndexMappingConfig indexMapping, + List methodDescribeList) throws Exception { + // 反序列化 JSON 后复用现有规则引擎生成 XML,避免文件输出链路重复实现。 + MappingDocument mappingDocument = objectMapper.readValue(mappingJson, MappingDocument.class); + return buildXmlFromMapping(mappingDocument, templateStream, ruleStreams, indexMapping, methodDescribeList); + } /** * 从MappingDocument构建XML内容 @@ -76,7 +96,8 @@ public class JsonToXmlConversionService { private String buildXmlFromMapping(MappingDocument mappingDocument, InputStream templateStream, List ruleStreams, - IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { + IcdToXmlMappingService.IndexMappingConfig indexMapping, + List methodDescribeList) throws Exception { String templateContent = readInputStreamToString(templateStream); @@ -84,7 +105,7 @@ public class JsonToXmlConversionService { xmlContent = fillReportControlsFromMapping(xmlContent, mappingDocument); - xmlContent = applyRulesFromMapping(xmlContent, mappingDocument, ruleStreams, indexMapping); + xmlContent = applyRulesFromMapping(xmlContent, mappingDocument, ruleStreams, indexMapping, methodDescribeList); return xmlContent; } @@ -217,21 +238,22 @@ public class JsonToXmlConversionService { private String applyRulesFromMapping(String xmlContent, MappingDocument mappingDocument, List ruleStreams, - IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { + IcdToXmlMappingService.IndexMappingConfig indexMapping, + List methodDescribeList) throws Exception { var mergedRules = mergeAllRulesDesc(ruleStreams, indexMapping); var mappingMetrics = extractMetricsFromMapping(mappingDocument); - System.out.println("========== 开始从JSON匹配规则 =========="); - System.out.println("规则总数: " + mergedRules.size()); - System.out.println("JSON中指标总数: " + mappingMetrics.size()); + addMethodDescribe(methodDescribeList, "========== 开始从JSON匹配规则 =========="); + addMethodDescribe(methodDescribeList, "规则总数: " + mergedRules.size()); + addMethodDescribe(methodDescribeList, "JSON中指标总数: " + mappingMetrics.size()); - var applicableRules = findApplicableRulesDesc(mergedRules, mappingMetrics); + var applicableRules = findApplicableRulesDesc(mergedRules, mappingMetrics, methodDescribeList); - System.out.println("匹配成功: " + applicableRules.size() + " 条规则"); - System.out.println("匹配失败: " + (mergedRules.size() - applicableRules.size()) + " 条规则"); - System.out.println("========== JSON规则匹配结束 ==========\n"); + addMethodDescribe(methodDescribeList, "匹配成功: " + applicableRules.size() + " 条规则"); + addMethodDescribe(methodDescribeList, "匹配失败: " + (mergedRules.size() - applicableRules.size()) + " 条规则"); + addMethodDescribe(methodDescribeList, "========== JSON规则匹配结束 =========="); return applyRulesToXml(xmlContent, applicableRules); } @@ -731,7 +753,8 @@ public class JsonToXmlConversionService { */ private java.util.Map findApplicableRulesDesc( java.util.Map> allRules, - java.util.Map mappingMetrics) { + java.util.Map mappingMetrics, + List methodDescribeList) { java.util.Map applicable = new java.util.HashMap<>(); List failedRules = new ArrayList<>(); @@ -753,7 +776,7 @@ public class JsonToXmlConversionService { rule.setDaPath(metric.getDaPath()); applicable.put(ruleKey, rule); matched = true; - System.out.println("✓ 规则匹配成功: " + ruleKey + " -> DO=" + rule.getDoPath() + ", DA=" + rule.getDaPath()); + addMethodDescribe(methodDescribeList, "✓ 规则匹配成功: " + ruleKey + " -> DO=" + rule.getDoPath() + ", DA=" + rule.getDaPath()); } @@ -765,22 +788,22 @@ public class JsonToXmlConversionService { if (!matched) { failedRules.add(ruleKey); - System.out.println("✗ 规则匹配失败: " + ruleKey + " (共" + ruleVariants.size() + "个候选)"); + addMethodDescribe(methodDescribeList, "✗ 规则匹配失败: " + ruleKey + " (共" + ruleVariants.size() + "个候选)"); for (RuleBasedXmlMappingService.ValueRule rule : ruleVariants) { - System.out.println(" 候选指标名: " + rule.getName()); + addMethodDescribe(methodDescribeList, " 候选指标名: " + rule.getName()); } } } - System.out.println("\n========== 详细匹配结果 =========="); - System.out.println("成功匹配的规则 (" + applicable.size() + " 条):"); + addMethodDescribe(methodDescribeList, "========== 详细匹配结果 =========="); + addMethodDescribe(methodDescribeList, "成功匹配的规则 (" + applicable.size() + " 条):"); for (String key : applicable.keySet()) { - System.out.println(" - " + key); + addMethodDescribe(methodDescribeList, " - " + key); } - System.out.println("\n失败匹配的规则 (" + failedRules.size() + " 条):"); + addMethodDescribe(methodDescribeList, "失败匹配的规则 (" + failedRules.size() + " 条):"); for (String key : failedRules) { - System.out.println(" - " + key); + addMethodDescribe(methodDescribeList, " - " + key); } return applicable; @@ -818,7 +841,7 @@ public class JsonToXmlConversionService { if (matchesPattern(ruleDo, icdDo) && matchesPattern(ruleDa, icdDa)) { applicable.put(ruleKey, rule); matched = true; - System.out.println("✓ 规则匹配成功: " + ruleKey + " -> DO=" + rule.getDoPath() + ", DA=" + rule.getDaPath()); + addMethodDescribe(null, "✓ 规则匹配成功: " + ruleKey + " -> DO=" + rule.getDoPath() + ", DA=" + rule.getDaPath()); break; } } @@ -830,22 +853,22 @@ public class JsonToXmlConversionService { if (!matched) { failedRules.add(ruleKey); - System.out.println("✗ 规则匹配失败: " + ruleKey + " (共" + ruleVariants.size() + "个候选)"); + addMethodDescribe(null, "✗ 规则匹配失败: " + ruleKey + " (共" + ruleVariants.size() + "个候选)"); for (RuleBasedXmlMappingService.ValueRule rule : ruleVariants) { - System.out.println(" 候选: DO=" + rule.getDoPath() + ", DA=" + rule.getDaPath()); + addMethodDescribe(null, " 候选: DO=" + rule.getDoPath() + ", DA=" + rule.getDaPath()); } } } - System.out.println("\n========== 详细匹配结果 =========="); - System.out.println("成功匹配的规则 (" + applicable.size() + " 条):"); + addMethodDescribe(null, "========== 详细匹配结果 =========="); + addMethodDescribe(null, "成功匹配的规则 (" + applicable.size() + " 条):"); for (String key : applicable.keySet()) { - System.out.println(" - " + key); + addMethodDescribe(null, " - " + key); } - System.out.println("\n失败匹配的规则 (" + failedRules.size() + " 条):"); + addMethodDescribe(null, "失败匹配的规则 (" + failedRules.size() + " 条):"); for (String key : failedRules) { - System.out.println(" - " + key); + addMethodDescribe(null, " - " + key); } return applicable; @@ -952,6 +975,13 @@ public class JsonToXmlConversionService { .replace("\"", """) .replace("'", "'"); } + + private void addMethodDescribe(List methodDescribeList, String message) { + if (methodDescribeList == null || message == null || message.trim().isEmpty()) { + return; + } + methodDescribeList.add(message); + } /** * 指标信息内部类 diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java index 0b63e05..9620bf1 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java @@ -38,6 +38,7 @@ public class MappingResponseConverter { MappingTaskResponse response = initBaseResponse(result); if (result.getStatus() == GenerateStatus.SUCCESS) { response.setMappingJson(result.getMappingJson()); + response.setMappingDocument(result.getMappingDocument()); response.setSavedPath(result.getSavedPath()); return response; } @@ -82,4 +83,4 @@ public class MappingResponseConverter { response.getIndexCandidates().add(candidateResponse); } } -} \ No newline at end of file +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java index 27dd6e6..61c2e26 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java @@ -5,7 +5,6 @@ import com.njcn.common.pojo.enums.common.LogEnum; import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; import com.njcn.common.utils.LogUtil; -import com.njcn.gather.icd.mapping.component.IcdToXmlRequestConverter; import com.njcn.gather.icd.mapping.component.IcdToXmlResponseConverter; import com.njcn.gather.icd.mapping.component.IndexSelectionBuildService; import com.njcn.gather.icd.mapping.component.MappingRequestConverter; @@ -13,10 +12,8 @@ import com.njcn.gather.icd.mapping.component.MappingResponseConverter; import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult; import com.njcn.gather.icd.mapping.pojo.bo.IcdToXmlGenerateResult; import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand; -import com.njcn.gather.icd.mapping.pojo.dto.IcdToXmlGenerateCommand; import com.njcn.gather.icd.mapping.pojo.param.BuildIndexSelectionRequest; import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest; -import com.njcn.gather.icd.mapping.pojo.param.IcdToXmlGenerateRequest; import com.njcn.gather.icd.mapping.pojo.param.IndexCandidateRequest; import com.njcn.gather.icd.mapping.pojo.param.JsonToXmlRequest; import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest; @@ -67,9 +64,6 @@ public class MappingController extends BaseController { /** 请求参数转换器,将接口入参转换为应用层命令。 */ private final IcdToXmlResponseConverter icdResponseConverter; - /** 响应转换器,按接口阶段裁剪最小返回字段。 */ - private final IcdToXmlRequestConverter icdRequestConverter; - /** 响应转换器,按接口阶段裁剪最小返回字段。 */ private final IcdToXmlTaskAppService icdToXmlTaskAppService; @@ -161,7 +155,7 @@ public class MappingController extends BaseController { } /** - * 直接将 MMS JSON 转换为 XML 文件。 + * 直接将 MMS JSON 转换为 XML 内容。 * 适用于已经通过 getIcdMmsJson 获得 JSON 后,单独进行 XML 转换的场景。 */ @OperateInfo(info = LogEnum.BUSINESS_COMMON) @@ -169,18 +163,18 @@ public class MappingController extends BaseController { @ApiImplicitParam(name = "request", value = "MMS JSON 转 XML 参数", required = true, dataType = "JsonToXmlRequest") @PostMapping("/get-xml-from-json") public IcdToXmlResponse getXmlFromJson(@Validated @RequestPart("request") JsonToXmlRequest request) { - String methodDescribe = getMethodDescribe("getXmlFromJson"); - LogUtil.njcnDebug(log, "{},开始将 MMS JSON 转换为 XML", methodDescribe); + String methodDescribe = getMethodDescribe("getXmlFromJson") + ",开始将 MMS JSON 转换为 XML"; -// // 将请求参数转换为 Command -// IcdToXmlGenerateCommand command = -// icdRequestConverter.toCommand(request); -// - // 调用服务生成 XML + // 直接返回 XML 内容给前端展示,不再输出临时文件。 IcdToXmlGenerateResult result = - icdToXmlTaskAppService.generateXmlFromJson(request.getMappingJson()); + icdToXmlTaskAppService.generateXmlFromJson(request == null ? null : request.getMappingJson()); + if (result.getMethodDescribe() != null && !result.getMethodDescribe().trim().isEmpty()) { + methodDescribe = methodDescribe + "\n" + result.getMethodDescribe(); + } - return icdResponseConverter.fromResult(result); + IcdToXmlResponse response = icdResponseConverter.fromResult(result); + response.setMethodDescribe(methodDescribe); + return response; } } diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/IcdToXmlGenerateResult.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/IcdToXmlGenerateResult.java index 43df5d9..6784742 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/IcdToXmlGenerateResult.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/IcdToXmlGenerateResult.java @@ -3,36 +3,42 @@ package com.njcn.gather.icd.mapping.pojo.bo; import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis; import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument; import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus; -import lombok.Builder; import lombok.Data; import java.util.ArrayList; import java.util.List; +/** + * XML 生成应用层返回对象。 + * + * 统一封装成功、需选择索引、失败三类结果。 + */ +@Data public class IcdToXmlGenerateResult { + /** 本次生成流程状态。 */ private GenerateStatus status; - private String message; - private String iedName; - private String ldInst; - private IndexAnalysis indexAnalysis; - private MappingDocument mappingDocument; - private String savedPath; - private List problems = new ArrayList(); - public GenerateStatus getStatus() { return status; } - public void setStatus(GenerateStatus status) { this.status = status; } - public String getMessage() { return message; } - public void setMessage(String message) { this.message = message; } - public String getIedName() { return iedName; } - public void setIedName(String iedName) { this.iedName = iedName; } - public String getLdInst() { return ldInst; } - public void setLdInst(String ldInst) { this.ldInst = ldInst; } - public IndexAnalysis getIndexAnalysis() { return indexAnalysis; } - public void setIndexAnalysis(IndexAnalysis indexAnalysis) { this.indexAnalysis = indexAnalysis; } - public MappingDocument getMappingDocument() { return mappingDocument; } - public void setMappingDocument(MappingDocument mappingDocument) { this.mappingDocument = mappingDocument; } - public String getSavedPath() { return savedPath; } - public void setSavedPath(String savedPath) { this.savedPath = savedPath; } - public List getProblems() { return problems; } - public void setProblems(List problems) { this.problems = problems; } -} \ No newline at end of file + /** 给前端或调用方展示的处理结果说明。 */ + private String message; + + /** 生成过程中需要返回的方法描述信息。 */ + private String methodDescribe; + + /** ICD 中解析到的 IED 名称。 */ + private String iedName; + + /** ICD 中解析到的 LD 实例名。 */ + private String ldInst; + + /** 需要人工绑定索引时返回的候选分析结果。 */ + private IndexAnalysis indexAnalysis; + + /** 生成成功后的结构化映射文档。 */ + private MappingDocument mappingDocument; + + /** 生成成功后的 XML 内容,用于前端直接展示。 */ + private String xmlContent; + + /** 解析、校验或生成过程中收集到的问题。 */ + private List problems = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IcdToXmlResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IcdToXmlResponse.java index 180fe0c..65e0ab2 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IcdToXmlResponse.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IcdToXmlResponse.java @@ -1,8 +1,10 @@ package com.njcn.gather.icd.mapping.pojo.vo; +import com.fasterxml.jackson.annotation.JsonInclude; import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.Data; import java.util.ArrayList; import java.util.List; @@ -10,6 +12,8 @@ import java.util.List; /** * MMS JSON 转 XML 的响应。 */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) @ApiModel("MMS JSON 转 XML 的响应") public class IcdToXmlResponse { @@ -19,14 +23,17 @@ public class IcdToXmlResponse { @ApiModelProperty("状态说明或错误提示") private String message; + @ApiModelProperty("接口方法描述") + private String methodDescribe; + @ApiModelProperty("IED 名称") private String iedName; @ApiModelProperty("逻辑设备实例名") private String ldInst; - @ApiModelProperty("落盘后的绝对路径") - private String savedPath; + @ApiModelProperty("生成后的 XML 文件展示容器") + private XmlFileResponse xmlFile; @ApiModelProperty("生成后的映射文档摘要") private MappingDocumentResponse mappingDocument; @@ -37,67 +44,4 @@ public class IcdToXmlResponse { @ApiModelProperty("处理过程中发现的问题列表") private List problems = new ArrayList(); - public GenerateStatus getStatus() { - return status; - } - - public void setStatus(GenerateStatus status) { - this.status = status; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public String getIedName() { - return iedName; - } - - public void setIedName(String iedName) { - this.iedName = iedName; - } - - public String getLdInst() { - return ldInst; - } - - public void setLdInst(String ldInst) { - this.ldInst = ldInst; - } - - public String getSavedPath() { - return savedPath; - } - - public void setSavedPath(String savedPath) { - this.savedPath = savedPath; - } - - public MappingDocumentResponse getMappingDocument() { - return mappingDocument; - } - - public void setMappingDocument(MappingDocumentResponse mappingDocument) { - this.mappingDocument = mappingDocument; - } - - public List getIndexCandidates() { - return indexCandidates; - } - - public void setIndexCandidates(List indexCandidates) { - this.indexCandidates = indexCandidates; - } - - public List getProblems() { - return problems; - } - - public void setProblems(List problems) { - this.problems = problems; - } } diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingTaskResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingTaskResponse.java index bc6003f..a855410 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingTaskResponse.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingTaskResponse.java @@ -2,6 +2,7 @@ package com.njcn.gather.icd.mapping.pojo.vo; import com.fasterxml.jackson.annotation.JsonInclude; import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument; +import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument; import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -34,6 +35,10 @@ public class MappingTaskResponse { @ApiModelProperty("正式生成成功后的完整映射 JSON") private String mappingJson; + /** 正式生成成功后的结构化映射文档。 */ + @ApiModelProperty("正式生成成功后的结构化映射文档") + private MappingDocument mappingDocument; + /** 落盘后的绝对路径。 */ @ApiModelProperty("落盘后的绝对路径") private String savedPath; diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/XmlFileResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/XmlFileResponse.java new file mode 100644 index 0000000..521f6df --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/XmlFileResponse.java @@ -0,0 +1,29 @@ +package com.njcn.gather.icd.mapping.pojo.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * XML 文件展示容器。 + */ +@Data +@ApiModel("XML 文件展示容器") +public class XmlFileResponse { + + /** XML 文件名。 */ + @ApiModelProperty("XML 文件名") + private String fileName; + + /** 文件内容类型。 */ + @ApiModelProperty("文件内容类型") + private String contentType; + + /** 文件编码。 */ + @ApiModelProperty("文件编码") + private String encoding; + + /** XML 文件内容。 */ + @ApiModelProperty("XML 文件内容") + private String content; +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/IcdToXmlTaskAppService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/IcdToXmlTaskAppService.java index 2349cb8..b2462f3 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/IcdToXmlTaskAppService.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/IcdToXmlTaskAppService.java @@ -1,7 +1,14 @@ package com.njcn.gather.icd.mapping.service.impl; - -import com.njcn.gather.icd.mapping.component.*; +import com.njcn.gather.icd.mapping.component.DefaultTemplateLoader; +import com.njcn.gather.icd.mapping.component.IcdParserService; +import com.njcn.gather.icd.mapping.component.IcdToXmlMappingService; +import com.njcn.gather.icd.mapping.component.IndexAnalysisService; +import com.njcn.gather.icd.mapping.component.IndexValidationService; +import com.njcn.gather.icd.mapping.component.JsonToXmlConversionService; +import com.njcn.gather.icd.mapping.component.MappingDocumentSerializer; +import com.njcn.gather.icd.mapping.component.MappingGenerationService; +import com.njcn.gather.icd.mapping.component.RuleBasedXmlMappingService; import com.njcn.gather.icd.mapping.pojo.bo.IcdToXmlGenerateResult; import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis; import com.njcn.gather.icd.mapping.pojo.bo.analysis.ValidationResult; @@ -9,173 +16,292 @@ import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument; import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument; import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate; import com.njcn.gather.icd.mapping.pojo.dto.IcdToXmlGenerateCommand; +import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand; import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; +/** + * ICD/XML 生成任务编排服务。 + * + * 负责组织 ICD 到 XML、MMS JSON 到 XML 两类转换链路,避免 Controller 关注模板、规则和异常处理细节。 + */ +@Slf4j @Service +@RequiredArgsConstructor public class IcdToXmlTaskAppService { + + /** ICD 转 XML 阶段名称。 */ + private static final String ICD_TO_XML_TASK_NAME = "ICD 转 XML"; + /** JSON 转 XML 阶段名称。 */ + private static final String JSON_TO_XML_TASK_NAME = "XML 生成"; + /** 缺少索引绑定提示。 */ + private static final String INDEX_SELECTION_MISSING_MESSAGE = "索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交"; + /** 映射生成成功提示。 */ + private static final String MAPPING_GENERATE_SUCCESS_MESSAGE = "映射生成成功"; + /** XML 生成成功提示。 */ + private static final String XML_GENERATE_SUCCESS_MESSAGE = "XML 生成成功"; + /** 空 JSON 提示。 */ + private static final String MAPPING_JSON_EMPTY_MESSAGE = "MMS映射JSON不能为空"; + /** 缺少默认 XML 模板提示。 */ + private static final String DEFAULT_XML_MISSING_MESSAGE = "缺少默认xml配置文件"; + /** 缺少默认规则文件提示。 */ + private static final String DEFAULT_RULE_MISSING_MESSAGE = "缺少默认规则配置文件"; + + /** ICD/SCL 文件解析服务。 */ private final IcdParserService icdParserService; + /** DefaultCfg.txt 默认模板加载器。 */ private final DefaultTemplateLoader defaultTemplateLoader; + /** 根据 ICD 和模板生成前端可选索引候选。 */ private final IndexAnalysisService indexAnalysisService; + /** 校验前端回传的标签与 lnInst 绑定关系。 */ private final IndexValidationService indexValidationService; + /** 根据有效绑定关系生成最终 MappingDocument。 */ private final MappingGenerationService mappingGenerationService; + /** XML 模板和规则文件加载服务。 */ private final RuleBasedXmlMappingService ruleBasedXmlMappingService; + /** 将 MappingDocument 序列化为 JSON。 */ private final MappingDocumentSerializer mappingDocumentSerializer; - private final FileStorageService fileStorageService; + /** 根据索引绑定关系重建 XML 规则中的实例索引。 */ private final IcdToXmlMappingService icdToXmlMappingService; + /** 将 MMS JSON 中间态转换为 XML 内容。 */ private final JsonToXmlConversionService jsonToXmlConversionService; - - public IcdToXmlTaskAppService(IcdParserService icdParserService, - DefaultTemplateLoader defaultTemplateLoader, - IndexAnalysisService indexAnalysisService, - IndexValidationService indexValidationService, - MappingGenerationService mappingGenerationService, - RuleBasedXmlMappingService ruleBasedXmlMappingService, - MappingDocumentSerializer mappingDocumentSerializer, - FileStorageService fileStorageService, - IcdToXmlMappingService icdToXmlMappingService, - JsonToXmlConversionService jsonToXmlConversionService) { - this.icdParserService = icdParserService; - this.defaultTemplateLoader = defaultTemplateLoader; - this.indexAnalysisService = indexAnalysisService; - this.indexValidationService = indexValidationService; - this.mappingGenerationService = mappingGenerationService; - this.ruleBasedXmlMappingService = ruleBasedXmlMappingService; - this.mappingDocumentSerializer = mappingDocumentSerializer; - this.fileStorageService = fileStorageService; - this.icdToXmlMappingService = icdToXmlMappingService; - this.jsonToXmlConversionService = jsonToXmlConversionService; - } + /** + * 解析 ICD 并按索引绑定关系直接生成 XML 内容。 + */ public IcdToXmlGenerateResult generateFromIcd(IcdToXmlGenerateCommand command) { - IcdToXmlGenerateResult result = new IcdToXmlGenerateResult(); - try { - // ========== 第一步:ICD → JSON ========== - - // 1. 解析 ICD - IcdDocument icdDocument = icdParserService.parse(command.getFileBytes(), command.getFileName()); - result.setIedName(icdDocument.getIedName()); - result.setLdInst(icdDocument.getLdInst()); + return executeTask(ICD_TO_XML_TASK_NAME, result -> { + IcdToXmlTaskContext context = buildTaskContext(command, result); - // 2. 加载 DefaultCfg.txt和默认配置 - //DefaultTemplate template = defaultTemplateLoader.loadDefaultTemplateToXml(); - DefaultTemplate template = defaultTemplateLoader.load(); - result.getProblems().addAll(template.verify()); - - InputStream templateStream = ruleBasedXmlMappingService.loadDefaultXmlFile(); - if(templateStream==null){result.getProblems().add("缺少默认xml配置文件");} - List ruleStreams = ruleBasedXmlMappingService.loadDefaultRuleFile(); - if(ruleStreams==null){result.getProblems().add("缺少默认规则配置文件");} - - - // 3. 分析索引候选 - IndexAnalysis indexAnalysis = indexAnalysisService.analyze(icdDocument, template); - result.setIndexAnalysis(indexAnalysis); - result.getProblems().addAll(indexAnalysis.getProblems()); - - // 4. 如果没有提交任何绑定关系,则直接返回待匹配项 - if (command.getIndexSelection() == null || command.getIndexSelection().isEmpty()) { - result.setStatus(GenerateStatus.NEED_INDEX_SELECTION); - result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交"); - return result; + if (isIndexSelectionEmpty(command.getIndexSelection())) { + markNeedIndexSelection(result); + return; } - // 5. 校验用户提交的绑定关系 - ValidationResult validationResult = indexValidationService.validate(indexAnalysis, command.getIndexSelection()); + ValidationResult validationResult = indexValidationService.validate(context.indexAnalysis, command.getIndexSelection()); if (!validationResult.isValid()) { - result.setStatus(GenerateStatus.NEED_INDEX_SELECTION); - result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交"); + markNeedIndexSelection(result); result.getProblems().addAll(validationResult.getProblems()); - return result; + return; } - // 6. 生成正式映射结构(JSON中间态) MappingDocument mappingDocument = mappingGenerationService.generate( - icdDocument, - template, - indexAnalysis, + context.icdDocument, + context.template, + context.indexAnalysis, command.getIndexSelection(), command.getVersion(), command.getAuthor() ); result.setMappingDocument(mappingDocument); - - // 7. 序列化为JSON字符串(中间产物) - String mappingJson = mappingDocumentSerializer.toPrettyJson(mappingDocument); - // ========== 第二步:JSON → XML ========== - - // 8. 重新绑定正确索引 - IcdToXmlMappingService.IndexMappingConfig mappingConfig = icdToXmlMappingService.buildIndexMappingFromSelection(command.getIndexSelection()); - icdToXmlMappingService.setIndexMapping(mappingConfig); - - // 9. 从JSON转换为XML - String xmlPath = jsonToXmlConversionService.convertFromJson( - mappingJson, - templateStream, - ruleStreams, - icdToXmlMappingService.getIndexMapping() - ); - result.setSavedPath(xmlPath); + String mappingJson = mappingDocumentSerializer.toPrettyJson(mappingDocument); + bindIndexMapping(command.getIndexSelection()); + fillXmlContent(result, mappingJson, loadXmlResources()); result.setStatus(GenerateStatus.SUCCESS); - result.setMessage("映射生成成功"); - return result; - } catch (Exception ex) { - result.setStatus(GenerateStatus.FAILED); - result.setMessage("映射生成失败:" + ex.getMessage()); - result.getProblems().add(ex.getMessage()); - return result; - } + result.setMessage(MAPPING_GENERATE_SUCCESS_MESSAGE); + }); } /** - * 直接从 JSON 字符串生成 XML 文件。 + * 直接从 JSON 字符串生成 XML 内容。 * * @param mappingJson MMS 映射 JSON 字符串(由 getIcdMmsJson 接口返回) * @return XML 生成结果 */ - public IcdToXmlGenerateResult generateXmlFromJson(String mappingJson){ + public IcdToXmlGenerateResult generateXmlFromJson(String mappingJson) { + return executeTask(JSON_TO_XML_TASK_NAME, result -> { + if (isBlank(mappingJson)) { + markFailed(result, JSON_TO_XML_TASK_NAME, MAPPING_JSON_EMPTY_MESSAGE); + return; + } + + fillXmlContent(result, mappingJson, loadXmlResources()); + result.setStatus(GenerateStatus.SUCCESS); + result.setMessage(XML_GENERATE_SUCCESS_MESSAGE); + }); + } + + /** + * 统一包装任务执行结果,避免每个入口重复编写异常兜底逻辑。 + */ + private IcdToXmlGenerateResult executeTask(String taskName, IcdToXmlTaskAction action) { IcdToXmlGenerateResult result = new IcdToXmlGenerateResult(); try { - // 1. 加载模板和规则文件 - InputStream templateStream = ruleBasedXmlMappingService.loadDefaultXmlFile(); - if (templateStream == null) { - result.setStatus(GenerateStatus.FAILED); - result.getProblems().add("缺少默认xml配置文件"); - result.setMessage("XML 生成失败:缺少默认xml配置文件"); - return result; - } - - List ruleStreams = ruleBasedXmlMappingService.loadDefaultRuleFile(); - if (ruleStreams == null) { - result.setStatus(GenerateStatus.FAILED); - result.getProblems().add("缺少默认规则配置文件"); - result.setMessage("XML 生成失败:缺少默认规则配置文件"); - return result; - } - - - // 3. 从 JSON 转换为 XML - String xmlPath = jsonToXmlConversionService.convertFromJson( - mappingJson, - templateStream, - ruleStreams, - icdToXmlMappingService.getIndexMapping() - ); - - result.setSavedPath(xmlPath); - result.setStatus(GenerateStatus.SUCCESS); - result.setMessage("XML 生成成功"); - return result; + action.execute(result); } catch (Exception ex) { - result.setStatus(GenerateStatus.FAILED); - result.setMessage("XML 生成失败:" + ex.getMessage()); - result.getProblems().add(ex.getMessage()); - return result; + handleTaskFailure(result, taskName, ex); + } + return result; + } + + /** + * 解析 ICD 并补齐模板、索引候选上下文。 + */ + private IcdToXmlTaskContext buildTaskContext(IcdToXmlGenerateCommand command, IcdToXmlGenerateResult result) { + IcdDocument icdDocument = icdParserService.parse(command.getFileBytes(), command.getFileName()); + fillIcdSummary(result, icdDocument); + + DefaultTemplate template = loadTemplate(result); + IndexAnalysis indexAnalysis = analyzeIndexCandidates(icdDocument, template, result); + result.setIndexAnalysis(indexAnalysis); + return new IcdToXmlTaskContext(icdDocument, template, indexAnalysis); + } + + /** + * 加载并校验默认模板。 + */ + private DefaultTemplate loadTemplate(IcdToXmlGenerateResult result) { + DefaultTemplate template = defaultTemplateLoader.load(); + result.getProblems().addAll(template.verify()); + return template; + } + + /** + * 分析 ICD 对应的索引候选。 + */ + private IndexAnalysis analyzeIndexCandidates(IcdDocument icdDocument, + DefaultTemplate template, + IcdToXmlGenerateResult result) { + IndexAnalysis indexAnalysis = indexAnalysisService.analyze(icdDocument, template); + result.getProblems().addAll(indexAnalysis.getProblems()); + return indexAnalysis; + } + + /** + * 加载 XML 模板和规则文件。 + */ + private XmlResourceContext loadXmlResources() throws Exception { + InputStream templateStream = ruleBasedXmlMappingService.loadDefaultXmlFile(); + if (templateStream == null) { + throw new IllegalArgumentException(DEFAULT_XML_MISSING_MESSAGE); + } + + List ruleStreams = ruleBasedXmlMappingService.loadDefaultRuleFile(); + if (ruleStreams == null) { + throw new IllegalArgumentException(DEFAULT_RULE_MISSING_MESSAGE); + } + + return new XmlResourceContext(templateStream, ruleStreams); + } + + /** + * 根据索引绑定关系刷新 XML 转换所需的实例索引。 + */ + private void bindIndexMapping(List indexSelection) { + IcdToXmlMappingService.IndexMappingConfig mappingConfig = icdToXmlMappingService.buildIndexMappingFromSelection(indexSelection); + icdToXmlMappingService.setIndexMapping(mappingConfig); + } + + /** + * 将 MMS JSON 中间态转换为 XML 内容。 + */ + private void fillXmlContent(IcdToXmlGenerateResult result, + String mappingJson, + XmlResourceContext xmlResourceContext) throws Exception { + List methodDescribeList = new ArrayList<>(); + String xmlContent = jsonToXmlConversionService.buildXmlContentFromJson( + mappingJson, + xmlResourceContext.templateStream, + xmlResourceContext.ruleStreams, + icdToXmlMappingService.getIndexMapping(), + methodDescribeList + ); + result.setXmlContent(xmlContent); + result.setMethodDescribe(String.join("\n", methodDescribeList)); + } + + /** + * 回填响应公共信息。 + */ + private void fillIcdSummary(IcdToXmlGenerateResult result, IcdDocument icdDocument) { + result.setIedName(icdDocument.getIedName()); + result.setLdInst(icdDocument.getLdInst()); + } + + /** + * 标记当前仍需用户重新确认索引绑定。 + */ + private void markNeedIndexSelection(IcdToXmlGenerateResult result) { + result.setStatus(GenerateStatus.NEED_INDEX_SELECTION); + result.setMessage(INDEX_SELECTION_MISSING_MESSAGE); + } + + /** + * 标记可预期的业务失败。 + */ + private void markFailed(IcdToXmlGenerateResult result, String taskName, String errorMessage) { + result.setStatus(GenerateStatus.FAILED); + result.setMessage(taskName + "失败:" + errorMessage); + result.getProblems().add(errorMessage); + } + + /** + * 统一处理任务异常,避免控制层再补失败分支。 + */ + private void handleTaskFailure(IcdToXmlGenerateResult result, String taskName, Exception ex) { + log.error("{}失败", taskName, ex); + String errorMessage = resolveErrorMessage(ex); + markFailed(result, taskName, errorMessage); + } + + private boolean isIndexSelectionEmpty(List indexSelection) { + return indexSelection == null || indexSelection.isEmpty(); + } + + private boolean isBlank(String value) { + return value == null || value.trim().isEmpty(); + } + + private String resolveErrorMessage(Exception ex) { + if (ex.getMessage() == null || ex.getMessage().trim().isEmpty()) { + return ex.getClass().getSimpleName(); + } + return ex.getMessage(); + } + + @FunctionalInterface + private interface IcdToXmlTaskAction { + void execute(IcdToXmlGenerateResult result) throws Exception; + } + + /** + * 供 ICD 转 XML 流程复用的编排上下文。 + */ + private static class IcdToXmlTaskContext { + + private final IcdDocument icdDocument; + + private final DefaultTemplate template; + + private final IndexAnalysis indexAnalysis; + + private IcdToXmlTaskContext(IcdDocument icdDocument, DefaultTemplate template, IndexAnalysis indexAnalysis) { + this.icdDocument = icdDocument; + this.template = template; + this.indexAnalysis = indexAnalysis; + } + } + + /** + * XML 转换所需的模板和规则资源。 + */ + private static class XmlResourceContext { + + private final InputStream templateStream; + + private final List ruleStreams; + + private XmlResourceContext(InputStream templateStream, List ruleStreams) { + this.templateStream = templateStream; + this.ruleStreams = ruleStreams; } }