puzhibing
4 天以前 3650ce7f5d42d1bef58abea9693f20d1f209d3b0
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++) {