feat(event): 添加暂态事件波形查看与导出功能
- 新增 getTransientEventWave 接口用于查看暂态事件波形 - 新增 exportTransientEventWaves 接口用于批量导出暂态事件波形 - 添加 EventWaveExportParam 参数类支持波形导出 - 在 EventListMapper 中增加 selectTransientDetailsByIds 查询方法 - 更新事件列表查询参数支持毫秒级时间格式 - 移除事件描述模糊查询条件优化查询性能 - 添加波形导出相关的常量和工具类集成
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user