package com.ycwl.basic.pricing.service; import com.ycwl.basic.pricing.dto.VoucherInfo; import com.ycwl.basic.pricing.entity.PriceVoucherBatchConfig; import com.ycwl.basic.pricing.entity.PriceVoucherCode; import com.ycwl.basic.pricing.entity.PriceVoucherUsageRecord; import com.ycwl.basic.pricing.enums.VoucherCodeStatus; import com.ycwl.basic.pricing.enums.VoucherDiscountType; import com.ycwl.basic.pricing.mapper.PriceVoucherBatchConfigMapper; import com.ycwl.basic.pricing.mapper.PriceVoucherCodeMapper; import com.ycwl.basic.pricing.mapper.PriceVoucherUsageRecordMapper; import com.ycwl.basic.pricing.service.impl.VoucherServiceImpl; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.math.BigDecimal; import java.util.Date; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; /** * 可重复使用券码服务测试 */ public class ReusableVoucherServiceTest { @Mock private PriceVoucherCodeMapper voucherCodeMapper; @Mock private PriceVoucherBatchConfigMapper voucherBatchConfigMapper; @Mock private PriceVoucherUsageRecordMapper usageRecordMapper; @InjectMocks private VoucherServiceImpl voucherService; private PriceVoucherCode testVoucherCode; private PriceVoucherBatchConfig testBatchConfig; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); // 创建测试数据 testVoucherCode = new PriceVoucherCode(); testVoucherCode.setId(1L); testVoucherCode.setCode("TEST123"); testVoucherCode.setBatchId(1L); testVoucherCode.setScenicId(1L); testVoucherCode.setFaceId(1001L); testVoucherCode.setStatus(VoucherCodeStatus.CLAIMED_AVAILABLE.getCode()); testVoucherCode.setCurrentUseCount(1); testVoucherCode.setLastUsedTime(new Date()); testVoucherCode.setDeleted(0); testBatchConfig = new PriceVoucherBatchConfig(); testBatchConfig.setId(1L); testBatchConfig.setBatchName("测试批次"); testBatchConfig.setDiscountType(VoucherDiscountType.REDUCE_PRICE.getCode()); testBatchConfig.setDiscountValue(new BigDecimal("10.00")); testBatchConfig.setMaxUseCount(3); // 可使用3次 testBatchConfig.setMaxUsePerUser(2); // 每用户最多2次 testBatchConfig.setUseIntervalHours(24); // 间隔24小时 testBatchConfig.setStatus(1); testBatchConfig.setDeleted(0); } @Test void testValidateReusableVoucherCode_Success() { // Given when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); when(usageRecordMapper.countByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(1); // 用户已使用1次 // 模拟距离上次使用已超过24小时 Date lastUseTime = new Date(System.currentTimeMillis() - 25 * 60 * 60 * 1000); // 25小时前 when(usageRecordMapper.getLastUseTimeByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(lastUseTime); // When VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L); // Then assertNotNull(result); assertEquals("TEST123", result.getVoucherCode()); assertTrue(result.getAvailable()); assertEquals(Integer.valueOf(1), result.getCurrentUseCount()); assertEquals(Integer.valueOf(3), result.getMaxUseCount()); assertEquals(Integer.valueOf(2), result.getRemainingUseCount()); } @Test void testValidateVoucherCode_ReachedMaxUseCount() { // Given - 券码已达到最大使用次数 testVoucherCode.setCurrentUseCount(3); testVoucherCode.setStatus(VoucherCodeStatus.CLAIMED_EXHAUSTED.getCode()); when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); // When VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L); // Then assertNotNull(result); assertFalse(result.getAvailable()); assertEquals("券码已用完", result.getUnavailableReason()); } @Test void testValidateVoucherCode_UserReachedMaxUsePerUser() { // Given - 用户已达到个人使用上限 when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); when(usageRecordMapper.countByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(2); // 用户已使用2次 // When VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L); // Then assertNotNull(result); assertFalse(result.getAvailable()); assertEquals("您使用该券码的次数已达上限", result.getUnavailableReason()); } @Test void testValidateVoucherCode_WithinInterval() { // Given - 距离上次使用不足24小时 when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); when(usageRecordMapper.countByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(1); // 模拟距离上次使用仅10小时 Date lastUseTime = new Date(System.currentTimeMillis() - 10 * 60 * 60 * 1000); when(usageRecordMapper.getLastUseTimeByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(lastUseTime); // When VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L); // Then assertNotNull(result); assertFalse(result.getAvailable()); assertTrue(result.getUnavailableReason().contains("请等待")); assertTrue(result.getUnavailableReason().contains("小时后再次使用")); } @Test void testMarkVoucherAsUsed_UpdateCountAndStatus() { // Given when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); // When voucherService.markVoucherAsUsed("TEST123", "测试使用", "ORDER001", new BigDecimal("10.00"), 1L); // Then verify(usageRecordMapper, times(1)).insert(any(PriceVoucherUsageRecord.class)); verify(voucherCodeMapper, times(1)).updateById(any(PriceVoucherCode.class)); verify(voucherBatchConfigMapper, times(1)).updateUsedCount(eq(1L), eq(1)); } @Test void testMarkVoucherAsUsed_ReachMaxUseCount() { // Given - 使用后将达到最大使用次数 testVoucherCode.setCurrentUseCount(2); // 当前已使用2次,再使用1次将达到上限3次 when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); // When voucherService.markVoucherAsUsed("TEST123", "测试使用", "ORDER001", new BigDecimal("10.00"), 1L); // Then verify(usageRecordMapper, times(1)).insert(any(PriceVoucherUsageRecord.class)); verify(voucherCodeMapper, times(1)).updateById(argThat(voucherCode -> voucherCode.getCurrentUseCount() == 3 && voucherCode.getStatus().equals(VoucherCodeStatus.CLAIMED_EXHAUSTED.getCode()) )); verify(voucherBatchConfigMapper, times(1)).updateUsedCount(eq(1L), eq(1)); } @Test void testMarkVoucherAsUsed_SingleUseCompatibility() { // Given - 测试单次使用兼容性(maxUseCount = 1) testBatchConfig.setMaxUseCount(1); testVoucherCode.setCurrentUseCount(0); when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode); when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig); // When voucherService.markVoucherAsUsed("TEST123", "测试使用", "ORDER001", new BigDecimal("10.00"), 1L); // Then - 应该设置为USED状态以兼容原有逻辑 verify(voucherCodeMapper, times(1)).updateById(argThat(voucherCode -> voucherCode.getCurrentUseCount() == 1 && voucherCode.getStatus().equals(VoucherCodeStatus.USED.getCode()) && voucherCode.getUsedTime() != null )); } }