mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-08-14 02:03:54 +08:00
fix: 修复无交易记录自动结算并增加分时电量观测对比
复用 settleOrder 主路径纠正状态/锁/实时数据取值等问题,补充 mock 单测与 sys_job SQL;交易记录到达时按实时曲线估算尖峰平谷仅打日志对比,不参与结算。
This commit is contained in:
@@ -33,7 +33,7 @@ public class AutoSettleConfigTest {
|
||||
Assert.assertEquals("金额阈值比例默认1.5", Double.valueOf(1.5), config.getAmountThresholdRatio());
|
||||
Assert.assertEquals("数据新鲜度默认30分钟", Integer.valueOf(30), config.getDataFreshnessMinutes());
|
||||
Assert.assertTrue("告警开关默认开启", config.getAlertEnabled());
|
||||
Assert.assertEquals("锁超时默认300秒", Long.valueOf(300L), config.getLockTimeout());
|
||||
Assert.assertEquals("锁超时默认60秒", Integer.valueOf(60), config.getLockTimeoutSeconds());
|
||||
Assert.assertEquals("默认cron表达式", "0 */10 * * * ?", config.getInterval());
|
||||
|
||||
System.out.println("✅ 默认值测试通过");
|
||||
@@ -83,7 +83,7 @@ public class AutoSettleConfigTest {
|
||||
config.setAmountThresholdRatio(2.0);
|
||||
config.setDataFreshnessMinutes(60);
|
||||
config.setAlertEnabled(false);
|
||||
config.setLockTimeout(600L);
|
||||
config.setLockTimeoutSeconds(600);
|
||||
|
||||
Assert.assertTrue(config.getEnabled());
|
||||
Assert.assertEquals(Integer.valueOf(20), config.getTimeoutMinutes());
|
||||
@@ -92,7 +92,7 @@ public class AutoSettleConfigTest {
|
||||
Assert.assertEquals(Double.valueOf(2.0), config.getAmountThresholdRatio());
|
||||
Assert.assertEquals(Integer.valueOf(60), config.getDataFreshnessMinutes());
|
||||
Assert.assertFalse(config.getAlertEnabled());
|
||||
Assert.assertEquals(Long.valueOf(600L), config.getLockTimeout());
|
||||
Assert.assertEquals(Integer.valueOf(600), config.getLockTimeoutSeconds());
|
||||
|
||||
System.out.println("✅ Setter/Getter测试通过");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
package com.jsowell.pile.service.impl;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
|
||||
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import com.jsowell.common.enums.adapay.MerchantDelayModeEnum;
|
||||
import com.jsowell.common.enums.ykc.OrderStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.PileConnectorDataBaseStatusEnum;
|
||||
import com.jsowell.pile.config.AutoSettleConfig;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.mapper.OrderBasicInfoMapper;
|
||||
import com.jsowell.pile.service.PileConnectorInfoService;
|
||||
import com.jsowell.pile.service.PileMerchantInfoService;
|
||||
import com.jsowell.pile.service.programlogic.AbstractProgramLogic;
|
||||
import com.jsowell.pile.service.programlogic.ProgramLogicFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
import org.objenesis.ObjenesisStd;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 无交易记录自动结算主流程 mock 单测(不连库、不启 Spring)。
|
||||
*/
|
||||
public class AutoSettleOrdersWithoutTransactionRecordTest {
|
||||
|
||||
private static final String MODE = MerchantDelayModeEnum.NOT_DELAY.getValue();
|
||||
private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private OrderBasicInfoServiceImpl service;
|
||||
private OrderBasicInfoMapper orderBasicInfoMapper;
|
||||
private RedisCache redisCache;
|
||||
private PileMerchantInfoService pileMerchantInfoService;
|
||||
private PileConnectorInfoService pileConnectorInfoService;
|
||||
private AutoSettleConfig autoSettleConfig;
|
||||
private AbstractProgramLogic orderLogic;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
orderBasicInfoMapper = mock(OrderBasicInfoMapper.class);
|
||||
redisCache = mock(RedisCache.class);
|
||||
pileMerchantInfoService = mock(PileMerchantInfoService.class);
|
||||
pileConnectorInfoService = mock(PileConnectorInfoService.class);
|
||||
autoSettleConfig = new AutoSettleConfig();
|
||||
autoSettleConfig.setEnabled(true);
|
||||
autoSettleConfig.setTimeoutMinutes(10);
|
||||
autoSettleConfig.setBatchSize(100);
|
||||
autoSettleConfig.setAmountThresholdRatio(1.5D);
|
||||
autoSettleConfig.setDataFreshnessMinutes(30);
|
||||
autoSettleConfig.setAlertEnabled(true);
|
||||
autoSettleConfig.setLockTimeoutSeconds(60);
|
||||
|
||||
OrderBasicInfoServiceImpl raw = new ObjenesisStd().newInstance(OrderBasicInfoServiceImpl.class);
|
||||
setField(raw, "logger", LoggerFactory.getLogger(AutoSettleOrdersWithoutTransactionRecordTest.class));
|
||||
setField(raw, "orderBasicInfoMapper", orderBasicInfoMapper);
|
||||
setField(raw, "redisCache", redisCache);
|
||||
setField(raw, "pileMerchantInfoService", pileMerchantInfoService);
|
||||
setField(raw, "pileConnectorInfoService", pileConnectorInfoService);
|
||||
setField(raw, "autoSettleConfig", autoSettleConfig);
|
||||
|
||||
service = Mockito.spy(raw);
|
||||
|
||||
orderLogic = mock(AbstractProgramLogic.class);
|
||||
ProgramLogicFactory.register(MODE, orderLogic);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ProgramLogicFactory.register(MODE, mock(AbstractProgramLogic.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabled_shouldSkipAll() {
|
||||
autoSettleConfig.setEnabled(false);
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
verify(orderBasicInfoMapper, never()).selectPendingAutoSettleOrders(any(), any(), anyInt());
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyPending_shouldNotSettle() {
|
||||
when(orderBasicInfoMapper.selectPendingAutoSettleOrders(any(), any(), anyInt()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lockFail_shouldSkip() {
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
when(orderBasicInfoMapper.selectPendingAutoSettleOrders(any(), any(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(pending));
|
||||
when(redisCache.setnx(eq("settle_order_T001"), anyString(), anyLong())).thenReturn(false);
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
verify(orderBasicInfoMapper, never()).selectOrderBasicInfoById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void amountOverThreshold_shouldSkipAndNotSettle() {
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
pending.setPayAmount(new BigDecimal("10.00"));
|
||||
stubCommonHappyPath(pending, "12.0000", "20.00");
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
verify(redisCache).unLock("settle_order_T001");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void offlinePile_shouldSkip() {
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
when(orderBasicInfoMapper.selectPendingAutoSettleOrders(any(), any(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(pending));
|
||||
stubLock("settle_order_T001");
|
||||
when(orderBasicInfoMapper.selectOrderBasicInfoById(1L)).thenReturn(copy(pending));
|
||||
when(pileConnectorInfoService.checkPileOffLine("32010600000001")).thenReturn(true);
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
verify(redisCache).unLock("settle_order_T001");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grayscaleNotMatch_shouldSkip() {
|
||||
autoSettleConfig.setGrayscaleStationIds(Collections.singletonList(1001L));
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
pending.setStationId("2002");
|
||||
when(orderBasicInfoMapper.selectPendingAutoSettleOrders(any(), any(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(pending));
|
||||
stubLock("settle_order_T001");
|
||||
when(orderBasicInfoMapper.selectOrderBasicInfoById(1L)).thenReturn(copy(pending));
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
verify(pileConnectorInfoService, never()).checkPileOffLine(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidRealtime_shouldSkip() {
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
stubCommonHappyPath(pending, "1.0000", "0");
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
verify(orderLogic, never()).settleOrder(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void success_shouldCallSettleOrderWithPileAmount() {
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
stubCommonHappyPath(pending, "12.3456", "23.45");
|
||||
|
||||
AtomicInteger selectCount = new AtomicInteger();
|
||||
when(orderBasicInfoMapper.selectOrderBasicInfoById(1L)).thenAnswer(inv -> {
|
||||
int n = selectCount.incrementAndGet();
|
||||
OrderBasicInfo o = copy(pending);
|
||||
if (n >= 2) {
|
||||
o.setOrderStatus(OrderStatusEnum.ORDER_COMPLETE.getValue());
|
||||
o.setSettlementTime(new Date());
|
||||
}
|
||||
return o;
|
||||
});
|
||||
doAnswer(inv -> null).when(orderLogic).settleOrder(any(TransactionRecordsData.class), any(OrderBasicInfo.class));
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
ArgumentCaptor<TransactionRecordsData> dataCaptor = ArgumentCaptor.forClass(TransactionRecordsData.class);
|
||||
verify(orderLogic, times(1)).settleOrder(dataCaptor.capture(), any(OrderBasicInfo.class));
|
||||
TransactionRecordsData data = dataCaptor.getValue();
|
||||
Assert.assertEquals("23.45", data.getConsumptionAmount());
|
||||
Assert.assertEquals("12.3456", data.getTotalElectricity());
|
||||
Assert.assertNull("不应填尖峰平谷,保证以桩端金额为准", data.getFlatUsedElectricity());
|
||||
Assert.assertEquals("无交易记录自动结算", data.getStopReasonMsg());
|
||||
verify(redisCache).unLock("settle_order_T001");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settleOrderNotComplete_shouldStillUnlock() {
|
||||
OrderBasicInfo pending = basePendingOrder();
|
||||
stubCommonHappyPath(pending, "12.3456", "23.45");
|
||||
when(orderBasicInfoMapper.selectOrderBasicInfoById(1L)).thenReturn(copy(pending));
|
||||
doAnswer(inv -> null).when(orderLogic).settleOrder(any(TransactionRecordsData.class), any(OrderBasicInfo.class));
|
||||
|
||||
service.autoSettleOrdersWithoutTransactionRecord();
|
||||
|
||||
verify(orderLogic, times(1)).settleOrder(any(TransactionRecordsData.class), any(OrderBasicInfo.class));
|
||||
verify(redisCache).unLock("settle_order_T001");
|
||||
}
|
||||
|
||||
private static void setField(Object target, String name, Object value) {
|
||||
try {
|
||||
Field field = null;
|
||||
Class<?> type = target.getClass();
|
||||
while (type != null) {
|
||||
try {
|
||||
field = type.getDeclaredField(name);
|
||||
break;
|
||||
} catch (NoSuchFieldException ex) {
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (field == null) {
|
||||
throw new NoSuchFieldException(name);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
field.set(target, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void stubLock(String lockKey) {
|
||||
final String[] owner = new String[1];
|
||||
when(redisCache.setnx(eq(lockKey), anyString(), anyLong())).thenAnswer(inv -> {
|
||||
owner[0] = inv.getArgument(1);
|
||||
return true;
|
||||
});
|
||||
when(redisCache.getCacheObject(lockKey)).thenAnswer(inv -> owner[0]);
|
||||
}
|
||||
|
||||
private void stubCommonHappyPath(OrderBasicInfo pending, String degree, String amount) {
|
||||
when(orderBasicInfoMapper.selectPendingAutoSettleOrders(any(), any(), anyInt()))
|
||||
.thenReturn(Collections.singletonList(pending));
|
||||
stubLock("settle_order_T001");
|
||||
when(orderBasicInfoMapper.selectOrderBasicInfoById(1L)).thenReturn(copy(pending));
|
||||
when(pileConnectorInfoService.checkPileOffLine("32010600000001")).thenReturn(false);
|
||||
when(redisCache.getCacheObject(contains("pile_connector_status:")))
|
||||
.thenReturn(PileConnectorDataBaseStatusEnum.FREE.getValue());
|
||||
when(pileMerchantInfoService.getDelayModeByMerchantId(anyString())).thenReturn(MODE);
|
||||
|
||||
RealTimeMonitorData realtime = new RealTimeMonitorData();
|
||||
realtime.setTransactionCode(pending.getTransactionCode());
|
||||
realtime.setChargingDegree(degree);
|
||||
realtime.setChargingAmount(amount);
|
||||
realtime.setLossDegree(degree);
|
||||
realtime.setDateTime(LocalDateTime.now().minusMinutes(15).format(FMT));
|
||||
doReturn(Collections.singletonList(realtime)).when(service).getChargingRealTimeData(pending.getTransactionCode());
|
||||
}
|
||||
|
||||
private OrderBasicInfo basePendingOrder() {
|
||||
OrderBasicInfo order = new OrderBasicInfo();
|
||||
order.setId(1);
|
||||
order.setOrderCode("O001");
|
||||
order.setTransactionCode("T001");
|
||||
order.setOrderStatus(OrderStatusEnum.STAY_SETTLEMENT.getValue());
|
||||
order.setStationId("1001");
|
||||
order.setMerchantId("M001");
|
||||
order.setPileSn("32010600000001");
|
||||
order.setConnectorCode("01");
|
||||
order.setPayAmount(new BigDecimal("50.00"));
|
||||
order.setChargeStartTime(new Date(System.currentTimeMillis() - 60L * 60L * 1000L));
|
||||
order.setChargeEndTime(new Date(System.currentTimeMillis() - 20L * 60L * 1000L));
|
||||
order.setSettlementTime(null);
|
||||
return order;
|
||||
}
|
||||
|
||||
private OrderBasicInfo copy(OrderBasicInfo src) {
|
||||
OrderBasicInfo order = new OrderBasicInfo();
|
||||
order.setId(src.getId());
|
||||
order.setOrderCode(src.getOrderCode());
|
||||
order.setTransactionCode(src.getTransactionCode());
|
||||
order.setOrderStatus(src.getOrderStatus());
|
||||
order.setStationId(src.getStationId());
|
||||
order.setMerchantId(src.getMerchantId());
|
||||
order.setPileSn(src.getPileSn());
|
||||
order.setConnectorCode(src.getConnectorCode());
|
||||
order.setPayAmount(src.getPayAmount());
|
||||
order.setChargeStartTime(src.getChargeStartTime());
|
||||
order.setChargeEndTime(src.getChargeEndTime());
|
||||
order.setSettlementTime(src.getSettlementTime());
|
||||
return order;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.jsowell.pile.util;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
|
||||
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* SettlementDataConverter 单元测试
|
||||
*/
|
||||
public class SettlementDataConverterTest {
|
||||
|
||||
@Test
|
||||
public void testConvertKeepsPileAmountWithoutTouBreakdown() {
|
||||
OrderBasicInfo order = new OrderBasicInfo();
|
||||
order.setOrderCode("O001");
|
||||
order.setTransactionCode("T001");
|
||||
order.setPileSn("32010600000001");
|
||||
order.setConnectorCode("01");
|
||||
order.setChargeStartTime(new Date(System.currentTimeMillis() - 3600_000L));
|
||||
order.setChargeEndTime(new Date(System.currentTimeMillis() - 1200_000L));
|
||||
|
||||
RealTimeMonitorData realTime = new RealTimeMonitorData();
|
||||
realTime.setTransactionCode("T001");
|
||||
realTime.setChargingDegree("12.3456");
|
||||
realTime.setChargingAmount("23.45");
|
||||
realTime.setLossDegree("12.3456");
|
||||
realTime.setDateTime("2026-08-11 12:00:00");
|
||||
|
||||
TransactionRecordsData data = SettlementDataConverter.convertFromRealTimeData(order, realTime);
|
||||
Assert.assertEquals("23.45", data.getConsumptionAmount());
|
||||
Assert.assertEquals("12.3456", data.getTotalElectricity());
|
||||
Assert.assertNull("不应填充尖段电量,否则 settleOrder 会改用平台计价", data.getSharpUsedElectricity());
|
||||
Assert.assertNull(data.getPeakUsedElectricity());
|
||||
Assert.assertNull(data.getFlatUsedElectricity());
|
||||
Assert.assertNull(data.getValleyUsedElectricity());
|
||||
Assert.assertEquals("无交易记录自动结算", data.getStopReasonMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidForSettlement() {
|
||||
RealTimeMonitorData ok = new RealTimeMonitorData();
|
||||
ok.setChargingDegree("1.0");
|
||||
ok.setChargingAmount("1.2");
|
||||
Assert.assertTrue(SettlementDataConverter.isValidForSettlement(ok));
|
||||
|
||||
RealTimeMonitorData zeroAmount = new RealTimeMonitorData();
|
||||
zeroAmount.setChargingDegree("1.0");
|
||||
zeroAmount.setChargingAmount("0");
|
||||
Assert.assertFalse(SettlementDataConverter.isValidForSettlement(zeroAmount));
|
||||
|
||||
RealTimeMonitorData zeroDegree = new RealTimeMonitorData();
|
||||
zeroDegree.setChargingDegree("0");
|
||||
zeroDegree.setChargingAmount("1.2");
|
||||
Assert.assertFalse(SettlementDataConverter.isValidForSettlement(zeroDegree));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.jsowell.pile.util;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
|
||||
import com.jsowell.pile.vo.web.BillingTemplateVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
public class TouElectricityEstimatorTest {
|
||||
|
||||
@Test
|
||||
public void emptySamples() {
|
||||
TouElectricityEstimateResult r = TouElectricityEstimator.estimate(Collections.emptyList(), null);
|
||||
Assert.assertEquals("no_realtime_samples", r.getNote());
|
||||
Assert.assertEquals(0, r.getSampleCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void splitByPeriodRules() {
|
||||
BillingTemplateVO template = new BillingTemplateVO();
|
||||
// 08:00-12:00 峰;其余若未配置则 unmatched 记入平
|
||||
template.setPeakApplyDate("08:00:00-12:00:00");
|
||||
template.setFlatApplyDate("00:00:00-08:00:00,12:00:00-23:59:59");
|
||||
|
||||
RealTimeMonitorData s1 = sample("2026-08-12 07:50:00", "1.0000");
|
||||
RealTimeMonitorData s2 = sample("2026-08-12 08:10:00", "2.0000"); // delta 1.0 -> peak? end 08:10 peak
|
||||
// wait: delta from 07:50 to 08:10 attributed to 08:10 -> peak
|
||||
RealTimeMonitorData s3 = sample("2026-08-12 09:00:00", "5.0000"); // delta 3.0 peak
|
||||
RealTimeMonitorData s4 = sample("2026-08-12 12:30:00", "6.5000"); // delta 1.5 flat (12:30 in flat)
|
||||
|
||||
TouElectricityEstimateResult r = TouElectricityEstimator.estimate(Arrays.asList(s1, s2, s3, s4), template);
|
||||
Assert.assertEquals(4, r.getSampleCount());
|
||||
Assert.assertEquals(0, new BigDecimal("4.0000").compareTo(r.getPeak().setScale(4))); // 1+3
|
||||
Assert.assertEquals(0, new BigDecimal("1.5000").compareTo(r.getFlat().setScale(4)));
|
||||
Assert.assertEquals(0, new BigDecimal("5.5000").compareTo(r.getTotalFromDeltas().setScale(4)));
|
||||
Assert.assertEquals(0, new BigDecimal("6.5000").compareTo(r.getTotalFromLastSample().setScale(4)));
|
||||
Assert.assertEquals("ok", r.getNote());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativeDeltaIgnored() {
|
||||
BillingTemplateVO template = new BillingTemplateVO();
|
||||
template.setFlatApplyDate("00:00:00-23:59:59");
|
||||
RealTimeMonitorData s1 = sample("2026-08-12 10:00:00", "2.0000");
|
||||
RealTimeMonitorData s2 = sample("2026-08-12 10:01:00", "1.5000"); // neg
|
||||
RealTimeMonitorData s3 = sample("2026-08-12 10:02:00", "3.0000"); // +1.5 from 1.5
|
||||
|
||||
TouElectricityEstimateResult r = TouElectricityEstimator.estimate(Arrays.asList(s1, s2, s3), template);
|
||||
Assert.assertEquals(1, r.getNegativeOrZeroDeltaCount());
|
||||
Assert.assertEquals(0, new BigDecimal("1.5000").compareTo(r.getFlat().setScale(4)));
|
||||
}
|
||||
|
||||
private static RealTimeMonitorData sample(String dt, String degree) {
|
||||
RealTimeMonitorData d = new RealTimeMonitorData();
|
||||
d.setDateTime(dt);
|
||||
d.setChargingDegree(degree);
|
||||
return d;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user