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

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

View File

@@ -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) {

View File

@@ -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")

View File

@@ -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,

View File

@@ -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 &gt;= #{param.timeStart}
</if>
<if test="param.timeEnd != null and param.timeEnd != ''">
AND TIMEID &lt;= #{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() &gt; 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=",">

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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());

View File

@@ -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"));
}
}

View File

@@ -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());
}
}

View File

@@ -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(), "本功能不提供分页检索接口");
}
}
}

View File

@@ -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(), "趋势检索请求不再携带相别条件");
}
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}