From 7617d46abee2950b7336e3379f8c7a564939cb54 Mon Sep 17 00:00:00 2001
From: yanderong <307678080@qq.com>
Date: 星期六, 10 十月 2026 17:54:56 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/2.3' into 2.3
---
ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TCompanyAllocateRecordServiceImplTest.java | 142 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 142 insertions(+), 0 deletions(-)
diff --git a/ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TCompanyAllocateRecordServiceImplTest.java b/ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TCompanyAllocateRecordServiceImplTest.java
index b55cf0d..b391f11 100644
--- a/ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TCompanyAllocateRecordServiceImplTest.java
+++ b/ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TCompanyAllocateRecordServiceImplTest.java
@@ -3,9 +3,17 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.api.dto.CompanyAllocateRecordQueryDTO;
+import com.ruoyi.account.api.model.TAppUserWhitelist;
+import com.ruoyi.account.api.model.TChargingCard;
import com.ruoyi.account.api.model.TCompanyAllocateRecord;
+import com.ruoyi.account.api.model.TVinManagement;
+import com.ruoyi.account.api.vo.CompanyAllocateSummaryVO;
+import com.ruoyi.account.mapper.TAppUserWhitelistMapper;
+import com.ruoyi.account.mapper.TChargingCardMapper;
import com.ruoyi.account.mapper.TCompanyAllocateRecordMapper;
+import com.ruoyi.account.mapper.TVinManagementMapper;
import com.ruoyi.common.core.context.SecurityContextHolder;
+import com.ruoyi.common.core.exception.ServiceException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -13,17 +21,26 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
+import java.util.Arrays;
+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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -35,13 +52,23 @@
class TCompanyAllocateRecordServiceImplTest {
@Mock private TCompanyAllocateRecordMapper mapper;
+ @Mock private TVinManagementMapper vinMapper;
+ @Mock private TChargingCardMapper cardMapper;
+ @Mock private TAppUserWhitelistMapper whitelistMapper;
+ @Mock private RedissonClient redissonClient;
+ @Mock private RLock lock;
private TCompanyAllocateRecordServiceImpl service;
@BeforeEach
void setUp() {
service = spy(new TCompanyAllocateRecordServiceImpl());
ReflectionTestUtils.setField(service, "baseMapper", mapper);
+ ReflectionTestUtils.setField(service, "vinManagementMapper", vinMapper);
+ ReflectionTestUtils.setField(service, "chargingCardMapper", cardMapper);
+ ReflectionTestUtils.setField(service, "appUserWhitelistMapper", whitelistMapper);
+ ReflectionTestUtils.setField(service, "redissonClient", redissonClient);
lenient().when(mapper.insert(any(TCompanyAllocateRecord.class))).thenReturn(1);
+ lenient().when(redissonClient.getLock(anyString())).thenReturn(lock);
SecurityContextHolder.setUserId("123");
SecurityContextHolder.setUserName("admin");
}
@@ -161,6 +188,121 @@
return captor.getValue();
}
+ // ============ 账户模式切换回收相关 ============
+
+ @Test
+ void sumAllocatedBalanceAggregatesThreeTypes() {
+ TVinManagement vin = new TVinManagement();
+ vin.setId(1L);
+ vin.setBalance(new BigDecimal("10.50"));
+ vin.setLockedAmount(new BigDecimal("2.00"));
+ TVinManagement vin2 = new TVinManagement();
+ vin2.setId(2L);
+ vin2.setBalance(new BigDecimal("5.25"));
+ vin2.setLockedAmount(BigDecimal.ZERO);
+ when(vinMapper.selectList(any())).thenReturn(Arrays.asList(vin, vin2));
+
+ TChargingCard card = new TChargingCard();
+ card.setId(3L);
+ card.setBalance(new BigDecimal("8.00"));
+ card.setLockedAmount(new BigDecimal("1.50"));
+ when(cardMapper.selectList(any())).thenReturn(Collections.singletonList(card));
+
+ TAppUserWhitelist whitelist = new TAppUserWhitelist();
+ whitelist.setId(4L);
+ whitelist.setBalance(new BigDecimal("6.00"));
+ whitelist.setLockedAmount(BigDecimal.ZERO);
+ when(whitelistMapper.selectList(any())).thenReturn(Collections.singletonList(whitelist));
+
+ CompanyAllocateSummaryVO summary = service.sumAllocatedBalance(9);
+
+ assertEquals(9, summary.getCompanyId());
+ assertEquals(new BigDecimal("29.75"), summary.getTotalBalance());
+ assertEquals(new BigDecimal("3.50"), summary.getTotalLocked());
+ assertEquals(4, summary.getBalanceUserCount());
+ }
+
+ @Test
+ void recycleAllocatedBalanceClearsAndWritesBothSides() throws InterruptedException {
+ when(lock.tryLock(5L, 30L, TimeUnit.SECONDS)).thenReturn(true);
+ when(lock.isHeldByCurrentThread()).thenReturn(true);
+
+ TVinManagement vin = new TVinManagement();
+ vin.setId(1L);
+ vin.setBalance(new BigDecimal("10.00"));
+ vin.setVin("LVIN0001");
+ vin.setPlateNumber("粤A12345");
+ when(vinMapper.selectList(any())).thenReturn(Collections.singletonList(vin));
+ when(vinMapper.clearBalance(eq(1L), eq(123L))).thenReturn(1);
+
+ TChargingCard card = new TChargingCard();
+ card.setId(2L);
+ card.setBalance(new BigDecimal("20.00"));
+ card.setCardNo("CARD0001");
+ when(cardMapper.selectList(any())).thenReturn(Collections.singletonList(card));
+ when(cardMapper.clearBalance(eq(2L), eq(123L))).thenReturn(1);
+
+ TAppUserWhitelist whitelist = new TAppUserWhitelist();
+ whitelist.setId(3L);
+ whitelist.setBalance(new BigDecimal("30.00"));
+ whitelist.setPhone("13800000000");
+ whitelist.setName("张三");
+ when(whitelistMapper.selectList(any())).thenReturn(Collections.singletonList(whitelist));
+ when(whitelistMapper.clearBalance(eq(3L), eq("admin"))).thenReturn(1);
+
+ BigDecimal recycled = service.recycleAllocatedBalance(9, "admin", "测试公司", new BigDecimal("100.00"));
+
+ assertEquals(new BigDecimal("60.00"), recycled);
+ verify(vinMapper).clearBalance(1L, 123L);
+ verify(cardMapper).clearBalance(2L, 123L);
+ verify(whitelistMapper).clearBalance(3L, "admin");
+ // 3 条用户负流水 + 1 条企业账户正流水
+ verify(mapper, times(4)).insert(any(TCompanyAllocateRecord.class));
+ ArgumentCaptor<TCompanyAllocateRecord> captor = ArgumentCaptor.forClass(TCompanyAllocateRecord.class);
+ verify(mapper, times(4)).insert(captor.capture());
+ List<TCompanyAllocateRecord> records = captor.getAllValues();
+ TCompanyAllocateRecord companyRecord = records.get(records.size() - 1);
+ assertEquals(TCompanyAllocateRecord.USER_TYPE_COMPANY, companyRecord.getUserType());
+ assertEquals("测试公司", companyRecord.getUserInfo());
+ assertEquals(TCompanyAllocateRecord.SOURCE_MODE_SWITCH, companyRecord.getChangeSource());
+ assertEquals(new BigDecimal("100.00"), companyRecord.getBeforeBalance());
+ assertEquals(new BigDecimal("60.00"), companyRecord.getAmount());
+ assertEquals(new BigDecimal("160.00"), companyRecord.getAfterBalance());
+ for (int i = 0; i < 3; i++) {
+ assertEquals(TCompanyAllocateRecord.SOURCE_MODE_SWITCH, records.get(i).getChangeSource());
+ assertTrue(records.get(i).getAmount().signum() < 0);
+ }
+ }
+
+ @Test
+ void recycleAllocatedBalanceIsIdempotentWhenNoBalanceLeft() throws InterruptedException {
+ when(lock.tryLock(5L, 30L, TimeUnit.SECONDS)).thenReturn(true);
+ when(lock.isHeldByCurrentThread()).thenReturn(true);
+ when(vinMapper.selectList(any())).thenReturn(Collections.emptyList());
+ when(cardMapper.selectList(any())).thenReturn(Collections.emptyList());
+ when(whitelistMapper.selectList(any())).thenReturn(Collections.emptyList());
+
+ BigDecimal recycled = service.recycleAllocatedBalance(9, "admin", "测试公司", new BigDecimal("100.00"));
+
+ assertEquals(new BigDecimal("0.00"), recycled);
+ verify(mapper, never()).insert(any(TCompanyAllocateRecord.class));
+ }
+
+ @Test
+ void recycleAllocatedBalanceThrowsWhenLockBusy() throws InterruptedException {
+ when(lock.tryLock(5L, 30L, TimeUnit.SECONDS)).thenReturn(false);
+
+ assertThrows(ServiceException.class,
+ () -> service.recycleAllocatedBalance(9, "admin", "测试公司", new BigDecimal("100.00")));
+ verify(vinMapper, never()).selectList(any());
+ }
+
+ @Test
+ void recycleAllocatedBalanceRejectsMissingCompany() {
+ assertThrows(ServiceException.class, () -> service.recycleAllocatedBalance(null, "admin", null, null));
+ verify(mapper, never()).insert(any(TCompanyAllocateRecord.class));
+ }
+
private String repeat(char value, int count) {
StringBuilder builder = new StringBuilder(count);
for (int i = 0; i < count; i++) {
--
Gitblit v1.7.1