| | |
| | | |
| | | 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 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() { |
| | |
| | | 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); |
| | |
| | | buildService(company, fleetMapper, rechargeMapper).getDetail(2); |
| | | |
| | | ArgumentCaptor<Wrapper<TCompanyRechargeRecord>> captor = ArgumentCaptor.forClass(Wrapper.class); |
| | | Mockito.verify(rechargeMapper).selectList(captor.capture()); |
| | | // 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.getValue().getSqlSegment(); |
| | | 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.getValue(), "paramNameValuePairs"); |
| | | 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)); |
| | |
| | | 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(); |
| | | } |
| | | } |