refactor(mms-mapping): 重构ICD到XML转换服务优化代码结构

- 移除IcdToXmlGenerateResult中未使用的iedName、ldInst、indexAnalysis、savedPath字段
- 注释掉相应的getter/setter方法减少代码冗余
- 重构IcdToXmlTaskAppService中generateFromIcd方法的业务逻辑
- 优化JsonToXmlConversionService的转换流程提高性能
- 添加详细的中文注释说明各个方法的功能和实现逻辑
- 调整规则匹配和XML生成的核心算法提升匹配准确性
- 修改未匹配规则的错误提示信息增加详细指标信息
This commit is contained in:
周宇 蔡
2026-05-27 08:45:36 +08:00
parent 8a92ff3be0
commit 58ca8b0c23
7 changed files with 280 additions and 715 deletions

View File

@@ -16,33 +16,10 @@ public class IcdToXmlResponseConverter {
IcdToXmlResponse response = new IcdToXmlResponse(); IcdToXmlResponse response = new IcdToXmlResponse();
response.setStatus(result.getStatus()); response.setStatus(result.getStatus());
response.setMessage(result.getMessage()); response.setMessage(result.getMessage());
response.setIedName(result.getIedName()); //response.setIedName(result.getIedName());
response.setLdInst(result.getLdInst()); //response.setLdInst(result.getLdInst());
response.setSavedPath(result.getSavedPath());
response.getProblems().addAll(result.getProblems()); response.getProblems().addAll(result.getProblems());
response.setMappingXml(result.getMappingXml()); response.setMappingXml(result.getMappingXml());
if (result.getIndexAnalysis() != null && result.getIndexAnalysis().getCandidates() != null) {
for (IndexCandidate candidate : result.getIndexAnalysis().getCandidates()) {
IndexCandidateResponse candidateResponse = new IndexCandidateResponse();
candidateResponse.setGroupKey(candidate.getGroupKey());
candidateResponse.setGroupDesc(candidate.getGroupDesc());
candidateResponse.setReportCount(candidate.getReportCount());
candidateResponse.getTemplateLabels().addAll(candidate.getTemplateLabels());
if (candidate.getReports() != null) {
for (IndexCandidateReportItem item : candidate.getReports()) {
IndexCandidateReportItemResponse itemResponse = new IndexCandidateReportItemResponse();
itemResponse.setReportName(item.getReportName());
itemResponse.setDataSetName(item.getDataSetName());
itemResponse.setReportDesc(item.getReportDesc());
itemResponse.getAvailableLnInstValues().addAll(item.getAvailableLnInstValues());
candidateResponse.getReports().add(itemResponse);
}
}
response.getIndexCandidates().add(candidateResponse);
}
}
if (result.getMappingDocument() != null) { if (result.getMappingDocument() != null) {
MappingDocumentResponse doc = new MappingDocumentResponse(); MappingDocumentResponse doc = new MappingDocumentResponse();

View File

@@ -30,84 +30,44 @@ import java.util.List;
*/ */
@Component @Component
public class JsonToXmlConversionService { public class JsonToXmlConversionService {
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final RuleBasedXmlMappingService ruleBasedXmlMappingService; private final RuleBasedXmlMappingService ruleBasedXmlMappingService;
public JsonToXmlConversionService(RuleBasedXmlMappingService ruleBasedXmlMappingService) { public JsonToXmlConversionService(RuleBasedXmlMappingService ruleBasedXmlMappingService) {
this.ruleBasedXmlMappingService = ruleBasedXmlMappingService; this.ruleBasedXmlMappingService = ruleBasedXmlMappingService;
this.objectMapper = new ObjectMapper(); this.objectMapper = new ObjectMapper();
// 配置ObjectMapper忽略未知属性避免反序列化时因字段不匹配而失败
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
} }
/**
* 从JSON字符串转换为XML文件
*/
public String convertFromJson(String mappingJson,
InputStream templateStream,
List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("[JSON转XML] 开始转换...");
try {
MappingDocument mappingDocument = objectMapper.readValue(mappingJson, MappingDocument.class);
long parseTime = System.currentTimeMillis();
System.out.println("[JSON转XML] JSON解析完成耗时: " + (parseTime - startTime) + " ms");
ConversionResult conversionResult = buildXmlFromMappingWithResult(mappingDocument, templateStream, ruleStreams, indexMapping);
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("[JSON转XML] 转换完成,总耗时: " + totalTime + " ms");
if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) {
System.err.println("[JSON转XML] 警告: 有 " + conversionResult.getUnmatchedRuleDetails().size() + " 条规则未匹配到指标");
}
return conversionResult.getXmlContent();
} catch (Exception e) {
long totalTime = System.currentTimeMillis() - startTime;
System.err.println("[JSON转XML] 转换失败,已耗时: " + totalTime + " ms错误: " + e.getMessage());
throw e;
}
}
/** /**
* 从JSON字符串转换为XML并返回包含未匹配规则信息的完整结果。 * 从JSON字符串转换为XML并返回包含未匹配规则信息的完整结果。
*/ */
public ConversionResult convertFromJsonWithResult(String mappingJson, public ConversionResult convertFromJsonWithResult(MappingDocument mappingDocument,
InputStream templateStream, InputStream templateStream,
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("[JSON转XML] 开始转换(带结果)...");
try {
MappingDocument mappingDocument = objectMapper.readValue(mappingJson, MappingDocument.class);
long parseTime = System.currentTimeMillis();
System.out.println("[JSON转XML] JSON解析完成耗时: " + (parseTime - startTime) + " ms");
ConversionResult conversionResult = buildXmlFromMappingWithResult(mappingDocument, templateStream, ruleStreams, indexMapping); ConversionResult conversionResult = buildXmlFromMappingWithResult(mappingDocument, templateStream, ruleStreams, indexMapping);
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("[JSON转XML] 转换完成,总耗时: " + totalTime + " ms");
if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) { if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) {
System.err.println("[JSON转XML] 警告: 有 " + conversionResult.getUnmatchedRuleDetails().size() + " 条规则未匹配到指标"); System.out.println("[JSON转XML] 警告: 有 " + conversionResult.getUnmatchedRuleDetails().size() + " 条规则未匹配到指标");
} }
return conversionResult; return conversionResult;
} catch (Exception e) {
long totalTime = System.currentTimeMillis() - startTime;
System.err.println("[JSON转XML] 转换失败,已耗时: " + totalTime + " ms错误: " + e.getMessage());
throw e;
}
} }
/**
* 将JSON字符串解析为MappingDocument对象
*/
public MappingDocument parseMappingJson(String mappingJson) throws Exception {
return objectMapper.readValue(mappingJson, MappingDocument.class);
}
/**
* 基于映射文档构建XML内容旧版方法仅返回XML内容
*/
private String buildXmlFromMapping(MappingDocument mappingDocument, private String buildXmlFromMapping(MappingDocument mappingDocument,
InputStream templateStream, InputStream templateStream,
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
@@ -116,42 +76,38 @@ public class JsonToXmlConversionService {
return buildXmlFromMappingWithResult(mappingDocument, templateStream, ruleStreams, indexMapping).getXmlContent(); return buildXmlFromMappingWithResult(mappingDocument, templateStream, ruleStreams, indexMapping).getXmlContent();
} }
private ConversionResult buildXmlFromMappingWithResult(MappingDocument mappingDocument, /**
* 基于映射文档构建XML内容并返回完整结果
* 主要流程:读取模板 -> 填充IED信息 -> 填充报告控制块 -> 应用规则匹配
*/
public ConversionResult buildXmlFromMappingWithResult(MappingDocument mappingDocument,
InputStream templateStream, InputStream templateStream,
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
long stepStart = System.currentTimeMillis(); // 读取XML模板内容为字符串
String templateContent = readInputStreamToString(templateStream); String templateContent = readInputStreamToString(templateStream);
long templateTime = System.currentTimeMillis(); // 填充IED名称和LD前缀信息到模板中
System.out.println(" [步骤1] 读取模板完成,耗时: " + (templateTime - stepStart) + " ms");
String xmlContent = fillIedInfo(templateContent, mappingDocument); String xmlContent = fillIedInfo(templateContent, mappingDocument);
long iedTime = System.currentTimeMillis();
System.out.println(" [步骤2] 填充IED信息完成耗时: " + (iedTime - templateTime) + " ms");
// 填充报告控制块信息到XML中
xmlContent = fillReportControlsFromMapping(xmlContent, mappingDocument); xmlContent = fillReportControlsFromMapping(xmlContent, mappingDocument);
long reportTime = System.currentTimeMillis(); // 应用规则匹配将JSON中的指标数据映射到XML节点
System.out.println(" [步骤3] 填充ReportControl完成耗时: " + (reportTime - iedTime) + " ms");
RuleMatchingResult matchingResult = applyRulesFromMappingWithResult(xmlContent, mappingDocument, ruleStreams, indexMapping); RuleMatchingResult matchingResult = applyRulesFromMappingWithResult(xmlContent, mappingDocument, ruleStreams, indexMapping);
long rulesTime = System.currentTimeMillis(); // 封装转换结果
System.out.println(" [步骤4] 应用规则完成,耗时: " + (rulesTime - reportTime) + " ms");
long totalStepTime = System.currentTimeMillis() - stepStart;
System.out.println(" [buildXmlFromMapping] 总计耗时: " + totalStepTime + " ms");
ConversionResult result = new ConversionResult(); ConversionResult result = new ConversionResult();
result.setXmlContent(matchingResult.getXmlContent()); result.setXmlContent(matchingResult.getXmlContent());
result.setUnmatchedRuleDetails(matchingResult.getUnmatchedRuleDetails()); result.setUnmatchedRuleDetails(matchingResult.getUnmatchedRuleDetails());
return result; return result;
} }
/**
* 将输入流读取为UTF-8编码的字符串
*/
private String readInputStreamToString(InputStream inputStream) throws Exception { private String readInputStreamToString(InputStream inputStream) throws Exception {
java.io.ByteArrayOutputStream result = new java.io.ByteArrayOutputStream(); java.io.ByteArrayOutputStream result = new java.io.ByteArrayOutputStream();
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
@@ -162,6 +118,10 @@ public class JsonToXmlConversionService {
return result.toString(StandardCharsets.UTF_8.name()); return result.toString(StandardCharsets.UTF_8.name());
} }
/**
* 填充IED名称和LD前缀信息到XML模板中
* 通过正则表达式替换模板中的占位符
*/
private String fillIedInfo(String xmlContent, MappingDocument mappingDocument) { private String fillIedInfo(String xmlContent, MappingDocument mappingDocument) {
if (mappingDocument == null) { if (mappingDocument == null) {
return xmlContent; return xmlContent;
@@ -170,6 +130,7 @@ public class JsonToXmlConversionService {
String iedName = mappingDocument.getIed(); String iedName = mappingDocument.getIed();
String ldPrefix = mappingDocument.getLd(); String ldPrefix = mappingDocument.getLd();
// 替换IED名称
if (iedName != null && !iedName.isEmpty()) { if (iedName != null && !iedName.isEmpty()) {
xmlContent = xmlContent.replaceAll( xmlContent = xmlContent.replaceAll(
"<IED\\s+name=\"[^\"]*\"", "<IED\\s+name=\"[^\"]*\"",
@@ -177,6 +138,7 @@ public class JsonToXmlConversionService {
); );
} }
// 替换LD前缀
if (ldPrefix != null && !ldPrefix.isEmpty()) { if (ldPrefix != null && !ldPrefix.isEmpty()) {
xmlContent = xmlContent.replaceAll( xmlContent = xmlContent.replaceAll(
"<LDevice\\s+Prefix=\"[^\"]*\"", "<LDevice\\s+Prefix=\"[^\"]*\"",
@@ -187,6 +149,10 @@ public class JsonToXmlConversionService {
return xmlContent; return xmlContent;
} }
/**
* 从映射文档中提取报告控制块信息并填充到XML中
* 只处理特定类型的报告控制块如brcbFluc、brcbStHarm等
*/
private String fillReportControlsFromMapping(String xmlContent, MappingDocument mappingDocument) { private String fillReportControlsFromMapping(String xmlContent, MappingDocument mappingDocument) {
if (mappingDocument == null || mappingDocument.getReportMap() == null || mappingDocument.getReportMap().isEmpty()) { if (mappingDocument == null || mappingDocument.getReportMap() == null || mappingDocument.getReportMap().isEmpty()) {
return xmlContent; return xmlContent;
@@ -195,10 +161,12 @@ public class JsonToXmlConversionService {
StringBuilder reportStatBuilder = new StringBuilder(); StringBuilder reportStatBuilder = new StringBuilder();
reportStatBuilder.append("\t\t<ReportStat>\n"); reportStatBuilder.append("\t\t<ReportStat>\n");
// 遍历报告映射项,构建报告控制块字符串
for (var reportItem : mappingDocument.getReportMap()) { for (var reportItem : mappingDocument.getReportMap()) {
if (reportItem.getName() != null && !reportItem.getName().isEmpty()) { if (reportItem.getName() != null && !reportItem.getName().isEmpty()) {
String name = reportItem.getName(); String name = reportItem.getName();
// 只处理特定类型的报告控制块
if (name.contains("brcbFluc") || if (name.contains("brcbFluc") ||
name.contains("brcbStHarm") || name.contains("brcbStHarm") ||
name.contains("brcbStIHarm") || name.contains("brcbStIHarm") ||
@@ -219,6 +187,7 @@ public class JsonToXmlConversionService {
reportStatBuilder.append("\t\t</ReportStat>"); reportStatBuilder.append("\t\t</ReportStat>");
// 使用正则表达式替换XML中的ReportStat节点
xmlContent = xmlContent.replaceAll( xmlContent = xmlContent.replaceAll(
"<ReportStat>[\\s\\S]*?</ReportStat>", "<ReportStat>[\\s\\S]*?</ReportStat>",
reportStatBuilder.toString().replace("$", "\\$").replace("[", "\\[").replace("]", "\\]") reportStatBuilder.toString().replace("$", "\\$").replace("[", "\\[").replace("]", "\\]")
@@ -227,25 +196,32 @@ public class JsonToXmlConversionService {
return xmlContent; return xmlContent;
} }
/**
* 构建报告控制块字符串
* 格式LLN0$BR/RP$名称,超时时间,重传次数等参数
*/
private String buildReportControlString(ReportMapItem rc) { private String buildReportControlString(ReportMapItem rc) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
// 构建报告控制块的基本路径LLN0$缓冲类型$名称
sb.append("LLN0$").append(rc.getBuffered() != null && rc.getBuffered().equals("BR") ? "BR$" : "RP$").append(rc.getName()); sb.append("LLN0$").append(rc.getBuffered() != null && rc.getBuffered().equals("BR") ? "BR$" : "RP$").append(rc.getName());
// 根据报告类型设置超时时间
if(rc.getName().contains("PLT") || rc.getName().contains("Flicker") || rc.getName().contains("PST") || rc.getName().contains("Fluc")){ if(rc.getName().contains("PLT") || rc.getName().contains("Flicker") || rc.getName().contains("PST") || rc.getName().contains("Fluc")){
sb.append(",600"); sb.append(",600"); // 闪变相关报告超时时间为600秒
}else{ }else{
sb.append(",60"); sb.append(",60"); // 其他报告超时时间为60秒
} }
sb.append(",1"); // 设置其他固定参数ReportControl:ID,RCBName,intgPd,dchg,qchg,dupd,period,gi,issuffixed,seqNum,timeStamp,reasonCode,dataSet,dataRef,bufOvfl,entryID,configRef,segmentation,FlickerFlag
sb.append(",0"); sb.append(",1");// intgPd: 完整性周期(启用)
sb.append(",0"); sb.append(",0"); // dchg: 数据变化触发(禁用)
sb.append(",0"); sb.append(",0");// qchg: 品质变化触发(禁用)
sb.append(",0"); sb.append(",0");// dupd: 数据更新触发(禁用)
sb.append(",yes"); sb.append(",0");// period: 周期性触发(禁用)
sb.append(",1"); sb.append(",yes");// gi: 总召唤支持(启用)
sb.append(",1"); sb.append(",1"); // issuffixed: 使用后缀命名(启用)
sb.append(",1"); sb.append(",1");// seqNum: 包含序列号(启用)
sb.append(",1"); // timeStamp: 包含时间戳(启用)
sb.append(",1"); sb.append(",1");
sb.append(",1"); sb.append(",1");
sb.append(",0"); sb.append(",0");
@@ -254,6 +230,7 @@ public class JsonToXmlConversionService {
sb.append(",1"); sb.append(",1");
sb.append(",3"); sb.append(",3");
// 根据报告类型设置最后一个参数
if(rc.getName().contains("PLT") || rc.getName().contains("Flicker")){ if(rc.getName().contains("PLT") || rc.getName().contains("Flicker")){
sb.append(",1"); sb.append(",1");
}else if(rc.getName().contains("PST") || rc.getName().contains("Fluc")){ }else if(rc.getName().contains("PST") || rc.getName().contains("Fluc")){
@@ -265,6 +242,9 @@ public class JsonToXmlConversionService {
return sb.toString(); return sb.toString();
} }
/**
* 从映射文档应用规则到XML旧版方法
*/
private String applyRulesFromMapping(String xmlContent, private String applyRulesFromMapping(String xmlContent,
MappingDocument mappingDocument, MappingDocument mappingDocument,
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
@@ -273,48 +253,43 @@ public class JsonToXmlConversionService {
return applyRulesFromMappingWithResult(xmlContent, mappingDocument, ruleStreams, indexMapping).getXmlContent(); return applyRulesFromMappingWithResult(xmlContent, mappingDocument, ruleStreams, indexMapping).getXmlContent();
} }
/**
* 从映射文档应用规则到XML并返回匹配结果
* 核心流程:合并规则 -> 提取指标 -> 匹配规则 -> 应用规则到XML
*/
private RuleMatchingResult applyRulesFromMappingWithResult(String xmlContent, private RuleMatchingResult applyRulesFromMappingWithResult(String xmlContent,
MappingDocument mappingDocument, MappingDocument mappingDocument,
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
long stepStart = System.currentTimeMillis(); // 合并所有规则文件中的规则
var mergedRules = mergeAllRulesDesc(ruleStreams, indexMapping); var mergedRules = mergeAllRulesDesc(ruleStreams, indexMapping);
long mergeTime = System.currentTimeMillis(); // 从映射文档中提取所有指标信息
System.out.println(" [规则合并] 耗时: " + (mergeTime - stepStart) + " ms规则数: " + mergedRules.size());
var mappingMetrics = extractMetricsFromMapping(mappingDocument); var mappingMetrics = extractMetricsFromMapping(mappingDocument);
long extractTime = System.currentTimeMillis();
System.out.println(" [指标提取] 耗时: " + (extractTime - mergeTime) + " ms指标数: " + mappingMetrics.size());
System.out.println("========== 开始从JSON匹配规则 =========="); System.out.println("========== 开始从JSON匹配规则 ==========");
System.out.println("规则总数: " + mergedRules.size()); System.out.println("规则总数: " + mergedRules.size());
System.out.println("JSON中指标总数: " + mappingMetrics.size()); System.out.println("JSON中指标总数: " + mappingMetrics.size());
// 执行规则匹配,获取匹配结果和未匹配规则详情
RuleMatchingResult matchingResult = findApplicableRulesDescWithUnmatched(mergedRules, mappingMetrics); RuleMatchingResult matchingResult = findApplicableRulesDescWithUnmatched(mergedRules, mappingMetrics);
long matchTime = System.currentTimeMillis();
System.out.println(" [规则匹配] 耗时: " + (matchTime - extractTime) + " ms");
System.out.println("匹配成功: " + matchingResult.getApplicableRules().size() + " 条规则"); System.out.println("匹配成功: " + matchingResult.getApplicableRules().size() + " 条规则");
System.out.println("匹配失败: " + matchingResult.getUnmatchedRuleDetails().size() + " 条规则"); System.out.println("匹配失败: " + matchingResult.getUnmatchedRuleDetails().size() + " 条规则");
// 将匹配成功的规则应用到XML内容中
String resultXml = applyRulesToXml(xmlContent, matchingResult.getApplicableRules()); String resultXml = applyRulesToXml(xmlContent, matchingResult.getApplicableRules());
long applyTime = System.currentTimeMillis();
System.out.println(" [规则应用] 耗时: " + (applyTime - matchTime) + " ms");
long totalApplyTime = System.currentTimeMillis() - stepStart;
System.out.println(" [applyRulesFromMapping] 总计耗时: " + totalApplyTime + " ms");
System.out.println("========== JSON规则匹配结束 ==========\n"); System.out.println("========== JSON规则匹配结束 ==========\n");
matchingResult.setXmlContent(resultXml); matchingResult.setXmlContent(resultXml);
return matchingResult; return matchingResult;
} }
/**
* 合并所有规则文件中的规则按name+desc作为key
*/
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> mergeAllRules( private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> mergeAllRules(
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
@@ -333,6 +308,9 @@ public class JsonToXmlConversionService {
return mergedRules; return mergedRules;
} }
/**
* 合并所有规则文件中的规则仅按desc作为key
*/
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> mergeAllRulesDesc( private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> mergeAllRulesDesc(
List<InputStream> ruleStreams, List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
@@ -351,6 +329,9 @@ public class JsonToXmlConversionService {
return mergedRules; return mergedRules;
} }
/**
* 解析单个规则文件按name+desc组合作为key
*/
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> parseRuleFile( private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> parseRuleFile(
InputStream ruleStream, InputStream ruleStream,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
@@ -364,6 +345,7 @@ public class JsonToXmlConversionService {
String line; String line;
String currentGroup = ""; String currentGroup = "";
// 定义Value标签的正则表达式模式用于解析规则文件中的<Value>节点
java.util.regex.Pattern valueRulePattern = java.util.regex.Pattern.compile( java.util.regex.Pattern valueRulePattern = java.util.regex.Pattern.compile(
"<Value\\s+" + "<Value\\s+" +
"name=\"([^\"]+)\"\\s+" + "name=\"([^\"]+)\"\\s+" +
@@ -381,6 +363,7 @@ public class JsonToXmlConversionService {
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
line = line.trim(); line = line.trim();
// 解析注释行确定当前规则所属的数据组MMXU、间谐波、MHAI等
if (line.startsWith("<!--")) { if (line.startsWith("<!--")) {
if (line.contains("MMXU") || line.contains("基本数据")) { if (line.contains("MMXU") || line.contains("基本数据")) {
currentGroup = "MMXU"; currentGroup = "MMXU";
@@ -400,6 +383,7 @@ public class JsonToXmlConversionService {
continue; continue;
} }
// 跳过空行和非Value标签行
if (line.isEmpty() || !line.startsWith("<Value")) { if (line.isEmpty() || !line.startsWith("<Value")) {
continue; continue;
} }
@@ -413,6 +397,7 @@ public class JsonToXmlConversionService {
String doPath = matcher.group(4) != null ? matcher.group(4) : ""; String doPath = matcher.group(4) != null ? matcher.group(4) : "";
String daPath = matcher.group(5) != null ? matcher.group(5) : ""; String daPath = matcher.group(5) != null ? matcher.group(5) : "";
// 如果配置了索引映射则应用索引映射转换DO路径
if (indexMapping != null && !doPath.isEmpty()) { if (indexMapping != null && !doPath.isEmpty()) {
doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc()); doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc());
} }
@@ -424,6 +409,7 @@ public class JsonToXmlConversionService {
rule.setLimitDown(matcher.group(8)); rule.setLimitDown(matcher.group(8));
rule.setCoefficient(matcher.group(9)); rule.setCoefficient(matcher.group(9));
// 构建规则key并添加到规则集合中
String key = buildRuleKey(rule.getName(), rule.getDesc()); String key = buildRuleKey(rule.getName(), rule.getDesc());
if (!key.isEmpty() && hasValidDoOrDa(rule)) { if (!key.isEmpty() && hasValidDoOrDa(rule)) {
rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule); rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule);
@@ -435,6 +421,9 @@ public class JsonToXmlConversionService {
return rules; return rules;
} }
/**
* 解析单个规则文件仅按desc作为key用于描述匹配
*/
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> parseRuleFileDesc( private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> parseRuleFileDesc(
InputStream ruleStream, InputStream ruleStream,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception { IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
@@ -448,6 +437,7 @@ public class JsonToXmlConversionService {
String line; String line;
String currentGroup = ""; String currentGroup = "";
// 定义Value标签的正则表达式模式用于解析规则文件中的<Value>节点
java.util.regex.Pattern valueRulePattern = java.util.regex.Pattern.compile( java.util.regex.Pattern valueRulePattern = java.util.regex.Pattern.compile(
"<Value\\s+" + "<Value\\s+" +
"name=\"([^\"]+)\"\\s+" + "name=\"([^\"]+)\"\\s+" +
@@ -465,6 +455,7 @@ public class JsonToXmlConversionService {
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
line = line.trim(); line = line.trim();
// 解析注释行,确定当前规则所属的数据组
if (line.startsWith("<!--")) { if (line.startsWith("<!--")) {
if (line.contains("MMXU") || line.contains("基本数据")) { if (line.contains("MMXU") || line.contains("基本数据")) {
currentGroup = "MMXU"; currentGroup = "MMXU";
@@ -484,6 +475,7 @@ public class JsonToXmlConversionService {
continue; continue;
} }
// 跳过空行和非Value标签行
if (line.isEmpty() || !line.startsWith("<Value")) { if (line.isEmpty() || !line.startsWith("<Value")) {
continue; continue;
} }
@@ -497,6 +489,7 @@ public class JsonToXmlConversionService {
String doPath = matcher.group(4) != null ? matcher.group(4) : ""; String doPath = matcher.group(4) != null ? matcher.group(4) : "";
String daPath = matcher.group(5) != null ? matcher.group(5) : ""; String daPath = matcher.group(5) != null ? matcher.group(5) : "";
// 如果配置了索引映射则应用索引映射转换DO路径
if (indexMapping != null && !doPath.isEmpty()) { if (indexMapping != null && !doPath.isEmpty()) {
doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc()); doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc());
} }
@@ -508,6 +501,7 @@ public class JsonToXmlConversionService {
rule.setLimitDown(matcher.group(8)); rule.setLimitDown(matcher.group(8));
rule.setCoefficient(matcher.group(9)); rule.setCoefficient(matcher.group(9));
// 仅使用desc作为规则key
String key = rule.getDesc(); String key = rule.getDesc();
if (!key.isEmpty() && hasValidDoOrDa(rule)) { if (!key.isEmpty() && hasValidDoOrDa(rule)) {
rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule); rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule);
@@ -519,6 +513,10 @@ public class JsonToXmlConversionService {
return rules; return rules;
} }
/**
* 应用索引映射将规则中的DO路径索引转换为实际使用的索引
* 例如将MMXU1转换为MMXU2根据用户选择的统计类型
*/
private String applyIndexMapping(String doPath, String currentGroup, private String applyIndexMapping(String doPath, String currentGroup,
IcdToXmlMappingService.IndexMappingConfig indexMapping, IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) { String name, String desc) {
@@ -526,7 +524,9 @@ public class JsonToXmlConversionService {
return doPath; return doPath;
} }
// 匹配带索引的逻辑节点名称如MMXU1、MHAI2等
java.util.regex.Pattern doIndexPattern = java.util.regex.Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)(\\d+)"); java.util.regex.Pattern doIndexPattern = java.util.regex.Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)(\\d+)");
// 匹配不带索引但以$结尾的逻辑节点名称
java.util.regex.Pattern doNoIndexPattern = java.util.regex.Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)\\$"); java.util.regex.Pattern doNoIndexPattern = java.util.regex.Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)\\$");
java.util.regex.Matcher matcherWithIndex = doIndexPattern.matcher(doPath); java.util.regex.Matcher matcherWithIndex = doIndexPattern.matcher(doPath);
@@ -534,6 +534,7 @@ public class JsonToXmlConversionService {
String lnClass = matcherWithIndex.group(1); String lnClass = matcherWithIndex.group(1);
int originalIndex = Integer.parseInt(matcherWithIndex.group(2)); int originalIndex = Integer.parseInt(matcherWithIndex.group(2));
// 根据统计类型平均值、最大值、最小值、95值确定新的索引
int newIndex = determineNewIndex(currentGroup, lnClass, originalIndex, indexMapping, name, desc); int newIndex = determineNewIndex(currentGroup, lnClass, originalIndex, indexMapping, name, desc);
if (newIndex >= 0) { if (newIndex >= 0) {
@@ -557,6 +558,10 @@ public class JsonToXmlConversionService {
return doPath; return doPath;
} }
/**
* 根据统计类型确定新的索引值
* 统计类型包括:平均值(avg)、最大值(max)、最小值(min)、95百分位值(g_)
*/
private int determineNewIndex(String currentGroup, String lnClass, int originalIndex, private int determineNewIndex(String currentGroup, String lnClass, int originalIndex,
IcdToXmlMappingService.IndexMappingConfig indexMapping, IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) { String name, String desc) {
@@ -567,13 +572,15 @@ public class JsonToXmlConversionService {
String lowerName = (name != null ? name : "").toLowerCase(); String lowerName = (name != null ? name : "").toLowerCase();
String lowerDesc = (desc != null ? desc : "").toLowerCase(); String lowerDesc = (desc != null ? desc : "").toLowerCase();
// 判断统计类型
boolean isAverage = lowerName.contains("avg") || lowerDesc.contains("平均值"); boolean isAverage = lowerName.contains("avg") || lowerDesc.contains("平均值");
boolean isMaximum = lowerName.startsWith("max_") || lowerDesc.contains("最大值"); boolean isMaximum = lowerName.startsWith("max_") || lowerDesc.contains("最大值");
boolean isMinimum = lowerName.startsWith("min_") || lowerDesc.contains("最小值"); boolean isMinimum = lowerName.startsWith("min_") || lowerDesc.contains("最小值");
boolean is95Percentile = lowerName.startsWith("g_") || lowerDesc.contains("95"); boolean is95Percentile = lowerName.startsWith("g_") || lowerDesc.contains("95");
boolean isInterHarmonic = lowerDesc.contains("间谐波"); boolean isInterHarmonic = lowerDesc.contains("间谐波");
// 根据不同的数据组和统计类型,从索引映射配置中获取对应的索引值
if ("MMXU".equals(currentGroup) || "MMXU".equals(lnClass)) { if ("MMXU".equals(currentGroup) || "MMXU".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mmxu = indexMapping.getMmxu(); IcdToXmlMappingService.IndexMappingConfig.StatIndex mmxu = indexMapping.getMmxu();
if (mmxu != null) { if (mmxu != null) {
@@ -635,6 +642,9 @@ public class JsonToXmlConversionService {
return originalIndex; return originalIndex;
} }
/**
* 构建规则key优先使用name如果name为空则使用desc
*/
private String buildRuleKey(String name, String desc) { private String buildRuleKey(String name, String desc) {
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
return name; return name;
@@ -642,11 +652,19 @@ public class JsonToXmlConversionService {
return desc != null ? desc : ""; return desc != null ? desc : "";
} }
/**
* 检查规则是否有有效的DO或DA路径
*/
private boolean hasValidDoOrDa(RuleBasedXmlMappingService.ValueRule rule) { private boolean hasValidDoOrDa(RuleBasedXmlMappingService.ValueRule rule) {
return (rule.getDoPath() != null && !rule.getDoPath().isEmpty()) || return (rule.getDoPath() != null && !rule.getDoPath().isEmpty()) ||
(rule.getDaPath() != null && !rule.getDaPath().isEmpty()); (rule.getDaPath() != null && !rule.getDaPath().isEmpty());
} }
/**
* 从映射文档中提取所有指标信息
* 遍历DataSetList -> InstList -> DoiList -> SdiList -> TypeList层级结构
* 构建以desc组合为key的指标映射表
*/
private java.util.Map<String, MetricInfo> extractMetricsFromMapping(MappingDocument mappingDocument) { private java.util.Map<String, MetricInfo> extractMetricsFromMapping(MappingDocument mappingDocument) {
java.util.Map<String, MetricInfo> metrics = new java.util.HashMap<>(); java.util.Map<String, MetricInfo> metrics = new java.util.HashMap<>();
@@ -654,19 +672,24 @@ public class JsonToXmlConversionService {
return metrics; return metrics;
} }
// 遍历数据集列表
for (var dataSetGroup : mappingDocument.getDataSetList()) { for (var dataSetGroup : mappingDocument.getDataSetList()) {
if (dataSetGroup.getInstList() == null) continue; if (dataSetGroup.getInstList() == null) continue;
// 遍历实例列表
for (var instItem : dataSetGroup.getInstList()) { for (var instItem : dataSetGroup.getInstList()) {
if (instItem.getDoiList() == null) continue; if (instItem.getDoiList() == null) continue;
// 遍历DOI列表
for (var doiItem : instItem.getDoiList()) { for (var doiItem : instItem.getDoiList()) {
String doPath = buildDoPath(dataSetGroup.getLnClass(), instItem.getInst(), doiItem.getName()); String doPath = buildDoPath(dataSetGroup.getLnClass(), instItem.getInst(), doiItem.getName());
// 遍历SDI列表和Type列表构建完整的指标信息
if (doiItem.getSdiList() != null) { if (doiItem.getSdiList() != null) {
for (var sdiItem : doiItem.getSdiList()) { for (var sdiItem : doiItem.getSdiList()) {
if (sdiItem.getTypeList() != null) { if (sdiItem.getTypeList() != null) {
for (var typeItem : sdiItem.getTypeList()) { for (var typeItem : sdiItem.getTypeList()) {
// 构建指标的唯一key基于desc组合
String fullKey = doiItem.getDesc() + instItem.getDesc()+typeItem.getDesc(); String fullKey = doiItem.getDesc() + instItem.getDesc()+typeItem.getDesc();
if(fullKey.contains("正序")){ if(fullKey.contains("正序")){
fullKey = doiItem.getDesc() + instItem.getDesc()+sdiItem.getDesc()+typeItem.getDesc(); fullKey = doiItem.getDesc() + instItem.getDesc()+sdiItem.getDesc()+typeItem.getDesc();
@@ -697,6 +720,9 @@ public class JsonToXmlConversionService {
return metrics; return metrics;
} }
/**
* 构建DO路径格式LN类名+LN实例+$MX+$DO名称
*/
private String buildDoPath(String lnClass, String lnInst, String doName) { private String buildDoPath(String lnClass, String lnInst, String doName) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (lnClass != null) { if (lnClass != null) {
@@ -716,22 +742,28 @@ public class JsonToXmlConversionService {
return sb.toString(); return sb.toString();
} }
/**
* 构建DA路径处理相位名称标准化和谐波索引
*/
private String buildDaPath(String sdiName, String typeName, DoiItem doiItem) { private String buildDaPath(String sdiName, String typeName, DoiItem doiItem) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (sdiName != null && !sdiName.isEmpty()) { if (sdiName != null && !sdiName.isEmpty()) {
String normalizedSdi = normalizePhaseName(sdiName); String normalizedSdi = normalizePhaseName(sdiName);
sb.append(normalizedSdi); sb.append(normalizedSdi);
// 处理谐波数据的特殊索引格式
if (sdiName.endsWith("Har") && doiItem != null) { if (sdiName.endsWith("Har") && doiItem != null) {
int baseflag = doiItem.getBaseflag(); int baseflag = doiItem.getBaseflag();
int start = doiItem.getStart(); int start = doiItem.getStart();
if (baseflag == 1) { if (baseflag == 1) {
// 间谐波使用特殊格式:[%-i]
boolean isInterHarmonic = isInterHarmonicMetric(doiItem); boolean isInterHarmonic = isInterHarmonicMetric(doiItem);
int m = isInterHarmonic ? 0 : 2; int m = isInterHarmonic ? 0 : 2;
int i = m - start; int i = m - start;
sb.append("[%-").append(i).append("]"); sb.append("[%-").append(i).append("]");
} else { } else {
// 普通谐波使用格式:[start]
sb.append("[").append(start).append("]"); sb.append("[").append(start).append("]");
} }
} }
@@ -745,6 +777,9 @@ public class JsonToXmlConversionService {
return sb.toString(); return sb.toString();
} }
/**
* 标准化相位名称将phsa、phsb、phsc统一为phs*
*/
private String normalizePhaseName(String name) { private String normalizePhaseName(String name) {
if (name == null || name.isEmpty()) { if (name == null || name.isEmpty()) {
return name; return name;
@@ -761,6 +796,9 @@ public class JsonToXmlConversionService {
return name; return name;
} }
/**
* 判断指标是否为间谐波类型
*/
private boolean isInterHarmonicMetric(DoiItem doiItem) { private boolean isInterHarmonicMetric(DoiItem doiItem) {
if (doiItem == null || doiItem.getDesc() == null) { if (doiItem == null || doiItem.getDesc() == null) {
return false; return false;
@@ -768,6 +806,9 @@ public class JsonToXmlConversionService {
return doiItem.getDesc().toLowerCase().contains("间谐波"); return doiItem.getDesc().toLowerCase().contains("间谐波");
} }
/**
* 查找适用的规则(旧版方法)
*/
private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> findApplicableRulesDesc( private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> findApplicableRulesDesc(
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules, java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
java.util.Map<String, MetricInfo> mappingMetrics) { java.util.Map<String, MetricInfo> mappingMetrics) {
@@ -775,6 +816,10 @@ public class JsonToXmlConversionService {
return findApplicableRulesDescWithUnmatched(allRules, mappingMetrics).getApplicableRules(); return findApplicableRulesDescWithUnmatched(allRules, mappingMetrics).getApplicableRules();
} }
/**
* 查找适用的规则并返回未匹配规则详情
* 核心匹配逻辑:遍历所有规则,在指标映射表中查找匹配的指标
*/
private RuleMatchingResult findApplicableRulesDescWithUnmatched( private RuleMatchingResult findApplicableRulesDescWithUnmatched(
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules, java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
java.util.Map<String, MetricInfo> mappingMetrics) { java.util.Map<String, MetricInfo> mappingMetrics) {
@@ -782,19 +827,23 @@ public class JsonToXmlConversionService {
java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicable = new java.util.HashMap<>(); java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicable = new java.util.HashMap<>();
List<UnmatchedRuleDetail> failedRuleDetails = new ArrayList<>(); List<UnmatchedRuleDetail> failedRuleDetails = new ArrayList<>();
// 遍历所有规则
for (var entry : allRules.entrySet()) { for (var entry : allRules.entrySet()) {
String ruleKey = entry.getKey(); String ruleKey = entry.getKey();
List<RuleBasedXmlMappingService.ValueRule> ruleVariants = entry.getValue(); List<RuleBasedXmlMappingService.ValueRule> ruleVariants = entry.getValue();
boolean matched = false; boolean matched = false;
// 尝试匹配规则的任意一个候选变体
for (RuleBasedXmlMappingService.ValueRule rule : ruleVariants) { for (RuleBasedXmlMappingService.ValueRule rule : ruleVariants) {
if (rule.getName() == null || rule.getDoPath().isEmpty()) { if (rule.getName() == null || rule.getDoPath().isEmpty()) {
continue; continue;
} }
// 直接通过name在指标映射表中查找
if(mappingMetrics.containsKey(rule.getName())){ if(mappingMetrics.containsKey(rule.getName())){
MetricInfo metric = mappingMetrics.get(rule.getName()); MetricInfo metric = mappingMetrics.get(rule.getName());
// 将匹配到的指标的DO和DA路径赋值给规则
rule.setDoPath(metric.getDoPath()); rule.setDoPath(metric.getDoPath());
rule.setDaPath(metric.getDaPath()); rule.setDaPath(metric.getDaPath());
applicable.put(ruleKey, rule); applicable.put(ruleKey, rule);
@@ -807,6 +856,7 @@ public class JsonToXmlConversionService {
} }
} }
// 如果所有候选都未匹配,记录未匹配规则详情
if (!matched) { if (!matched) {
UnmatchedRuleDetail detail = new UnmatchedRuleDetail(); UnmatchedRuleDetail detail = new UnmatchedRuleDetail();
detail.setRuleKey(ruleKey); detail.setRuleKey(ruleKey);
@@ -856,6 +906,9 @@ public class JsonToXmlConversionService {
return result; return result;
} }
/**
* 查找适用的规则(旧版方法,使用路径匹配)
*/
private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> findApplicableRules( private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> findApplicableRules(
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules, java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
java.util.Map<String, MetricInfo> mappingMetrics) { java.util.Map<String, MetricInfo> mappingMetrics) {
@@ -877,6 +930,7 @@ public class JsonToXmlConversionService {
String ruleDo = normalizePath(rule.getDoPath()); String ruleDo = normalizePath(rule.getDoPath());
String ruleDa = normalizePath(rule.getDaPath()); String ruleDa = normalizePath(rule.getDaPath());
// 遍历所有指标,尝试通过路径模式匹配
for (var metricEntry : mappingMetrics.entrySet()) { for (var metricEntry : mappingMetrics.entrySet()) {
MetricInfo metric = metricEntry.getValue(); MetricInfo metric = metricEntry.getValue();
@@ -919,16 +973,24 @@ public class JsonToXmlConversionService {
return applicable; return applicable;
} }
/**
* 标准化路径字符串,转为小写并去除首尾空格
*/
private String normalizePath(String path) { private String normalizePath(String path) {
if (path == null) return ""; if (path == null) return "";
return path.trim().toLowerCase(); return path.trim().toLowerCase();
} }
/**
* 判断路径是否匹配模式
* 支持通配符匹配,忽略数组索引部分
*/
private boolean matchesPattern(String pattern, String actual) { private boolean matchesPattern(String pattern, String actual) {
if (pattern == null || actual == null) { if (pattern == null || actual == null) {
return pattern == null && actual == null; return pattern == null && actual == null;
} }
// 移除数组索引部分(如[1]、[%-0]等)后再比较
String normalizedPattern = pattern.replaceAll("\\[[^\\]]*\\]", ""); String normalizedPattern = pattern.replaceAll("\\[[^\\]]*\\]", "");
if (normalizedPattern.isEmpty() || actual.isEmpty()) { if (normalizedPattern.isEmpty() || actual.isEmpty()) {
@@ -938,9 +1000,14 @@ public class JsonToXmlConversionService {
return actual.equals(normalizedPattern); return actual.equals(normalizedPattern);
} }
/**
* 将匹配的规则应用到XML内容中
* 通过正则表达式查找XML中的<Value>节点,并用匹配的规则替换
*/
private String applyRulesToXml(String xmlContent, private String applyRulesToXml(String xmlContent,
java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicableRules) { java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicableRules) {
// 定义XML Value标签的正则表达式模式
java.util.regex.Pattern xmlValuePattern = java.util.regex.Pattern.compile( java.util.regex.Pattern xmlValuePattern = java.util.regex.Pattern.compile(
"<Value\\s+" + "<Value\\s+" +
"name=\"([^\"]+)\"\\s+" + "name=\"([^\"]+)\"\\s+" +
@@ -958,13 +1025,16 @@ public class JsonToXmlConversionService {
java.util.regex.Matcher matcher = xmlValuePattern.matcher(xmlContent); java.util.regex.Matcher matcher = xmlValuePattern.matcher(xmlContent);
StringBuffer result = new StringBuffer(); StringBuffer result = new StringBuffer();
// 遍历所有匹配的Value节点
while (matcher.find()) { while (matcher.find()) {
String name = matcher.group(1); String name = matcher.group(1);
String desc = matcher.group(2); String desc = matcher.group(2);
String type = matcher.group(3); String type = matcher.group(3);
// 根据desc查找匹配的规则
RuleBasedXmlMappingService.ValueRule matchedRule = applicableRules.get(desc); RuleBasedXmlMappingService.ValueRule matchedRule = applicableRules.get(desc);
if (matchedRule != null) { if (matchedRule != null) {
// 构建新的Value节点字符串使用规则中的DO、DA等信息
StringBuilder valueNode = new StringBuilder("<Value "); StringBuilder valueNode = new StringBuilder("<Value ");
valueNode.append("name=\"").append(escapeXml(name)).append("\" "); valueNode.append("name=\"").append(escapeXml(name)).append("\" ");
valueNode.append("desc=\"").append(escapeXml(desc)).append("\" "); valueNode.append("desc=\"").append(escapeXml(desc)).append("\" ");
@@ -998,6 +1068,9 @@ public class JsonToXmlConversionService {
return result.toString(); return result.toString();
} }
/**
* XML特殊字符转义
*/
private String escapeXml(String text) { private String escapeXml(String text) {
if (text == null) return ""; if (text == null) return "";
return text.replace("&", "&amp;") return text.replace("&", "&amp;")
@@ -1007,6 +1080,9 @@ public class JsonToXmlConversionService {
.replace("'", "&apos;"); .replace("'", "&apos;");
} }
/**
* 指标信息内部类存储从JSON中提取的指标数据
*/
static class MetricInfo { static class MetricInfo {
private String lnClass; private String lnClass;
private String lnInst; private String lnInst;
@@ -1060,6 +1136,9 @@ public class JsonToXmlConversionService {
} }
} }
/**
* 规则匹配结果类,包含匹配成功的规则和未匹配的规则详情
*/
public static class RuleMatchingResult { public static class RuleMatchingResult {
private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicableRules; private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicableRules;
private List<UnmatchedRuleDetail> unmatchedRuleDetails; private List<UnmatchedRuleDetail> unmatchedRuleDetails;
@@ -1090,6 +1169,9 @@ public class JsonToXmlConversionService {
} }
} }
/**
* 转换结果类包含生成的XML内容和未匹配的规则详情
*/
public static class ConversionResult { public static class ConversionResult {
private String xmlContent; private String xmlContent;
private List<UnmatchedRuleDetail> unmatchedRuleDetails; private List<UnmatchedRuleDetail> unmatchedRuleDetails;
@@ -1111,6 +1193,9 @@ public class JsonToXmlConversionService {
} }
} }
/**
* 未匹配规则详情类,记录匹配失败的规则信息
*/
public static class UnmatchedRuleDetail { public static class UnmatchedRuleDetail {
private String ruleKey; private String ruleKey;
private String ruleVariants; private String ruleVariants;

View File

@@ -1,31 +1,26 @@
package com.njcn.gather.icd.mapping.component; package com.njcn.gather.icd.mapping.component;
import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
import com.njcn.gather.icd.mapping.pojo.bo.icd.*; import com.njcn.gather.icd.mapping.pojo.bo.icd.*;
import com.njcn.gather.icd.mapping.utils.XmlTemplateParser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.*; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.util.ArrayList;
import java.nio.file.Files; import java.util.List;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.njcn.gather.icd.mapping.pojo.dto.*;
import com.njcn.gather.icd.mapping.pojo.bo.*;
import com.njcn.gather.icd.mapping.pojo.param.*;
import com.njcn.gather.icd.mapping.pojo.vo.*;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
@Component @Component
public class RuleBasedXmlMappingService { public class RuleBasedXmlMappingService {
@Autowired @Autowired
private IcdParserService icdParserService; private IcdParserService icdParserService;
@Autowired
private MappingModuleConfig mappingModuleConfig;
private static final Pattern VALUE_RULE_PATTERN = Pattern.compile( private static final Pattern VALUE_RULE_PATTERN = Pattern.compile(
"<Value\\s+" + "<Value\\s+" +
"name=\"([^\"]+)\"\\s+" + "name=\"([^\"]+)\"\\s+" +
@@ -57,20 +52,27 @@ public class RuleBasedXmlMappingService {
private static final Pattern DO_INDEX_PATTERN = Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)(\\d+)"); private static final Pattern DO_INDEX_PATTERN = Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)(\\d+)");
private static final Pattern DO_NO_INDEX_PATTERN = Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)\\$"); private static final Pattern DO_NO_INDEX_PATTERN = Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)\\$");
/**
* 加载JSON转XML使用的默认XML模板文件
* 路径通过MappingModuleConfig配置默认为template/JiangSu_Config2.xml
*/
public InputStream loadDefaultXmlFile() throws Exception { public InputStream loadDefaultXmlFile() throws Exception {
ClassPathResource templateResource = new ClassPathResource("template/JiangSu_Config2.xml"); ClassPathResource templateResource = new ClassPathResource(mappingModuleConfig.getJsonToXmlTemplatePath());
if (!templateResource.exists()) { if (!templateResource.exists()) {
return null; return null;
} }
return templateResource.getInputStream(); return templateResource.getInputStream();
} }
/**
* 加载JSON转XML使用的默认规则文件
* 路径通过MappingModuleConfig配置默认为template/默认规则.txt
*/
public List<InputStream> loadDefaultRuleFile() throws Exception { public List<InputStream> loadDefaultRuleFile() throws Exception {
ClassPathResource ruleResource = new ClassPathResource("template/默认规则.txt"); ClassPathResource ruleResource = new ClassPathResource(mappingModuleConfig.getJsonToXmlRulePath());
if (!ruleResource.exists()) { if (!ruleResource.exists()) {
return null; return null;
} }
@@ -81,117 +83,6 @@ public class RuleBasedXmlMappingService {
return ruleStreams; return ruleStreams;
} }
public String generateWithRuleFiles(IcdDocument icdDocument,
InputStream templateStream,
List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
Map<String, List<ValueRule>> mergedRules = mergeAllRules(ruleStreams, indexMapping);
Map<String, IcdMetricInfo> icdMetrics = extractIcdMetrics(icdDocument);
Map<String, ValueRule> applicableRules = findApplicableRules(mergedRules, icdMetrics);
XmlTemplateParser.ParseResult parseResult = new XmlTemplateParser().parse(templateStream);
String xmlContent = parseResult.getTemplateContent();
xmlContent = fillIedInfo(xmlContent, icdDocument);
xmlContent = fillReportControls(xmlContent, icdDocument);
xmlContent = applyRulesToXml(xmlContent, applicableRules);
Path tempPath = Files.createTempFile("rule_mapped_", ".xml");
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8));
return tempPath.toString();
}
private Map<String, List<ValueRule>> mergeAllRules(List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
Map<String, List<ValueRule>> mergedRules = new LinkedHashMap<>();
for (InputStream ruleStream : ruleStreams) {
Map<String, List<ValueRule>> rules = parseRuleFile(ruleStream, indexMapping);
for (Map.Entry<String, List<ValueRule>> entry : rules.entrySet()) {
String key = entry.getKey();
List<ValueRule> ruleList = entry.getValue();
mergedRules.computeIfAbsent(key, k -> new ArrayList<>()).addAll(ruleList);
}
}
return mergedRules;
}
private Map<String, List<ValueRule>> parseRuleFile(InputStream ruleStream,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
Map<String, List<ValueRule>> rules = new HashMap<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(ruleStream, StandardCharsets.UTF_8));
String line;
String currentGroup = "";
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("<!--")) {
if (line.contains("MMXU") || line.contains("基本数据")) {
currentGroup = "MMXU";
} else if (line.contains("间谐波")) {
currentGroup = "INTER_HARMONIC";
} else if (line.contains("MHAI") && line.contains("谐波数据")) {
currentGroup = "MHAI";
} else if (line.contains("MSQI") || line.contains("序分量")) {
currentGroup = "MSQI";
} else if (line.contains("短闪") || line.contains("短时闪变")) {
currentGroup = "MFLK_SHORT";
} else if (line.contains("长闪") || line.contains("长时闪变")) {
currentGroup = "MFLK_LONG";
} else if (line.contains("QVVR") || line.contains("电压变动")) {
currentGroup = "QVVR";
}
continue;
}
if (line.isEmpty() || !line.startsWith("<Value")) {
continue;
}
Matcher matcher = VALUE_RULE_PATTERN.matcher(line);
if (matcher.find()) {
ValueRule rule = new ValueRule();
rule.setName(matcher.group(1));
rule.setDesc(matcher.group(2));
rule.setType(matcher.group(3));
String doPath = matcher.group(4) != null ? matcher.group(4) : "";
String daPath = matcher.group(5) != null ? matcher.group(5) : "";
if (indexMapping != null && !doPath.isEmpty()) {
doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc());
}
rule.setDoPath(doPath);
rule.setDaPath(daPath);
rule.setBaseFlag(matcher.group(6));
rule.setLimitUp(matcher.group(7));
rule.setLimitDown(matcher.group(8));
rule.setCoefficient(matcher.group(9));
String key = buildRuleKey(rule.getName(), rule.getDesc());
if (!key.isEmpty() && hasValidDoOrDa(rule)) {
rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule);
}
}
}
reader.close();
return rules;
}
private String applyIndexMapping(String doPath, String currentGroup, private String applyIndexMapping(String doPath, String currentGroup,
IcdToXmlMappingService.IndexMappingConfig indexMapping, IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) { String name, String desc) {
@@ -229,85 +120,6 @@ public class RuleBasedXmlMappingService {
return doPath; return doPath;
} }
private int determineNewIndexByName(String currentGroup, String lnClass,
IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) {
if (indexMapping == null || name == null) {
return -1;
}
String upperName = name.toUpperCase();
String lowerDesc = (desc != null ? desc : "").toLowerCase();
boolean isMaximum = upperName.startsWith("MAX_");
boolean isMinimum = upperName.startsWith("MIN_");
boolean is95Percentile = upperName.startsWith("G_");
boolean isAverage = !isMaximum && !isMinimum && !is95Percentile;
// 判断是否为间谐波指标:通过desc是否包含"间谐波"
boolean isInterHarmonic = lowerDesc.contains("间谐波");
if ("MMXU".equals(currentGroup) || "MMXU".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mmxu = indexMapping.getMmxu();
if (mmxu != null) {
if (isAverage && mmxu.getAverage() != null) return mmxu.getAverage();
if (isMaximum && mmxu.getMaximum() != null) return mmxu.getMaximum();
if (isMinimum && mmxu.getMinimum() != null) return mmxu.getMinimum();
if (is95Percentile && mmxu.getPercentile95() != null) return mmxu.getPercentile95();
}
}
if ("INTER_HARMONIC".equals(currentGroup) || isInterHarmonic) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex interHarmonic = indexMapping.getInterHarmonic();
if (interHarmonic != null) {
if (isAverage && interHarmonic.getAverage() != null) return interHarmonic.getAverage();
if (isMaximum && interHarmonic.getMaximum() != null) return interHarmonic.getMaximum();
if (isMinimum && interHarmonic.getMinimum() != null) return interHarmonic.getMinimum();
if (is95Percentile && interHarmonic.getPercentile95() != null) return interHarmonic.getPercentile95();
}
}
if ("MHAI".equals(currentGroup) && !isInterHarmonic) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mhai = indexMapping.getMhai();
if (mhai != null) {
if (isAverage && mhai.getAverage() != null) return mhai.getAverage();
if (isMaximum && mhai.getMaximum() != null) return mhai.getMaximum();
if (isMinimum && mhai.getMinimum() != null) return mhai.getMinimum();
if (is95Percentile && mhai.getPercentile95() != null) return mhai.getPercentile95();
}
}
if ("MSQI".equals(currentGroup) || "MSQI".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex msqi = indexMapping.getMsqi();
if (msqi != null) {
if (isAverage && msqi.getAverage() != null) return msqi.getAverage();
if (isMaximum && msqi.getMaximum() != null) return msqi.getMaximum();
if (isMinimum && msqi.getMinimum() != null) return msqi.getMinimum();
if (is95Percentile && msqi.getPercentile95() != null) return msqi.getPercentile95();
}
}
if ("MFLK_SHORT".equals(currentGroup) || "MFLK".equals(lnClass)) {
if (indexMapping.getMflkShort() != null) {
return indexMapping.getMflkShort();
}
}
if ("MFLK_LONG".equals(currentGroup)) {
if (indexMapping.getMflkLong() != null) {
return indexMapping.getMflkLong();
}
}
if ("QVVR".equals(currentGroup) || "QVVR".equals(lnClass)) {
if (indexMapping.getQvvr() != null) {
return indexMapping.getQvvr();
}
}
return -1;
}
private int determineNewIndex(String currentGroup, String lnClass, int originalIndex, private int determineNewIndex(String currentGroup, String lnClass, int originalIndex,
IcdToXmlMappingService.IndexMappingConfig indexMapping, IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) { String name, String desc) {
@@ -387,40 +199,6 @@ public class RuleBasedXmlMappingService {
return originalIndex; return originalIndex;
} }
private Map<String, IcdMetricInfo> extractIcdMetrics(IcdDocument icdDocument) {
Map<String, IcdMetricInfo> metrics = new HashMap<>();
if (icdDocument == null || icdDocument.getLogicalNodes() == null) {
return metrics;
}
for (LnNode lnNode : icdDocument.getLogicalNodes()) {
if (lnNode.getDoiList() == null) continue;
for (DoiNode doiNode : lnNode.getDoiList()) {
String doPath = buildDoPath(lnNode, doiNode);
List<String> daPaths = collectAllDaPaths(doiNode.getChildren(), new ArrayList<>());
for (String daPath : daPaths) {
String fullKey = buildMetricKey(doPath, daPath);
IcdMetricInfo info = new IcdMetricInfo();
info.setLnClass(lnNode.getLnClass());
info.setLnInst(lnNode.getLnInst());
info.setDoName(doiNode.getName());
info.setDoPath(doPath);
info.setDaPath(daPath);
info.setPhase(extractPhase(daPath));
info.setType(extractType(daPath));
metrics.put(fullKey.toLowerCase(), info);
}
}
}
return metrics;
}
private List<String> collectAllDaPaths(List<DoiElementNode> nodes, List<String> currentPath) { private List<String> collectAllDaPaths(List<DoiElementNode> nodes, List<String> currentPath) {
List<String> results = new ArrayList<>(); List<String> results = new ArrayList<>();
if (nodes == null) return results; if (nodes == null) return results;
@@ -490,87 +268,6 @@ public class RuleBasedXmlMappingService {
return sb.toString(); return sb.toString();
} }
private String buildMetricKey(String doPath, String daPath) {
return doPath + "|" + daPath;
}
private Map<String, ValueRule> findApplicableRules(Map<String, List<ValueRule>> allRules,
Map<String, IcdMetricInfo> icdMetrics) {
Map<String, ValueRule> applicable = new HashMap<>();
System.out.println("========== 开始匹配规则 ==========");
System.out.println("规则总数: " + allRules.size());
System.out.println("ICD指标总数: " + icdMetrics.size());
int matchedCount = 0;
int unmatchedCount = 0;
for (Map.Entry<String, List<ValueRule>> entry : allRules.entrySet()) {
String ruleKey = entry.getKey();
List<ValueRule> ruleVariants = entry.getValue();
boolean foundInIcd = false;
ValueRule matchedRule = null;
System.out.println("\n正在匹配规则: " + ruleKey + " (共" + ruleVariants.size() + "个候选)");
for (int i = 0; i < ruleVariants.size(); i++) {
ValueRule rule = ruleVariants.get(i);
if (rule.getDoPath() == null || rule.getDoPath().isEmpty()) {
System.out.println(" 候选[" + (i+1) + "] 跳过: DO为空");
continue;
}
String ruleDo = normalizePath(rule.getDoPath());
String ruleDa = normalizePath(rule.getDaPath());
System.out.println(" 尝试候选[" + (i+1) + "]: DO=" + rule.getDoPath() + " DA=" + rule.getDaPath());
for (Map.Entry<String, IcdMetricInfo> icdEntry : icdMetrics.entrySet()) {
String icdKey = icdEntry.getKey();
IcdMetricInfo icdMetric = icdEntry.getValue();
String icdDo = normalizePath(icdMetric.getDoPath());
String icdDa = normalizePath(icdMetric.getDaPath());
boolean doMatch = matchesPattern(ruleDo, icdDo);
boolean daMatch = matchesPattern(ruleDa, icdDa);
if (doMatch && daMatch) {
System.out.println(" ✓ 匹配成功! ICD Key: " + icdKey);
System.out.println(" ICD DO=" + icdMetric.getDoPath() + " DA=" + icdMetric.getDaPath());
foundInIcd = true;
matchedRule = rule;
break;
}
}
if (foundInIcd) {
System.out.println(" >> 规则 " + ruleKey + " 匹配成功,使用候选[" + (i+1) + "]");
break;
}
}
if (foundInIcd && matchedRule != null) {
applicable.put(ruleKey, matchedRule);
matchedCount++;
} else {
unmatchedCount++;
if (unmatchedCount <= 15) {
System.out.println("✗ 未匹配: " + ruleKey + " (共" + ruleVariants.size() + "个候选)");
for (ValueRule rule : ruleVariants) {
System.out.println(" 候选: DO=" + rule.getDoPath() + " DA=" + rule.getDaPath());
}
}
}
}
System.out.println("\n========== 规则匹配结束 ==========");
System.out.println("匹配成功: " + matchedCount + " 条规则");
System.out.println("未匹配: " + unmatchedCount + " 条规则");
return applicable;
}
private String normalizePath(String path) { private String normalizePath(String path) {
if (path == null) return ""; if (path == null) return "";
@@ -604,49 +301,6 @@ public class RuleBasedXmlMappingService {
return xmlContent; return xmlContent;
} }
private String fillReportControls(String xmlContent, IcdDocument icdDocument) {
if (icdDocument == null || icdDocument.getReportControls() == null || icdDocument.getReportControls().isEmpty()) {
return xmlContent;
}
// 构建 ReportStat 内容
StringBuilder reportStatBuilder = new StringBuilder();
reportStatBuilder.append("\t\t<ReportStat>\n");
for (ReportControlNode rc : icdDocument.getReportControls()) {
if (rc.getName() != null && !rc.getName().isEmpty()) {
String name = rc.getName();
// 只处理特定的 ReportControl 名称
if (name.contains("brcbFluc") ||
name.contains("brcbStHarm") ||
name.contains("brcbStIHarm") ||
name.contains("brcbStMMXU") ||
name.contains("brcbStMSQI") ||
name.contains("brcbPLT")||
name.contains("brcbPST")||
name.contains("brcbFlicker")||
name.contains("brcbStatistic") ){
// 构建 ReportControl 字符串格式:
// LLN0$BR$brcbFlickerData,600,0,0,1,0,0,yes,1,1,1,1,1,0,1,1,1,3,1
String reportControlStr = buildReportControlString(rc);
reportStatBuilder.append("\t\t\t<Report ReportControl=\"")
.append(escapeXml(reportControlStr))
.append("\" />\n");
}
}
}
reportStatBuilder.append("\t\t</ReportStat>");
// 替换原有的 <ReportStat>...</ReportStat> 部分
xmlContent = xmlContent.replaceAll(
"<ReportStat>[\\s\\S]*?</ReportStat>",
reportStatBuilder.toString().replace("$", "\\$").replace("[", "\\[").replace("]", "\\]")
);
return xmlContent;
}
private String buildReportControlString(ReportControlNode rc) { private String buildReportControlString(ReportControlNode rc) {
// 格式LLN0$BR$brcbName,intgPd,dchg,qchg,dupd,period,gi,issuffixed,seqNum,timeStamp,reasonCode,dataSet,dataRef,bufOvfl,entryID,configRef,segmentation,FlickerFlag // 格式LLN0$BR$brcbName,intgPd,dchg,qchg,dupd,period,gi,issuffixed,seqNum,timeStamp,reasonCode,dataSet,dataRef,bufOvfl,entryID,configRef,segmentation,FlickerFlag
@@ -704,51 +358,6 @@ public class RuleBasedXmlMappingService {
return actual.equals(normalizedPattern); return actual.equals(normalizedPattern);
} }
private String applyRulesToXml(String xmlContent, Map<String, ValueRule> applicableRules) {
Matcher matcher = XML_VALUE_PATTERN.matcher(xmlContent);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String name = matcher.group(1);
String desc = matcher.group(2);
String type = matcher.group(3);
String key = buildRuleKey(name, desc);
ValueRule matchedRule = applicableRules.get(key.toLowerCase());
if (matchedRule != null) {
StringBuilder valueNode = new StringBuilder("<Value ");
valueNode.append("name=\"").append(escapeXml(name)).append("\" ");
valueNode.append("desc=\"").append(escapeXml(desc)).append("\" ");
valueNode.append("type=\"").append(type).append("\" ");
if ((matchedRule.getDoPath() != null) && !matchedRule.getDoPath().isEmpty()) {
valueNode.append("DO=\"").append(escapeXml(matchedRule.getDoPath())).append("\" ");
}
if (matchedRule.getDaPath() != null && !matchedRule.getDaPath().isEmpty()) {
valueNode.append("DA=\"").append(escapeXml(matchedRule.getDaPath())).append("\" ");
}
if (matchedRule.getBaseFlag() != null && !matchedRule.getBaseFlag().isEmpty()) {
valueNode.append("BaseFlag=\"").append(escapeXml(matchedRule.getBaseFlag())).append("\" ");
}
if (matchedRule.getLimitUp() != null && !matchedRule.getLimitUp().isEmpty()) {
valueNode.append("LimitUp=\"").append(escapeXml(matchedRule.getLimitUp())).append("\" ");
}
if (matchedRule.getLimitDown() != null && !matchedRule.getLimitDown().isEmpty()) {
valueNode.append("LimitDown=\"").append(escapeXml(matchedRule.getLimitDown())).append("\" ");
}
if (matchedRule.getCoefficient() != null && !matchedRule.getCoefficient().isEmpty()) {
valueNode.append("Coefficient=\"").append(escapeXml(matchedRule.getCoefficient())).append("\" ");
}
valueNode.append("/>");
matcher.appendReplacement(result, Matcher.quoteReplacement(valueNode.toString()));
}
}
matcher.appendTail(result);
return result.toString();
}
private String buildRuleKey(String name, String desc) { private String buildRuleKey(String name, String desc) {
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
@@ -825,28 +434,4 @@ public class RuleBasedXmlMappingService {
public void setCoefficient(String coefficient) { this.coefficient = coefficient; } public void setCoefficient(String coefficient) { this.coefficient = coefficient; }
} }
static class IcdMetricInfo {
private String lnClass;
private String lnInst;
private String doName;
private String doPath;
private String daPath;
private String phase;
private String type;
public String getLnClass() { return lnClass; }
public void setLnClass(String lnClass) { this.lnClass = lnClass; }
public String getLnInst() { return lnInst; }
public void setLnInst(String lnInst) { this.lnInst = lnInst; }
public String getDoName() { return doName; }
public void setDoName(String doName) { this.doName = doName; }
public String getDoPath() { return doPath; }
public void setDoPath(String doPath) { this.doPath = doPath; }
public String getDaPath() { return daPath; }
public void setDaPath(String daPath) { this.daPath = daPath; }
public String getPhase() { return phase; }
public void setPhase(String phase) { this.phase = phase; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
}
} }

View File

@@ -26,4 +26,12 @@ public class MappingModuleConfig {
/** 默认输出目录。 */ /** 默认输出目录。 */
@Value("${icd.mapping.default-output-dir:}") @Value("${icd.mapping.default-output-dir:}")
private String defaultOutputDir; private String defaultOutputDir;
/** JSON转XML使用的默认XML模板路径。 */
@Value("${icd.mapping.json-to-xml-template-path:template/JiangSu_Config2.xml}")
private String jsonToXmlTemplatePath;
/** JSON转XML使用的默认规则文件路径。 */
@Value("${icd.mapping.json-to-xml-rule-path:template/默认规则.txt}")
private String jsonToXmlRulePath;
} }

View File

@@ -12,14 +12,12 @@ import java.util.List;
public class IcdToXmlGenerateResult { public class IcdToXmlGenerateResult {
private GenerateStatus status; private GenerateStatus status;
private String message; private String message;
private String iedName; //private String iedName;
private String ldInst; //private String ldInst;
private IndexAnalysis indexAnalysis;
private MappingDocument mappingDocument;
private String savedPath;
private List<String> problems = new ArrayList<String>(); private List<String> problems = new ArrayList<String>();
/** 生成成功后的 Xml 字符串。 */ /** 生成成功后的 Xml 字符串。 */
private String mappingXml; private String mappingXml;
private MappingDocument mappingDocument;
public String getMappingXml() { public String getMappingXml() {
return mappingXml; return mappingXml;
} }
@@ -34,16 +32,13 @@ public class IcdToXmlGenerateResult {
public void setStatus(GenerateStatus status) { this.status = status; } public void setStatus(GenerateStatus status) { this.status = status; }
public String getMessage() { return message; } public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; } public void setMessage(String message) { this.message = message; }
public String getIedName() { return iedName; } // public String getIedName() { return iedName; }
public void setIedName(String iedName) { this.iedName = iedName; } // public void setIedName(String iedName) { this.iedName = iedName; }
public String getLdInst() { return ldInst; } // public String getLdInst() { return ldInst; }
public void setLdInst(String ldInst) { this.ldInst = 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 MappingDocument getMappingDocument() { return mappingDocument; }
public void setMappingDocument(MappingDocument mappingDocument) { this.mappingDocument = 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 List<String> getProblems() { return problems; }
public void setProblems(List<String> problems) { this.problems = problems; } public void setProblems(List<String> problems) { this.problems = problems; }
} }

View File

@@ -28,6 +28,8 @@ public class IcdToXmlTaskAppService {
private final IcdToXmlMappingService icdToXmlMappingService; private final IcdToXmlMappingService icdToXmlMappingService;
private final JsonToXmlConversionService jsonToXmlConversionService; private final JsonToXmlConversionService jsonToXmlConversionService;
public IcdToXmlTaskAppService(IcdParserService icdParserService, public IcdToXmlTaskAppService(IcdParserService icdParserService,
DefaultTemplateLoader defaultTemplateLoader, DefaultTemplateLoader defaultTemplateLoader,
IndexAnalysisService indexAnalysisService, IndexAnalysisService indexAnalysisService,
@@ -50,96 +52,6 @@ public class IcdToXmlTaskAppService {
this.jsonToXmlConversionService = jsonToXmlConversionService; this.jsonToXmlConversionService = jsonToXmlConversionService;
} }
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());
// 2. 加载 DefaultCfg.txt和默认配置
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;
}
// 5. 校验用户提交的绑定关系
ValidationResult validationResult = indexValidationService.validate(indexAnalysis, command.getIndexSelection());
if (!validationResult.isValid()) {
result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交");
result.getProblems().addAll(validationResult.getProblems());
return result;
}
// 6. 生成正式映射结构JSON中间态
MappingDocument mappingDocument = mappingGenerationService.generate(
icdDocument,
template,
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带未匹配规则信息
JsonToXmlConversionService.ConversionResult conversionResult =
jsonToXmlConversionService.convertFromJsonWithResult(
mappingJson,
templateStream,
ruleStreams,
icdToXmlMappingService.getIndexMapping()
);
result.setSavedPath(conversionResult.getXmlContent());
// 10. 将未匹配的规则详细信息添加到 problems 中
if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) {
for (JsonToXmlConversionService.UnmatchedRuleDetail detail : conversionResult.getUnmatchedRuleDetails()) {
String problemMsg = "规则匹配失败: " + detail.getRuleKey() + " [" + detail.getRuleVariants() + "]";
result.getProblems().add(problemMsg);
}
}
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;
}
}
/** /**
* 直接从 JSON 字符串生成 XML 文件。 * 直接从 JSON 字符串生成 XML 文件。
*/ */
@@ -163,27 +75,30 @@ public class IcdToXmlTaskAppService {
return result; return result;
} }
// 2.JSON 转换为 XML带未匹配规则信息 // 2.JSON字符串反序列化为MappingDocument对象
JsonToXmlConversionService.ConversionResult conversionResult = MappingDocument mappingDocument = jsonToXmlConversionService.parseMappingJson(mappingJson);
jsonToXmlConversionService.convertFromJsonWithResult(
mappingJson,
templateStream,
ruleStreams,
icdToXmlMappingService.getIndexMapping()
);
// 3.基于映射文档构建XML内容并获取匹配结果
JsonToXmlConversionService.ConversionResult conversionResult = jsonToXmlConversionService.convertFromJsonWithResult(mappingDocument, templateStream, ruleStreams, icdToXmlMappingService.getIndexMapping());
result.setMappingDocument(mappingDocument);
//result.setLdInst(mappingDocument.getLd());
result.setMappingXml(conversionResult.getXmlContent()); result.setMappingXml(conversionResult.getXmlContent());
// 3. 将未匹配的规则详细信息添加到 problems 中 // 4. 将未匹配的规则详细信息添加到 problems 中
if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) { if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) {
for (JsonToXmlConversionService.UnmatchedRuleDetail detail : conversionResult.getUnmatchedRuleDetails()) { for (JsonToXmlConversionService.UnmatchedRuleDetail detail : conversionResult.getUnmatchedRuleDetails()) {
String problemMsg = "规则匹配失败: " + detail.getRuleKey() + " [" + detail.getRuleVariants() + "]"; String problemMsg = "规则匹配失败指标: " + detail.getRuleKey() ;
result.getProblems().add(problemMsg); result.getProblems().add(problemMsg);
} }
} result.setMessage("XML 生成成功,但存在未成功匹配指标");
result.setStatus(GenerateStatus.SUCCESS); }
else{
result.setMessage("XML 生成成功"); result.setMessage("XML 生成成功");
}
result.setStatus(GenerateStatus.SUCCESS);
return result; return result;
} catch (Exception ex) { } catch (Exception ex) {
result.setStatus(GenerateStatus.FAILED); result.setStatus(GenerateStatus.FAILED);

View File

@@ -166,21 +166,21 @@ public class JsonToXmlDebugRunner {
} }
} }
if (result.getStatus() == GenerateStatus.SUCCESS) { // if (result.getStatus() == GenerateStatus.SUCCESS) {
System.out.println(); // System.out.println();
System.out.println("----- 生成的 XML 内容 -----"); // System.out.println("----- 生成的 XML 内容 -----");
String xmlContent = result.getMappingXml(); // String xmlContent = result.getMappingXml();
//
if (PRETTY_XML) { // if (PRETTY_XML) {
// 简单格式化:每个标签独占一行 // // 简单格式化:每个标签独占一行
xmlContent = formatXml(xmlContent); // xmlContent = formatXml(xmlContent);
} // }
//
System.out.println(xmlContent); // System.out.println(xmlContent);
System.out.println("--------------------------"); // System.out.println("--------------------------");
System.out.println(); // System.out.println();
System.out.println("XML 长度: " + result.getMappingXml().length() + " 字符"); // System.out.println("XML 长度: " + result.getMappingXml().length() + " 字符");
} // }
} }
/** /**