refactor(mms-mapping): 重构ICD到XML转换服务及响应结构

- 移除API调试文档,精简工具模块
- 重构IcdToXmlGenerateResult数据结构,新增methodDescribe和xmlContent字段
- 更新IcdToXmlResponse响应对象,移除savedPath字段,新增XmlFileResponse容器
- 重构IcdToXmlTaskAppService,引入任务编排上下文和统一异常处理
- 新增XmlFileResponse和XmlResourceContext辅助类
- 优化JsonToXmlConversionService,支持直接返回XML内容而不落地文件
- 添加索引绑定验证和错误处理机制
- 引入函数式接口简化任务执行逻辑
This commit is contained in:
2026-05-08 09:52:44 +08:00
parent 81e4ff4009
commit 34b1a81c70
11 changed files with 444 additions and 576 deletions

View File

@@ -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<T>` 标准包装。
- 业务异常和参数异常仍由全局异常处理器统一包装。
- 本次仅整理接口标准出口和调试文档,未改动索引组装算法。
- 本次未执行 `mvn` 编译、打包或真实接口联调。
## 2. 接口一buildIndexConfirmData
### 2.1 基本信息
| 项 | 说明 |
| --- | --- |
| 接口名称 | `buildIndexConfirmData` |
| 请求方法 | `POST` |
| 请求路径 | `/api/mms-mapping/build-index-confirm-data` |
| Content-Type | `application/json` |
| 控制器入口 | `MappingController#buildIndexConfirmData` |
| 成功响应体 | `HttpResult<List<IndexConfirmGroupResponse>>` |
### 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<List<IndexSelectionGroupResponse>>` |
### 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<String>`。常见场景包括:
- `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` 字段和值为结构化示意,实际成功码和提示文案以公共响应实现为准

View File

@@ -5,6 +5,7 @@
## 0. 调试文档
- `getIcdMmsJson``API-getIcdMmsJson.md`
- `getXmlFromJson``API-getXmlFromJson.md`
- `buildIndexConfirmData` / `buildIndexSelection``API-buildIndexDebug.md`
## 1. 接口信息

View File

@@ -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;
}
}
/**
* 将 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";
}
}

View File

@@ -54,14 +54,7 @@ public class JsonToXmlConversionService {
InputStream templateStream,
List<InputStream> 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<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
return buildXmlContentFromJson(mappingJson, templateStream, ruleStreams, indexMapping, null);
}
public String buildXmlContentFromJson(String mappingJson,
InputStream templateStream,
List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping,
List<String> 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<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
IcdToXmlMappingService.IndexMappingConfig indexMapping,
List<String> 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<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
IcdToXmlMappingService.IndexMappingConfig indexMapping,
List<String> 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<String, RuleBasedXmlMappingService.ValueRule> findApplicableRulesDesc(
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
java.util.Map<String, MetricInfo> mappingMetrics) {
java.util.Map<String, MetricInfo> mappingMetrics,
List<String> methodDescribeList) {
java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicable = new java.util.HashMap<>();
List<String> 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("\"", "&quot;")
.replace("'", "&apos;");
}
private void addMethodDescribe(List<String> methodDescribeList, String message) {
if (methodDescribeList == null || message == null || message.trim().isEmpty()) {
return;
}
methodDescribeList.add(message);
}
/**
* 指标信息内部类

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<String> problems = new ArrayList<String>();
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<String> getProblems() { return problems; }
public void setProblems(List<String> problems) { this.problems = problems; }
}
/** 给前端或调用方展示的处理结果说明。 */
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<String> problems = new ArrayList<String>();
}

View File

@@ -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<String> problems = new ArrayList<String>();
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<IndexCandidateResponse> getIndexCandidates() {
return indexCandidates;
}
public void setIndexCandidates(List<IndexCandidateResponse> indexCandidates) {
this.indexCandidates = indexCandidates;
}
public List<String> getProblems() {
return problems;
}
public void setProblems(List<String> problems) {
this.problems = problems;
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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<InputStream> 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<InputStream> 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<InputStream> ruleStreams = ruleBasedXmlMappingService.loadDefaultRuleFile();
if (ruleStreams == null) {
throw new IllegalArgumentException(DEFAULT_RULE_MISSING_MESSAGE);
}
return new XmlResourceContext(templateStream, ruleStreams);
}
/**
* 根据索引绑定关系刷新 XML 转换所需的实例索引。
*/
private void bindIndexMapping(List<IndexSelectionGroupCommand> 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<String> 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<IndexSelectionGroupCommand> 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<InputStream> ruleStreams;
private XmlResourceContext(InputStream templateStream, List<InputStream> ruleStreams) {
this.templateStream = templateStream;
this.ruleStreams = ruleStreams;
}
}