feat(event): 添加暂态事件波形查看与导出功能
- 新增 getTransientEventWave 接口用于查看暂态事件波形 - 新增 exportTransientEventWaves 接口用于批量导出暂态事件波形 - 添加 EventWaveExportParam 参数类支持波形导出 - 在 EventListMapper 中增加 selectTransientDetailsByIds 查询方法 - 更新事件列表查询参数支持毫秒级时间格式 - 移除事件描述模糊查询条件优化查询性能 - 添加波形导出相关的常量和工具类集成
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
- 只清理自己造成的问题:可以删除因本次修改而产生的未使用 `import`、变量或方法;不要删除仓库中原本就存在的死代码,除非用户明确要求。
|
||||
- 先定义验证方式:执行方案里要写清楚“改哪里、怎么判断改对了”;默认通过代码路径、配置一致性和受影响范围检查进行验证。
|
||||
- 除非用户明确要求,否则不执行任何 `mvn` 编译、打包、测试或其他构建命令。
|
||||
- 所有导出或生成文件名统一追加日期,格式为“原文件名 + `_` + `yyyyMMdd` + 原扩展名”,例如 `暂态事件列表_20260516.xlsx`;新增下载导出、临时生成或落盘保存文件时,优先复用目标模块已有文件名工具,不要在业务代码中散落手写日期拼接。
|
||||
|
||||
## 项目结构与模块划分
|
||||
`CN_Tool` 是一个 Maven 多模块后端项目,根目录的 [`pom.xml`](C:/code/gitea/cn_tool/CN_Tool/pom.xml) 聚合了 `entrance`、`system`、`user`、`detection` 和 `tools`。
|
||||
@@ -40,6 +41,8 @@ Java 源码位于 `src/main/java`,配置文件位于 `src/main/resources`,My
|
||||
- 不要假设运行时存在自动数据库迁移;如果代码依赖新表、新字段或新索引,必须同步补齐对应 SQL 与文档说明。
|
||||
- SQL 脚本应放在目标模块的 `src/main/resources/sql/...` 下,并保持可审阅、可单独执行、语义清晰。
|
||||
- 变更缓存、日志、审计相关逻辑时,优先沿用现有机制,不要绕开现有登录上下文、缓存约定和审计字段填充方式。
|
||||
- 涉及 `sys_dict_type`、`sys_dict_data` 的字典类型编码、字典数据编码或固定字典数据 ID 时,必须统一维护在 `system/src/main/java/com/njcn/gather/system/pojo/constant/DictConst.java`;后续新增使用点也要先补常量再引用,不要在业务代码、SQL 或文档中散落硬编码。
|
||||
- 新增或保留字典初始化 SQL 前,必须先确认对应字典类型在后端代码、配置或明确页面契约中确实被使用;未使用的字典类型和字典数据不要写入 `sys_dict_type`、`sys_dict_data` 初始化脚本。
|
||||
|
||||
## 注释与编码
|
||||
- 新增或修改代码时,关键字段、关键分支、关键约束和非直观实现应补充简洁中文注释。
|
||||
|
||||
@@ -48,6 +48,16 @@ socket:
|
||||
webSocket:
|
||||
port: 7777
|
||||
|
||||
steady:
|
||||
influxdb:
|
||||
url: http://192.168.1.103:18086
|
||||
database: pqsbase
|
||||
username: admin
|
||||
password: ${STEADY_INFLUXDB_PASSWORD:}
|
||||
ssl: false
|
||||
connect-timeout-ms: 5000
|
||||
read-timeout-ms: 30000
|
||||
|
||||
log:
|
||||
homeDir: D:\logs
|
||||
commonLevel: info
|
||||
|
||||
@@ -35,5 +35,26 @@
|
||||
<artifactId>add-ledger</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>wave-tool</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -9,11 +9,11 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 暂态事件时间字段按秒输出,避免接口响应携带毫秒。
|
||||
* 暂态事件时间字段按毫秒输出,保持与数据库 datetime(3) 精度一致。
|
||||
*/
|
||||
public class EventSecondTimeSerializer extends JsonSerializer<LocalDateTime> {
|
||||
public class EventMillisecondTimeSerializer extends JsonSerializer<LocalDateTime> {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
@Override
|
||||
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
@@ -8,8 +8,10 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventListQueryParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventWaveExportParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.vo.EventListVO;
|
||||
import com.njcn.gather.event.eventlist.service.EventListService;
|
||||
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
@@ -64,4 +66,21 @@ public class EventListController extends BaseController {
|
||||
public void exportTransientEvents(@RequestBody EventListQueryParam param) {
|
||||
eventListService.exportTransientEvents(param);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查看暂态事件波形")
|
||||
@GetMapping("/transient/{eventId}/wave")
|
||||
public HttpResult<WaveComtradeResultVO> getTransientEventWave(@PathVariable("eventId") String eventId) {
|
||||
String methodDescribe = getMethodDescribe("getTransientEventWave");
|
||||
LogUtil.njcnDebug(log, "{},开始查看暂态事件波形,eventId={}", methodDescribe, eventId);
|
||||
WaveComtradeResultVO result = eventListService.getTransientEventWave(eventId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DOWNLOAD)
|
||||
@ApiOperation("导出选中暂态事件波形")
|
||||
@PostMapping("/transient/wave/export")
|
||||
public void exportTransientEventWaves(@RequestBody EventWaveExportParam param) {
|
||||
eventListService.exportTransientEventWaves(param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,6 @@ public interface EventListMapper extends BaseMapper<MpEventDetailPO> {
|
||||
List<MpEventDetailPO> selectTransientExportList(@Param("param") EventListQueryParam param, @Param("limit") Integer limit);
|
||||
|
||||
MpEventDetailPO selectTransientDetail(@Param("eventId") String eventId);
|
||||
|
||||
List<MpEventDetailPO> selectTransientDetailsByIds(@Param("eventIds") List<String> eventIds);
|
||||
}
|
||||
|
||||
@@ -47,9 +47,6 @@
|
||||
<if test="param.phase != null and param.phase != ''">
|
||||
AND phase = #{param.phase}
|
||||
</if>
|
||||
<if test="param.eventDescribe != null and param.eventDescribe != ''">
|
||||
AND event_describe LIKE CONCAT('%', #{param.eventDescribe}, '%')
|
||||
</if>
|
||||
<if test="param.durationMin != null">
|
||||
AND duration >= #{param.durationMin}
|
||||
</if>
|
||||
@@ -101,4 +98,14 @@
|
||||
WHERE event_id = #{eventId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectTransientDetailsByIds" resultType="com.njcn.gather.event.eventlist.pojo.po.MpEventDetailPO">
|
||||
SELECT
|
||||
<include refid="EventDetailColumns"/>
|
||||
FROM r_mp_event_detail
|
||||
WHERE event_id IN
|
||||
<foreach collection="eventIds" item="eventId" open="(" separator="," close=")">
|
||||
#{eventId}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -18,10 +18,10 @@ import java.util.List;
|
||||
@ApiModel("暂态事件列表查询参数")
|
||||
public class EventListQueryParam extends BaseParam {
|
||||
|
||||
@ApiModelProperty("发生时刻开始,格式 yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("发生时刻开始,格式 yyyy-MM-dd HH:mm:ss.SSS,兼容 yyyy-MM-dd HH:mm:ss")
|
||||
private String startTimeStart;
|
||||
|
||||
@ApiModelProperty("发生时刻结束,格式 yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("发生时刻结束,格式 yyyy-MM-dd HH:mm:ss.SSS,兼容 yyyy-MM-dd HH:mm:ss")
|
||||
private String startTimeEnd;
|
||||
|
||||
@ApiModelProperty("事件类型")
|
||||
@@ -30,9 +30,6 @@ public class EventListQueryParam extends BaseParam {
|
||||
@ApiModelProperty("相别")
|
||||
private String phase;
|
||||
|
||||
@ApiModelProperty("事件描述关键字")
|
||||
private String eventDescribe;
|
||||
|
||||
@ApiModelProperty("持续时间下限,单位秒")
|
||||
private BigDecimal durationMin;
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.gather.event.eventlist.pojo.param;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 事件波形批量导出参数。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("事件波形批量导出参数")
|
||||
public class EventWaveExportParam {
|
||||
|
||||
@ApiModelProperty("页面勾选的事件 ID 列表")
|
||||
private List<String> eventIds = new ArrayList<String>();
|
||||
}
|
||||
@@ -2,11 +2,12 @@ package com.njcn.gather.event.eventlist.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.njcn.gather.event.eventlist.config.EventSecondTimeSerializer;
|
||||
import com.njcn.gather.event.eventlist.config.EventMillisecondTimeSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -25,10 +26,11 @@ public class EventListVO implements Serializable {
|
||||
|
||||
private String measurementPointId;
|
||||
|
||||
private String eventType;
|
||||
|
||||
@Excel(name = "设备名称", width = 25)
|
||||
private String equipmentName;
|
||||
@Excel(name = "发生时刻", width = 25, exportFormat = "yyyy-MM-dd HH:mm:ss.SSS")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = EventMillisecondTimeSerializer.class)
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Excel(name = "工程名称", width = 25)
|
||||
private String engineeringName;
|
||||
@@ -36,29 +38,32 @@ public class EventListVO implements Serializable {
|
||||
@Excel(name = "项目名称", width = 25)
|
||||
private String projectName;
|
||||
|
||||
@Excel(name = "发生时刻", width = 25, exportFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = EventSecondTimeSerializer.class)
|
||||
private LocalDateTime startTime;
|
||||
@Excel(name = "设备名称", width = 25)
|
||||
private String equipmentName;
|
||||
|
||||
private String mac;
|
||||
|
||||
@Excel(name = "监测点名称", width = 25)
|
||||
private String lineName;
|
||||
|
||||
@Excel(name = "事件描述", width = 25)
|
||||
private String eventDescribe;
|
||||
|
||||
@Excel(name = "事件发生位置", width = 18)
|
||||
private String sagsource;
|
||||
|
||||
@Excel(name = "相别", width = 15)
|
||||
private String phase;
|
||||
@Excel(name = "暂降/暂升幅值(%)", width = 20)
|
||||
private BigDecimal featureAmplitude;
|
||||
|
||||
@Excel(name = "持续时间(s)", width = 18)
|
||||
private BigDecimal duration;
|
||||
|
||||
@Excel(name = "暂降/暂升幅值(%)", width = 20)
|
||||
private BigDecimal featureAmplitude;
|
||||
@Excel(name = "事件类型", width = 18)
|
||||
private String eventType;
|
||||
|
||||
@Excel(name = "相别", width = 15)
|
||||
private String phase;
|
||||
|
||||
@Excel(name = "事件描述", width = 25)
|
||||
@JsonProperty("event_describe")
|
||||
private String eventDescribe;
|
||||
|
||||
@Excel(name = "事件发生位置", width = 18)
|
||||
private String sagsource;
|
||||
|
||||
private String wavePath;
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.njcn.gather.event.eventlist.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventListQueryParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventWaveExportParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.vo.EventListVO;
|
||||
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
|
||||
|
||||
/**
|
||||
* 事件列表服务。
|
||||
@@ -14,4 +16,8 @@ public interface EventListService {
|
||||
EventListVO getTransientEventDetail(String eventId);
|
||||
|
||||
void exportTransientEvents(EventListQueryParam param);
|
||||
|
||||
WaveComtradeResultVO getTransientEventWave(String eventId);
|
||||
|
||||
void exportTransientEventWaves(EventWaveExportParam param);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,61 @@
|
||||
package com.njcn.gather.event.eventlist.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.event.eventlist.mapper.EventListMapper;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventListQueryParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventWaveExportParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.po.MpEventDetailPO;
|
||||
import com.njcn.gather.event.eventlist.pojo.vo.EventListVO;
|
||||
import com.njcn.gather.event.eventlist.service.EventListService;
|
||||
import com.njcn.gather.system.cfg.service.ISysConfigService;
|
||||
import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
||||
import com.njcn.gather.system.dictionary.pojo.po.DictType;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.gather.system.dictionary.service.IDictTypeService;
|
||||
import com.njcn.gather.system.pojo.constant.DictConst;
|
||||
import com.njcn.gather.system.util.ExportFileNameUtil;
|
||||
import com.njcn.gather.tool.addledger.pojo.param.AddLedgerLinePathQueryParam;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||
import com.njcn.gather.tool.addledger.service.AddLedgerService;
|
||||
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
|
||||
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
|
||||
import com.njcn.gather.tool.wave.service.WaveService;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import com.njcn.web.utils.HttpServletUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* 事件列表服务实现。
|
||||
@@ -37,8 +68,12 @@ public class EventListServiceImpl implements EventListService {
|
||||
private static final int LEDGER_LINE_QUERY_LIMIT = 1000;
|
||||
private static final int EVENT_LINE_ID_QUERY_LIMIT = 1000;
|
||||
private static final int EXPORT_LIMIT = 5000;
|
||||
private static final int WAVE_EXPORT_LIMIT = 500;
|
||||
private static final String EMPTY_TEXT = "-";
|
||||
private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final String WAVE_EXPORT_SUCCESS = "成功";
|
||||
private static final String WAVE_EXPORT_FAIL = "失败";
|
||||
private static final BigDecimal PERCENT_BASE = new BigDecimal("100");
|
||||
private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
private static final DateTimeFormatter[] INPUT_FORMATTERS = new DateTimeFormatter[]{
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"),
|
||||
@@ -48,6 +83,10 @@ public class EventListServiceImpl implements EventListService {
|
||||
|
||||
private final EventListMapper eventListMapper;
|
||||
private final AddLedgerService addLedgerService;
|
||||
private final ISysConfigService sysConfigService;
|
||||
private final IDictTypeService dictTypeService;
|
||||
private final IDictDataService dictDataService;
|
||||
private final WaveService waveService;
|
||||
|
||||
@Override
|
||||
public Page<EventListVO> pageTransientEvents(EventListQueryParam param) {
|
||||
@@ -89,10 +128,298 @@ public class EventListServiceImpl implements EventListService {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导出数据超过 5000 条,请缩小查询条件");
|
||||
}
|
||||
exportRecords = buildEventList(eventDetails);
|
||||
fillExportEventTypeNames(exportRecords);
|
||||
} else {
|
||||
exportRecords = Collections.emptyList();
|
||||
}
|
||||
ExcelUtil.exportExcel("暂态事件列表.xlsx", "暂态事件列表", EventListVO.class, exportRecords);
|
||||
ExcelUtil.exportExcel(ExportFileNameUtil.appendToday("暂态事件列表.xlsx"), "暂态事件列表", EventListVO.class, exportRecords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WaveComtradeResultVO getTransientEventWave(String eventId) {
|
||||
MpEventDetailPO eventDetail = requireEventDetail(eventId);
|
||||
AddLedgerLinePathVO linePath = requireLinePath(eventDetail);
|
||||
EventWavePathResolver.WaveFilePath waveFilePath = resolveWaveFilePath(eventDetail, linePath);
|
||||
if (!Files.exists(waveFilePath.getCfgPath())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "cfg 波形文件不存在");
|
||||
}
|
||||
if (!Files.exists(waveFilePath.getDatPath())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "dat 波形文件不存在");
|
||||
}
|
||||
WaveComtradeParseParam parseParam = new WaveComtradeParseParam();
|
||||
parseParam.setMonitorName(defaultText(linePath.getLineName()));
|
||||
try (InputStream cfgInputStream = new FileInputStream(waveFilePath.getCfgPath().toFile());
|
||||
InputStream datInputStream = new FileInputStream(waveFilePath.getDatPath().toFile())) {
|
||||
return waveService.parseComtrade(cfgInputStream, datInputStream, parseParam);
|
||||
} catch (IOException ex) {
|
||||
log.error("读取暂态事件波形文件失败,eventId={}", eventDetail.getEventId(), ex);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "读取波形文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportTransientEventWaves(EventWaveExportParam param) {
|
||||
List<String> eventIds = normalizeExportEventIds(param);
|
||||
List<WaveExportItem> exportItems = buildWaveExportItems(eventIds);
|
||||
writeWaveExportZip(exportItems);
|
||||
}
|
||||
|
||||
private MpEventDetailPO requireEventDetail(String eventId) {
|
||||
String normalizedEventId = trimToNull(eventId);
|
||||
if (normalizedEventId == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "事件 ID 不能为空");
|
||||
}
|
||||
MpEventDetailPO eventDetail = eventListMapper.selectTransientDetail(normalizedEventId);
|
||||
if (eventDetail == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "暂态事件不存在");
|
||||
}
|
||||
return eventDetail;
|
||||
}
|
||||
|
||||
private AddLedgerLinePathVO requireLinePath(MpEventDetailPO eventDetail) {
|
||||
List<String> lineIds = new ArrayList<String>();
|
||||
lineIds.add(eventDetail.getMeasurementPointId());
|
||||
Map<String, AddLedgerLinePathVO> linePathMap = addLedgerService.listLinePathByLineIds(lineIds);
|
||||
AddLedgerLinePathVO linePath = linePathMap.get(eventDetail.getMeasurementPointId());
|
||||
if (linePath == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "监测点台账不存在或已删除");
|
||||
}
|
||||
return linePath;
|
||||
}
|
||||
|
||||
private List<String> normalizeExportEventIds(EventWaveExportParam param) {
|
||||
List<String> eventIds = normalizeIds(param == null ? null : param.getEventIds());
|
||||
if (eventIds.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "请选择需要导出的事件");
|
||||
}
|
||||
if (eventIds.size() > WAVE_EXPORT_LIMIT) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "波形导出事件数量不能超过 500 条");
|
||||
}
|
||||
return eventIds;
|
||||
}
|
||||
|
||||
private List<WaveExportItem> buildWaveExportItems(List<String> eventIds) {
|
||||
List<MpEventDetailPO> eventDetails = eventListMapper.selectTransientDetailsByIds(eventIds);
|
||||
Map<String, MpEventDetailPO> eventMap = new LinkedHashMap<String, MpEventDetailPO>();
|
||||
List<String> lineIds = new ArrayList<String>();
|
||||
for (MpEventDetailPO eventDetail : eventDetails) {
|
||||
eventMap.put(eventDetail.getEventId(), eventDetail);
|
||||
String lineId = trimToNull(eventDetail.getMeasurementPointId());
|
||||
if (lineId != null && !lineIds.contains(lineId)) {
|
||||
lineIds.add(lineId);
|
||||
}
|
||||
}
|
||||
Map<String, AddLedgerLinePathVO> linePathMap = addLedgerService.listLinePathByLineIds(lineIds);
|
||||
List<WaveExportItem> exportItems = new ArrayList<WaveExportItem>();
|
||||
for (String eventId : eventIds) {
|
||||
WaveExportItem item = new WaveExportItem();
|
||||
item.setEventId(eventId);
|
||||
MpEventDetailPO eventDetail = eventMap.get(eventId);
|
||||
item.setEventDetail(eventDetail);
|
||||
if (eventDetail == null) {
|
||||
item.fail("事件不存在");
|
||||
exportItems.add(item);
|
||||
continue;
|
||||
}
|
||||
AddLedgerLinePathVO linePath = linePathMap.get(eventDetail.getMeasurementPointId());
|
||||
item.setLinePath(linePath);
|
||||
item.setEventVO(buildEventVO(eventDetail, linePath));
|
||||
fillWaveExportFileState(item);
|
||||
exportItems.add(item);
|
||||
}
|
||||
return exportItems;
|
||||
}
|
||||
|
||||
private void fillWaveExportFileState(WaveExportItem item) {
|
||||
MpEventDetailPO eventDetail = item.getEventDetail();
|
||||
if (item.getLinePath() == null) {
|
||||
item.fail("监测点台账不存在或已删除");
|
||||
return;
|
||||
}
|
||||
if (eventDetail.getFileFlag() == null || eventDetail.getFileFlag() != 1) {
|
||||
item.fail("波形文件未招");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
EventWavePathResolver.WaveFilePath waveFilePath = resolveWaveFilePath(eventDetail, item.getLinePath());
|
||||
item.setWaveFilePath(waveFilePath);
|
||||
boolean cfgExists = Files.exists(waveFilePath.getCfgPath());
|
||||
boolean datExists = Files.exists(waveFilePath.getDatPath());
|
||||
if (cfgExists && datExists) {
|
||||
item.success();
|
||||
return;
|
||||
}
|
||||
if (!cfgExists && !datExists) {
|
||||
item.fail("cfg/dat 文件不存在");
|
||||
} else if (!cfgExists) {
|
||||
item.fail("cfg 文件不存在");
|
||||
} else {
|
||||
item.fail("dat 文件不存在");
|
||||
}
|
||||
} catch (BusinessException ex) {
|
||||
item.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private EventWavePathResolver.WaveFilePath resolveWaveFilePath(MpEventDetailPO eventDetail, AddLedgerLinePathVO linePath) {
|
||||
try {
|
||||
return EventWavePathResolver.resolve(sysConfigService.getWaveStoragePath(), linePath.getEquipmentMac(), eventDetail.getWavePath());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeWaveExportZip(List<WaveExportItem> exportItems) {
|
||||
HttpServletResponse response = HttpServletUtil.getResponse();
|
||||
try {
|
||||
String fileName = URLEncoder.encode(ExportFileNameUtil.appendToday("选中事件波形导出.zip"), "UTF-8");
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/zip;charset=UTF-8");
|
||||
try (ServletOutputStream outputStream = response.getOutputStream();
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
|
||||
writeZipEntry(zipOutputStream, ExportFileNameUtil.appendToday("选中事件日志.xlsx"), buildWaveExportWorkbook(exportItems));
|
||||
for (int i = 0; i < exportItems.size(); i++) {
|
||||
WaveExportItem item = exportItems.get(i);
|
||||
EventWavePathResolver.WaveFilePath waveFilePath = item.getWaveFilePath();
|
||||
if (waveFilePath == null) {
|
||||
continue;
|
||||
}
|
||||
String folder = buildZipFolderName(i + 1, item.getEventId());
|
||||
writeWaveFileIfExists(zipOutputStream, folder + "/" + waveFilePath.getCfgPath().getFileName(), waveFilePath.getCfgPath());
|
||||
writeWaveFileIfExists(zipOutputStream, folder + "/" + waveFilePath.getDatPath().getFileName(), waveFilePath.getDatPath());
|
||||
}
|
||||
zipOutputStream.flush();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("导出暂态事件波形失败", ex);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导出波形文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] buildWaveExportWorkbook(List<WaveExportItem> exportItems) throws IOException {
|
||||
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||
try {
|
||||
writeEventLogSheet(workbook, exportItems);
|
||||
writeWaveFileSheet(workbook, exportItems);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
} finally {
|
||||
workbook.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEventLogSheet(XSSFWorkbook workbook, List<WaveExportItem> exportItems) {
|
||||
XSSFSheet sheet = workbook.createSheet("选中事件列表");
|
||||
String[] headers = new String[]{"发生时刻", "工程名称", "项目名称", "设备名称", "监测点名称", "暂降/暂升幅值(%)",
|
||||
"持续时间(s)", "事件类型", "相别", "事件描述", "事件发生位置"};
|
||||
int[] columnWidths = new int[]{25, 25, 25, 25, 25, 20, 18, 18, 15, 25, 18};
|
||||
writeHeader(sheet, headers, createHeaderStyle(workbook));
|
||||
Map<String, String> eventTypeNameMap = buildEventTypeNameMap();
|
||||
int rowIndex = 1;
|
||||
for (WaveExportItem item : exportItems) {
|
||||
EventListVO vo = item.getEventVO();
|
||||
if (vo == null) {
|
||||
continue;
|
||||
}
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
writeCells(row, new Object[]{formatDateTime(vo.getStartTime()), vo.getEngineeringName(), vo.getProjectName(),
|
||||
vo.getEquipmentName(), vo.getLineName(), vo.getFeatureAmplitude(), vo.getDuration(),
|
||||
translateEventType(vo.getEventType(), eventTypeNameMap), vo.getPhase(), vo.getEventDescribe(),
|
||||
vo.getSagsource()});
|
||||
}
|
||||
applyColumnWidth(sheet, columnWidths);
|
||||
}
|
||||
|
||||
private void writeWaveFileSheet(XSSFWorkbook workbook, List<WaveExportItem> exportItems) {
|
||||
XSSFSheet sheet = workbook.createSheet("波形文件清单");
|
||||
String[] headers = new String[]{"事件ID", "设备MAC", "wavePath", "cfg文件路径", "dat文件路径", "导出状态", "失败原因"};
|
||||
writeHeader(sheet, headers, createHeaderStyle(workbook));
|
||||
int rowIndex = 1;
|
||||
for (WaveExportItem item : exportItems) {
|
||||
EventWavePathResolver.WaveFilePath waveFilePath = item.getWaveFilePath();
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
writeCells(row, new Object[]{item.getEventId(),
|
||||
item.getLinePath() == null ? EMPTY_TEXT : item.getLinePath().getEquipmentMac(),
|
||||
item.getEventDetail() == null ? EMPTY_TEXT : item.getEventDetail().getWavePath(),
|
||||
waveFilePath == null ? EMPTY_TEXT : waveFilePath.getCfgPath().toString(),
|
||||
waveFilePath == null ? EMPTY_TEXT : waveFilePath.getDatPath().toString(),
|
||||
item.getStatus(), defaultText(item.getFailReason())});
|
||||
}
|
||||
applyColumnWidth(sheet, headers.length);
|
||||
}
|
||||
|
||||
private CellStyle createHeaderStyle(XSSFWorkbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
return style;
|
||||
}
|
||||
|
||||
private void writeHeader(XSSFSheet sheet, String[] headers, CellStyle style) {
|
||||
Row row = sheet.createRow(0);
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = row.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCells(Row row, Object[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
Cell cell = row.createCell(i);
|
||||
Object value = values[i];
|
||||
cell.setCellValue(value == null ? "" : String.valueOf(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyColumnWidth(XSSFSheet sheet, int columnCount) {
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
sheet.setColumnWidth(i, 20 * 256);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyColumnWidth(XSSFSheet sheet, int[] columnWidths) {
|
||||
for (int i = 0; i < columnWidths.length; i++) {
|
||||
sheet.setColumnWidth(i, columnWidths[i] * 256);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeZipEntry(ZipOutputStream zipOutputStream, String entryName, byte[] content) throws IOException {
|
||||
ZipEntry zipEntry = new ZipEntry(entryName);
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
zipOutputStream.write(content);
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
|
||||
private void writeWaveFileIfExists(ZipOutputStream zipOutputStream, String entryName, Path filePath) throws IOException {
|
||||
if (!Files.exists(filePath)) {
|
||||
return;
|
||||
}
|
||||
zipOutputStream.putNextEntry(new ZipEntry(entryName));
|
||||
try (InputStream inputStream = new FileInputStream(filePath.toFile())) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
zipOutputStream.write(buffer, 0, length);
|
||||
}
|
||||
}
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
|
||||
private String buildZipFolderName(int index, String eventId) {
|
||||
return index + "_" + sanitizeZipSegment(eventId);
|
||||
}
|
||||
|
||||
private String sanitizeZipSegment(String value) {
|
||||
String text = trimToNull(value);
|
||||
return text == null ? "unknown" : text.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||
}
|
||||
|
||||
private String formatDateTime(LocalDateTime value) {
|
||||
return value == null ? EMPTY_TEXT : OUTPUT_FORMATTER.format(value);
|
||||
}
|
||||
|
||||
private List<EventListVO> buildEventList(List<MpEventDetailPO> eventDetails) {
|
||||
@@ -121,15 +448,16 @@ public class EventListServiceImpl implements EventListService {
|
||||
vo.setMeasurementPointId(eventDetail.getMeasurementPointId());
|
||||
vo.setEventType(eventDetail.getEventType());
|
||||
vo.setEquipmentName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getEquipmentName()));
|
||||
vo.setMac(linePath == null ? EMPTY_TEXT : defaultText(linePath.getEquipmentMac()));
|
||||
vo.setEngineeringName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getEngineeringName()));
|
||||
vo.setProjectName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getProjectName()));
|
||||
vo.setStartTime(eventDetail.getStartTime());
|
||||
vo.setLineName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getLineName()));
|
||||
vo.setEventDescribe(defaultText(eventDetail.getEventDescribe(), eventDetail.getEventType()));
|
||||
vo.setEventDescribe(trimToNull(eventDetail.getEventDescribe()));
|
||||
vo.setSagsource(defaultText(eventDetail.getSagsource()));
|
||||
vo.setPhase(defaultText(eventDetail.getPhase()));
|
||||
vo.setDuration(eventDetail.getDuration());
|
||||
vo.setFeatureAmplitude(eventDetail.getFeatureAmplitude());
|
||||
vo.setFeatureAmplitude(toPercent(eventDetail.getFeatureAmplitude()));
|
||||
vo.setWavePath(eventDetail.getWavePath());
|
||||
vo.setFileFlag(eventDetail.getFileFlag());
|
||||
vo.setDealFlag(eventDetail.getDealFlag());
|
||||
@@ -155,11 +483,12 @@ public class EventListServiceImpl implements EventListService {
|
||||
validateRange(queryParam.getFeatureAmplitudeMin(), queryParam.getFeatureAmplitudeMax(), "幅值下限不能大于上限");
|
||||
validateFlag(queryParam.getFileFlag(), "波形文件状态只能是 0 或 1");
|
||||
validateDealFlag(queryParam.getDealFlag());
|
||||
queryParam.setFeatureAmplitudeMin(fromPercent(queryParam.getFeatureAmplitudeMin()));
|
||||
queryParam.setFeatureAmplitudeMax(fromPercent(queryParam.getFeatureAmplitudeMax()));
|
||||
queryParam.setStartTimeStart(OUTPUT_FORMATTER.format(startTime));
|
||||
queryParam.setStartTimeEnd(OUTPUT_FORMATTER.format(endTime));
|
||||
queryParam.setEventType(trimToNull(queryParam.getEventType()));
|
||||
queryParam.setPhase(trimToNull(queryParam.getPhase()));
|
||||
queryParam.setEventDescribe(trimToNull(queryParam.getEventDescribe()));
|
||||
queryParam.setEngineeringName(trimToNull(queryParam.getEngineeringName()));
|
||||
queryParam.setProjectName(trimToNull(queryParam.getProjectName()));
|
||||
queryParam.setEquipmentName(trimToNull(queryParam.getEquipmentName()));
|
||||
@@ -222,10 +551,10 @@ public class EventListServiceImpl implements EventListService {
|
||||
try {
|
||||
return LocalDateTime.parse(text, formatter);
|
||||
} catch (DateTimeParseException ignored) {
|
||||
// 尝试下一个前端可能传入的时间格式。
|
||||
// 尝试下一种前端可能传入的时间格式。
|
||||
}
|
||||
}
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss");
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss 或 yyyy-MM-dd HH:mm:ss.SSS");
|
||||
}
|
||||
|
||||
private List<String> normalizeIds(List<String> ids) {
|
||||
@@ -255,6 +584,59 @@ public class EventListServiceImpl implements EventListService {
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal toPercent(BigDecimal value) {
|
||||
return value == null ? null : value.multiply(PERCENT_BASE);
|
||||
}
|
||||
|
||||
private BigDecimal fromPercent(BigDecimal value) {
|
||||
return value == null ? null : value.divide(PERCENT_BASE);
|
||||
}
|
||||
|
||||
private void fillExportEventTypeNames(List<EventListVO> exportRecords) {
|
||||
Map<String, String> eventTypeNameMap = buildEventTypeNameMap();
|
||||
for (EventListVO record : exportRecords) {
|
||||
record.setEventType(translateEventType(record.getEventType(), eventTypeNameMap));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> buildEventTypeNameMap() {
|
||||
DictType dictType = dictTypeService.getByCode(DictConst.EVENT_TYPE_DICT);
|
||||
if (dictType == null) {
|
||||
dictType = dictTypeService.lambdaQuery()
|
||||
.eq(DictType::getName, DictConst.EVENT_TYPE_DICT)
|
||||
.eq(DictType::getState, DataStateEnum.ENABLE.getCode())
|
||||
.orderByAsc(DictType::getSort)
|
||||
.orderByDesc(DictType::getId)
|
||||
.last("LIMIT 1")
|
||||
.one();
|
||||
}
|
||||
if (dictType == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<DictData> dictDataList = dictDataService.listDictDataByTypeId(dictType.getId());
|
||||
Map<String, String> eventTypeNameMap = new LinkedHashMap<String, String>();
|
||||
for (DictData dictData : dictDataList) {
|
||||
String id = trimToNull(dictData.getId());
|
||||
String code = trimToNull(dictData.getCode());
|
||||
if (id != null) {
|
||||
eventTypeNameMap.put(id, defaultText(dictData.getName(), id));
|
||||
}
|
||||
if (code != null) {
|
||||
eventTypeNameMap.put(code, defaultText(dictData.getName(), code));
|
||||
}
|
||||
}
|
||||
return eventTypeNameMap;
|
||||
}
|
||||
|
||||
private String translateEventType(String eventType, Map<String, String> eventTypeNameMap) {
|
||||
String normalizedEventType = trimToNull(eventType);
|
||||
if (normalizedEventType == null) {
|
||||
return EMPTY_TEXT;
|
||||
}
|
||||
String eventTypeName = eventTypeNameMap.get(normalizedEventType);
|
||||
return eventTypeName == null ? normalizedEventType : eventTypeName;
|
||||
}
|
||||
|
||||
private void validateFlag(Integer flag, String message) {
|
||||
if (flag != null && flag != 0 && flag != 1) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, message);
|
||||
@@ -283,4 +665,79 @@ public class EventListServiceImpl implements EventListService {
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
private static class WaveExportItem {
|
||||
|
||||
private String eventId;
|
||||
|
||||
private MpEventDetailPO eventDetail;
|
||||
|
||||
private AddLedgerLinePathVO linePath;
|
||||
|
||||
private EventListVO eventVO;
|
||||
|
||||
private EventWavePathResolver.WaveFilePath waveFilePath;
|
||||
|
||||
private String status = WAVE_EXPORT_FAIL;
|
||||
|
||||
private String failReason;
|
||||
|
||||
private String getEventId() {
|
||||
return eventId;
|
||||
}
|
||||
|
||||
private void setEventId(String eventId) {
|
||||
this.eventId = eventId;
|
||||
}
|
||||
|
||||
private MpEventDetailPO getEventDetail() {
|
||||
return eventDetail;
|
||||
}
|
||||
|
||||
private void setEventDetail(MpEventDetailPO eventDetail) {
|
||||
this.eventDetail = eventDetail;
|
||||
}
|
||||
|
||||
private AddLedgerLinePathVO getLinePath() {
|
||||
return linePath;
|
||||
}
|
||||
|
||||
private void setLinePath(AddLedgerLinePathVO linePath) {
|
||||
this.linePath = linePath;
|
||||
}
|
||||
|
||||
private EventListVO getEventVO() {
|
||||
return eventVO;
|
||||
}
|
||||
|
||||
private void setEventVO(EventListVO eventVO) {
|
||||
this.eventVO = eventVO;
|
||||
}
|
||||
|
||||
private EventWavePathResolver.WaveFilePath getWaveFilePath() {
|
||||
return waveFilePath;
|
||||
}
|
||||
|
||||
private void setWaveFilePath(EventWavePathResolver.WaveFilePath waveFilePath) {
|
||||
this.waveFilePath = waveFilePath;
|
||||
}
|
||||
|
||||
private String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
private String getFailReason() {
|
||||
return failReason;
|
||||
}
|
||||
|
||||
private void success() {
|
||||
this.status = WAVE_EXPORT_SUCCESS;
|
||||
this.failReason = null;
|
||||
}
|
||||
|
||||
private void fail(String failReason) {
|
||||
this.status = WAVE_EXPORT_FAIL;
|
||||
this.failReason = failReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.njcn.gather.event.eventlist.service.impl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* 暂态事件波形文件路径解析器。
|
||||
*/
|
||||
final class EventWavePathResolver {
|
||||
|
||||
private EventWavePathResolver() {
|
||||
}
|
||||
|
||||
static WaveFilePath resolve(String storageRoot, String equipmentMac, String wavePath) {
|
||||
String rootText = requireText(storageRoot, "波形存储路径未配置");
|
||||
String mac = requireText(equipmentMac, "设备 MAC 为空");
|
||||
String normalizedWavePath = normalizeWavePath(wavePath);
|
||||
|
||||
Path rootPath = Paths.get(rootText).normalize();
|
||||
Path rawWavePath = Paths.get(normalizedWavePath).normalize();
|
||||
Path eventBasePath;
|
||||
if (rawWavePath.isAbsolute()) {
|
||||
eventBasePath = rawWavePath;
|
||||
} else if (startsWithSegment(rawWavePath, mac)) {
|
||||
eventBasePath = rootPath.resolve(rawWavePath).normalize();
|
||||
} else {
|
||||
eventBasePath = rootPath.resolve(mac).resolve(rawWavePath).normalize();
|
||||
}
|
||||
|
||||
if (!eventBasePath.startsWith(rootPath)) {
|
||||
throw new IllegalArgumentException("波形路径非法");
|
||||
}
|
||||
|
||||
String fileName = eventBasePath.getFileName().toString();
|
||||
return new WaveFilePath(eventBasePath.resolveSibling(fileName + ".cfg"),
|
||||
eventBasePath.resolveSibling(fileName + ".dat"));
|
||||
}
|
||||
|
||||
private static String normalizeWavePath(String value) {
|
||||
String wavePath = requireText(value, "事件 wave_path 为空").replace("\\", "/");
|
||||
String lowerWavePath = wavePath.toLowerCase();
|
||||
if (lowerWavePath.endsWith(".cfg") || lowerWavePath.endsWith(".dat")) {
|
||||
wavePath = wavePath.substring(0, wavePath.length() - 4);
|
||||
}
|
||||
if (wavePath.contains("..")) {
|
||||
throw new IllegalArgumentException("波形路径非法");
|
||||
}
|
||||
return wavePath;
|
||||
}
|
||||
|
||||
private static boolean startsWithSegment(Path path, String segment) {
|
||||
return path.getNameCount() > 0 && path.getName(0).toString().equalsIgnoreCase(segment);
|
||||
}
|
||||
|
||||
private static String requireText(String value, String message) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
static class WaveFilePath {
|
||||
|
||||
private final Path cfgPath;
|
||||
|
||||
private final Path datPath;
|
||||
|
||||
private WaveFilePath(Path cfgPath, Path datPath) {
|
||||
this.cfgPath = cfgPath;
|
||||
this.datPath = datPath;
|
||||
}
|
||||
|
||||
Path getCfgPath() {
|
||||
return cfgPath;
|
||||
}
|
||||
|
||||
Path getDatPath() {
|
||||
return datPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.gather.event.eventlist.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.njcn.gather.event.eventlist.pojo.param.EventListQueryParam;
|
||||
import com.njcn.gather.event.eventlist.pojo.vo.EventListVO;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class EventListTimeFormatTest {
|
||||
|
||||
@Test
|
||||
public void startTimeJsonKeepsMilliseconds() throws Exception {
|
||||
EventListVO vo = new EventListVO();
|
||||
vo.setStartTime(LocalDateTime.of(2026, 5, 10, 13, 41, 17, 944000000));
|
||||
|
||||
String json = new ObjectMapper().writeValueAsString(vo);
|
||||
|
||||
assertTrue(json.contains("\"startTime\":\"2026-05-10 13:41:17.944\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryTimeNormalizationKeepsMilliseconds() throws Exception {
|
||||
EventListQueryParam param = new EventListQueryParam();
|
||||
param.setStartTimeStart("2026-05-10 13:41:17.944");
|
||||
param.setStartTimeEnd("2026-05-10 13:41:18.123");
|
||||
|
||||
normalizeQueryParam(param);
|
||||
|
||||
assertEquals("2026-05-10 13:41:17.944", param.getStartTimeStart());
|
||||
assertEquals("2026-05-10 13:41:18.123", param.getStartTimeEnd());
|
||||
}
|
||||
|
||||
private void normalizeQueryParam(EventListQueryParam param) throws Exception {
|
||||
EventListServiceImpl service = new EventListServiceImpl(null, null, null, null, null, null);
|
||||
Method method = EventListServiceImpl.class.getDeclaredMethod("normalizeQueryParam", EventListQueryParam.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(service, param);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.gather.event.eventlist.service.impl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class EventWavePathResolverTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
resolvesAbsoluteWavePath();
|
||||
resolvesRelativeWavePathWithMacPrefix();
|
||||
resolvesRelativeWavePathAlreadyContainingMac();
|
||||
}
|
||||
|
||||
private static void resolvesAbsoluteWavePath() {
|
||||
EventWavePathResolver.WaveFilePath result = EventWavePathResolver.resolve("D:/", "AA-BB", "D:/wave/event-001.cfg");
|
||||
|
||||
assertPathEquals(Paths.get("D:/wave/event-001.cfg"), result.getCfgPath(), "absolute cfg");
|
||||
assertPathEquals(Paths.get("D:/wave/event-001.dat"), result.getDatPath(), "absolute dat");
|
||||
}
|
||||
|
||||
private static void resolvesRelativeWavePathWithMacPrefix() {
|
||||
EventWavePathResolver.WaveFilePath result = EventWavePathResolver.resolve("D:/wave-root", "AA-BB", "event-001");
|
||||
|
||||
assertPathEquals(Paths.get("D:/wave-root/AA-BB/event-001.cfg"), result.getCfgPath(), "relative cfg");
|
||||
assertPathEquals(Paths.get("D:/wave-root/AA-BB/event-001.dat"), result.getDatPath(), "relative dat");
|
||||
}
|
||||
|
||||
private static void resolvesRelativeWavePathAlreadyContainingMac() {
|
||||
EventWavePathResolver.WaveFilePath result = EventWavePathResolver.resolve("D:/wave-root", "AA-BB", "AA-BB/event-001.dat");
|
||||
|
||||
assertPathEquals(Paths.get("D:/wave-root/AA-BB/event-001.cfg"), result.getCfgPath(), "relative mac cfg");
|
||||
assertPathEquals(Paths.get("D:/wave-root/AA-BB/event-001.dat"), result.getDatPath(), "relative mac dat");
|
||||
}
|
||||
|
||||
private static void assertPathEquals(Path expected, Path actual, String label) {
|
||||
if (!expected.normalize().equals(actual.normalize())) {
|
||||
throw new AssertionError(label + " expected " + expected + " but got " + actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ public class SteadyTrendFieldResolver {
|
||||
validateBasicParam(param);
|
||||
List<String> lineIds = normalizeTextList(param.getLineIds());
|
||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
||||
List<String> requestPhases = normalizeUpperList(param.getPhases());
|
||||
List<String> statTypes = normalizeUpperList(param.getStatTypes());
|
||||
if (statTypes.isEmpty()) {
|
||||
statTypes.add("AVG");
|
||||
@@ -47,7 +46,7 @@ public class SteadyTrendFieldResolver {
|
||||
for (String lineId : lineIds) {
|
||||
for (String indicatorCode : indicatorCodes) {
|
||||
SteadyTrendIndicatorDefinitionBO indicator = requireIndicator(indicatorCode);
|
||||
List<String> phases = resolvePhases(indicator, requestPhases);
|
||||
List<String> phases = resolvePhases(indicator);
|
||||
for (String phase : phases) {
|
||||
for (String statType : statTypes) {
|
||||
validateStatType(indicator, statType);
|
||||
@@ -57,7 +56,7 @@ public class SteadyTrendFieldResolver {
|
||||
}
|
||||
}
|
||||
if (result.size() > MAX_SERIES_COUNT) {
|
||||
throw fail("趋势曲线数量不能超过 24 条,请缩小监测点、指标、相别或统计类型范围");
|
||||
throw fail("趋势曲线数量不能超过 24 条,请缩小监测点、指标或统计类型范围");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -165,23 +164,8 @@ public class SteadyTrendFieldResolver {
|
||||
return indicator;
|
||||
}
|
||||
|
||||
private List<String> resolvePhases(SteadyTrendIndicatorDefinitionBO indicator, List<String> requestPhases) {
|
||||
if (requestPhases.isEmpty()) {
|
||||
return new ArrayList<String>(indicator.getPhaseCodes());
|
||||
}
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (String phase : requestPhases) {
|
||||
if (!"A".equals(phase) && !"B".equals(phase) && !"C".equals(phase) && !"T".equals(phase)) {
|
||||
throw fail("相别只能是 A、B、C、T");
|
||||
}
|
||||
if (indicator.getPhaseCodes().contains(phase) && !result.contains(phase)) {
|
||||
result.add(phase);
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
throw fail("指标 " + indicator.getIndicatorCode() + " 不支持当前相别");
|
||||
}
|
||||
return result;
|
||||
private List<String> resolvePhases(SteadyTrendIndicatorDefinitionBO indicator) {
|
||||
return new ArrayList<String>(indicator.getPhaseCodes());
|
||||
}
|
||||
|
||||
private void validateStatType(SteadyTrendIndicatorDefinitionBO indicator, String statType) {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.njcn.gather.steady.datavie.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewDetailParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewTemplateVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewVO;
|
||||
import com.njcn.gather.steady.datavie.service.SteadyDataViewService;
|
||||
@@ -40,16 +38,6 @@ public class SteadyDataViewController extends BaseController {
|
||||
/** 稳态数据查看服务。 */
|
||||
private final SteadyDataViewService steadyDataViewService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("分页查询稳态数据")
|
||||
@PostMapping("/page")
|
||||
public HttpResult<Page<SteadyDataViewVO>> pageSteadyData(@RequestBody SteadyDataViewQueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("pageSteadyData");
|
||||
LogUtil.njcnDebug(log, "{},开始分页查询稳态数据,param={}", methodDescribe, param);
|
||||
Page<SteadyDataViewVO> result = steadyDataViewService.pageSteadyData(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询稳态数据详情")
|
||||
@PostMapping("/detail")
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.njcn.gather.steady.datavie.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
@@ -12,11 +10,6 @@ import java.util.Map;
|
||||
*/
|
||||
public interface SteadyDataViewMapper {
|
||||
|
||||
Page<Map<String, Object>> selectSteadyPage(Page<Map<String, Object>> page,
|
||||
@Param("tableName") String tableName,
|
||||
@Param("columns") List<String> columns,
|
||||
@Param("param") SteadyDataViewQueryParam param);
|
||||
|
||||
Map<String, Object> selectSteadyDetail(@Param("tableName") String tableName,
|
||||
@Param("columns") List<String> columns,
|
||||
@Param("lineId") String lineId,
|
||||
|
||||
@@ -3,39 +3,6 @@
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.steady.datavie.mapper.SteadyDataViewMapper">
|
||||
|
||||
<sql id="SteadyDataWhere">
|
||||
<where>
|
||||
<if test="param.timeStart != null and param.timeStart != ''">
|
||||
AND TIMEID >= #{param.timeStart}
|
||||
</if>
|
||||
<if test="param.timeEnd != null and param.timeEnd != ''">
|
||||
AND TIMEID <= #{param.timeEnd}
|
||||
</if>
|
||||
<if test="param.phasicType != null and param.phasicType != ''">
|
||||
AND PHASIC_TYPE = #{param.phasicType}
|
||||
</if>
|
||||
<if test="param.qualityFlag != null">
|
||||
AND QUALITYFLAG = #{param.qualityFlag}
|
||||
</if>
|
||||
<if test="param.lineIds != null and param.lineIds.size() > 0">
|
||||
AND LINEID IN
|
||||
<foreach collection="param.lineIds" item="lineId" open="(" separator="," close=")">
|
||||
#{lineId}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
</sql>
|
||||
|
||||
<select id="selectSteadyPage" resultType="java.util.LinkedHashMap">
|
||||
SELECT
|
||||
<foreach collection="columns" item="column" separator=",">
|
||||
`${column}`
|
||||
</foreach>
|
||||
FROM `${tableName}`
|
||||
<include refid="SteadyDataWhere"/>
|
||||
ORDER BY TIMEID DESC, LINEID ASC, PHASIC_TYPE ASC
|
||||
</select>
|
||||
|
||||
<select id="selectSteadyDetail" resultType="java.util.LinkedHashMap">
|
||||
SELECT
|
||||
<foreach collection="columns" item="column" separator=",">
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.njcn.gather.steady.datavie.pojo.param;
|
||||
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 稳态数据查看查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ApiModel("稳态数据查看查询参数")
|
||||
public class SteadyDataViewQueryParam extends BaseParam {
|
||||
|
||||
@ApiModelProperty("表名,对应 add-data 模板表名")
|
||||
private String tableName;
|
||||
|
||||
@ApiModelProperty("时间开始,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeStart;
|
||||
|
||||
@ApiModelProperty("时间结束,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeEnd;
|
||||
|
||||
@ApiModelProperty("相别:A/B/C/T")
|
||||
private String phasicType;
|
||||
|
||||
@ApiModelProperty("质量标识")
|
||||
private Integer qualityFlag;
|
||||
|
||||
@ApiModelProperty("监测点 ID 列表")
|
||||
private List<String> lineIds = new ArrayList<String>();
|
||||
|
||||
@ApiModelProperty("工程名称关键字")
|
||||
private String engineeringName;
|
||||
|
||||
@ApiModelProperty("项目名称关键字")
|
||||
private String projectName;
|
||||
|
||||
@ApiModelProperty("设备名称关键字")
|
||||
private String equipmentName;
|
||||
|
||||
@ApiModelProperty("监测点名称关键字")
|
||||
private String lineName;
|
||||
}
|
||||
@@ -23,9 +23,6 @@ public class SteadyTrendQueryParam {
|
||||
@ApiModelProperty("统计类型:AVG/MAX/MIN/CP95")
|
||||
private List<String> statTypes = new ArrayList<String>();
|
||||
|
||||
@ApiModelProperty("相别:A/B/C/T")
|
||||
private List<String> phases = new ArrayList<String>();
|
||||
|
||||
@ApiModelProperty("开始时间,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeStart;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.njcn.gather.steady.datavie.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewDetailParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewTemplateVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewVO;
|
||||
|
||||
@@ -13,8 +11,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface SteadyDataViewService {
|
||||
|
||||
Page<SteadyDataViewVO> pageSteadyData(SteadyDataViewQueryParam param);
|
||||
|
||||
SteadyDataViewVO getSteadyDataDetail(SteadyDataViewDetailParam param);
|
||||
|
||||
List<SteadyDataViewTemplateVO> listTemplates();
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.njcn.gather.steady.datavie.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.steady.datavie.mapper.SteadyDataViewMapper;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewDetailParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewTemplateVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewVO;
|
||||
import com.njcn.gather.steady.datavie.service.SteadyDataViewService;
|
||||
@@ -13,12 +11,9 @@ import com.njcn.gather.tool.adddata.component.AddDataTableRegistry;
|
||||
import com.njcn.gather.tool.adddata.component.AddDataTemplateRegistry;
|
||||
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition;
|
||||
import com.njcn.gather.tool.adddata.pojo.vo.AddDataTemplateVO;
|
||||
import com.njcn.gather.tool.addledger.pojo.param.AddLedgerLinePathQueryParam;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||
import com.njcn.gather.tool.addledger.service.AddLedgerService;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
@@ -34,13 +29,10 @@ import java.util.Map;
|
||||
/**
|
||||
* 稳态数据查看服务实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SteadyDataViewServiceImpl implements SteadyDataViewService {
|
||||
|
||||
private static final int LEDGER_LINE_QUERY_LIMIT = 1000;
|
||||
private static final int STEADY_LINE_ID_QUERY_LIMIT = 1000;
|
||||
private static final String DEFAULT_TABLE_NAME = "data_v";
|
||||
private static final String EMPTY_TEXT = "-";
|
||||
private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
@@ -56,22 +48,6 @@ public class SteadyDataViewServiceImpl implements SteadyDataViewService {
|
||||
private final AddDataTableRegistry addDataTableRegistry;
|
||||
private final AddDataTemplateRegistry addDataTemplateRegistry;
|
||||
|
||||
@Override
|
||||
public Page<SteadyDataViewVO> pageSteadyData(SteadyDataViewQueryParam param) {
|
||||
SteadyDataViewQueryParam queryParam = normalizeQueryParam(param);
|
||||
AddDataTableDefinition definition = resolveTableDefinition(queryParam.getTableName());
|
||||
if (!resolveLineFilter(queryParam)) {
|
||||
return emptyPage(queryParam);
|
||||
}
|
||||
Page<Map<String, Object>> steadyPage = steadyDataViewMapper.selectSteadyPage(
|
||||
new Page<Map<String, Object>>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)),
|
||||
definition.getTableName(), definition.getColumns(), queryParam);
|
||||
List<SteadyDataViewVO> records = buildSteadyDataList(definition.getTableName(), definition.getColumns(), steadyPage.getRecords());
|
||||
Page<SteadyDataViewVO> resultPage = new Page<SteadyDataViewVO>(steadyPage.getCurrent(), steadyPage.getSize(), steadyPage.getTotal());
|
||||
resultPage.setRecords(records);
|
||||
return resultPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SteadyDataViewVO getSteadyDataDetail(SteadyDataViewDetailParam param) {
|
||||
if (param == null) {
|
||||
@@ -156,69 +132,6 @@ public class SteadyDataViewServiceImpl implements SteadyDataViewService {
|
||||
return vo;
|
||||
}
|
||||
|
||||
private SteadyDataViewQueryParam normalizeQueryParam(SteadyDataViewQueryParam param) {
|
||||
SteadyDataViewQueryParam queryParam = param == null ? new SteadyDataViewQueryParam() : param;
|
||||
queryParam.setTableName(normalizeTableName(queryParam.getTableName()));
|
||||
LocalDateTime startTime = parseDateTime(queryParam.getTimeStart());
|
||||
LocalDateTime endTime = parseDateTime(queryParam.getTimeEnd());
|
||||
if (startTime == null) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
startTime = LocalDateTime.of(now.getYear(), now.getMonth(), 1, 0, 0, 0);
|
||||
}
|
||||
if (endTime == null) {
|
||||
endTime = LocalDateTime.now();
|
||||
}
|
||||
if (startTime.isAfter(endTime)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "开始时间不能大于结束时间");
|
||||
}
|
||||
queryParam.setTimeStart(OUTPUT_FORMATTER.format(startTime));
|
||||
queryParam.setTimeEnd(OUTPUT_FORMATTER.format(endTime));
|
||||
queryParam.setPhasicType(normalizePhasicType(queryParam.getPhasicType()));
|
||||
validateQualityFlag(queryParam.getQualityFlag());
|
||||
queryParam.setEngineeringName(trimToNull(queryParam.getEngineeringName()));
|
||||
queryParam.setProjectName(trimToNull(queryParam.getProjectName()));
|
||||
queryParam.setEquipmentName(trimToNull(queryParam.getEquipmentName()));
|
||||
queryParam.setLineName(trimToNull(queryParam.getLineName()));
|
||||
List<String> lineIds = normalizeIds(queryParam.getLineIds());
|
||||
if (lineIds.size() > STEADY_LINE_ID_QUERY_LIMIT) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "监测点 ID 查询数量不能超过 1000 个");
|
||||
}
|
||||
queryParam.setLineIds(lineIds);
|
||||
return queryParam;
|
||||
}
|
||||
|
||||
private boolean resolveLineFilter(SteadyDataViewQueryParam queryParam) {
|
||||
if (!hasLedgerKeyword(queryParam)) {
|
||||
return true;
|
||||
}
|
||||
AddLedgerLinePathQueryParam linePathQueryParam = new AddLedgerLinePathQueryParam();
|
||||
linePathQueryParam.setEngineeringName(queryParam.getEngineeringName());
|
||||
linePathQueryParam.setProjectName(queryParam.getProjectName());
|
||||
linePathQueryParam.setEquipmentName(queryParam.getEquipmentName());
|
||||
linePathQueryParam.setLineName(queryParam.getLineName());
|
||||
linePathQueryParam.setLimit(LEDGER_LINE_QUERY_LIMIT + 1);
|
||||
List<String> ledgerLineIds = addLedgerService.listLineIdsByPathQuery(linePathQueryParam);
|
||||
if (ledgerLineIds.size() > LEDGER_LINE_QUERY_LIMIT) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "台账检索匹配监测点过多,请缩小查询条件");
|
||||
}
|
||||
if (ledgerLineIds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
List<String> explicitLineIds = normalizeIds(queryParam.getLineIds());
|
||||
if (explicitLineIds.isEmpty()) {
|
||||
queryParam.setLineIds(ledgerLineIds);
|
||||
return true;
|
||||
}
|
||||
List<String> intersectLineIds = new ArrayList<String>();
|
||||
for (String lineId : explicitLineIds) {
|
||||
if (ledgerLineIds.contains(lineId)) {
|
||||
intersectLineIds.add(lineId);
|
||||
}
|
||||
}
|
||||
queryParam.setLineIds(intersectLineIds);
|
||||
return !intersectLineIds.isEmpty();
|
||||
}
|
||||
|
||||
private AddDataTableDefinition resolveTableDefinition(String tableName) {
|
||||
try {
|
||||
return addDataTableRegistry.getDefinition(tableName);
|
||||
@@ -267,39 +180,6 @@ public class SteadyDataViewServiceImpl implements SteadyDataViewService {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void validateQualityFlag(Integer qualityFlag) {
|
||||
if (qualityFlag != null && qualityFlag != 0 && qualityFlag != 1) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "质量标识只能是 0 或 1");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> normalizeIds(List<String> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> normalizedIds = new ArrayList<String>();
|
||||
for (String id : ids) {
|
||||
String normalizedId = trimToNull(id);
|
||||
if (normalizedId != null && !normalizedIds.contains(normalizedId)) {
|
||||
normalizedIds.add(normalizedId);
|
||||
}
|
||||
}
|
||||
return normalizedIds;
|
||||
}
|
||||
|
||||
private boolean hasLedgerKeyword(SteadyDataViewQueryParam queryParam) {
|
||||
return trimToNull(queryParam.getEngineeringName()) != null
|
||||
|| trimToNull(queryParam.getProjectName()) != null
|
||||
|| trimToNull(queryParam.getEquipmentName()) != null
|
||||
|| trimToNull(queryParam.getLineName()) != null;
|
||||
}
|
||||
|
||||
private Page<SteadyDataViewVO> emptyPage(SteadyDataViewQueryParam queryParam) {
|
||||
Page<SteadyDataViewVO> page = new Page<SteadyDataViewVO>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam), 0);
|
||||
page.setRecords(Collections.<SteadyDataViewVO>emptyList());
|
||||
return page;
|
||||
}
|
||||
|
||||
private List<String> resolveValueColumns(List<String> columns) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (String column : columns) {
|
||||
|
||||
@@ -183,7 +183,6 @@ public class SteadyDataViewTrendServiceImpl implements SteadyDataViewTrendServic
|
||||
target.setLineIds(copyList(source.getLineIds()));
|
||||
target.setIndicatorCodes(copyList(source.getIndicatorCodes()));
|
||||
target.setStatTypes(copyList(source.getStatTypes()));
|
||||
target.setPhases(copyList(source.getPhases()));
|
||||
target.setTimeStart(source.getTimeStart());
|
||||
target.setTimeEnd(source.getTimeEnd());
|
||||
target.setBucket(source.getBucket());
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.njcn.gather.steady.datavie.component;
|
||||
|
||||
import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* InfluxQL 查询语句生成测试。
|
||||
*/
|
||||
class SteadyInfluxQueryComponentTest {
|
||||
|
||||
@Test
|
||||
void shouldBuildBucketedTrendQueryWithRequiredTags() {
|
||||
SteadyInfluxQueryComponent component = new SteadyInfluxQueryComponent(new SteadyInfluxDbProperties());
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_v");
|
||||
field.setField("RMS_CP95");
|
||||
field.setLineId("line-001");
|
||||
field.setPhase("A");
|
||||
|
||||
String query = component.buildTrendQuery(field,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 1, 0, 0),
|
||||
"10m",
|
||||
1);
|
||||
|
||||
Assertions.assertEquals("SELECT mean(\"RMS_CP95\") AS \"value\" FROM \"data_v\" WHERE time >= '2026-05-01T00:00:00Z' AND time <= '2026-05-01T01:00:00Z' AND \"LINEID\" = 'line-001' AND \"PHASIC_TYPE\" = 'A' AND \"QUALITYFLAG\" = '1' GROUP BY time(10m) fill(none)", query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEscapeTagValuesInTrendQuery() {
|
||||
SteadyInfluxQueryComponent component = new SteadyInfluxQueryComponent(new SteadyInfluxDbProperties());
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_v");
|
||||
field.setField("RMS");
|
||||
field.setLineId("line'001");
|
||||
field.setPhase("A");
|
||||
|
||||
String query = component.buildTrendQuery(field,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 1, 0, 0),
|
||||
null,
|
||||
null);
|
||||
|
||||
Assertions.assertTrue(query.contains("\"LINEID\" = 'line\\'001'"));
|
||||
Assertions.assertFalse(query.contains("GROUP BY time"));
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 稳态趋势字段解析测试。
|
||||
@@ -26,31 +27,25 @@ class SteadyTrendFieldResolverTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveVoltageRmsAverageAndCp95Fields() {
|
||||
void shouldExpandAllCatalogPhasesWithoutRequestPhaseFilter() {
|
||||
SteadyTrendQueryParam param = new SteadyTrendQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001"));
|
||||
param.setIndicatorCodes(Arrays.asList("V_RMS"));
|
||||
param.setPhases(Arrays.asList("A"));
|
||||
param.setStatTypes(Arrays.asList("AVG", "CP95"));
|
||||
param.setStatTypes(Arrays.asList("AVG"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 01:00:00");
|
||||
|
||||
List<SteadyTrendResolvedFieldBO> fields = resolver.resolveFields(param);
|
||||
List<String> phases = fields.stream().map(SteadyTrendResolvedFieldBO::getPhase).collect(Collectors.toList());
|
||||
|
||||
Assertions.assertEquals(2, fields.size());
|
||||
Assertions.assertEquals("data_v", fields.get(0).getMeasurement());
|
||||
Assertions.assertEquals("RMS", fields.get(0).getField());
|
||||
Assertions.assertEquals("AVG", fields.get(0).getStatType());
|
||||
Assertions.assertEquals("RMS_CP95", fields.get(1).getField());
|
||||
Assertions.assertEquals("V", fields.get(1).getUnit());
|
||||
Assertions.assertEquals(Arrays.asList("A", "B", "C"), phases);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExpandLineVoltageTotalPhaseToThreeSeries() {
|
||||
void shouldExpandTotalPhaseIndicatorWithoutRequestPhaseFilter() {
|
||||
SteadyTrendQueryParam param = new SteadyTrendQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001"));
|
||||
param.setIndicatorCodes(Arrays.asList("V_LINE_RMS"));
|
||||
param.setPhases(Arrays.asList("T"));
|
||||
param.setStatTypes(Arrays.asList("AVG"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 01:00:00");
|
||||
@@ -58,10 +53,10 @@ class SteadyTrendFieldResolverTest {
|
||||
List<SteadyTrendResolvedFieldBO> fields = resolver.resolveFields(param);
|
||||
|
||||
Assertions.assertEquals(3, fields.size());
|
||||
Assertions.assertEquals("T", fields.get(0).getPhase());
|
||||
Assertions.assertEquals("RMSAB", fields.get(0).getField());
|
||||
Assertions.assertEquals("RMSBC", fields.get(1).getField());
|
||||
Assertions.assertEquals("RMSCA", fields.get(2).getField());
|
||||
Assertions.assertEquals("T", fields.get(0).getPhase());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,7 +64,6 @@ class SteadyTrendFieldResolverTest {
|
||||
SteadyTrendQueryParam param = new SteadyTrendQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001"));
|
||||
param.setIndicatorCodes(Arrays.asList("V_HARMONIC"));
|
||||
param.setPhases(Arrays.asList("A"));
|
||||
param.setStatTypes(Arrays.asList("AVG"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 01:00:00");
|
||||
@@ -80,11 +74,10 @@ class SteadyTrendFieldResolverTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveSelectedHarmonicOrdersOnly() {
|
||||
void shouldResolveSelectedHarmonicOrdersForAllCatalogPhases() {
|
||||
SteadyTrendQueryParam param = new SteadyTrendQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001"));
|
||||
param.setIndicatorCodes(Arrays.asList("V_HARMONIC"));
|
||||
param.setPhases(Arrays.asList("A"));
|
||||
param.setStatTypes(Arrays.asList("MAX"));
|
||||
param.setHarmonicOrders(Arrays.asList(3, 5));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
@@ -92,8 +85,11 @@ class SteadyTrendFieldResolverTest {
|
||||
|
||||
List<SteadyTrendResolvedFieldBO> fields = resolver.resolveFields(param);
|
||||
|
||||
Assertions.assertEquals(2, fields.size());
|
||||
Assertions.assertEquals(6, fields.size());
|
||||
Assertions.assertEquals("A", fields.get(0).getPhase());
|
||||
Assertions.assertEquals("V_3_MAX", fields.get(0).getField());
|
||||
Assertions.assertEquals("V_5_MAX", fields.get(1).getField());
|
||||
Assertions.assertEquals("B", fields.get(2).getPhase());
|
||||
Assertions.assertEquals("C", fields.get(4).getPhase());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.gather.steady.datavie.controller;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 稳态数据查看接口契约测试。
|
||||
*/
|
||||
class SteadyDataViewControllerTest {
|
||||
|
||||
@Test
|
||||
void shouldNotExposePageQueryEndpointMethod() {
|
||||
for (Method method : SteadyDataViewController.class.getDeclaredMethods()) {
|
||||
Assertions.assertNotEquals("pageSteadyData", method.getName(), "本功能不提供分页检索接口");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.gather.steady.datavie.pojo.param;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 稳态趋势查询参数契约测试。
|
||||
*/
|
||||
class SteadyTrendQueryParamTest {
|
||||
|
||||
@Test
|
||||
void shouldNotExposePhaseFilterInTrendQueryParam() {
|
||||
for (Field field : SteadyTrendQueryParam.class.getDeclaredFields()) {
|
||||
Assertions.assertNotEquals("phases", field.getName(), "趋势检索请求不再携带相别条件");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.njcn.gather.steady.datavie.service.impl;
|
||||
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewIndicatorNodeVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 稳态趋势指标服务测试。
|
||||
*/
|
||||
class SteadyDataViewIndicatorServiceImplTest {
|
||||
|
||||
@Test
|
||||
void shouldGroupIndicatorsByCategory() {
|
||||
SteadyDataViewIndicatorServiceImpl service = new SteadyDataViewIndicatorServiceImpl(new SteadyTrendIndicatorCatalog());
|
||||
|
||||
List<SteadyDataViewIndicatorNodeVO> tree = service.listIndicatorTree();
|
||||
|
||||
Assertions.assertEquals(5, tree.size());
|
||||
Assertions.assertEquals("VOLTAGE", tree.get(0).getGroupCode());
|
||||
Assertions.assertTrue(tree.get(0).getChildren().size() >= 2);
|
||||
Assertions.assertEquals("V_RMS", tree.get(0).getChildren().get(0).getIndicatorCode());
|
||||
Assertions.assertTrue(tree.get(0).getChildren().get(0).getSelectable());
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package com.njcn.gather.steady.datavie.service.impl;
|
||||
|
||||
import com.njcn.gather.steady.datavie.component.SteadyInfluxQueryComponent;
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendFieldResolver;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendPointVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendQueryVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryVO;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||
import com.njcn.gather.tool.addledger.service.AddLedgerService;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 稳态趋势服务编排测试。
|
||||
*/
|
||||
class SteadyDataViewTrendServiceImplTest {
|
||||
|
||||
@Test
|
||||
void shouldBuildTrendQueryWithDefaultBucketAndLineName() {
|
||||
SteadyTrendFieldResolver fieldResolver = Mockito.mock(SteadyTrendFieldResolver.class);
|
||||
SteadyInfluxQueryComponent influxQueryComponent = Mockito.mock(SteadyInfluxQueryComponent.class);
|
||||
AddLedgerService addLedgerService = Mockito.mock(AddLedgerService.class);
|
||||
|
||||
SteadyTrendQueryParam param = new SteadyTrendQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001"));
|
||||
param.setIndicatorCodes(Arrays.asList("V_RMS"));
|
||||
param.setPhases(Arrays.asList("A"));
|
||||
param.setStatTypes(Arrays.asList("AVG"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 05:59:59");
|
||||
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_v");
|
||||
field.setField("RMS");
|
||||
field.setLineId("line-001");
|
||||
field.setIndicatorCode("V_RMS");
|
||||
field.setIndicatorName("相电压有效值");
|
||||
field.setSeriesName("相电压有效值");
|
||||
field.setPhase("A");
|
||||
field.setStatType("AVG");
|
||||
field.setUnit("V");
|
||||
field.setSeriesKey("line-001|V_RMS|A|AVG|RMS");
|
||||
|
||||
Mockito.when(fieldResolver.parseRequiredTime("2026-05-01 00:00:00", "开始时间不能为空"))
|
||||
.thenReturn(LocalDateTime.of(2026, 5, 1, 0, 0, 0));
|
||||
Mockito.when(fieldResolver.parseRequiredTime("2026-05-01 05:59:59", "结束时间不能为空"))
|
||||
.thenReturn(LocalDateTime.of(2026, 5, 1, 5, 59, 59));
|
||||
Mockito.when(fieldResolver.resolveFields(Mockito.any())).thenReturn(Collections.singletonList(field));
|
||||
Mockito.when(addLedgerService.listLinePathByLineIds(Collections.singletonList("line-001")))
|
||||
.thenReturn(Collections.singletonMap("line-001", buildLinePath("进线一")));
|
||||
Mockito.when(influxQueryComponent.queryTrendPoints(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.eq("1m"), Mockito.isNull()))
|
||||
.thenReturn(Collections.singletonList(new SteadyTrendPointVO("2026-05-01 00:00:00", new BigDecimal("1.2"))));
|
||||
|
||||
SteadyDataViewTrendServiceImpl service = new SteadyDataViewTrendServiceImpl(fieldResolver, influxQueryComponent, addLedgerService);
|
||||
SteadyTrendQueryVO result = service.queryTrend(param);
|
||||
|
||||
Assertions.assertEquals("1m", result.getBucket());
|
||||
Assertions.assertTrue(result.getSampled());
|
||||
Assertions.assertEquals("进线一", result.getSeries().get(0).getLineName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCalculateTrendSummaryFromSeriesPoints() {
|
||||
SteadyTrendFieldResolver fieldResolver = Mockito.mock(SteadyTrendFieldResolver.class);
|
||||
SteadyInfluxQueryComponent influxQueryComponent = Mockito.mock(SteadyInfluxQueryComponent.class);
|
||||
AddLedgerService addLedgerService = Mockito.mock(AddLedgerService.class);
|
||||
|
||||
SteadyTrendQueryParam param = new SteadyTrendQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001"));
|
||||
param.setIndicatorCodes(Arrays.asList("V_RMS"));
|
||||
param.setPhases(Arrays.asList("A"));
|
||||
param.setStatTypes(Arrays.asList("AVG"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 05:59:59");
|
||||
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_v");
|
||||
field.setField("RMS");
|
||||
field.setLineId("line-001");
|
||||
field.setIndicatorCode("V_RMS");
|
||||
field.setIndicatorName("相电压有效值");
|
||||
field.setSeriesName("相电压有效值");
|
||||
field.setPhase("A");
|
||||
field.setStatType("AVG");
|
||||
field.setUnit("V");
|
||||
field.setSeriesKey("line-001|V_RMS|A|AVG|RMS");
|
||||
|
||||
Mockito.when(fieldResolver.parseRequiredTime(Mockito.anyString(), Mockito.anyString()))
|
||||
.thenReturn(LocalDateTime.of(2026, 5, 1, 0, 0, 0))
|
||||
.thenReturn(LocalDateTime.of(2026, 5, 1, 5, 59, 59));
|
||||
Mockito.when(fieldResolver.resolveFields(param)).thenReturn(Collections.singletonList(field));
|
||||
Mockito.when(addLedgerService.listLinePathByLineIds(Collections.singletonList("line-001")))
|
||||
.thenReturn(Collections.<String, AddLedgerLinePathVO>emptyMap());
|
||||
Mockito.when(influxQueryComponent.queryTrendPoints(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.isNull(), Mockito.isNull()))
|
||||
.thenReturn(Arrays.asList(
|
||||
new SteadyTrendPointVO("2026-05-01 00:00:00", new BigDecimal("1")),
|
||||
new SteadyTrendPointVO("2026-05-01 01:00:00", new BigDecimal("3")),
|
||||
new SteadyTrendPointVO("2026-05-01 02:00:00", new BigDecimal("2"))
|
||||
));
|
||||
|
||||
SteadyDataViewTrendServiceImpl service = new SteadyDataViewTrendServiceImpl(fieldResolver, influxQueryComponent, addLedgerService);
|
||||
SteadyTrendSummaryVO summary = service.summarizeTrend(param);
|
||||
|
||||
Assertions.assertEquals(new BigDecimal("3"), summary.getItems().get(0).getMax());
|
||||
Assertions.assertEquals(new BigDecimal("1"), summary.getItems().get(0).getMin());
|
||||
Assertions.assertEquals(new BigDecimal("2.000000"), summary.getItems().get(0).getAvg());
|
||||
Assertions.assertEquals(new BigDecimal("3"), summary.getItems().get(0).getCp95());
|
||||
}
|
||||
|
||||
private AddLedgerLinePathVO buildLinePath(String lineName) {
|
||||
AddLedgerLinePathVO linePathVO = new AddLedgerLinePathVO();
|
||||
linePathVO.setLineName(lineName);
|
||||
return linePathVO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.njcn.gather.system.cfg.controller;
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.system.cfg.pojo.param.SysConfigParam;
|
||||
import com.njcn.gather.system.cfg.pojo.po.SysConfig;
|
||||
import com.njcn.gather.system.cfg.service.ISysConfigService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
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.GetMapping;
|
||||
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.RestController;
|
||||
|
||||
/**
|
||||
* 系统配置接口。
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "系统配置")
|
||||
@RestController
|
||||
@RequestMapping("/sysConfig")
|
||||
@RequiredArgsConstructor
|
||||
public class SysConfigController extends BaseController {
|
||||
|
||||
private final ISysConfigService sysConfigService;
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@GetMapping("/getConfig")
|
||||
@ApiOperation("获取系统配置")
|
||||
public HttpResult<SysConfig> getConfig() {
|
||||
String methodDescribe = getMethodDescribe("getConfig");
|
||||
LogUtil.njcnDebug(log, "{}", methodDescribe);
|
||||
SysConfig sysConfig = sysConfigService.getOneConfig();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, sysConfig, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("修改系统配置")
|
||||
@ApiImplicitParam(name = "sysConfig", value = "系统配置", required = true)
|
||||
public HttpResult<Boolean> update(@RequestBody @Validated SysConfigParam.UpdateParam sysConfig) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
LogUtil.njcnDebug(log, "{}", methodDescribe);
|
||||
boolean result = sysConfigService.updateConfig(sysConfig);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
}
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.njcn.gather.system.cfg.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.system.cfg.pojo.po.SysConfig;
|
||||
|
||||
/**
|
||||
* 系统配置 Mapper。
|
||||
*/
|
||||
public interface SysConfigMapper extends MPJBaseMapper<SysConfig> {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.system.cfg.mapper.SysConfigMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.njcn.gather.system.cfg.pojo.param;
|
||||
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.system.pojo.constant.SystemValidMessage;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* 系统配置参数。
|
||||
*/
|
||||
@Data
|
||||
public class SysConfigParam {
|
||||
|
||||
@ApiModelProperty("波形文件存储根路径")
|
||||
private String waveStoragePath;
|
||||
|
||||
@Data
|
||||
public static class UpdateParam extends SysConfigParam {
|
||||
|
||||
@ApiModelProperty("id")
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = SystemValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.gather.system.cfg.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.mybatisplus.bo.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 系统配置。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_config")
|
||||
public class SysConfig extends BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3293972902209033082L;
|
||||
|
||||
/**
|
||||
* 系统配置表Id。
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 波形文件存储根路径,默认 D:/。
|
||||
*/
|
||||
@TableField("wave_storage_path")
|
||||
private String waveStoragePath;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常。
|
||||
*/
|
||||
private Integer state;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.gather.system.cfg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.system.cfg.pojo.param.SysConfigParam;
|
||||
import com.njcn.gather.system.cfg.pojo.po.SysConfig;
|
||||
|
||||
/**
|
||||
* 系统配置服务。
|
||||
*/
|
||||
public interface ISysConfigService extends IService<SysConfig> {
|
||||
|
||||
/**
|
||||
* 更新系统配置。
|
||||
*
|
||||
* @param param 系统配置
|
||||
* @return 是否更新成功
|
||||
*/
|
||||
boolean updateConfig(SysConfigParam.UpdateParam param);
|
||||
|
||||
/**
|
||||
* 获取系统配置。
|
||||
*
|
||||
* @return 系统配置
|
||||
*/
|
||||
SysConfig getOneConfig();
|
||||
|
||||
/**
|
||||
* 获取波形文件存储根路径。
|
||||
*
|
||||
* @return 波形文件存储根路径,未配置时返回默认 D:/
|
||||
*/
|
||||
String getWaveStoragePath();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.njcn.gather.system.cfg.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.gather.system.cfg.mapper.SysConfigMapper;
|
||||
import com.njcn.gather.system.cfg.pojo.param.SysConfigParam;
|
||||
import com.njcn.gather.system.cfg.pojo.po.SysConfig;
|
||||
import com.njcn.gather.system.cfg.service.ISysConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 系统配置服务实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig> implements ISysConfigService {
|
||||
|
||||
private static final String DEFAULT_CONFIG_ID = "00000000000000000000000000000001";
|
||||
private static final String DEFAULT_WAVE_STORAGE_PATH = "D:/";
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updateConfig(SysConfigParam.UpdateParam param) {
|
||||
SysConfig config = getOneConfig();
|
||||
if (config == null) {
|
||||
config = new SysConfig();
|
||||
config.setId(param != null && StringUtils.isNotBlank(param.getId()) ? param.getId().trim() : DEFAULT_CONFIG_ID);
|
||||
config.setState(DataStateEnum.ENABLE.getCode());
|
||||
config.setWaveStoragePath(DEFAULT_WAVE_STORAGE_PATH);
|
||||
}
|
||||
if (param != null && StringUtils.isNotBlank(param.getWaveStoragePath())) {
|
||||
config.setWaveStoragePath(param.getWaveStoragePath().trim());
|
||||
}
|
||||
return this.saveOrUpdate(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysConfig getOneConfig() {
|
||||
QueryWrapper<SysConfig> queryWrapper = new QueryWrapper<SysConfig>();
|
||||
queryWrapper.eq("state", DataStateEnum.ENABLE.getCode());
|
||||
queryWrapper.last("LIMIT 1");
|
||||
return this.getOne(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWaveStoragePath() {
|
||||
SysConfig config = getOneConfig();
|
||||
if (config == null || StringUtils.isBlank(config.getWaveStoragePath())) {
|
||||
return DEFAULT_WAVE_STORAGE_PATH;
|
||||
}
|
||||
return config.getWaveStoragePath().trim();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.njcn.gather.system.dictionary.pojo.po.DictType;
|
||||
import com.njcn.gather.system.dictionary.pojo.vo.DictDataExcel;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.gather.system.pojo.enums.SystemResponseEnum;
|
||||
import com.njcn.gather.system.util.ExportFileNameUtil;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import com.njcn.web.pojo.dto.SimpleDTO;
|
||||
import com.njcn.web.pojo.dto.SimpleTreeDTO;
|
||||
@@ -179,7 +180,7 @@ public class DictDataServiceImpl extends ServiceImpl<DictDataMapper, DictData> i
|
||||
.eq("sys_dict_data.type_id", queryParam.getTypeId());
|
||||
List<DictData> dictDatas = this.list(queryWrapper);
|
||||
List<DictDataExcel> dictDataExcels = BeanUtil.copyToList(dictDatas, DictDataExcel.class);
|
||||
ExcelUtil.exportExcel("字典数据导出数据.xlsx", "字典数据", DictDataExcel.class, dictDataExcels);
|
||||
ExcelUtil.exportExcel(ExportFileNameUtil.appendToday("字典数据导出数据.xlsx"), "字典数据", DictDataExcel.class, dictDataExcels);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.njcn.gather.system.dictionary.pojo.vo.DictTypeExcel;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.gather.system.dictionary.service.IDictTypeService;
|
||||
import com.njcn.gather.system.pojo.enums.SystemResponseEnum;
|
||||
import com.njcn.gather.system.util.ExportFileNameUtil;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -115,7 +116,7 @@ public class DictTypeServiceImpl extends ServiceImpl<DictTypeMapper, DictType> i
|
||||
dictTypeVOS.add(dictTypeExcel);
|
||||
});
|
||||
|
||||
ExcelUtil.exportExcel("字典类型导出数据.xlsx", "字典类型", DictTypeExcel.class, dictTypeVOS);
|
||||
ExcelUtil.exportExcel(ExportFileNameUtil.appendToday("字典类型导出数据.xlsx"), "字典类型", DictTypeExcel.class, dictTypeVOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.njcn.gather.system.log.pojo.param.SysLogParam;
|
||||
import com.njcn.gather.system.log.pojo.po.SysLogAudit;
|
||||
import com.njcn.gather.system.log.service.ISysLogAuditService;
|
||||
import com.njcn.gather.system.log.util.CSVUtil;
|
||||
import com.njcn.gather.system.util.ExportFileNameUtil;
|
||||
import com.njcn.gather.user.user.pojo.po.SysUser;
|
||||
import com.njcn.gather.user.user.service.ISysUserService;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
@@ -149,7 +150,7 @@ public class SysLogAuditServiceImpl extends ServiceImpl<SysLogAuditMapper, SysLo
|
||||
XSSFWorkbook wb = new XSSFWorkbook();
|
||||
|
||||
try (ServletOutputStream outputStream = response.getOutputStream()) {
|
||||
fileName = URLEncoder.encode(fileName, CharsetUtil.UTF_8);
|
||||
fileName = URLEncoder.encode(ExportFileNameUtil.appendToday(fileName), CharsetUtil.UTF_8);
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
|
||||
@@ -5,6 +5,7 @@ import cn.afterturn.easypoi.csv.CsvExportUtil;
|
||||
import cn.afterturn.easypoi.csv.entity.CsvExportParams;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.njcn.gather.system.util.ExportFileNameUtil;
|
||||
import com.njcn.web.utils.HttpServletUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -73,7 +74,7 @@ public class CSVUtil {
|
||||
Throwable var1 = null;
|
||||
|
||||
try {
|
||||
fileName = URLEncoder.encode(fileName, "UTF-8");
|
||||
fileName = URLEncoder.encode(ExportFileNameUtil.appendToday(fileName), "UTF-8");
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
@@ -108,7 +109,7 @@ public class CSVUtil {
|
||||
ServletOutputStream os = null;
|
||||
try {
|
||||
os = response.getOutputStream();
|
||||
fileName = URLEncoder.encode(fileName, "UTF-8");
|
||||
fileName = URLEncoder.encode(ExportFileNameUtil.appendToday(fileName), "UTF-8");
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
|
||||
@@ -38,4 +38,9 @@ public interface DictConst {
|
||||
* 注册资源字典数据 ID:比对式
|
||||
*/
|
||||
String REG_RES_CONTRAST_ID = "7cd65363a6bf675ae408f28a281b77d4";
|
||||
|
||||
/**
|
||||
* 事件类型字典类型。
|
||||
*/
|
||||
String EVENT_TYPE_DICT = "事件类型";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.njcn.gather.system.util;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 导出文件名处理工具。
|
||||
*/
|
||||
public final class ExportFileNameUtil {
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
private ExportFileNameUtil() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
34
system/src/main/resources/sql/system/sys-config.sql
Normal file
34
system/src/main/resources/sql/system/sys-config.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- 系统配置表。
|
||||
-- wave_storage_path 为波形文件存储根路径,默认值为 D:/。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sys_config` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '系统配置表Id',
|
||||
`wave_storage_path` VARCHAR(255) NULL DEFAULT 'D:/' COMMENT '波形文件存储根路径',
|
||||
`state` TINYINT NULL DEFAULT 1 COMMENT '状态:0-删除,1-正常',
|
||||
`create_by` VARCHAR(64) NULL COMMENT '创建人',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) NULL COMMENT '更新人',
|
||||
`update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_sys_config_state` (`state`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';
|
||||
|
||||
INSERT INTO `sys_config` (
|
||||
`id`,
|
||||
`wave_storage_path`,
|
||||
`state`,
|
||||
`create_time`,
|
||||
`update_time`
|
||||
)
|
||||
SELECT
|
||||
'00000000000000000000000000000001',
|
||||
'D:/',
|
||||
1,
|
||||
NOW(),
|
||||
NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_config`
|
||||
WHERE `state` = 1
|
||||
LIMIT 1
|
||||
);
|
||||
@@ -11,6 +11,7 @@ import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
|
||||
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
|
||||
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
|
||||
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
|
||||
import com.njcn.gather.systemmonitor.disk.util.GeneratedFileNameUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -102,7 +103,7 @@ public class DiskMonitorNotificationComponent {
|
||||
String fileName = String.format("disk-monitor-%s-%s-%s.json",
|
||||
job.getJobNo(), target.getDriveLetter().replace(":", ""),
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
|
||||
Path filePath = directoryPath.resolve(fileName);
|
||||
Path filePath = directoryPath.resolve(GeneratedFileNameUtil.appendToday(fileName));
|
||||
Map<String, Object> payload = buildNotifyPayload(job, target, usedPercent, currentStatus, notifyReason, notifyLevel, scanTime, message);
|
||||
Files.write(filePath, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(payload));
|
||||
notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_SUCCESS);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.njcn.gather.systemmonitor.disk.util;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.njcn.gather.tool.adddata.component;
|
||||
|
||||
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表定义注册测试。
|
||||
*/
|
||||
class AddDataTableRegistryTest {
|
||||
|
||||
@Test
|
||||
void shouldLoadAllThirteenTablesFromSchema() throws Exception {
|
||||
AddDataTableRegistry registry = new AddDataTableRegistry();
|
||||
registry.afterPropertiesSet();
|
||||
|
||||
List<AddDataTableDefinition> definitions = registry.getTableDefinitions();
|
||||
|
||||
Assertions.assertEquals(13, definitions.size());
|
||||
Assertions.assertEquals("data_flicker", definitions.get(0).getTableName());
|
||||
Assertions.assertEquals("data_v", definitions.get(definitions.size() - 1).getTableName());
|
||||
Assertions.assertTrue(registry.getDefinition("data_v").getColumns().contains("V_THD"));
|
||||
Assertions.assertEquals(4, registry.getDefinition("data_v").getPhaseCodes().size());
|
||||
Assertions.assertTrue(registry.getDefinition("data_v").getPhaseCodes().contains("T"));
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.njcn.gather.tool.adddata.component;
|
||||
|
||||
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTaskCommand;
|
||||
import com.njcn.gather.tool.adddata.pojo.vo.AddDataTaskStatusVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 补数任务状态持有器测试。
|
||||
*/
|
||||
class AddDataTaskStatusHolderTest {
|
||||
|
||||
private final AddDataTaskStatusHolder holder = new AddDataTaskStatusHolder(new AddDataTimeSlotCalculator());
|
||||
|
||||
@Test
|
||||
void shouldReturnHourlyTimeResultsWhenCreateTask() {
|
||||
AddDataTaskCommand command = new AddDataTaskCommand(
|
||||
Arrays.asList("1"),
|
||||
LocalDateTime.of(2026, 4, 28, 10, 7, 0),
|
||||
LocalDateTime.of(2026, 4, 28, 13, 0, 0),
|
||||
5);
|
||||
|
||||
AddDataTaskStatusVO status = holder.createWaitingTask(command);
|
||||
|
||||
Assertions.assertEquals(Arrays.asList(
|
||||
"2026-04-28 11:00:00",
|
||||
"2026-04-28 12:00:00",
|
||||
"2026-04-28 13:00:00"), status.getHourlyTimeResults());
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.njcn.gather.tool.adddata.component;
|
||||
|
||||
import com.njcn.gather.tool.adddata.pojo.vo.AddDataTemplateVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 模板注册测试。
|
||||
*/
|
||||
class AddDataTemplateRegistryTest {
|
||||
|
||||
@Test
|
||||
void shouldOnlyExposeABCTPhaseCodes() {
|
||||
AddDataTemplateRegistry registry = new AddDataTemplateRegistry();
|
||||
|
||||
List<AddDataTemplateVO> templates = registry.getTemplates();
|
||||
Set<String> allowedPhaseCodes = new HashSet<String>();
|
||||
allowedPhaseCodes.add("A");
|
||||
allowedPhaseCodes.add("B");
|
||||
allowedPhaseCodes.add("C");
|
||||
allowedPhaseCodes.add("T");
|
||||
|
||||
for (AddDataTemplateVO template : templates) {
|
||||
for (String phaseCode : template.getPhaseCodes()) {
|
||||
Assertions.assertTrue(allowedPhaseCodes.contains(phaseCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.njcn.gather.tool.adddata.component;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 时间槽计算测试。
|
||||
*/
|
||||
class AddDataTimeSlotCalculatorTest {
|
||||
|
||||
private final AddDataTimeSlotCalculator calculator = new AddDataTimeSlotCalculator();
|
||||
|
||||
@Test
|
||||
void shouldAlignToNextNaturalSlot() {
|
||||
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 7, 12);
|
||||
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 10, 22, 0);
|
||||
|
||||
List<LocalDateTime> slots = calculator.buildTimeSlots(start, end, 5);
|
||||
|
||||
Assertions.assertEquals(3, slots.size());
|
||||
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 10, 10, 0), slots.get(0));
|
||||
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 10, 20, 0), slots.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyWhenRangeDoesNotContainAnySlot() {
|
||||
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 7, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 10, 9, 59);
|
||||
|
||||
List<LocalDateTime> slots = calculator.buildTimeSlots(start, end, 10);
|
||||
|
||||
Assertions.assertTrue(slots.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBuildHourlySlotsFromNextNaturalHour() {
|
||||
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 7, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 13, 0, 0);
|
||||
|
||||
List<LocalDateTime> slots = calculator.buildHourlyTimeSlots(start, end);
|
||||
|
||||
Assertions.assertEquals(3, slots.size());
|
||||
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 11, 0, 0), slots.get(0));
|
||||
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 13, 0, 0), slots.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIncludeStartWhenAlreadyAtNaturalHour() {
|
||||
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 0, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 12, 30, 0);
|
||||
|
||||
List<LocalDateTime> slots = calculator.buildHourlyTimeSlots(start, end);
|
||||
|
||||
Assertions.assertEquals(3, slots.size());
|
||||
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 10, 0, 0), slots.get(0));
|
||||
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 12, 0, 0), slots.get(2));
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.njcn.gather.tool.adddata.component;
|
||||
|
||||
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数值生成器测试。
|
||||
*/
|
||||
class AddDataValueGeneratorTest {
|
||||
|
||||
@Test
|
||||
void shouldGenerateStableDataVRowWithExpectedColumnCount() throws Exception {
|
||||
AddDataTableRegistry registry = new AddDataTableRegistry();
|
||||
registry.afterPropertiesSet();
|
||||
AddDataTableDefinition definition = registry.getDefinition("data_v");
|
||||
AddDataValueGenerator generator = new AddDataValueGenerator();
|
||||
|
||||
List<Object> row = generator.generateRow(definition, "f04a9d62e3d24e6580e4f32b40967505", LocalDateTime.of(2026, 4, 28, 10, 10, 0), "A");
|
||||
|
||||
Assertions.assertEquals(definition.getColumns().size(), row.size());
|
||||
Assertions.assertEquals("A", row.get(definition.getColumns().indexOf("PHASIC_TYPE")));
|
||||
Double rms = (Double) row.get(definition.getColumns().indexOf("RMS"));
|
||||
Double rmsMax = (Double) row.get(definition.getColumns().indexOf("RMS_MAX"));
|
||||
Double rmsMin = (Double) row.get(definition.getColumns().indexOf("RMS_MIN"));
|
||||
Assertions.assertTrue(rmsMax >= rms);
|
||||
Assertions.assertTrue(rmsMin <= rms);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
project.name AS projectName,
|
||||
equipment.id AS equipmentId,
|
||||
equipment.name AS equipmentName,
|
||||
equipment.mac AS equipmentMac,
|
||||
line.line_id AS lineId,
|
||||
line.name AS lineName
|
||||
FROM cs_line line
|
||||
@@ -55,6 +56,7 @@
|
||||
project.name AS projectName,
|
||||
equipment.id AS equipmentId,
|
||||
equipment.name AS equipmentName,
|
||||
equipment.mac AS equipmentMac,
|
||||
line.line_id AS lineId,
|
||||
line.name AS lineName
|
||||
FROM cs_line line
|
||||
|
||||
@@ -24,6 +24,8 @@ public class AddLedgerLinePathVO implements Serializable {
|
||||
|
||||
private String equipmentName;
|
||||
|
||||
private String equipmentMac;
|
||||
|
||||
private String lineId;
|
||||
|
||||
private String lineName;
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
Navicat/MySQL 初始化脚本
|
||||
|
||||
用途:
|
||||
1. 初始化 add-ledger 台账设备相关字典。
|
||||
2. 脚本可重复执行,按字典编码避免重复插入。
|
||||
|
||||
说明:
|
||||
- dev_type 对应 cs_equipment_delivery.dev_type,保存 sys_dict_data.id。
|
||||
- dev_model 对应 cs_equipment_delivery.dev_model,保存 sys_dict_data.id。
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ----------------------------
|
||||
-- 字典类型:装置类型
|
||||
-- ----------------------------
|
||||
SET @ledger_device_type_type_id := (
|
||||
SELECT `id`
|
||||
FROM `sys_dict_type`
|
||||
WHERE `code` = 'ledger_device_type'
|
||||
AND `state` = 1
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_type` (
|
||||
`id`, `name`, `code`, `sort`, `open_level`, `open_describe`, `remark`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d00101',
|
||||
'装置类型',
|
||||
'ledger_device_type',
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
'数据台账设备装置类型',
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE @ledger_device_type_type_id IS NULL;
|
||||
|
||||
SET @ledger_device_type_type_id := (
|
||||
SELECT `id`
|
||||
FROM `sys_dict_type`
|
||||
WHERE `code` = 'ledger_device_type'
|
||||
AND `state` = 1
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_data` (
|
||||
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d10101',
|
||||
@ledger_device_type_type_id,
|
||||
'直连设备',
|
||||
'direct_device',
|
||||
1,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_dict_data`
|
||||
WHERE `type_id` = @ledger_device_type_type_id
|
||||
AND `code` = 'direct_device'
|
||||
AND `state` = 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_data` (
|
||||
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d10102',
|
||||
@ledger_device_type_type_id,
|
||||
'网关',
|
||||
'gateway',
|
||||
2,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_dict_data`
|
||||
WHERE `type_id` = @ledger_device_type_type_id
|
||||
AND `code` = 'gateway'
|
||||
AND `state` = 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_data` (
|
||||
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d10103',
|
||||
@ledger_device_type_type_id,
|
||||
'装置',
|
||||
'device',
|
||||
3,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_dict_data`
|
||||
WHERE `type_id` = @ledger_device_type_type_id
|
||||
AND `code` = 'device'
|
||||
AND `state` = 1
|
||||
);
|
||||
|
||||
-- ----------------------------
|
||||
-- 字典类型:装置型号
|
||||
-- ----------------------------
|
||||
SET @ledger_device_model_type_id := (
|
||||
SELECT `id`
|
||||
FROM `sys_dict_type`
|
||||
WHERE `code` = 'ledger_device_model'
|
||||
AND `state` = 1
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_type` (
|
||||
`id`, `name`, `code`, `sort`, `open_level`, `open_describe`, `remark`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d00201',
|
||||
'装置型号',
|
||||
'ledger_device_model',
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
'数据台账设备装置型号',
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE @ledger_device_model_type_id IS NULL;
|
||||
|
||||
SET @ledger_device_model_type_id := (
|
||||
SELECT `id`
|
||||
FROM `sys_dict_type`
|
||||
WHERE `code` = 'ledger_device_model'
|
||||
AND `state` = 1
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_data` (
|
||||
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d20101',
|
||||
@ledger_device_model_type_id,
|
||||
'PQS588',
|
||||
'pqs588',
|
||||
1,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_dict_data`
|
||||
WHERE `type_id` = @ledger_device_model_type_id
|
||||
AND `code` = 'pqs588'
|
||||
AND `state` = 1
|
||||
);
|
||||
|
||||
INSERT INTO `sys_dict_data` (
|
||||
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
|
||||
`create_by`, `create_time`, `update_by`, `update_time`
|
||||
)
|
||||
SELECT
|
||||
'7f91c2a1e9f44b47a6e7c8b227d20102',
|
||||
@ledger_device_model_type_id,
|
||||
'PQS680',
|
||||
'pqs680',
|
||||
2,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
'system',
|
||||
NOW(),
|
||||
'system',
|
||||
NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_dict_data`
|
||||
WHERE `type_id` = @ledger_device_model_type_id
|
||||
AND `code` = 'pqs680'
|
||||
AND `state` = 1
|
||||
);
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.tool.addledger.component;
|
||||
|
||||
import com.njcn.gather.tool.addledger.pojo.po.AddLedgerLedgerPO;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerTreeNodeVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 台账树组装测试。
|
||||
*/
|
||||
class AddLedgerTreeBuilderTest {
|
||||
|
||||
private final AddLedgerTreeBuilder treeBuilder = new AddLedgerTreeBuilder();
|
||||
|
||||
@Test
|
||||
void shouldBuildFourLevelTreeFromFlatLedgerNodes() {
|
||||
AddLedgerLedgerPO engineering = buildLedger("engineering-1", "0", 0, "工程");
|
||||
AddLedgerLedgerPO project = buildLedger("project-1", "engineering-1", 1, "项目");
|
||||
AddLedgerLedgerPO equipment = buildLedger("equipment-1", "project-1", 2, "设备");
|
||||
AddLedgerLedgerPO line = buildLedger("line-1", "equipment-1", 3, "测点");
|
||||
|
||||
List<AddLedgerTreeNodeVO> result = treeBuilder.buildTree(Arrays.asList(line, equipment, project, engineering));
|
||||
|
||||
Assertions.assertEquals(1, result.size());
|
||||
Assertions.assertEquals("engineering-1", result.get(0).getId());
|
||||
Assertions.assertEquals("project-1", result.get(0).getChildren().get(0).getId());
|
||||
Assertions.assertEquals("equipment-1", result.get(0).getChildren().get(0).getChildren().get(0).getId());
|
||||
Assertions.assertEquals("line-1", result.get(0).getChildren().get(0).getChildren().get(0).getChildren().get(0).getId());
|
||||
}
|
||||
|
||||
private AddLedgerLedgerPO buildLedger(String id, String parentId, Integer level, String name) {
|
||||
AddLedgerLedgerPO ledger = new AddLedgerLedgerPO();
|
||||
ledger.setId(id);
|
||||
ledger.setPid(parentId);
|
||||
ledger.setLevel(level);
|
||||
ledger.setName(name);
|
||||
ledger.setSort(0);
|
||||
return ledger;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.njcn.gather.tool.addledger.util;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 台账测点线路号工具测试。
|
||||
*/
|
||||
class AddLedgerLineNoUtilTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnOnlyUnusedLineNosWhenCreateLine() {
|
||||
List<Integer> result = AddLedgerLineNoUtil.resolveAvailableLineNos(Arrays.asList(1, 2, 20), null);
|
||||
|
||||
Assertions.assertEquals(17, result.size());
|
||||
Assertions.assertFalse(result.contains(1));
|
||||
Assertions.assertFalse(result.contains(2));
|
||||
Assertions.assertFalse(result.contains(20));
|
||||
Assertions.assertTrue(result.contains(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepCurrentLineNoAvailableWhenEditLine() {
|
||||
List<Integer> result = AddLedgerLineNoUtil.resolveAvailableLineNos(Arrays.asList(1, 2, 20), 2);
|
||||
|
||||
Assertions.assertTrue(result.contains(2));
|
||||
Assertions.assertFalse(result.contains(1));
|
||||
Assertions.assertFalse(result.contains(20));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
package com.njcn.gather.tool.wave.component;
|
||||
|
||||
import com.njcn.gather.tool.wave.pojo.dto.EigenvalueDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveCycleVectorDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveHarmonicDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WavePhaseVectorDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
|
||||
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeVectorResultVO;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 本地调试 getComtrade 的命令行入口。
|
||||
*/
|
||||
public final class WaveFileComponentTestMain {
|
||||
|
||||
/** 默认测试 CFG 文件路径。 */
|
||||
private static final String DEFAULT_CFG_PATH = "D:\\00-B7-8D-00-E4-09\\PQMonitor_PQM2_006970_20260320_175033_734.CFG";
|
||||
/** 默认测试 DAT 文件路径。 */
|
||||
private static final String DEFAULT_DAT_PATH = "D:\\00-B7-8D-00-E4-09\\PQMonitor_PQM2_006970_20260320_175033_734.DAT";
|
||||
/** 默认解析类型,1 表示普通展示。 */
|
||||
private static final int DEFAULT_PARSE_TYPE = 1;
|
||||
/** 向量调试固定使用原始波形。 */
|
||||
private static final int VECTOR_PARSE_TYPE = 3;
|
||||
/** 默认使用浮动门槛计算特征值。 */
|
||||
private static final boolean DEFAULT_DYNAMIC_THRESHOLD = true;
|
||||
|
||||
private WaveFileComponentTestMain() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用法:
|
||||
* java com.njcn.gather.tool.wave.component.WaveFileComponentTestMain <cfgPath> <datPath> [parseType]
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 2) {
|
||||
System.out.println("未传入参数,使用默认测试文件路径");
|
||||
System.out.println("cfgPath: " + DEFAULT_CFG_PATH);
|
||||
System.out.println("datPath: " + DEFAULT_DAT_PATH);
|
||||
}
|
||||
|
||||
Path cfgPath = Paths.get(args.length > 0 ? args[0] : DEFAULT_CFG_PATH);
|
||||
Path datPath = Paths.get(args.length > 1 ? args[1] : DEFAULT_DAT_PATH);
|
||||
int parseType = args.length > 2 ? Integer.parseInt(args[2]) : DEFAULT_PARSE_TYPE;
|
||||
|
||||
WaveFileComponent waveFileComponent = new WaveFileComponent();
|
||||
try (InputStream cfgStream = Files.newInputStream(cfgPath);
|
||||
InputStream datStream = Files.newInputStream(datPath)) {
|
||||
WaveDataDTO waveDataDTO = waveFileComponent.getComtrade(cfgStream, datStream, parseType);
|
||||
printComtradeResult(waveDataDTO);
|
||||
|
||||
WaveDataDTO validWaveDataDTO = waveFileComponent.getValidData(waveDataDTO);
|
||||
printValidDataResult(validWaveDataDTO);
|
||||
|
||||
List<EigenvalueDTO> eigenvalues = waveFileComponent.getEigenvalue(validWaveDataDTO, DEFAULT_DYNAMIC_THRESHOLD);
|
||||
printEigenvalueResult(eigenvalues);
|
||||
}
|
||||
|
||||
runParseComtradeVectorTest(cfgPath, datPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试 parseComtradeVector 逻辑。
|
||||
*/
|
||||
private static void runParseComtradeVectorTest(Path cfgPath, Path datPath) throws Exception {
|
||||
System.out.println("=== parseComtradeVector 结果 ===");
|
||||
WaveFileComponent waveFileComponent = new WaveFileComponent();
|
||||
WaveVectorComponent waveVectorComponent = new WaveVectorComponent();
|
||||
WaveComtradeParseParam param = new WaveComtradeParseParam();
|
||||
param.setParseType(VECTOR_PARSE_TYPE);
|
||||
param.setPtType(0);
|
||||
param.setPt(1D);
|
||||
param.setCt(1D);
|
||||
param.setMonitorName("main方法调试测点");
|
||||
|
||||
try (InputStream cfgStream = Files.newInputStream(cfgPath);
|
||||
InputStream datStream = Files.newInputStream(datPath)) {
|
||||
WaveDataDTO waveDataDTO = waveFileComponent.getComtrade(cfgStream, datStream, VECTOR_PARSE_TYPE);
|
||||
applyWaveMetadata(waveDataDTO, param);
|
||||
|
||||
List<WaveVectorGroupDTO> vectorGroups = waveVectorComponent.calculateVectors(waveDataDTO);
|
||||
WaveComtradeVectorResultVO result = new WaveComtradeVectorResultVO();
|
||||
result.setMonitorName(waveDataDTO.getMonitorName());
|
||||
result.setTime(waveDataDTO.getTime());
|
||||
result.setSamplePerCycle(waveDataDTO.getComtradeCfgDTO() == null ? null : waveDataDTO.getComtradeCfgDTO().getFinalSampleRate());
|
||||
result.setCycleCount(resolveCycleCount(vectorGroups));
|
||||
result.setVectorGroups(vectorGroups);
|
||||
printParseComtradeVectorResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给向量调试结果补齐 PT/CT/测点信息。
|
||||
*/
|
||||
private static void applyWaveMetadata(WaveDataDTO waveDataDTO, WaveComtradeParseParam param) {
|
||||
waveDataDTO.setPt(param.getPt() == null || param.getPt() <= 0 ? 1D : param.getPt());
|
||||
waveDataDTO.setCt(param.getCt() == null || param.getCt() <= 0 ? 1D : param.getCt());
|
||||
waveDataDTO.setPtType(param.getPtType() == null ? 0 : param.getPtType());
|
||||
waveDataDTO.setMonitorName(param.getMonitorName() == null || param.getMonitorName().trim().isEmpty()
|
||||
? "未命名测点"
|
||||
: param.getMonitorName().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印 parseComtradeVector 的关键摘要。
|
||||
*/
|
||||
private static void printParseComtradeVectorResult(WaveComtradeVectorResultVO result) {
|
||||
System.out.println("测点名称: " + result.getMonitorName());
|
||||
System.out.println("事件时间: " + result.getTime());
|
||||
System.out.println("每周波采样点数: " + result.getSamplePerCycle());
|
||||
System.out.println("可计算周波数: " + result.getCycleCount());
|
||||
System.out.println("分组数量: " + sizeOf(result.getVectorGroups()));
|
||||
if (result.getVectorGroups() == null || result.getVectorGroups().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (WaveVectorGroupDTO group : result.getVectorGroups()) {
|
||||
System.out.println("-- 分组: channelName=" + group.getChannelName()
|
||||
+ ", unit=" + group.getUnit()
|
||||
+ ", phaseCount=" + group.getPhaseCount()
|
||||
+ ", phaseNames=" + group.getPhaseNames());
|
||||
if (group.getVectorSeries() == null || group.getVectorSeries().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
WaveCycleVectorDTO firstCycle = group.getVectorSeries().get(0);
|
||||
System.out.println(" 首周波: cycleIndex=" + firstCycle.getCycleIndex() + ", time=" + firstCycle.getTime());
|
||||
printPhaseVectorSummary(firstCycle.getPhaseVectors());
|
||||
if (firstCycle.getPositiveSequence() != null) {
|
||||
System.out.println(" 正序: amplitude=" + firstCycle.getPositiveSequence().getAmplitude()
|
||||
+ ", rms=" + firstCycle.getPositiveSequence().getRms()
|
||||
+ ", phaseAngle=" + firstCycle.getPositiveSequence().getPhaseAngle());
|
||||
}
|
||||
if (firstCycle.getNegativeSequence() != null) {
|
||||
System.out.println(" 负序: amplitude=" + firstCycle.getNegativeSequence().getAmplitude()
|
||||
+ ", rms=" + firstCycle.getNegativeSequence().getRms()
|
||||
+ ", phaseAngle=" + firstCycle.getNegativeSequence().getPhaseAngle());
|
||||
}
|
||||
if (firstCycle.getZeroSequence() != null) {
|
||||
System.out.println(" 零序: amplitude=" + firstCycle.getZeroSequence().getAmplitude()
|
||||
+ ", rms=" + firstCycle.getZeroSequence().getRms()
|
||||
+ ", phaseAngle=" + firstCycle.getZeroSequence().getPhaseAngle());
|
||||
}
|
||||
if (firstCycle.getUnbalance() != null) {
|
||||
System.out.println(" 不平衡度: negative=" + firstCycle.getUnbalance().getNegativeUnbalanceRate()
|
||||
+ ", zero=" + firstCycle.getUnbalance().getZeroUnbalanceRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印单相电能质量结果摘要。
|
||||
*/
|
||||
private static void printPhaseVectorSummary(List<WavePhaseVectorDTO> phaseVectors) {
|
||||
if (phaseVectors == null || phaseVectors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (WavePhaseVectorDTO phaseVector : phaseVectors) {
|
||||
System.out.println(" 相别=" + phaseVector.getPhaseName()
|
||||
+ ", totalRms=" + phaseVector.getTotalRms()
|
||||
+ ", fundamentalAmplitude=" + phaseVector.getFundamentalAmplitude()
|
||||
+ ", fundamentalRms=" + phaseVector.getFundamentalRms()
|
||||
+ ", fundamentalPhaseAngle=" + phaseVector.getFundamentalPhaseAngle()
|
||||
+ ", harmonicDistortionRate=" + phaseVector.getHarmonicDistortionRate());
|
||||
printHarmonicSummary("电压谐波", phaseVector.getHarmonicVoltageContentRates());
|
||||
printHarmonicSummary("电流谐波", phaseVector.getHarmonicCurrentAmplitudes());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印前几个谐波结果,避免控制台过长。
|
||||
*/
|
||||
private static void printHarmonicSummary(String label, List<WaveHarmonicDTO> harmonics) {
|
||||
if (harmonics == null || harmonics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int limit = Math.min(3, harmonics.size());
|
||||
for (int i = 0; i < limit; i++) {
|
||||
WaveHarmonicDTO harmonic = harmonics.get(i);
|
||||
System.out.println(" " + label + "-第" + harmonic.getHarmonicOrder() + "次: amplitude=" + harmonic.getAmplitude()
|
||||
+ ", rms=" + harmonic.getRms()
|
||||
+ ", rate=" + harmonic.getRate());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析向量结果中的可计算周波数。
|
||||
*/
|
||||
private static Integer resolveCycleCount(List<WaveVectorGroupDTO> vectorGroups) {
|
||||
if (vectorGroups == null || vectorGroups.isEmpty() || vectorGroups.get(0).getVectorSeries() == null) {
|
||||
return 0;
|
||||
}
|
||||
return vectorGroups.get(0).getVectorSeries().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印 getComtrade 的关键摘要,便于快速人工核对。
|
||||
*/
|
||||
private static void printComtradeResult(WaveDataDTO waveDataDTO) {
|
||||
System.out.println("=== getComtrade 结果 ===");
|
||||
System.out.println("事件时间: " + waveDataDTO.getTime());
|
||||
System.out.println("相别数: " + waveDataDTO.getIPhasic());
|
||||
System.out.println("标题数: " + sizeOf(waveDataDTO.getWaveTitle()));
|
||||
System.out.println("通道数: " + sizeOf(waveDataDTO.getChannelNames()));
|
||||
System.out.println("波形点数: " + sizeOf(waveDataDTO.getListWaveData()));
|
||||
if (waveDataDTO.getComtradeCfgDTO() != null) {
|
||||
System.out.println("模拟量通道数: " + waveDataDTO.getComtradeCfgDTO().getNAnalogNum());
|
||||
System.out.println("开关量通道数: " + waveDataDTO.getComtradeCfgDTO().getNDigitalNum());
|
||||
System.out.println("最终采样率: " + waveDataDTO.getComtradeCfgDTO().getFinalSampleRate());
|
||||
}
|
||||
if (waveDataDTO.getWaveTitle() != null && !waveDataDTO.getWaveTitle().isEmpty()) {
|
||||
System.out.println("波形标题: " + waveDataDTO.getWaveTitle());
|
||||
}
|
||||
if (waveDataDTO.getListWaveData() != null && !waveDataDTO.getListWaveData().isEmpty()) {
|
||||
System.out.println("首行数据: " + waveDataDTO.getListWaveData().get(0));
|
||||
System.out.println("末行数据: " + waveDataDTO.getListWaveData().get(waveDataDTO.getListWaveData().size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印 getValidData 的关键摘要。
|
||||
*/
|
||||
private static void printValidDataResult(WaveDataDTO waveDataDTO) {
|
||||
System.out.println("=== getValidData 结果 ===");
|
||||
System.out.println("RMS 点数: " + sizeOf(waveDataDTO.getListRmsData()));
|
||||
System.out.println("RMS 最小值点数: " + sizeOf(waveDataDTO.getListRmsMinData()));
|
||||
if (waveDataDTO.getListRmsData() != null && !waveDataDTO.getListRmsData().isEmpty()) {
|
||||
System.out.println("RMS 首行数据: " + waveDataDTO.getListRmsData().get(0));
|
||||
System.out.println("RMS 末行数据: " + waveDataDTO.getListRmsData().get(waveDataDTO.getListRmsData().size() - 1));
|
||||
}
|
||||
if (waveDataDTO.getListRmsMinData() != null && !waveDataDTO.getListRmsMinData().isEmpty()) {
|
||||
System.out.println("RMS 最小值数据: " + waveDataDTO.getListRmsMinData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印特征值结果摘要。
|
||||
*/
|
||||
private static void printEigenvalueResult(List<EigenvalueDTO> eigenvalues) {
|
||||
System.out.println("=== getEigenvalue 结果 ===");
|
||||
System.out.println("特征值数量: " + sizeOf(eigenvalues));
|
||||
if (eigenvalues == null || eigenvalues.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < eigenvalues.size(); i++) {
|
||||
EigenvalueDTO eigenvalueDTO = eigenvalues.get(i);
|
||||
System.out.println("第" + (i + 1) + "相: amplitude=" + eigenvalueDTO.getAmplitude()
|
||||
+ ", residualVoltage=" + eigenvalueDTO.getResidualVoltage()
|
||||
+ ", ratedVoltage=" + eigenvalueDTO.getRatedVoltage()
|
||||
+ ", durationTime=" + eigenvalueDTO.getDurationTime());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全返回列表大小,避免空指针。
|
||||
*/
|
||||
private static int sizeOf(List<?> list) {
|
||||
return list == null ? 0 : list.size();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.njcn.gather.tool.wave.component;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* 波形文件解析组件测试。
|
||||
*/
|
||||
class WaveFileComponentTest {
|
||||
|
||||
@Test
|
||||
void shouldRoundWaveTimeToThreeDecimals() {
|
||||
Assertions.assertEquals(1.235F, WaveFileComponent.roundWaveTime(1.2345F));
|
||||
Assertions.assertEquals(-99.988F, WaveFileComponent.roundWaveTime(-99.9876F));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRoundWaveAmplitudeToThreeDecimals() {
|
||||
Assertions.assertEquals(220.123F, WaveFileComponent.roundWaveAmplitude(220.1234F));
|
||||
Assertions.assertEquals(-12.988F, WaveFileComponent.roundWaveAmplitude(-12.9876F));
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.njcn.gather.tool.wave.component;
|
||||
|
||||
import com.njcn.gather.tool.wave.pojo.dto.ComtradeCfgDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 波形向量计算组件测试。
|
||||
*/
|
||||
class WaveVectorComponentTest {
|
||||
|
||||
@Test
|
||||
void shouldScaleVoltageVectorValuesToKv() {
|
||||
WaveVectorComponent component = new WaveVectorComponent();
|
||||
WaveDataDTO waveDataDTO = buildThreePhaseWaveData("U", 8153.209D, 1D);
|
||||
|
||||
List<WaveVectorGroupDTO> groups = component.calculateVectors(waveDataDTO);
|
||||
|
||||
Assertions.assertEquals("kV", groups.get(0).getUnit());
|
||||
Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getTotalRms());
|
||||
Assertions.assertEquals(8.1532F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalAmplitude());
|
||||
Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalRms());
|
||||
Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPositiveSequence().getRms());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepCurrentVectorValuesInAmpere() {
|
||||
WaveVectorComponent component = new WaveVectorComponent();
|
||||
WaveDataDTO waveDataDTO = buildThreePhaseWaveData("I", 35D, 1D);
|
||||
|
||||
List<WaveVectorGroupDTO> groups = component.calculateVectors(waveDataDTO);
|
||||
|
||||
Assertions.assertEquals("A", groups.get(0).getUnit());
|
||||
Assertions.assertEquals(24.7487F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getTotalRms());
|
||||
Assertions.assertEquals(35F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalAmplitude());
|
||||
Assertions.assertEquals(24.7487F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalRms());
|
||||
}
|
||||
|
||||
private WaveDataDTO buildThreePhaseWaveData(String titlePrefix, double amplitude, double ratio) {
|
||||
int samplePerCycle = 32;
|
||||
WaveDataDTO waveDataDTO = new WaveDataDTO();
|
||||
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
|
||||
cfgDTO.setFinalSampleRate(samplePerCycle);
|
||||
waveDataDTO.setComtradeCfgDTO(cfgDTO);
|
||||
waveDataDTO.setIPhasic(3);
|
||||
waveDataDTO.setPt(ratio);
|
||||
waveDataDTO.setCt(ratio);
|
||||
waveDataDTO.setWaveTitle(Arrays.asList("x", titlePrefix + "A", titlePrefix + "B", titlePrefix + "C"));
|
||||
waveDataDTO.setChannelNames(Arrays.asList("x", titlePrefix));
|
||||
waveDataDTO.setListWaveData(buildRows(samplePerCycle, amplitude));
|
||||
return waveDataDTO;
|
||||
}
|
||||
|
||||
private List<List<Float>> buildRows(int samplePerCycle, double amplitude) {
|
||||
List<List<Float>> rows = new ArrayList<>();
|
||||
for (int i = 0; i < samplePerCycle; i++) {
|
||||
double angle = 2D * Math.PI * i / samplePerCycle;
|
||||
List<Float> row = new ArrayList<>();
|
||||
row.add((float) i);
|
||||
row.add((float) (amplitude * Math.sin(angle)));
|
||||
row.add((float) (amplitude * Math.sin(angle - 2D * Math.PI / 3D)));
|
||||
row.add((float) (amplitude * Math.sin(angle + 2D * Math.PI / 3D)));
|
||||
rows.add(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package com.njcn.gather.tool.wave.service.impl;
|
||||
|
||||
import com.njcn.gather.tool.wave.component.WaveFileComponent;
|
||||
import com.njcn.gather.tool.wave.component.WaveVectorComponent;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.AnalogDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.ComtradeCfgDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
|
||||
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
|
||||
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 波形服务测试。
|
||||
*/
|
||||
class WaveServiceImplTest {
|
||||
|
||||
@Test
|
||||
void shouldUseCfgRatioWhenCfgContainsValidPtAndCt() throws Exception {
|
||||
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
|
||||
WaveDataDTO waveDataDTO = new WaveDataDTO();
|
||||
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
|
||||
cfgDTO.setLstAnalogDTO(Arrays.asList(
|
||||
buildAnalog("V", 10000F, 100F),
|
||||
buildAnalog("A", 200F, 1F)
|
||||
));
|
||||
waveDataDTO.setComtradeCfgDTO(cfgDTO);
|
||||
|
||||
WaveComtradeParseParam param = new WaveComtradeParseParam();
|
||||
param.setPt(1D);
|
||||
param.setCt(1D);
|
||||
|
||||
Method method = WaveServiceImpl.class.getDeclaredMethod("applyWaveMetadata", WaveDataDTO.class, WaveComtradeParseParam.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(service, waveDataDTO, param);
|
||||
|
||||
Assertions.assertEquals(100D, waveDataDTO.getPt());
|
||||
Assertions.assertEquals(200D, waveDataDTO.getCt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFillComtradeSummaryFields() throws Exception {
|
||||
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
|
||||
WaveDataDTO waveDataDTO = new WaveDataDTO();
|
||||
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
|
||||
cfgDTO.setNChannelNum(8);
|
||||
cfgDTO.setNPhasic(3);
|
||||
cfgDTO.setLstAnalogDTO(Arrays.asList(
|
||||
buildAnalog("V", 10000F, 100F),
|
||||
buildAnalog("A", 200F, 1F)
|
||||
));
|
||||
waveDataDTO.setComtradeCfgDTO(cfgDTO);
|
||||
waveDataDTO.setIPhasic(3);
|
||||
|
||||
WaveComtradeResultVO result = new WaveComtradeResultVO();
|
||||
Method method = WaveServiceImpl.class.getDeclaredMethod("fillComtradeSummary", WaveComtradeResultVO.class, WaveDataDTO.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(service, result, waveDataDTO);
|
||||
|
||||
Assertions.assertEquals(8, result.getTotalChannels());
|
||||
Assertions.assertEquals(3, result.getPhaseCount());
|
||||
Assertions.assertEquals("kV/A", result.getUnit());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMaskValuesInsideBottomPlatformMiddle() throws Exception {
|
||||
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
|
||||
WaveDataDTO waveDataDTO = new WaveDataDTO();
|
||||
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
|
||||
cfgDTO.setFinalSampleRate(1);
|
||||
waveDataDTO.setComtradeCfgDTO(cfgDTO);
|
||||
waveDataDTO.setListWaveData(buildRows(new float[][]{
|
||||
{0F, 100F, 100F, 100F},
|
||||
{1F, 100F, 100F, 100F},
|
||||
{2F, 100F, 100F, 100F},
|
||||
{3F, 100F, 100F, 100F},
|
||||
{4F, 80F, 100F, 100F},
|
||||
{5F, 60F, 100F, 100F},
|
||||
{6F, 60F, 100F, 100F},
|
||||
{7F, 60F, 100F, 100F},
|
||||
{8F, 95F, 100F, 100F},
|
||||
{9F, 100F, 100F, 100F}
|
||||
}));
|
||||
waveDataDTO.setListRmsData(buildRows(new float[][]{
|
||||
{0F, 100F, 100F, 100F},
|
||||
{1F, 100F, 100F, 100F},
|
||||
{2F, 100F, 100F, 100F},
|
||||
{3F, 100F, 100F, 100F},
|
||||
{4F, 80F, 100F, 100F},
|
||||
{5F, 60F, 100F, 100F},
|
||||
{6F, 60F, 100F, 100F},
|
||||
{7F, 60F, 100F, 100F},
|
||||
{8F, 95F, 100F, 100F},
|
||||
{9F, 100F, 100F, 100F}
|
||||
}));
|
||||
|
||||
Method method = WaveServiceImpl.class.getDeclaredMethod("applySimplifiedDisplay", WaveDataDTO.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(service, waveDataDTO);
|
||||
|
||||
Assertions.assertEquals(0F, waveDataDTO.getListWaveData().get(0).get(0));
|
||||
Assertions.assertEquals(100F, waveDataDTO.getListWaveData().get(0).get(1));
|
||||
Assertions.assertEquals(100F, waveDataDTO.getListWaveData().get(2).get(1));
|
||||
Assertions.assertEquals(80F, waveDataDTO.getListWaveData().get(4).get(1));
|
||||
Assertions.assertEquals(60F, waveDataDTO.getListWaveData().get(5).get(1));
|
||||
Assertions.assertNull(waveDataDTO.getListWaveData().get(6).get(1));
|
||||
Assertions.assertEquals(60F, waveDataDTO.getListWaveData().get(7).get(1));
|
||||
Assertions.assertEquals(95F, waveDataDTO.getListWaveData().get(8).get(1));
|
||||
Assertions.assertEquals(100F, waveDataDTO.getListWaveData().get(9).get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepDataWhenSimplifiedRangeCannotBeDetected() throws Exception {
|
||||
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
|
||||
WaveDataDTO waveDataDTO = new WaveDataDTO();
|
||||
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
|
||||
cfgDTO.setFinalSampleRate(1);
|
||||
waveDataDTO.setComtradeCfgDTO(cfgDTO);
|
||||
waveDataDTO.setListWaveData(buildRows(new float[][]{
|
||||
{0F, 100F, 100F, 100F},
|
||||
{1F, 99F, 100F, 100F},
|
||||
{2F, 98F, 100F, 100F}
|
||||
}));
|
||||
waveDataDTO.setListRmsData(buildRows(new float[][]{
|
||||
{0F, 100F, 100F, 100F},
|
||||
{1F, 99F, 100F, 100F},
|
||||
{2F, 98F, 100F, 100F}
|
||||
}));
|
||||
|
||||
Method method = WaveServiceImpl.class.getDeclaredMethod("applySimplifiedDisplay", WaveDataDTO.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(service, waveDataDTO);
|
||||
|
||||
Assertions.assertEquals(99F, waveDataDTO.getListWaveData().get(1).get(1));
|
||||
Assertions.assertEquals(98F, waveDataDTO.getListRmsData().get(2).get(1));
|
||||
}
|
||||
|
||||
private AnalogDTO buildAnalog(String unit, Float primary, Float secondary) {
|
||||
AnalogDTO analogDTO = new AnalogDTO();
|
||||
analogDTO.setSzUnitName(unit);
|
||||
analogDTO.setFPrimary(primary);
|
||||
analogDTO.setFSecondary(secondary);
|
||||
return analogDTO;
|
||||
}
|
||||
|
||||
private List<List<Float>> buildRows(float[][] values) {
|
||||
List<List<Float>> rows = new ArrayList<>();
|
||||
for (float[] value : values) {
|
||||
List<Float> row = new ArrayList<>();
|
||||
for (float item : value) {
|
||||
row.add(item);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user