package com.ruoyi.order.service.impl;
|
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
|
import com.ruoyi.account.api.dto.ChargingCardDeductDTO;
|
import com.ruoyi.account.api.feignClient.ChargingCardClient;
|
import com.ruoyi.account.api.feignClient.CompanyAllocateRecordClient;
|
import com.ruoyi.account.api.vo.ChargingCardDeductVO;
|
import com.ruoyi.chargingPile.api.feignClient.AccountingStrategyDetailClient;
|
import com.ruoyi.chargingPile.api.feignClient.BillingTemplateClient;
|
import com.ruoyi.chargingPile.api.feignClient.ChargingGunClient;
|
import com.ruoyi.chargingPile.api.model.TChargingGun;
|
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.integration.api.model.UploadRealTimeMonitoringData;
|
import com.ruoyi.order.api.model.TCardAccountingStrategyDetailOrder;
|
import com.ruoyi.order.api.model.TCardChargingOrder;
|
import com.ruoyi.order.api.model.TCardChargingOrderAccountingStrategy;
|
import com.ruoyi.order.api.model.TVinAccountingStrategyDetailOrder;
|
import com.ruoyi.order.api.model.TVinChargingOrder;
|
import com.ruoyi.order.api.model.TVinChargingOrderAccountingStrategy;
|
import com.ruoyi.order.api.vo.TransactionRecordMessageVO;
|
import com.ruoyi.order.mapper.TCardChargingOrderMapper;
|
import com.ruoyi.order.mapper.TVinChargingOrderMapper;
|
import com.ruoyi.order.service.TCardAccountingStrategyDetailOrderService;
|
import com.ruoyi.order.service.TCardChargingOrderAccountingStrategyService;
|
import com.ruoyi.order.service.TVinAccountingStrategyDetailOrderService;
|
import com.ruoyi.order.service.TVinChargingOrderAccountingStrategyService;
|
import com.ruoyi.order.support.ChargeAccountSupport;
|
import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService;
|
import com.ruoyi.other.api.feignClient.CompanyPreferentialClient;
|
import com.ruoyi.other.api.feignClient.GroupCompanyClient;
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.Test;
|
import org.mockito.ArgumentCaptor;
|
import org.redisson.api.RLock;
|
import org.redisson.api.RedissonClient;
|
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionTemplate;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.concurrent.TimeUnit;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
/**
|
* VIN/刷卡充电结算明细开始/结束时间口径对齐测试(与 CD 充电 TaskUtil.endOrder 一致):
|
* 1) 循环内:start_time/end_time 截取快照时段位 HH:mm;
|
* 2) 循环外:details 按策略开始时间顺序生成,首段 start_time 用订单实际开始时间 HH:mm 覆盖,
|
* 末段 end_time 用订单实际结束时间 HH:mm 覆盖;
|
* 3) 订单实际时间缺失时保持快照时段位兜底。
|
*
|
* 注:本测试类中所有 R 类型的 mock 都在测试体内显式分步 stubbing,
|
* 避免在 .when().thenReturn() 调用链中再嵌套调用其他会触发 Mockito 状态切换的辅助方法。
|
*/
|
class CardVinSettlementTimeAlignmentTest {
|
|
@BeforeAll
|
static void initMybatisPlusTableInfo() {
|
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
TableInfoHelper.initTableInfo(assistant, TCardChargingOrder.class);
|
TableInfoHelper.initTableInfo(assistant, TVinChargingOrder.class);
|
}
|
|
private RLock mockedLock(boolean tryLockResult) throws InterruptedException {
|
RLock lock = mock(RLock.class);
|
when(lock.tryLock(anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(tryLockResult);
|
when(lock.isHeldByCurrentThread()).thenReturn(false);
|
return lock;
|
}
|
|
private RedissonClient mockedRedisson(RLock lock) {
|
RedissonClient redisson = mock(RedissonClient.class);
|
when(redisson.getLock(anyString())).thenReturn(lock);
|
return redisson;
|
}
|
|
/**
|
* 账户支持组件(与生产同源):企业/授权/优惠/分配流水等公共逻辑已抽取到该组件。
|
* 纯单测不启 Spring 容器,此处内联装配,远程依赖以宽松 mock 兜底(内部均有判空与兜底)。
|
*/
|
private ChargeAccountSupport newChargeAccountSupport() {
|
ChargeAccountSupport support = new ChargeAccountSupport();
|
ReflectionTestUtils.setField(support, "accountingStrategyDetailClient", mock(AccountingStrategyDetailClient.class));
|
ReflectionTestUtils.setField(support, "groupCompanyClient", mock(GroupCompanyClient.class));
|
ReflectionTestUtils.setField(support, "companyPreferentialClient", mock(CompanyPreferentialClient.class));
|
ReflectionTestUtils.setField(support, "billingTemplateClient", mock(BillingTemplateClient.class));
|
ReflectionTestUtils.setField(support, "companyAllocateRecordClient", mock(CompanyAllocateRecordClient.class));
|
return support;
|
}
|
|
/** 构造三段(尖 00-08 / 平 08-16 / 谷 16-23:59)的快照 */
|
private List<TVinAccountingStrategyDetailOrder> vinThreePeriodSnapshots() {
|
TVinAccountingStrategyDetailOrder spike = new TVinAccountingStrategyDetailOrder();
|
spike.setId(1);
|
spike.setType(1);
|
spike.setStartTime("00:00");
|
spike.setEndTime("08:00");
|
spike.setElectrovalence(new BigDecimal("1.2000"));
|
spike.setServiceCharge(new BigDecimal("0.6000"));
|
spike.setCostServiceCharge(new BigDecimal("0.6000"));
|
|
TVinAccountingStrategyDetailOrder flat = new TVinAccountingStrategyDetailOrder();
|
flat.setId(3);
|
flat.setType(3);
|
flat.setStartTime("08:00");
|
flat.setEndTime("16:00");
|
flat.setElectrovalence(new BigDecimal("0.9000"));
|
flat.setServiceCharge(new BigDecimal("0.5000"));
|
flat.setCostServiceCharge(new BigDecimal("0.5000"));
|
|
TVinAccountingStrategyDetailOrder valley = new TVinAccountingStrategyDetailOrder();
|
valley.setId(4);
|
valley.setType(4);
|
valley.setStartTime("16:00");
|
valley.setEndTime("23:59");
|
valley.setElectrovalence(new BigDecimal("0.5000"));
|
valley.setServiceCharge(new BigDecimal("0.4000"));
|
valley.setCostServiceCharge(new BigDecimal("0.4000"));
|
|
return Arrays.asList(spike, flat, valley);
|
}
|
|
private List<TCardAccountingStrategyDetailOrder> cardThreePeriodSnapshots() {
|
TCardAccountingStrategyDetailOrder spike = new TCardAccountingStrategyDetailOrder();
|
spike.setId(11);
|
spike.setType(1);
|
spike.setStartTime("00:00");
|
spike.setEndTime("08:00");
|
spike.setElectrovalence(new BigDecimal("1.2000"));
|
spike.setServiceCharge(new BigDecimal("0.6000"));
|
spike.setCostServiceCharge(new BigDecimal("0.6000"));
|
|
TCardAccountingStrategyDetailOrder flat = new TCardAccountingStrategyDetailOrder();
|
flat.setId(13);
|
flat.setType(3);
|
flat.setStartTime("08:00");
|
flat.setEndTime("16:00");
|
flat.setElectrovalence(new BigDecimal("0.9000"));
|
flat.setServiceCharge(new BigDecimal("0.5000"));
|
flat.setCostServiceCharge(new BigDecimal("0.5000"));
|
|
TCardAccountingStrategyDetailOrder valley = new TCardAccountingStrategyDetailOrder();
|
valley.setId(14);
|
valley.setType(4);
|
valley.setStartTime("16:00");
|
valley.setEndTime("23:59");
|
valley.setElectrovalence(new BigDecimal("0.5000"));
|
valley.setServiceCharge(new BigDecimal("0.4000"));
|
valley.setCostServiceCharge(new BigDecimal("0.4000"));
|
|
return Arrays.asList(spike, flat, valley);
|
}
|
|
private List<TVinAccountingStrategyDetailOrder> vinSixPeriodSnapshots() {
|
return Arrays.asList(
|
vinSnapshot(948524, 4, "00:00", "08:00"),
|
vinSnapshot(948525, 3, "08:00", "10:00"),
|
vinSnapshot(948526, 2, "10:00", "12:00"),
|
vinSnapshot(948527, 3, "12:00", "17:00"),
|
vinSnapshot(948528, 2, "17:00", "22:00"),
|
vinSnapshot(948529, 4, "22:00", "00:00"));
|
}
|
|
private TVinAccountingStrategyDetailOrder vinSnapshot(int id, int type, String start, String end) {
|
TVinAccountingStrategyDetailOrder snapshot = new TVinAccountingStrategyDetailOrder();
|
snapshot.setId(id);
|
snapshot.setType(type);
|
snapshot.setStartTime(start);
|
snapshot.setEndTime(end);
|
snapshot.setElectrovalence(new BigDecimal("1.0000"));
|
snapshot.setServiceCharge(new BigDecimal("1.0000"));
|
snapshot.setCostServiceCharge(new BigDecimal("1.0000"));
|
return snapshot;
|
}
|
|
private List<TCardAccountingStrategyDetailOrder> cardSixPeriodSnapshots() {
|
return Arrays.asList(
|
cardSnapshot(31, 4, "00:00", "08:00"),
|
cardSnapshot(32, 3, "08:00", "10:00"),
|
cardSnapshot(33, 2, "10:00", "12:00"),
|
cardSnapshot(34, 3, "12:00", "17:00"),
|
cardSnapshot(35, 2, "17:00", "22:00"),
|
cardSnapshot(36, 4, "22:00", "00:00"));
|
}
|
|
private TCardAccountingStrategyDetailOrder cardSnapshot(int id, int type, String start, String end) {
|
TCardAccountingStrategyDetailOrder snapshot = new TCardAccountingStrategyDetailOrder();
|
snapshot.setId(id);
|
snapshot.setType(type);
|
snapshot.setStartTime(start);
|
snapshot.setEndTime(end);
|
snapshot.setElectrovalence(new BigDecimal("1.0000"));
|
snapshot.setServiceCharge(new BigDecimal("1.0000"));
|
snapshot.setCostServiceCharge(new BigDecimal("1.0000"));
|
return snapshot;
|
}
|
|
private void stubVinStrategyQuerySave(TVinAccountingStrategyDetailOrderService detailOrderService,
|
TVinChargingOrderAccountingStrategyService strategyService,
|
List<TVinAccountingStrategyDetailOrder> snapshots) {
|
LambdaQueryChainWrapper<TVinAccountingStrategyDetailOrder> queryWrapper = mock(LambdaQueryChainWrapper.class);
|
when(detailOrderService.lambdaQuery()).thenReturn(queryWrapper);
|
when(queryWrapper.eq(any(), any())).thenReturn(queryWrapper);
|
when(queryWrapper.list()).thenReturn(snapshots);
|
|
LambdaUpdateChainWrapper<TVinChargingOrderAccountingStrategy> updateWrapper = mock(LambdaUpdateChainWrapper.class);
|
when(strategyService.lambdaUpdate()).thenReturn(updateWrapper);
|
when(updateWrapper.eq(any(), any())).thenReturn(updateWrapper);
|
when(updateWrapper.remove()).thenReturn(true);
|
when(strategyService.saveBatch(any(Collection.class))).thenReturn(true);
|
}
|
|
private void stubCardStrategyQuerySave(TCardAccountingStrategyDetailOrderService detailOrderService,
|
TCardChargingOrderAccountingStrategyService strategyService,
|
List<TCardAccountingStrategyDetailOrder> snapshots) {
|
LambdaQueryChainWrapper<TCardAccountingStrategyDetailOrder> queryWrapper = mock(LambdaQueryChainWrapper.class);
|
when(detailOrderService.lambdaQuery()).thenReturn(queryWrapper);
|
when(queryWrapper.eq(any(), any())).thenReturn(queryWrapper);
|
when(queryWrapper.list()).thenReturn(snapshots);
|
|
LambdaUpdateChainWrapper<TCardChargingOrderAccountingStrategy> updateWrapper = mock(LambdaUpdateChainWrapper.class);
|
when(strategyService.lambdaUpdate()).thenReturn(updateWrapper);
|
when(updateWrapper.eq(any(), any())).thenReturn(updateWrapper);
|
when(updateWrapper.remove()).thenReturn(true);
|
when(strategyService.saveBatch(any(Collection.class))).thenReturn(true);
|
}
|
|
private void stubTxExecute(TransactionTemplate txTemplate) {
|
when(txTemplate.execute(any())).thenAnswer(invocation -> {
|
TransactionCallback<?> callback = invocation.getArgument(0);
|
return callback.doInTransaction(mock(TransactionStatus.class));
|
});
|
}
|
|
/** mock 复位枪桩需要的两个 R 返回:getChargingGunById / updateChargingGunById */
|
private void stubGunAndUpdate(ChargingGunClient gunClient, int gunId) {
|
TChargingGun gun = new TChargingGun();
|
gun.setId(gunId);
|
gun.setStatus(4);
|
R<TChargingGun> gunResult = mock(R.class);
|
when(gunResult.getCode()).thenReturn(R.SUCCESS);
|
when(gunResult.getData()).thenReturn(gun);
|
when(gunClient.getChargingGunById(any())).thenReturn(gunResult);
|
|
R<String> updateResult = mock(R.class);
|
when(updateResult.getCode()).thenReturn(R.SUCCESS);
|
when(gunClient.updateChargingGunById(any(TChargingGun.class))).thenReturn(updateResult);
|
}
|
|
/** mock 刷卡解锁/扣款 R 返回 */
|
private void stubCardDeductAndUnlock(ChargingCardClient cardClient) {
|
R<Boolean> unlockResult = mock(R.class);
|
when(unlockResult.getCode()).thenReturn(R.SUCCESS);
|
when(unlockResult.getData()).thenReturn(true);
|
when(cardClient.unlockForCharging(anyLong(), anyString())).thenReturn(unlockResult);
|
|
ChargingCardDeductVO deductVO = new ChargingCardDeductVO();
|
deductVO.setDuplicated(false);
|
R<ChargingCardDeductVO> deductResult = mock(R.class);
|
when(deductResult.getCode()).thenReturn(R.SUCCESS);
|
when(deductResult.getData()).thenReturn(deductVO);
|
when(cardClient.deductForCharging(any(ChargingCardDeductDTO.class))).thenReturn(deductResult);
|
}
|
|
// ===== VD:多时段首末段覆盖 =====
|
// 订单实际开始 07:30 / 实际结束 21:45;快照三段(尖/平/谷)。
|
// 期望:首段(type=1)start_time=07:30;末段(type=4)end_time=21:45;中段(type=3)保持快照时段位。
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void vinThreePeriodOverlaysStartAndEndWithOrderActualTime() throws InterruptedException {
|
TVinChargingOrderServiceImpl service = new TVinChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TVinChargingOrderMapper mapper = mock(TVinChargingOrderMapper.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
UploadRealTimeMonitoringDataService monitoringDataService = mock(UploadRealTimeMonitoringDataService.class);
|
TVinAccountingStrategyDetailOrderService detailOrderService = mock(TVinAccountingStrategyDetailOrderService.class);
|
TVinChargingOrderAccountingStrategyService strategyService = mock(TVinChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "monitoringDataService", monitoringDataService);
|
ReflectionTestUtils.setField(service, "vinAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "vinChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TVinChargingOrder charging = new TVinChargingOrder();
|
charging.setId(2L);
|
charging.setCode("VD1");
|
charging.setStatus(4);
|
charging.setChargingGunId(7);
|
charging.setStartTime(LocalDateTime.of(2026, 9, 14, 7, 30, 0));
|
charging.setEndTime(LocalDateTime.of(2026, 9, 14, 21, 45, 0));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
UploadRealTimeMonitoringData firstMonitoring = new UploadRealTimeMonitoringData();
|
firstMonitoring.setSoc(18);
|
UploadRealTimeMonitoringData lastMonitoring = new UploadRealTimeMonitoringData();
|
lastMonitoring.setSoc(87);
|
when(monitoringDataService.getDataByOrderCode("VD1", "asc")).thenReturn(firstMonitoring);
|
when(monitoringDataService.getDataByOrderCode("VD1", "desc")).thenReturn(lastMonitoring);
|
|
stubGunAndUpdate(gunClient, 7);
|
stubVinStrategyQuerySave(detailOrderService, strategyService, vinThreePeriodSnapshots());
|
stubTxExecute(txTemplate);
|
|
// VO:尖 3 / 平 5 / 谷 8 度,触发三段写入
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("VD1");
|
vo.setSpike_charge1(new BigDecimal("3"));
|
vo.setFlat_charge2(new BigDecimal("5"));
|
vo.setValley_charge3(new BigDecimal("8"));
|
|
R result = service.vinEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<TVinChargingOrder> orderCaptor = ArgumentCaptor.forClass(TVinChargingOrder.class);
|
verify(mapper).updateById(orderCaptor.capture());
|
assertEquals(0, new BigDecimal("18").compareTo(orderCaptor.getValue().getStartSoc()));
|
assertEquals(0, new BigDecimal("87").compareTo(orderCaptor.getValue().getEndSoc()));
|
verify(monitoringDataService).getDataByOrderCode("VD1", "asc");
|
verify(monitoringDataService).getDataByOrderCode("VD1", "desc");
|
|
ArgumentCaptor<Collection<TVinChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TVinChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(3, saved.size());
|
assertEquals(Integer.valueOf(1), saved.get(0).getType());
|
assertEquals(Integer.valueOf(3), saved.get(1).getType());
|
assertEquals(Integer.valueOf(4), saved.get(2).getType());
|
|
// 首段:start_time = 07:30(订单实际开始时间 HH:mm);end_time 保持快照 "08:00"
|
assertEquals("07:30", saved.get(0).getStartTime());
|
assertEquals("08:00", saved.get(0).getEndTime());
|
|
// 中段:保持快照时段位
|
assertEquals("08:00", saved.get(1).getStartTime());
|
assertEquals("16:00", saved.get(1).getEndTime());
|
|
// 末段:end_time = 21:45(订单实际结束时间 HH:mm);start_time 保持快照 "16:00"
|
assertEquals("16:00", saved.get(2).getStartTime());
|
assertEquals("21:45", saved.get(2).getEndTime());
|
|
saved.forEach(s -> {
|
assertNotNull(s.getCreateTime());
|
assertNotNull(s.getAccountingStrategyDetailId());
|
});
|
}
|
|
// ===== VD:订单 startTime/endTime 缺失时首末段保持快照兜底 =====
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void vinThreePeriodKeepsSnapshotWhenOrderTimeMissing() throws InterruptedException {
|
TVinChargingOrderServiceImpl service = new TVinChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TVinChargingOrderMapper mapper = mock(TVinChargingOrderMapper.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
UploadRealTimeMonitoringDataService monitoringDataService = mock(UploadRealTimeMonitoringDataService.class);
|
TVinAccountingStrategyDetailOrderService detailOrderService = mock(TVinAccountingStrategyDetailOrderService.class);
|
TVinChargingOrderAccountingStrategyService strategyService = mock(TVinChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "monitoringDataService", monitoringDataService);
|
ReflectionTestUtils.setField(service, "vinAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "vinChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TVinChargingOrder charging = new TVinChargingOrder();
|
charging.setId(2L);
|
charging.setCode("VD1");
|
charging.setStatus(4);
|
charging.setChargingGunId(7);
|
// startTime/endTime 均为 null,Mongo 首末监控记录也不存在
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
when(monitoringDataService.getDataByOrderCode("VD1", "asc")).thenReturn(null);
|
when(monitoringDataService.getDataByOrderCode("VD1", "desc")).thenReturn(null);
|
|
stubGunAndUpdate(gunClient, 7);
|
stubVinStrategyQuerySave(detailOrderService, strategyService, vinThreePeriodSnapshots());
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("VD1");
|
vo.setSpike_charge1(new BigDecimal("3"));
|
vo.setFlat_charge2(new BigDecimal("5"));
|
vo.setValley_charge3(new BigDecimal("8"));
|
|
R result = service.vinEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<TVinChargingOrder> orderCaptor = ArgumentCaptor.forClass(TVinChargingOrder.class);
|
verify(mapper).updateById(orderCaptor.capture());
|
assertNull(orderCaptor.getValue().getStartSoc());
|
assertNull(orderCaptor.getValue().getEndSoc());
|
verify(monitoringDataService).getDataByOrderCode("VD1", "asc");
|
verify(monitoringDataService).getDataByOrderCode("VD1", "desc");
|
|
ArgumentCaptor<Collection<TVinChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TVinChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(3, saved.size());
|
// 首末段均未覆盖,保持快照时段位
|
assertEquals("00:00", saved.get(0).getStartTime());
|
assertEquals("08:00", saved.get(0).getEndTime());
|
assertEquals("16:00", saved.get(2).getStartTime());
|
assertEquals("23:59", saved.get(2).getEndTime());
|
}
|
|
// ===== SD:多时段首末段覆盖 =====
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void cardThreePeriodOverlaysStartAndEndWithOrderActualTime() throws InterruptedException {
|
TCardChargingOrderServiceImpl service = new TCardChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TCardChargingOrderMapper mapper = mock(TCardChargingOrderMapper.class);
|
ChargingCardClient cardClient = mock(ChargingCardClient.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TCardAccountingStrategyDetailOrderService detailOrderService = mock(TCardAccountingStrategyDetailOrderService.class);
|
TCardChargingOrderAccountingStrategyService strategyService = mock(TCardChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "cardAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "cardChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TCardChargingOrder charging = new TCardChargingOrder();
|
charging.setId(1L);
|
charging.setCode("SD1");
|
charging.setStatus(4);
|
charging.setCardId(5L);
|
charging.setChargingGunId(9);
|
charging.setChargeAmount(new BigDecimal("100.00"));
|
charging.setStartTime(LocalDateTime.of(2026, 9, 14, 7, 30, 0));
|
charging.setEndTime(LocalDateTime.of(2026, 9, 14, 21, 45, 0));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 9);
|
stubCardDeductAndUnlock(cardClient);
|
stubCardStrategyQuerySave(detailOrderService, strategyService, cardThreePeriodSnapshots());
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("SD1");
|
vo.setSpike_charge1(new BigDecimal("3"));
|
vo.setFlat_charge2(new BigDecimal("5"));
|
vo.setValley_charge3(new BigDecimal("8"));
|
|
R result = service.cardEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TCardChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TCardChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(3, saved.size());
|
assertEquals(Integer.valueOf(1), saved.get(0).getType());
|
assertEquals(Integer.valueOf(3), saved.get(1).getType());
|
assertEquals(Integer.valueOf(4), saved.get(2).getType());
|
|
// 首段:start_time = 07:30;end_time 保持快照 "08:00"
|
assertEquals("07:30", saved.get(0).getStartTime());
|
assertEquals("08:00", saved.get(0).getEndTime());
|
|
// 中段:保持快照时段位
|
assertEquals("08:00", saved.get(1).getStartTime());
|
assertEquals("16:00", saved.get(1).getEndTime());
|
|
// 末段:end_time = 21:45;start_time 保持快照 "16:00"
|
assertEquals("16:00", saved.get(2).getStartTime());
|
assertEquals("21:45", saved.get(2).getEndTime());
|
|
saved.forEach(s -> {
|
assertNotNull(s.getCreateTime());
|
assertNotNull(s.getAccountingStrategyDetailId());
|
});
|
}
|
|
// ===== SD:订单 startTime/endTime 缺失时首末段保持快照兜底 =====
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void cardThreePeriodKeepsSnapshotWhenOrderTimeMissing() throws InterruptedException {
|
TCardChargingOrderServiceImpl service = new TCardChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TCardChargingOrderMapper mapper = mock(TCardChargingOrderMapper.class);
|
ChargingCardClient cardClient = mock(ChargingCardClient.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TCardAccountingStrategyDetailOrderService detailOrderService = mock(TCardAccountingStrategyDetailOrderService.class);
|
TCardChargingOrderAccountingStrategyService strategyService = mock(TCardChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "cardAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "cardChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TCardChargingOrder charging = new TCardChargingOrder();
|
charging.setId(1L);
|
charging.setCode("SD1");
|
charging.setStatus(4);
|
charging.setCardId(5L);
|
charging.setChargingGunId(9);
|
charging.setChargeAmount(new BigDecimal("100.00"));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 9);
|
stubCardDeductAndUnlock(cardClient);
|
stubCardStrategyQuerySave(detailOrderService, strategyService, cardThreePeriodSnapshots());
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("SD1");
|
vo.setSpike_charge1(new BigDecimal("3"));
|
vo.setFlat_charge2(new BigDecimal("5"));
|
vo.setValley_charge3(new BigDecimal("8"));
|
|
R result = service.cardEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TCardChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TCardChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(3, saved.size());
|
assertEquals("00:00", saved.get(0).getStartTime());
|
assertEquals("08:00", saved.get(0).getEndTime());
|
assertEquals("16:00", saved.get(2).getStartTime());
|
assertEquals("23:59", saved.get(2).getEndTime());
|
}
|
|
// ===== SD:单时段且订单时间缺失,保持快照时段位(兼容 V0.2 现有行为) =====
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void cardSinglePeriodKeepsSnapshotWhenOrderTimeMissing() throws InterruptedException {
|
TCardChargingOrderServiceImpl service = new TCardChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TCardChargingOrderMapper mapper = mock(TCardChargingOrderMapper.class);
|
ChargingCardClient cardClient = mock(ChargingCardClient.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TCardAccountingStrategyDetailOrderService detailOrderService = mock(TCardAccountingStrategyDetailOrderService.class);
|
TCardChargingOrderAccountingStrategyService strategyService = mock(TCardChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "cardAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "cardChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TCardChargingOrder charging = new TCardChargingOrder();
|
charging.setId(1L);
|
charging.setCode("SD1");
|
charging.setStatus(4);
|
charging.setCardId(5L);
|
charging.setChargingGunId(9);
|
charging.setChargeAmount(new BigDecimal("100.00"));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 9);
|
stubCardDeductAndUnlock(cardClient);
|
|
// 单段快照(type=1,00:00-06:00)
|
TCardAccountingStrategyDetailOrder spike = new TCardAccountingStrategyDetailOrder();
|
spike.setId(11);
|
spike.setType(1);
|
spike.setStartTime("00:00");
|
spike.setEndTime("06:00");
|
spike.setElectrovalence(new BigDecimal("1.0000"));
|
spike.setServiceCharge(new BigDecimal("0.5000"));
|
spike.setCostServiceCharge(new BigDecimal("0.5000"));
|
stubCardStrategyQuerySave(detailOrderService, strategyService, Collections.singletonList(spike));
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("SD1");
|
vo.setSpike_charge1(new BigDecimal("5"));
|
|
R result = service.cardEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TCardChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TCardChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(1, saved.size());
|
// 单段:start/end 都是快照时段位(订单时间为 null 时不覆盖)
|
assertEquals("00:00", saved.get(0).getStartTime());
|
assertEquals("06:00", saved.get(0).getEndTime());
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void vinFlatCharge4UsesSnapshot948527ForReportedOrder() throws InterruptedException {
|
TVinChargingOrderServiceImpl service = new TVinChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TVinChargingOrderMapper mapper = mock(TVinChargingOrderMapper.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TVinAccountingStrategyDetailOrderService detailOrderService = mock(TVinAccountingStrategyDetailOrderService.class);
|
TVinChargingOrderAccountingStrategyService strategyService = mock(TVinChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "vinAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "vinChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TVinChargingOrder charging = new TVinChargingOrder();
|
charging.setId(2099403373421596674L);
|
charging.setCode("VD10220260914154223913");
|
charging.setStatus(4);
|
charging.setChargingGunId(7);
|
charging.setStartTime(LocalDateTime.of(2026, 9, 14, 15, 41, 49));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 7);
|
List<TVinAccountingStrategyDetailOrder> snapshots = new ArrayList<>(vinSixPeriodSnapshots());
|
snapshots.get(1).setElectrovalence(new BigDecimal("0.1000"));
|
snapshots.get(3).setElectrovalence(new BigDecimal("1.0000"));
|
Collections.reverse(snapshots);
|
stubVinStrategyQuerySave(detailOrderService, strategyService, snapshots);
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("VD10220260914154223913");
|
vo.setFlat_charge4(new BigDecimal("0.167"));
|
vo.setTotal_electricity(new BigDecimal("0.167"));
|
vo.setStart_time("2026-09-14 15:41:49.00");
|
vo.setEnd_time("2026-09-14 15:43:45.00");
|
|
R result = service.vinEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TVinChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TVinChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(1, saved.size());
|
assertEquals(Integer.valueOf(948527), saved.get(0).getAccountingStrategyDetailId());
|
assertEquals(Integer.valueOf(3), saved.get(0).getType());
|
assertEquals("15:41", saved.get(0).getStartTime());
|
assertEquals("15:43", saved.get(0).getEndTime());
|
assertEquals(0, new BigDecimal("0.167").compareTo(saved.get(0).getChargingCapacity()));
|
assertEquals(0, new BigDecimal("1.0000").compareTo(saved.get(0).getElectrovalence()));
|
assertEquals(0, new BigDecimal("0.17").compareTo(saved.get(0).getPeriodElectricPrice()));
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void cardFlatCharge4UsesFourthSnapshotInsteadOfFirstFlatSnapshot() throws InterruptedException {
|
TCardChargingOrderServiceImpl service = new TCardChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TCardChargingOrderMapper mapper = mock(TCardChargingOrderMapper.class);
|
ChargingCardClient cardClient = mock(ChargingCardClient.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TCardAccountingStrategyDetailOrderService detailOrderService = mock(TCardAccountingStrategyDetailOrderService.class);
|
TCardChargingOrderAccountingStrategyService strategyService = mock(TCardChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "cardAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "cardChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TCardChargingOrder charging = new TCardChargingOrder();
|
charging.setId(1L);
|
charging.setCode("SD-SLOT-4");
|
charging.setStatus(4);
|
charging.setCardId(5L);
|
charging.setChargingGunId(9);
|
charging.setChargeAmount(new BigDecimal("100.00"));
|
charging.setStartTime(LocalDateTime.of(2026, 9, 14, 15, 41, 49));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 9);
|
stubCardDeductAndUnlock(cardClient);
|
List<TCardAccountingStrategyDetailOrder> snapshots = new ArrayList<>(cardSixPeriodSnapshots());
|
snapshots.get(1).setElectrovalence(new BigDecimal("0.1000"));
|
snapshots.get(3).setElectrovalence(new BigDecimal("1.2000"));
|
Collections.reverse(snapshots);
|
stubCardStrategyQuerySave(detailOrderService, strategyService, snapshots);
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("SD-SLOT-4");
|
vo.setFlat_charge4(new BigDecimal("0.167"));
|
vo.setTotal_electricity(new BigDecimal("0.167"));
|
vo.setStart_time("2026-09-14 15:41:49.00");
|
vo.setEnd_time("2026-09-14 15:43:45.00");
|
|
R result = service.cardEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TCardChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TCardChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(1, saved.size());
|
assertEquals(Integer.valueOf(34), saved.get(0).getAccountingStrategyDetailId());
|
assertEquals(Integer.valueOf(3), saved.get(0).getType());
|
assertEquals("15:41", saved.get(0).getStartTime());
|
assertEquals("15:43", saved.get(0).getEndTime());
|
assertEquals(0, new BigDecimal("0.167").compareTo(saved.get(0).getChargingCapacity()));
|
assertEquals(0, new BigDecimal("1.2000").compareTo(saved.get(0).getElectrovalence()));
|
assertEquals(0, new BigDecimal("0.20").compareTo(saved.get(0).getPeriodElectricPrice()));
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void vinPeakCharge5UsesFifthSnapshotAndDeviceEndTime() throws InterruptedException {
|
TVinChargingOrderServiceImpl service = new TVinChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TVinChargingOrderMapper mapper = mock(TVinChargingOrderMapper.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TVinAccountingStrategyDetailOrderService detailOrderService = mock(TVinAccountingStrategyDetailOrderService.class);
|
TVinChargingOrderAccountingStrategyService strategyService = mock(TVinChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "vinAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "vinChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TVinChargingOrder charging = new TVinChargingOrder();
|
charging.setId(2L);
|
charging.setCode("VD-SLOT-5");
|
charging.setStatus(4);
|
charging.setChargingGunId(7);
|
charging.setStartTime(LocalDateTime.of(2026, 9, 11, 19, 46, 15));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 7);
|
stubVinStrategyQuerySave(detailOrderService, strategyService, vinSixPeriodSnapshots());
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("VD-SLOT-5");
|
vo.setPeak_charge5(new BigDecimal("0.182"));
|
vo.setStart_time("2026-09-11 19:46:15.00");
|
vo.setEnd_time("2026-09-11 19:53:44.00");
|
|
R result = service.vinEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TVinChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TVinChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(1, saved.size());
|
assertEquals(Integer.valueOf(948528), saved.get(0).getAccountingStrategyDetailId());
|
assertEquals(Integer.valueOf(2), saved.get(0).getType());
|
assertEquals("19:46", saved.get(0).getStartTime());
|
assertEquals("19:53", saved.get(0).getEndTime());
|
assertEquals(0, new BigDecimal("0.182").compareTo(saved.get(0).getChargingCapacity()));
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void cardPeakCharge5UsesFifthSnapshotAndDeviceEndTime() throws InterruptedException {
|
TCardChargingOrderServiceImpl service = new TCardChargingOrderServiceImpl();
|
ReflectionTestUtils.setField(service, "chargeAccountSupport", newChargeAccountSupport());
|
TCardChargingOrderMapper mapper = mock(TCardChargingOrderMapper.class);
|
ChargingCardClient cardClient = mock(ChargingCardClient.class);
|
ChargingGunClient gunClient = mock(ChargingGunClient.class);
|
TCardAccountingStrategyDetailOrderService detailOrderService = mock(TCardAccountingStrategyDetailOrderService.class);
|
TCardChargingOrderAccountingStrategyService strategyService = mock(TCardChargingOrderAccountingStrategyService.class);
|
TransactionTemplate txTemplate = mock(TransactionTemplate.class);
|
ReflectionTestUtils.setField(service, "baseMapper", mapper);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "chargingGunClient", gunClient);
|
ReflectionTestUtils.setField(service, "cardAccountingStrategyDetailOrderService", detailOrderService);
|
ReflectionTestUtils.setField(service, "cardChargingOrderAccountingStrategyService", strategyService);
|
ReflectionTestUtils.setField(service, "transactionTemplate", txTemplate);
|
ReflectionTestUtils.setField(service, "redissonClient", mockedRedisson(mockedLock(true)));
|
|
TCardChargingOrder charging = new TCardChargingOrder();
|
charging.setId(1L);
|
charging.setCode("SD-SLOT-5");
|
charging.setStatus(4);
|
charging.setCardId(5L);
|
charging.setChargingGunId(9);
|
charging.setChargeAmount(new BigDecimal("100.00"));
|
charging.setStartTime(LocalDateTime.of(2026, 9, 11, 20, 16, 48));
|
when(mapper.selectOne(any())).thenReturn(charging);
|
when(mapper.updateById(any())).thenReturn(1);
|
|
stubGunAndUpdate(gunClient, 9);
|
stubCardDeductAndUnlock(cardClient);
|
stubCardStrategyQuerySave(detailOrderService, strategyService, cardSixPeriodSnapshots());
|
stubTxExecute(txTemplate);
|
|
TransactionRecordMessageVO vo = new TransactionRecordMessageVO();
|
vo.setTransaction_serial_number("SD-SLOT-5");
|
vo.setPeak_charge5(new BigDecimal("0.079"));
|
vo.setStart_time("2026-09-11 20:16:48.00");
|
vo.setEnd_time("2026-09-11 20:20:54.00");
|
|
R result = service.cardEndChargeBillingCharge(vo);
|
assertEquals(R.SUCCESS, result.getCode());
|
|
ArgumentCaptor<Collection<TCardChargingOrderAccountingStrategy>> captor = ArgumentCaptor.forClass(Collection.class);
|
verify(strategyService).saveBatch(captor.capture());
|
List<TCardChargingOrderAccountingStrategy> saved = new ArrayList<>(captor.getValue());
|
assertEquals(1, saved.size());
|
assertEquals(Integer.valueOf(35), saved.get(0).getAccountingStrategyDetailId());
|
assertEquals(Integer.valueOf(2), saved.get(0).getType());
|
assertEquals("20:16", saved.get(0).getStartTime());
|
assertEquals("20:20", saved.get(0).getEndTime());
|
assertEquals(0, new BigDecimal("0.079").compareTo(saved.get(0).getChargingCapacity()));
|
}
|
}
|