refactor(mms-mapping): 重构ICD到XML转换服务优化代码结构
- 移除IcdToXmlGenerateResult中未使用的iedName、ldInst、indexAnalysis、savedPath字段 - 注释掉相应的getter/setter方法减少代码冗余 - 重构IcdToXmlTaskAppService中generateFromIcd方法的业务逻辑 - 优化JsonToXmlConversionService的转换流程提高性能 - 添加详细的中文注释说明各个方法的功能和实现逻辑 - 调整规则匹配和XML生成的核心算法提升匹配准确性 - 修改未匹配规则的错误提示信息增加详细指标信息
This commit is contained in:
@@ -16,33 +16,10 @@ public class IcdToXmlResponseConverter {
|
||||
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.setIedName(result.getIedName());
|
||||
//response.setLdInst(result.getLdInst());
|
||||
response.getProblems().addAll(result.getProblems());
|
||||
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) {
|
||||
MappingDocumentResponse doc = new MappingDocumentResponse();
|
||||
|
||||
@@ -30,84 +30,44 @@ import java.util.List;
|
||||
*/
|
||||
@Component
|
||||
public class JsonToXmlConversionService {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final RuleBasedXmlMappingService ruleBasedXmlMappingService;
|
||||
|
||||
public JsonToXmlConversionService(RuleBasedXmlMappingService ruleBasedXmlMappingService) {
|
||||
this.ruleBasedXmlMappingService = ruleBasedXmlMappingService;
|
||||
this.objectMapper = new ObjectMapper();
|
||||
// 配置ObjectMapper忽略未知属性,避免反序列化时因字段不匹配而失败
|
||||
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,并返回包含未匹配规则信息的完整结果。
|
||||
*/
|
||||
public ConversionResult convertFromJsonWithResult(String mappingJson,
|
||||
public ConversionResult convertFromJsonWithResult(MappingDocument mappingDocument,
|
||||
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() + " 条规则未匹配到指标");
|
||||
System.out.println("[JSON转XML] 警告: 有 " + conversionResult.getUnmatchedRuleDetails().size() + " 条规则未匹配到指标");
|
||||
}
|
||||
|
||||
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,
|
||||
InputStream templateStream,
|
||||
List<InputStream> ruleStreams,
|
||||
@@ -116,42 +76,38 @@ public class JsonToXmlConversionService {
|
||||
return buildXmlFromMappingWithResult(mappingDocument, templateStream, ruleStreams, indexMapping).getXmlContent();
|
||||
}
|
||||
|
||||
private ConversionResult buildXmlFromMappingWithResult(MappingDocument mappingDocument,
|
||||
/**
|
||||
* 基于映射文档构建XML内容并返回完整结果
|
||||
* 主要流程:读取模板 -> 填充IED信息 -> 填充报告控制块 -> 应用规则匹配
|
||||
*/
|
||||
public ConversionResult buildXmlFromMappingWithResult(MappingDocument mappingDocument,
|
||||
InputStream templateStream,
|
||||
List<InputStream> ruleStreams,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
|
||||
|
||||
long stepStart = System.currentTimeMillis();
|
||||
|
||||
// 读取XML模板内容为字符串
|
||||
String templateContent = readInputStreamToString(templateStream);
|
||||
|
||||
long templateTime = System.currentTimeMillis();
|
||||
System.out.println(" [步骤1] 读取模板完成,耗时: " + (templateTime - stepStart) + " ms");
|
||||
|
||||
// 填充IED名称和LD前缀信息到模板中
|
||||
String xmlContent = fillIedInfo(templateContent, mappingDocument);
|
||||
|
||||
long iedTime = System.currentTimeMillis();
|
||||
System.out.println(" [步骤2] 填充IED信息完成,耗时: " + (iedTime - templateTime) + " ms");
|
||||
|
||||
// 填充报告控制块信息到XML中
|
||||
xmlContent = fillReportControlsFromMapping(xmlContent, mappingDocument);
|
||||
|
||||
long reportTime = System.currentTimeMillis();
|
||||
System.out.println(" [步骤3] 填充ReportControl完成,耗时: " + (reportTime - iedTime) + " ms");
|
||||
|
||||
// 应用规则匹配,将JSON中的指标数据映射到XML节点
|
||||
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();
|
||||
result.setXmlContent(matchingResult.getXmlContent());
|
||||
result.setUnmatchedRuleDetails(matchingResult.getUnmatchedRuleDetails());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入流读取为UTF-8编码的字符串
|
||||
*/
|
||||
private String readInputStreamToString(InputStream inputStream) throws Exception {
|
||||
java.io.ByteArrayOutputStream result = new java.io.ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
@@ -162,6 +118,10 @@ public class JsonToXmlConversionService {
|
||||
return result.toString(StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充IED名称和LD前缀信息到XML模板中
|
||||
* 通过正则表达式替换模板中的占位符
|
||||
*/
|
||||
private String fillIedInfo(String xmlContent, MappingDocument mappingDocument) {
|
||||
if (mappingDocument == null) {
|
||||
return xmlContent;
|
||||
@@ -170,6 +130,7 @@ public class JsonToXmlConversionService {
|
||||
String iedName = mappingDocument.getIed();
|
||||
String ldPrefix = mappingDocument.getLd();
|
||||
|
||||
// 替换IED名称
|
||||
if (iedName != null && !iedName.isEmpty()) {
|
||||
xmlContent = xmlContent.replaceAll(
|
||||
"<IED\\s+name=\"[^\"]*\"",
|
||||
@@ -177,6 +138,7 @@ public class JsonToXmlConversionService {
|
||||
);
|
||||
}
|
||||
|
||||
// 替换LD前缀
|
||||
if (ldPrefix != null && !ldPrefix.isEmpty()) {
|
||||
xmlContent = xmlContent.replaceAll(
|
||||
"<LDevice\\s+Prefix=\"[^\"]*\"",
|
||||
@@ -187,6 +149,10 @@ public class JsonToXmlConversionService {
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从映射文档中提取报告控制块信息并填充到XML中
|
||||
* 只处理特定类型的报告控制块(如brcbFluc、brcbStHarm等)
|
||||
*/
|
||||
private String fillReportControlsFromMapping(String xmlContent, MappingDocument mappingDocument) {
|
||||
if (mappingDocument == null || mappingDocument.getReportMap() == null || mappingDocument.getReportMap().isEmpty()) {
|
||||
return xmlContent;
|
||||
@@ -195,10 +161,12 @@ public class JsonToXmlConversionService {
|
||||
StringBuilder reportStatBuilder = new StringBuilder();
|
||||
reportStatBuilder.append("\t\t<ReportStat>\n");
|
||||
|
||||
// 遍历报告映射项,构建报告控制块字符串
|
||||
for (var reportItem : mappingDocument.getReportMap()) {
|
||||
if (reportItem.getName() != null && !reportItem.getName().isEmpty()) {
|
||||
String name = reportItem.getName();
|
||||
|
||||
// 只处理特定类型的报告控制块
|
||||
if (name.contains("brcbFluc") ||
|
||||
name.contains("brcbStHarm") ||
|
||||
name.contains("brcbStIHarm") ||
|
||||
@@ -219,6 +187,7 @@ public class JsonToXmlConversionService {
|
||||
|
||||
reportStatBuilder.append("\t\t</ReportStat>");
|
||||
|
||||
// 使用正则表达式替换XML中的ReportStat节点
|
||||
xmlContent = xmlContent.replaceAll(
|
||||
"<ReportStat>[\\s\\S]*?</ReportStat>",
|
||||
reportStatBuilder.toString().replace("$", "\\$").replace("[", "\\[").replace("]", "\\]")
|
||||
@@ -227,25 +196,32 @@ public class JsonToXmlConversionService {
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建报告控制块字符串
|
||||
* 格式:LLN0$BR/RP$名称,超时时间,重传次数等参数
|
||||
*/
|
||||
private String buildReportControlString(ReportMapItem rc) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// 构建报告控制块的基本路径:LLN0$缓冲类型$名称
|
||||
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")){
|
||||
sb.append(",600");
|
||||
sb.append(",600"); // 闪变相关报告超时时间为600秒
|
||||
}else{
|
||||
sb.append(",60");
|
||||
sb.append(",60"); // 其他报告超时时间为60秒
|
||||
}
|
||||
sb.append(",1");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",yes");
|
||||
sb.append(",1");
|
||||
sb.append(",1");
|
||||
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(",1");// intgPd: 完整性周期(启用)
|
||||
sb.append(",0"); // dchg: 数据变化触发(禁用)
|
||||
sb.append(",0");// qchg: 品质变化触发(禁用)
|
||||
sb.append(",0");// dupd: 数据更新触发(禁用)
|
||||
sb.append(",0");// period: 周期性触发(禁用)
|
||||
sb.append(",yes");// gi: 总召唤支持(启用)
|
||||
sb.append(",1"); // issuffixed: 使用后缀命名(启用)
|
||||
sb.append(",1");// seqNum: 包含序列号(启用)
|
||||
sb.append(",1"); // timeStamp: 包含时间戳(启用)
|
||||
sb.append(",1");
|
||||
sb.append(",1");
|
||||
sb.append(",0");
|
||||
@@ -254,6 +230,7 @@ public class JsonToXmlConversionService {
|
||||
sb.append(",1");
|
||||
sb.append(",3");
|
||||
|
||||
// 根据报告类型设置最后一个参数
|
||||
if(rc.getName().contains("PLT") || rc.getName().contains("Flicker")){
|
||||
sb.append(",1");
|
||||
}else if(rc.getName().contains("PST") || rc.getName().contains("Fluc")){
|
||||
@@ -265,6 +242,9 @@ public class JsonToXmlConversionService {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从映射文档应用规则到XML(旧版方法)
|
||||
*/
|
||||
private String applyRulesFromMapping(String xmlContent,
|
||||
MappingDocument mappingDocument,
|
||||
List<InputStream> ruleStreams,
|
||||
@@ -273,48 +253,43 @@ public class JsonToXmlConversionService {
|
||||
return applyRulesFromMappingWithResult(xmlContent, mappingDocument, ruleStreams, indexMapping).getXmlContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从映射文档应用规则到XML并返回匹配结果
|
||||
* 核心流程:合并规则 -> 提取指标 -> 匹配规则 -> 应用规则到XML
|
||||
*/
|
||||
private RuleMatchingResult applyRulesFromMappingWithResult(String xmlContent,
|
||||
MappingDocument mappingDocument,
|
||||
List<InputStream> ruleStreams,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
|
||||
|
||||
long stepStart = System.currentTimeMillis();
|
||||
|
||||
// 合并所有规则文件中的规则
|
||||
var mergedRules = mergeAllRulesDesc(ruleStreams, indexMapping);
|
||||
|
||||
long mergeTime = System.currentTimeMillis();
|
||||
System.out.println(" [规则合并] 耗时: " + (mergeTime - stepStart) + " ms,规则数: " + mergedRules.size());
|
||||
|
||||
// 从映射文档中提取所有指标信息
|
||||
var mappingMetrics = extractMetricsFromMapping(mappingDocument);
|
||||
|
||||
long extractTime = System.currentTimeMillis();
|
||||
System.out.println(" [指标提取] 耗时: " + (extractTime - mergeTime) + " ms,指标数: " + mappingMetrics.size());
|
||||
|
||||
System.out.println("========== 开始从JSON匹配规则 ==========");
|
||||
System.out.println("规则总数: " + mergedRules.size());
|
||||
System.out.println("JSON中指标总数: " + mappingMetrics.size());
|
||||
|
||||
// 执行规则匹配,获取匹配结果和未匹配规则详情
|
||||
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.getUnmatchedRuleDetails().size() + " 条规则");
|
||||
|
||||
// 将匹配成功的规则应用到XML内容中
|
||||
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");
|
||||
|
||||
matchingResult.setXmlContent(resultXml);
|
||||
return matchingResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并所有规则文件中的规则(按name+desc作为key)
|
||||
*/
|
||||
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> mergeAllRules(
|
||||
List<InputStream> ruleStreams,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
|
||||
@@ -333,6 +308,9 @@ public class JsonToXmlConversionService {
|
||||
return mergedRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并所有规则文件中的规则(仅按desc作为key)
|
||||
*/
|
||||
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> mergeAllRulesDesc(
|
||||
List<InputStream> ruleStreams,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
|
||||
@@ -351,6 +329,9 @@ public class JsonToXmlConversionService {
|
||||
return mergedRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个规则文件,按name+desc组合作为key
|
||||
*/
|
||||
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> parseRuleFile(
|
||||
InputStream ruleStream,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
|
||||
@@ -364,6 +345,7 @@ public class JsonToXmlConversionService {
|
||||
String line;
|
||||
String currentGroup = "";
|
||||
|
||||
// 定义Value标签的正则表达式模式,用于解析规则文件中的<Value>节点
|
||||
java.util.regex.Pattern valueRulePattern = java.util.regex.Pattern.compile(
|
||||
"<Value\\s+" +
|
||||
"name=\"([^\"]+)\"\\s+" +
|
||||
@@ -381,6 +363,7 @@ public class JsonToXmlConversionService {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
|
||||
// 解析注释行,确定当前规则所属的数据组(MMXU、间谐波、MHAI等)
|
||||
if (line.startsWith("<!--")) {
|
||||
if (line.contains("MMXU") || line.contains("基本数据")) {
|
||||
currentGroup = "MMXU";
|
||||
@@ -400,6 +383,7 @@ public class JsonToXmlConversionService {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过空行和非Value标签行
|
||||
if (line.isEmpty() || !line.startsWith("<Value")) {
|
||||
continue;
|
||||
}
|
||||
@@ -413,6 +397,7 @@ public class JsonToXmlConversionService {
|
||||
String doPath = matcher.group(4) != null ? matcher.group(4) : "";
|
||||
String daPath = matcher.group(5) != null ? matcher.group(5) : "";
|
||||
|
||||
// 如果配置了索引映射,则应用索引映射转换DO路径
|
||||
if (indexMapping != null && !doPath.isEmpty()) {
|
||||
doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc());
|
||||
}
|
||||
@@ -424,6 +409,7 @@ public class JsonToXmlConversionService {
|
||||
rule.setLimitDown(matcher.group(8));
|
||||
rule.setCoefficient(matcher.group(9));
|
||||
|
||||
// 构建规则key并添加到规则集合中
|
||||
String key = buildRuleKey(rule.getName(), rule.getDesc());
|
||||
if (!key.isEmpty() && hasValidDoOrDa(rule)) {
|
||||
rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule);
|
||||
@@ -435,6 +421,9 @@ public class JsonToXmlConversionService {
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个规则文件,仅按desc作为key(用于描述匹配)
|
||||
*/
|
||||
private java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> parseRuleFileDesc(
|
||||
InputStream ruleStream,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
|
||||
@@ -448,6 +437,7 @@ public class JsonToXmlConversionService {
|
||||
String line;
|
||||
String currentGroup = "";
|
||||
|
||||
// 定义Value标签的正则表达式模式,用于解析规则文件中的<Value>节点
|
||||
java.util.regex.Pattern valueRulePattern = java.util.regex.Pattern.compile(
|
||||
"<Value\\s+" +
|
||||
"name=\"([^\"]+)\"\\s+" +
|
||||
@@ -465,6 +455,7 @@ public class JsonToXmlConversionService {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
|
||||
// 解析注释行,确定当前规则所属的数据组
|
||||
if (line.startsWith("<!--")) {
|
||||
if (line.contains("MMXU") || line.contains("基本数据")) {
|
||||
currentGroup = "MMXU";
|
||||
@@ -484,6 +475,7 @@ public class JsonToXmlConversionService {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过空行和非Value标签行
|
||||
if (line.isEmpty() || !line.startsWith("<Value")) {
|
||||
continue;
|
||||
}
|
||||
@@ -497,6 +489,7 @@ public class JsonToXmlConversionService {
|
||||
String doPath = matcher.group(4) != null ? matcher.group(4) : "";
|
||||
String daPath = matcher.group(5) != null ? matcher.group(5) : "";
|
||||
|
||||
// 如果配置了索引映射,则应用索引映射转换DO路径
|
||||
if (indexMapping != null && !doPath.isEmpty()) {
|
||||
doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc());
|
||||
}
|
||||
@@ -508,6 +501,7 @@ public class JsonToXmlConversionService {
|
||||
rule.setLimitDown(matcher.group(8));
|
||||
rule.setCoefficient(matcher.group(9));
|
||||
|
||||
// 仅使用desc作为规则key
|
||||
String key = rule.getDesc();
|
||||
if (!key.isEmpty() && hasValidDoOrDa(rule)) {
|
||||
rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule);
|
||||
@@ -519,6 +513,10 @@ public class JsonToXmlConversionService {
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用索引映射,将规则中的DO路径索引转换为实际使用的索引
|
||||
* 例如:将MMXU1转换为MMXU2(根据用户选择的统计类型)
|
||||
*/
|
||||
private String applyIndexMapping(String doPath, String currentGroup,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping,
|
||||
String name, String desc) {
|
||||
@@ -526,7 +524,9 @@ public class JsonToXmlConversionService {
|
||||
return doPath;
|
||||
}
|
||||
|
||||
// 匹配带索引的逻辑节点名称,如MMXU1、MHAI2等
|
||||
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.Matcher matcherWithIndex = doIndexPattern.matcher(doPath);
|
||||
@@ -534,6 +534,7 @@ public class JsonToXmlConversionService {
|
||||
String lnClass = matcherWithIndex.group(1);
|
||||
int originalIndex = Integer.parseInt(matcherWithIndex.group(2));
|
||||
|
||||
// 根据统计类型(平均值、最大值、最小值、95值)确定新的索引
|
||||
int newIndex = determineNewIndex(currentGroup, lnClass, originalIndex, indexMapping, name, desc);
|
||||
|
||||
if (newIndex >= 0) {
|
||||
@@ -557,6 +558,10 @@ public class JsonToXmlConversionService {
|
||||
return doPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据统计类型确定新的索引值
|
||||
* 统计类型包括:平均值(avg)、最大值(max)、最小值(min)、95百分位值(g_)
|
||||
*/
|
||||
private int determineNewIndex(String currentGroup, String lnClass, int originalIndex,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping,
|
||||
String name, String desc) {
|
||||
@@ -567,13 +572,15 @@ public class JsonToXmlConversionService {
|
||||
String lowerName = (name != null ? name : "").toLowerCase();
|
||||
String lowerDesc = (desc != null ? desc : "").toLowerCase();
|
||||
|
||||
// 判断统计类型
|
||||
boolean isAverage = lowerName.contains("avg") || lowerDesc.contains("平均值");
|
||||
boolean isMaximum = lowerName.startsWith("max_") || 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("间谐波");
|
||||
|
||||
// 根据不同的数据组和统计类型,从索引映射配置中获取对应的索引值
|
||||
if ("MMXU".equals(currentGroup) || "MMXU".equals(lnClass)) {
|
||||
IcdToXmlMappingService.IndexMappingConfig.StatIndex mmxu = indexMapping.getMmxu();
|
||||
if (mmxu != null) {
|
||||
@@ -635,6 +642,9 @@ public class JsonToXmlConversionService {
|
||||
return originalIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建规则key,优先使用name,如果name为空则使用desc
|
||||
*/
|
||||
private String buildRuleKey(String name, String desc) {
|
||||
if (name != null && !name.isEmpty()) {
|
||||
return name;
|
||||
@@ -642,11 +652,19 @@ public class JsonToXmlConversionService {
|
||||
return desc != null ? desc : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查规则是否有有效的DO或DA路径
|
||||
*/
|
||||
private boolean hasValidDoOrDa(RuleBasedXmlMappingService.ValueRule rule) {
|
||||
return (rule.getDoPath() != null && !rule.getDoPath().isEmpty()) ||
|
||||
(rule.getDaPath() != null && !rule.getDaPath().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从映射文档中提取所有指标信息
|
||||
* 遍历DataSetList -> InstList -> DoiList -> SdiList -> TypeList层级结构
|
||||
* 构建以desc组合为key的指标映射表
|
||||
*/
|
||||
private java.util.Map<String, MetricInfo> extractMetricsFromMapping(MappingDocument mappingDocument) {
|
||||
java.util.Map<String, MetricInfo> metrics = new java.util.HashMap<>();
|
||||
|
||||
@@ -654,19 +672,24 @@ public class JsonToXmlConversionService {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
// 遍历数据集列表
|
||||
for (var dataSetGroup : mappingDocument.getDataSetList()) {
|
||||
if (dataSetGroup.getInstList() == null) continue;
|
||||
|
||||
// 遍历实例列表
|
||||
for (var instItem : dataSetGroup.getInstList()) {
|
||||
if (instItem.getDoiList() == null) continue;
|
||||
|
||||
// 遍历DOI列表
|
||||
for (var doiItem : instItem.getDoiList()) {
|
||||
String doPath = buildDoPath(dataSetGroup.getLnClass(), instItem.getInst(), doiItem.getName());
|
||||
|
||||
// 遍历SDI列表和Type列表,构建完整的指标信息
|
||||
if (doiItem.getSdiList() != null) {
|
||||
for (var sdiItem : doiItem.getSdiList()) {
|
||||
if (sdiItem.getTypeList() != null) {
|
||||
for (var typeItem : sdiItem.getTypeList()) {
|
||||
// 构建指标的唯一key(基于desc组合)
|
||||
String fullKey = doiItem.getDesc() + instItem.getDesc()+typeItem.getDesc();
|
||||
if(fullKey.contains("正序")){
|
||||
fullKey = doiItem.getDesc() + instItem.getDesc()+sdiItem.getDesc()+typeItem.getDesc();
|
||||
@@ -697,6 +720,9 @@ public class JsonToXmlConversionService {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建DO路径,格式:LN类名+LN实例+$MX+$DO名称
|
||||
*/
|
||||
private String buildDoPath(String lnClass, String lnInst, String doName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (lnClass != null) {
|
||||
@@ -716,22 +742,28 @@ public class JsonToXmlConversionService {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建DA路径,处理相位名称标准化和谐波索引
|
||||
*/
|
||||
private String buildDaPath(String sdiName, String typeName, DoiItem doiItem) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (sdiName != null && !sdiName.isEmpty()) {
|
||||
String normalizedSdi = normalizePhaseName(sdiName);
|
||||
sb.append(normalizedSdi);
|
||||
|
||||
// 处理谐波数据的特殊索引格式
|
||||
if (sdiName.endsWith("Har") && doiItem != null) {
|
||||
int baseflag = doiItem.getBaseflag();
|
||||
int start = doiItem.getStart();
|
||||
|
||||
if (baseflag == 1) {
|
||||
// 间谐波使用特殊格式:[%-i]
|
||||
boolean isInterHarmonic = isInterHarmonicMetric(doiItem);
|
||||
int m = isInterHarmonic ? 0 : 2;
|
||||
int i = m - start;
|
||||
sb.append("[%-").append(i).append("]");
|
||||
} else {
|
||||
// 普通谐波使用格式:[start]
|
||||
sb.append("[").append(start).append("]");
|
||||
}
|
||||
}
|
||||
@@ -745,6 +777,9 @@ public class JsonToXmlConversionService {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化相位名称,将phsa、phsb、phsc统一为phs*
|
||||
*/
|
||||
private String normalizePhaseName(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
return name;
|
||||
@@ -761,6 +796,9 @@ public class JsonToXmlConversionService {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指标是否为间谐波类型
|
||||
*/
|
||||
private boolean isInterHarmonicMetric(DoiItem doiItem) {
|
||||
if (doiItem == null || doiItem.getDesc() == null) {
|
||||
return false;
|
||||
@@ -768,6 +806,9 @@ public class JsonToXmlConversionService {
|
||||
return doiItem.getDesc().toLowerCase().contains("间谐波");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找适用的规则(旧版方法)
|
||||
*/
|
||||
private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> findApplicableRulesDesc(
|
||||
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
|
||||
java.util.Map<String, MetricInfo> mappingMetrics) {
|
||||
@@ -775,6 +816,10 @@ public class JsonToXmlConversionService {
|
||||
return findApplicableRulesDescWithUnmatched(allRules, mappingMetrics).getApplicableRules();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找适用的规则并返回未匹配规则详情
|
||||
* 核心匹配逻辑:遍历所有规则,在指标映射表中查找匹配的指标
|
||||
*/
|
||||
private RuleMatchingResult findApplicableRulesDescWithUnmatched(
|
||||
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
|
||||
java.util.Map<String, MetricInfo> mappingMetrics) {
|
||||
@@ -782,19 +827,23 @@ public class JsonToXmlConversionService {
|
||||
java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicable = new java.util.HashMap<>();
|
||||
List<UnmatchedRuleDetail> failedRuleDetails = new ArrayList<>();
|
||||
|
||||
// 遍历所有规则
|
||||
for (var entry : allRules.entrySet()) {
|
||||
String ruleKey = entry.getKey();
|
||||
List<RuleBasedXmlMappingService.ValueRule> ruleVariants = entry.getValue();
|
||||
|
||||
boolean matched = false;
|
||||
|
||||
// 尝试匹配规则的任意一个候选变体
|
||||
for (RuleBasedXmlMappingService.ValueRule rule : ruleVariants) {
|
||||
if (rule.getName() == null || rule.getDoPath().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 直接通过name在指标映射表中查找
|
||||
if(mappingMetrics.containsKey(rule.getName())){
|
||||
MetricInfo metric = mappingMetrics.get(rule.getName());
|
||||
// 将匹配到的指标的DO和DA路径赋值给规则
|
||||
rule.setDoPath(metric.getDoPath());
|
||||
rule.setDaPath(metric.getDaPath());
|
||||
applicable.put(ruleKey, rule);
|
||||
@@ -807,6 +856,7 @@ public class JsonToXmlConversionService {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有候选都未匹配,记录未匹配规则详情
|
||||
if (!matched) {
|
||||
UnmatchedRuleDetail detail = new UnmatchedRuleDetail();
|
||||
detail.setRuleKey(ruleKey);
|
||||
@@ -856,6 +906,9 @@ public class JsonToXmlConversionService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找适用的规则(旧版方法,使用路径匹配)
|
||||
*/
|
||||
private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> findApplicableRules(
|
||||
java.util.Map<String, List<RuleBasedXmlMappingService.ValueRule>> allRules,
|
||||
java.util.Map<String, MetricInfo> mappingMetrics) {
|
||||
@@ -877,6 +930,7 @@ public class JsonToXmlConversionService {
|
||||
String ruleDo = normalizePath(rule.getDoPath());
|
||||
String ruleDa = normalizePath(rule.getDaPath());
|
||||
|
||||
// 遍历所有指标,尝试通过路径模式匹配
|
||||
for (var metricEntry : mappingMetrics.entrySet()) {
|
||||
MetricInfo metric = metricEntry.getValue();
|
||||
|
||||
@@ -919,16 +973,24 @@ public class JsonToXmlConversionService {
|
||||
return applicable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化路径字符串,转为小写并去除首尾空格
|
||||
*/
|
||||
private String normalizePath(String path) {
|
||||
if (path == null) return "";
|
||||
return path.trim().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断路径是否匹配模式
|
||||
* 支持通配符匹配,忽略数组索引部分
|
||||
*/
|
||||
private boolean matchesPattern(String pattern, String actual) {
|
||||
if (pattern == null || actual == null) {
|
||||
return pattern == null && actual == null;
|
||||
}
|
||||
|
||||
// 移除数组索引部分(如[1]、[%-0]等)后再比较
|
||||
String normalizedPattern = pattern.replaceAll("\\[[^\\]]*\\]", "");
|
||||
|
||||
if (normalizedPattern.isEmpty() || actual.isEmpty()) {
|
||||
@@ -938,9 +1000,14 @@ public class JsonToXmlConversionService {
|
||||
return actual.equals(normalizedPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将匹配的规则应用到XML内容中
|
||||
* 通过正则表达式查找XML中的<Value>节点,并用匹配的规则替换
|
||||
*/
|
||||
private String applyRulesToXml(String xmlContent,
|
||||
java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicableRules) {
|
||||
|
||||
// 定义XML Value标签的正则表达式模式
|
||||
java.util.regex.Pattern xmlValuePattern = java.util.regex.Pattern.compile(
|
||||
"<Value\\s+" +
|
||||
"name=\"([^\"]+)\"\\s+" +
|
||||
@@ -958,13 +1025,16 @@ public class JsonToXmlConversionService {
|
||||
java.util.regex.Matcher matcher = xmlValuePattern.matcher(xmlContent);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
// 遍历所有匹配的Value节点
|
||||
while (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
String desc = matcher.group(2);
|
||||
String type = matcher.group(3);
|
||||
|
||||
// 根据desc查找匹配的规则
|
||||
RuleBasedXmlMappingService.ValueRule matchedRule = applicableRules.get(desc);
|
||||
if (matchedRule != null) {
|
||||
// 构建新的Value节点字符串,使用规则中的DO、DA等信息
|
||||
StringBuilder valueNode = new StringBuilder("<Value ");
|
||||
valueNode.append("name=\"").append(escapeXml(name)).append("\" ");
|
||||
valueNode.append("desc=\"").append(escapeXml(desc)).append("\" ");
|
||||
@@ -998,6 +1068,9 @@ public class JsonToXmlConversionService {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* XML特殊字符转义
|
||||
*/
|
||||
private String escapeXml(String text) {
|
||||
if (text == null) return "";
|
||||
return text.replace("&", "&")
|
||||
@@ -1007,6 +1080,9 @@ public class JsonToXmlConversionService {
|
||||
.replace("'", "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 指标信息内部类,存储从JSON中提取的指标数据
|
||||
*/
|
||||
static class MetricInfo {
|
||||
private String lnClass;
|
||||
private String lnInst;
|
||||
@@ -1060,6 +1136,9 @@ public class JsonToXmlConversionService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规则匹配结果类,包含匹配成功的规则和未匹配的规则详情
|
||||
*/
|
||||
public static class RuleMatchingResult {
|
||||
private java.util.Map<String, RuleBasedXmlMappingService.ValueRule> applicableRules;
|
||||
private List<UnmatchedRuleDetail> unmatchedRuleDetails;
|
||||
@@ -1090,6 +1169,9 @@ public class JsonToXmlConversionService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换结果类,包含生成的XML内容和未匹配的规则详情
|
||||
*/
|
||||
public static class ConversionResult {
|
||||
private String xmlContent;
|
||||
private List<UnmatchedRuleDetail> unmatchedRuleDetails;
|
||||
@@ -1111,6 +1193,9 @@ public class JsonToXmlConversionService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 未匹配规则详情类,记录匹配失败的规则信息
|
||||
*/
|
||||
public static class UnmatchedRuleDetail {
|
||||
private String ruleKey;
|
||||
private String ruleVariants;
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
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.utils.XmlTemplateParser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
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
|
||||
public class RuleBasedXmlMappingService {
|
||||
|
||||
@Autowired
|
||||
private IcdParserService icdParserService;
|
||||
|
||||
@Autowired
|
||||
private MappingModuleConfig mappingModuleConfig;
|
||||
|
||||
private static final Pattern VALUE_RULE_PATTERN = Pattern.compile(
|
||||
"<Value\\s+" +
|
||||
"name=\"([^\"]+)\"\\s+" +
|
||||
@@ -57,21 +52,28 @@ public class RuleBasedXmlMappingService {
|
||||
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)\\$");
|
||||
|
||||
public InputStream loadDefaultXmlFile( ) throws Exception {
|
||||
ClassPathResource templateResource = new ClassPathResource("template/JiangSu_Config2.xml");
|
||||
/**
|
||||
* 加载JSON转XML使用的默认XML模板文件
|
||||
* 路径通过MappingModuleConfig配置,默认为template/JiangSu_Config2.xml
|
||||
*/
|
||||
public InputStream loadDefaultXmlFile() throws Exception {
|
||||
ClassPathResource templateResource = new ClassPathResource(mappingModuleConfig.getJsonToXmlTemplatePath());
|
||||
|
||||
if (!templateResource.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return templateResource.getInputStream();
|
||||
}
|
||||
|
||||
public List<InputStream> loadDefaultRuleFile( ) throws Exception {
|
||||
/**
|
||||
* 加载JSON转XML使用的默认规则文件
|
||||
* 路径通过MappingModuleConfig配置,默认为template/默认规则.txt
|
||||
*/
|
||||
public List<InputStream> loadDefaultRuleFile() throws Exception {
|
||||
|
||||
ClassPathResource ruleResource = new ClassPathResource("template/默认规则.txt");
|
||||
if ( !ruleResource.exists()) {
|
||||
ClassPathResource ruleResource = new ClassPathResource(mappingModuleConfig.getJsonToXmlRulePath());
|
||||
if (!ruleResource.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -81,117 +83,6 @@ public class RuleBasedXmlMappingService {
|
||||
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,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping,
|
||||
String name, String desc) {
|
||||
@@ -229,85 +120,6 @@ public class RuleBasedXmlMappingService {
|
||||
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,
|
||||
IcdToXmlMappingService.IndexMappingConfig indexMapping,
|
||||
String name, String desc) {
|
||||
@@ -387,40 +199,6 @@ public class RuleBasedXmlMappingService {
|
||||
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) {
|
||||
List<String> results = new ArrayList<>();
|
||||
if (nodes == null) return results;
|
||||
@@ -490,87 +268,6 @@ public class RuleBasedXmlMappingService {
|
||||
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) {
|
||||
if (path == null) return "";
|
||||
@@ -604,49 +301,6 @@ public class RuleBasedXmlMappingService {
|
||||
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) {
|
||||
// 格式: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);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (name != null && !name.isEmpty()) {
|
||||
@@ -825,28 +434,4 @@ public class RuleBasedXmlMappingService {
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,12 @@ public class MappingModuleConfig {
|
||||
/** 默认输出目录。 */
|
||||
@Value("${icd.mapping.default-output-dir:}")
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -12,14 +12,12 @@ import java.util.List;
|
||||
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 String iedName;
|
||||
//private String ldInst;
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
/** 生成成功后的 Xml 字符串。 */
|
||||
private String mappingXml;
|
||||
private MappingDocument mappingDocument;
|
||||
public String getMappingXml() {
|
||||
return mappingXml;
|
||||
}
|
||||
@@ -34,16 +32,13 @@ public class IcdToXmlGenerateResult {
|
||||
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 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 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; }
|
||||
}
|
||||
@@ -28,6 +28,8 @@ public class IcdToXmlTaskAppService {
|
||||
private final IcdToXmlMappingService icdToXmlMappingService;
|
||||
private final JsonToXmlConversionService jsonToXmlConversionService;
|
||||
|
||||
|
||||
|
||||
public IcdToXmlTaskAppService(IcdParserService icdParserService,
|
||||
DefaultTemplateLoader defaultTemplateLoader,
|
||||
IndexAnalysisService indexAnalysisService,
|
||||
@@ -50,96 +52,6 @@ public class IcdToXmlTaskAppService {
|
||||
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 文件。
|
||||
*/
|
||||
@@ -163,27 +75,30 @@ public class IcdToXmlTaskAppService {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 2. 从 JSON 转换为 XML(带未匹配规则信息)
|
||||
JsonToXmlConversionService.ConversionResult conversionResult =
|
||||
jsonToXmlConversionService.convertFromJsonWithResult(
|
||||
mappingJson,
|
||||
templateStream,
|
||||
ruleStreams,
|
||||
icdToXmlMappingService.getIndexMapping()
|
||||
);
|
||||
// 2.将JSON字符串反序列化为MappingDocument对象
|
||||
MappingDocument mappingDocument = jsonToXmlConversionService.parseMappingJson(mappingJson);
|
||||
|
||||
// 3.基于映射文档构建XML内容并获取匹配结果
|
||||
JsonToXmlConversionService.ConversionResult conversionResult = jsonToXmlConversionService.convertFromJsonWithResult(mappingDocument, templateStream, ruleStreams, icdToXmlMappingService.getIndexMapping());
|
||||
|
||||
|
||||
result.setMappingDocument(mappingDocument);
|
||||
//result.setLdInst(mappingDocument.getLd());
|
||||
result.setMappingXml(conversionResult.getXmlContent());
|
||||
|
||||
// 3. 将未匹配的规则详细信息添加到 problems 中
|
||||
// 4. 将未匹配的规则详细信息添加到 problems 中
|
||||
if (conversionResult.getUnmatchedRuleDetails() != null && !conversionResult.getUnmatchedRuleDetails().isEmpty()) {
|
||||
for (JsonToXmlConversionService.UnmatchedRuleDetail detail : conversionResult.getUnmatchedRuleDetails()) {
|
||||
String problemMsg = "规则匹配失败: " + detail.getRuleKey() + " [" + detail.getRuleVariants() + "]";
|
||||
String problemMsg = "规则匹配失败指标: " + detail.getRuleKey() ;
|
||||
result.getProblems().add(problemMsg);
|
||||
}
|
||||
}
|
||||
result.setMessage("XML 生成成功,但存在未成功匹配指标");
|
||||
|
||||
result.setStatus(GenerateStatus.SUCCESS);
|
||||
}
|
||||
else{
|
||||
result.setMessage("XML 生成成功");
|
||||
}
|
||||
result.setStatus(GenerateStatus.SUCCESS);
|
||||
return result;
|
||||
} catch (Exception ex) {
|
||||
result.setStatus(GenerateStatus.FAILED);
|
||||
|
||||
@@ -166,21 +166,21 @@ public class JsonToXmlDebugRunner {
|
||||
}
|
||||
}
|
||||
|
||||
if (result.getStatus() == GenerateStatus.SUCCESS) {
|
||||
System.out.println();
|
||||
System.out.println("----- 生成的 XML 内容 -----");
|
||||
String xmlContent = result.getMappingXml();
|
||||
|
||||
if (PRETTY_XML) {
|
||||
// 简单格式化:每个标签独占一行
|
||||
xmlContent = formatXml(xmlContent);
|
||||
}
|
||||
|
||||
System.out.println(xmlContent);
|
||||
System.out.println("--------------------------");
|
||||
System.out.println();
|
||||
System.out.println("XML 长度: " + result.getMappingXml().length() + " 字符");
|
||||
}
|
||||
// if (result.getStatus() == GenerateStatus.SUCCESS) {
|
||||
// System.out.println();
|
||||
// System.out.println("----- 生成的 XML 内容 -----");
|
||||
// String xmlContent = result.getMappingXml();
|
||||
//
|
||||
// if (PRETTY_XML) {
|
||||
// // 简单格式化:每个标签独占一行
|
||||
// xmlContent = formatXml(xmlContent);
|
||||
// }
|
||||
//
|
||||
// System.out.println(xmlContent);
|
||||
// System.out.println("--------------------------");
|
||||
// System.out.println();
|
||||
// System.out.println("XML 长度: " + result.getMappingXml().length() + " 字符");
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user