最近新增的工作
This commit is contained in:
@@ -1,135 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.application;
|
||||
|
||||
import com.njcn.gather.icd.mapping.application.command.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.application.result.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.ValidationResult;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
|
||||
import com.njcn.gather.icd.mapping.domain.service.DefaultTemplateLoader;
|
||||
import com.njcn.gather.icd.mapping.domain.service.IcdParserService;
|
||||
import com.njcn.gather.icd.mapping.domain.service.IndexAnalysisService;
|
||||
import com.njcn.gather.icd.mapping.domain.service.IndexValidationService;
|
||||
import com.njcn.gather.icd.mapping.domain.service.MappingGenerationService;
|
||||
import com.njcn.gather.icd.mapping.enums.GenerateStatus;
|
||||
import com.njcn.gather.icd.mapping.infrastructure.serializer.MappingDocumentSerializer;
|
||||
import com.njcn.gather.icd.mapping.infrastructure.storage.FileStorageService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 生成任务应用服务。
|
||||
*
|
||||
* 完整流程:
|
||||
* 1. 解析 ICD;
|
||||
* 2. 读取 DefaultCfg.txt;
|
||||
* 3. 按业务分组生成候选项;
|
||||
* 4. 如果用户未提交绑定关系,返回 NEED_INDEX_SELECTION;
|
||||
* 5. 如果提交了绑定关系,先做合法性校验;
|
||||
* 6. 校验通过后生成正式 MappingDocument;
|
||||
* 7. 序列化并按需落盘。
|
||||
*/
|
||||
@Service
|
||||
public class MappingTaskAppService {
|
||||
|
||||
private final IcdParserService icdParserService;
|
||||
private final DefaultTemplateLoader defaultTemplateLoader;
|
||||
private final IndexAnalysisService indexAnalysisService;
|
||||
private final IndexValidationService indexValidationService;
|
||||
private final MappingGenerationService mappingGenerationService;
|
||||
private final MappingDocumentSerializer mappingDocumentSerializer;
|
||||
private final FileStorageService fileStorageService;
|
||||
|
||||
public MappingTaskAppService(IcdParserService icdParserService,
|
||||
DefaultTemplateLoader defaultTemplateLoader,
|
||||
IndexAnalysisService indexAnalysisService,
|
||||
IndexValidationService indexValidationService,
|
||||
MappingGenerationService mappingGenerationService,
|
||||
MappingDocumentSerializer mappingDocumentSerializer,
|
||||
FileStorageService fileStorageService) {
|
||||
this.icdParserService = icdParserService;
|
||||
this.defaultTemplateLoader = defaultTemplateLoader;
|
||||
this.indexAnalysisService = indexAnalysisService;
|
||||
this.indexValidationService = indexValidationService;
|
||||
this.mappingGenerationService = mappingGenerationService;
|
||||
this.mappingDocumentSerializer = mappingDocumentSerializer;
|
||||
this.fileStorageService = fileStorageService;
|
||||
}
|
||||
|
||||
public GenerateMappingResult generateFromIcd(GenerateFromIcdCommand command) {
|
||||
GenerateMappingResult result = new GenerateMappingResult();
|
||||
try {
|
||||
// 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());
|
||||
|
||||
// 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. 生成正式映射结构
|
||||
MappingDocument mappingDocument = mappingGenerationService.generate(
|
||||
icdDocument,
|
||||
template,
|
||||
indexAnalysis,
|
||||
command.getIndexSelection(),
|
||||
command.getVersion(),
|
||||
command.getAuthor()
|
||||
);
|
||||
result.setMappingDocument(mappingDocument);
|
||||
|
||||
// 7. 序列化输出
|
||||
String mappingJson = command.isPrettyJson()
|
||||
? mappingDocumentSerializer.toPrettyJson(mappingDocument)
|
||||
: mappingDocumentSerializer.toCompactJson(mappingDocument);
|
||||
result.setMappingJson(mappingJson);
|
||||
|
||||
if (command.isSaveToDisk()) {
|
||||
String fileName = buildOutputFileName(icdDocument, command.isPrettyJson());
|
||||
String savedPath = fileStorageService.save(fileName, mappingJson, command.getOutputDir());
|
||||
result.setSavedPath(savedPath);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private String buildOutputFileName(IcdDocument icdDocument, boolean prettyJson) {
|
||||
String baseName = icdDocument.getIedName() == null ? "mapping" : icdDocument.getIedName();
|
||||
// 落盘文件名只保留安全字符,避免 IED 名称携带路径分隔符导致越界写入。
|
||||
String safeBaseName = baseName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim();
|
||||
if (safeBaseName.isEmpty()) {
|
||||
safeBaseName = "mapping";
|
||||
}
|
||||
return safeBaseName + (prettyJson ? "-mapping-pretty.json" : "-mapping.json");
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.application.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生成命令对象。
|
||||
*
|
||||
* 说明:
|
||||
* controller 层不要把 MultipartFile 和 request 直接传进领域服务;
|
||||
* 统一转成 command,便于应用层做流程编排。
|
||||
*/
|
||||
public class GenerateFromIcdCommand {
|
||||
|
||||
/** 原始文件名。 */
|
||||
private String fileName;
|
||||
|
||||
/** ICD 文件字节数组。 */
|
||||
private byte[] fileBytes;
|
||||
|
||||
/** 输出版本号。 */
|
||||
private String version;
|
||||
|
||||
/** 作者。 */
|
||||
private String author;
|
||||
|
||||
/** 是否保存到磁盘。 */
|
||||
private boolean saveToDisk;
|
||||
|
||||
/** 是否输出美化 JSON。 */
|
||||
private boolean prettyJson;
|
||||
|
||||
/** 输出目录。 */
|
||||
private String outputDir;
|
||||
|
||||
/** 用户上送的索引选择结果。 */
|
||||
private List<IndexSelectionGroupCommand> indexSelection = new ArrayList<IndexSelectionGroupCommand>();
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public byte[] getFileBytes() {
|
||||
return fileBytes;
|
||||
}
|
||||
|
||||
public void setFileBytes(byte[] fileBytes) {
|
||||
this.fileBytes = fileBytes;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public boolean isSaveToDisk() {
|
||||
return saveToDisk;
|
||||
}
|
||||
|
||||
public void setSaveToDisk(boolean saveToDisk) {
|
||||
this.saveToDisk = saveToDisk;
|
||||
}
|
||||
|
||||
public boolean isPrettyJson() {
|
||||
return prettyJson;
|
||||
}
|
||||
|
||||
public void setPrettyJson(boolean prettyJson) {
|
||||
this.prettyJson = prettyJson;
|
||||
}
|
||||
|
||||
public String getOutputDir() {
|
||||
return outputDir;
|
||||
}
|
||||
|
||||
public void setOutputDir(String outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
public List<IndexSelectionGroupCommand> getIndexSelection() {
|
||||
return indexSelection;
|
||||
}
|
||||
|
||||
public void setIndexSelection(List<IndexSelectionGroupCommand> indexSelection) {
|
||||
this.indexSelection = indexSelection;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.application.command;
|
||||
|
||||
/**
|
||||
* 应用层单条绑定命令。
|
||||
*/
|
||||
public class IndexBindingCommand {
|
||||
|
||||
/** 报告名。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 标签。 */
|
||||
private String label;
|
||||
|
||||
/** 绑定到的 lnInst 数字。 */
|
||||
private String lnInst;
|
||||
|
||||
public String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
public void setReportName(String reportName) {
|
||||
this.reportName = reportName;
|
||||
}
|
||||
|
||||
public String getDataSetName() {
|
||||
return dataSetName;
|
||||
}
|
||||
|
||||
public void setDataSetName(String dataSetName) {
|
||||
this.dataSetName = dataSetName;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLnInst() {
|
||||
return lnInst;
|
||||
}
|
||||
|
||||
public void setLnInst(String lnInst) {
|
||||
this.lnInst = lnInst;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.application.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 应用层分组选择命令。
|
||||
*/
|
||||
public class IndexSelectionGroupCommand {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组中文描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前分组下的多条绑定关系。 */
|
||||
private List<IndexBindingCommand> bindings = new ArrayList<IndexBindingCommand>();
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public List<IndexBindingCommand> getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
public void setBindings(List<IndexBindingCommand> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.application.result;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.enums.GenerateStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 应用层返回对象。统一封装成功、需要选择索引、失败三类结果。
|
||||
*/
|
||||
public class GenerateMappingResult {
|
||||
private GenerateStatus status;
|
||||
private String message;
|
||||
private String iedName;
|
||||
private String ldInst;
|
||||
private IndexAnalysis indexAnalysis;
|
||||
private MappingDocument mappingDocument;
|
||||
private String mappingJson;
|
||||
private String savedPath;
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
|
||||
public GenerateStatus getStatus() { return status; }
|
||||
public void setStatus(GenerateStatus status) { this.status = status; }
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
public String getIedName() { return iedName; }
|
||||
public void setIedName(String iedName) { this.iedName = iedName; }
|
||||
public String getLdInst() { return ldInst; }
|
||||
public void setLdInst(String ldInst) { this.ldInst = ldInst; }
|
||||
public IndexAnalysis getIndexAnalysis() { return indexAnalysis; }
|
||||
public void setIndexAnalysis(IndexAnalysis indexAnalysis) { this.indexAnalysis = indexAnalysis; }
|
||||
public MappingDocument getMappingDocument() { return mappingDocument; }
|
||||
public void setMappingDocument(MappingDocument mappingDocument) { this.mappingDocument = mappingDocument; }
|
||||
public String getMappingJson() { return mappingJson; }
|
||||
public void setMappingJson(String mappingJson) { this.mappingJson = mappingJson; }
|
||||
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; }
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.njcn.gather.icd.mapping.domain.service;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
|
||||
import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
|
||||
import com.njcn.gather.icd.mapping.utils.JsonUtils;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -23,7 +23,10 @@ import java.util.List;
|
||||
@Service
|
||||
public class DefaultTemplateLoader {
|
||||
|
||||
/** 模块配置,提供默认模板路径等运行参数。 */
|
||||
private final MappingModuleConfig moduleConfig;
|
||||
|
||||
/** 模板反序列化使用的 Jackson 实例。 */
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public DefaultTemplateLoader(MappingModuleConfig moduleConfig) {
|
||||
@@ -32,6 +35,11 @@ public class DefaultTemplateLoader {
|
||||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载并校验默认映射模板。
|
||||
*
|
||||
* 返回值会直接参与索引候选分析和最终 MappingDocument 生成。
|
||||
*/
|
||||
public DefaultTemplate load() {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(moduleConfig.getDefaultTemplatePath());
|
||||
@@ -55,6 +63,9 @@ public class DefaultTemplateLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 classpath 资源的完整字节内容。
|
||||
*/
|
||||
private byte[] readAllBytes(ClassPathResource resource) throws Exception {
|
||||
try (InputStream inputStream = resource.getInputStream();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.njcn.gather.icd.mapping.infrastructure.storage;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -11,8 +11,14 @@ import java.nio.charset.StandardCharsets;
|
||||
*/
|
||||
@Component
|
||||
public class FileStorageService {
|
||||
/**
|
||||
* 将映射 JSON 写入目标目录。
|
||||
*
|
||||
* outputDir 为空时使用当前工作目录,返回最终文件绝对路径。
|
||||
*/
|
||||
public String save(String fileName, String content, String outputDir) {
|
||||
try {
|
||||
// 输出目录允许由请求覆盖,未传时落到当前工作目录。
|
||||
File dir = outputDir == null || outputDir.trim().isEmpty() ? new File(".") : new File(outputDir);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IllegalStateException("输出目录创建失败:" + dir.getAbsolutePath());
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.njcn.gather.icd.mapping.domain.service;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.infrastructure.parser.SclParserAdapter;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -11,12 +11,16 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class IcdParserService {
|
||||
|
||||
/** SCL 底层解析适配器,封装 JAXB generated 模型处理。 */
|
||||
private final SclParserAdapter parserAdapter;
|
||||
|
||||
public IcdParserService(SclParserAdapter parserAdapter) {
|
||||
this.parserAdapter = parserAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 ICD 文件内容为模块内部统一领域模型。
|
||||
*/
|
||||
public IcdDocument parse(byte[] bytes, String fileName) {
|
||||
return parserAdapter.parse(bytes, fileName);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.njcn.gather.icd.mapping.domain.service;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.DataSetNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.FcdaNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.ReportControlNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.DataSetNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.FcdaNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.ReportControlNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -21,17 +21,15 @@ import java.util.Set;
|
||||
/**
|
||||
* 索引候选分析服务。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 新版不再按“单个报告”平铺返回,而是按 DefaultCfg.ReportList 的业务配置项聚合。
|
||||
* 2. 一个业务配置项下可能包含多个报告,因此这里会计算 reportCount,并返回 reports 子列表。
|
||||
* 3. templateLabels 只是模板参考,不要求与 ICD 解析到的 lnInst 数量完全一一对应。
|
||||
* 4. 关键修正:
|
||||
* 在这里就把 DefaultCfg.ReportList 的 inst / Select / TrgOps 一并带入 IndexCandidate,
|
||||
* 后续正式生成阶段直接使用,不再重新查模板。
|
||||
* 按 `DefaultCfg.ReportList` 的业务分组聚合 ICD 中的报告信息,
|
||||
* 同时把后续正式生成阶段需要的模板字段一并带入候选结果。
|
||||
*/
|
||||
@Service
|
||||
public class IndexAnalysisService {
|
||||
|
||||
/**
|
||||
* 根据 ICD 报告控制块和 DefaultCfg.ReportList 生成索引候选分组。
|
||||
*/
|
||||
public IndexAnalysis analyze(IcdDocument icdDocument, DefaultTemplate template) {
|
||||
IndexAnalysis analysis = new IndexAnalysis();
|
||||
if (icdDocument == null) {
|
||||
@@ -43,7 +41,7 @@ public class IndexAnalysisService {
|
||||
return analysis;
|
||||
}
|
||||
|
||||
// 先按模板分组聚合
|
||||
// 先按模板分组聚合候选项,而不是按单个报告平铺返回。
|
||||
for (DefaultTemplate.ReportCfgItem reportCfg : template.getReportList()) {
|
||||
List<ReportControlNode> matchedReports = collectMatchedReports(icdDocument, reportCfg);
|
||||
if (matchedReports.isEmpty()) {
|
||||
@@ -55,7 +53,7 @@ public class IndexAnalysisService {
|
||||
candidate.setGroupDesc(reportCfg.getDesc());
|
||||
candidate.setReportCount(matchedReports.size());
|
||||
|
||||
// 关键:把 DefaultCfg.ReportList 的配置项直接带入候选对象
|
||||
// 把模板中的关键配置直接带入候选对象,避免正式生成阶段再次回查模板。
|
||||
candidate.setReportInst(reportCfg.getInst());
|
||||
candidate.setSelect(reportCfg.getSelect());
|
||||
candidate.setTrgOps(reportCfg.getTrgOps());
|
||||
@@ -76,7 +74,7 @@ public class IndexAnalysisService {
|
||||
analysis.getCandidates().add(candidate);
|
||||
}
|
||||
|
||||
// 再检查是否有 ICD 报告没有被模板覆盖
|
||||
// 检查 ICD 中是否存在未被模板覆盖的报告配置。
|
||||
if (icdDocument.getReportControls() != null) {
|
||||
for (ReportControlNode reportControl : icdDocument.getReportControls()) {
|
||||
if (!isCoveredByTemplate(reportControl, template)) {
|
||||
@@ -91,6 +89,9 @@ public class IndexAnalysisService {
|
||||
return analysis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集命中当前模板报告分组 DataSetList 的报告控制块。
|
||||
*/
|
||||
private List<ReportControlNode> collectMatchedReports(IcdDocument icdDocument, DefaultTemplate.ReportCfgItem reportCfg) {
|
||||
List<ReportControlNode> result = new ArrayList<ReportControlNode>();
|
||||
if (icdDocument.getReportControls() == null || reportCfg.getDataSetList() == null) {
|
||||
@@ -104,6 +105,9 @@ public class IndexAnalysisService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 ICD 中的报告是否已经被模板 ReportList 覆盖。
|
||||
*/
|
||||
private boolean isCoveredByTemplate(ReportControlNode reportControl, DefaultTemplate template) {
|
||||
if (template == null || template.getReportList() == null) {
|
||||
return false;
|
||||
@@ -116,6 +120,9 @@ public class IndexAnalysisService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定 DataSet 的 FCDA 中提取可选 lnInst 数字,并按数字优先排序。
|
||||
*/
|
||||
private List<String> collectLnInstValues(IcdDocument icdDocument, String dataSetName) {
|
||||
if (icdDocument.getDataSets() == null) {
|
||||
return Collections.emptyList();
|
||||
@@ -151,6 +158,9 @@ public class IndexAnalysisService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造前端回传使用的稳定分组 key。
|
||||
*/
|
||||
private String buildGroupKey(DefaultTemplate.ReportCfgItem reportCfg) {
|
||||
String desc = reportCfg.getDesc() == null ? "GROUP" : reportCfg.getDesc();
|
||||
String firstDataSet = (reportCfg.getDataSetList() == null || reportCfg.getDataSetList().isEmpty())
|
||||
@@ -158,6 +168,9 @@ public class IndexAnalysisService {
|
||||
return normalize(desc) + "__" + normalize(firstDataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将中文描述和 DataSet 名转换为可比较的 key 片段。
|
||||
*/
|
||||
private String normalize(String value) {
|
||||
return value.replaceAll("[^0-9A-Za-z\\u4e00-\\u9fa5]+", "_")
|
||||
.replaceAll("_+", "_")
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.njcn.gather.icd.mapping.domain.service;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.njcn.gather.icd.mapping.application.command.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.application.command.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.ValidationResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.ValidationResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
@@ -15,13 +15,16 @@ import java.util.List;
|
||||
*
|
||||
* 校验原则:
|
||||
* 1. 只校验用户明确提交的绑定项;
|
||||
* 2. 不要求模板里的所有标签都必须配置;
|
||||
* 2. 不强制模板中的所有标签都必须配置;
|
||||
* 3. label 必须属于当前业务分组的 templateLabels;
|
||||
* 4. lnInst 必须属于当前报告对应的 ICD 候选数字。
|
||||
*/
|
||||
@Service
|
||||
public class IndexValidationService {
|
||||
|
||||
/**
|
||||
* 校验前端提交的分组绑定是否能落到当前 ICD 候选范围内。
|
||||
*/
|
||||
public ValidationResult validate(IndexAnalysis analysis, List<IndexSelectionGroupCommand> selections) {
|
||||
ValidationResult result = new ValidationResult();
|
||||
|
||||
@@ -33,7 +36,7 @@ public class IndexValidationService {
|
||||
if (selections == null || selections.isEmpty()) {
|
||||
for (IndexCandidate candidate : analysis.getCandidates()) {
|
||||
result.getProblems().add(
|
||||
"报告组【" + candidate.getGroupDesc() + "】未提交绑定关系,请根据 templateLabels 与 reports[*].availableLnInstValues 完成配置"
|
||||
"分组【" + candidate.getGroupDesc() + "】未提交绑定关系,请根据 templateLabels 与 reports[*].availableLnInstValues 完成配置"
|
||||
);
|
||||
}
|
||||
return result;
|
||||
@@ -62,6 +65,9 @@ public class IndexValidationService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验单条 label + report + lnInst 绑定。
|
||||
*/
|
||||
private void validateBinding(IndexCandidate candidate, IndexBindingCommand binding, ValidationResult result) {
|
||||
if (binding == null) {
|
||||
result.getProblems().add("存在空的绑定项");
|
||||
@@ -93,6 +99,9 @@ public class IndexValidationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 优先按 groupKey 匹配候选分组,兼容只传 groupDesc 的旧调用。
|
||||
*/
|
||||
private IndexCandidate findCandidate(IndexAnalysis analysis, String groupKey, String groupDesc) {
|
||||
for (IndexCandidate candidate : analysis.getCandidates()) {
|
||||
if (same(candidate.getGroupKey(), groupKey)) {
|
||||
@@ -105,6 +114,9 @@ public class IndexValidationService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在候选分组内定位具体报告,dataSetName 为空时只按报告名匹配。
|
||||
*/
|
||||
private IndexCandidateReportItem findReport(IndexCandidate candidate, String reportName, String dataSetName) {
|
||||
if (candidate.getReports() == null) {
|
||||
return null;
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.njcn.gather.icd.mapping.infrastructure.serializer;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,10 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class MappingDocumentSerializer {
|
||||
|
||||
/** 紧凑 JSON 输出器,用于减少响应体体积。 */
|
||||
private final ObjectMapper compactMapper;
|
||||
|
||||
/** 格式化 JSON 输出器,用于人工查看和落盘审阅。 */
|
||||
private final ObjectMapper prettyMapper;
|
||||
|
||||
public MappingDocumentSerializer() {
|
||||
@@ -24,6 +27,9 @@ public class MappingDocumentSerializer {
|
||||
prettyMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化为紧凑 JSON。
|
||||
*/
|
||||
public String toCompactJson(MappingDocument document) {
|
||||
try {
|
||||
return compactMapper.writeValueAsString(document);
|
||||
@@ -32,6 +38,9 @@ public class MappingDocumentSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化为格式化 JSON。
|
||||
*/
|
||||
public String toPrettyJson(MappingDocument document) {
|
||||
try {
|
||||
return prettyMapper.writeValueAsString(document);
|
||||
@@ -1,29 +1,29 @@
|
||||
package com.njcn.gather.icd.mapping.domain.service;
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.njcn.gather.icd.mapping.application.command.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.application.command.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.DataSetNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.DoiElementNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.DoiNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.FcdaNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.LnNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.ReportControlNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.intermediate.DataSetSelectionState;
|
||||
import com.njcn.gather.icd.mapping.domain.model.intermediate.ReportAndDataSetState;
|
||||
import com.njcn.gather.icd.mapping.domain.model.intermediate.ReportBindingState;
|
||||
import com.njcn.gather.icd.mapping.domain.model.intermediate.ReportGroupState;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.DataSetGroupItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.DoiItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.InstItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.ReportMapItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.SdiItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.mapping.TypeItem;
|
||||
import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.DataSetNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.DoiElementNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.DoiNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.FcdaNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.LnNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.ReportControlNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.state.DataSetSelectionState;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.state.ReportAndDataSetState;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.state.ReportBindingState;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.state.ReportGroupState;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.DataSetGroupItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.DoiItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.InstItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.ReportMapItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.SdiItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.TypeItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -46,6 +46,9 @@ import java.time.format.DateTimeFormatter;
|
||||
@Service
|
||||
public class MappingGenerationService {
|
||||
|
||||
/**
|
||||
* 根据 ICD、默认模板和用户索引绑定生成最终映射文档。
|
||||
*/
|
||||
public MappingDocument generate(IcdDocument icdDocument,
|
||||
DefaultTemplate template,
|
||||
IndexAnalysis analysis,
|
||||
@@ -103,6 +106,7 @@ public class MappingGenerationService {
|
||||
}
|
||||
|
||||
// 2. 生成 DataSetList
|
||||
// DataSetList 按 desc + lnClass 聚合,保持输出顺序稳定。
|
||||
Map<String, DataSetGroupItem> dataSetGroupMap = new LinkedHashMap<String, DataSetGroupItem>();
|
||||
for (DataSetSelectionState selectionState : state.getDataSetSelections()) {
|
||||
if (selectionState.getLnNodes() == null || selectionState.getLnNodes().isEmpty()) {
|
||||
@@ -140,6 +144,11 @@ public class MappingGenerationService {
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建正式生成前的中间态。
|
||||
*
|
||||
* 中间态把报告分组、用户绑定和命中的 LN 节点拆开保存,避免生成 JSON 时重复查找。
|
||||
*/
|
||||
private ReportAndDataSetState buildState(IcdDocument icdDocument,
|
||||
DefaultTemplate template,
|
||||
IndexAnalysis analysis,
|
||||
@@ -263,6 +272,9 @@ public class MappingGenerationService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 lnClass 分组下查找或创建指定 lnInst 的输出节点。
|
||||
*/
|
||||
private InstItem findOrCreateInst(DataSetGroupItem groupItem, String inst, String desc) {
|
||||
for (InstItem item : groupItem.getInstList()) {
|
||||
if (equalsTrim(item.getInst(), inst)) {
|
||||
@@ -372,6 +384,9 @@ public class MappingGenerationService {
|
||||
instItem.getDoiList().add(doiItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 queueList 命中的对象是否应按原 C# 规则跳过。
|
||||
*/
|
||||
private boolean shouldSkipQueueItem(boolean queueMode, int icdCount, DefaultTemplate.ObjectCfgItem objectCfg) {
|
||||
if (!queueMode || objectCfg == null) {
|
||||
return false;
|
||||
@@ -418,6 +433,9 @@ public class MappingGenerationService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户绑定标签查找对应的 DataObjectsList 配置项。
|
||||
*/
|
||||
private List<DefaultTemplate.DataObjectCfgItem> findDataObjectCfgItems(DefaultTemplate template, String label) {
|
||||
List<DefaultTemplate.DataObjectCfgItem> result = new ArrayList<DefaultTemplate.DataObjectCfgItem>();
|
||||
if (template == null || template.getDataObjectsList() == null) {
|
||||
@@ -639,6 +657,11 @@ public class MappingGenerationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 DOI 的倍率系数。
|
||||
*
|
||||
* 找不到模板倍率配置时默认返回 1.0。
|
||||
*/
|
||||
private float resolveCoefficient(DoiNode doiNode, DefaultTemplate template) {
|
||||
Set<String> values = collectAllLeafValues(doiNode.getChildren());
|
||||
for (String value : values) {
|
||||
@@ -837,6 +860,9 @@ public class MappingGenerationService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从模板 LnClassList 中查找当前 LN 的业务分组配置。
|
||||
*/
|
||||
private DefaultTemplate.LnClassCfgItem findLnClassCfg(DefaultTemplate template, String lnClass) {
|
||||
if (template == null || template.getLnClassList() == null) {
|
||||
return null;
|
||||
@@ -850,6 +876,9 @@ public class MappingGenerationService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在候选分析结果中定位当前前端回传的业务分组。
|
||||
*/
|
||||
private IndexCandidate findCandidate(IndexAnalysis analysis, String groupKey, String groupDesc) {
|
||||
if (analysis == null || analysis.getCandidates() == null) {
|
||||
return null;
|
||||
@@ -862,6 +891,9 @@ public class MappingGenerationService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归收集 DOI 子树下所有 Val 文本,用于匹配倍率等模板配置。
|
||||
*/
|
||||
private Set<String> collectAllLeafValues(List<DoiElementNode> nodes) {
|
||||
Set<String> result = new LinkedHashSet<String>();
|
||||
if (nodes == null) {
|
||||
@@ -880,6 +912,9 @@ public class MappingGenerationService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从模板 UnitList 中查找指定 DAI 值对应的单位配置。
|
||||
*/
|
||||
private DefaultTemplate.UnitCfgItem findUnitCfg(DefaultTemplate template, String value) {
|
||||
if (template == null || template.getUnitList() == null) {
|
||||
return null;
|
||||
@@ -892,6 +927,9 @@ public class MappingGenerationService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从模板 MultiplierList 中查找指定 DAI 值对应的倍率配置。
|
||||
*/
|
||||
private DefaultTemplate.MultiplierCfgItem findMultiplierCfg(DefaultTemplate template, String value) {
|
||||
if (template == null || template.getMultiplierList() == null) {
|
||||
return null;
|
||||
@@ -904,6 +942,9 @@ public class MappingGenerationService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将相别节点名称转换为模板中的中文描述。
|
||||
*/
|
||||
private String resolvePhaseDesc(DefaultTemplate template, String value) {
|
||||
if (template == null || template.getPhaseList() == null) {
|
||||
return value;
|
||||
@@ -916,6 +957,9 @@ public class MappingGenerationService {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将类型节点名称转换为模板中的中文描述。
|
||||
*/
|
||||
private String resolveTypeDesc(DefaultTemplate template, String value) {
|
||||
if (template == null || template.getTypeList() == null) {
|
||||
return value;
|
||||
@@ -929,6 +973,9 @@ public class MappingGenerationService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 空安全、去首尾空白的字符串比较。
|
||||
*/
|
||||
private boolean equalsTrim(String left, String right) {
|
||||
if (left == null && right == null) {
|
||||
return true;
|
||||
@@ -1002,6 +1049,9 @@ public class MappingGenerationService {
|
||||
return "96";
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析输出版本号;请求未传时使用当天日期。
|
||||
*/
|
||||
private String resolveVersion(String version) {
|
||||
if (version != null && !version.trim().isEmpty()) {
|
||||
return version.trim();
|
||||
@@ -1009,6 +1059,9 @@ public class MappingGenerationService {
|
||||
return LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按原 C# 规则转换 reportCount:单报告写 0,多报告写实际数量。
|
||||
*/
|
||||
private int resolveReportCount(int size) {
|
||||
return size <= 1 ? 0 : size;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
|
||||
import com.njcn.gather.icd.mapping.pojo.param.IndexBindingRequest;
|
||||
import com.njcn.gather.icd.mapping.pojo.param.IndexSelectionGroupRequest;
|
||||
import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 请求转换器。
|
||||
*
|
||||
* 作用:
|
||||
* 1. 将接口层 request 转成应用层 command;
|
||||
* 2. 统一处理 null 和空集合,避免后续业务层到处判空。
|
||||
*/
|
||||
@Component
|
||||
public class MappingRequestConverter {
|
||||
|
||||
/** 模块默认配置,用于补齐作者、输出目录等缺省值。 */
|
||||
private final MappingModuleConfig moduleConfig;
|
||||
|
||||
public MappingRequestConverter(MappingModuleConfig moduleConfig) {
|
||||
this.moduleConfig = moduleConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将候选接口上传文件转换为应用层命令,其他参数走模块默认值。
|
||||
*/
|
||||
public GenerateFromIcdCommand toCommand(MultipartFile icdFile) {
|
||||
return toCommand(icdFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将上传文件和请求体转换为应用层命令。
|
||||
*/
|
||||
public GenerateFromIcdCommand toCommand(MultipartFile icdFile, GenerateMappingFromIcdRequest request) {
|
||||
try {
|
||||
if (icdFile == null || icdFile.isEmpty()) {
|
||||
throw new IllegalArgumentException("ICD 文件不能为空");
|
||||
}
|
||||
GenerateFromIcdCommand command = new GenerateFromIcdCommand();
|
||||
command.setFileName(icdFile.getOriginalFilename());
|
||||
command.setFileBytes(icdFile.getBytes());
|
||||
command.setVersion(request == null ? null : request.getVersion());
|
||||
command.setAuthor(resolveText(request == null ? null : request.getAuthor(), moduleConfig.getDefaultAuthor()));
|
||||
command.setSaveToDisk(request != null && request.isSaveToDisk());
|
||||
command.setPrettyJson(request != null && request.isPrettyJson());
|
||||
command.setOutputDir(resolveText(request == null ? null : request.getOutputDir(), moduleConfig.getDefaultOutputDir()));
|
||||
fillIndexSelection(command, request == null ? null : request.getIndexSelection());
|
||||
return command;
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("请求转换失败:" + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将提交绑定接口请求体转换为应用层命令。
|
||||
*/
|
||||
public GenerateFromIcdCommand toCommand(SubmitIndexSelectionRequest request) {
|
||||
try {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("请求体不能为空");
|
||||
}
|
||||
if (request.getIcdDocument() == null) {
|
||||
throw new IllegalArgumentException("ICD 解析结果不能为空");
|
||||
}
|
||||
|
||||
GenerateFromIcdCommand command = new GenerateFromIcdCommand();
|
||||
command.setIcdDocument(request.getIcdDocument());
|
||||
command.setFileName(request.getIcdDocument().getFileName());
|
||||
command.setVersion(request.getVersion());
|
||||
command.setAuthor(resolveText(request.getAuthor(), moduleConfig.getDefaultAuthor()));
|
||||
command.setSaveToDisk(request.isSaveToDisk());
|
||||
command.setPrettyJson(request.isPrettyJson());
|
||||
command.setOutputDir(resolveText(request.getOutputDir(), moduleConfig.getDefaultOutputDir()));
|
||||
fillIndexSelection(command, request.getIndexSelection());
|
||||
return command;
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("请求转换失败:" + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制前端提交的索引绑定结构。
|
||||
*/
|
||||
private void fillIndexSelection(GenerateFromIcdCommand command, List<IndexSelectionGroupRequest> indexSelection) {
|
||||
if (indexSelection == null) {
|
||||
return;
|
||||
}
|
||||
for (IndexSelectionGroupRequest groupRequest : indexSelection) {
|
||||
if (groupRequest == null) {
|
||||
continue;
|
||||
}
|
||||
IndexSelectionGroupCommand groupCommand = new IndexSelectionGroupCommand();
|
||||
groupCommand.setGroupKey(groupRequest.getGroupKey());
|
||||
groupCommand.setGroupDesc(groupRequest.getGroupDesc());
|
||||
|
||||
if (groupRequest.getBindings() != null) {
|
||||
for (IndexBindingRequest bindingRequest : groupRequest.getBindings()) {
|
||||
if (bindingRequest == null) {
|
||||
continue;
|
||||
}
|
||||
IndexBindingCommand bindingCommand = new IndexBindingCommand();
|
||||
bindingCommand.setReportName(bindingRequest.getReportName());
|
||||
bindingCommand.setDataSetName(bindingRequest.getDataSetName());
|
||||
bindingCommand.setLabel(bindingRequest.getLabel());
|
||||
bindingCommand.setLnInst(bindingRequest.getLnInst());
|
||||
groupCommand.getBindings().add(bindingCommand);
|
||||
}
|
||||
}
|
||||
|
||||
command.getIndexSelection().add(groupCommand);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理文本参数的 trim 和默认值回退。
|
||||
*/
|
||||
private String resolveText(String value, String defaultValue) {
|
||||
if (value != null && !value.trim().isEmpty()) {
|
||||
return value.trim();
|
||||
}
|
||||
return defaultValue == null ? null : defaultValue.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.njcn.gather.icd.mapping.component;
|
||||
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
|
||||
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 接口响应转换器。
|
||||
*
|
||||
* 按接口阶段仅组装当前场景必需字段,避免返回冗余信息。
|
||||
*/
|
||||
@Component
|
||||
public class MappingResponseConverter {
|
||||
|
||||
/**
|
||||
* 候选接口响应。
|
||||
*/
|
||||
public MappingTaskResponse fromCandidateResult(GenerateMappingResult result) {
|
||||
MappingTaskResponse response = initBaseResponse(result);
|
||||
response.setIcdDocument(result.getIcdDocument());
|
||||
fillIndexCandidates(response, result);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 正式提交类接口响应。
|
||||
*
|
||||
* SUCCESS:仅返回最终结果;
|
||||
* NEED_INDEX_SELECTION:返回重新选择所需的候选信息;
|
||||
* FAILED:仅返回错误信息。
|
||||
*/
|
||||
public MappingTaskResponse fromSubmitResult(GenerateMappingResult result) {
|
||||
MappingTaskResponse response = initBaseResponse(result);
|
||||
if (result.getStatus() == GenerateStatus.SUCCESS) {
|
||||
response.setMappingJson(result.getMappingJson());
|
||||
response.setSavedPath(result.getSavedPath());
|
||||
return response;
|
||||
}
|
||||
|
||||
if (result.getStatus() == GenerateStatus.NEED_INDEX_SELECTION) {
|
||||
response.setIcdDocument(result.getIcdDocument());
|
||||
fillIndexCandidates(response, result);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private MappingTaskResponse initBaseResponse(GenerateMappingResult result) {
|
||||
MappingTaskResponse response = new MappingTaskResponse();
|
||||
response.setStatus(result.getStatus());
|
||||
response.setMessage(result.getMessage());
|
||||
response.getProblems().addAll(result.getProblems());
|
||||
return response;
|
||||
}
|
||||
|
||||
private void fillIndexCandidates(MappingTaskResponse response, GenerateMappingResult result) {
|
||||
if (result.getIndexAnalysis() == null || result.getIndexAnalysis().getCandidates() == null) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
package com.njcn.gather.icd.mapping.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 模块配置。
|
||||
* 模块配置类。集中管理默认作者、默认模板路径等可配置项。
|
||||
* `mms-mapping` 模块配置。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 这里把模板路径、输出目录、默认作者等集中管理。
|
||||
* 2. 当前先用 @Value + 默认值,后续你也可以改成 @ConfigurationProperties。
|
||||
* 统一管理默认模板路径、默认作者和默认输出目录等配置项。
|
||||
*/
|
||||
@Component
|
||||
@Getter
|
||||
@Setter
|
||||
public class MappingModuleConfig {
|
||||
|
||||
/** 默认模板资源路径。 */
|
||||
@@ -25,28 +26,4 @@ public class MappingModuleConfig {
|
||||
/** 默认输出目录。 */
|
||||
@Value("${icd.mapping.default-output-dir:}")
|
||||
private String defaultOutputDir;
|
||||
|
||||
public String getDefaultTemplatePath() {
|
||||
return defaultTemplatePath;
|
||||
}
|
||||
|
||||
public void setDefaultTemplatePath(String defaultTemplatePath) {
|
||||
this.defaultTemplatePath = defaultTemplatePath;
|
||||
}
|
||||
|
||||
public String getDefaultAuthor() {
|
||||
return defaultAuthor;
|
||||
}
|
||||
|
||||
public void setDefaultAuthor(String defaultAuthor) {
|
||||
this.defaultAuthor = defaultAuthor;
|
||||
}
|
||||
|
||||
public String getDefaultOutputDir() {
|
||||
return defaultOutputDir;
|
||||
}
|
||||
|
||||
public void setDefaultOutputDir(String defaultOutputDir) {
|
||||
this.defaultOutputDir = defaultOutputDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,92 @@
|
||||
package com.njcn.gather.icd.mapping.controller;
|
||||
|
||||
import com.njcn.gather.icd.mapping.application.MappingTaskAppService;
|
||||
import com.njcn.gather.icd.mapping.application.command.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.application.result.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.controller.request.GenerateMappingFromIcdRequest;
|
||||
import com.njcn.gather.icd.mapping.controller.response.MappingTaskResponse;
|
||||
import com.njcn.gather.icd.mapping.converter.MappingRequestConverter;
|
||||
import com.njcn.gather.icd.mapping.converter.MappingResponseConverter;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.icd.mapping.component.MappingRequestConverter;
|
||||
import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
|
||||
import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
|
||||
import com.njcn.gather.icd.mapping.service.MappingTaskService;
|
||||
import com.njcn.gather.icd.mapping.utils.DateUtils;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* ICD 映射接口。
|
||||
* ICD 映射接口入口。
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "ICD 映射")
|
||||
@RestController
|
||||
@RequestMapping("/api/mms-mapping")
|
||||
public class MappingController {
|
||||
@RequiredArgsConstructor
|
||||
public class MappingController extends BaseController {
|
||||
|
||||
private final MappingTaskAppService mappingTaskAppService;
|
||||
/** 映射任务编排服务,负责从 ICD 解析到映射生成的完整链路。 */
|
||||
private final MappingTaskService mappingTaskService;
|
||||
|
||||
/** 请求参数转换器,将接口入参转换为应用层命令。 */
|
||||
private final MappingRequestConverter requestConverter;
|
||||
|
||||
/** 响应转换器,按接口阶段裁剪最小返回字段。 */
|
||||
private final MappingResponseConverter responseConverter;
|
||||
|
||||
public MappingController(MappingTaskAppService mappingTaskAppService,
|
||||
MappingRequestConverter requestConverter,
|
||||
MappingResponseConverter responseConverter) {
|
||||
this.mappingTaskAppService = mappingTaskAppService;
|
||||
this.requestConverter = requestConverter;
|
||||
this.responseConverter = responseConverter;
|
||||
/**
|
||||
* 上传 ICD 文件,返回候选结果和可编辑的 ICD 解析结果。
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("上传 ICD 文件并生成索引候选")
|
||||
@PostMapping(value = "/get-icd", consumes = {"multipart/form-data"})
|
||||
public MappingTaskResponse getICD(@RequestPart("icdFile") MultipartFile icdFile) {
|
||||
String methodDescribe = getMethodDescribe("getICD");
|
||||
LogUtil.njcnDebug(log, "{},开始解析 ICD 文件并生成索引候选,fileName={}", methodDescribe, DateUtils.resolveFileName(icdFile));
|
||||
GenerateFromIcdCommand command = requestConverter.toCommand(icdFile);
|
||||
GenerateMappingResult result = mappingTaskService.getICD(command);
|
||||
return responseConverter.fromCandidateResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传 ICD 并生成映射。
|
||||
*
|
||||
* 表单参数:
|
||||
* 1. icdFile:ICD 文件
|
||||
* 2. request:JSON 请求体
|
||||
* 根据前端确认后的索引绑定关系生成 MMS JSON。
|
||||
*/
|
||||
@PostMapping(value = "/generate-from-icd", consumes = {"multipart/form-data"})
|
||||
public MappingTaskResponse generateFromIcd(@RequestPart("icdFile") MultipartFile icdFile,
|
||||
@Validated @RequestPart("request") GenerateMappingFromIcdRequest request) {
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("获取 MMS JSON")
|
||||
@PostMapping("/get-mms-json")
|
||||
public MappingTaskResponse getMmsJson(@Validated @RequestBody SubmitIndexSelectionRequest request) {
|
||||
String methodDescribe = getMethodDescribe("getMmsJson");
|
||||
LogUtil.njcnDebug(log, "{},开始生成 MMS JSON,indexSelectionCount={}",
|
||||
methodDescribe, DateUtils.resolveSelectionCount(request == null ? null : request.getIndexSelection()));
|
||||
GenerateFromIcdCommand command = requestConverter.toCommand(request);
|
||||
GenerateMappingResult result = mappingTaskService.getMmsJson(command);
|
||||
return responseConverter.fromSubmitResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传 ICD 后直接串联候选生成和索引提交,统一返回正式提交阶段结果。
|
||||
*/
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("上传 ICD 后直接获取 MMS JSON")
|
||||
@PostMapping(value = "/get-icd-mms-json", consumes = {"multipart/form-data"})
|
||||
public MappingTaskResponse getIcdMmsJson(@RequestPart("icdFile") MultipartFile icdFile,
|
||||
@Validated @RequestPart("request") GenerateMappingFromIcdRequest request) {
|
||||
String methodDescribe = getMethodDescribe("getIcdMmsJson");
|
||||
LogUtil.njcnDebug(log, "{},开始获取 ICD MMS JSON,fileName={}, indexSelectionCount={}",
|
||||
methodDescribe, DateUtils.resolveFileName(icdFile),
|
||||
DateUtils.resolveSelectionCount(request == null ? null : request.getIndexSelection()));
|
||||
GenerateFromIcdCommand command = requestConverter.toCommand(icdFile, request);
|
||||
GenerateMappingResult result = mappingTaskAppService.generateFromIcd(command);
|
||||
return responseConverter.fromResult(result);
|
||||
GenerateMappingResult result = mappingTaskService.getIcdMmsJson(command);
|
||||
return responseConverter.fromSubmitResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.request;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生成映射接口请求体。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 旧版结构中,indexSelection 是 Map<String, String>,只能表达“一个报告对应一个值”,
|
||||
* 无法表达“一个业务分组下有多个报告、一个报告下又有多条标签绑定”的真实场景。
|
||||
* 2. 新版结构改成 List<IndexSelectionGroupRequest>,用来完整承载用户在前端完成的绑定结果。
|
||||
* 3. 第一次只上传 ICD 时,这个字段可以为空;第二次用户确认绑定后再把完整结构上送即可。
|
||||
*/
|
||||
public class GenerateMappingFromIcdRequest {
|
||||
|
||||
/** 输出版本号。为空时后端默认补 1.0。 */
|
||||
private String version;
|
||||
|
||||
/** 作者。为空时默认空字符串。 */
|
||||
private String author;
|
||||
|
||||
/** 是否保存到磁盘。 */
|
||||
private boolean saveToDisk;
|
||||
|
||||
/** 是否返回美化 JSON。 */
|
||||
private boolean prettyJson;
|
||||
|
||||
/** 输出目录。saveToDisk=true 时才会用到。 */
|
||||
private String outputDir;
|
||||
|
||||
/**
|
||||
* 索引选择结果。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 每一个元素代表一个“业务分组”,例如:实时数据、统计数据、波动闪变。
|
||||
* 2. 每个业务分组下面又包含多条绑定关系。
|
||||
* 3. 允许为空;为空时后端返回 NEED_INDEX_SELECTION,给前端候选参考项。
|
||||
*/
|
||||
private List<IndexSelectionGroupRequest> indexSelection = new ArrayList<IndexSelectionGroupRequest>();
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public boolean isSaveToDisk() {
|
||||
return saveToDisk;
|
||||
}
|
||||
|
||||
public void setSaveToDisk(boolean saveToDisk) {
|
||||
this.saveToDisk = saveToDisk;
|
||||
}
|
||||
|
||||
public boolean isPrettyJson() {
|
||||
return prettyJson;
|
||||
}
|
||||
|
||||
public void setPrettyJson(boolean prettyJson) {
|
||||
this.prettyJson = prettyJson;
|
||||
}
|
||||
|
||||
public String getOutputDir() {
|
||||
return outputDir;
|
||||
}
|
||||
|
||||
public void setOutputDir(String outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
public List<IndexSelectionGroupRequest> getIndexSelection() {
|
||||
return indexSelection;
|
||||
}
|
||||
|
||||
public void setIndexSelection(List<IndexSelectionGroupRequest> indexSelection) {
|
||||
this.indexSelection = indexSelection;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.request;
|
||||
|
||||
/**
|
||||
* 单条索引绑定请求。
|
||||
*
|
||||
* 一条绑定只表达一个最小关系:
|
||||
* 某个报告(reportName)下,使用某个标签(label)与某个 lnInst 数字做绑定。
|
||||
*
|
||||
* 这样做的好处:
|
||||
* 1. 一个报告可以出现多次,对应多个标签。
|
||||
* 2. 一个业务分组下也可以有多个报告。
|
||||
* 3. 后端校验、生成映射时都更容易逐条处理。
|
||||
*/
|
||||
public class IndexBindingRequest {
|
||||
|
||||
/** 绑定发生在哪个报告上,例如:brcbStHarm。 */
|
||||
private String reportName;
|
||||
|
||||
/** 绑定发生在哪个数据集上,例如:dsStHarm。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 业务标签,例如:最大值、最小值、实时数据。 */
|
||||
private String label;
|
||||
|
||||
/** 当前标签最终绑定到的 lnInst 数字,例如:1、2、8。 */
|
||||
private String lnInst;
|
||||
|
||||
public String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
public void setReportName(String reportName) {
|
||||
this.reportName = reportName;
|
||||
}
|
||||
|
||||
public String getDataSetName() {
|
||||
return dataSetName;
|
||||
}
|
||||
|
||||
public void setDataSetName(String dataSetName) {
|
||||
this.dataSetName = dataSetName;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLnInst() {
|
||||
return lnInst;
|
||||
}
|
||||
|
||||
public void setLnInst(String lnInst) {
|
||||
this.lnInst = lnInst;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.request;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单个业务分组的索引选择请求。
|
||||
*
|
||||
* 例如:
|
||||
* - groupKey = REALTIME_DATA
|
||||
* - groupDesc = 实时数据
|
||||
* - bindings = 多条“报告 + 标签 + lnInst”的绑定关系
|
||||
*/
|
||||
public class IndexSelectionGroupRequest {
|
||||
|
||||
/**
|
||||
* 分组唯一键。
|
||||
*
|
||||
* 说明:
|
||||
* 这个值由后端在 NEED_INDEX_SELECTION 时返回,前端原样带回即可,避免仅靠中文描述做匹配。
|
||||
*/
|
||||
private String groupKey;
|
||||
|
||||
/** 分组中文描述,例如:实时数据、统计数据。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前业务分组下,用户最终确认的绑定关系。 */
|
||||
private List<IndexBindingRequest> bindings = new ArrayList<IndexBindingRequest>();
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public List<IndexBindingRequest> getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
public void setBindings(List<IndexBindingRequest> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 索引候选分组下的单个报告响应。
|
||||
*/
|
||||
public class IndexCandidateReportItemResponse {
|
||||
|
||||
/** 报告名称。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名称。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 报告描述。 */
|
||||
private String reportDesc;
|
||||
|
||||
/** 当前报告可选的 lnInst 数字。 */
|
||||
private List<String> availableLnInstValues = new ArrayList<String>();
|
||||
|
||||
public String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
public void setReportName(String reportName) {
|
||||
this.reportName = reportName;
|
||||
}
|
||||
|
||||
public String getDataSetName() {
|
||||
return dataSetName;
|
||||
}
|
||||
|
||||
public void setDataSetName(String dataSetName) {
|
||||
this.dataSetName = dataSetName;
|
||||
}
|
||||
|
||||
public String getReportDesc() {
|
||||
return reportDesc;
|
||||
}
|
||||
|
||||
public void setReportDesc(String reportDesc) {
|
||||
this.reportDesc = reportDesc;
|
||||
}
|
||||
|
||||
public List<String> getAvailableLnInstValues() {
|
||||
return availableLnInstValues;
|
||||
}
|
||||
|
||||
public void setAvailableLnInstValues(List<String> availableLnInstValues) {
|
||||
this.availableLnInstValues = availableLnInstValues;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 索引候选返回对象。
|
||||
*
|
||||
* 说明:
|
||||
* 这是给前端“待匹配界面”使用的正式结构:
|
||||
* - 一个候选就是一个业务分组;
|
||||
* - 分组下面再挂多个报告;
|
||||
* - 前端根据 templateLabels 与 reports[*].availableLnInstValues 做人工绑定。
|
||||
*/
|
||||
public class IndexCandidateResponse {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组中文描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前分组包含的报告数。 */
|
||||
private int reportCount;
|
||||
|
||||
/** 模板里配置的可选标签。 */
|
||||
private List<String> templateLabels = new ArrayList<String>();
|
||||
|
||||
/** 当前分组下的报告候选列表。 */
|
||||
private List<IndexCandidateReportItemResponse> reports = new ArrayList<IndexCandidateReportItemResponse>();
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public int getReportCount() {
|
||||
return reportCount;
|
||||
}
|
||||
|
||||
public void setReportCount(int reportCount) {
|
||||
this.reportCount = reportCount;
|
||||
}
|
||||
|
||||
public List<String> getTemplateLabels() {
|
||||
return templateLabels;
|
||||
}
|
||||
|
||||
public void setTemplateLabels(List<String> templateLabels) {
|
||||
this.templateLabels = templateLabels;
|
||||
}
|
||||
|
||||
public List<IndexCandidateReportItemResponse> getReports() {
|
||||
return reports;
|
||||
}
|
||||
|
||||
public void setReports(List<IndexCandidateReportItemResponse> reports) {
|
||||
this.reports = reports;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.response;
|
||||
|
||||
/**
|
||||
* 映射摘要响应。
|
||||
* 映射摘要响应 DTO。用于把最终映射的关键信息单独封装返回。
|
||||
*/
|
||||
public class MappingDocumentResponse {
|
||||
private String version;
|
||||
private String author;
|
||||
private String ied;
|
||||
private String ld;
|
||||
private int reportCount;
|
||||
private int dataSetCount;
|
||||
public String getVersion() { return version; }
|
||||
public void setVersion(String version) { this.version = version; }
|
||||
public String getAuthor() { return author; }
|
||||
public void setAuthor(String author) { this.author = author; }
|
||||
public String getIed() { return ied; }
|
||||
public void setIed(String ied) { this.ied = ied; }
|
||||
public String getLd() { return ld; }
|
||||
public void setLd(String ld) { this.ld = ld; }
|
||||
public int getReportCount() { return reportCount; }
|
||||
public void setReportCount(int reportCount) { this.reportCount = reportCount; }
|
||||
public int getDataSetCount() { return dataSetCount; }
|
||||
public void setDataSetCount(int dataSetCount) { this.dataSetCount = dataSetCount; }
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.controller.response;
|
||||
|
||||
import com.njcn.gather.icd.mapping.enums.GenerateStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 第一个接口统一响应。
|
||||
* 接口统一响应 DTO。返回生成状态、映射内容、候选索引、问题列表等。
|
||||
*/
|
||||
public class MappingTaskResponse {
|
||||
private GenerateStatus status;
|
||||
private String message;
|
||||
private String iedName;
|
||||
private String ldInst;
|
||||
private String mappingJson;
|
||||
private String savedPath;
|
||||
private MappingDocumentResponse mappingDocument;
|
||||
private List<IndexCandidateResponse> indexCandidates = new ArrayList<IndexCandidateResponse>();
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
public GenerateStatus getStatus() { return status; }
|
||||
public void setStatus(GenerateStatus status) { this.status = status; }
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
public String getIedName() { return iedName; }
|
||||
public void setIedName(String iedName) { this.iedName = iedName; }
|
||||
public String getLdInst() { return ldInst; }
|
||||
public void setLdInst(String ldInst) { this.ldInst = ldInst; }
|
||||
public String getMappingJson() { return mappingJson; }
|
||||
public void setMappingJson(String mappingJson) { this.mappingJson = mappingJson; }
|
||||
public String getSavedPath() { return savedPath; }
|
||||
public void setSavedPath(String savedPath) { this.savedPath = savedPath; }
|
||||
public MappingDocumentResponse getMappingDocument() { return mappingDocument; }
|
||||
public void setMappingDocument(MappingDocumentResponse mappingDocument) { this.mappingDocument = mappingDocument; }
|
||||
public List<IndexCandidateResponse> getIndexCandidates() { return indexCandidates; }
|
||||
public void setIndexCandidates(List<IndexCandidateResponse> indexCandidates) { this.indexCandidates = indexCandidates; }
|
||||
public List<String> getProblems() { return problems; }
|
||||
public void setProblems(List<String> problems) { this.problems = problems; }
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.converter;
|
||||
|
||||
import com.njcn.gather.icd.mapping.application.command.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.application.command.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.application.command.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
|
||||
import com.njcn.gather.icd.mapping.controller.request.GenerateMappingFromIcdRequest;
|
||||
import com.njcn.gather.icd.mapping.controller.request.IndexBindingRequest;
|
||||
import com.njcn.gather.icd.mapping.controller.request.IndexSelectionGroupRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 请求转换器。
|
||||
*
|
||||
* 作用:
|
||||
* 1. 把接口层 request 转成应用层 command。
|
||||
* 2. 统一处理 null 和空集合,避免后面业务层到处判空。
|
||||
*/
|
||||
@Component
|
||||
public class MappingRequestConverter {
|
||||
|
||||
private final MappingModuleConfig moduleConfig;
|
||||
|
||||
public MappingRequestConverter(MappingModuleConfig moduleConfig) {
|
||||
this.moduleConfig = moduleConfig;
|
||||
}
|
||||
|
||||
public GenerateFromIcdCommand toCommand(MultipartFile icdFile, GenerateMappingFromIcdRequest request) {
|
||||
try {
|
||||
if (icdFile == null || icdFile.isEmpty()) {
|
||||
throw new IllegalArgumentException("ICD 文件不能为空");
|
||||
}
|
||||
GenerateFromIcdCommand command = new GenerateFromIcdCommand();
|
||||
command.setFileName(icdFile.getOriginalFilename());
|
||||
command.setFileBytes(icdFile.getBytes());
|
||||
command.setVersion(request == null ? null : request.getVersion());
|
||||
command.setAuthor(resolveText(request == null ? null : request.getAuthor(), moduleConfig.getDefaultAuthor()));
|
||||
command.setSaveToDisk(request != null && request.isSaveToDisk());
|
||||
command.setPrettyJson(request == null || request.isPrettyJson());
|
||||
command.setOutputDir(resolveText(request == null ? null : request.getOutputDir(), moduleConfig.getDefaultOutputDir()));
|
||||
|
||||
if (request != null && request.getIndexSelection() != null) {
|
||||
for (IndexSelectionGroupRequest groupRequest : request.getIndexSelection()) {
|
||||
if (groupRequest == null) {
|
||||
continue;
|
||||
}
|
||||
IndexSelectionGroupCommand groupCommand = new IndexSelectionGroupCommand();
|
||||
groupCommand.setGroupKey(groupRequest.getGroupKey());
|
||||
groupCommand.setGroupDesc(groupRequest.getGroupDesc());
|
||||
|
||||
if (groupRequest.getBindings() != null) {
|
||||
for (IndexBindingRequest bindingRequest : groupRequest.getBindings()) {
|
||||
if (bindingRequest == null) {
|
||||
continue;
|
||||
}
|
||||
IndexBindingCommand bindingCommand = new IndexBindingCommand();
|
||||
bindingCommand.setReportName(bindingRequest.getReportName());
|
||||
bindingCommand.setDataSetName(bindingRequest.getDataSetName());
|
||||
bindingCommand.setLabel(bindingRequest.getLabel());
|
||||
bindingCommand.setLnInst(bindingRequest.getLnInst());
|
||||
groupCommand.getBindings().add(bindingCommand);
|
||||
}
|
||||
}
|
||||
|
||||
command.getIndexSelection().add(groupCommand);
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("请求转换失败:" + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveText(String value, String defaultValue) {
|
||||
if (value != null && !value.trim().isEmpty()) {
|
||||
return value.trim();
|
||||
}
|
||||
return defaultValue == null ? null : defaultValue.trim();
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.converter;
|
||||
|
||||
import com.njcn.gather.icd.mapping.application.result.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.controller.response.IndexCandidateReportItemResponse;
|
||||
import com.njcn.gather.icd.mapping.controller.response.IndexCandidateResponse;
|
||||
import com.njcn.gather.icd.mapping.controller.response.MappingDocumentResponse;
|
||||
import com.njcn.gather.icd.mapping.controller.response.MappingTaskResponse;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 响应转换器。
|
||||
*
|
||||
* 作用:
|
||||
* 1. 把应用层结果转换成接口层响应对象;
|
||||
* 2. 对待匹配索引场景,输出新的“按业务分组返回”的结构。
|
||||
*/
|
||||
@Component
|
||||
public class MappingResponseConverter {
|
||||
|
||||
public MappingTaskResponse fromResult(GenerateMappingResult result) {
|
||||
MappingTaskResponse response = new MappingTaskResponse();
|
||||
response.setStatus(result.getStatus());
|
||||
response.setMessage(result.getMessage());
|
||||
response.setIedName(result.getIedName());
|
||||
response.setLdInst(result.getLdInst());
|
||||
response.setMappingJson(result.getMappingJson());
|
||||
response.setSavedPath(result.getSavedPath());
|
||||
response.getProblems().addAll(result.getProblems());
|
||||
|
||||
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();
|
||||
doc.setVersion(result.getMappingDocument().getVersion());
|
||||
doc.setAuthor(result.getMappingDocument().getAuthor());
|
||||
doc.setIed(result.getMappingDocument().getIed());
|
||||
doc.setLd(result.getMappingDocument().getLd());
|
||||
doc.setReportCount(result.getMappingDocument().getReportMap() == null
|
||||
? 0 : result.getMappingDocument().getReportMap().size());
|
||||
doc.setDataSetCount(result.getMappingDocument().getDataSetList() == null
|
||||
? 0 : result.getMappingDocument().getDataSetList().size());
|
||||
response.setMappingDocument(doc);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.analysis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 索引候选分组。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 一条候选对应一个业务分组,例如:统计数据、实时数据;
|
||||
* 2. 一个业务分组下可以包含多个报告;
|
||||
* 3. 这里不仅保存返回给前端的候选项,也保存从 DefaultCfg.ReportList 带下来的配置项,
|
||||
* 供后续 MappingGenerationService 直接使用,避免“二次查模板”失败。
|
||||
*/
|
||||
public class IndexCandidate {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 该分组下实际匹配到的报告数量。 */
|
||||
private int reportCount;
|
||||
|
||||
/** DefaultCfg.txt 中该分组可用的标签模板。 */
|
||||
private List<String> templateLabels = new ArrayList<String>();
|
||||
|
||||
/** 当前分组下匹配到的报告列表。 */
|
||||
private List<IndexCandidateReportItem> reports = new ArrayList<IndexCandidateReportItem>();
|
||||
|
||||
/**
|
||||
* DefaultCfg.ReportList.inst
|
||||
* 例如:01 / 02 / 03 / 04
|
||||
*/
|
||||
private String reportInst;
|
||||
|
||||
/**
|
||||
* DefaultCfg.ReportList.Select
|
||||
* 例如:DataStatFileMap / DataRealFileMap / FlickerFileMap
|
||||
*/
|
||||
private String select;
|
||||
|
||||
/**
|
||||
* DefaultCfg.ReportList.TrgOps
|
||||
* 例如:40 / 96
|
||||
*/
|
||||
private String trgOps;
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public int getReportCount() {
|
||||
return reportCount;
|
||||
}
|
||||
|
||||
public void setReportCount(int reportCount) {
|
||||
this.reportCount = reportCount;
|
||||
}
|
||||
|
||||
public List<String> getTemplateLabels() {
|
||||
return templateLabels;
|
||||
}
|
||||
|
||||
public void setTemplateLabels(List<String> templateLabels) {
|
||||
this.templateLabels = templateLabels;
|
||||
}
|
||||
|
||||
public List<IndexCandidateReportItem> getReports() {
|
||||
return reports;
|
||||
}
|
||||
|
||||
public void setReports(List<IndexCandidateReportItem> reports) {
|
||||
this.reports = reports;
|
||||
}
|
||||
|
||||
public String getReportInst() {
|
||||
return reportInst;
|
||||
}
|
||||
|
||||
public void setReportInst(String reportInst) {
|
||||
this.reportInst = reportInst;
|
||||
}
|
||||
|
||||
public String getSelect() {
|
||||
return select;
|
||||
}
|
||||
|
||||
public void setSelect(String select) {
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
public String getTrgOps() {
|
||||
return trgOps;
|
||||
}
|
||||
|
||||
public void setTrgOps(String trgOps) {
|
||||
this.trgOps = trgOps;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.analysis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 候选分组下的单个报告项。
|
||||
*
|
||||
* 这个对象专门给前端展示“这个分组下面有哪些报告,以及每个报告对应哪些可用 lnInst 数字”。
|
||||
*/
|
||||
public class IndexCandidateReportItem {
|
||||
|
||||
/** 报告名称。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名称。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 报告中文描述。一般和分组描述相同,保留它是为了前端渲染更灵活。 */
|
||||
private String reportDesc;
|
||||
|
||||
/** 当前报告在 ICD 中解析出来的所有可选 lnInst 数字。 */
|
||||
private List<String> availableLnInstValues = new ArrayList<String>();
|
||||
|
||||
public String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
public void setReportName(String reportName) {
|
||||
this.reportName = reportName;
|
||||
}
|
||||
|
||||
public String getDataSetName() {
|
||||
return dataSetName;
|
||||
}
|
||||
|
||||
public void setDataSetName(String dataSetName) {
|
||||
this.dataSetName = dataSetName;
|
||||
}
|
||||
|
||||
public String getReportDesc() {
|
||||
return reportDesc;
|
||||
}
|
||||
|
||||
public void setReportDesc(String reportDesc) {
|
||||
this.reportDesc = reportDesc;
|
||||
}
|
||||
|
||||
public List<String> getAvailableLnInstValues() {
|
||||
return availableLnInstValues;
|
||||
}
|
||||
|
||||
public void setAvailableLnInstValues(List<String> availableLnInstValues) {
|
||||
this.availableLnInstValues = availableLnInstValues;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.analysis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 校验结果。
|
||||
* 索引校验结果模型。保存是否通过以及问题列表。
|
||||
*/
|
||||
public class ValidationResult {
|
||||
private boolean valid;
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
|
||||
public boolean isValid() { return valid; }
|
||||
public void setValid(boolean valid) { this.valid = valid; }
|
||||
public List<String> getProblems() { return problems; }
|
||||
public void setProblems(List<String> problems) { this.problems = problems; }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DataSet 节点。
|
||||
* 数据集模型。用于承接 DataSet 下的 FCDA 列表。
|
||||
*/
|
||||
public class DataSetNode {
|
||||
private String name;
|
||||
private List<FcdaNode> fcdas = new ArrayList<FcdaNode>();
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public List<FcdaNode> getFcdas() { return fcdas; }
|
||||
public void setFcdas(List<FcdaNode> fcdas) { this.fcdas = fcdas; }
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DOI/SDI/DAI 统一节点。
|
||||
* DOI/SDI/DAI 细项模型。用于递归承接 DOI 树明细。
|
||||
*
|
||||
* kind 常见值:
|
||||
* - SDI
|
||||
* - DAI
|
||||
*/
|
||||
public class DoiElementNode {
|
||||
private String kind;
|
||||
private String name;
|
||||
private Long ix;
|
||||
private List<String> values = new ArrayList<String>();
|
||||
private List<DoiElementNode> children = new ArrayList<DoiElementNode>();
|
||||
|
||||
public String getKind() { return kind; }
|
||||
public void setKind(String kind) { this.kind = kind; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Long getIx() { return ix; }
|
||||
public void setIx(Long ix) { this.ix = ix; }
|
||||
public List<String> getValues() { return values; }
|
||||
public void setValues(List<String> values) { this.values = values; }
|
||||
public List<DoiElementNode> getChildren() { return children; }
|
||||
public void setChildren(List<DoiElementNode> children) { this.children = children; }
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DOI 节点。
|
||||
* DOI 模型。表示逻辑节点下的一个数据对象节点。
|
||||
*/
|
||||
public class DoiNode {
|
||||
private String name;
|
||||
private Long ix;
|
||||
private String lnClass;
|
||||
private String lnInst;
|
||||
private int sequenceCount;
|
||||
private List<DoiElementNode> children = new ArrayList<DoiElementNode>();
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Long getIx() { return ix; }
|
||||
public void setIx(Long ix) { this.ix = ix; }
|
||||
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 int getSequenceCount() { return sequenceCount; }
|
||||
public void setSequenceCount(int sequenceCount) { this.sequenceCount = sequenceCount; }
|
||||
public List<DoiElementNode> getChildren() { return children; }
|
||||
public void setChildren(List<DoiElementNode> children) { this.children = children; }
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
/**
|
||||
* FCDA 节点。
|
||||
* FCDA 模型。保存 lnClass、lnInst、doName、daName、fc、ix 等信息。
|
||||
*/
|
||||
public class FcdaNode {
|
||||
private String ldInst;
|
||||
private String prefix;
|
||||
private String lnClass;
|
||||
private String lnInst;
|
||||
private String doName;
|
||||
private String daName;
|
||||
private String fc;
|
||||
private Long ix;
|
||||
private int sequenceCount;
|
||||
|
||||
public String getLdInst() { return ldInst; }
|
||||
public void setLdInst(String ldInst) { this.ldInst = ldInst; }
|
||||
public String getPrefix() { return prefix; }
|
||||
public void setPrefix(String prefix) { this.prefix = prefix; }
|
||||
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 getDaName() { return daName; }
|
||||
public void setDaName(String daName) { this.daName = daName; }
|
||||
public String getFc() { return fc; }
|
||||
public void setFc(String fc) { this.fc = fc; }
|
||||
public Long getIx() { return ix; }
|
||||
public void setIx(Long ix) { this.ix = ix; }
|
||||
public int getSequenceCount() { return sequenceCount; }
|
||||
public void setSequenceCount(int sequenceCount) { this.sequenceCount = sequenceCount; }
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ICD 文档聚合根。
|
||||
* ICD 统一领域模型的聚合根。承接 IED、LDevice、ReportControl、DataSet、LN 等解析结果。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 这是系统内部统一的 ICD 模型。
|
||||
* 2. 外部 JAXB generated 类只在 parser 层使用。
|
||||
* 3. 业务层全部依赖这个标准化模型,便于后续替换解析实现。
|
||||
*/
|
||||
public class IcdDocument {
|
||||
private String fileName;
|
||||
private String iedName;
|
||||
private String ldInst;
|
||||
private String ldPrefix;
|
||||
private IedNode primaryIed;
|
||||
private LogicalDeviceNode logicalDevice;
|
||||
private List<LnNode> logicalNodes = new ArrayList<LnNode>();
|
||||
private List<ReportControlNode> reportControls = new ArrayList<ReportControlNode>();
|
||||
private Map<String, DataSetNode> dataSets = new LinkedHashMap<String, DataSetNode>();
|
||||
|
||||
public String getFileName() { return fileName; }
|
||||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||||
public String getIedName() { return iedName; }
|
||||
public void setIedName(String iedName) { this.iedName = iedName; }
|
||||
public String getLdInst() { return ldInst; }
|
||||
public void setLdInst(String ldInst) { this.ldInst = ldInst; }
|
||||
public String getLdPrefix() { return ldPrefix; }
|
||||
public void setLdPrefix(String ldPrefix) { this.ldPrefix = ldPrefix; }
|
||||
public IedNode getPrimaryIed() { return primaryIed; }
|
||||
public void setPrimaryIed(IedNode primaryIed) { this.primaryIed = primaryIed; }
|
||||
public LogicalDeviceNode getLogicalDevice() { return logicalDevice; }
|
||||
public void setLogicalDevice(LogicalDeviceNode logicalDevice) { this.logicalDevice = logicalDevice; }
|
||||
public List<LnNode> getLogicalNodes() { return logicalNodes; }
|
||||
public void setLogicalNodes(List<LnNode> logicalNodes) { this.logicalNodes = logicalNodes; }
|
||||
public List<ReportControlNode> getReportControls() { return reportControls; }
|
||||
public void setReportControls(List<ReportControlNode> reportControls) { this.reportControls = reportControls; }
|
||||
public Map<String, DataSetNode> getDataSets() { return dataSets; }
|
||||
public void setDataSets(Map<String, DataSetNode> dataSets) { this.dataSets = dataSets; }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IED 节点。
|
||||
* IED 节点模型。保存 IED 名称以及其下逻辑设备引用。
|
||||
*/
|
||||
public class IedNode {
|
||||
private String name;
|
||||
private List<String> accessPointNames = new ArrayList<String>();
|
||||
private List<String> lDeviceInstList = new ArrayList<String>();
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public List<String> getAccessPointNames() { return accessPointNames; }
|
||||
public void setAccessPointNames(List<String> accessPointNames) { this.accessPointNames = accessPointNames; }
|
||||
public List<String> getLDeviceInstList() { return lDeviceInstList; }
|
||||
public void setLDeviceInstList(List<String> lDeviceInstList) { this.lDeviceInstList = lDeviceInstList; }
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 逻辑节点。
|
||||
* 逻辑节点模型。保存 lnClass、lnInst、prefix、DOI 等信息。
|
||||
*
|
||||
* LN0 和 LN 最终都统一抽象为这个模型。
|
||||
*/
|
||||
public class LnNode {
|
||||
private boolean ln0;
|
||||
private String prefix;
|
||||
private String lnClass;
|
||||
private String lnInst;
|
||||
private String lnType;
|
||||
private List<DoiNode> doiList = new ArrayList<DoiNode>();
|
||||
|
||||
public boolean isLn0() { return ln0; }
|
||||
public void setLn0(boolean ln0) { this.ln0 = ln0; }
|
||||
public String getPrefix() { return prefix; }
|
||||
public void setPrefix(String prefix) { this.prefix = prefix; }
|
||||
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 getLnType() { return lnType; }
|
||||
public void setLnType(String lnType) { this.lnType = lnType; }
|
||||
public List<DoiNode> getDoiList() { return doiList; }
|
||||
public void setDoiList(List<DoiNode> doiList) { this.doiList = doiList; }
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
/**
|
||||
* 逻辑设备节点。
|
||||
* 逻辑设备模型。保存 inst、prefix 以及其下 LN/LN0 列表。
|
||||
*/
|
||||
public class LogicalDeviceNode {
|
||||
private String inst;
|
||||
private String ldName;
|
||||
public String getInst() { return inst; }
|
||||
public void setInst(String inst) { this.inst = inst; }
|
||||
public String getLdName() { return ldName; }
|
||||
public void setLdName(String ldName) { this.ldName = ldName; }
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.icd;
|
||||
|
||||
/**
|
||||
* ReportControl 节点。
|
||||
* 报告控制块模型。用于保存报告名称、关联 DataSet、缓冲属性等。
|
||||
*/
|
||||
public class ReportControlNode {
|
||||
private String name;
|
||||
private String rptId;
|
||||
private boolean buffered;
|
||||
private String dataSetName;
|
||||
private String trgOps;
|
||||
private String confRev;
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getRptId() { return rptId; }
|
||||
public void setRptId(String rptId) { this.rptId = rptId; }
|
||||
public boolean isBuffered() { return buffered; }
|
||||
public void setBuffered(boolean buffered) { this.buffered = buffered; }
|
||||
public String getDataSetName() { return dataSetName; }
|
||||
public void setDataSetName(String dataSetName) { this.dataSetName = dataSetName; }
|
||||
public String getTrgOps() { return trgOps; }
|
||||
public void setTrgOps(String trgOps) { this.trgOps = trgOps; }
|
||||
public String getConfRev() { return confRev; }
|
||||
public void setConfRev(String confRev) { this.confRev = confRev; }
|
||||
public Boolean getBuffered() { return buffered; }
|
||||
public void setBuffered(Boolean buffered) { this.buffered = buffered; }
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.intermediate;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.LnNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终参与生成 DataSetList 的选择状态。
|
||||
*
|
||||
* 关键修正:
|
||||
* 旧版本一个绑定只保存一个 LnNode,导致:
|
||||
* - MSQI 整组丢失
|
||||
* - MHAI 只生成一半
|
||||
*
|
||||
* 新版本改成:
|
||||
* 一个绑定可以关联多个 LnNode,后续生成阶段再逐个展开。
|
||||
*/
|
||||
public class DataSetSelectionState {
|
||||
|
||||
/** 所属分组 key。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 所属分组 desc。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 报告名。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 绑定标签。 */
|
||||
private String label;
|
||||
|
||||
/** 绑定的 lnInst。 */
|
||||
private String lnInst;
|
||||
|
||||
/**
|
||||
* 当前绑定最终命中的 LN 节点列表。
|
||||
*
|
||||
* 说明:
|
||||
* 同一个 lnInst 在不同 lnClass 下可能都需要展开,
|
||||
* 例如:MMXU / MSQI / MHAI。
|
||||
*/
|
||||
private List<LnNode> lnNodes = new ArrayList<LnNode>();
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
public void setReportName(String reportName) {
|
||||
this.reportName = reportName;
|
||||
}
|
||||
|
||||
public String getDataSetName() {
|
||||
return dataSetName;
|
||||
}
|
||||
|
||||
public void setDataSetName(String dataSetName) {
|
||||
this.dataSetName = dataSetName;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLnInst() {
|
||||
return lnInst;
|
||||
}
|
||||
|
||||
public void setLnInst(String lnInst) {
|
||||
this.lnInst = lnInst;
|
||||
}
|
||||
|
||||
public List<LnNode> getLnNodes() {
|
||||
return lnNodes;
|
||||
}
|
||||
|
||||
public void setLnNodes(List<LnNode> lnNodes) {
|
||||
this.lnNodes = lnNodes;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.intermediate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 中间态总对象。
|
||||
*
|
||||
* 作用:
|
||||
* 1. 对应原 C# 里“先形成中间态,再做最终 JSON 组装”的思路;
|
||||
* 2. 把业务分组、用户绑定、最终 DataSet 选择结果集中保存;
|
||||
* 3. 避免在 MappingGenerationService 里直接边遍历边拼 JSON,便于后续扩展和排查。
|
||||
*/
|
||||
public class ReportAndDataSetState {
|
||||
|
||||
/** IED 名称。 */
|
||||
private String iedName;
|
||||
|
||||
/** LD 实例名。 */
|
||||
private String ldInst;
|
||||
|
||||
/** 分组状态列表。 */
|
||||
private List<ReportGroupState> reportGroups = new ArrayList<ReportGroupState>();
|
||||
|
||||
/** 数据集选择状态列表。 */
|
||||
private List<DataSetSelectionState> dataSetSelections = new ArrayList<DataSetSelectionState>();
|
||||
|
||||
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 List<ReportGroupState> getReportGroups() {
|
||||
return reportGroups;
|
||||
}
|
||||
|
||||
public void setReportGroups(List<ReportGroupState> reportGroups) {
|
||||
this.reportGroups = reportGroups;
|
||||
}
|
||||
|
||||
public List<DataSetSelectionState> getDataSetSelections() {
|
||||
return dataSetSelections;
|
||||
}
|
||||
|
||||
public void setDataSetSelections(List<DataSetSelectionState> dataSetSelections) {
|
||||
this.dataSetSelections = dataSetSelections;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.intermediate;
|
||||
|
||||
/**
|
||||
* 单条最终有效绑定关系的中间态。
|
||||
*/
|
||||
public class ReportBindingState {
|
||||
|
||||
/** 分组 key。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 报告名。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 标签。 */
|
||||
private String label;
|
||||
|
||||
/** 绑定的 lnInst 数字。 */
|
||||
private String lnInst;
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
public void setReportName(String reportName) {
|
||||
this.reportName = reportName;
|
||||
}
|
||||
|
||||
public String getDataSetName() {
|
||||
return dataSetName;
|
||||
}
|
||||
|
||||
public void setDataSetName(String dataSetName) {
|
||||
this.dataSetName = dataSetName;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLnInst() {
|
||||
return lnInst;
|
||||
}
|
||||
|
||||
public void setLnInst(String lnInst) {
|
||||
this.lnInst = lnInst;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.intermediate;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单个业务分组的中间态。
|
||||
*/
|
||||
public class ReportGroupState {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前分组匹配到的报告数量。 */
|
||||
private int reportCount;
|
||||
|
||||
/** 报表 inst(来自 DefaultCfg.ReportList.inst)。 */
|
||||
private String reportInst;
|
||||
|
||||
/** Select(来自 DefaultCfg.ReportList.Select)。 */
|
||||
private String select;
|
||||
|
||||
/** TrgOps(来自 DefaultCfg.ReportList.TrgOps)。 */
|
||||
private String trgOps;
|
||||
|
||||
/** 当前分组包含的报告列表。 */
|
||||
private List<IndexCandidateReportItem> reportItems = new ArrayList<IndexCandidateReportItem>();
|
||||
|
||||
/** 用户最终确认的绑定关系。 */
|
||||
private List<ReportBindingState> bindings = new ArrayList<ReportBindingState>();
|
||||
|
||||
public String getGroupKey() {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
public void setGroupKey(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
}
|
||||
|
||||
public int getReportCount() {
|
||||
return reportCount;
|
||||
}
|
||||
|
||||
public void setReportCount(int reportCount) {
|
||||
this.reportCount = reportCount;
|
||||
}
|
||||
|
||||
public String getReportInst() {
|
||||
return reportInst;
|
||||
}
|
||||
|
||||
public void setReportInst(String reportInst) {
|
||||
this.reportInst = reportInst;
|
||||
}
|
||||
|
||||
public String getSelect() {
|
||||
return select;
|
||||
}
|
||||
|
||||
public void setSelect(String select) {
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
public String getTrgOps() {
|
||||
return trgOps;
|
||||
}
|
||||
|
||||
public void setTrgOps(String trgOps) {
|
||||
this.trgOps = trgOps;
|
||||
}
|
||||
|
||||
public List<IndexCandidateReportItem> getReportItems() {
|
||||
return reportItems;
|
||||
}
|
||||
|
||||
public void setReportItems(List<IndexCandidateReportItem> reportItems) {
|
||||
this.reportItems = reportItems;
|
||||
}
|
||||
|
||||
public List<ReportBindingState> getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
public void setBindings(List<ReportBindingState> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 DataSetList 单项。
|
||||
*/
|
||||
public class DataSetGroupItem {
|
||||
|
||||
/** 分组描述。一般来自 LnClassList.desc。 */
|
||||
private String desc;
|
||||
|
||||
/** lnClass。 */
|
||||
private String lnClass;
|
||||
|
||||
/** 该 lnClass 下的 inst 列表。 */
|
||||
private List<InstItem> instList = new ArrayList<InstItem>();
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getLnClass() {
|
||||
return lnClass;
|
||||
}
|
||||
|
||||
public void setLnClass(String lnClass) {
|
||||
this.lnClass = lnClass;
|
||||
}
|
||||
|
||||
public List<InstItem> getInstList() {
|
||||
return instList;
|
||||
}
|
||||
|
||||
public void setInstList(List<InstItem> instList) {
|
||||
this.instList = instList;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 doiList 单项。
|
||||
*/
|
||||
public class DoiItem {
|
||||
|
||||
/** DOI 名称。 */
|
||||
private String name;
|
||||
|
||||
/** DOI 描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 起始序号。 */
|
||||
private int start;
|
||||
|
||||
/** 结束序号。 */
|
||||
private int end;
|
||||
|
||||
/** 单位。 */
|
||||
private String unit;
|
||||
|
||||
/** 系数。 */
|
||||
private float coefficient;
|
||||
|
||||
/** 基波标志。 */
|
||||
private int baseflag;
|
||||
|
||||
/** 基波数量。 */
|
||||
private int basecount;
|
||||
|
||||
/** ICD 实际序列数。 */
|
||||
private int icdcout;
|
||||
|
||||
/** SDI 列表。 */
|
||||
private List<SdiItem> sdiList = new ArrayList<SdiItem>();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(int end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public float getCoefficient() {
|
||||
return coefficient;
|
||||
}
|
||||
|
||||
public void setCoefficient(float coefficient) {
|
||||
this.coefficient = coefficient;
|
||||
}
|
||||
|
||||
public int getBaseflag() {
|
||||
return baseflag;
|
||||
}
|
||||
|
||||
public void setBaseflag(int baseflag) {
|
||||
this.baseflag = baseflag;
|
||||
}
|
||||
|
||||
public int getBasecount() {
|
||||
return basecount;
|
||||
}
|
||||
|
||||
public void setBasecount(int basecount) {
|
||||
this.basecount = basecount;
|
||||
}
|
||||
|
||||
public int getIcdcout() {
|
||||
return icdcout;
|
||||
}
|
||||
|
||||
public void setIcdcout(int icdcout) {
|
||||
this.icdcout = icdcout;
|
||||
}
|
||||
|
||||
public List<SdiItem> getSdiList() {
|
||||
return sdiList;
|
||||
}
|
||||
|
||||
public void setSdiList(List<SdiItem> sdiList) {
|
||||
this.sdiList = sdiList;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 instList 单项。
|
||||
*/
|
||||
public class InstItem {
|
||||
|
||||
/** lnInst。 */
|
||||
private String inst;
|
||||
|
||||
/** 该 inst 的描述。通常使用当前绑定的 label。 */
|
||||
private String desc;
|
||||
|
||||
/** DOI 列表。 */
|
||||
private List<DoiItem> doiList = new ArrayList<DoiItem>();
|
||||
|
||||
public String getInst() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
public void setInst(String inst) {
|
||||
this.inst = inst;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public List<DoiItem> getDoiList() {
|
||||
return doiList;
|
||||
}
|
||||
|
||||
public void setDoiList(List<DoiItem> doiList) {
|
||||
this.doiList = doiList;
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射文档。
|
||||
*
|
||||
* 关键说明:
|
||||
* 1. Java 字段统一使用 lowerCamelCase,避免 Jackson 同时输出 ied/IED 这类重复字段。
|
||||
* 2. JSON 输出名通过 @JsonProperty 显式指定,确保与原 C# 输出格式一致。
|
||||
*/
|
||||
public class MappingDocument {
|
||||
|
||||
@JsonProperty("version")
|
||||
private String version;
|
||||
|
||||
@JsonProperty("author")
|
||||
private String author;
|
||||
|
||||
@JsonProperty("IED")
|
||||
private String ied;
|
||||
|
||||
@JsonProperty("LD")
|
||||
private String ld;
|
||||
|
||||
/**
|
||||
* 原 C# mainFrom.txt 固定调用:
|
||||
* Set_WaveTimeFlag("BeiJing")
|
||||
*/
|
||||
@JsonProperty("WaveTimeFlag")
|
||||
private String waveTimeFlag;
|
||||
|
||||
/**
|
||||
* 原 C# mainFrom.txt 固定调用:
|
||||
* Set_DataType("1")
|
||||
*/
|
||||
@JsonProperty("DataType")
|
||||
private String dataType;
|
||||
|
||||
/**
|
||||
* 原 C# mainFrom.txt 固定调用:
|
||||
* Set_unit("1")
|
||||
*/
|
||||
@JsonProperty("unit")
|
||||
private String unit;
|
||||
|
||||
@JsonProperty("ReportMap")
|
||||
private List<ReportMapItem> reportMap = new ArrayList<ReportMapItem>();
|
||||
|
||||
@JsonProperty("DataSetList")
|
||||
private List<DataSetGroupItem> dataSetList = new ArrayList<DataSetGroupItem>();
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public String getIed() {
|
||||
return ied;
|
||||
}
|
||||
|
||||
public void setIed(String ied) {
|
||||
this.ied = ied;
|
||||
}
|
||||
|
||||
public String getLd() {
|
||||
return ld;
|
||||
}
|
||||
|
||||
public void setLd(String ld) {
|
||||
this.ld = ld;
|
||||
}
|
||||
|
||||
public String getWaveTimeFlag() {
|
||||
return waveTimeFlag;
|
||||
}
|
||||
|
||||
public void setWaveTimeFlag(String waveTimeFlag) {
|
||||
this.waveTimeFlag = waveTimeFlag;
|
||||
}
|
||||
|
||||
public String getDataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
public void setDataType(String dataType) {
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public List<ReportMapItem> getReportMap() {
|
||||
return reportMap;
|
||||
}
|
||||
|
||||
public void setReportMap(List<ReportMapItem> reportMap) {
|
||||
this.reportMap = reportMap;
|
||||
}
|
||||
|
||||
public List<DataSetGroupItem> getDataSetList() {
|
||||
return dataSetList;
|
||||
}
|
||||
|
||||
public void setDataSetList(List<DataSetGroupItem> dataSetList) {
|
||||
this.dataSetList = dataSetList;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* 最终映射中的 ReportMap 单项。
|
||||
*
|
||||
* 关键说明:
|
||||
* 1. 原 C# 不是把同组报告合并成一条,而是组内每个报告各自输出一条。
|
||||
* 2. 但 reportCount 仍然写该分组总数。
|
||||
* 3. buffered 不是 boolean,而是:
|
||||
* - true -> BR
|
||||
* - false -> RP
|
||||
*/
|
||||
public class ReportMapItem {
|
||||
|
||||
@JsonProperty("desc")
|
||||
private String desc;
|
||||
|
||||
@JsonProperty("reportCount")
|
||||
private int reportCount;
|
||||
|
||||
@JsonProperty("rptID")
|
||||
private String rptId;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("buffered")
|
||||
private String buffered;
|
||||
|
||||
@JsonProperty("inst")
|
||||
private String inst;
|
||||
|
||||
/**
|
||||
* 原 C# Set_FlickerFlag() 当前固定写 "0"
|
||||
*/
|
||||
@JsonProperty("FlickerFlag")
|
||||
private String flickerFlag;
|
||||
|
||||
/**
|
||||
* 原 C# 来自 DefaultCfg.ReportList.Select
|
||||
*/
|
||||
@JsonProperty("Select")
|
||||
private String select;
|
||||
|
||||
/**
|
||||
* 原 C# 来自 DefaultCfg.ReportList.TrgOps
|
||||
*/
|
||||
@JsonProperty("TrgOps")
|
||||
private String trgOps;
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public int getReportCount() {
|
||||
return reportCount;
|
||||
}
|
||||
|
||||
public void setReportCount(int reportCount) {
|
||||
this.reportCount = reportCount;
|
||||
}
|
||||
|
||||
public String getRptId() {
|
||||
return rptId;
|
||||
}
|
||||
|
||||
public void setRptId(String rptId) {
|
||||
this.rptId = rptId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getBuffered() {
|
||||
return buffered;
|
||||
}
|
||||
|
||||
public void setBuffered(String buffered) {
|
||||
this.buffered = buffered;
|
||||
}
|
||||
|
||||
public String getInst() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
public void setInst(String inst) {
|
||||
this.inst = inst;
|
||||
}
|
||||
|
||||
public String getFlickerFlag() {
|
||||
return flickerFlag;
|
||||
}
|
||||
|
||||
public void setFlickerFlag(String flickerFlag) {
|
||||
this.flickerFlag = flickerFlag;
|
||||
}
|
||||
|
||||
public String getSelect() {
|
||||
return select;
|
||||
}
|
||||
|
||||
public void setSelect(String select) {
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
public String getTrgOps() {
|
||||
return trgOps;
|
||||
}
|
||||
|
||||
public void setTrgOps(String trgOps) {
|
||||
this.trgOps = trgOps;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 sdiList 单项。
|
||||
*/
|
||||
public class SdiItem {
|
||||
|
||||
/** SDI 名称。 */
|
||||
private String name;
|
||||
|
||||
/** SDI 描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 类型列表。 */
|
||||
private List<TypeItem> typeList = new ArrayList<TypeItem>();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public List<TypeItem> getTypeList() {
|
||||
return typeList;
|
||||
}
|
||||
|
||||
public void setTypeList(List<TypeItem> typeList) {
|
||||
this.typeList = typeList;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.mapping;
|
||||
|
||||
/**
|
||||
* 最终映射中的 typeList 单项。
|
||||
*/
|
||||
public class TypeItem {
|
||||
|
||||
/** 类型名称。 */
|
||||
private String name;
|
||||
|
||||
/** 类型描述。 */
|
||||
private String desc;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.template;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
/**
|
||||
* 默认模板。
|
||||
* 默认模板模型。把 default-template.json 解析成可直接使用的对象。
|
||||
*
|
||||
* 当前只保留第一个接口真正会用到的部分。
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DefaultTemplate {
|
||||
|
||||
@JsonProperty("ReportList")
|
||||
private List<ReportCfgItem> reportList = new ArrayList<ReportCfgItem>();
|
||||
|
||||
@JsonProperty("LnClassList")
|
||||
private List<LnClassCfgItem> lnClassList = new ArrayList<LnClassCfgItem>();
|
||||
|
||||
@JsonProperty("PhaseList")
|
||||
private List<PhaseCfgItem> phaseList = new ArrayList<PhaseCfgItem>();
|
||||
|
||||
@JsonProperty("MultiplierList")
|
||||
private List<MultiplierCfgItem> multiplierList = new ArrayList<MultiplierCfgItem>();
|
||||
|
||||
@JsonProperty("UnitList")
|
||||
private List<UnitCfgItem> unitList = new ArrayList<UnitCfgItem>();
|
||||
|
||||
@JsonProperty("TypeList")
|
||||
private List<TypeCfgItem> typeList = new ArrayList<TypeCfgItem>();
|
||||
|
||||
@JsonProperty("DataObjectsList")
|
||||
private List<DataObjectCfgItem> dataObjectsList = new ArrayList<DataObjectCfgItem>();
|
||||
|
||||
/**
|
||||
* 基础校验。
|
||||
*
|
||||
* 返回问题列表;为空表示通过。
|
||||
*/
|
||||
public List<String> verify() {
|
||||
List<String> problems = new ArrayList<String>();
|
||||
ensureDuplicateNames("LnClassList", extractNames(lnClassList), problems);
|
||||
ensureDuplicateNames("PhaseList", extractNames(phaseList), problems);
|
||||
ensureDuplicateNames("MultiplierList", extractNames(multiplierList), problems);
|
||||
ensureDuplicateNames("UnitList", extractNames(unitList), problems);
|
||||
ensureDuplicateNames("TypeList", extractNames(typeList), problems);
|
||||
ensureDuplicateObjectNames(problems);
|
||||
if (reportList == null || reportList.isEmpty()) {
|
||||
problems.add("DefaultCfg.ReportList 为空");
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
|
||||
private List<String> extractNames(List<? extends NameListSupport> list) {
|
||||
List<String> names = new ArrayList<String>();
|
||||
if (list == null) {
|
||||
return names;
|
||||
}
|
||||
for (NameListSupport item : list) {
|
||||
if (item.getNameList() != null) {
|
||||
names.addAll(item.getNameList());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private void ensureDuplicateNames(String section, List<String> names, List<String> problems) {
|
||||
Set<String> set = new HashSet<String>();
|
||||
for (String name : names) {
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
String key = name.trim();
|
||||
if (!set.add(key)) {
|
||||
problems.add(section + " 中存在重复配置项:" + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureDuplicateObjectNames(List<String> problems) {
|
||||
if (dataObjectsList == null) {
|
||||
return;
|
||||
}
|
||||
for (DataObjectCfgItem dataObject : dataObjectsList) {
|
||||
List<String> names = new ArrayList<String>();
|
||||
if (dataObject.getObjectList() != null) {
|
||||
for (ObjectCfgItem object : dataObject.getObjectList()) {
|
||||
if (object.getNameList() != null) {
|
||||
names.addAll(object.getNameList());
|
||||
}
|
||||
}
|
||||
}
|
||||
ensureDuplicateNames("DataObjectsList[" + dataObject.getDesc() + "]", names, problems);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ReportCfgItem> getReportList() {
|
||||
return reportList;
|
||||
}
|
||||
|
||||
public void setReportList(List<ReportCfgItem> reportList) {
|
||||
this.reportList = reportList;
|
||||
}
|
||||
|
||||
public List<LnClassCfgItem> getLnClassList() {
|
||||
return lnClassList;
|
||||
}
|
||||
|
||||
public void setLnClassList(List<LnClassCfgItem> lnClassList) {
|
||||
this.lnClassList = lnClassList;
|
||||
}
|
||||
|
||||
public List<PhaseCfgItem> getPhaseList() {
|
||||
return phaseList;
|
||||
}
|
||||
|
||||
public void setPhaseList(List<PhaseCfgItem> phaseList) {
|
||||
this.phaseList = phaseList;
|
||||
}
|
||||
|
||||
public List<MultiplierCfgItem> getMultiplierList() {
|
||||
return multiplierList;
|
||||
}
|
||||
|
||||
public void setMultiplierList(List<MultiplierCfgItem> multiplierList) {
|
||||
this.multiplierList = multiplierList;
|
||||
}
|
||||
|
||||
public List<UnitCfgItem> getUnitList() {
|
||||
return unitList;
|
||||
}
|
||||
|
||||
public void setUnitList(List<UnitCfgItem> unitList) {
|
||||
this.unitList = unitList;
|
||||
}
|
||||
|
||||
public List<TypeCfgItem> getTypeList() {
|
||||
return typeList;
|
||||
}
|
||||
|
||||
public void setTypeList(List<TypeCfgItem> typeList) {
|
||||
this.typeList = typeList;
|
||||
}
|
||||
|
||||
public List<DataObjectCfgItem> getDataObjectsList() {
|
||||
return dataObjectsList;
|
||||
}
|
||||
|
||||
public void setDataObjectsList(List<DataObjectCfgItem> dataObjectsList) {
|
||||
this.dataObjectsList = dataObjectsList;
|
||||
}
|
||||
|
||||
/** 统一抽象:凡是有 nameList 的配置项都实现它。 */
|
||||
public interface NameListSupport {
|
||||
List<String> getNameList();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ReportCfgItem {
|
||||
|
||||
/**
|
||||
* 报告分组描述,例如:统计数据、实时数据、波动闪变
|
||||
*/
|
||||
@JsonProperty("desc")
|
||||
private String desc;
|
||||
|
||||
/**
|
||||
* 报告 inst,例如:01
|
||||
*/
|
||||
@JsonProperty("inst")
|
||||
private String inst;
|
||||
|
||||
/**
|
||||
* 原始配置中的 TrgOps。
|
||||
* 例如:40 / 96
|
||||
*
|
||||
* 这里必须显式加 @JsonProperty("TrgOps"),
|
||||
* 否则在当前项目里很容易反序列化后为 null。
|
||||
*/
|
||||
@JsonProperty("TrgOps")
|
||||
private String trgOps;
|
||||
|
||||
/**
|
||||
* 原始配置中的 Select。
|
||||
* 例如:DataStatFileMap / DataRealFileMap / FlickerFileMap
|
||||
*
|
||||
* 这里必须显式加 @JsonProperty("Select"),
|
||||
* 否则在当前项目里很容易反序列化后为 null。
|
||||
*/
|
||||
@JsonProperty("Select")
|
||||
private String select;
|
||||
|
||||
/**
|
||||
* 该分组覆盖的数据集名称列表
|
||||
*/
|
||||
@JsonProperty("DataSetList")
|
||||
private List<String> dataSetList = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* 该分组可配置的标签模板列表
|
||||
*/
|
||||
@JsonProperty("LnInstList")
|
||||
private List<String> lnInstList = new ArrayList<String>();
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getInst() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
public void setInst(String inst) {
|
||||
this.inst = inst;
|
||||
}
|
||||
|
||||
public String getTrgOps() {
|
||||
return trgOps;
|
||||
}
|
||||
|
||||
public void setTrgOps(String trgOps) {
|
||||
this.trgOps = trgOps;
|
||||
}
|
||||
|
||||
public String getSelect() {
|
||||
return select;
|
||||
}
|
||||
|
||||
public void setSelect(String select) {
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
public List<String> getDataSetList() {
|
||||
return dataSetList;
|
||||
}
|
||||
|
||||
public void setDataSetList(List<String> dataSetList) {
|
||||
this.dataSetList = dataSetList;
|
||||
}
|
||||
|
||||
public List<String> getLnInstList() {
|
||||
return lnInstList;
|
||||
}
|
||||
|
||||
public void setLnInstList(List<String> lnInstList) {
|
||||
this.lnInstList = lnInstList;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class LnClassCfgItem implements NameListSupport {
|
||||
private String desc;
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
public String getDesc() { return desc; }
|
||||
public void setDesc(String desc) { this.desc = desc; }
|
||||
public List<String> getNameList() { return nameList; }
|
||||
public void setNameList(List<String> nameList) { this.nameList = nameList; }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class PhaseCfgItem implements NameListSupport {
|
||||
private String desc;
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
public String getDesc() { return desc; }
|
||||
public void setDesc(String desc) { this.desc = desc; }
|
||||
public List<String> getNameList() { return nameList; }
|
||||
public void setNameList(List<String> nameList) { this.nameList = nameList; }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class MultiplierCfgItem implements NameListSupport {
|
||||
private int multiplier;
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
public int getMultiplier() { return multiplier; }
|
||||
public void setMultiplier(int multiplier) { this.multiplier = multiplier; }
|
||||
public List<String> getNameList() { return nameList; }
|
||||
public void setNameList(List<String> nameList) { this.nameList = nameList; }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class UnitCfgItem implements NameListSupport {
|
||||
private String desc;
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
public String getDesc() { return desc; }
|
||||
public void setDesc(String desc) { this.desc = desc; }
|
||||
public List<String> getNameList() { return nameList; }
|
||||
public void setNameList(List<String> nameList) { this.nameList = nameList; }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class TypeCfgItem implements NameListSupport {
|
||||
private String desc;
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
public String getDesc() { return desc; }
|
||||
public void setDesc(String desc) { this.desc = desc; }
|
||||
public List<String> getNameList() { return nameList; }
|
||||
public void setNameList(List<String> nameList) { this.nameList = nameList; }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class DataObjectCfgItem {
|
||||
private String desc;
|
||||
@JsonProperty("LnInstList")
|
||||
private List<String> lnInstList = new ArrayList<String>();
|
||||
@JsonProperty("ObjectList")
|
||||
private List<ObjectCfgItem> objectList = new ArrayList<ObjectCfgItem>();
|
||||
public String getDesc() { return desc; }
|
||||
public void setDesc(String desc) { this.desc = desc; }
|
||||
public List<String> getLnInstList() { return lnInstList; }
|
||||
public void setLnInstList(List<String> lnInstList) { this.lnInstList = lnInstList; }
|
||||
public List<ObjectCfgItem> getObjectList() { return objectList; }
|
||||
public void setObjectList(List<ObjectCfgItem> objectList) { this.objectList = objectList; }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ObjectCfgItem implements NameListSupport {
|
||||
private String desc;
|
||||
private int baseflag;
|
||||
private int basecount;
|
||||
private int queuecount;
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
private List<String> queueList = new ArrayList<String>();
|
||||
public String getDesc() { return desc; }
|
||||
public void setDesc(String desc) { this.desc = desc; }
|
||||
public int getBaseflag() { return baseflag; }
|
||||
public void setBaseflag(int baseflag) { this.baseflag = baseflag; }
|
||||
public int getBasecount() { return basecount; }
|
||||
public void setBasecount(int basecount) { this.basecount = basecount; }
|
||||
public int getQueuecount() { return queuecount; }
|
||||
public void setQueuecount(int queuecount) { this.queuecount = queuecount; }
|
||||
public List<String> getNameList() { return nameList; }
|
||||
public void setNameList(List<String> nameList) { this.nameList = nameList; }
|
||||
public List<String> getQueueList() { return queueList; }
|
||||
public void setQueueList(List<String> queueList) { this.queueList = queueList; }
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,29 @@
|
||||
package com.njcn.gather.icd.mapping.infrastructure.parser;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.*;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.*;
|
||||
import com.njcn.gather.icd.mapping.infrastructure.parser.generated.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JAXB generated 模型读取器。
|
||||
* generated 模型读取器。遍历 SCL/IED/LDevice/LN0/LN/ReportControl/DataSet/DOI 树并转换。
|
||||
* JAXB 生成模型读取器。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 这里只负责“把 generated 模型读取出来”。
|
||||
* 2. 不直接做接口编排,也不做 JSON 序列化。
|
||||
* 负责遍历 `SCL/IED/LDevice/LN0/LN/ReportControl/DataSet/DOI` 结构,
|
||||
* 并转换为模块内部使用的 ICD 领域模型。
|
||||
*/
|
||||
public class SclGeneratedModelReader {
|
||||
|
||||
/**
|
||||
* 从 JAXB SCL 根对象中读取当前模块需要的 ICD 结构。
|
||||
*/
|
||||
public IcdDocument read(SCL scl, String fileName) {
|
||||
IcdDocument document = new IcdDocument();
|
||||
document.setFileName(fileName);
|
||||
|
||||
// 当前业务只处理第一个可用的非 LD0/PQLD0 逻辑设备。
|
||||
TIED targetIed = null;
|
||||
TLDevice targetDevice = null;
|
||||
|
||||
@@ -82,15 +85,16 @@ public class SclGeneratedModelReader {
|
||||
document.getLogicalNodes().add(readLogicalNode(ln, false));
|
||||
}
|
||||
}
|
||||
// 关键修正:
|
||||
// C# 原版的 icdcout 优先来自 DataTypeTemplates -> LNodeType -> DO -> DOType -> DA.count,
|
||||
// 不是靠 DataSet/FCDA 重复数来反推。
|
||||
// 这里改成:先按模板 count 回填 DOI.sequenceCount,找不到再退回 FCDA。
|
||||
// `icdcout` 优先取自模板链路 `DataTypeTemplates -> LNodeType -> DO -> DOType -> DA.count`,
|
||||
// 只有模板未提供时才退回到 `DataSet/FCDA` 反查,保持与原 C# 行为一致。
|
||||
syncDoiSequenceCount(scl, document);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 LN0 或 LN 中读取 DataSet 与 ReportControl 信息。
|
||||
*/
|
||||
private void readReportAndDataSetFromAnyLn(TAnyLN anyLn, IcdDocument document) {
|
||||
List<FcdaNode> allFcdas = new ArrayList<FcdaNode>();
|
||||
if (anyLn.getDataSet() != null) {
|
||||
@@ -127,6 +131,9 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 ReportControl.TrgOps 转成后续模板使用的触发编码。
|
||||
*/
|
||||
private String readTrgOps(TReportControl reportControl) {
|
||||
if (reportControl.getTrgOps() == null) {
|
||||
return null;
|
||||
@@ -146,24 +153,22 @@ public class SclGeneratedModelReader {
|
||||
/**
|
||||
* 读取逻辑节点。
|
||||
*
|
||||
* 注意:
|
||||
* 当前这套 JAXB 生成类中,TAnyLN 只提供了通用能力,
|
||||
* prefix / lnClass / inst 这些属性实际定义在子类 TLN / TLN0 上,
|
||||
* 所以这里不能直接对 TAnyLN 调用 getPrefix()/getLnClass()/getInst()。
|
||||
* `TAnyLN` 只暴露通用能力,`prefix / lnClass / inst` 实际定义在 `TLN / TLN0` 子类上,
|
||||
* 因此这里需要按具体类型分别读取。
|
||||
*/
|
||||
private LnNode readLogicalNode(TAnyLN anyLn, boolean isLn0) {
|
||||
LnNode node = new LnNode();
|
||||
node.setLn0(isLn0);
|
||||
|
||||
// lnType 在 TAnyLN 上是存在的,可以直接读取
|
||||
// `lnType` 定义在 `TAnyLN` 上,可直接读取。
|
||||
node.setLnType(anyLn.getLnType());
|
||||
|
||||
// prefix / lnClass / inst 需要根据具体子类读取
|
||||
// `prefix / lnClass / inst` 需要根据具体子类读取。
|
||||
node.setPrefix(resolveLnPrefix(anyLn));
|
||||
node.setLnClass(resolveLnClass(anyLn));
|
||||
node.setLnInst(resolveLnInst(anyLn));
|
||||
|
||||
// DOI 是定义在 TAnyLN 上的,可以直接读取
|
||||
// `DOI` 定义在 `TAnyLN` 上,可直接读取。
|
||||
if (anyLn.getDOI() != null) {
|
||||
for (TDOI doi : anyLn.getDOI()) {
|
||||
DoiNode doiNode = new DoiNode();
|
||||
@@ -188,10 +193,9 @@ public class SclGeneratedModelReader {
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* 解析逻辑节点 prefix。
|
||||
* 解析逻辑节点的 `prefix`。
|
||||
*
|
||||
* TLN 有 prefix;
|
||||
* TLN0 按这套生成类没有单独 prefix 字段,返回空字符串即可。
|
||||
* `TLN` 有 `prefix` 字段,`TLN0` 在当前生成模型中没有单独字段,统一返回空字符串。
|
||||
*/
|
||||
private String resolveLnPrefix(TAnyLN anyLn) {
|
||||
if (anyLn instanceof TLN) {
|
||||
@@ -201,11 +205,9 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析逻辑节点 lnClass。
|
||||
* 解析逻辑节点的 `lnClass`。
|
||||
*
|
||||
* 当前生成类里:
|
||||
* - TLN.getLnClass() 返回 List<String>
|
||||
* - TLN0.getLnClass() 返回 List<String>
|
||||
* 当前生成模型中,`TLN` 和 `TLN0` 都返回 `List<String>`,这里只取首个值。
|
||||
*/
|
||||
private String resolveLnClass(TAnyLN anyLn) {
|
||||
if (anyLn instanceof TLN) {
|
||||
@@ -218,11 +220,9 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析逻辑节点 inst。
|
||||
* 解析逻辑节点的 `inst`。
|
||||
*
|
||||
* 当前生成类里:
|
||||
* - TLN.getInst() 存在
|
||||
* - TLN0.getInst() 也存在
|
||||
* 当前生成模型中,`TLN` 和 `TLN0` 都提供了该字段。
|
||||
*/
|
||||
private String resolveLnInst(TAnyLN anyLn) {
|
||||
if (anyLn instanceof TLN) {
|
||||
@@ -234,6 +234,9 @@ public class SclGeneratedModelReader {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归读取 DOI 下的 SDI/DAI 混合子节点。
|
||||
*/
|
||||
private DoiElementNode readUnNamingNode(TUnNaming source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
@@ -272,6 +275,9 @@ public class SclGeneratedModelReader {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JAXB FCDA 节点转换为内部 FCDA 模型。
|
||||
*/
|
||||
private FcdaNode toFcdaNode(TFCDA fcda) {
|
||||
FcdaNode node = new FcdaNode();
|
||||
node.setLdInst(fcda.getLdInst());
|
||||
@@ -285,10 +291,16 @@ public class SclGeneratedModelReader {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 JAXB 列表型字段的首个值。
|
||||
*/
|
||||
private String first(List<String> values) {
|
||||
return values == null || values.isEmpty() ? null : values.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 LD 实例名中提取非数字前缀,例如 PQM1 -> PQM。
|
||||
*/
|
||||
private String extractLdPrefix(String ldInst) {
|
||||
if (ldInst == null) {
|
||||
return null;
|
||||
@@ -305,19 +317,16 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 DOI 的 sequenceCount 同步出来。
|
||||
* 回填 DOI 的 `sequenceCount`。
|
||||
*
|
||||
* 规则严格贴近原 C#:
|
||||
* 1. 优先从 DataTypeTemplates -> LNodeType -> DO -> DOType -> DA.count 取值;
|
||||
* 2. 如果模板里没有 count,再退回 DataSet/FCDA 反查;
|
||||
* 3. 这样后续 MappingGenerationService 里的 icdcout 才会和 C# 一致。
|
||||
* 优先按模板链路取值,模板缺失时再退回 `DataSet/FCDA` 反查,确保后续生成结果与原 C# 逻辑一致。
|
||||
*/
|
||||
private void syncDoiSequenceCount(SCL scl, IcdDocument document) {
|
||||
if (document == null || document.getLogicalNodes() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 先把 “lnType + doName -> count” 建好缓存
|
||||
// 先构建 `lnType + doName -> count` 缓存。
|
||||
Map<String, Integer> templateSequenceCountMap = buildTemplateSequenceCountMap(scl);
|
||||
|
||||
for (LnNode lnNode : document.getLogicalNodes()) {
|
||||
@@ -338,23 +347,17 @@ public class SclGeneratedModelReader {
|
||||
|
||||
int fcdaCount = findDoiSequenceCountFromFcda(document, doiNode);
|
||||
|
||||
// 关键:优先使用模板 count,保持与 C# 原版一致
|
||||
// 优先使用模板中的 `count`,保持与原 C# 行为一致。
|
||||
doiNode.setSequenceCount(templateCount > 0 ? templateCount : fcdaCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 “lnType + doName -> 序列数量” 缓存。
|
||||
* 构建 `lnType + doName -> 序列数量` 缓存。
|
||||
*
|
||||
* 来源:
|
||||
* DataTypeTemplates
|
||||
* -> LNodeType(id = lnType)
|
||||
* -> DO(name/type)
|
||||
* -> DOType(id = type)
|
||||
* -> DA.count
|
||||
*
|
||||
* C# 原版本质上就是沿这条链把 doi.tNUM 算出来。
|
||||
* 数据来源为 `DataTypeTemplates -> LNodeType -> DO -> DOType -> DA.count`,
|
||||
* 原 C# 版本也是沿着这条链路计算 `doi.tNUM`。
|
||||
*/
|
||||
private Map<String, Integer> buildTemplateSequenceCountMap(SCL scl) {
|
||||
Map<String, Integer> result = new LinkedHashMap<String, Integer>();
|
||||
@@ -367,7 +370,7 @@ public class SclGeneratedModelReader {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 1. 先建 DOType.id -> count
|
||||
// 1. 先构建 `DOType.id -> count`。
|
||||
Map<String, Integer> doTypeCountMap = new LinkedHashMap<String, Integer>();
|
||||
for (TDOType doType : templates.getDOType()) {
|
||||
if (doType == null || doType.getId() == null) {
|
||||
@@ -378,7 +381,7 @@ public class SclGeneratedModelReader {
|
||||
doTypeCountMap.put(doType.getId(), count);
|
||||
}
|
||||
|
||||
// 2. 再建 lnType + doName -> count
|
||||
// 2. 再构建 `lnType + doName -> count`。
|
||||
for (TLNodeType lNodeType : templates.getLNodeType()) {
|
||||
if (lNodeType == null || lNodeType.getId() == null || lNodeType.getDO() == null) {
|
||||
continue;
|
||||
@@ -402,10 +405,9 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个 DOType 中提取序列数量。
|
||||
* 从单个 `DOType` 中提取序列数量。
|
||||
*
|
||||
* C# 原版用的是 DOType 下 DA.count。
|
||||
* 这里取所有顶层 DA 里最大的正整数 count。
|
||||
* 这里取顶层 `DA.count` 中最大的正整数,贴近原 C# 的处理方式。
|
||||
*/
|
||||
private int extractDoTypeSequenceCount(TDOType doType) {
|
||||
int max = 0;
|
||||
@@ -429,10 +431,9 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 DA.count。
|
||||
* 解析 `DA.count`。
|
||||
*
|
||||
* JAXB 这套生成类把 count 生成为 List<String>,
|
||||
* 所以这里要做一次安全转换。
|
||||
* 当前 JAXB 生成类把该字段定义为 `List<String>`,这里统一做安全转换。
|
||||
*/
|
||||
private int parseDaCount(TAbstractDataAttribute dataAttribute) {
|
||||
if (dataAttribute == null || dataAttribute.getCount() == null || dataAttribute.getCount().isEmpty()) {
|
||||
@@ -456,16 +457,16 @@ public class SclGeneratedModelReader {
|
||||
max = value;
|
||||
}
|
||||
} catch (NumberFormatException ignore) {
|
||||
// 非法 count 直接忽略,保持容错
|
||||
// 非法 `count` 直接忽略,保持容错。
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退回到 DataSet/FCDA 的 sequenceCount 反查。
|
||||
* 从 `DataSet/FCDA` 反查 `sequenceCount`。
|
||||
*
|
||||
* 这里只作为模板 count 找不到时的兜底逻辑。
|
||||
* 这里只在模板未提供 `count` 时作为兜底逻辑使用。
|
||||
*/
|
||||
private int findDoiSequenceCountFromFcda(IcdDocument document, DoiNode doiNode) {
|
||||
int max = 0;
|
||||
@@ -501,6 +502,9 @@ public class SclGeneratedModelReader {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造模板序列数量缓存使用的复合 key。
|
||||
*/
|
||||
private String buildLnTypeDoKey(String lnType, String doName) {
|
||||
String left = lnType == null ? "" : lnType.trim();
|
||||
String right = doName == null ? "" : doName.trim();
|
||||
@@ -508,7 +512,7 @@ public class SclGeneratedModelReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 空安全字符串比较。
|
||||
* 空安全的字符串比较。
|
||||
*/
|
||||
private boolean equalsTrim(String left, String right) {
|
||||
if (left == null && right == null) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.njcn.gather.icd.mapping.infrastructure.parser;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.infrastructure.parser.generated.SCL;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -11,18 +11,18 @@ import java.io.ByteArrayInputStream;
|
||||
|
||||
/**
|
||||
* SCL 解析适配器。
|
||||
* JAXB 解析适配器。负责把 ICD XML 反序列化为 SCL 根对象,再转成内部模型。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 这是真正会用到的 JAXB 版解析入口。
|
||||
* 2. 这里只负责把 ICD XML 反序列化成 SCL 根对象,再交给 reader 转成内部模型。
|
||||
* 3. 业务层不会直接依赖 JAXB generated 类。
|
||||
* 负责把 ICD XML 反序列化为 SCL 根对象,再交给读取器转换为内部模型。
|
||||
*/
|
||||
@Component
|
||||
public class SclParserAdapter {
|
||||
|
||||
/** JAXB generated 模型读取器,负责转成内部 ICD 领域模型。 */
|
||||
private final SclGeneratedModelReader modelReader = new SclGeneratedModelReader();
|
||||
|
||||
/**
|
||||
* 解析 ICD/SCL XML 字节内容。
|
||||
*/
|
||||
public IcdDocument parse(byte[] content, String fileName) {
|
||||
if (content == null || content.length == 0) {
|
||||
throw new IllegalArgumentException("ICD 文件内容不能为空");
|
||||
@@ -38,6 +38,9 @@ public class SclParserAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 JAXB 直接返回 SCL 或 JAXBElement<SCL> 两种根对象形式。
|
||||
*/
|
||||
private SCL resolveSclRoot(Object raw) {
|
||||
if (raw instanceof SCL) {
|
||||
return (SCL) raw;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package com.njcn.gather.icd.mapping.infrastructure.parser;
|
||||
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.DoiElementNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.DoiNode;
|
||||
import com.njcn.gather.icd.mapping.domain.model.icd.FcdaNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.DoiElementNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.DoiNode;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.FcdaNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SCL 遍历辅助工具。
|
||||
*
|
||||
* 提供序列数量统计和 DOI 叶子节点展开等公共能力。
|
||||
*/
|
||||
public final class SclTraversalSupport {
|
||||
|
||||
@@ -16,7 +18,7 @@ public final class SclTraversalSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计同一个数据对象在同数据集里出现的次数,作为 ICD 实际序列个数参考。
|
||||
* 统计同一数据对象在数据集中的出现次数,作为 ICD 实际序列数量的参考值。
|
||||
*/
|
||||
public static int calculateSequenceCount(List<FcdaNode> allFcdas, FcdaNode current) {
|
||||
int count = 0;
|
||||
@@ -30,6 +32,9 @@ public final class SclTraversalSupport {
|
||||
return count <= 1 ? 0 : count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 展开 DOI 树下所有 DAI 节点,便于后续读取单位、倍率等叶子值。
|
||||
*/
|
||||
public static List<DoiElementNode> flattenLeafDai(DoiNode doi) {
|
||||
List<DoiElementNode> result = new ArrayList<DoiElementNode>();
|
||||
if (doi == null || doi.getChildren() == null) {
|
||||
@@ -41,6 +46,9 @@ public final class SclTraversalSupport {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归收集 DAI 叶子节点。
|
||||
*/
|
||||
private static void collectLeafDai(DoiElementNode node, List<DoiElementNode> result) {
|
||||
if (node == null) {
|
||||
return;
|
||||
@@ -55,6 +63,9 @@ public final class SclTraversalSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空安全字符串比较,供解析阶段统计序列数量使用。
|
||||
*/
|
||||
public static boolean safeEquals(String a, String b) {
|
||||
return a == null ? b == null : a.equals(b);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 应用层返回对象。
|
||||
*
|
||||
* 统一封装成功、需选择索引、失败三类结果。
|
||||
*/
|
||||
@Data
|
||||
public class GenerateMappingResult {
|
||||
/** 本次生成流程状态。 */
|
||||
private GenerateStatus status;
|
||||
|
||||
/** 给前端或调用方展示的处理结果说明。 */
|
||||
private String message;
|
||||
|
||||
/** ICD 中解析到的 IED 名称。 */
|
||||
private String iedName;
|
||||
|
||||
/** ICD 中解析到的 LD 实例名。 */
|
||||
private String ldInst;
|
||||
|
||||
/** 前端可编辑的 ICD 解析结果。 */
|
||||
private IcdDocument icdDocument;
|
||||
|
||||
/** 需要人工绑定索引时返回的候选分析结果。 */
|
||||
private IndexAnalysis indexAnalysis;
|
||||
|
||||
/** 生成成功后的结构化映射文档。 */
|
||||
private MappingDocument mappingDocument;
|
||||
|
||||
/** 生成成功后的 JSON 字符串。 */
|
||||
private String mappingJson;
|
||||
|
||||
/** saveToDisk=true 时的文件保存路径。 */
|
||||
private String savedPath;
|
||||
|
||||
/** 解析、校验或生成过程中收集到的问题。 */
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.gather.icd.mapping.domain.model.analysis;
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.analysis;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -9,12 +10,11 @@ import java.util.List;
|
||||
*
|
||||
* key = reportName
|
||||
*/
|
||||
@Data
|
||||
public class IndexAnalysis {
|
||||
/** 按 DefaultCfg.ReportList 分组后的索引候选。 */
|
||||
private List<IndexCandidate> candidates = new ArrayList<IndexCandidate>();
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
|
||||
public List<IndexCandidate> getCandidates() { return candidates; }
|
||||
public void setCandidates(List<IndexCandidate> candidates) { this.candidates = candidates; }
|
||||
public List<String> getProblems() { return problems; }
|
||||
public void setProblems(List<String> problems) { this.problems = problems; }
|
||||
/** 分析过程中发现但不一定阻断流程的问题。 */
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.analysis;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 索引候选分组。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 一条候选对应一个业务分组,例如:统计数据、实时数据;
|
||||
* 2. 一个业务分组下可以包含多个报告;
|
||||
* 3. 这里不仅保存返回给前端的候选项,也保存从 DefaultCfg.ReportList 带下来的配置项,
|
||||
* 供后续 MappingGenerationService 直接使用,避免“二次查模板”失败。
|
||||
*/
|
||||
@Data
|
||||
public class IndexCandidate {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 该分组下实际匹配到的报告数量。 */
|
||||
private int reportCount;
|
||||
|
||||
/** DefaultCfg.txt 中该分组可用的标签模板。 */
|
||||
private List<String> templateLabels = new ArrayList<String>();
|
||||
|
||||
/** 当前分组下匹配到的报告列表。 */
|
||||
private List<IndexCandidateReportItem> reports = new ArrayList<IndexCandidateReportItem>();
|
||||
|
||||
/**
|
||||
* DefaultCfg.ReportList.inst
|
||||
* 例如:01 / 02 / 03 / 04
|
||||
*/
|
||||
private String reportInst;
|
||||
|
||||
/**
|
||||
* DefaultCfg.ReportList.Select
|
||||
* 例如:DataStatFileMap / DataRealFileMap / FlickerFileMap
|
||||
*/
|
||||
private String select;
|
||||
|
||||
/**
|
||||
* DefaultCfg.ReportList.TrgOps
|
||||
* 例如:40 / 96
|
||||
*/
|
||||
private String trgOps;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.analysis;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 候选分组下的单个报告项。
|
||||
*
|
||||
* 这个对象专门给前端展示“这个分组下面有哪些报告,以及每个报告对应哪些可用 lnInst 数字”。
|
||||
*/
|
||||
@Data
|
||||
public class IndexCandidateReportItem {
|
||||
|
||||
/** 报告名称。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名称。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 报告中文描述。一般和分组描述相同,保留它是为了前端渲染更灵活。 */
|
||||
private String reportDesc;
|
||||
|
||||
/** 当前报告在 ICD 中解析出来的所有可选 lnInst 数字。 */
|
||||
private List<String> availableLnInstValues = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.analysis;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 校验结果。
|
||||
* 索引校验结果模型。保存是否通过以及问题列表。
|
||||
*/
|
||||
@Data
|
||||
public class ValidationResult {
|
||||
/** 是否通过索引绑定校验。 */
|
||||
private boolean valid;
|
||||
|
||||
/** 未通过校验时的具体问题列表。 */
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DataSet 节点。
|
||||
* 数据集模型。用于承接 DataSet 下的 FCDA 列表。
|
||||
*/
|
||||
@Data
|
||||
public class DataSetNode {
|
||||
/** DataSet 名称。 */
|
||||
private String name;
|
||||
|
||||
/** DataSet 下的 FCDA 引用列表。 */
|
||||
private List<FcdaNode> fcdas = new ArrayList<FcdaNode>();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DOI/SDI/DAI 统一节点。
|
||||
* DOI/SDI/DAI 细项模型。用于递归承接 DOI 树明细。
|
||||
*
|
||||
* kind 常见值:
|
||||
* - SDI
|
||||
* - DAI
|
||||
*/
|
||||
@Data
|
||||
public class DoiElementNode {
|
||||
/** 节点类型,当前主要为 SDI 或 DAI。 */
|
||||
private String kind;
|
||||
|
||||
/** SDI/DAI 名称。 */
|
||||
private String name;
|
||||
|
||||
/** SDI/DAI 自带的 ix 序号。 */
|
||||
private Long ix;
|
||||
|
||||
/** DAI 节点下的 Val 文本值。 */
|
||||
private List<String> values = new ArrayList<String>();
|
||||
|
||||
/** SDI 节点下继续嵌套的子节点。 */
|
||||
private List<DoiElementNode> children = new ArrayList<DoiElementNode>();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DOI 节点。
|
||||
* DOI 模型。表示逻辑节点下的一个数据对象节点。
|
||||
*/
|
||||
@Data
|
||||
public class DoiNode {
|
||||
/** DOI 名称,例如 PhV、Hz。 */
|
||||
private String name;
|
||||
|
||||
/** DOI 自带的 ix 序号。 */
|
||||
private Long ix;
|
||||
|
||||
/** DOI 所属 LN 的 lnClass。 */
|
||||
private String lnClass;
|
||||
|
||||
/** DOI 所属 LN 的 lnInst。 */
|
||||
private String lnInst;
|
||||
|
||||
/** 从模板或 FCDA 反查得到的实际序列数量。 */
|
||||
private int sequenceCount;
|
||||
|
||||
/** DOI 下的 SDI/DAI 子节点树。 */
|
||||
private List<DoiElementNode> children = new ArrayList<DoiElementNode>();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* FCDA 节点。
|
||||
* FCDA 模型。保存 lnClass、lnInst、doName、daName、fc、ix 等信息。
|
||||
*/
|
||||
@Data
|
||||
public class FcdaNode {
|
||||
/** FCDA.ldInst。 */
|
||||
private String ldInst;
|
||||
|
||||
/** FCDA.prefix。 */
|
||||
private String prefix;
|
||||
|
||||
/** FCDA.lnClass。 */
|
||||
private String lnClass;
|
||||
|
||||
/** FCDA.lnInst。 */
|
||||
private String lnInst;
|
||||
|
||||
/** FCDA.doName,对应 DOI 名称。 */
|
||||
private String doName;
|
||||
|
||||
/** FCDA.daName,对应 DAI 名称。 */
|
||||
private String daName;
|
||||
|
||||
/** FCDA.fc 功能约束。 */
|
||||
private String fc;
|
||||
|
||||
/** FCDA.ix 序号。 */
|
||||
private Long ix;
|
||||
|
||||
/** 同一数据对象在数据集中的序列数量。 */
|
||||
private int sequenceCount;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ICD 文档聚合根。
|
||||
* ICD 统一领域模型的聚合根。承接 IED、LDevice、ReportControl、DataSet、LN 等解析结果。
|
||||
*
|
||||
* 说明:
|
||||
* 1. 这是系统内部统一的 ICD 模型。
|
||||
* 2. 外部 JAXB generated 类只在 parser 层使用。
|
||||
* 3. 业务层全部依赖这个标准化模型,便于后续替换解析实现。
|
||||
*/
|
||||
@Data
|
||||
public class IcdDocument {
|
||||
/** 原始 ICD 文件名。 */
|
||||
private String fileName;
|
||||
|
||||
/** 当前选中的 IED 名称。 */
|
||||
private String iedName;
|
||||
|
||||
/** 当前选中的逻辑设备实例名。 */
|
||||
private String ldInst;
|
||||
|
||||
/** LD 输出前缀,通常去掉实例名尾部数字。 */
|
||||
private String ldPrefix;
|
||||
|
||||
/** 当前解析到的主 IED 节点。 */
|
||||
private IedNode primaryIed;
|
||||
|
||||
/** 当前参与映射生成的逻辑设备。 */
|
||||
private LogicalDeviceNode logicalDevice;
|
||||
|
||||
/** 当前逻辑设备下的 LN0 和 LN 列表。 */
|
||||
private List<LnNode> logicalNodes = new ArrayList<LnNode>();
|
||||
|
||||
/** 当前逻辑设备下的报告控制块列表。 */
|
||||
private List<ReportControlNode> reportControls = new ArrayList<ReportControlNode>();
|
||||
|
||||
/** 当前逻辑设备下的数据集,key 为 DataSet 名称。 */
|
||||
private Map<String, DataSetNode> dataSets = new LinkedHashMap<String, DataSetNode>();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IED 节点。
|
||||
* IED 节点模型。保存 IED 名称以及其下逻辑设备引用。
|
||||
*/
|
||||
@Data
|
||||
public class IedNode {
|
||||
/** IED 名称。 */
|
||||
private String name;
|
||||
|
||||
/** IED 下的 AccessPoint 名称列表。 */
|
||||
private List<String> accessPointNames = new ArrayList<String>();
|
||||
|
||||
/** IED 下的逻辑设备实例名列表。 */
|
||||
private List<String> lDeviceInstList = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 逻辑节点。
|
||||
* 逻辑节点模型。保存 lnClass、lnInst、prefix、DOI 等信息。
|
||||
*
|
||||
* LN0 和 LN 最终都统一抽象为这个模型。
|
||||
*/
|
||||
@Data
|
||||
public class LnNode {
|
||||
/** 是否来自 LN0 节点。 */
|
||||
private boolean ln0;
|
||||
|
||||
/** LN 前缀。 */
|
||||
private String prefix;
|
||||
|
||||
/** LN 类型类别,例如 MMXU、MHAI。 */
|
||||
private String lnClass;
|
||||
|
||||
/** LN 实例号,用于和前端选择的数字索引绑定。 */
|
||||
private String lnInst;
|
||||
|
||||
/** LN 类型模板 ID,用于反查 DataTypeTemplates。 */
|
||||
private String lnType;
|
||||
|
||||
/** LN 下解析出的 DOI 列表。 */
|
||||
private List<DoiNode> doiList = new ArrayList<DoiNode>();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 逻辑设备节点。
|
||||
* 逻辑设备模型。保存 inst、prefix 以及其下 LN/LN0 列表。
|
||||
*/
|
||||
@Data
|
||||
public class LogicalDeviceNode {
|
||||
/** LDevice.inst。 */
|
||||
private String inst;
|
||||
|
||||
/** LDevice.ldName。 */
|
||||
private String ldName;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.icd;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* ReportControl 节点。
|
||||
* 报告控制块模型。用于保存报告名称、关联 DataSet、缓冲属性等。
|
||||
*/
|
||||
@Data
|
||||
public class ReportControlNode {
|
||||
/** ReportControl.name。 */
|
||||
private String name;
|
||||
|
||||
/** ReportControl.rptID。 */
|
||||
private String rptId;
|
||||
|
||||
/** 是否为缓存报告,后续会转换为 BR/RP。 */
|
||||
private boolean buffered;
|
||||
|
||||
/** 当前报告关联的 DataSet 名称。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 解析后的触发选项编码。 */
|
||||
private String trgOps;
|
||||
|
||||
/** ReportControl.confRev。 */
|
||||
private String confRev;
|
||||
|
||||
/** 兼容现有调用链,继续保留 Boolean 形式的 buffered 访问器。 */
|
||||
public Boolean getBuffered() { return buffered; }
|
||||
|
||||
public void setBuffered(Boolean buffered) { this.buffered = buffered; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 DataSetList 单项。
|
||||
*/
|
||||
@Data
|
||||
public class DataSetGroupItem {
|
||||
|
||||
/** 分组描述。一般来自 LnClassList.desc。 */
|
||||
private String desc;
|
||||
|
||||
/** lnClass。 */
|
||||
private String lnClass;
|
||||
|
||||
/** 该 lnClass 下的 inst 列表。 */
|
||||
private List<InstItem> instList = new ArrayList<InstItem>();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 doiList 单项。
|
||||
*/
|
||||
@Data
|
||||
public class DoiItem {
|
||||
|
||||
/** DOI 名称。 */
|
||||
private String name;
|
||||
|
||||
/** DOI 描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 起始序号。 */
|
||||
private int start;
|
||||
|
||||
/** 结束序号。 */
|
||||
private int end;
|
||||
|
||||
/** 单位。 */
|
||||
private String unit;
|
||||
|
||||
/** 系数。 */
|
||||
private float coefficient;
|
||||
|
||||
/** 基波标志。 */
|
||||
private int baseflag;
|
||||
|
||||
/** 基波数量。 */
|
||||
private int basecount;
|
||||
|
||||
/** ICD 实际序列数。 */
|
||||
private int icdcout;
|
||||
|
||||
/** SDI 列表。 */
|
||||
private List<SdiItem> sdiList = new ArrayList<SdiItem>();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 instList 单项。
|
||||
*/
|
||||
@Data
|
||||
public class InstItem {
|
||||
|
||||
/** lnInst。 */
|
||||
private String inst;
|
||||
|
||||
/** 该 inst 的描述。通常使用当前绑定的 label。 */
|
||||
private String desc;
|
||||
|
||||
/** DOI 列表。 */
|
||||
private List<DoiItem> doiList = new ArrayList<DoiItem>();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射文档。
|
||||
*
|
||||
* 关键说明:
|
||||
* 1. Java 字段统一使用 lowerCamelCase,避免 Jackson 同时输出 ied/IED 这类重复字段。
|
||||
* 2. JSON 输出名通过 @JsonProperty 显式指定,确保与原 C# 输出格式一致。
|
||||
*/
|
||||
@Data
|
||||
public class MappingDocument {
|
||||
|
||||
/** 映射文档版本。 */
|
||||
@JsonProperty("version")
|
||||
private String version;
|
||||
|
||||
/** 映射文档作者。 */
|
||||
@JsonProperty("author")
|
||||
private String author;
|
||||
|
||||
/** ICD 中的 IED 名称,输出字段固定为 IED。 */
|
||||
@JsonProperty("IED")
|
||||
private String ied;
|
||||
|
||||
/** LD 前缀,输出字段固定为 LD。 */
|
||||
@JsonProperty("LD")
|
||||
private String ld;
|
||||
|
||||
/**
|
||||
* 原 C# mainFrom.txt 固定调用:
|
||||
* Set_WaveTimeFlag("BeiJing")
|
||||
*/
|
||||
@JsonProperty("WaveTimeFlag")
|
||||
private String waveTimeFlag;
|
||||
|
||||
/**
|
||||
* 原 C# mainFrom.txt 固定调用:
|
||||
* Set_DataType("1")
|
||||
*/
|
||||
@JsonProperty("DataType")
|
||||
private String dataType;
|
||||
|
||||
/**
|
||||
* 原 C# mainFrom.txt 固定调用:
|
||||
* Set_unit("1")
|
||||
*/
|
||||
@JsonProperty("unit")
|
||||
private String unit;
|
||||
|
||||
/** 报告映射列表,每个报告控制块输出一条。 */
|
||||
@JsonProperty("ReportMap")
|
||||
private List<ReportMapItem> reportMap = new ArrayList<ReportMapItem>();
|
||||
|
||||
/** 数据集映射列表,按 lnClass 和 inst 聚合。 */
|
||||
@JsonProperty("DataSetList")
|
||||
private List<DataSetGroupItem> dataSetList = new ArrayList<DataSetGroupItem>();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* 最终映射中的 ReportMap 单项。
|
||||
*
|
||||
* 关键说明:
|
||||
* 1. 原 C# 不是把同组报告合并成一条,而是组内每个报告各自输出一条。
|
||||
* 2. 但 reportCount 仍然写该分组总数。
|
||||
* 3. buffered 不是 boolean,而是:
|
||||
* - true -> BR
|
||||
* - false -> RP
|
||||
*/
|
||||
@Data
|
||||
public class ReportMapItem {
|
||||
|
||||
/** 报告分组描述。 */
|
||||
@JsonProperty("desc")
|
||||
private String desc;
|
||||
|
||||
/** 同一分组下的报告数量,单报告按原规则写 0。 */
|
||||
@JsonProperty("reportCount")
|
||||
private int reportCount;
|
||||
|
||||
/** ICD ReportControl.rptID。 */
|
||||
@JsonProperty("rptID")
|
||||
private String rptId;
|
||||
|
||||
/** ICD ReportControl.name。 */
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
/** 报告缓存类型编码,取值 BR 或 RP。 */
|
||||
@JsonProperty("buffered")
|
||||
private String buffered;
|
||||
|
||||
/** 模板 ReportList.inst,限定 01~04。 */
|
||||
@JsonProperty("inst")
|
||||
private String inst;
|
||||
|
||||
/**
|
||||
* 原 C# Set_FlickerFlag() 当前固定写 "0"
|
||||
*/
|
||||
@JsonProperty("FlickerFlag")
|
||||
private String flickerFlag;
|
||||
|
||||
/**
|
||||
* 原 C# 来自 DefaultCfg.ReportList.Select
|
||||
*/
|
||||
@JsonProperty("Select")
|
||||
private String select;
|
||||
|
||||
/**
|
||||
* 原 C# 来自 DefaultCfg.ReportList.TrgOps
|
||||
*/
|
||||
@JsonProperty("TrgOps")
|
||||
private String trgOps;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终映射中的 sdiList 单项。
|
||||
*/
|
||||
@Data
|
||||
public class SdiItem {
|
||||
|
||||
/** SDI 名称。 */
|
||||
private String name;
|
||||
|
||||
/** SDI 描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 类型列表。 */
|
||||
private List<TypeItem> typeList = new ArrayList<TypeItem>();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.mapping;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 最终映射中的 typeList 单项。
|
||||
*/
|
||||
@Data
|
||||
public class TypeItem {
|
||||
|
||||
/** 类型名称。 */
|
||||
private String name;
|
||||
|
||||
/** 类型描述。 */
|
||||
private String desc;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.state;
|
||||
|
||||
import lombok.Data;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.LnNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 最终参与生成 DataSetList 的选择状态。
|
||||
*
|
||||
* 关键修正:
|
||||
* 旧版本一个绑定只保存一个 LnNode,导致:
|
||||
* - MSQI 整组丢失
|
||||
* - MHAI 只生成一半
|
||||
*
|
||||
* 新版本改成:
|
||||
* 一个绑定可以关联多个 LnNode,后续生成阶段再逐个展开。
|
||||
*/
|
||||
@Data
|
||||
public class DataSetSelectionState {
|
||||
|
||||
/** 所属分组 key。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 所属分组 desc。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 报告名。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 绑定标签。 */
|
||||
private String label;
|
||||
|
||||
/** 绑定的 lnInst。 */
|
||||
private String lnInst;
|
||||
|
||||
/**
|
||||
* 当前绑定最终命中的 LN 节点列表。
|
||||
*
|
||||
* 说明:
|
||||
* 同一个 lnInst 在不同 lnClass 下可能都需要展开,
|
||||
* 例如:MMXU / MSQI / MHAI。
|
||||
*/
|
||||
private List<LnNode> lnNodes = new ArrayList<LnNode>();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.state;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 中间态总对象。
|
||||
*
|
||||
* 作用:
|
||||
* 1. 对应原 C# 里“先形成中间态,再做最终 JSON 组装”的思路;
|
||||
* 2. 把业务分组、用户绑定、最终 DataSet 选择结果集中保存;
|
||||
* 3. 避免在 MappingGenerationService 里直接边遍历边拼 JSON,便于后续扩展和排查。
|
||||
*/
|
||||
@Data
|
||||
public class ReportAndDataSetState {
|
||||
|
||||
/** IED 名称。 */
|
||||
private String iedName;
|
||||
|
||||
/** LD 实例名。 */
|
||||
private String ldInst;
|
||||
|
||||
/** 分组状态列表。 */
|
||||
private List<ReportGroupState> reportGroups = new ArrayList<ReportGroupState>();
|
||||
|
||||
/** 数据集选择状态列表。 */
|
||||
private List<DataSetSelectionState> dataSetSelections = new ArrayList<DataSetSelectionState>();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.state;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 单条最终有效绑定关系的中间态。
|
||||
*/
|
||||
@Data
|
||||
public class ReportBindingState {
|
||||
|
||||
/** 分组 key。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 报告名。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 标签。 */
|
||||
private String label;
|
||||
|
||||
/** 绑定的 lnInst 数字。 */
|
||||
private String lnInst;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.state;
|
||||
|
||||
import lombok.Data;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单个业务分组的中间态。
|
||||
*/
|
||||
@Data
|
||||
public class ReportGroupState {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前分组匹配到的报告数量。 */
|
||||
private int reportCount;
|
||||
|
||||
/** 报表 inst(来自 DefaultCfg.ReportList.inst)。 */
|
||||
private String reportInst;
|
||||
|
||||
/** Select(来自 DefaultCfg.ReportList.Select)。 */
|
||||
private String select;
|
||||
|
||||
/** TrgOps(来自 DefaultCfg.ReportList.TrgOps)。 */
|
||||
private String trgOps;
|
||||
|
||||
/** 当前分组包含的报告列表。 */
|
||||
private List<IndexCandidateReportItem> reportItems = new ArrayList<IndexCandidateReportItem>();
|
||||
|
||||
/** 用户最终确认的绑定关系。 */
|
||||
private List<ReportBindingState> bindings = new ArrayList<ReportBindingState>();
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.bo.template;
|
||||
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 默认模板模型。
|
||||
*
|
||||
* 用于承接 `DefaultCfg.txt` 解析后的配置内容,当前仅保留映射接口实际使用的字段。
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public class DefaultTemplate {
|
||||
|
||||
/** 报告分组配置,决定 ReportMap 和前端候选分组。 */
|
||||
@JsonProperty("ReportList")
|
||||
private List<ReportCfgItem> reportList = new ArrayList<ReportCfgItem>();
|
||||
|
||||
/** LN 类别配置,决定 DataSetList 的业务分组描述。 */
|
||||
@JsonProperty("LnClassList")
|
||||
private List<LnClassCfgItem> lnClassList = new ArrayList<LnClassCfgItem>();
|
||||
|
||||
/** 相别配置,用于生成 sdiList 的相别描述。 */
|
||||
@JsonProperty("PhaseList")
|
||||
private List<PhaseCfgItem> phaseList = new ArrayList<PhaseCfgItem>();
|
||||
|
||||
/** 倍率配置,用于从 DOI/DAI 值解析 coefficient。 */
|
||||
@JsonProperty("MultiplierList")
|
||||
private List<MultiplierCfgItem> multiplierList = new ArrayList<MultiplierCfgItem>();
|
||||
|
||||
/** 单位配置,用于从 DOI/DAI 值解析 unit。 */
|
||||
@JsonProperty("UnitList")
|
||||
private List<UnitCfgItem> unitList = new ArrayList<UnitCfgItem>();
|
||||
|
||||
/** 类型配置,用于识别 mag/ang 等 typeList 节点。 */
|
||||
@JsonProperty("TypeList")
|
||||
private List<TypeCfgItem> typeList = new ArrayList<TypeCfgItem>();
|
||||
|
||||
/** 数据对象配置,决定每个绑定标签应生成哪些 DOI。 */
|
||||
@JsonProperty("DataObjectsList")
|
||||
private List<DataObjectCfgItem> dataObjectsList = new ArrayList<DataObjectCfgItem>();
|
||||
|
||||
/**
|
||||
* 执行模板基础校验。
|
||||
*
|
||||
* 返回问题列表;为空表示校验通过。
|
||||
*/
|
||||
public List<String> verify() {
|
||||
List<String> problems = new ArrayList<String>();
|
||||
ensureDuplicateNames("LnClassList", extractNames(lnClassList), problems);
|
||||
ensureDuplicateNames("PhaseList", extractNames(phaseList), problems);
|
||||
ensureDuplicateNames("MultiplierList", extractNames(multiplierList), problems);
|
||||
ensureDuplicateNames("UnitList", extractNames(unitList), problems);
|
||||
ensureDuplicateNames("TypeList", extractNames(typeList), problems);
|
||||
ensureDuplicateObjectNames(problems);
|
||||
if (reportList == null || reportList.isEmpty()) {
|
||||
problems.add("DefaultCfg.ReportList 为空");
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总配置项中的所有 nameList 名称,用于重复项校验。
|
||||
*/
|
||||
private List<String> extractNames(List<? extends NameListSupport> list) {
|
||||
List<String> names = new ArrayList<String>();
|
||||
if (list == null) {
|
||||
return names;
|
||||
}
|
||||
for (NameListSupport item : list) {
|
||||
if (item.getNameList() != null) {
|
||||
names.addAll(item.getNameList());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验指定配置段是否存在重复名称。
|
||||
*/
|
||||
private void ensureDuplicateNames(String section, List<String> names, List<String> problems) {
|
||||
Set<String> set = new HashSet<String>();
|
||||
for (String name : names) {
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
String key = name.trim();
|
||||
if (!set.add(key)) {
|
||||
problems.add(section + " 中存在重复配置项:" + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验每个 DataObjectsList 分组内的对象名称是否重复。
|
||||
*/
|
||||
private void ensureDuplicateObjectNames(List<String> problems) {
|
||||
if (dataObjectsList == null) {
|
||||
return;
|
||||
}
|
||||
for (DataObjectCfgItem dataObject : dataObjectsList) {
|
||||
List<String> names = new ArrayList<String>();
|
||||
if (dataObject.getObjectList() != null) {
|
||||
for (ObjectCfgItem object : dataObject.getObjectList()) {
|
||||
if (object.getNameList() != null) {
|
||||
names.addAll(object.getNameList());
|
||||
}
|
||||
}
|
||||
}
|
||||
ensureDuplicateNames("DataObjectsList[" + dataObject.getDesc() + "]", names, problems);
|
||||
}
|
||||
}
|
||||
/** 统一抽象:所有包含 `nameList` 的配置项都实现该接口。 */
|
||||
public interface NameListSupport {
|
||||
List<String> getNameList();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class ReportCfgItem {
|
||||
|
||||
/**
|
||||
* 报告分组描述,例如“统计数据”“实时数据”“波动闪变”。
|
||||
*/
|
||||
@JsonProperty("desc")
|
||||
private String desc;
|
||||
|
||||
/**
|
||||
* 报告 `inst`,例如 `01`。
|
||||
*/
|
||||
@JsonProperty("inst")
|
||||
private String inst;
|
||||
|
||||
/**
|
||||
* 原始配置中的 `TrgOps`,例如 `40`、`96`。
|
||||
*
|
||||
* 这里必须显式声明 `@JsonProperty("TrgOps")`,否则在当前项目中容易反序列化为 `null`。
|
||||
*/
|
||||
@JsonProperty("TrgOps")
|
||||
private String trgOps;
|
||||
|
||||
/**
|
||||
* 原始配置中的 `Select`,例如 `DataStatFileMap`、`DataRealFileMap`、`FlickerFileMap`。
|
||||
*
|
||||
* 这里必须显式声明 `@JsonProperty("Select")`,否则在当前项目中容易反序列化为 `null`。
|
||||
*/
|
||||
@JsonProperty("Select")
|
||||
private String select;
|
||||
|
||||
/**
|
||||
* 当前分组覆盖的数据集名称列表。
|
||||
*/
|
||||
@JsonProperty("DataSetList")
|
||||
private List<String> dataSetList = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* 当前分组允许配置的标签模板列表。
|
||||
*/
|
||||
@JsonProperty("LnInstList")
|
||||
private List<String> lnInstList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class LnClassCfgItem implements NameListSupport {
|
||||
/** LN 类别描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 当前描述覆盖的 lnClass 名称列表。 */
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class PhaseCfgItem implements NameListSupport {
|
||||
/** 相别中文描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 当前相别描述覆盖的 SDI 名称列表。 */
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class MultiplierCfgItem implements NameListSupport {
|
||||
/** 倍率数值。 */
|
||||
private int multiplier;
|
||||
|
||||
/** 能映射到该倍率的 DAI 值列表。 */
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class UnitCfgItem implements NameListSupport {
|
||||
/** 单位描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 能映射到该单位的 DAI 值列表。 */
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class TypeCfgItem implements NameListSupport {
|
||||
/** 类型中文描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 能映射到该类型的 SDI 名称列表。 */
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class DataObjectCfgItem {
|
||||
/** 数据对象业务描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 当前数据对象配置适用的绑定标签列表。 */
|
||||
@JsonProperty("LnInstList")
|
||||
private List<String> lnInstList = new ArrayList<String>();
|
||||
|
||||
/** 当前标签下需要输出的 DOI 对象配置。 */
|
||||
@JsonProperty("ObjectList")
|
||||
private List<ObjectCfgItem> objectList = new ArrayList<ObjectCfgItem>();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Data
|
||||
public static class ObjectCfgItem implements NameListSupport {
|
||||
/** DOI 输出描述。 */
|
||||
private String desc;
|
||||
|
||||
/** 基波标志,直接写入最终 doiList.baseflag。 */
|
||||
private int baseflag;
|
||||
|
||||
/** 普通 DOI 分支下的基波数量。 */
|
||||
private int basecount;
|
||||
|
||||
/** queueList 分支下的队列数量。 */
|
||||
private int queuecount;
|
||||
|
||||
/** 普通匹配优先使用的 DOI 名称列表。 */
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
|
||||
/** nameList 未命中时才使用的 DOI 兜底名称列表。 */
|
||||
private List<String> queueList = new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ICD 映射生成命令。
|
||||
*
|
||||
* 用于隔离接口层参数,便于应用层统一编排生成流程。
|
||||
*/
|
||||
@Data
|
||||
public class GenerateFromIcdCommand {
|
||||
|
||||
/** 原始文件名。 */
|
||||
private String fileName;
|
||||
|
||||
/** ICD 文件字节数组。 */
|
||||
private byte[] fileBytes;
|
||||
|
||||
/** 前端确认或修改后的 ICD 解析结果。 */
|
||||
private IcdDocument icdDocument;
|
||||
|
||||
/** 输出版本号。 */
|
||||
private String version;
|
||||
|
||||
/** 作者。 */
|
||||
private String author;
|
||||
|
||||
/** 是否保存到磁盘。 */
|
||||
private boolean saveToDisk;
|
||||
|
||||
/** 是否输出美化 JSON。 */
|
||||
private boolean prettyJson;
|
||||
|
||||
/** 输出目录。 */
|
||||
private String outputDir;
|
||||
|
||||
/** 用户提交的索引选择结果。 */
|
||||
private List<IndexSelectionGroupCommand> indexSelection = new ArrayList<IndexSelectionGroupCommand>();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 应用层单条绑定命令。
|
||||
*/
|
||||
@Data
|
||||
public class IndexBindingCommand {
|
||||
|
||||
/** 报告名。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 标签。 */
|
||||
private String label;
|
||||
|
||||
/** 绑定到的 lnInst 数字。 */
|
||||
private String lnInst;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 应用层业务分组选择命令。
|
||||
*/
|
||||
@Data
|
||||
public class IndexSelectionGroupCommand {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组中文描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前分组下的多条绑定关系。 */
|
||||
private List<IndexBindingCommand> bindings = new ArrayList<IndexBindingCommand>();
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.njcn.gather.icd.mapping.enums;
|
||||
package com.njcn.gather.icd.mapping.pojo.enums;
|
||||
|
||||
/**
|
||||
* 接口生成状态。
|
||||
* 映射生成状态。
|
||||
*/
|
||||
public enum GenerateStatus {
|
||||
/** 生成成功。 */
|
||||
SUCCESS,
|
||||
/** 需要前端重新选择索引。 */
|
||||
/** 需要前端补充或重新选择索引。 */
|
||||
NEED_INDEX_SELECTION,
|
||||
/** 生成失败。 */
|
||||
FAILED
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.param;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 候选接口请求体。
|
||||
*
|
||||
* 当前主要承载输出版本、作者、落盘选项等基础参数。
|
||||
* 为兼容旧调用,仍保留 indexSelection 字段,但候选接口本身不依赖该字段。
|
||||
*/
|
||||
@Data
|
||||
public class GenerateMappingFromIcdRequest {
|
||||
|
||||
/** 输出版本号。为空时后端默认补当天日期。 */
|
||||
private String version;
|
||||
|
||||
/** 作者。为空时使用模块默认作者。 */
|
||||
private String author;
|
||||
|
||||
/** 是否保存到磁盘。 */
|
||||
private boolean saveToDisk;
|
||||
|
||||
/** 是否返回格式化 JSON。 */
|
||||
private boolean prettyJson;
|
||||
|
||||
/** 输出目录。saveToDisk=true 时才会用到。 */
|
||||
private String outputDir;
|
||||
|
||||
/**
|
||||
* 兼容保留的索引选择结果。
|
||||
*
|
||||
* 当前候选接口会直接返回 indexCandidates 和 icdDocument;
|
||||
* 正式提交时请改用 get-mms-json 接口。
|
||||
*/
|
||||
private List<IndexSelectionGroupRequest> indexSelection = new ArrayList<IndexSelectionGroupRequest>();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.param;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 单条索引绑定请求。
|
||||
*
|
||||
* 一条绑定只表达一个最小关系:
|
||||
* 某个报告 reportName 下,使用某个标签 label 与某个 lnInst 数字做绑定。
|
||||
*/
|
||||
@Data
|
||||
public class IndexBindingRequest {
|
||||
|
||||
/** 绑定发生在哪个报告上,例如 brcbStHarm。 */
|
||||
private String reportName;
|
||||
|
||||
/** 绑定发生在哪个数据集上,例如 dsStHarm。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 业务标签,例如最大值、最小值、实时数据。 */
|
||||
private String label;
|
||||
|
||||
/** 当前标签最终绑定到的 lnInst 数字,例如 1、2、3。 */
|
||||
private String lnInst;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.param;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单个业务分组的索引选择请求。
|
||||
*
|
||||
* 用于回传某个业务分组下,前端最终确认的多条绑定关系。
|
||||
*/
|
||||
@Data
|
||||
public class IndexSelectionGroupRequest {
|
||||
|
||||
/**
|
||||
* 分组唯一键。
|
||||
*
|
||||
* 该值由后端在 NEED_INDEX_SELECTION 场景返回,前端应原样回传,
|
||||
* 避免仅依赖中文描述做匹配。
|
||||
*/
|
||||
private String groupKey;
|
||||
|
||||
/** 分组中文描述,例如“实时数据”“统计数据”。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前业务分组下,用户最终确认的绑定关系。 */
|
||||
private List<IndexBindingRequest> bindings = new ArrayList<IndexBindingRequest>();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.param;
|
||||
|
||||
import lombok.Data;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 提交索引绑定并生成映射的请求体。
|
||||
*
|
||||
* 第二个接口不再重复上传 ICD 文件,而是直接接收前端确认或修改后的 ICD 解析结果。
|
||||
*/
|
||||
@Data
|
||||
public class SubmitIndexSelectionRequest {
|
||||
|
||||
/** 前端基于候选接口返回值确认或修改后的 ICD 解析结果。 */
|
||||
private IcdDocument icdDocument;
|
||||
|
||||
/** 输出版本号。为空时后端默认补当天日期。 */
|
||||
private String version;
|
||||
|
||||
/** 作者。为空时使用模块默认作者。 */
|
||||
private String author;
|
||||
|
||||
/** 是否保存到磁盘。 */
|
||||
private boolean saveToDisk;
|
||||
|
||||
/** 是否返回格式化 JSON。 */
|
||||
private boolean prettyJson;
|
||||
|
||||
/** 输出目录。saveToDisk=true 时才会用到。 */
|
||||
private String outputDir;
|
||||
|
||||
/** 用户最终确认的索引绑定关系。 */
|
||||
private List<IndexSelectionGroupRequest> indexSelection = new ArrayList<IndexSelectionGroupRequest>();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务分组下的单个报告候选响应。
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@Data
|
||||
public class IndexCandidateReportItemResponse {
|
||||
|
||||
/** 报告名称。 */
|
||||
private String reportName;
|
||||
|
||||
/** 数据集名称。 */
|
||||
private String dataSetName;
|
||||
|
||||
/** 报告描述。 */
|
||||
private String reportDesc;
|
||||
|
||||
/** 当前报告可选的 `lnInst` 数值。 */
|
||||
private List<String> availableLnInstValues = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 索引候选响应对象。
|
||||
*
|
||||
* 一个候选对应一个业务分组,分组下可包含多个报告,
|
||||
* 前端据此完成模板标签与 `lnInst` 的人工绑定。
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@Data
|
||||
public class IndexCandidateResponse {
|
||||
|
||||
/** 分组唯一键。 */
|
||||
private String groupKey;
|
||||
|
||||
/** 分组中文描述。 */
|
||||
private String groupDesc;
|
||||
|
||||
/** 当前分组包含的报告数。 */
|
||||
private int reportCount;
|
||||
|
||||
/** 模板里配置的可选标签。 */
|
||||
private List<String> templateLabels = new ArrayList<String>();
|
||||
|
||||
/** 当前分组下的报告候选列表。 */
|
||||
private List<IndexCandidateReportItemResponse> reports = new ArrayList<IndexCandidateReportItemResponse>();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 映射文档摘要响应。
|
||||
*
|
||||
* 用于返回最终映射结果中的关键信息摘要。
|
||||
*/
|
||||
@Data
|
||||
public class MappingDocumentResponse {
|
||||
/** 映射文档版本。 */
|
||||
private String version;
|
||||
|
||||
/** 映射文档作者。 */
|
||||
private String author;
|
||||
|
||||
/** 输出 JSON 中的 IED。 */
|
||||
private String ied;
|
||||
|
||||
/** 输出 JSON 中的 LD。 */
|
||||
private String ld;
|
||||
|
||||
/** ReportMap 条目数量。 */
|
||||
private int reportCount;
|
||||
|
||||
/** DataSetList 分组数量。 */
|
||||
private int dataSetCount;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ICD 映射接口统一响应。
|
||||
*
|
||||
* 按接口阶段仅返回当前场景必需字段;空字段和空集合不参与序列化。
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@Data
|
||||
public class MappingTaskResponse {
|
||||
/** 本次接口处理状态。 */
|
||||
private GenerateStatus status;
|
||||
|
||||
/** 状态说明或错误提示。 */
|
||||
private String message;
|
||||
|
||||
/** 候选接口或需要重新选择索引时返回的 ICD 解析结果。 */
|
||||
private IcdDocument icdDocument;
|
||||
|
||||
/** 正式生成成功后的完整映射 JSON。 */
|
||||
private String mappingJson;
|
||||
|
||||
/** 生成文件落盘后的绝对路径。 */
|
||||
private String savedPath;
|
||||
|
||||
/** 待绑定状态下返回的索引候选分组。 */
|
||||
private List<IndexCandidateResponse> indexCandidates = new ArrayList<IndexCandidateResponse>();
|
||||
|
||||
/** 模板校验、候选分析或绑定校验问题。 */
|
||||
private List<String> problems = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.gather.icd.mapping.service;
|
||||
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
||||
|
||||
/**
|
||||
* ICD 映射任务编排服务。
|
||||
*
|
||||
* 统一封装 ICD 解析、索引候选分析、索引绑定校验和正式映射生成流程。
|
||||
*/
|
||||
public interface MappingTaskService {
|
||||
|
||||
/**
|
||||
* 解析 ICD 并返回索引候选结果。
|
||||
*
|
||||
* @param command ICD 文件与生成参数
|
||||
* @return 候选分析结果
|
||||
*/
|
||||
GenerateMappingResult getICD(GenerateFromIcdCommand command);
|
||||
|
||||
/**
|
||||
* 串联 getICD 与索引提交流程,直接返回正式提交阶段的处理结果。
|
||||
*
|
||||
* @param command ICD 文件与前端提交参数
|
||||
* @return 正式提交阶段结果
|
||||
*/
|
||||
GenerateMappingResult getIcdMmsJson(GenerateFromIcdCommand command);
|
||||
|
||||
/**
|
||||
* 根据前端确认后的索引绑定关系生成正式映射。
|
||||
*
|
||||
* @param command 已确认的 ICD 解析结果与索引绑定关系
|
||||
* @return 映射生成结果
|
||||
*/
|
||||
GenerateMappingResult getMmsJson(GenerateFromIcdCommand command);
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.njcn.gather.icd.mapping.service.impl;
|
||||
|
||||
import com.njcn.gather.icd.mapping.component.DefaultTemplateLoader;
|
||||
import com.njcn.gather.icd.mapping.component.FileStorageService;
|
||||
import com.njcn.gather.icd.mapping.component.IcdParserService;
|
||||
import com.njcn.gather.icd.mapping.component.IndexAnalysisService;
|
||||
import com.njcn.gather.icd.mapping.component.IndexValidationService;
|
||||
import com.njcn.gather.icd.mapping.component.MappingDocumentSerializer;
|
||||
import com.njcn.gather.icd.mapping.component.MappingGenerationService;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.analysis.ValidationResult;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
|
||||
import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
|
||||
import com.njcn.gather.icd.mapping.service.MappingTaskService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ICD 映射任务编排服务实现。
|
||||
*
|
||||
* 按固定链路组织 ICD 解析、索引分析、校验和映射生成,避免 Controller 关注业务编排细节。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MappingTaskServiceImpl implements MappingTaskService {
|
||||
|
||||
/** ICD 解析阶段名称。 */
|
||||
private static final String ICD_PARSE_TASK_NAME = "ICD 解析";
|
||||
/** 正式映射生成阶段名称。 */
|
||||
private static final String MAPPING_GENERATE_TASK_NAME = "映射生成";
|
||||
/** 候选分析成功提示。 */
|
||||
private static final String INDEX_SELECTION_SUCCESS_MESSAGE = "ICD 解析成功,请确认解析结果并完成索引绑定后提交";
|
||||
/** 索引绑定缺失提示。 */
|
||||
private static final String INDEX_SELECTION_MISSING_MESSAGE = "索引配置缺失,请根据候选信息完成标签与数字索引的绑定后重新提交";
|
||||
/** 索引绑定非法提示。 */
|
||||
private static final String INDEX_SELECTION_INVALID_MESSAGE = "索引配置不合法,请根据候选信息完成标签与数字索引的绑定后重新提交";
|
||||
/** 映射生成成功提示。 */
|
||||
private static final String MAPPING_GENERATE_SUCCESS_MESSAGE = "映射生成成功";
|
||||
|
||||
/** ICD/SCL 文件解析服务。 */
|
||||
private final IcdParserService icdParserService;
|
||||
/** DefaultCfg.txt 默认模板加载器。 */
|
||||
private final DefaultTemplateLoader defaultTemplateLoader;
|
||||
/** 根据 ICD 和模板生成前端可选索引候选。 */
|
||||
private final IndexAnalysisService indexAnalysisService;
|
||||
/** 校验前端回传的标签与 lnInst 绑定关系。 */
|
||||
private final IndexValidationService indexValidationService;
|
||||
/** 根据有效绑定关系生成最终 MappingDocument。 */
|
||||
private final MappingGenerationService mappingGenerationService;
|
||||
/** 将 MappingDocument 序列化为 JSON。 */
|
||||
private final MappingDocumentSerializer mappingDocumentSerializer;
|
||||
/** 按需把生成结果保存到本地磁盘。 */
|
||||
private final FileStorageService fileStorageService;
|
||||
|
||||
@Override
|
||||
public GenerateMappingResult getICD(GenerateFromIcdCommand command) {
|
||||
return executeTask(ICD_PARSE_TASK_NAME, result -> {
|
||||
parseTaskContext(command, result);
|
||||
result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
|
||||
result.setMessage(INDEX_SELECTION_SUCCESS_MESSAGE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenerateMappingResult getIcdMmsJson(GenerateFromIcdCommand command) {
|
||||
GenerateMappingResult icdResult = getICD(command);
|
||||
if (icdResult.getStatus() == GenerateStatus.FAILED || icdResult.getIcdDocument() == null) {
|
||||
return icdResult;
|
||||
}
|
||||
|
||||
command.setIcdDocument(icdResult.getIcdDocument());
|
||||
return getMmsJson(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenerateMappingResult getMmsJson(GenerateFromIcdCommand command) {
|
||||
return executeTask(MAPPING_GENERATE_TASK_NAME, result -> {
|
||||
MappingTaskContext context = buildTaskContext(requireIcdDocument(command), result);
|
||||
if (isIndexSelectionEmpty(command.getIndexSelection())) {
|
||||
markNeedIndexSelection(result, INDEX_SELECTION_MISSING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
ValidationResult validationResult = indexValidationService.validate(context.indexAnalysis, command.getIndexSelection());
|
||||
if (!validationResult.isValid()) {
|
||||
markNeedIndexSelection(result, INDEX_SELECTION_INVALID_MESSAGE);
|
||||
result.getProblems().addAll(validationResult.getProblems());
|
||||
return;
|
||||
}
|
||||
|
||||
MappingDocument mappingDocument = mappingGenerationService.generate(
|
||||
context.icdDocument,
|
||||
context.template,
|
||||
context.indexAnalysis,
|
||||
command.getIndexSelection(),
|
||||
command.getVersion(),
|
||||
command.getAuthor()
|
||||
);
|
||||
result.setMappingDocument(mappingDocument);
|
||||
|
||||
String mappingJson = serializeMappingDocument(mappingDocument, command.isPrettyJson());
|
||||
result.setMappingJson(mappingJson);
|
||||
|
||||
if (command.isSaveToDisk()) {
|
||||
String fileName = buildOutputFileName(context.icdDocument, command.isPrettyJson());
|
||||
String savedPath = fileStorageService.save(fileName, mappingJson, command.getOutputDir());
|
||||
result.setSavedPath(savedPath);
|
||||
}
|
||||
|
||||
result.setStatus(GenerateStatus.SUCCESS);
|
||||
result.setMessage(MAPPING_GENERATE_SUCCESS_MESSAGE);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一包装任务执行结果,避免每个入口重复编写异常兜底逻辑。
|
||||
*/
|
||||
private GenerateMappingResult executeTask(String taskName, MappingTaskAction action) {
|
||||
GenerateMappingResult result = new GenerateMappingResult();
|
||||
try {
|
||||
action.execute(result);
|
||||
} catch (Exception ex) {
|
||||
handleTaskFailure(result, taskName, ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析上传的 ICD 文件,并补齐候选分析上下文。
|
||||
*/
|
||||
private MappingTaskContext parseTaskContext(GenerateFromIcdCommand command, GenerateMappingResult result) {
|
||||
IcdDocument icdDocument = icdParserService.parse(command.getFileBytes(), command.getFileName());
|
||||
return buildTaskContext(icdDocument, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于 ICD 解析结果补齐模板和候选分析信息,供后续提交流程复用。
|
||||
*/
|
||||
private MappingTaskContext buildTaskContext(IcdDocument icdDocument, GenerateMappingResult result) {
|
||||
fillIcdSummary(result, icdDocument);
|
||||
|
||||
DefaultTemplate template = loadTemplate(result);
|
||||
IndexAnalysis indexAnalysis = analyzeIndexCandidates(icdDocument, template, result);
|
||||
result.setIndexAnalysis(indexAnalysis);
|
||||
return new MappingTaskContext(icdDocument, template, indexAnalysis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交流程必须带上前端确认后的 ICD 解析结果。
|
||||
*/
|
||||
private IcdDocument requireIcdDocument(GenerateFromIcdCommand command) {
|
||||
if (command.getIcdDocument() == null) {
|
||||
throw new IllegalArgumentException("ICD 解析结果不能为空");
|
||||
}
|
||||
return command.getIcdDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载并校验默认模板。
|
||||
*/
|
||||
private DefaultTemplate loadTemplate(GenerateMappingResult result) {
|
||||
DefaultTemplate template = defaultTemplateLoader.load();
|
||||
result.getProblems().addAll(template.verify());
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析 ICD 对应的索引候选。
|
||||
*/
|
||||
private IndexAnalysis analyzeIndexCandidates(IcdDocument icdDocument,
|
||||
DefaultTemplate template,
|
||||
GenerateMappingResult result) {
|
||||
IndexAnalysis indexAnalysis = indexAnalysisService.analyze(icdDocument, template);
|
||||
result.getProblems().addAll(indexAnalysis.getProblems());
|
||||
return indexAnalysis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回填响应公共信息。
|
||||
*/
|
||||
private void fillIcdSummary(GenerateMappingResult result, IcdDocument icdDocument) {
|
||||
result.setIedName(icdDocument.getIedName());
|
||||
result.setLdInst(icdDocument.getLdInst());
|
||||
result.setIcdDocument(icdDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记当前仍需用户重新确认索引绑定。
|
||||
*/
|
||||
private void markNeedIndexSelection(GenerateMappingResult result, String message) {
|
||||
result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
|
||||
result.setMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输出模式选择紧凑或美化 JSON。
|
||||
*/
|
||||
private String serializeMappingDocument(MappingDocument mappingDocument, boolean prettyJson) {
|
||||
return prettyJson
|
||||
? mappingDocumentSerializer.toPrettyJson(mappingDocument)
|
||||
: mappingDocumentSerializer.toCompactJson(mappingDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造落盘文件名。
|
||||
*
|
||||
* 文件名优先使用 IED 名称;如 IED 名称为空或清理后为空,则回退使用 mapping。
|
||||
*/
|
||||
private String buildOutputFileName(IcdDocument icdDocument, boolean prettyJson) {
|
||||
String baseName = icdDocument.getIedName() == null ? "mapping" : icdDocument.getIedName();
|
||||
String safeBaseName = baseName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim();
|
||||
if (safeBaseName.isEmpty()) {
|
||||
safeBaseName = "mapping";
|
||||
}
|
||||
return safeBaseName + (prettyJson ? "-mapping-pretty.json" : "-mapping.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理任务异常,避免控制层再补失败分支。
|
||||
*/
|
||||
private void handleTaskFailure(GenerateMappingResult result, String taskName, Exception ex) {
|
||||
log.error("{}失败", taskName, ex);
|
||||
String errorMessage = resolveErrorMessage(ex);
|
||||
result.setStatus(GenerateStatus.FAILED);
|
||||
result.setMessage(taskName + "失败:" + errorMessage);
|
||||
result.getProblems().add(errorMessage);
|
||||
}
|
||||
|
||||
private boolean isIndexSelectionEmpty(List<IndexSelectionGroupCommand> indexSelection) {
|
||||
return indexSelection == null || indexSelection.isEmpty();
|
||||
}
|
||||
|
||||
private String resolveErrorMessage(Exception ex) {
|
||||
if (ex.getMessage() == null || ex.getMessage().trim().isEmpty()) {
|
||||
return ex.getClass().getSimpleName();
|
||||
}
|
||||
return ex.getMessage();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface MappingTaskAction {
|
||||
void execute(GenerateMappingResult result) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* 供多个任务阶段复用的编排上下文。
|
||||
*/
|
||||
private static class MappingTaskContext {
|
||||
|
||||
private final IcdDocument icdDocument;
|
||||
|
||||
private final DefaultTemplate template;
|
||||
|
||||
private final IndexAnalysis indexAnalysis;
|
||||
|
||||
private MappingTaskContext(IcdDocument icdDocument, DefaultTemplate template, IndexAnalysis indexAnalysis) {
|
||||
this.icdDocument = icdDocument;
|
||||
this.template = template;
|
||||
this.indexAnalysis = indexAnalysis;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,37 @@
|
||||
package com.njcn.gather.icd.mapping.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 日期工具。
|
||||
* 日期格式化工具。
|
||||
*/
|
||||
public final class DateUtils {
|
||||
|
||||
private DateUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前时间文本,格式为 yyyy-MM-dd HH:mm:ss。
|
||||
*/
|
||||
public static String nowText() {
|
||||
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一提取上传文件名,避免控制器重复判空。
|
||||
*/
|
||||
public static String resolveFileName(MultipartFile file) {
|
||||
return file == null ? null : file.getOriginalFilename();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一统计选择项数量,空集合按 0 处理。
|
||||
*/
|
||||
public static int resolveSelectionCount(Collection<?> selections) {
|
||||
return selections == null ? 0 : selections.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
package com.njcn.gather.icd.mapping.utils;
|
||||
|
||||
/**
|
||||
* JSON 文本处理工具。
|
||||
* JSON 文本清洗工具。
|
||||
*
|
||||
* 作用:
|
||||
* 1. 兼容老配置文件中常见的尾逗号问题。
|
||||
* 2. 降低 DefaultCfg.txt 因历史书写习惯造成的解析失败概率。
|
||||
* 当前主要用于兼容历史模板中常见的尾逗号写法,减少 `DefaultCfg.txt` 解析失败。
|
||||
*/
|
||||
public final class JsonUtils {
|
||||
|
||||
private JsonUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理历史 JSON 模板中对象或数组结束前的尾逗号。
|
||||
*/
|
||||
public static String sanitizeJson(String text) {
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
// 去掉对象或数组结束前的尾逗号。
|
||||
// 删除对象或数组结束前的尾逗号,兼容历史配置写法。
|
||||
return text.replaceAll(",\\s*([}\\]])", "$1");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.njcn.gather.icd.mapping.debug;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse;
|
||||
import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
|
||||
import com.njcn.gather.icd.mapping.service.MappingTaskService;
|
||||
import org.springframework.boot.Banner;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* `getIcdMmsJson` 本地调试入口。
|
||||
*
|
||||
* 使用方式:
|
||||
* 1. 先修改 ICD_FILE_PATH 指向本地真实 ICD/SCD 文件。
|
||||
* 2. 直接运行 main,先观察第一次调试返回的 indexCandidates。
|
||||
* 3. 按第一次返回结果补齐 buildSecondStepSelection() 中的绑定关系。
|
||||
* 4. 把 RUN_SECOND_STEP 改成 true,再次运行 main 获取最终 mappingJson。
|
||||
*/
|
||||
public class GetIcdMmsJsonDebugRunner {
|
||||
|
||||
/** 本地 ICD/SCD 文件路径,运行前请改成真实文件。 */
|
||||
private static final String ICD_FILE_PATH = "D:/temp/demo.icd";
|
||||
|
||||
/** 调试时可固定版本号,便于对比输出。 */
|
||||
private static final String VERSION = "20260421";
|
||||
|
||||
/** 调试作者标识。 */
|
||||
private static final String AUTHOR = "debug-user";
|
||||
|
||||
/** 是否输出格式化 JSON,便于人工查看。 */
|
||||
private static final boolean PRETTY_JSON = true;
|
||||
|
||||
/** 是否把生成结果写入磁盘。 */
|
||||
private static final boolean SAVE_TO_DISK = false;
|
||||
|
||||
/** saveToDisk=true 时使用的输出目录。 */
|
||||
private static final String OUTPUT_DIR = "D:/temp/mms-output";
|
||||
|
||||
/**
|
||||
* 第二次正式生成开关。
|
||||
* 默认先只跑第一次,确认 groupKey/reportName/dataSetName/lnInst 候选值后再打开。
|
||||
*/
|
||||
private static final boolean RUN_SECOND_STEP = false;
|
||||
|
||||
public static void main(String[] args) {
|
||||
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(DebugApplication.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.bannerMode(Banner.Mode.OFF)
|
||||
.logStartupInfo(false)
|
||||
.run(args)) {
|
||||
MappingTaskService mappingTaskService = context.getBean(MappingTaskService.class);
|
||||
MappingResponseConverter responseConverter = context.getBean(MappingResponseConverter.class);
|
||||
ObjectMapper objectMapper = createObjectMapper();
|
||||
|
||||
MappingTaskResponse firstResponse = debugNeedIndexSelection(mappingTaskService, responseConverter);
|
||||
printResponse("第一次调试:获取索引候选", firstResponse, objectMapper);
|
||||
printIndexCandidateSummary(firstResponse);
|
||||
|
||||
if (!RUN_SECOND_STEP) {
|
||||
System.out.println("第二次调试未开启。请先根据第一次返回结果补齐 buildSecondStepSelection(),再把 RUN_SECOND_STEP 改成 true。");
|
||||
return;
|
||||
}
|
||||
|
||||
MappingTaskResponse secondResponse = debugGenerateMapping(mappingTaskService, responseConverter);
|
||||
printResponse("第二次调试:生成 MMS JSON", secondResponse, objectMapper);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("getIcdMmsJson 调试失败:" + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第一次调试:不传 indexSelection,只获取 icdDocument 和 indexCandidates。
|
||||
*/
|
||||
private static MappingTaskResponse debugNeedIndexSelection(MappingTaskService mappingTaskService,
|
||||
MappingResponseConverter responseConverter) {
|
||||
GenerateFromIcdCommand command = buildBaseCommand();
|
||||
return responseConverter.fromSubmitResult(mappingTaskService.getIcdMmsJson(command));
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二次调试:补齐 indexSelection 后直接生成最终 mappingJson。
|
||||
*/
|
||||
private static MappingTaskResponse debugGenerateMapping(MappingTaskService mappingTaskService,
|
||||
MappingResponseConverter responseConverter) {
|
||||
List<IndexSelectionGroupCommand> selectionGroups = buildSecondStepSelection();
|
||||
if (selectionGroups.isEmpty()) {
|
||||
throw new IllegalArgumentException("第二次调试缺少 indexSelection,请先补齐 buildSecondStepSelection()");
|
||||
}
|
||||
|
||||
GenerateFromIcdCommand command = buildBaseCommand();
|
||||
command.getIndexSelection().addAll(selectionGroups);
|
||||
return responseConverter.fromSubmitResult(mappingTaskService.getIcdMmsJson(command));
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装调试用基础命令,等价于接口中的 request 基础参数。
|
||||
*/
|
||||
private static GenerateFromIcdCommand buildBaseCommand() {
|
||||
Path icdPath = Paths.get(ICD_FILE_PATH);
|
||||
if (!Files.exists(icdPath)) {
|
||||
throw new IllegalArgumentException("ICD 文件不存在:" + icdPath.toAbsolutePath());
|
||||
}
|
||||
|
||||
try {
|
||||
GenerateFromIcdCommand command = new GenerateFromIcdCommand();
|
||||
command.setFileName(icdPath.getFileName().toString());
|
||||
command.setFileBytes(Files.readAllBytes(icdPath));
|
||||
command.setVersion(VERSION);
|
||||
command.setAuthor(AUTHOR);
|
||||
command.setPrettyJson(PRETTY_JSON);
|
||||
command.setSaveToDisk(SAVE_TO_DISK);
|
||||
command.setOutputDir(OUTPUT_DIR);
|
||||
return command;
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("读取 ICD 文件失败:" + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二次调试的索引绑定示例。
|
||||
*
|
||||
* 注意:
|
||||
* 1. groupKey/groupDesc/reportName/dataSetName/lnInst 必须使用第一次返回的真实值。
|
||||
* 2. 下面示例仅作占位,默认不参与运行。
|
||||
*/
|
||||
private static List<IndexSelectionGroupCommand> buildSecondStepSelection() {
|
||||
List<IndexSelectionGroupCommand> groups = new ArrayList<IndexSelectionGroupCommand>();
|
||||
|
||||
// 示例:
|
||||
// IndexSelectionGroupCommand group = createSelectionGroup("HARM__DSSTHARM", "谐波数据");
|
||||
// group.getBindings().add(createBinding("brcbStHarm", "dsStHarm", "A相", "1"));
|
||||
// group.getBindings().add(createBinding("brcbStHarm", "dsStHarm", "B相", "2"));
|
||||
// group.getBindings().add(createBinding("brcbStHarm", "dsStHarm", "C相", "3"));
|
||||
// groups.add(group);
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
private static IndexSelectionGroupCommand createSelectionGroup(String groupKey, String groupDesc) {
|
||||
IndexSelectionGroupCommand group = new IndexSelectionGroupCommand();
|
||||
group.setGroupKey(groupKey);
|
||||
group.setGroupDesc(groupDesc);
|
||||
return group;
|
||||
}
|
||||
|
||||
private static IndexBindingCommand createBinding(String reportName,
|
||||
String dataSetName,
|
||||
String label,
|
||||
String lnInst) {
|
||||
IndexBindingCommand binding = new IndexBindingCommand();
|
||||
binding.setReportName(reportName);
|
||||
binding.setDataSetName(dataSetName);
|
||||
binding.setLabel(label);
|
||||
binding.setLnInst(lnInst);
|
||||
return binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台输出候选摘要,方便把第一次返回值填回第二次调试配置。
|
||||
*/
|
||||
private static void printIndexCandidateSummary(MappingTaskResponse response) {
|
||||
if (response == null || response.getIndexCandidates() == null || response.getIndexCandidates().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("===== 索引候选摘要 =====");
|
||||
for (IndexCandidateResponse candidate : response.getIndexCandidates()) {
|
||||
System.out.println("groupKey=" + candidate.getGroupKey() + ", groupDesc=" + candidate.getGroupDesc());
|
||||
if (candidate.getReports() == null) {
|
||||
continue;
|
||||
}
|
||||
for (IndexCandidateReportItemResponse report : candidate.getReports()) {
|
||||
System.out.println(" reportName=" + report.getReportName()
|
||||
+ ", dataSetName=" + report.getDataSetName()
|
||||
+ ", availableLnInstValues=" + report.getAvailableLnInstValues());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printResponse(String title, MappingTaskResponse response, ObjectMapper objectMapper) {
|
||||
try {
|
||||
System.out.println();
|
||||
System.out.println("===== " + title + " =====");
|
||||
System.out.println(objectMapper.writeValueAsString(response));
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("打印调试响应失败:" + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectMapper createObjectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
@SpringBootApplication(scanBasePackages = "com.njcn.gather.icd.mapping")
|
||||
public static class DebugApplication {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user