feat(event): 添加暂态事件波形查看与导出功能

- 新增 getTransientEventWave 接口用于查看暂态事件波形
- 新增 exportTransientEventWaves 接口用于批量导出暂态事件波形
- 添加 EventWaveExportParam 参数类支持波形导出
- 在 EventListMapper 中增加 selectTransientDetailsByIds 查询方法
- 更新事件列表查询参数支持毫秒级时间格式
- 移除事件描述模糊查询条件优化查询性能
- 添加波形导出相关的常量和工具类集成
This commit is contained in:
2026-05-18 08:45:05 +08:00
parent 90219a3daf
commit 38f910fccd
67 changed files with 1203 additions and 1760 deletions

View File

@@ -196,7 +196,7 @@ curl.exe -X POST "http://localhost:8080/api/mms-mapping/get-icd-mms-json" `
说明:
- `mappingJson` 是字符串字段,字段值本身也是一段 JSON 文本。
-`saveToDisk=true` 时,响应中还会返回 `savedPath`
-`saveToDisk=true` 时,响应中还会返回 `savedPath`,落盘文件名按统一规则追加 `_yyyyMMdd`
### 5.3 FAILED

View File

@@ -1,5 +1,6 @@
package com.njcn.gather.icd.mapping.component;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
import org.springframework.stereotype.Component;
import java.io.File;
@@ -26,7 +27,7 @@ public class FileStorageService {
if (!dir.isDirectory()) {
throw new IllegalStateException("输出路径不是目录:" + dir.getAbsolutePath());
}
File target = new File(dir, fileName);
File target = new File(dir, GeneratedFileNameUtil.appendToday(fileName));
try (FileOutputStream fos = new FileOutputStream(target)) {
fos.write(content.getBytes(StandardCharsets.UTF_8));
}

View File

@@ -8,6 +8,7 @@ import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse;
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse;
import com.njcn.gather.icd.mapping.pojo.vo.MappingDocumentResponse;
import com.njcn.gather.icd.mapping.pojo.vo.XmlFileResponse;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
import org.springframework.stereotype.Component;
@Component
@@ -90,13 +91,13 @@ public class IcdToXmlResponseConverter {
private String resolveXmlFileName(IcdToXmlGenerateResult result) {
String iedName = result.getIedName();
if (iedName == null || iedName.trim().isEmpty()) {
return DEFAULT_XML_FILE_NAME;
return GeneratedFileNameUtil.appendToday(DEFAULT_XML_FILE_NAME);
}
String safeName = iedName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim();
if (safeName.isEmpty()) {
return DEFAULT_XML_FILE_NAME;
return GeneratedFileNameUtil.appendToday(DEFAULT_XML_FILE_NAME);
}
return safeName + ".xml";
return GeneratedFileNameUtil.appendToday(safeName + ".xml");
}
}

View File

@@ -9,6 +9,7 @@ import com.njcn.gather.icd.mapping.pojo.param.*;
import com.njcn.gather.icd.mapping.pojo.vo.*;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
import lombok.var;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@@ -17,6 +18,8 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
@@ -57,8 +60,9 @@ public class JsonToXmlConversionService {
String xmlContent = buildXmlContentFromJson(mappingJson, templateStream, ruleStreams, indexMapping);
// 3. 保存为临时文件
Path tempPath = Files.createTempFile("converted_", ".xml");
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8));
Path tempPath = Paths.get(System.getProperty("java.io.tmpdir"),
GeneratedFileNameUtil.appendToday("converted_" + java.util.UUID.randomUUID().toString().replace("-", "") + ".xml"));
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW);
return tempPath.toString();
}

View File

@@ -12,6 +12,8 @@ import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -20,6 +22,7 @@ import com.njcn.gather.icd.mapping.pojo.bo.*;
import com.njcn.gather.icd.mapping.pojo.param.*;
import com.njcn.gather.icd.mapping.pojo.vo.*;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
@Component
public class RuleBasedXmlMappingService {
@@ -104,8 +107,9 @@ public class RuleBasedXmlMappingService {
xmlContent = applyRulesToXml(xmlContent, applicableRules);
Path tempPath = Files.createTempFile("rule_mapped_", ".xml");
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8));
Path tempPath = Paths.get(System.getProperty("java.io.tmpdir"),
GeneratedFileNameUtil.appendToday("rule_mapped_" + UUID.randomUUID().toString().replace("-", "") + ".xml"));
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW);
return tempPath.toString();
}

View File

@@ -0,0 +1,33 @@
package com.njcn.gather.icd.mapping.utils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* 生成文件名处理工具。
*/
public final class GeneratedFileNameUtil {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
private GeneratedFileNameUtil() {
}
public static String appendToday(String fileName) {
return appendDate(fileName, LocalDate.now());
}
public static String appendDate(String fileName, LocalDate date) {
if (fileName == null || date == null) {
return fileName;
}
String dateText = DATE_FORMATTER.format(date);
int separatorIndex = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
int dotIndex = fileName.lastIndexOf('.');
if (dotIndex > separatorIndex) {
return fileName.substring(0, dotIndex) + "_" + dateText + fileName.substring(dotIndex);
}
return fileName + "_" + dateText;
}
}

View File

@@ -1,232 +0,0 @@
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:\\Work\\工作资料\\1灿能项目资料\\01自研\\01灿能\\09灿能C端功能\\01需求文档\\灿能工具箱开发\\icd\\PQS882_VX_BJ_1(V111).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);
printIndexCandidatesJson(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());
}
}
}
/**
* 单独输出 indexCandidates 的 JSON便于直接复制做第二次调试绑定。
*/
private static void printIndexCandidatesJson(MappingTaskResponse response, ObjectMapper objectMapper) {
try {
System.out.println();
System.out.println("===== indexCandidates JSON =====");
if (response == null) {
System.out.println("null");
return;
}
System.out.println(objectMapper.writeValueAsString(response.getIndexCandidates()));
} catch (Exception ex) {
throw new IllegalArgumentException("print indexCandidates JSON failed: " + ex.getMessage(), ex);
}
}
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 {
}
}