package com.ruoyi.other.service.impl;
|
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import com.ruoyi.account.api.feignClient.AppUserWhitelistClient;
|
import com.ruoyi.account.api.feignClient.ChargingCardClient;
|
import com.ruoyi.account.api.feignClient.CompanyAllocateRecordClient;
|
import com.ruoyi.account.api.feignClient.VinManagementClient;
|
import com.ruoyi.account.api.model.TVinManagement;
|
import com.ruoyi.account.api.vo.CompanyAllocateSummaryVO;
|
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.exception.ServiceException;
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
import com.ruoyi.other.api.domain.TGroupCompany;
|
import com.ruoyi.other.api.model.TCompanyRechargeRecord;
|
import com.ruoyi.other.mapper.TCompanyRechargeRecordMapper;
|
import com.ruoyi.other.mapper.TFleetMapper;
|
import com.ruoyi.order.api.feignClient.ChargingOrderClient;
|
import com.ruoyi.system.api.domain.SysUser;
|
import com.ruoyi.system.api.feignClient.SysRoleClient;
|
import com.ruoyi.system.api.feignClient.SysUserClient;
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.Test;
|
import org.mockito.ArgumentCaptor;
|
import org.mockito.Mockito;
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Collections;
|
import java.util.Map;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
/**
|
* 公司详情(车队数量、本月充值金额)统计逻辑单测,不依赖 Spring 容器与数据库。
|
*/
|
class TGroupCompanyServiceImplTest {
|
|
/** 编辑公司用例共享的远程账号客户端 mock,供 verify 使用 */
|
private SysUserClient editUserClient;
|
|
@BeforeAll
|
static void initTableInfo() {
|
// 初始化 MyBatis-Plus 表元数据,使 LambdaQueryWrapper 可在无 Spring/DB 环境下解析列名
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
|
TCompanyRechargeRecord.class);
|
}
|
|
@Test
|
void fillsFleetCountAndCurrentMonthRechargeAmount() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
|
TFleetMapper fleetMapper = Mockito.mock(TFleetMapper.class);
|
Mockito.when(fleetMapper.selectCount(Mockito.any())).thenReturn(3L);
|
|
TCompanyRechargeRecordMapper rechargeMapper = Mockito.mock(TCompanyRechargeRecordMapper.class);
|
Mockito.when(rechargeMapper.selectList(Mockito.any()))
|
.thenReturn(Arrays.asList(record("100.00"), record(null), record("50.50")));
|
|
TGroupCompany detail = buildService(company, fleetMapper, rechargeMapper).getDetail(1);
|
|
assertEquals(3, detail.getFleetCount());
|
assertEquals(new BigDecimal("150.50"), detail.getMonthRechargeAmount());
|
}
|
|
@Test
|
void returnsZeroAmountWhenNoRechargeRecordInCurrentMonth() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
|
TFleetMapper fleetMapper = Mockito.mock(TFleetMapper.class);
|
Mockito.when(fleetMapper.selectCount(Mockito.any())).thenReturn(null);
|
|
TCompanyRechargeRecordMapper rechargeMapper = Mockito.mock(TCompanyRechargeRecordMapper.class);
|
Mockito.when(rechargeMapper.selectList(Mockito.any())).thenReturn(Collections.emptyList());
|
|
TGroupCompany detail = buildService(company, fleetMapper, rechargeMapper).getDetail(1);
|
|
assertEquals(0, detail.getFleetCount());
|
assertEquals(0, new BigDecimal("0.00").compareTo(detail.getMonthRechargeAmount()));
|
assertEquals(2, detail.getMonthRechargeAmount().scale());
|
}
|
|
/**
|
* 公司详情(带充电用户聚合)统计 VIN码车辆数量:与 VIN 列表接口同口径,
|
* 且聚合充电用户时复用同一远程结果,不重复调用远程接口。
|
*/
|
@Test
|
void fillsVinCountAndReusesRemoteVinList() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
company.setAccountMode(1);
|
company.setAvailableBalance(new BigDecimal("100.00"));
|
|
TVinManagement vin1 = new TVinManagement();
|
vin1.setId(11L);
|
vin1.setVin("TESTVIN0001");
|
vin1.setBalance(new BigDecimal("10.00"));
|
vin1.setStatus(1);
|
TVinManagement vin2 = new TVinManagement();
|
vin2.setId(12L);
|
vin2.setVin("TESTVIN0002");
|
vin2.setBalance(new BigDecimal("20.00"));
|
vin2.setStatus(1);
|
|
TFleetMapper fleetMapper = Mockito.mock(TFleetMapper.class);
|
Mockito.when(fleetMapper.selectCount(Mockito.any())).thenReturn(0L);
|
Mockito.when(fleetMapper.selectList(Mockito.any())).thenReturn(Collections.emptyList());
|
|
TCompanyRechargeRecordMapper rechargeMapper = Mockito.mock(TCompanyRechargeRecordMapper.class);
|
Mockito.when(rechargeMapper.selectList(Mockito.any())).thenReturn(Collections.emptyList());
|
|
VinManagementClient vinClient = Mockito.mock(VinManagementClient.class);
|
Mockito.when(vinClient.listByCompany(1)).thenReturn(R.ok(Arrays.asList(vin1, vin2)));
|
ChargingCardClient cardClient = Mockito.mock(ChargingCardClient.class);
|
Mockito.when(cardClient.listByCompany(1)).thenReturn(R.ok(Collections.emptyList()));
|
AppUserWhitelistClient whitelistClient = Mockito.mock(AppUserWhitelistClient.class);
|
Mockito.when(whitelistClient.listByCompany(1)).thenReturn(R.ok(Collections.emptyList()));
|
|
TGroupCompanyServiceImpl service = buildService(company, fleetMapper, rechargeMapper);
|
ReflectionTestUtils.setField(service, "vinManagementClient", vinClient);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "appUserWhitelistClient", whitelistClient);
|
|
TGroupCompany detail = service.getDetailWithChargingUsers(1);
|
|
assertEquals(2, detail.getVinCount());
|
assertEquals(2, detail.getChargingUsers().size());
|
// VIN 远程接口仅调用一次:vinCount 统计与充电用户聚合复用同一结果
|
Mockito.verify(vinClient, Mockito.times(1)).listByCompany(1);
|
}
|
|
/**
|
* VIN 远程接口异常降级:vinCount 返回 0,详情主流程不受影响
|
*/
|
@Test
|
void vinCountZeroWhenVinRemoteFails() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
company.setAccountMode(1);
|
|
TFleetMapper fleetMapper = Mockito.mock(TFleetMapper.class);
|
Mockito.when(fleetMapper.selectCount(Mockito.any())).thenReturn(0L);
|
Mockito.when(fleetMapper.selectList(Mockito.any())).thenReturn(Collections.emptyList());
|
|
TCompanyRechargeRecordMapper rechargeMapper = Mockito.mock(TCompanyRechargeRecordMapper.class);
|
Mockito.when(rechargeMapper.selectList(Mockito.any())).thenReturn(Collections.emptyList());
|
|
VinManagementClient vinClient = Mockito.mock(VinManagementClient.class);
|
Mockito.when(vinClient.listByCompany(1)).thenThrow(new RuntimeException("remote down"));
|
ChargingCardClient cardClient = Mockito.mock(ChargingCardClient.class);
|
Mockito.when(cardClient.listByCompany(1)).thenReturn(R.ok(Collections.emptyList()));
|
AppUserWhitelistClient whitelistClient = Mockito.mock(AppUserWhitelistClient.class);
|
Mockito.when(whitelistClient.listByCompany(1)).thenReturn(R.ok(Collections.emptyList()));
|
|
TGroupCompanyServiceImpl service = buildService(company, fleetMapper, rechargeMapper);
|
ReflectionTestUtils.setField(service, "vinManagementClient", vinClient);
|
ReflectionTestUtils.setField(service, "chargingCardClient", cardClient);
|
ReflectionTestUtils.setField(service, "appUserWhitelistClient", whitelistClient);
|
|
TGroupCompany detail = service.getDetailWithChargingUsers(1);
|
|
assertEquals(0, detail.getVinCount());
|
assertTrue(detail.getChargingUsers().isEmpty());
|
}
|
|
@Test
|
void returnsNullAndSkipsStatisticsWhenCompanyNotExists() {
|
TFleetMapper fleetMapper = Mockito.mock(TFleetMapper.class);
|
TCompanyRechargeRecordMapper rechargeMapper = Mockito.mock(TCompanyRechargeRecordMapper.class);
|
|
TGroupCompany detail = buildService(null, fleetMapper, rechargeMapper).getDetail(9);
|
|
assertNull(detail);
|
Mockito.verifyNoInteractions(fleetMapper, rechargeMapper);
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
void queriesCurrentMonthRechargeRecordsByCompanyAndTimeRange() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(2);
|
|
TFleetMapper fleetMapper = Mockito.mock(TFleetMapper.class);
|
Mockito.when(fleetMapper.selectCount(Mockito.any())).thenReturn(0L);
|
|
TCompanyRechargeRecordMapper rechargeMapper = Mockito.mock(TCompanyRechargeRecordMapper.class);
|
Mockito.when(rechargeMapper.selectList(Mockito.any())).thenReturn(Collections.emptyList());
|
|
buildService(company, fleetMapper, rechargeMapper).getDetail(2);
|
|
ArgumentCaptor<Wrapper<TCompanyRechargeRecord>> captor = ArgumentCaptor.forClass(Wrapper.class);
|
// getDetail 会查询两次充值记录:本月 + 累计;首次调用为本月查询
|
Mockito.verify(rechargeMapper, Mockito.times(2)).selectList(captor.capture());
|
// 过滤条件:company_id = 2,且 recharge_time ∈ [本月1日 00:00:00, 次月1日 00:00:00)
|
String sqlSegment = captor.getAllValues().get(0).getSqlSegment();
|
assertTrue(sqlSegment.contains("company_id"), sqlSegment);
|
assertTrue(sqlSegment.contains("recharge_time"), sqlSegment);
|
|
LocalDateTime start = TGroupCompanyServiceImpl.monthStart(LocalDate.now());
|
Map<?, ?> paramValues = (Map<?, ?>) ReflectionTestUtils.getField(captor.getAllValues().get(0), "paramNameValuePairs");
|
assertTrue(paramValues != null && paramValues.containsValue(2), String.valueOf(paramValues));
|
assertTrue(paramValues != null && paramValues.containsValue(start), String.valueOf(paramValues));
|
assertTrue(paramValues != null && paramValues.containsValue(start.plusMonths(1)), String.valueOf(paramValues));
|
}
|
|
@Test
|
void monthStartIsTheFirstDayOfNaturalMonth() {
|
assertEquals(LocalDateTime.of(2026, 9, 1, 0, 0, 0),
|
TGroupCompanyServiceImpl.monthStart(LocalDate.of(2026, 9, 24)));
|
assertEquals(LocalDateTime.of(2026, 9, 1, 0, 0, 0),
|
TGroupCompanyServiceImpl.monthStart(LocalDate.of(2026, 9, 1)));
|
assertEquals(LocalDateTime.of(2027, 1, 1, 0, 0, 0),
|
TGroupCompanyServiceImpl.monthStart(LocalDate.of(2026, 12, 31)).plusMonths(1));
|
}
|
|
@Test
|
void sumArrivalAmountReturnsZeroWithScaleForEmptyList() {
|
assertEquals(new BigDecimal("0.00"),
|
TGroupCompanyServiceImpl.sumArrivalAmount(new ArrayList<>()));
|
}
|
|
private TGroupCompanyServiceImpl buildService(TGroupCompany company, TFleetMapper fleetMapper,
|
TCompanyRechargeRecordMapper rechargeMapper) {
|
TGroupCompanyServiceImpl service = Mockito.spy(new TGroupCompanyServiceImpl());
|
Mockito.doReturn(company).when(service).getById(Mockito.any());
|
ReflectionTestUtils.setField(service, "fleetMapper", fleetMapper);
|
ReflectionTestUtils.setField(service, "companyRechargeRecordMapper", rechargeMapper);
|
return service;
|
}
|
|
private TCompanyRechargeRecord record(String arrivalAmount) {
|
TCompanyRechargeRecord record = new TCompanyRechargeRecord();
|
record.setArrivalAmount(arrivalAmount == null ? null : new BigDecimal(arrivalAmount));
|
return record;
|
}
|
|
/**
|
* 添加/编辑企业弹窗不再配置账户模式:未传账户模式时按共享模式(1)建档
|
*/
|
@Test
|
void addCompanyDefaultsAccountModeToSharedWhenAbsent() {
|
TGroupCompany company = new TGroupCompany();
|
company.setAccount("company01");
|
company.setPassword("pwd");
|
company.setName("测试公司");
|
|
TGroupCompanyServiceImpl service = buildAddCompanyService();
|
assertEquals(200, code(service.addCompany(company)));
|
|
ArgumentCaptor<TGroupCompany> captor = ArgumentCaptor.forClass(TGroupCompany.class);
|
Mockito.verify(service).save(captor.capture());
|
assertEquals(1, captor.getValue().getAccountMode());
|
}
|
|
/**
|
* 显式传入账户模式时不被兜底值覆盖(兼容后台/脚本直接建档)
|
*/
|
@Test
|
void addCompanyKeepsExplicitAccountMode() {
|
TGroupCompany company = new TGroupCompany();
|
company.setAccount("company02");
|
company.setPassword("pwd");
|
company.setAccountMode(2);
|
|
TGroupCompanyServiceImpl service = buildAddCompanyService();
|
assertEquals(200, code(service.addCompany(company)));
|
|
ArgumentCaptor<TGroupCompany> captor = ArgumentCaptor.forClass(TGroupCompany.class);
|
Mockito.verify(service).save(captor.capture());
|
assertEquals(2, captor.getValue().getAccountMode());
|
}
|
|
@Test
|
void updateAccountModeRejectsIllegalArgs() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
|
assertEquals(500, code(service.updateAccountMode(null, 1)));
|
assertEquals(500, code(service.updateAccountMode(1, null)));
|
assertEquals(500, code(service.updateAccountMode(1, 3)));
|
Mockito.verify(service, Mockito.never()).updateById(Mockito.any());
|
}
|
|
@Test
|
void updateAccountModeRejectsUnknownCompany() {
|
TGroupCompanyServiceImpl service = buildService(null, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
|
assertEquals(500, code(service.updateAccountMode(9, 2)));
|
Mockito.verify(service, Mockito.never()).updateById(Mockito.any());
|
}
|
|
@Test
|
void updateAccountModeOnlyUpdatesAccountModeColumn() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(2);
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(2)).thenReturn(R.ok(0));
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
Mockito.doReturn(true).when(service).updateById(Mockito.any());
|
|
assertEquals(200, code(service.updateAccountMode(2, 2)));
|
|
ArgumentCaptor<TGroupCompany> captor = ArgumentCaptor.forClass(TGroupCompany.class);
|
Mockito.verify(service).updateById(captor.capture());
|
TGroupCompany update = captor.getValue();
|
assertEquals(2, update.getId());
|
assertEquals(2, update.getAccountMode());
|
// 只更新账户模式列,其余字段保持 null,避免覆盖公司其它资料
|
assertNull(update.getName());
|
assertNull(update.getAccount());
|
assertNull(update.getStatus());
|
}
|
|
/**
|
* 分配→共享切换成功:加回公司余额、切换模式、远程回收、差额一致无需调整
|
*/
|
@Test
|
void switchAllocationToSharedRecyclesBalanceAndWritesRecords() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(2);
|
company.setName("测试公司");
|
company.setAccountMode(2);
|
company.setAvailableBalance(new BigDecimal("100.00"));
|
company.setLockedAmount(new BigDecimal("0.00"));
|
|
CompanyAllocateRecordClient allocateClient = Mockito.mock(CompanyAllocateRecordClient.class);
|
CompanyAllocateSummaryVO summary = new CompanyAllocateSummaryVO();
|
summary.setCompanyId(2);
|
summary.setTotalBalance(new BigDecimal("30.50"));
|
summary.setTotalLocked(new BigDecimal("0.00"));
|
summary.setBalanceUserCount(2);
|
Mockito.when(allocateClient.sumAllocatedBalance(2)).thenReturn(R.ok(summary));
|
Mockito.when(allocateClient.recycleAllocatedBalance(Mockito.eq(2), Mockito.any(), Mockito.eq("测试公司"),
|
Mockito.eq(new BigDecimal("100.00")))).thenReturn(R.ok(new BigDecimal("30.50")));
|
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(2)).thenReturn(R.ok(0));
|
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "companyAllocateRecordClient", allocateClient);
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
stubLambdaUpdate(service, true);
|
|
assertEquals(200, code(service.updateAccountMode(2, 1)));
|
|
// 远程回收携带回退前公司余额,写入企业账户正数流水与用户负数流水由 account 侧完成
|
Mockito.verify(allocateClient).recycleAllocatedBalance(2, null, "测试公司", new BigDecimal("100.00"));
|
Mockito.verify(orderClient).countActiveByCompany(2);
|
}
|
|
/**
|
* 存在进行中充电订单:拒绝切换且不触发回收
|
*/
|
@Test
|
void switchAllocationToSharedRejectsWhenChargingInProgress() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(3);
|
company.setName("充电中公司");
|
company.setAccountMode(2);
|
company.setAvailableBalance(new BigDecimal("50.00"));
|
company.setLockedAmount(new BigDecimal("0.00"));
|
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(3)).thenReturn(R.ok(2));
|
CompanyAllocateRecordClient allocateClient = Mockito.mock(CompanyAllocateRecordClient.class);
|
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "companyAllocateRecordClient", allocateClient);
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
|
AjaxResult result = service.updateAccountMode(3, 1);
|
|
assertEquals(500, code(result));
|
assertTrue(String.valueOf(result.get("msg")).contains("正在充电"));
|
Mockito.verify(allocateClient, Mockito.never()).sumAllocatedBalance(Mockito.anyInt());
|
Mockito.verify(allocateClient, Mockito.never()).recycleAllocatedBalance(Mockito.anyInt(), Mockito.any(),
|
Mockito.any(), Mockito.any());
|
Mockito.verify(service, Mockito.never()).updateById(Mockito.any());
|
}
|
|
/**
|
* 存在未释放充电锁定额:拒绝切换且不触发回收
|
*/
|
@Test
|
void switchAllocationToSharedRejectsWhenLockedAmountNotReleased() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(4);
|
company.setName("锁定残留公司");
|
company.setAccountMode(2);
|
company.setAvailableBalance(new BigDecimal("50.00"));
|
company.setLockedAmount(new BigDecimal("0.00"));
|
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(4)).thenReturn(R.ok(0));
|
CompanyAllocateRecordClient allocateClient = Mockito.mock(CompanyAllocateRecordClient.class);
|
CompanyAllocateSummaryVO summary = new CompanyAllocateSummaryVO();
|
summary.setCompanyId(4);
|
summary.setTotalBalance(new BigDecimal("10.00"));
|
summary.setTotalLocked(new BigDecimal("3.00"));
|
Mockito.when(allocateClient.sumAllocatedBalance(4)).thenReturn(R.ok(summary));
|
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "companyAllocateRecordClient", allocateClient);
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
|
AjaxResult result = service.updateAccountMode(4, 1);
|
|
assertEquals(500, code(result));
|
assertTrue(String.valueOf(result.get("msg")).contains("锁定金额"));
|
Mockito.verify(allocateClient, Mockito.never()).recycleAllocatedBalance(Mockito.anyInt(), Mockito.any(),
|
Mockito.any(), Mockito.any());
|
Mockito.verify(service, Mockito.never()).updateById(Mockito.any());
|
}
|
|
/**
|
* 回收失败:抛业务异常并回滚公司余额与模式(避免资金总账丢失)
|
*/
|
@Test
|
void switchAllocationToSharedRollsBackWhenRecycleFails() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(5);
|
company.setName("回滚公司");
|
company.setAccountMode(2);
|
company.setAvailableBalance(new BigDecimal("80.00"));
|
company.setLockedAmount(new BigDecimal("0.00"));
|
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(5)).thenReturn(R.ok(0));
|
CompanyAllocateRecordClient allocateClient = Mockito.mock(CompanyAllocateRecordClient.class);
|
CompanyAllocateSummaryVO summary = new CompanyAllocateSummaryVO();
|
summary.setCompanyId(5);
|
summary.setTotalBalance(new BigDecimal("20.00"));
|
summary.setTotalLocked(new BigDecimal("0.00"));
|
Mockito.when(allocateClient.sumAllocatedBalance(5)).thenReturn(R.ok(summary));
|
Mockito.when(allocateClient.recycleAllocatedBalance(Mockito.eq(5), Mockito.any(), Mockito.any(),
|
Mockito.any())).thenReturn(R.fail("回收失败"));
|
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "companyAllocateRecordClient", allocateClient);
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
stubLambdaUpdate(service, true);
|
|
assertThrows(ServiceException.class, () -> service.updateAccountMode(5, 1));
|
// 加回与回滚各一次:第二次(回滚)将模式恢复为分配模式
|
Mockito.verify(service, Mockito.times(2)).lambdaUpdate();
|
}
|
|
/**
|
* 共享→分配无进行中充电订单:通过充电校验后仅切换模式,不做任何资金操作
|
*/
|
@Test
|
void switchSharedToAllocationChecksChargingThenOnlyUpdatesMode() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(6);
|
company.setName("切分配公司");
|
company.setAccountMode(1);
|
company.setAvailableBalance(new BigDecimal("60.00"));
|
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(6)).thenReturn(R.ok(0));
|
CompanyAllocateRecordClient allocateClient = Mockito.mock(CompanyAllocateRecordClient.class);
|
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "companyAllocateRecordClient", allocateClient);
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
Mockito.doReturn(true).when(service).updateById(Mockito.any());
|
|
assertEquals(200, code(service.updateAccountMode(6, 2)));
|
|
Mockito.verify(orderClient).countActiveByCompany(6);
|
Mockito.verify(allocateClient, Mockito.never()).sumAllocatedBalance(Mockito.anyInt());
|
Mockito.verify(allocateClient, Mockito.never()).recycleAllocatedBalance(Mockito.anyInt(), Mockito.any(),
|
Mockito.any(), Mockito.any());
|
Mockito.verify(service).updateById(Mockito.any(TGroupCompany.class));
|
}
|
|
/**
|
* 共享→分配存在进行中充电订单:拒绝切换且不改模式
|
*/
|
@Test
|
void switchSharedToAllocationRejectsWhenChargingInProgress() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(7);
|
company.setName("充电中切分配公司");
|
company.setAccountMode(1);
|
company.setAvailableBalance(new BigDecimal("60.00"));
|
|
ChargingOrderClient orderClient = Mockito.mock(ChargingOrderClient.class);
|
Mockito.when(orderClient.countActiveByCompany(7)).thenReturn(R.ok(3));
|
CompanyAllocateRecordClient allocateClient = Mockito.mock(CompanyAllocateRecordClient.class);
|
|
TGroupCompanyServiceImpl service = buildService(company, Mockito.mock(TFleetMapper.class),
|
Mockito.mock(TCompanyRechargeRecordMapper.class));
|
ReflectionTestUtils.setField(service, "companyAllocateRecordClient", allocateClient);
|
ReflectionTestUtils.setField(service, "chargingOrderClient", orderClient);
|
|
AjaxResult result = service.updateAccountMode(7, 2);
|
|
assertEquals(500, code(result));
|
assertTrue(String.valueOf(result.get("msg")).contains("正在充电"));
|
Mockito.verify(allocateClient, Mockito.never()).sumAllocatedBalance(Mockito.anyInt());
|
Mockito.verify(service, Mockito.never()).updateById(Mockito.any());
|
}
|
|
/**
|
* 打桩 lambdaUpdate 链:eq/setSql/set 返回自身,update 返回指定结果
|
*/
|
@SuppressWarnings("unchecked")
|
private void stubLambdaUpdate(TGroupCompanyServiceImpl service, boolean updateResult) {
|
LambdaUpdateChainWrapper<TGroupCompany> chain = Mockito.mock(LambdaUpdateChainWrapper.class);
|
Mockito.when(chain.eq(Mockito.any(), Mockito.any())).thenReturn(chain);
|
Mockito.when(chain.setSql(Mockito.anyString())).thenReturn(chain);
|
Mockito.when(chain.set(Mockito.any(), Mockito.any())).thenReturn(chain);
|
Mockito.when(chain.update()).thenReturn(updateResult);
|
Mockito.doReturn(chain).when(service).lambdaUpdate();
|
}
|
|
/**
|
* 构建关注建档字段的 spy:远程账号相关调用全部打桩,不依赖 Spring 容器与数据库
|
*/
|
private TGroupCompanyServiceImpl buildAddCompanyService() {
|
SysUserClient sysUserClient = Mockito.mock(SysUserClient.class);
|
Mockito.when(sysUserClient.queryUserByUserName(Mockito.any())).thenReturn(R.ok());
|
Mockito.when(sysUserClient.addSysUser(Mockito.any())).thenReturn(R.ok(100L));
|
Mockito.when(sysUserClient.getSysUser(Mockito.anyLong())).thenReturn(R.ok());
|
|
SysRoleClient sysRoleClient = Mockito.mock(SysRoleClient.class);
|
Mockito.when(sysRoleClient.getRoleIdByRoleKey(Mockito.any())).thenReturn(R.ok(74L));
|
|
TGroupCompanyServiceImpl service = Mockito.spy(new TGroupCompanyServiceImpl());
|
Mockito.doReturn(0L).when(service).count(Mockito.any());
|
Mockito.doReturn(true).when(service).save(Mockito.any());
|
ReflectionTestUtils.setField(service, "sysUserClient", sysUserClient);
|
ReflectionTestUtils.setField(service, "sysRoleClient", sysRoleClient);
|
return service;
|
}
|
|
/**
|
* 编辑公司修改登录账号:按旧账号定位用户后把 userName 更新为新账号,
|
* 不误走补建分支(避免双账号残留)
|
*/
|
@Test
|
void editCompanyUpdatesUserNameWhenAccountChanged() {
|
TGroupCompany old = persistedCompany("company01");
|
TGroupCompany edit = editCompanyBody();
|
edit.setAccount("company02");
|
|
TGroupCompanyServiceImpl service = buildEditCompanyService(old, "company01", owner(100L), "company02", null);
|
|
assertEquals(200, code(service.editCompany(edit)));
|
|
ArgumentCaptor<SysUser> captor = ArgumentCaptor.forClass(SysUser.class);
|
Mockito.verify(editUserClient).updateSysUser(captor.capture());
|
assertEquals("company02", captor.getValue().getUserName());
|
Mockito.verify(editUserClient, Mockito.never()).addSysUser(Mockito.any());
|
assertEquals("company02", edit.getAccount());
|
}
|
|
/**
|
* 编辑公司修改登录密码:调用 resetPassword 且仅携带 userId 与密码(前端已 HMAC-MD5)
|
*/
|
@Test
|
void editCompanyResetsPasswordWhenProvided() {
|
TGroupCompany old = persistedCompany("company01");
|
TGroupCompany edit = editCompanyBody();
|
edit.setAccount("company01");
|
edit.setPassword("hmac-value");
|
|
TGroupCompanyServiceImpl service = buildEditCompanyService(old, "company01", owner(100L), null, null);
|
|
assertEquals(200, code(service.editCompany(edit)));
|
|
ArgumentCaptor<SysUser> pwdCaptor = ArgumentCaptor.forClass(SysUser.class);
|
Mockito.verify(editUserClient).resetPassword(pwdCaptor.capture());
|
assertEquals(Long.valueOf(100L), pwdCaptor.getValue().getUserId());
|
assertEquals("hmac-value", pwdCaptor.getValue().getPassword());
|
|
// 资料更新调用不携带密码,避免哈希/脱敏值写回
|
ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
|
Mockito.verify(editUserClient).updateSysUser(userCaptor.capture());
|
assertNull(userCaptor.getValue().getPassword());
|
}
|
|
/**
|
* 新账号已被其他用户占用:返回错误且不更新任何数据
|
*/
|
@Test
|
void editCompanyRejectsAccountOccupiedByOtherUser() {
|
TGroupCompany old = persistedCompany("company01");
|
TGroupCompany edit = editCompanyBody();
|
edit.setAccount("company02");
|
|
TGroupCompanyServiceImpl service = buildEditCompanyService(old, "company01", owner(100L),
|
"company02", owner(999L));
|
|
assertEquals(500, code(service.editCompany(edit)));
|
|
Mockito.verify(editUserClient, Mockito.never()).updateSysUser(Mockito.any());
|
Mockito.verify(editUserClient, Mockito.never()).resetPassword(Mockito.any());
|
Mockito.verify(service, Mockito.never()).updateById(Mockito.any());
|
}
|
|
/**
|
* 编辑时密码留空:不调用 resetPassword,密码保持不变
|
*/
|
@Test
|
void editCompanyKeepsPasswordWhenAbsent() {
|
TGroupCompany old = persistedCompany("company01");
|
TGroupCompany edit = editCompanyBody();
|
edit.setAccount("company01");
|
|
TGroupCompanyServiceImpl service = buildEditCompanyService(old, "company01", owner(100L), null, null);
|
|
assertEquals(200, code(service.editCompany(edit)));
|
|
Mockito.verify(editUserClient, Mockito.never()).resetPassword(Mockito.any());
|
Mockito.verify(editUserClient).updateSysUser(Mockito.any(SysUser.class));
|
}
|
|
private TGroupCompany persistedCompany(String account) {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
company.setAccount(account);
|
company.setName("旧公司名");
|
return company;
|
}
|
|
private TGroupCompany editCompanyBody() {
|
TGroupCompany company = new TGroupCompany();
|
company.setId(1);
|
company.setName("新公司名");
|
company.setContactPhone("13800000000");
|
return company;
|
}
|
|
private SysUser owner(Long userId) {
|
SysUser user = new SysUser();
|
user.setUserId(userId);
|
user.setPassword("hashed");
|
return user;
|
}
|
|
/**
|
* 构建编辑公司场景的 spy:getById 返回库里旧记录,updateById 打桩;
|
* queryUserByUserName 按账号返回不同用户(newAccountUser 非 null 时用于模拟新账号占用)
|
*/
|
private TGroupCompanyServiceImpl buildEditCompanyService(TGroupCompany old, String oldAccount, SysUser oldAccountUser,
|
String newAccount, SysUser newAccountUser) {
|
SysUserClient sysUserClient = Mockito.mock(SysUserClient.class);
|
Mockito.when(sysUserClient.queryUserByUserName(oldAccount)).thenReturn(R.ok(oldAccountUser));
|
if (newAccount != null) {
|
Mockito.when(sysUserClient.queryUserByUserName(newAccount)).thenReturn(R.ok(newAccountUser));
|
}
|
Mockito.when(sysUserClient.updateSysUser(Mockito.any())).thenReturn(R.ok(true));
|
Mockito.when(sysUserClient.resetPassword(Mockito.any())).thenReturn(R.ok());
|
this.editUserClient = sysUserClient;
|
|
TGroupCompanyServiceImpl service = Mockito.spy(new TGroupCompanyServiceImpl());
|
Mockito.doReturn(old).when(service).getById(Mockito.any());
|
Mockito.doReturn(true).when(service).updateById(Mockito.any());
|
Mockito.doReturn(0L).when(service).count(Mockito.any());
|
ReflectionTestUtils.setField(service, "sysUserClient", sysUserClient);
|
return service;
|
}
|
|
private int code(AjaxResult result) {
|
return ((Number) result.get("code")).intValue();
|
}
|
}
|