Commit fdfca502e5859011cf8bf41d8dad3665de069f6d
1 parent
f93e2579
test: 完善 PR 修复回归测试并启用商城历史用例
启用并修复商品、DIY 页面及模板、交易订单测试,补齐测试表结构与依赖。 增加 CRM 删除日志上下文、优惠券模板已删除场景的回归测试,并对齐相关注释。 关联 #1313、#1315、#1317、#1319、#1321、#1323。
Showing
15 changed files
with
913 additions
and
546 deletions
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java
| @@ -118,6 +118,7 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService | @@ -118,6 +118,7 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService | ||
| 118 | if (crmProductService.getProductByCategoryId(id) > 0) { | 118 | if (crmProductService.getProductByCategoryId(id) > 0) { |
| 119 | throw exception(PRODUCT_CATEGORY_USED); | 119 | throw exception(PRODUCT_CATEGORY_USED); |
| 120 | } | 120 | } |
| 121 | + | ||
| 121 | // 2. 删除 | 122 | // 2. 删除 |
| 122 | productCategoryMapper.deleteById(id); | 123 | productCategoryMapper.deleteById(id); |
| 123 | 124 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java
| @@ -135,9 +135,9 @@ public class CrmProductServiceImpl implements CrmProductService { | @@ -135,9 +135,9 @@ public class CrmProductServiceImpl implements CrmProductService { | ||
| 135 | success = CRM_PRODUCT_DELETE_SUCCESS) | 135 | success = CRM_PRODUCT_DELETE_SUCCESS) |
| 136 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) | 136 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) |
| 137 | public void deleteProduct(Long id) { | 137 | public void deleteProduct(Long id) { |
| 138 | - // 校验存在 | 138 | + // 1.1 校验存在 |
| 139 | CrmProductDO product = validateProductExists(id); | 139 | CrmProductDO product = validateProductExists(id); |
| 140 | - // 校验未被合同/商机引用,避免删除后产生悬空引用 | 140 | + // 1.2 校验未被合同/商机引用,避免删除后产生悬空引用 |
| 141 | if (contractService.getContractProductCountByProductId(id) > 0) { | 141 | if (contractService.getContractProductCountByProductId(id) > 0) { |
| 142 | throw exception(PRODUCT_DELETE_FAIL_CONTRACT_EXISTS); | 142 | throw exception(PRODUCT_DELETE_FAIL_CONTRACT_EXISTS); |
| 143 | } | 143 | } |
| @@ -145,10 +145,10 @@ public class CrmProductServiceImpl implements CrmProductService { | @@ -145,10 +145,10 @@ public class CrmProductServiceImpl implements CrmProductService { | ||
| 145 | throw exception(PRODUCT_DELETE_FAIL_BUSINESS_EXISTS); | 145 | throw exception(PRODUCT_DELETE_FAIL_BUSINESS_EXISTS); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | - // 删除 | 148 | + // 2. 删除 |
| 149 | productMapper.deleteById(id); | 149 | productMapper.deleteById(id); |
| 150 | 150 | ||
| 151 | - // 记录操作日志上下文 | 151 | + // 3. 记录操作日志上下文 |
| 152 | LogRecordContext.putVariable("product", product); | 152 | LogRecordContext.putVariable("product", product); |
| 153 | } | 153 | } |
| 154 | 154 |
yudao-module-crm/src/test/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.product; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; | ||
| 5 | +import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductCategoryMapper; | ||
| 6 | +import com.mzt.logapi.context.LogRecordContext; | ||
| 7 | +import org.junit.jupiter.api.AfterEach; | ||
| 8 | +import org.junit.jupiter.api.BeforeEach; | ||
| 9 | +import org.junit.jupiter.api.Test; | ||
| 10 | +import org.mockito.InjectMocks; | ||
| 11 | +import org.mockito.Mock; | ||
| 12 | + | ||
| 13 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||
| 14 | +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; | ||
| 15 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 16 | +import static org.mockito.ArgumentMatchers.anyLong; | ||
| 17 | +import static org.mockito.Mockito.*; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * {@link CrmProductCategoryServiceImpl} 的单元测试类 | ||
| 21 | + * | ||
| 22 | + * @author 芋道源码 | ||
| 23 | + */ | ||
| 24 | +public class CrmProductCategoryServiceImplTest extends BaseMockitoUnitTest { | ||
| 25 | + | ||
| 26 | + @InjectMocks | ||
| 27 | + private CrmProductCategoryServiceImpl productCategoryService; | ||
| 28 | + | ||
| 29 | + @Mock | ||
| 30 | + private CrmProductCategoryMapper productCategoryMapper; | ||
| 31 | + @Mock | ||
| 32 | + private CrmProductService crmProductService; | ||
| 33 | + | ||
| 34 | + @BeforeEach | ||
| 35 | + public void setUp() { | ||
| 36 | + LogRecordContext.putEmptySpan(); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + @AfterEach | ||
| 40 | + public void tearDown() { | ||
| 41 | + LogRecordContext.clear(); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Test | ||
| 45 | + public void testDeleteProductCategory_success() { | ||
| 46 | + // mock 数据 | ||
| 47 | + CrmProductCategoryDO category = new CrmProductCategoryDO().setId(1L).setName("芋道分类").setParentId(0L); | ||
| 48 | + when(productCategoryMapper.selectById(1L)).thenReturn(category); | ||
| 49 | + // mock 方法(无子分类、未被产品使用) | ||
| 50 | + when(productCategoryMapper.selectCountByParentId(1L)).thenReturn(0L); | ||
| 51 | + when(crmProductService.getProductByCategoryId(1L)).thenReturn(0L); | ||
| 52 | + | ||
| 53 | + // 调用 | ||
| 54 | + productCategoryService.deleteProductCategory(1L); | ||
| 55 | + // 断言 | ||
| 56 | + verify(productCategoryMapper).deleteById(1L); | ||
| 57 | + // 断言操作日志上下文,为删除前的分类 | ||
| 58 | + CrmProductCategoryDO logCategory = (CrmProductCategoryDO) LogRecordContext.getVariable("productCategory"); | ||
| 59 | + assertSame(category, logCategory); | ||
| 60 | + assertEquals("芋道分类", logCategory.getName()); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Test | ||
| 64 | + public void testDeleteProductCategory_notExists() { | ||
| 65 | + // 调用,并断言异常 | ||
| 66 | + assertServiceException(() -> productCategoryService.deleteProductCategory(1L), PRODUCT_CATEGORY_NOT_EXISTS); | ||
| 67 | + // 断言 | ||
| 68 | + verify(productCategoryMapper, never()).deleteById(anyLong()); | ||
| 69 | + assertNull(LogRecordContext.getVariable("productCategory")); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Test | ||
| 73 | + public void testDeleteProductCategory_existsChildren() { | ||
| 74 | + // mock 数据 | ||
| 75 | + when(productCategoryMapper.selectById(1L)).thenReturn(new CrmProductCategoryDO().setId(1L).setName("芋道分类")); | ||
| 76 | + // mock 方法(存在子分类) | ||
| 77 | + when(productCategoryMapper.selectCountByParentId(1L)).thenReturn(1L); | ||
| 78 | + | ||
| 79 | + // 调用,并断言异常 | ||
| 80 | + assertServiceException(() -> productCategoryService.deleteProductCategory(1L), PRODUCT_CATEGORY_EXISTS_CHILDREN); | ||
| 81 | + // 断言 | ||
| 82 | + verify(productCategoryMapper, never()).deleteById(anyLong()); | ||
| 83 | + verifyNoInteractions(crmProductService); | ||
| 84 | + assertNull(LogRecordContext.getVariable("productCategory")); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + @Test | ||
| 88 | + public void testDeleteProductCategory_used() { | ||
| 89 | + // mock 数据 | ||
| 90 | + when(productCategoryMapper.selectById(1L)).thenReturn(new CrmProductCategoryDO().setId(1L).setName("芋道分类")); | ||
| 91 | + // mock 方法(被产品使用) | ||
| 92 | + when(productCategoryMapper.selectCountByParentId(1L)).thenReturn(0L); | ||
| 93 | + when(crmProductService.getProductByCategoryId(1L)).thenReturn(1L); | ||
| 94 | + | ||
| 95 | + // 调用,并断言异常 | ||
| 96 | + assertServiceException(() -> productCategoryService.deleteProductCategory(1L), PRODUCT_CATEGORY_USED); | ||
| 97 | + // 断言 | ||
| 98 | + verify(productCategoryMapper, never()).deleteById(anyLong()); | ||
| 99 | + assertNull(LogRecordContext.getVariable("productCategory")); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | +} |
yudao-module-crm/src/test/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.product; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; | ||
| 5 | +import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductMapper; | ||
| 6 | +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; | ||
| 7 | +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; | ||
| 8 | +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; | ||
| 9 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | ||
| 10 | +import com.mzt.logapi.context.LogRecordContext; | ||
| 11 | +import org.junit.jupiter.api.AfterEach; | ||
| 12 | +import org.junit.jupiter.api.BeforeEach; | ||
| 13 | +import org.junit.jupiter.api.Test; | ||
| 14 | +import org.mockito.InjectMocks; | ||
| 15 | +import org.mockito.Mock; | ||
| 16 | + | ||
| 17 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||
| 18 | +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; | ||
| 19 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 20 | +import static org.mockito.ArgumentMatchers.anyLong; | ||
| 21 | +import static org.mockito.Mockito.*; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * {@link CrmProductServiceImpl} 的单元测试类 | ||
| 25 | + * | ||
| 26 | + * @author 芋道源码 | ||
| 27 | + */ | ||
| 28 | +public class CrmProductServiceImplTest extends BaseMockitoUnitTest { | ||
| 29 | + | ||
| 30 | + @InjectMocks | ||
| 31 | + private CrmProductServiceImpl productService; | ||
| 32 | + | ||
| 33 | + @Mock | ||
| 34 | + private CrmProductMapper productMapper; | ||
| 35 | + @Mock | ||
| 36 | + private CrmProductCategoryService productCategoryService; | ||
| 37 | + @Mock | ||
| 38 | + private CrmContractService contractService; | ||
| 39 | + @Mock | ||
| 40 | + private CrmBusinessService businessService; | ||
| 41 | + @Mock | ||
| 42 | + private CrmPermissionService permissionService; | ||
| 43 | + @Mock | ||
| 44 | + private AdminUserApi adminUserApi; | ||
| 45 | + | ||
| 46 | + @BeforeEach | ||
| 47 | + public void setUp() { | ||
| 48 | + LogRecordContext.putEmptySpan(); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + @AfterEach | ||
| 52 | + public void tearDown() { | ||
| 53 | + LogRecordContext.clear(); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Test | ||
| 57 | + public void testDeleteProduct_success() { | ||
| 58 | + // mock 数据 | ||
| 59 | + CrmProductDO product = new CrmProductDO().setId(1L).setName("芋道产品"); | ||
| 60 | + when(productMapper.selectById(1L)).thenReturn(product); | ||
| 61 | + // mock 方法(未被合同、商机引用) | ||
| 62 | + when(contractService.getContractProductCountByProductId(1L)).thenReturn(0L); | ||
| 63 | + when(businessService.getBusinessProductCountByProductId(1L)).thenReturn(0L); | ||
| 64 | + | ||
| 65 | + // 调用 | ||
| 66 | + productService.deleteProduct(1L); | ||
| 67 | + // 断言 | ||
| 68 | + verify(productMapper).deleteById(1L); | ||
| 69 | + // 断言操作日志上下文,为删除前的产品 | ||
| 70 | + CrmProductDO logProduct = (CrmProductDO) LogRecordContext.getVariable("product"); | ||
| 71 | + assertSame(product, logProduct); | ||
| 72 | + assertEquals("芋道产品", logProduct.getName()); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + @Test | ||
| 76 | + public void testDeleteProduct_notExists() { | ||
| 77 | + // 调用,并断言异常 | ||
| 78 | + assertServiceException(() -> productService.deleteProduct(1L), PRODUCT_NOT_EXISTS); | ||
| 79 | + // 断言 | ||
| 80 | + verify(productMapper, never()).deleteById(anyLong()); | ||
| 81 | + assertNull(LogRecordContext.getVariable("product")); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Test | ||
| 85 | + public void testDeleteProduct_contractExists() { | ||
| 86 | + // mock 数据 | ||
| 87 | + when(productMapper.selectById(1L)).thenReturn(new CrmProductDO().setId(1L).setName("芋道产品")); | ||
| 88 | + // mock 方法(被合同引用) | ||
| 89 | + when(contractService.getContractProductCountByProductId(1L)).thenReturn(1L); | ||
| 90 | + | ||
| 91 | + // 调用,并断言异常 | ||
| 92 | + assertServiceException(() -> productService.deleteProduct(1L), PRODUCT_DELETE_FAIL_CONTRACT_EXISTS); | ||
| 93 | + // 断言 | ||
| 94 | + verify(productMapper, never()).deleteById(anyLong()); | ||
| 95 | + assertNull(LogRecordContext.getVariable("product")); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + @Test | ||
| 99 | + public void testDeleteProduct_businessExists() { | ||
| 100 | + // mock 数据 | ||
| 101 | + when(productMapper.selectById(1L)).thenReturn(new CrmProductDO().setId(1L).setName("芋道产品")); | ||
| 102 | + // mock 方法(被商机引用) | ||
| 103 | + when(contractService.getContractProductCountByProductId(1L)).thenReturn(0L); | ||
| 104 | + when(businessService.getBusinessProductCountByProductId(1L)).thenReturn(1L); | ||
| 105 | + | ||
| 106 | + // 调用,并断言异常 | ||
| 107 | + assertServiceException(() -> productService.deleteProduct(1L), PRODUCT_DELETE_FAIL_BUSINESS_EXISTS); | ||
| 108 | + // 断言 | ||
| 109 | + verify(productMapper, never()).deleteById(anyLong()); | ||
| 110 | + assertNull(LogRecordContext.getVariable("product")); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | +} |
yudao-module-mall/yudao-module-product/pom.xml
| @@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
| 24 | <version>${revision}</version> | 24 | <version>${revision}</version> |
| 25 | </dependency> | 25 | </dependency> |
| 26 | 26 | ||
| 27 | - <!-- trade-api:配送方式枚举 DeliveryTypeEnum 校验(A68) --> | 27 | + <!-- trade-api:配送方式枚举 DeliveryTypeEnum 校验 --> |
| 28 | <dependency> | 28 | <dependency> |
| 29 | <groupId>cn.iocoder.boot</groupId> | 29 | <groupId>cn.iocoder.boot</groupId> |
| 30 | <artifactId>yudao-module-trade-api</artifactId> | 30 | <artifactId>yudao-module-trade-api</artifactId> |
yudao-module-mall/yudao-module-product/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
| @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.product.service.spu; | @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.product.service.spu; | ||
| 3 | import cn.hutool.core.date.DateUtil; | 3 | import cn.hutool.core.date.DateUtil; |
| 4 | import cn.hutool.core.map.MapUtil; | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | import cn.hutool.core.util.RandomUtil; | 5 | import cn.hutool.core.util.RandomUtil; |
| 6 | -import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||
| 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | ||
| 8 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 8 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 9 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO; | 9 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO; |
| 10 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; | 10 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; |
| @@ -17,29 +17,37 @@ import cn.iocoder.yudao.module.product.service.category.ProductCategoryServiceIm | @@ -17,29 +17,37 @@ import cn.iocoder.yudao.module.product.service.category.ProductCategoryServiceIm | ||
| 17 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; | 17 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; |
| 18 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; | 18 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; |
| 19 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl; | 19 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl; |
| 20 | +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; | ||
| 20 | import com.google.common.collect.Lists; | 21 | import com.google.common.collect.Lists; |
| 21 | import jakarta.annotation.Resource; | 22 | import jakarta.annotation.Resource; |
| 22 | -import org.junit.jupiter.api.Assertions; | ||
| 23 | -import org.junit.jupiter.api.Disabled; | 23 | +import jakarta.validation.ConstraintViolation; |
| 24 | +import jakarta.validation.Validation; | ||
| 25 | +import jakarta.validation.Validator; | ||
| 24 | import org.junit.jupiter.api.Test; | 26 | import org.junit.jupiter.api.Test; |
| 25 | import org.springframework.context.annotation.Import; | 27 | import org.springframework.context.annotation.Import; |
| 26 | import org.springframework.test.context.bean.override.mockito.MockitoBean; | 28 | import org.springframework.test.context.bean.override.mockito.MockitoBean; |
| 27 | 29 | ||
| 28 | import java.math.RoundingMode; | 30 | import java.math.RoundingMode; |
| 29 | -import java.util.ArrayList; | ||
| 30 | -import java.util.Date; | ||
| 31 | -import java.util.List; | ||
| 32 | -import java.util.Map; | 31 | +import java.util.*; |
| 32 | +import java.util.function.Consumer; | ||
| 33 | import java.util.stream.Collectors; | 33 | import java.util.stream.Collectors; |
| 34 | import java.util.stream.Stream; | 34 | import java.util.stream.Stream; |
| 35 | 35 | ||
| 36 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 37 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 38 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||
| 39 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||
| 36 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | 40 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 37 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 41 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 42 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||
| 38 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 43 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 44 | +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; | ||
| 45 | +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_RECYCLE; | ||
| 46 | +import static java.util.Collections.singletonList; | ||
| 39 | import static org.assertj.core.util.Lists.newArrayList; | 47 | import static org.assertj.core.util.Lists.newArrayList; |
| 40 | -import static org.junit.jupiter.api.Assertions.assertEquals; | 48 | +import static org.junit.jupiter.api.Assertions.*; |
| 41 | import static org.mockito.ArgumentMatchers.eq; | 49 | import static org.mockito.ArgumentMatchers.eq; |
| 42 | -import static org.mockito.Mockito.when; | 50 | +import static org.mockito.Mockito.*; |
| 43 | 51 | ||
| 44 | // TODO @芋艿:review 下单元测试 | 52 | // TODO @芋艿:review 下单元测试 |
| 45 | 53 | ||
| @@ -48,10 +56,12 @@ import static org.mockito.Mockito.when; | @@ -48,10 +56,12 @@ import static org.mockito.Mockito.when; | ||
| 48 | * | 56 | * |
| 49 | * @author 芋道源码 | 57 | * @author 芋道源码 |
| 50 | */ | 58 | */ |
| 51 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | 59 | +// DONE 芋艿:后续 fix 补充的单测 |
| 52 | @Import(ProductSpuServiceImpl.class) | 60 | @Import(ProductSpuServiceImpl.class) |
| 53 | public class ProductSpuServiceImplTest extends BaseDbUnitTest { | 61 | public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 54 | 62 | ||
| 63 | + private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); | ||
| 64 | + | ||
| 55 | @Resource | 65 | @Resource |
| 56 | private ProductSpuServiceImpl productSpuService; | 66 | private ProductSpuServiceImpl productSpuService; |
| 57 | 67 | ||
| @@ -84,18 +94,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | @@ -84,18 +94,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | ||
| 84 | @Test | 94 | @Test |
| 85 | public void testCreateSpu_success() { | 95 | public void testCreateSpu_success() { |
| 86 | // 准备参数 | 96 | // 准备参数 |
| 87 | - ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomPojo(ProductSkuSaveReqVO.class, o->{ | ||
| 88 | - // 限制范围为正整数 | ||
| 89 | - o.setCostPrice(generaInt()); | ||
| 90 | - o.setPrice(generaInt()); | ||
| 91 | - o.setMarketPrice(generaInt()); | ||
| 92 | - o.setStock(generaInt()); | ||
| 93 | - o.setFirstBrokeragePrice(generaInt()); | ||
| 94 | - o.setSecondBrokeragePrice(generaInt()); | ||
| 95 | - // 限制分数为两位数 | ||
| 96 | - o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | ||
| 97 | - o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | ||
| 98 | - }); | 97 | + ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomSkuSaveReqVO(); |
| 99 | ProductSpuSaveReqVO createReqVO = randomPojo(ProductSpuSaveReqVO.class,o->{ | 98 | ProductSpuSaveReqVO createReqVO = randomPojo(ProductSpuSaveReqVO.class,o->{ |
| 100 | o.setCategoryId(generateId()); | 99 | o.setCategoryId(generateId()); |
| 101 | o.setBrandId(generateId()); | 100 | o.setBrandId(generateId()); |
| @@ -104,291 +103,215 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | @@ -104,291 +103,215 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | ||
| 104 | o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | 103 | o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 |
| 105 | o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO)); | 104 | o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO)); |
| 106 | }); | 105 | }); |
| 106 | + // mock 方法(分类层级) | ||
| 107 | when(categoryService.getCategoryLevel(eq(createReqVO.getCategoryId()))).thenReturn(2); | 107 | when(categoryService.getCategoryLevel(eq(createReqVO.getCategoryId()))).thenReturn(2); |
| 108 | - Long spu = productSpuService.createSpu(createReqVO); | ||
| 109 | - ProductSpuDO productSpuDO = productSpuMapper.selectById(spu); | ||
| 110 | - assertPojoEquals(createReqVO, productSpuDO); | 108 | + |
| 109 | + // 调用 | ||
| 110 | + Long spuId = productSpuService.createSpu(createReqVO); | ||
| 111 | + // 断言:销量、浏览量为初始值 | ||
| 112 | + ProductSpuDO productSpuDO = productSpuMapper.selectById(spuId); | ||
| 113 | + assertPojoEquals(createReqVO, productSpuDO, "salesCount", "browseCount"); | ||
| 114 | + assertEquals(ProductSpuStatusEnum.ENABLE.getStatus(), productSpuDO.getStatus()); | ||
| 115 | + assertEquals(0, productSpuDO.getSalesCount()); | ||
| 116 | + assertEquals(0, productSpuDO.getBrowseCount()); | ||
| 117 | + // 断言:基于 SKU 初始化价格、库存 | ||
| 118 | + assertEquals(skuCreateOrUpdateReqVO.getPrice(), productSpuDO.getPrice()); | ||
| 119 | + assertEquals(skuCreateOrUpdateReqVO.getMarketPrice(), productSpuDO.getMarketPrice()); | ||
| 120 | + assertEquals(skuCreateOrUpdateReqVO.getCostPrice(), productSpuDO.getCostPrice()); | ||
| 121 | + assertEquals(skuCreateOrUpdateReqVO.getStock() * 3, productSpuDO.getStock()); | ||
| 122 | + // 断言:创建 SKU | ||
| 123 | + verify(productSkuService).createSkuList(eq(spuId), eq(createReqVO.getSkus())); | ||
| 111 | } | 124 | } |
| 112 | 125 | ||
| 113 | @Test | 126 | @Test |
| 114 | public void testUpdateSpu_success() { | 127 | public void testUpdateSpu_success() { |
| 128 | + // mock 数据 | ||
| 129 | + ProductSpuDO dbSpu = randomSpuDO(); | ||
| 130 | + productSpuMapper.insert(dbSpu); | ||
| 115 | // 准备参数 | 131 | // 准备参数 |
| 116 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | ||
| 117 | - o.setCategoryId(generateId()); | ||
| 118 | - o.setBrandId(generateId()); | ||
| 119 | - o.setDeliveryTemplateId(generateId()); | ||
| 120 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 121 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 122 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 123 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 124 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 125 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 126 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 127 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 128 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 129 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 130 | - }); | ||
| 131 | - productSpuMapper.insert(createReqVO); | ||
| 132 | - // 准备参数 | ||
| 133 | - ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomPojo(ProductSkuSaveReqVO.class, o->{ | ||
| 134 | - // 限制范围为正整数 | ||
| 135 | - o.setCostPrice(generaInt()); | ||
| 136 | - o.setPrice(generaInt()); | ||
| 137 | - o.setMarketPrice(generaInt()); | ||
| 138 | - o.setStock(generaInt()); | ||
| 139 | - o.setFirstBrokeragePrice(generaInt()); | ||
| 140 | - o.setSecondBrokeragePrice(generaInt()); | ||
| 141 | - // 限制分数为两位数 | ||
| 142 | - o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | ||
| 143 | - o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | ||
| 144 | - }); | ||
| 145 | - // 准备参数 | 132 | + ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomSkuSaveReqVO(); |
| 146 | ProductSpuSaveReqVO reqVO = randomPojo(ProductSpuSaveReqVO.class, o -> { | 133 | ProductSpuSaveReqVO reqVO = randomPojo(ProductSpuSaveReqVO.class, o -> { |
| 147 | - o.setId(createReqVO.getId()); // 设置更新的 ID | 134 | + o.setId(dbSpu.getId()); // 设置更新的 ID |
| 148 | o.setCategoryId(generateId()); | 135 | o.setCategoryId(generateId()); |
| 149 | o.setBrandId(generateId()); | 136 | o.setBrandId(generateId()); |
| 150 | o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | 137 | o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 |
| 151 | o.setGiveIntegral(generaInt()); // 限制范围为正整数 | 138 | o.setGiveIntegral(generaInt()); // 限制范围为正整数 |
| 152 | o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | 139 | o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 |
| 153 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 154 | o.setSalesCount(generaInt()); // 限制范围为正整数 | 140 | o.setSalesCount(generaInt()); // 限制范围为正整数 |
| 155 | o.setBrowseCount(generaInt()); // 限制范围为正整数 | 141 | o.setBrowseCount(generaInt()); // 限制范围为正整数 |
| 156 | o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO)); | 142 | o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO)); |
| 157 | }); | 143 | }); |
| 144 | + // mock 方法(分类层级) | ||
| 158 | when(categoryService.getCategoryLevel(eq(reqVO.getCategoryId()))).thenReturn(2); | 145 | when(categoryService.getCategoryLevel(eq(reqVO.getCategoryId()))).thenReturn(2); |
| 146 | + | ||
| 159 | // 调用 | 147 | // 调用 |
| 160 | productSpuService.updateSpu(reqVO); | 148 | productSpuService.updateSpu(reqVO); |
| 161 | - // 校验是否更新正确 | 149 | + // 校验是否更新正确:状态保持不变 |
| 162 | ProductSpuDO spu = productSpuMapper.selectById(reqVO.getId()); // 获取最新的 | 150 | ProductSpuDO spu = productSpuMapper.selectById(reqVO.getId()); // 获取最新的 |
| 163 | assertPojoEquals(reqVO, spu); | 151 | assertPojoEquals(reqVO, spu); |
| 152 | + assertEquals(dbSpu.getStatus(), spu.getStatus()); | ||
| 153 | + assertEquals(skuCreateOrUpdateReqVO.getStock() * 3, spu.getStock()); | ||
| 154 | + // 断言:更新 SKU | ||
| 155 | + verify(productSkuService).updateSkuList(eq(reqVO.getId()), eq(reqVO.getSkus())); | ||
| 164 | } | 156 | } |
| 165 | 157 | ||
| 166 | @Test | 158 | @Test |
| 167 | - public void testValidateSpuExists_exception() { | 159 | + public void testUpdateSpu_notExists() { |
| 160 | + // 准备参数 | ||
| 168 | ProductSpuSaveReqVO reqVO = randomPojo(ProductSpuSaveReqVO.class); | 161 | ProductSpuSaveReqVO reqVO = randomPojo(ProductSpuSaveReqVO.class); |
| 169 | - // 调用 | ||
| 170 | - Assertions.assertThrows(ServiceException.class, () -> productSpuService.updateSpu(reqVO)); | 162 | + |
| 163 | + // 调用, 并断言异常 | ||
| 164 | + assertServiceException(() -> productSpuService.updateSpu(reqVO), SPU_NOT_EXISTS); | ||
| 171 | } | 165 | } |
| 172 | 166 | ||
| 173 | @Test | 167 | @Test |
| 174 | - void deleteSpu() { | ||
| 175 | - // 准备参数 | ||
| 176 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | ||
| 177 | - o.setCategoryId(generateId()); | ||
| 178 | - o.setBrandId(generateId()); | ||
| 179 | - o.setDeliveryTemplateId(generateId()); | ||
| 180 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 181 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 182 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 183 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 184 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 185 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 186 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 187 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 188 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 189 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 190 | - o.setStatus(-1); // 加入回收站才可删除 | ||
| 191 | - }); | ||
| 192 | - productSpuMapper.insert(createReqVO); | 168 | + public void testValidateDeliveryTypes_success() { |
| 169 | + // 调用,并断言:快递发货、用户自提,及其组合 | ||
| 170 | + assertEquals(Collections.emptySet(), validateDeliveryTypes(singletonList(DeliveryTypeEnum.EXPRESS.getType()))); | ||
| 171 | + assertEquals(Collections.emptySet(), validateDeliveryTypes(singletonList(DeliveryTypeEnum.PICK_UP.getType()))); | ||
| 172 | + assertEquals(Collections.emptySet(), validateDeliveryTypes(Arrays.asList( | ||
| 173 | + DeliveryTypeEnum.EXPRESS.getType(), DeliveryTypeEnum.PICK_UP.getType()))); | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + @Test | ||
| 177 | + public void testValidateDeliveryTypes_invalid() { | ||
| 178 | + // 调用,并断言:不在枚举范围内 | ||
| 179 | + Set<String> invalidMessages = Collections.singleton("配送方式不正确"); | ||
| 180 | + assertEquals(invalidMessages, validateDeliveryTypes(singletonList(0))); | ||
| 181 | + assertEquals(invalidMessages, validateDeliveryTypes(singletonList(3))); | ||
| 182 | + assertEquals(invalidMessages, validateDeliveryTypes(singletonList(-1))); | ||
| 183 | + assertEquals(invalidMessages, validateDeliveryTypes(Arrays.asList(DeliveryTypeEnum.EXPRESS.getType(), 3))); | ||
| 184 | + assertEquals(invalidMessages, validateDeliveryTypes(Arrays.asList(DeliveryTypeEnum.EXPRESS.getType(), null))); | ||
| 185 | + // 调用,并断言:为空 | ||
| 186 | + Set<String> emptyMessages = Collections.singleton("配送方式不能为空"); | ||
| 187 | + assertEquals(emptyMessages, validateDeliveryTypes(null)); | ||
| 188 | + assertEquals(emptyMessages, validateDeliveryTypes(Collections.emptyList())); | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + @Test | ||
| 192 | + public void testDeleteSpu_success() { | ||
| 193 | + // mock 数据 | ||
| 194 | + ProductSpuDO dbSpu = randomSpuDO(o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus())); // 加入回收站才可删除 | ||
| 195 | + productSpuMapper.insert(dbSpu); | ||
| 193 | 196 | ||
| 194 | // 调用 | 197 | // 调用 |
| 195 | - productSpuService.deleteSpu(createReqVO.getId()); | 198 | + productSpuService.deleteSpu(dbSpu.getId()); |
| 199 | + // 断言 | ||
| 200 | + assertNull(productSpuMapper.selectById(dbSpu.getId())); | ||
| 201 | + verify(productSkuService).deleteSkuBySpuId(eq(dbSpu.getId())); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + @Test | ||
| 205 | + public void testDeleteSpu_notRecycle() { | ||
| 206 | + // mock 数据 | ||
| 207 | + ProductSpuDO dbSpu = randomSpuDO(o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus())); | ||
| 208 | + productSpuMapper.insert(dbSpu); | ||
| 196 | 209 | ||
| 197 | - Assertions.assertNull(productSpuMapper.selectById(createReqVO.getId())); | 210 | + // 调用, 并断言异常 |
| 211 | + assertServiceException(() -> productSpuService.deleteSpu(dbSpu.getId()), SPU_NOT_RECYCLE); | ||
| 212 | + // 断言 | ||
| 213 | + assertNotNull(productSpuMapper.selectById(dbSpu.getId())); | ||
| 214 | + verify(productSkuService, never()).deleteSkuBySpuId(any()); | ||
| 198 | } | 215 | } |
| 199 | 216 | ||
| 200 | @Test | 217 | @Test |
| 201 | - void getSpu() { | ||
| 202 | - // 准备参数 | ||
| 203 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | ||
| 204 | - o.setCategoryId(generateId()); | ||
| 205 | - o.setBrandId(generateId()); | ||
| 206 | - o.setDeliveryTemplateId(generateId()); | ||
| 207 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 208 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 209 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 210 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 211 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 212 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 213 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 214 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 215 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 216 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 217 | - }); | ||
| 218 | - productSpuMapper.insert(createReqVO); | 218 | + public void testGetSpu() { |
| 219 | + // mock 数据 | ||
| 220 | + ProductSpuDO dbSpu = randomSpuDO(); | ||
| 221 | + productSpuMapper.insert(dbSpu); | ||
| 219 | 222 | ||
| 220 | - ProductSpuDO spu = productSpuService.getSpu(createReqVO.getId()); | ||
| 221 | - assertPojoEquals(createReqVO, spu); | 223 | + // 调用 |
| 224 | + ProductSpuDO spu = productSpuService.getSpu(dbSpu.getId()); | ||
| 225 | + // 断言 | ||
| 226 | + assertPojoEquals(dbSpu, spu); | ||
| 222 | } | 227 | } |
| 223 | 228 | ||
| 224 | @Test | 229 | @Test |
| 225 | - void getSpuList() { | ||
| 226 | - // 准备参数 | ||
| 227 | - ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{ | ||
| 228 | - o.setCategoryId(generateId()); | ||
| 229 | - o.setBrandId(generateId()); | ||
| 230 | - o.setDeliveryTemplateId(generateId()); | ||
| 231 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 232 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 233 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 234 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 235 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 236 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 237 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 238 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 239 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 240 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 241 | - }), randomPojo(ProductSpuDO.class,o->{ | ||
| 242 | - o.setCategoryId(generateId()); | ||
| 243 | - o.setBrandId(generateId()); | ||
| 244 | - o.setDeliveryTemplateId(generateId()); | ||
| 245 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 246 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 247 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 248 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 249 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 250 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 251 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 252 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 253 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 254 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 255 | - })); | ||
| 256 | - productSpuMapper.insertBatch(createReqVOs); | 230 | + public void testGetSpuList() { |
| 231 | + // mock 数据 | ||
| 232 | + ArrayList<ProductSpuDO> dbSpus = Lists.newArrayList(randomSpuDO(), randomSpuDO()); | ||
| 233 | + productSpuMapper.insertBatch(dbSpus); | ||
| 234 | + // 准备参数:倒序传入 | ||
| 235 | + List<Long> ids = Arrays.asList(dbSpus.get(1).getId(), dbSpus.get(0).getId()); | ||
| 257 | 236 | ||
| 258 | // 调用 | 237 | // 调用 |
| 259 | - List<ProductSpuDO> spuList = productSpuService.getSpuList(createReqVOs.stream().map(ProductSpuDO::getId).collect(Collectors.toList())); | ||
| 260 | - Assertions.assertIterableEquals(createReqVOs, spuList); | 238 | + List<ProductSpuDO> spuList = productSpuService.getSpuList(ids); |
| 239 | + // 断言:按照 ids 顺序返回 | ||
| 240 | + assertEquals(ids, convertList(spuList, ProductSpuDO::getId)); | ||
| 241 | + assertPojoEquals(dbSpus.get(1), spuList.get(0)); | ||
| 242 | + assertPojoEquals(dbSpus.get(0), spuList.get(1)); | ||
| 261 | } | 243 | } |
| 262 | 244 | ||
| 263 | @Test | 245 | @Test |
| 264 | - void getSpuPage_alarmStock_empty() { | 246 | + public void testGetSpuPage_alertStockEmpty() { |
| 247 | + // mock 数据 | ||
| 248 | + ArrayList<ProductSpuDO> dbSpus = Lists.newArrayList(randomSpuDO(o -> o.setStock(11)), | ||
| 249 | + randomSpuDO(o -> o.setStock(11))); | ||
| 250 | + productSpuMapper.insertBatch(dbSpus); | ||
| 265 | // 准备参数 | 251 | // 准备参数 |
| 266 | - ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{ | ||
| 267 | - o.setCategoryId(generateId()); | ||
| 268 | - o.setBrandId(generateId()); | ||
| 269 | - o.setDeliveryTemplateId(generateId()); | ||
| 270 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 271 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 272 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 273 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 274 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 275 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 276 | - o.setStock(11); // 限制范围为正整数 | ||
| 277 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 278 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 279 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 280 | - }), randomPojo(ProductSpuDO.class,o->{ | ||
| 281 | - o.setCategoryId(generateId()); | ||
| 282 | - o.setBrandId(generateId()); | ||
| 283 | - o.setDeliveryTemplateId(generateId()); | ||
| 284 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 285 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 286 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 287 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 288 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 289 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 290 | - o.setStock(11); // 限制范围为正整数 | ||
| 291 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 292 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 293 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 294 | - })); | ||
| 295 | - productSpuMapper.insertBatch(createReqVOs); | ||
| 296 | - // 调用 | ||
| 297 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); | 252 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); |
| 298 | productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); | 253 | productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); |
| 299 | 254 | ||
| 255 | + // 调用 | ||
| 300 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); | 256 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); |
| 301 | - | ||
| 302 | - PageResult<Object> result = PageResult.empty(); | ||
| 303 | - Assertions.assertIterableEquals(result.getList(), spuPage.getList()); | ||
| 304 | - assertEquals(spuPage.getTotal(), result.getTotal()); | 257 | + // 断言 |
| 258 | + assertEquals(0, spuPage.getTotal()); | ||
| 259 | + assertEquals(0, spuPage.getList().size()); | ||
| 305 | } | 260 | } |
| 306 | 261 | ||
| 307 | @Test | 262 | @Test |
| 308 | - void getSpuPage_alarmStock() { | 263 | + public void testGetSpuPage_alertStock() { |
| 264 | + // mock 数据 | ||
| 265 | + ArrayList<ProductSpuDO> dbSpus = Lists.newArrayList(randomSpuDO(o -> o.setStock(5)), | ||
| 266 | + randomSpuDO(o -> o.setStock(9))); | ||
| 267 | + productSpuMapper.insertBatch(dbSpus); | ||
| 268 | + // 测试 stock 不匹配 | ||
| 269 | + productSpuMapper.insert(randomSpuDO(o -> o.setStock(11))); | ||
| 270 | + // 测试 status 不匹配(回收站) | ||
| 271 | + productSpuMapper.insert(randomSpuDO(o -> o.setStock(5).setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); | ||
| 309 | // 准备参数 | 272 | // 准备参数 |
| 310 | - ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{ | ||
| 311 | - o.setCategoryId(generateId()); | ||
| 312 | - o.setBrandId(generateId()); | ||
| 313 | - o.setDeliveryTemplateId(generateId()); | ||
| 314 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 315 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 316 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 317 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 318 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 319 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 320 | - o.setStock(5); // 限制范围为正整数 | ||
| 321 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 322 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 323 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 324 | - }), randomPojo(ProductSpuDO.class,o->{ | ||
| 325 | - o.setCategoryId(generateId()); | ||
| 326 | - o.setBrandId(generateId()); | ||
| 327 | - o.setDeliveryTemplateId(generateId()); | ||
| 328 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 329 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 330 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 331 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 332 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 333 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 334 | - o.setStock(9); // 限制范围为正整数 | ||
| 335 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 336 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 337 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 338 | - })); | ||
| 339 | - productSpuMapper.insertBatch(createReqVOs); | ||
| 340 | - // 调用 | ||
| 341 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); | 273 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); |
| 342 | productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); | 274 | productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); |
| 275 | + | ||
| 276 | + // 调用 | ||
| 343 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); | 277 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); |
| 344 | - assertEquals(createReqVOs.size(), spuPage.getTotal()); | 278 | + // 断言 |
| 279 | + assertEquals(dbSpus.size(), spuPage.getTotal()); | ||
| 280 | + assertEquals(convertSet(dbSpus, ProductSpuDO::getId), convertSet(spuPage.getList(), ProductSpuDO::getId)); | ||
| 345 | } | 281 | } |
| 346 | 282 | ||
| 347 | @Test | 283 | @Test |
| 348 | - void testGetSpuPage() { | ||
| 349 | - // 准备参数 | ||
| 350 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | ||
| 351 | - o.setCategoryId(generateId()); | ||
| 352 | - o.setBrandId(generateId()); | ||
| 353 | - o.setDeliveryTemplateId(generateId()); | ||
| 354 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 355 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 356 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 357 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 358 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 359 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 360 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 361 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 362 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 363 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | 284 | + public void testGetSpuPage() { |
| 285 | + // mock 数据 | ||
| 286 | + ProductSpuDO dbSpu = randomSpuDO(o -> { // 等会查询到 | ||
| 287 | + o.setName("芋道商品"); | ||
| 288 | + o.setCategoryId(1L); | ||
| 289 | + o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); | ||
| 290 | + o.setCreateTime(buildTime(2023, 2, 10)); | ||
| 364 | }); | 291 | }); |
| 365 | - | 292 | + productSpuMapper.insert(dbSpu); |
| 293 | + // 测试 name 不匹配 | ||
| 294 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setName("土豆商品"))); | ||
| 295 | + // 测试 categoryId 不匹配 | ||
| 296 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCategoryId(2L))); | ||
| 297 | + // 测试 createTime 不匹配 | ||
| 298 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCreateTime(buildTime(2023, 3, 10)))); | ||
| 299 | + // 测试 status 不匹配(出售中) | ||
| 300 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()))); | ||
| 301 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); | ||
| 366 | // 准备参数 | 302 | // 准备参数 |
| 367 | - productSpuMapper.insert(createReqVO); | ||
| 368 | - // 测试 status 不匹配 | ||
| 369 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()))); | ||
| 370 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); | ||
| 371 | - // 测试 SpecType 不匹配 | ||
| 372 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setSpecType(true))); | ||
| 373 | - // 测试 BrandId 不匹配 | ||
| 374 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setBrandId(generateId()))); | ||
| 375 | - // 测试 CategoryId 不匹配 | ||
| 376 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setCategoryId(generateId()))); | ||
| 377 | - | ||
| 378 | - // 调用 | ||
| 379 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); | 303 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); |
| 380 | - // 查询条件 按需打开 | ||
| 381 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); | ||
| 382 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.RECYCLE_BIN); | ||
| 383 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.FOR_SALE); | ||
| 384 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.IN_WAREHOUSE); | ||
| 385 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.SOLD_OUT); | ||
| 386 | - //productSpuPageReqVO.setName(createReqVO.getName()); | ||
| 387 | - //productSpuPageReqVO.setCategoryId(createReqVO.getCategoryId()); | 304 | + productSpuPageReqVO.setName("芋道"); |
| 305 | + productSpuPageReqVO.setCategoryId(1L); | ||
| 306 | + productSpuPageReqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | ||
| 307 | + productSpuPageReqVO.setTabType(ProductSpuPageReqVO.FOR_SALE); | ||
| 388 | 308 | ||
| 309 | + // 调用 | ||
| 389 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); | 310 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); |
| 390 | - | 311 | + // 断言 |
| 391 | assertEquals(1, spuPage.getTotal()); | 312 | assertEquals(1, spuPage.getTotal()); |
| 313 | + assertEquals(1, spuPage.getList().size()); | ||
| 314 | + assertPojoEquals(dbSpu, spuPage.getList().get(0)); | ||
| 392 | } | 315 | } |
| 393 | 316 | ||
| 394 | /** | 317 | /** |
| @@ -427,23 +350,49 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | @@ -427,23 +350,49 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | ||
| 427 | // 准备参数 | 350 | // 准备参数 |
| 428 | Map<Long, Integer> stockIncrCounts = MapUtil.builder(1L, 10).put(2L, -20).build(); | 351 | Map<Long, Integer> stockIncrCounts = MapUtil.builder(1L, 10).put(2L, -20).build(); |
| 429 | // mock 方法(数据) | 352 | // mock 方法(数据) |
| 430 | - productSpuMapper.insert(randomPojo(ProductSpuDO.class, o ->{ | ||
| 431 | - o.setCategoryId(generateId()); | ||
| 432 | - o.setBrandId(generateId()); | ||
| 433 | - o.setDeliveryTemplateId(generateId()); | ||
| 434 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | ||
| 435 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 436 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | ||
| 437 | - o.setPrice(generaInt()); // 限制范围为正整数 | ||
| 438 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | ||
| 439 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | ||
| 440 | - o.setStock(generaInt()); // 限制范围为正整数 | ||
| 441 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 442 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | ||
| 443 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | ||
| 444 | - o.setId(1L).setStock(20); | ||
| 445 | - })); | ||
| 446 | - productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> { | 353 | + productSpuMapper.insert(randomSpuDO(o -> o.setId(1L).setStock(20))); |
| 354 | + productSpuMapper.insert(randomSpuDO(o -> o.setId(2L).setStock(30))); | ||
| 355 | + | ||
| 356 | + // 调用 | ||
| 357 | + productSpuService.updateSpuStock(stockIncrCounts); | ||
| 358 | + // 断言 | ||
| 359 | + assertEquals(productSpuService.getSpu(1L).getStock(), 30); | ||
| 360 | + assertEquals(productSpuService.getSpu(2L).getStock(), 10); | ||
| 361 | + } | ||
| 362 | + | ||
| 363 | + /** | ||
| 364 | + * 校验 {@link ProductSpuSaveReqVO#getDeliveryTypes()} 字段 | ||
| 365 | + * | ||
| 366 | + * @param deliveryTypes 配送方式数组 | ||
| 367 | + * @return 校验不通过的提示 | ||
| 368 | + */ | ||
| 369 | + private static Set<String> validateDeliveryTypes(List<Integer> deliveryTypes) { | ||
| 370 | + ProductSpuSaveReqVO reqVO = new ProductSpuSaveReqVO(); | ||
| 371 | + reqVO.setDeliveryTypes(deliveryTypes); | ||
| 372 | + Set<ConstraintViolation<ProductSpuSaveReqVO>> violations = VALIDATOR.validateProperty(reqVO, "deliveryTypes"); | ||
| 373 | + return convertSet(violations, ConstraintViolation::getMessage); | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + // ========== 随机对象 ========== | ||
| 377 | + | ||
| 378 | + private ProductSkuSaveReqVO randomSkuSaveReqVO() { | ||
| 379 | + return randomPojo(ProductSkuSaveReqVO.class, o->{ | ||
| 380 | + // 限制范围为正整数 | ||
| 381 | + o.setCostPrice(generaInt()); | ||
| 382 | + o.setPrice(generaInt()); | ||
| 383 | + o.setMarketPrice(generaInt()); | ||
| 384 | + o.setStock(generaInt()); | ||
| 385 | + o.setFirstBrokeragePrice(generaInt()); | ||
| 386 | + o.setSecondBrokeragePrice(generaInt()); | ||
| 387 | + // 限制分数为两位数 | ||
| 388 | + o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | ||
| 389 | + o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | ||
| 390 | + }); | ||
| 391 | + } | ||
| 392 | + | ||
| 393 | + @SafeVarargs | ||
| 394 | + private final ProductSpuDO randomSpuDO(Consumer<ProductSpuDO>... consumers) { | ||
| 395 | + Consumer<ProductSpuDO> consumer = o -> { | ||
| 447 | o.setCategoryId(generateId()); | 396 | o.setCategoryId(generateId()); |
| 448 | o.setBrandId(generateId()); | 397 | o.setBrandId(generateId()); |
| 449 | o.setDeliveryTemplateId(generateId()); | 398 | o.setDeliveryTemplateId(generateId()); |
| @@ -454,17 +403,10 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | @@ -454,17 +403,10 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | ||
| 454 | o.setMarketPrice(generaInt()); // 限制范围为正整数 | 403 | o.setMarketPrice(generaInt()); // 限制范围为正整数 |
| 455 | o.setCostPrice(generaInt()); // 限制范围为正整数 | 404 | o.setCostPrice(generaInt()); // 限制范围为正整数 |
| 456 | o.setStock(generaInt()); // 限制范围为正整数 | 405 | o.setStock(generaInt()); // 限制范围为正整数 |
| 457 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | ||
| 458 | o.setSalesCount(generaInt()); // 限制范围为正整数 | 406 | o.setSalesCount(generaInt()); // 限制范围为正整数 |
| 459 | o.setBrowseCount(generaInt()); // 限制范围为正整数 | 407 | o.setBrowseCount(generaInt()); // 限制范围为正整数 |
| 460 | - o.setId(2L).setStock(30); | ||
| 461 | - })); | ||
| 462 | - | ||
| 463 | - // 调用 | ||
| 464 | - productSpuService.updateSpuStock(stockIncrCounts); | ||
| 465 | - // 断言 | ||
| 466 | - assertEquals(productSpuService.getSpu(1L).getStock(), 30); | ||
| 467 | - assertEquals(productSpuService.getSpu(2L).getStock(), 10); | 408 | + }; |
| 409 | + return randomPojo(ProductSpuDO.class, ArrayUtils.append(consumer, consumers)); | ||
| 468 | } | 410 | } |
| 469 | 411 | ||
| 470 | } | 412 | } |
yudao-module-mall/yudao-module-product/src/test/resources/sql/create_tables.sql
| @@ -28,13 +28,10 @@ CREATE TABLE IF NOT EXISTS `product_spu` ( | @@ -28,13 +28,10 @@ CREATE TABLE IF NOT EXISTS `product_spu` ( | ||
| 28 | `keyword` varchar(256) NOT NULL COMMENT '关键字', | 28 | `keyword` varchar(256) NOT NULL COMMENT '关键字', |
| 29 | `introduction` varchar(256) NOT NULL COMMENT '商品简介', | 29 | `introduction` varchar(256) NOT NULL COMMENT '商品简介', |
| 30 | `description` text NOT NULL COMMENT '商品详情', | 30 | `description` text NOT NULL COMMENT '商品详情', |
| 31 | - `bar_code` varchar(64) NOT NULL COMMENT '条形码', | ||
| 32 | `category_id` bigint NOT NULL COMMENT '商品分类编号', | 31 | `category_id` bigint NOT NULL COMMENT '商品分类编号', |
| 33 | - `brand_id` int DEFAULT NULL COMMENT '商品品牌编号', | 32 | + `brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号', |
| 34 | `pic_url` varchar(256) NOT NULL COMMENT '商品封面图', | 33 | `pic_url` varchar(256) NOT NULL COMMENT '商品封面图', |
| 35 | `slider_pic_urls` varchar(2000) DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张', | 34 | `slider_pic_urls` varchar(2000) DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张', |
| 36 | - `video_url` varchar(256) DEFAULT NULL COMMENT '商品视频', | ||
| 37 | - `unit` tinyint NOT NULL COMMENT '单位', | ||
| 38 | `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段', | 35 | `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段', |
| 39 | `status` tinyint NOT NULL COMMENT '商品状态: 0 上架(开启) 1 下架(禁用)-1 回收', | 36 | `status` tinyint NOT NULL COMMENT '商品状态: 0 上架(开启) 1 下架(禁用)-1 回收', |
| 40 | `spec_type` bit(1) NOT NULL COMMENT '规格类型:0 单规格 1 多规格', | 37 | `spec_type` bit(1) NOT NULL COMMENT '规格类型:0 单规格 1 多规格', |
| @@ -42,16 +39,10 @@ CREATE TABLE IF NOT EXISTS `product_spu` ( | @@ -42,16 +39,10 @@ CREATE TABLE IF NOT EXISTS `product_spu` ( | ||
| 42 | `market_price` int NOT NULL COMMENT '市场价,单位使用:分', | 39 | `market_price` int NOT NULL COMMENT '市场价,单位使用:分', |
| 43 | `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分', | 40 | `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分', |
| 44 | `stock` int NOT NULL DEFAULT '0' COMMENT '库存', | 41 | `stock` int NOT NULL DEFAULT '0' COMMENT '库存', |
| 42 | + `delivery_types` varchar(255) NOT NULL COMMENT '配送方式数组', | ||
| 45 | `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号', | 43 | `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号', |
| 46 | - `recommend_hot` bit(1) NOT NULL COMMENT '是否热卖推荐: 0 默认 1 热卖', | ||
| 47 | - `recommend_benefit` bit(1) NOT NULL COMMENT '是否优惠推荐: 0 默认 1 优选', | ||
| 48 | - `recommend_best` bit(1) NOT NULL COMMENT '是否精品推荐: 0 默认 1 精品', | ||
| 49 | - `recommend_new` bit(1) NOT NULL COMMENT '是否新品推荐: 0 默认 1 新品', | ||
| 50 | - `recommend_good` bit(1) NOT NULL COMMENT '是否优品推荐', | ||
| 51 | `give_integral` int NOT NULL COMMENT '赠送积分', | 44 | `give_integral` int NOT NULL COMMENT '赠送积分', |
| 52 | - `give_coupon_template_ids` varchar(512) DEFAULT '' COMMENT '赠送的优惠劵编号的数组', | ||
| 53 | `sub_commission_type` bit(1) NOT NULL COMMENT '分销类型', | 45 | `sub_commission_type` bit(1) NOT NULL COMMENT '分销类型', |
| 54 | - `activity_orders` varchar(16) NOT NULL DEFAULT '' COMMENT '活动显示排序0=默认, 1=秒杀,2=砍价,3=拼团', | ||
| 55 | `sales_count` int DEFAULT '0' COMMENT '商品销量', | 46 | `sales_count` int DEFAULT '0' COMMENT '商品销量', |
| 56 | `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量', | 47 | `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量', |
| 57 | `browse_count` int DEFAULT '0' COMMENT '商品点击量', | 48 | `browse_count` int DEFAULT '0' COMMENT '商品点击量', |
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImpl.java
| @@ -102,7 +102,7 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | @@ -102,7 +102,7 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | ||
| 102 | } | 102 | } |
| 103 | // 删除 | 103 | // 删除 |
| 104 | diyTemplateMapper.deleteById(id); | 104 | diyTemplateMapper.deleteById(id); |
| 105 | - // 级联删除模板下自动生成的默认页面(createDiyTemplate 的「首页/我的」),避免页面列表残留孤儿页 | 105 | + // 删除模板下的页面 |
| 106 | diyPageService.getDiyPageByTemplateId(id) | 106 | diyPageService.getDiyPageByTemplateId(id) |
| 107 | .forEach(page -> diyPageService.deleteDiyPage(page.getId())); | 107 | .forEach(page -> diyPageService.deleteDiyPage(page.getId())); |
| 108 | } | 108 | } |
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.promotion.service.coupon; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | ||
| 5 | +import cn.iocoder.yudao.module.product.api.category.ProductCategoryApi; | ||
| 6 | +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | ||
| 7 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; | ||
| 8 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; | ||
| 9 | +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; | ||
| 10 | +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper; | ||
| 11 | +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; | ||
| 12 | +import jakarta.annotation.Resource; | ||
| 13 | +import org.junit.jupiter.api.Test; | ||
| 14 | +import org.springframework.context.annotation.Import; | ||
| 15 | +import org.springframework.jdbc.core.JdbcTemplate; | ||
| 16 | +import org.springframework.test.context.bean.override.mockito.MockitoBean; | ||
| 17 | + | ||
| 18 | +import javax.sql.DataSource; | ||
| 19 | +import java.util.function.Consumer; | ||
| 20 | + | ||
| 21 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||
| 22 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||
| 23 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||
| 24 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_DELETE_FAIL_USED; | ||
| 25 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NOT_EXISTS; | ||
| 26 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 27 | + | ||
| 28 | +/** | ||
| 29 | + * {@link CouponServiceImpl} 的单元测试类 | ||
| 30 | + * | ||
| 31 | + * @author 芋道源码 | ||
| 32 | + */ | ||
| 33 | +@Import({CouponServiceImpl.class, CouponTemplateServiceImpl.class}) | ||
| 34 | +public class CouponServiceImplTest extends BaseDbUnitTest { | ||
| 35 | + | ||
| 36 | + @Resource | ||
| 37 | + private CouponServiceImpl couponService; | ||
| 38 | + | ||
| 39 | + @Resource | ||
| 40 | + private CouponMapper couponMapper; | ||
| 41 | + @Resource | ||
| 42 | + private CouponTemplateMapper couponTemplateMapper; | ||
| 43 | + | ||
| 44 | + @Resource | ||
| 45 | + private DataSource dataSource; | ||
| 46 | + | ||
| 47 | + @MockitoBean | ||
| 48 | + private MemberUserApi memberUserApi; | ||
| 49 | + @MockitoBean | ||
| 50 | + private ProductCategoryApi productCategoryApi; | ||
| 51 | + @MockitoBean | ||
| 52 | + private ProductSpuApi productSpuApi; | ||
| 53 | + | ||
| 54 | + @Test | ||
| 55 | + public void testDeleteCoupon_success() { | ||
| 56 | + // mock 数据 | ||
| 57 | + CouponTemplateDO dbTemplate = randomCouponTemplateDO(o -> o.setTakeCount(5)); | ||
| 58 | + couponTemplateMapper.insert(dbTemplate); | ||
| 59 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(dbTemplate.getId()) | ||
| 60 | + .setStatus(CouponStatusEnum.UNUSED.getStatus())); | ||
| 61 | + couponMapper.insert(dbCoupon); | ||
| 62 | + | ||
| 63 | + // 调用 | ||
| 64 | + couponService.deleteCoupon(dbCoupon.getId()); | ||
| 65 | + // 断言:优惠劵被删除,模板领取数量 -1 | ||
| 66 | + assertNull(couponMapper.selectById(dbCoupon.getId())); | ||
| 67 | + assertEquals(4, couponTemplateMapper.selectById(dbTemplate.getId()).getTakeCount()); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Test | ||
| 71 | + public void testDeleteCoupon_templateDeleted() { | ||
| 72 | + // mock 数据(模板已删除) | ||
| 73 | + CouponTemplateDO dbTemplate = randomCouponTemplateDO(o -> o.setTakeCount(5)); | ||
| 74 | + couponTemplateMapper.insert(dbTemplate); | ||
| 75 | + couponTemplateMapper.deleteById(dbTemplate.getId()); | ||
| 76 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(dbTemplate.getId()) | ||
| 77 | + .setStatus(CouponStatusEnum.UNUSED.getStatus())); | ||
| 78 | + couponMapper.insert(dbCoupon); | ||
| 79 | + | ||
| 80 | + // 调用 | ||
| 81 | + couponService.deleteCoupon(dbCoupon.getId()); | ||
| 82 | + // 断言:优惠劵被删除,已删除模板的领取数量不变 | ||
| 83 | + assertNull(couponMapper.selectById(dbCoupon.getId())); | ||
| 84 | + assertEquals(5, selectTemplateTakeCount(dbTemplate.getId())); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + @Test | ||
| 88 | + public void testDeleteCoupon_templateNotExists() { | ||
| 89 | + // mock 数据(模板不存在) | ||
| 90 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(randomLongId()) | ||
| 91 | + .setStatus(CouponStatusEnum.EXPIRE.getStatus())); | ||
| 92 | + couponMapper.insert(dbCoupon); | ||
| 93 | + | ||
| 94 | + // 调用 | ||
| 95 | + couponService.deleteCoupon(dbCoupon.getId()); | ||
| 96 | + // 断言:优惠劵被删除 | ||
| 97 | + assertNull(couponMapper.selectById(dbCoupon.getId())); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @Test | ||
| 101 | + public void testDeleteCoupon_used() { | ||
| 102 | + // mock 数据 | ||
| 103 | + CouponTemplateDO dbTemplate = randomCouponTemplateDO(o -> o.setTakeCount(5)); | ||
| 104 | + couponTemplateMapper.insert(dbTemplate); | ||
| 105 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(dbTemplate.getId()) | ||
| 106 | + .setStatus(CouponStatusEnum.USED.getStatus())); | ||
| 107 | + couponMapper.insert(dbCoupon); | ||
| 108 | + | ||
| 109 | + // 调用, 并断言异常 | ||
| 110 | + assertServiceException(() -> couponService.deleteCoupon(dbCoupon.getId()), COUPON_DELETE_FAIL_USED); | ||
| 111 | + // 断言:优惠劵未删除,模板领取数量不变 | ||
| 112 | + CouponDO coupon = couponMapper.selectById(dbCoupon.getId()); | ||
| 113 | + assertNotNull(coupon); | ||
| 114 | + assertEquals(CouponStatusEnum.USED.getStatus(), coupon.getStatus()); | ||
| 115 | + assertEquals(5, couponTemplateMapper.selectById(dbTemplate.getId()).getTakeCount()); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + @Test | ||
| 119 | + public void testDeleteCoupon_notExists() { | ||
| 120 | + // 准备参数 | ||
| 121 | + Long id = randomLongId(); | ||
| 122 | + | ||
| 123 | + // 调用, 并断言异常 | ||
| 124 | + assertServiceException(() -> couponService.deleteCoupon(id), COUPON_NOT_EXISTS); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + /** | ||
| 128 | + * 查询模板的领取数量,包括已删除的模板 | ||
| 129 | + * | ||
| 130 | + * @param id 模板编号 | ||
| 131 | + * @return 领取数量 | ||
| 132 | + */ | ||
| 133 | + private Integer selectTemplateTakeCount(Long id) { | ||
| 134 | + return new JdbcTemplate(dataSource).queryForObject( | ||
| 135 | + "SELECT take_count FROM promotion_coupon_template WHERE id = ?", Integer.class, id); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + // ========== 随机对象 ========== | ||
| 139 | + | ||
| 140 | + @SafeVarargs | ||
| 141 | + private static CouponTemplateDO randomCouponTemplateDO(Consumer<CouponTemplateDO>... consumers) { | ||
| 142 | + return randomPojo(CouponTemplateDO.class, consumers); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + @SafeVarargs | ||
| 146 | + private static CouponDO randomCouponDO(Consumer<CouponDO>... consumers) { | ||
| 147 | + return randomPojo(CouponDO.class, consumers); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | +} |
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImplTest.java
| @@ -7,18 +7,19 @@ import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePag | @@ -7,18 +7,19 @@ import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePag | ||
| 7 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO; | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO; |
| 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO; | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO; |
| 9 | import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper; | 9 | import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper; |
| 10 | -import org.junit.jupiter.api.Disabled; | ||
| 11 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
| 12 | import org.springframework.context.annotation.Import; | 11 | import org.springframework.context.annotation.Import; |
| 13 | 12 | ||
| 14 | import jakarta.annotation.Resource; | 13 | import jakarta.annotation.Resource; |
| 15 | 14 | ||
| 16 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | 15 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
| 16 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||
| 17 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | 17 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 18 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 18 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 19 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | 19 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 20 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | 20 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
| 21 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 21 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 22 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NAME_USED; | ||
| 22 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NOT_EXISTS; | 23 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NOT_EXISTS; |
| 23 | import static org.junit.jupiter.api.Assertions.*; | 24 | import static org.junit.jupiter.api.Assertions.*; |
| 24 | 25 | ||
| @@ -27,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.*; | @@ -27,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.*; | ||
| 27 | * | 28 | * |
| 28 | * @author owen | 29 | * @author owen |
| 29 | */ | 30 | */ |
| 30 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | 31 | +// DONE 芋艿:后续 fix 补充的单测 |
| 31 | @Import(DiyPageServiceImpl.class) | 32 | @Import(DiyPageServiceImpl.class) |
| 32 | public class DiyPageServiceImplTest extends BaseDbUnitTest { | 33 | public class DiyPageServiceImplTest extends BaseDbUnitTest { |
| 33 | 34 | ||
| @@ -49,6 +50,21 @@ public class DiyPageServiceImplTest extends BaseDbUnitTest { | @@ -49,6 +50,21 @@ public class DiyPageServiceImplTest extends BaseDbUnitTest { | ||
| 49 | // 校验记录的属性是否正确 | 50 | // 校验记录的属性是否正确 |
| 50 | DiyPageDO diyPage = diyPageMapper.selectById(diyPageId); | 51 | DiyPageDO diyPage = diyPageMapper.selectById(diyPageId); |
| 51 | assertPojoEquals(reqVO, diyPage); | 52 | assertPojoEquals(reqVO, diyPage); |
| 53 | + assertEquals("{}", diyPage.getProperty()); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Test | ||
| 57 | + public void testCreateDiyPage_nameUsed() { | ||
| 58 | + // mock 数据(独立页面) | ||
| 59 | + diyPageMapper.insert(randomPojo(DiyPageDO.class, o -> o.setTemplateId(null).setName("芋道首页"))); | ||
| 60 | + // 准备参数 | ||
| 61 | + DiyPageCreateReqVO reqVO = randomPojo(DiyPageCreateReqVO.class, o -> { | ||
| 62 | + o.setTemplateId(null); | ||
| 63 | + o.setName("芋道首页"); | ||
| 64 | + }); | ||
| 65 | + | ||
| 66 | + // 调用, 并断言异常 | ||
| 67 | + assertServiceException(() -> diyPageService.createDiyPage(reqVO), DIY_PAGE_NAME_USED, "芋道首页"); | ||
| 52 | } | 68 | } |
| 53 | 69 | ||
| 54 | @Test | 70 | @Test |
| @@ -101,21 +117,24 @@ public class DiyPageServiceImplTest extends BaseDbUnitTest { | @@ -101,21 +117,24 @@ public class DiyPageServiceImplTest extends BaseDbUnitTest { | ||
| 101 | } | 117 | } |
| 102 | 118 | ||
| 103 | @Test | 119 | @Test |
| 104 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | 120 | + // DONE 请修改 null 为需要的值,然后删除 @Disabled 注解 |
| 105 | public void testGetDiyPagePage() { | 121 | public void testGetDiyPagePage() { |
| 106 | // mock 数据 | 122 | // mock 数据 |
| 107 | DiyPageDO dbDiyPage = randomPojo(DiyPageDO.class, o -> { // 等会查询到 | 123 | DiyPageDO dbDiyPage = randomPojo(DiyPageDO.class, o -> { // 等会查询到 |
| 108 | - o.setName(null); | ||
| 109 | - o.setCreateTime(null); | 124 | + o.setTemplateId(null); // 独立页面 |
| 125 | + o.setName("芋道首页"); | ||
| 126 | + o.setCreateTime(buildTime(2023, 2, 10)); | ||
| 110 | }); | 127 | }); |
| 111 | diyPageMapper.insert(dbDiyPage); | 128 | diyPageMapper.insert(dbDiyPage); |
| 112 | // 测试 name 不匹配 | 129 | // 测试 name 不匹配 |
| 113 | - diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setName(null))); | 130 | + diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setName("土豆首页"))); |
| 114 | // 测试 createTime 不匹配 | 131 | // 测试 createTime 不匹配 |
| 115 | - diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setCreateTime(null))); | 132 | + diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setCreateTime(buildTime(2023, 3, 10)))); |
| 133 | + // 测试 templateId 不匹配(模板下的页面,不在独立页面列表) | ||
| 134 | + diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setTemplateId(1L))); | ||
| 116 | // 准备参数 | 135 | // 准备参数 |
| 117 | DiyPagePageReqVO reqVO = new DiyPagePageReqVO(); | 136 | DiyPagePageReqVO reqVO = new DiyPagePageReqVO(); |
| 118 | - reqVO.setName(null); | 137 | + reqVO.setName("芋道"); |
| 119 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | 138 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 120 | 139 | ||
| 121 | // 调用 | 140 | // 调用 |
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImplTest.java
| @@ -5,30 +5,40 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | @@ -5,30 +5,40 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||
| 5 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO; | 5 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO; |
| 6 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO; | 6 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO; |
| 7 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO; | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO; |
| 8 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO; | ||
| 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO; | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO; |
| 10 | +import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper; | ||
| 9 | import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper; | 11 | import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper; |
| 10 | -import org.junit.jupiter.api.Disabled; | 12 | +import jakarta.annotation.Resource; |
| 11 | import org.junit.jupiter.api.Test; | 13 | import org.junit.jupiter.api.Test; |
| 12 | import org.springframework.context.annotation.Import; | 14 | import org.springframework.context.annotation.Import; |
| 15 | +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; | ||
| 13 | 16 | ||
| 14 | -import jakarta.annotation.Resource; | 17 | +import java.util.List; |
| 18 | +import java.util.function.Consumer; | ||
| 15 | 19 | ||
| 20 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 21 | +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||
| 16 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | 22 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
| 23 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||
| 17 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | 24 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 18 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 25 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 19 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | 26 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 20 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | 27 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
| 21 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 28 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 22 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_NOT_EXISTS; | 29 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_NOT_EXISTS; |
| 30 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_USED_CANNOT_DELETE; | ||
| 23 | import static org.junit.jupiter.api.Assertions.*; | 31 | import static org.junit.jupiter.api.Assertions.*; |
| 32 | +import static org.mockito.ArgumentMatchers.anyLong; | ||
| 33 | +import static org.mockito.Mockito.doCallRealMethod; | ||
| 24 | 34 | ||
| 25 | /** | 35 | /** |
| 26 | * {@link DiyTemplateServiceImpl} 的单元测试类 | 36 | * {@link DiyTemplateServiceImpl} 的单元测试类 |
| 27 | * | 37 | * |
| 28 | * @author owen | 38 | * @author owen |
| 29 | */ | 39 | */ |
| 30 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | ||
| 31 | -@Import(DiyTemplateServiceImpl.class) | 40 | +// DONE 芋艿:后续 fix 补充的单测 |
| 41 | +@Import({DiyTemplateServiceImpl.class, DiyPageServiceImpl.class}) | ||
| 32 | public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | 42 | public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 33 | 43 | ||
| 34 | @Resource | 44 | @Resource |
| @@ -36,6 +46,11 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | @@ -36,6 +46,11 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | ||
| 36 | 46 | ||
| 37 | @Resource | 47 | @Resource |
| 38 | private DiyTemplateMapper diyTemplateMapper; | 48 | private DiyTemplateMapper diyTemplateMapper; |
| 49 | + @Resource | ||
| 50 | + private DiyPageMapper diyPageMapper; | ||
| 51 | + | ||
| 52 | + @MockitoSpyBean | ||
| 53 | + private DiyPageService diyPageService; | ||
| 39 | 54 | ||
| 40 | @Test | 55 | @Test |
| 41 | public void testCreateDiyTemplate_success() { | 56 | public void testCreateDiyTemplate_success() { |
| @@ -49,6 +64,15 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | @@ -49,6 +64,15 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | ||
| 49 | // 校验记录的属性是否正确 | 64 | // 校验记录的属性是否正确 |
| 50 | DiyTemplateDO diyTemplate = diyTemplateMapper.selectById(diyTemplateId); | 65 | DiyTemplateDO diyTemplate = diyTemplateMapper.selectById(diyTemplateId); |
| 51 | assertPojoEquals(reqVO, diyTemplate); | 66 | assertPojoEquals(reqVO, diyTemplate); |
| 67 | + assertEquals("{}", diyTemplate.getProperty()); | ||
| 68 | + // 校验默认页面:首页、我的 | ||
| 69 | + List<DiyPageDO> pages = diyPageMapper.selectListByTemplateId(diyTemplateId); | ||
| 70 | + assertEquals(asSet("首页", "我的"), convertSet(pages, DiyPageDO::getName)); | ||
| 71 | + String remark = String.format("模板【%s】自动创建", reqVO.getName()); | ||
| 72 | + pages.forEach(page -> { | ||
| 73 | + assertEquals(remark, page.getRemark()); | ||
| 74 | + assertEquals("{}", page.getProperty()); | ||
| 75 | + }); | ||
| 52 | } | 76 | } |
| 53 | 77 | ||
| 54 | @Test | 78 | @Test |
| @@ -79,9 +103,20 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | @@ -79,9 +103,20 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | ||
| 79 | 103 | ||
| 80 | @Test | 104 | @Test |
| 81 | public void testDeleteDiyTemplate_success() { | 105 | public void testDeleteDiyTemplate_success() { |
| 82 | - // mock 数据 | ||
| 83 | - DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class); | 106 | + // mock 数据(模板) |
| 107 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(false)); | ||
| 84 | diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 | 108 | diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 |
| 109 | + DiyTemplateDO otherDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(false)); | ||
| 110 | + diyTemplateMapper.insert(otherDiyTemplate); | ||
| 111 | + // mock 数据(页面):该模板下的全部页面,包括默认页面及后续新增页面 | ||
| 112 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()).setName("首页"))); | ||
| 113 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()).setName("我的"))); | ||
| 114 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()).setName("活动页"))); | ||
| 115 | + // mock 数据(页面):其它模板下的页面、独立页面 | ||
| 116 | + DiyPageDO otherTemplatePage = randomDiyPageDO(o -> o.setTemplateId(otherDiyTemplate.getId())); | ||
| 117 | + diyPageMapper.insert(otherTemplatePage); | ||
| 118 | + DiyPageDO standalonePage = randomDiyPageDO(o -> o.setTemplateId(null)); | ||
| 119 | + diyPageMapper.insert(standalonePage); | ||
| 85 | // 准备参数 | 120 | // 准备参数 |
| 86 | Long id = dbDiyTemplate.getId(); | 121 | Long id = dbDiyTemplate.getId(); |
| 87 | 122 | ||
| @@ -89,6 +124,11 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | @@ -89,6 +124,11 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | ||
| 89 | diyTemplateService.deleteDiyTemplate(id); | 124 | diyTemplateService.deleteDiyTemplate(id); |
| 90 | // 校验数据不存在了 | 125 | // 校验数据不存在了 |
| 91 | assertNull(diyTemplateMapper.selectById(id)); | 126 | assertNull(diyTemplateMapper.selectById(id)); |
| 127 | + assertEquals(0, diyPageMapper.selectListByTemplateId(id).size()); | ||
| 128 | + // 校验其它模板、其它模板下的页面、独立页面保留 | ||
| 129 | + assertNotNull(diyTemplateMapper.selectById(otherDiyTemplate.getId())); | ||
| 130 | + assertNotNull(diyPageMapper.selectById(otherTemplatePage.getId())); | ||
| 131 | + assertNotNull(diyPageMapper.selectById(standalonePage.getId())); | ||
| 92 | } | 132 | } |
| 93 | 133 | ||
| 94 | @Test | 134 | @Test |
| @@ -101,37 +141,65 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | @@ -101,37 +141,65 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | ||
| 101 | } | 141 | } |
| 102 | 142 | ||
| 103 | @Test | 143 | @Test |
| 104 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | 144 | + public void testDeleteDiyTemplate_used() { |
| 145 | + // mock 数据 | ||
| 146 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(true)); | ||
| 147 | + diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 | ||
| 148 | + DiyPageDO dbDiyPage = randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId())); | ||
| 149 | + diyPageMapper.insert(dbDiyPage); | ||
| 150 | + // 准备参数 | ||
| 151 | + Long id = dbDiyTemplate.getId(); | ||
| 152 | + | ||
| 153 | + // 调用, 并断言异常 | ||
| 154 | + assertServiceException(() -> diyTemplateService.deleteDiyTemplate(id), DIY_TEMPLATE_USED_CANNOT_DELETE); | ||
| 155 | + // 校验模板、页面都未删除 | ||
| 156 | + assertNotNull(diyTemplateMapper.selectById(id)); | ||
| 157 | + assertNotNull(diyPageMapper.selectById(dbDiyPage.getId())); | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + @Test | ||
| 161 | + public void testDeleteDiyTemplate_deletePageFailRollback() { | ||
| 162 | + // mock 数据 | ||
| 163 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(false)); | ||
| 164 | + diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 | ||
| 165 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()))); | ||
| 166 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()))); | ||
| 167 | + // 准备参数 | ||
| 168 | + Long id = dbDiyTemplate.getId(); | ||
| 169 | + // mock 方法(第 1 个页面真实删除,第 2 个页面删除失败) | ||
| 170 | + doCallRealMethod().doThrow(new IllegalStateException("删除页面失败")) | ||
| 171 | + .when(diyPageService).deleteDiyPage(anyLong()); | ||
| 172 | + | ||
| 173 | + // 调用, 并断言异常 | ||
| 174 | + assertThrows(IllegalStateException.class, () -> diyTemplateService.deleteDiyTemplate(id)); | ||
| 175 | + // 校验模板、页面都已回滚 | ||
| 176 | + assertNotNull(diyTemplateMapper.selectById(id)); | ||
| 177 | + assertEquals(2, diyPageMapper.selectListByTemplateId(id).size()); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + @Test | ||
| 181 | + // DONE 请修改 null 为需要的值,然后删除 @Disabled 注解 | ||
| 105 | public void testGetDiyTemplatePage() { | 182 | public void testGetDiyTemplatePage() { |
| 106 | // mock 数据 | 183 | // mock 数据 |
| 107 | DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到 | 184 | DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到 |
| 108 | - o.setName(null); | ||
| 109 | - o.setUsed(null); | ||
| 110 | - o.setUsedTime(null); | ||
| 111 | - o.setRemark(null); | ||
| 112 | - o.setPreviewPicUrls(null); | ||
| 113 | - o.setProperty(null); | ||
| 114 | - o.setCreateTime(null); | 185 | + o.setName("芋道商城"); |
| 186 | + o.setUsed(true); | ||
| 187 | + o.setUsedTime(buildTime(2023, 2, 10)); | ||
| 188 | + o.setCreateTime(buildTime(2023, 2, 10)); | ||
| 115 | }); | 189 | }); |
| 116 | diyTemplateMapper.insert(dbDiyTemplate); | 190 | diyTemplateMapper.insert(dbDiyTemplate); |
| 117 | // 测试 name 不匹配 | 191 | // 测试 name 不匹配 |
| 118 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName(null))); | 192 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName("土豆商城"))); |
| 119 | // 测试 used 不匹配 | 193 | // 测试 used 不匹配 |
| 120 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(null))); | 194 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(false))); |
| 121 | // 测试 usedTime 不匹配 | 195 | // 测试 usedTime 不匹配 |
| 122 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(null))); | ||
| 123 | - // 测试 remark 不匹配 | ||
| 124 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setRemark(null))); | ||
| 125 | - // 测试 previewPicUrls 不匹配 | ||
| 126 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setPreviewPicUrls(null))); | ||
| 127 | - // 测试 property 不匹配 | ||
| 128 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setProperty(null))); | 196 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(buildTime(2023, 3, 10)))); |
| 129 | // 测试 createTime 不匹配 | 197 | // 测试 createTime 不匹配 |
| 130 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(null))); | 198 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(buildTime(2023, 3, 10)))); |
| 131 | // 准备参数 | 199 | // 准备参数 |
| 132 | DiyTemplatePageReqVO reqVO = new DiyTemplatePageReqVO(); | 200 | DiyTemplatePageReqVO reqVO = new DiyTemplatePageReqVO(); |
| 133 | - reqVO.setName(null); | ||
| 134 | - reqVO.setUsed(null); | 201 | + reqVO.setName("芋道"); |
| 202 | + reqVO.setUsed(true); | ||
| 135 | reqVO.setUsedTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | 203 | reqVO.setUsedTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 136 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | 204 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 137 | 205 | ||
| @@ -144,33 +212,25 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | @@ -144,33 +212,25 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { | ||
| 144 | } | 212 | } |
| 145 | 213 | ||
| 146 | @Test | 214 | @Test |
| 147 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | ||
| 148 | - public void testGetDiyTemplateList() { | 215 | + // DONE 请修改 null 为需要的值,然后删除 @Disabled 注解(DiyTemplateService 无列表查询方法,原 testGetDiyTemplateList 改为测试 getUsedDiyTemplate) |
| 216 | + public void testGetUsedDiyTemplate() { | ||
| 149 | // mock 数据 | 217 | // mock 数据 |
| 150 | - DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到 | ||
| 151 | - o.setName(null); | ||
| 152 | - o.setUsed(null); | ||
| 153 | - o.setUsedTime(null); | ||
| 154 | - o.setRemark(null); | ||
| 155 | - o.setPreviewPicUrls(null); | ||
| 156 | - o.setProperty(null); | ||
| 157 | - o.setCreateTime(null); | ||
| 158 | - }); | 218 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(true)); |
| 159 | diyTemplateMapper.insert(dbDiyTemplate); | 219 | diyTemplateMapper.insert(dbDiyTemplate); |
| 160 | - // 测试 name 不匹配 | ||
| 161 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName(null))); | ||
| 162 | // 测试 used 不匹配 | 220 | // 测试 used 不匹配 |
| 163 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(null))); | ||
| 164 | - // 测试 usedTime 不匹配 | ||
| 165 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(null))); | ||
| 166 | - // 测试 remark 不匹配 | ||
| 167 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setRemark(null))); | ||
| 168 | - // 测试 previewPicUrls 不匹配 | ||
| 169 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setPreviewPicUrls(null))); | ||
| 170 | - // 测试 property 不匹配 | ||
| 171 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setProperty(null))); | ||
| 172 | - // 测试 createTime 不匹配 | ||
| 173 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(null))); | 221 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName("土豆商城").setUsed(false))); |
| 222 | + | ||
| 223 | + // 调用 | ||
| 224 | + DiyTemplateDO usedDiyTemplate = diyTemplateService.getUsedDiyTemplate(); | ||
| 225 | + // 断言 | ||
| 226 | + assertPojoEquals(dbDiyTemplate, usedDiyTemplate); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + // ========== 随机对象 ========== | ||
| 230 | + | ||
| 231 | + @SafeVarargs | ||
| 232 | + private static DiyPageDO randomDiyPageDO(Consumer<DiyPageDO>... consumers) { | ||
| 233 | + return randomPojo(DiyPageDO.class, consumers); | ||
| 174 | } | 234 | } |
| 175 | 235 | ||
| 176 | } | 236 | } |
yudao-module-mall/yudao-module-promotion/src/test/resources/sql/create_tables.sql
| @@ -277,8 +277,8 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_template" | @@ -277,8 +277,8 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_template" | ||
| 277 | ( | 277 | ( |
| 278 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | 278 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 279 | "name" varchar NOT NULL, | 279 | "name" varchar NOT NULL, |
| 280 | - "used" bit NOT NULL, | ||
| 281 | - "used_time" varchar, | 280 | + "used" bit NOT NULL DEFAULT FALSE, |
| 281 | + "used_time" datetime, | ||
| 282 | "remark" varchar, | 282 | "remark" varchar, |
| 283 | "preview_pic_urls" varchar, | 283 | "preview_pic_urls" varchar, |
| 284 | "property" varchar NOT NULL, | 284 | "property" varchar NOT NULL, |
| @@ -293,7 +293,7 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_template" | @@ -293,7 +293,7 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_template" | ||
| 293 | CREATE TABLE IF NOT EXISTS "promotion_diy_page" | 293 | CREATE TABLE IF NOT EXISTS "promotion_diy_page" |
| 294 | ( | 294 | ( |
| 295 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | 295 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 296 | - "template_id" bigint NOT NULL, | 296 | + "template_id" bigint, |
| 297 | "name" varchar NOT NULL, | 297 | "name" varchar NOT NULL, |
| 298 | "remark" varchar, | 298 | "remark" varchar, |
| 299 | "preview_pic_urls" varchar, | 299 | "preview_pic_urls" varchar, |
| @@ -303,6 +303,6 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_page" | @@ -303,6 +303,6 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_page" | ||
| 303 | "updater" varchar DEFAULT '', | 303 | "updater" varchar DEFAULT '', |
| 304 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | 304 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| 305 | "deleted" bit NOT NULL DEFAULT FALSE, | 305 | "deleted" bit NOT NULL DEFAULT FALSE, |
| 306 | - "tenant_id" bigint NOT NULL, | 306 | + "tenant_id" bigint NOT NULL DEFAULT 0, |
| 307 | PRIMARY KEY ("id") | 307 | PRIMARY KEY ("id") |
| 308 | ) COMMENT '装修页面'; | 308 | ) COMMENT '装修页面'; |
yudao-module-mall/yudao-module-trade/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceTest.java
| 1 | package cn.iocoder.yudao.module.trade.service.order; | 1 | package cn.iocoder.yudao.module.trade.service.order; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.IdUtil; | 3 | import cn.hutool.core.util.IdUtil; |
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 4 | +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; |
| 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 6 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | ||
| 6 | import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; | 7 | import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; |
| 7 | -import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | 8 | +import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO; |
| 8 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; | 9 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; |
| 9 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | 10 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; |
| 11 | +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; | ||
| 10 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | 12 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 11 | import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; | 13 | import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; |
| 12 | -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; | ||
| 13 | -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | ||
| 14 | -import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; | ||
| 15 | -import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; | ||
| 16 | -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO; | 14 | +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; |
| 15 | +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; | ||
| 17 | import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; | 16 | import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; |
| 17 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | ||
| 18 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; | ||
| 19 | +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO; | ||
| 20 | +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; | ||
| 18 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | 21 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 22 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | ||
| 19 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; | 23 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; |
| 20 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; | 24 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; |
| 21 | import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; | 25 | import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; |
| 22 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; | 26 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 27 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; | ||
| 23 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; | 28 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; |
| 24 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; | 29 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 25 | -import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; | 30 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; |
| 26 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; | 31 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 27 | -import cn.iocoder.yudao.module.trade.service.cart.CartServiceImpl; | 32 | +import cn.iocoder.yudao.module.trade.framework.order.core.aop.TradeOrderLogAspect; |
| 33 | +import cn.iocoder.yudao.module.trade.service.cart.CartService; | ||
| 28 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; | 34 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 29 | -import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressServiceImpl; | ||
| 30 | -import cn.iocoder.yudao.module.trade.service.message.TradeMessageServiceImpl; | 35 | +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService; |
| 36 | +import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; | ||
| 31 | import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler; | 37 | import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler; |
| 32 | -import cn.iocoder.yudao.module.trade.service.price.TradePriceServiceImpl; | ||
| 33 | -import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator; | 38 | +import cn.iocoder.yudao.module.trade.service.price.TradePriceService; |
| 39 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | ||
| 34 | import jakarta.annotation.Resource; | 40 | import jakarta.annotation.Resource; |
| 41 | +import org.junit.jupiter.api.AfterEach; | ||
| 35 | import org.junit.jupiter.api.BeforeEach; | 42 | import org.junit.jupiter.api.BeforeEach; |
| 36 | -import org.junit.jupiter.api.Disabled; | ||
| 37 | import org.junit.jupiter.api.Test; | 43 | import org.junit.jupiter.api.Test; |
| 38 | import org.springframework.context.annotation.Import; | 44 | import org.springframework.context.annotation.Import; |
| 45 | +import org.springframework.mock.web.MockHttpServletRequest; | ||
| 39 | import org.springframework.test.context.bean.override.mockito.MockitoBean; | 46 | import org.springframework.test.context.bean.override.mockito.MockitoBean; |
| 47 | +import org.springframework.test.util.ReflectionTestUtils; | ||
| 48 | +import org.springframework.web.context.request.RequestContextHolder; | ||
| 49 | +import org.springframework.web.context.request.ServletRequestAttributes; | ||
| 40 | 50 | ||
| 41 | import java.time.Duration; | 51 | import java.time.Duration; |
| 52 | +import java.util.Arrays; | ||
| 53 | +import java.util.List; | ||
| 42 | 54 | ||
| 55 | +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||
| 43 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 56 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 44 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 57 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 45 | import static org.junit.jupiter.api.Assertions.*; | 58 | import static org.junit.jupiter.api.Assertions.*; |
| 46 | -import static org.mockito.ArgumentMatchers.anyString; | ||
| 47 | -import static org.mockito.ArgumentMatchers.eq; | 59 | +import static org.mockito.ArgumentMatchers.*; |
| 60 | +import static org.mockito.Mockito.verify; | ||
| 48 | import static org.mockito.Mockito.when; | 61 | import static org.mockito.Mockito.when; |
| 49 | 62 | ||
| 50 | /** | 63 | /** |
| @@ -53,10 +66,8 @@ import static org.mockito.Mockito.when; | @@ -53,10 +66,8 @@ import static org.mockito.Mockito.when; | ||
| 53 | * @author LeeYan9 | 66 | * @author LeeYan9 |
| 54 | * @since 2022-09-07 | 67 | * @since 2022-09-07 |
| 55 | */ | 68 | */ |
| 56 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | ||
| 57 | -@Import({TradeOrderUpdateServiceImpl.class, TradeOrderConfig.class, CartServiceImpl.class, TradePriceServiceImpl.class, | ||
| 58 | - DeliveryExpressServiceImpl.class, TradeMessageServiceImpl.class | ||
| 59 | -}) | 69 | +// DONE 芋艿:后续 fix 补充的单测 |
| 70 | +@Import(TradeOrderUpdateServiceImpl.class) | ||
| 60 | public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | 71 | public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 61 | 72 | ||
| 62 | @Resource | 73 | @Resource |
| @@ -68,34 +79,33 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -68,34 +79,33 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 68 | private TradeOrderItemMapper tradeOrderItemMapper; | 79 | private TradeOrderItemMapper tradeOrderItemMapper; |
| 69 | 80 | ||
| 70 | @MockitoBean | 81 | @MockitoBean |
| 71 | - private MemberUserApi memberUserApi; | 82 | + private TradeNoRedisDAO tradeNoRedisDAO; |
| 72 | @MockitoBean | 83 | @MockitoBean |
| 73 | - private ProductSpuApi productSpuApi; | 84 | + private TradeOrderHandler tradeOrderHandler; |
| 74 | @MockitoBean | 85 | @MockitoBean |
| 75 | - private ProductSkuApi productSkuApi; | 86 | + private CartService cartService; |
| 76 | @MockitoBean | 87 | @MockitoBean |
| 77 | - private ProductCommentApi productCommentApi; | ||
| 78 | - // @MockitoBean | ||
| 79 | -// private PriceApi priceApi; | 88 | + private TradePriceService tradePriceService; |
| 80 | @MockitoBean | 89 | @MockitoBean |
| 81 | - private PayOrderApi payOrderApi; | 90 | + private DeliveryExpressService deliveryExpressService; |
| 82 | @MockitoBean | 91 | @MockitoBean |
| 83 | - private MemberAddressApi addressApi; | 92 | + private TradeMessageService tradeMessageService; |
| 84 | @MockitoBean | 93 | @MockitoBean |
| 85 | - private CouponApi couponApi; | ||
| 86 | - | 94 | + private DeliveryPickUpStoreService pickUpStoreService; |
| 87 | @MockitoBean | 95 | @MockitoBean |
| 88 | - private TradeOrderProperties tradeOrderProperties; | 96 | + private PayOrderApi payOrderApi; |
| 89 | @MockitoBean | 97 | @MockitoBean |
| 90 | - private TradeNoRedisDAO tradeNoRedisDAO; | 98 | + private MemberAddressApi addressApi; |
| 91 | @MockitoBean | 99 | @MockitoBean |
| 92 | - private TradeOrderHandler tradeOrderHandler; | 100 | + private ProductCommentApi productCommentApi; |
| 93 | @MockitoBean | 101 | @MockitoBean |
| 94 | - private TradePriceCalculator tradePriceCalculator; | 102 | + private SocialClientApi socialClientApi; |
| 95 | @MockitoBean | 103 | @MockitoBean |
| 96 | - private NotifyMessageSendApi notifyMessageSendApi; | 104 | + private PayRefundApi payRefundApi; |
| 97 | @MockitoBean | 105 | @MockitoBean |
| 98 | - private DeliveryExpressService deliveryExpressService; | 106 | + private CombinationRecordApi combinationRecordApi; |
| 107 | + @MockitoBean | ||
| 108 | + private TradeOrderProperties tradeOrderProperties; | ||
| 99 | 109 | ||
| 100 | @BeforeEach | 110 | @BeforeEach |
| 101 | public void setUp() { | 111 | public void setUp() { |
| @@ -104,159 +114,134 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -104,159 +114,134 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 104 | when(tradeNoRedisDAO.generate(anyString())).thenReturn(IdUtil.randomUUID()); | 114 | when(tradeNoRedisDAO.generate(anyString())).thenReturn(IdUtil.randomUUID()); |
| 105 | } | 115 | } |
| 106 | 116 | ||
| 107 | -// @Test | ||
| 108 | -// public void testCreateTradeOrder_success() { | ||
| 109 | -// // 准备参数 | ||
| 110 | -// Long userId = 100L; | ||
| 111 | -// String userIp = "127.0.0.1"; | ||
| 112 | -//// AppTradeOrderCreateReqVO reqVO = new AppTradeOrderCreateReqVO() | ||
| 113 | -//// .setAddressId(10L).setCouponId(101L).setRemark("我是备注").setFromCart(true) | ||
| 114 | -//// .setItems(Arrays.asList(new AppTradeOrderCreateReqVO.Item().setSkuId(1L).setCount(3), | ||
| 115 | -//// new AppTradeOrderCreateReqVO.Item().setSkuId(2L).setCount(4))); | ||
| 116 | -// AppTradeOrderCreateReqVO reqVO = null; | ||
| 117 | -// // TODO 芋艿:重新高下 | ||
| 118 | -// // mock 方法(商品 SKU 检查) | ||
| 119 | -// ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L) | ||
| 120 | -// .setPrice(50).setStock(100) | ||
| 121 | -// .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(111L).setValueId(222L)))); | ||
| 122 | -// ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L) | ||
| 123 | -// .setPrice(20).setStock(50)) | ||
| 124 | -// .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(333L).setValueId(444L))); | ||
| 125 | -// when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02)); | ||
| 126 | -// // mock 方法(商品 SPU 检查) | ||
| 127 | -// ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L) | ||
| 128 | -// .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()).setName("商品 1")); | ||
| 129 | -// ProductSpuRespDTO spu02 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(21L) | ||
| 130 | -// .setStatus(ProductSpuStatusEnum.ENABLE.getStatus())); | ||
| 131 | -// when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02)); | ||
| 132 | -// // mock 方法(用户收件地址的校验) | ||
| 133 | -// MemberAddressRespDTO addressRespDTO = new MemberAddressRespDTO().setId(10L).setUserId(userId).setName("芋艿") | ||
| 134 | -// .setMobile("15601691300").setAreaId(3306).setDetailAddress("土豆村"); | ||
| 135 | -// when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO); | ||
| 136 | -// // mock 方法(价格计算) | ||
| 137 | -// PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem() | ||
| 138 | -// .setSpuId(11L).setSkuId(1L).setCount(3).setOriginalPrice(150).setOriginalUnitPrice(50) | ||
| 139 | -// .setDiscountPrice(20).setPayPrice(130).setOrderPartPrice(7).setOrderDividePrice(35); | ||
| 140 | -// PriceCalculateRespDTO.OrderItem priceOrderItem02 = new PriceCalculateRespDTO.OrderItem() | ||
| 141 | -// .setSpuId(21L).setSkuId(2L).setCount(4).setOriginalPrice(80).setOriginalUnitPrice(20) | ||
| 142 | -// .setDiscountPrice(40).setPayPrice(40).setOrderPartPrice(15).setOrderDividePrice(25); | ||
| 143 | -// PriceCalculateRespDTO.Order priceOrder = new PriceCalculateRespDTO.Order() | ||
| 144 | -// .setTotalPrice(230).setDiscountPrice(0).setCouponPrice(30) | ||
| 145 | -// .setPointPrice(10).setDeliveryPrice(20).setPayPrice(80).setCouponId(101L).setCouponPrice(30) | ||
| 146 | -// .setItems(Arrays.asList(priceOrderItem01, priceOrderItem02)); | ||
| 147 | -// when(priceApi.calculatePrice(argThat(priceCalculateReqDTO -> { | ||
| 148 | -// assertEquals(priceCalculateReqDTO.getUserId(), 100L); | ||
| 149 | -// assertEquals(priceCalculateReqDTO.getCouponId(), 101L); | ||
| 150 | -// assertEquals(priceCalculateReqDTO.getItems().get(0).getSkuId(), 1L); | ||
| 151 | -// assertEquals(priceCalculateReqDTO.getItems().get(0).getCount(), 3); | ||
| 152 | -// assertEquals(priceCalculateReqDTO.getItems().get(1).getSkuId(), 2L); | ||
| 153 | -// assertEquals(priceCalculateReqDTO.getItems().get(1).getCount(), 4); | ||
| 154 | -// return true; | ||
| 155 | -// }))).thenReturn(new PriceCalculateRespDTO().setOrder(priceOrder)); | ||
| 156 | -// // mock 方法(创建支付单) | ||
| 157 | -// when(payOrderApi.createOrder(argThat(createReqDTO -> { | ||
| 158 | -// assertEquals(createReqDTO.getAppId(), 888L); | ||
| 159 | -// assertEquals(createReqDTO.getUserIp(), userIp); | ||
| 160 | -// assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 | ||
| 161 | -// assertEquals(createReqDTO.getSubject(), "商品 1 等多件"); | ||
| 162 | -// assertNull(createReqDTO.getBody()); | ||
| 163 | -// assertEquals(createReqDTO.getPrice(), 80); | ||
| 164 | -// assertNotNull(createReqDTO.getExpireTime()); | ||
| 165 | -// return true; | ||
| 166 | -// }))).thenReturn(1000L); | ||
| 167 | -// | ||
| 168 | -// // 调用方法 | ||
| 169 | -// TradeOrderDO order = tradeOrderUpdateService.createOrder(userId, userIp, reqVO, null); | ||
| 170 | -// // 断言 TradeOrderDO 订单 | ||
| 171 | -// List<TradeOrderDO> tradeOrderDOs = tradeOrderMapper.selectList(); | ||
| 172 | -// assertEquals(tradeOrderDOs.size(), 1); | ||
| 173 | -// TradeOrderDO tradeOrderDO = tradeOrderDOs.get(0); | ||
| 174 | -// assertEquals(tradeOrderDO.getId(), order.getId()); | ||
| 175 | -// assertNotNull(tradeOrderDO.getNo()); | ||
| 176 | -// assertEquals(tradeOrderDO.getType(), TradeOrderTypeEnum.NORMAL.getType()); | ||
| 177 | -// assertEquals(tradeOrderDO.getTerminal(), TerminalEnum.H5.getTerminal()); | ||
| 178 | -// assertEquals(tradeOrderDO.getUserId(), userId); | ||
| 179 | -// assertEquals(tradeOrderDO.getUserIp(), userIp); | ||
| 180 | -// assertEquals(tradeOrderDO.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus()); | ||
| 181 | -// assertEquals(tradeOrderDO.getProductCount(), 7); | ||
| 182 | -// assertNull(tradeOrderDO.getFinishTime()); | ||
| 183 | -// assertNull(tradeOrderDO.getCancelTime()); | ||
| 184 | -// assertNull(tradeOrderDO.getCancelType()); | ||
| 185 | -// assertEquals(tradeOrderDO.getUserRemark(), "我是备注"); | ||
| 186 | -// assertNull(tradeOrderDO.getRemark()); | ||
| 187 | -// assertFalse(tradeOrderDO.getPayStatus()); | ||
| 188 | -// assertNull(tradeOrderDO.getPayTime()); | ||
| 189 | -// assertEquals(tradeOrderDO.getTotalPrice(), 230); | ||
| 190 | -// assertEquals(tradeOrderDO.getDiscountPrice(), 0); | ||
| 191 | -// assertEquals(tradeOrderDO.getAdjustPrice(), 0); | ||
| 192 | -// assertEquals(tradeOrderDO.getPayPrice(), 80); | ||
| 193 | -// assertEquals(tradeOrderDO.getPayOrderId(), 1000L); | ||
| 194 | -// assertNull(tradeOrderDO.getPayChannelCode()); | ||
| 195 | -// assertNull(tradeOrderDO.getLogisticsId()); | ||
| 196 | -// assertNull(tradeOrderDO.getDeliveryTime()); | ||
| 197 | -// assertNull(tradeOrderDO.getReceiveTime()); | ||
| 198 | -// assertEquals(tradeOrderDO.getReceiverName(), "芋艿"); | ||
| 199 | -// assertEquals(tradeOrderDO.getReceiverMobile(), "15601691300"); | ||
| 200 | -// assertEquals(tradeOrderDO.getReceiverAreaId(), 3306); | ||
| 201 | -// assertEquals(tradeOrderDO.getReceiverDetailAddress(), "土豆村"); | ||
| 202 | -// assertEquals(tradeOrderDO.getRefundStatus(), TradeOrderRefundStatusEnum.NONE.getStatus()); | ||
| 203 | -// assertEquals(tradeOrderDO.getRefundPrice(), 0); | ||
| 204 | -// assertEquals(tradeOrderDO.getCouponPrice(), 30); | ||
| 205 | -// assertEquals(tradeOrderDO.getPointPrice(), 10); | ||
| 206 | -// // 断言 TradeOrderItemDO 订单(第 1 个) | ||
| 207 | -// List<TradeOrderItemDO> tradeOrderItemDOs = tradeOrderItemMapper.selectList(); | ||
| 208 | -// assertEquals(tradeOrderItemDOs.size(), 2); | ||
| 209 | -// TradeOrderItemDO tradeOrderItemDO01 = tradeOrderItemDOs.get(0); | ||
| 210 | -// assertNotNull(tradeOrderItemDO01.getId()); | ||
| 211 | -// assertEquals(tradeOrderItemDO01.getUserId(), userId); | ||
| 212 | -// assertEquals(tradeOrderItemDO01.getOrderId(), order.getId()); | ||
| 213 | -// assertEquals(tradeOrderItemDO01.getSpuId(), 11L); | ||
| 214 | -// assertEquals(tradeOrderItemDO01.getSkuId(), 1L); | ||
| 215 | -// assertEquals(tradeOrderItemDO01.getProperties().size(), 1); | ||
| 216 | -// assertEquals(tradeOrderItemDO01.getProperties().get(0).getPropertyId(), 111L); | ||
| 217 | -// assertEquals(tradeOrderItemDO01.getProperties().get(0).getValueId(), 222L); | ||
| 218 | -// //assertEquals(tradeOrderItemDO01.getSpuName(), sku01.getSpuName()); TODO 找不到spuName | ||
| 219 | -// assertEquals(tradeOrderItemDO01.getPicUrl(), sku01.getPicUrl()); | ||
| 220 | -// assertEquals(tradeOrderItemDO01.getCount(), 3); | ||
| 221 | -//// assertEquals(tradeOrderItemDO01.getOriginalPrice(), 150); | ||
| 222 | -// assertEquals(tradeOrderItemDO01.getPrice(), 50); | ||
| 223 | -// assertEquals(tradeOrderItemDO01.getDiscountPrice(), 20); | ||
| 224 | -// assertEquals(tradeOrderItemDO01.getPayPrice(), 130); | ||
| 225 | -// assertEquals(tradeOrderItemDO01.getAfterSaleStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); | ||
| 226 | -// // 断言 TradeOrderItemDO 订单(第 2 个) | ||
| 227 | -// TradeOrderItemDO tradeOrderItemDO02 = tradeOrderItemDOs.get(1); | ||
| 228 | -// assertNotNull(tradeOrderItemDO02.getId()); | ||
| 229 | -// assertEquals(tradeOrderItemDO02.getUserId(), userId); | ||
| 230 | -// assertEquals(tradeOrderItemDO02.getOrderId(), order.getId()); | ||
| 231 | -// assertEquals(tradeOrderItemDO02.getSpuId(), 21L); | ||
| 232 | -// assertEquals(tradeOrderItemDO02.getSkuId(), 2L); | ||
| 233 | -// assertEquals(tradeOrderItemDO02.getProperties().size(), 1); | ||
| 234 | -// assertEquals(tradeOrderItemDO02.getProperties().get(0).getPropertyId(), 333L); | ||
| 235 | -// assertEquals(tradeOrderItemDO02.getProperties().get(0).getValueId(), 444L); | ||
| 236 | -// //assertEquals(tradeOrderItemDO02.getSpuName(), sku02.getSpuName()); TODO 找不到spuName | ||
| 237 | -// assertEquals(tradeOrderItemDO02.getPicUrl(), sku02.getPicUrl()); | ||
| 238 | -// assertEquals(tradeOrderItemDO02.getCount(), 4); | ||
| 239 | -//// assertEquals(tradeOrderItemDO02.getOriginalPrice(), 80); | ||
| 240 | -// assertEquals(tradeOrderItemDO02.getPrice(), 20); | ||
| 241 | -// assertEquals(tradeOrderItemDO02.getDiscountPrice(), 40); | ||
| 242 | -// assertEquals(tradeOrderItemDO02.getPayPrice(), 40); | ||
| 243 | -// assertEquals(tradeOrderItemDO02.getAfterSaleStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); | ||
| 244 | -// // 校验调用 | ||
| 245 | -// verify(productSkuApi).updateSkuStock(argThat(updateStockReqDTO -> { | ||
| 246 | -// assertEquals(updateStockReqDTO.getItems().size(), 2); | ||
| 247 | -// assertEquals(updateStockReqDTO.getItems().get(0).getId(), 1L); | ||
| 248 | -// assertEquals(updateStockReqDTO.getItems().get(0).getIncrCount(), 3); | ||
| 249 | -// assertEquals(updateStockReqDTO.getItems().get(1).getId(), 2L); | ||
| 250 | -// assertEquals(updateStockReqDTO.getItems().get(1).getIncrCount(), 4); | ||
| 251 | -// return true; | ||
| 252 | -// })); | ||
| 253 | -// verify(couponApi).useCoupon(argThat(reqDTO -> { | ||
| 254 | -// assertEquals(reqDTO.getId(), reqVO.getCouponId()); | ||
| 255 | -// assertEquals(reqDTO.getUserId(), userId); | ||
| 256 | -// assertEquals(reqDTO.getOrderId(), order.getId()); | ||
| 257 | -// return true; | ||
| 258 | -// })); | ||
| 259 | -// } | 117 | + @AfterEach |
| 118 | + public void tearDown() { | ||
| 119 | + RequestContextHolder.resetRequestAttributes(); | ||
| 120 | + // 清理订单日志的上下文,单测没有 TradeOrderLogAspect 切面清理 | ||
| 121 | + ReflectionTestUtils.invokeMethod(TradeOrderLogAspect.class, "clear"); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @Test | ||
| 125 | + public void testCreateOrder_success() { | ||
| 126 | + // DONE 芋艿:重新高下 | ||
| 127 | + // 准备参数 | ||
| 128 | + Long userId = 100L; | ||
| 129 | + String userIp = "127.0.0.1"; | ||
| 130 | + AppTradeOrderCreateReqVO reqVO = new AppTradeOrderCreateReqVO(); | ||
| 131 | + reqVO.setRemark("我是备注"); | ||
| 132 | + reqVO.setItems(Arrays.asList(new AppTradeOrderSettlementReqVO.Item().setCartId(1L), | ||
| 133 | + new AppTradeOrderSettlementReqVO.Item().setCartId(2L))); | ||
| 134 | + reqVO.setCouponId(101L); | ||
| 135 | + reqVO.setPointStatus(false); | ||
| 136 | + reqVO.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()); | ||
| 137 | + reqVO.setAddressId(10L); | ||
| 138 | + // mock 请求(用户 IP、终端) | ||
| 139 | + MockHttpServletRequest request = new MockHttpServletRequest(); | ||
| 140 | + request.setRemoteAddr(userIp); | ||
| 141 | + request.addHeader(WebFrameworkUtils.HEADER_TERMINAL, TerminalEnum.H5.getTerminal()); | ||
| 142 | + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); | ||
| 143 | + // mock 方法(购物车) | ||
| 144 | + when(cartService.getCartList(eq(userId), eq(asSet(1L, 2L)))).thenReturn(Arrays.asList( | ||
| 145 | + new CartDO().setId(1L).setUserId(userId).setSpuId(11L).setSkuId(1L).setCount(3).setSelected(true), | ||
| 146 | + new CartDO().setId(2L).setUserId(userId).setSpuId(21L).setSkuId(2L).setCount(4).setSelected(true))); | ||
| 147 | + // mock 方法(价格计算) | ||
| 148 | + TradePriceCalculateRespBO.OrderItem priceOrderItem01 = new TradePriceCalculateRespBO.OrderItem() | ||
| 149 | + .setSpuId(11L).setSkuId(1L).setCount(3).setCartId(1L).setSelected(true).setSpuName("商品 1") | ||
| 150 | + .setPrice(50).setDiscountPrice(20).setDeliveryPrice(10).setCouponPrice(10).setPointPrice(5) | ||
| 151 | + .setVipPrice(0).setPayPrice(105); | ||
| 152 | + TradePriceCalculateRespBO.OrderItem priceOrderItem02 = new TradePriceCalculateRespBO.OrderItem() | ||
| 153 | + .setSpuId(21L).setSkuId(2L).setCount(4).setCartId(2L).setSelected(true).setSpuName("商品 2") | ||
| 154 | + .setPrice(20).setDiscountPrice(10).setDeliveryPrice(10).setCouponPrice(20).setPointPrice(5) | ||
| 155 | + .setVipPrice(0).setPayPrice(55); | ||
| 156 | + TradePriceCalculateRespBO.Price price = new TradePriceCalculateRespBO.Price() | ||
| 157 | + .setTotalPrice(230).setDiscountPrice(30).setDeliveryPrice(20).setCouponPrice(30) | ||
| 158 | + .setPointPrice(10).setVipPrice(0).setPayPrice(160); | ||
| 159 | + when(tradePriceService.calculateOrderPrice(argThat(calculateReqBO -> { | ||
| 160 | + assertEquals(userId, calculateReqBO.getUserId()); | ||
| 161 | + assertEquals(101L, calculateReqBO.getCouponId()); | ||
| 162 | + assertEquals(2, calculateReqBO.getItems().size()); | ||
| 163 | + assertEquals(1L, calculateReqBO.getItems().get(0).getSkuId()); | ||
| 164 | + assertEquals(3, calculateReqBO.getItems().get(0).getCount()); | ||
| 165 | + assertEquals(2L, calculateReqBO.getItems().get(1).getSkuId()); | ||
| 166 | + assertEquals(4, calculateReqBO.getItems().get(1).getCount()); | ||
| 167 | + return true; | ||
| 168 | + }))).thenReturn(new TradePriceCalculateRespBO().setType(TradeOrderTypeEnum.NORMAL.getType()) | ||
| 169 | + .setPrice(price).setItems(Arrays.asList(priceOrderItem01, priceOrderItem02))); | ||
| 170 | + // mock 方法(用户收件地址) | ||
| 171 | + when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(new MemberAddressRespDTO().setId(10L) | ||
| 172 | + .setUserId(userId).setName("芋艿").setMobile("15601691300").setAreaId(3306).setDetailAddress("土豆村")); | ||
| 173 | + // mock 方法(创建支付单) | ||
| 174 | + when(payOrderApi.createOrder(argThat(createReqDTO -> { | ||
| 175 | + assertEquals("mall", createReqDTO.getAppKey()); | ||
| 176 | + assertEquals(userIp, createReqDTO.getUserIp()); | ||
| 177 | + assertEquals(userId, createReqDTO.getUserId()); | ||
| 178 | + assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 | ||
| 179 | + assertEquals("商品 1", createReqDTO.getSubject()); | ||
| 180 | + assertEquals(160, createReqDTO.getPrice()); | ||
| 181 | + assertNotNull(createReqDTO.getExpireTime()); | ||
| 182 | + return true; | ||
| 183 | + }))).thenReturn(1000L); | ||
| 184 | + | ||
| 185 | + // 调用 | ||
| 186 | + TradeOrderDO order = tradeOrderUpdateService.createOrder(userId, reqVO); | ||
| 187 | + // 断言 TradeOrderDO 订单 | ||
| 188 | + List<TradeOrderDO> tradeOrderDOs = tradeOrderMapper.selectList(); | ||
| 189 | + assertEquals(1, tradeOrderDOs.size()); | ||
| 190 | + TradeOrderDO tradeOrderDO = tradeOrderDOs.get(0); | ||
| 191 | + assertEquals(order.getId(), tradeOrderDO.getId()); | ||
| 192 | + assertNotNull(tradeOrderDO.getNo()); | ||
| 193 | + assertEquals(TradeOrderTypeEnum.NORMAL.getType(), tradeOrderDO.getType()); | ||
| 194 | + assertEquals(TerminalEnum.H5.getTerminal(), tradeOrderDO.getTerminal()); | ||
| 195 | + assertEquals(userId, tradeOrderDO.getUserId()); | ||
| 196 | + assertEquals(userIp, tradeOrderDO.getUserIp()); | ||
| 197 | + assertEquals(TradeOrderStatusEnum.UNPAID.getStatus(), tradeOrderDO.getStatus()); | ||
| 198 | + assertEquals(7, tradeOrderDO.getProductCount()); | ||
| 199 | + assertEquals("我是备注", tradeOrderDO.getUserRemark()); | ||
| 200 | + assertFalse(tradeOrderDO.getPayStatus()); | ||
| 201 | + assertNull(tradeOrderDO.getPayTime()); | ||
| 202 | + assertEquals(1000L, tradeOrderDO.getPayOrderId()); | ||
| 203 | + assertEquals(230, tradeOrderDO.getTotalPrice()); | ||
| 204 | + assertEquals(30, tradeOrderDO.getDiscountPrice()); | ||
| 205 | + assertEquals(20, tradeOrderDO.getDeliveryPrice()); | ||
| 206 | + assertEquals(0, tradeOrderDO.getAdjustPrice()); | ||
| 207 | + assertEquals(160, tradeOrderDO.getPayPrice()); | ||
| 208 | + assertEquals(DeliveryTypeEnum.EXPRESS.getType(), tradeOrderDO.getDeliveryType()); | ||
| 209 | + assertEquals("芋艿", tradeOrderDO.getReceiverName()); | ||
| 210 | + assertEquals("15601691300", tradeOrderDO.getReceiverMobile()); | ||
| 211 | + assertEquals(3306, tradeOrderDO.getReceiverAreaId()); | ||
| 212 | + assertEquals("土豆村", tradeOrderDO.getReceiverDetailAddress()); | ||
| 213 | + assertEquals(TradeOrderRefundStatusEnum.NONE.getStatus(), tradeOrderDO.getRefundStatus()); | ||
| 214 | + assertEquals(0, tradeOrderDO.getRefundPrice()); | ||
| 215 | + assertEquals(101L, tradeOrderDO.getCouponId()); | ||
| 216 | + assertEquals(30, tradeOrderDO.getCouponPrice()); | ||
| 217 | + assertEquals(10, tradeOrderDO.getPointPrice()); | ||
| 218 | + // 断言 TradeOrderItemDO 订单项 | ||
| 219 | + List<TradeOrderItemDO> tradeOrderItemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId()); | ||
| 220 | + assertEquals(2, tradeOrderItemDOs.size()); | ||
| 221 | + assertOrderItem(tradeOrderItemDOs.get(0), order, priceOrderItem01); | ||
| 222 | + assertOrderItem(tradeOrderItemDOs.get(1), order, priceOrderItem02); | ||
| 223 | + // 断言调用 | ||
| 224 | + verify(tradeOrderHandler).beforeOrderCreate(any(), any()); | ||
| 225 | + verify(tradeOrderHandler).afterOrderCreate(any(), any()); | ||
| 226 | + verify(cartService).deleteCart(eq(userId), eq(asSet(1L, 2L))); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + private static void assertOrderItem(TradeOrderItemDO orderItem, TradeOrderDO order, | ||
| 230 | + TradePriceCalculateRespBO.OrderItem priceOrderItem) { | ||
| 231 | + assertNotNull(orderItem.getId()); | ||
| 232 | + assertEquals(order.getUserId(), orderItem.getUserId()); | ||
| 233 | + assertEquals(order.getId(), orderItem.getOrderId()); | ||
| 234 | + assertEquals(priceOrderItem.getSpuId(), orderItem.getSpuId()); | ||
| 235 | + assertEquals(priceOrderItem.getSpuName(), orderItem.getSpuName()); // DONE 找不到spuName | ||
| 236 | + assertEquals(priceOrderItem.getSkuId(), orderItem.getSkuId()); | ||
| 237 | + assertEquals(priceOrderItem.getCartId(), orderItem.getCartId()); | ||
| 238 | + assertEquals(priceOrderItem.getCount(), orderItem.getCount()); | ||
| 239 | + assertEquals(priceOrderItem.getPrice(), orderItem.getPrice()); | ||
| 240 | + assertEquals(priceOrderItem.getDiscountPrice(), orderItem.getDiscountPrice()); | ||
| 241 | + assertEquals(priceOrderItem.getPayPrice(), orderItem.getPayPrice()); | ||
| 242 | + assertEquals(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), orderItem.getAfterSaleStatus()); | ||
| 243 | + assertFalse(orderItem.getCommentStatus()); | ||
| 244 | + } | ||
| 260 | 245 | ||
| 261 | @Test | 246 | @Test |
| 262 | public void testUpdateOrderPaid() { | 247 | public void testUpdateOrderPaid() { |
| @@ -282,6 +267,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -282,6 +267,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 282 | assertTrue(dbOrder.getPayStatus()); | 267 | assertTrue(dbOrder.getPayStatus()); |
| 283 | assertNotNull(dbOrder.getPayTime()); | 268 | assertNotNull(dbOrder.getPayTime()); |
| 284 | assertEquals(dbOrder.getPayChannelCode(), "wx_pub"); | 269 | assertEquals(dbOrder.getPayChannelCode(), "wx_pub"); |
| 270 | + verify(tradeOrderHandler).afterPayOrder(argThat(o -> o.getId().equals(id)), anyList()); | ||
| 285 | } | 271 | } |
| 286 | 272 | ||
| 287 | @Test | 273 | @Test |
| @@ -294,15 +280,12 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -294,15 +280,12 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 294 | o.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()); | 280 | o.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()); |
| 295 | }); | 281 | }); |
| 296 | tradeOrderMapper.insert(order); | 282 | tradeOrderMapper.insert(order); |
| 297 | - | ||
| 298 | - DeliveryExpressCreateReqVO expressCreateReqVO = new DeliveryExpressCreateReqVO(); | ||
| 299 | - expressCreateReqVO.setCode("code").setName("Name").setLogo("logo").setSort(0).setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 300 | - Long deliveryExpressId = deliveryExpressService.createDeliveryExpress(expressCreateReqVO); | ||
| 301 | // 准备参数 | 283 | // 准备参数 |
| 302 | TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L) | 284 | TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L) |
| 303 | - .setLogisticsId(deliveryExpressId).setLogisticsNo("100"); | ||
| 304 | - | ||
| 305 | - // mock 方法(支付单) | 285 | + .setLogisticsId(20L).setLogisticsNo("100"); |
| 286 | + // mock 方法(物流公司) | ||
| 287 | + when(deliveryExpressService.validateDeliveryExpress(eq(20L))).thenReturn( | ||
| 288 | + randomPojo(DeliveryExpressDO.class, o -> o.setId(20L))); | ||
| 306 | 289 | ||
| 307 | // 调用 | 290 | // 调用 |
| 308 | tradeOrderUpdateService.deliveryOrder(deliveryReqVO); | 291 | tradeOrderUpdateService.deliveryOrder(deliveryReqVO); |
| @@ -311,6 +294,9 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -311,6 +294,9 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 311 | assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus()); | 294 | assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus()); |
| 312 | assertPojoEquals(dbOrder, deliveryReqVO); | 295 | assertPojoEquals(dbOrder, deliveryReqVO); |
| 313 | assertNotNull(dbOrder.getDeliveryTime()); | 296 | assertNotNull(dbOrder.getDeliveryTime()); |
| 297 | + // 断言调用(站内信) | ||
| 298 | + verify(tradeMessageService).sendMessageWhenDeliveryOrder(argThat(reqBO -> | ||
| 299 | + reqBO.getOrderId().equals(1L) && reqBO.getUserId().equals(order.getUserId()))); | ||
| 314 | } | 300 | } |
| 315 | 301 | ||
| 316 | @Test | 302 | @Test |
| @@ -324,7 +310,6 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -324,7 +310,6 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 324 | // 准备参数 | 310 | // 准备参数 |
| 325 | Long id = 1L; | 311 | Long id = 1L; |
| 326 | Long userId = 10L; | 312 | Long userId = 10L; |
| 327 | - // mock 方法(支付单) | ||
| 328 | 313 | ||
| 329 | // 调用 | 314 | // 调用 |
| 330 | tradeOrderUpdateService.receiveOrderByMember(userId, id); | 315 | tradeOrderUpdateService.receiveOrderByMember(userId, id); |
| @@ -332,6 +317,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | @@ -332,6 +317,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { | ||
| 332 | TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L); | 317 | TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L); |
| 333 | assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); | 318 | assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); |
| 334 | assertNotNull(dbOrder.getReceiveTime()); | 319 | assertNotNull(dbOrder.getReceiveTime()); |
| 320 | + verify(tradeOrderHandler).afterReceiveOrder(argThat(o -> o.getId().equals(id))); | ||
| 335 | } | 321 | } |
| 336 | 322 | ||
| 337 | } | 323 | } |
yudao-module-mall/yudao-module-trade/src/test/resources/sql/create_tables.sql
| @@ -49,12 +49,15 @@ CREATE TABLE IF NOT EXISTS "trade_order" | @@ -49,12 +49,15 @@ CREATE TABLE IF NOT EXISTS "trade_order" | ||
| 49 | "refund_point" int NULL, | 49 | "refund_point" int NULL, |
| 50 | "vip_price" int NULL, | 50 | "vip_price" int NULL, |
| 51 | "give_coupons_map" varchar NULL, | 51 | "give_coupons_map" varchar NULL, |
| 52 | + "give_coupon_template_counts" varchar NULL, | ||
| 53 | + "give_coupon_ids" varchar NULL, | ||
| 52 | "seckill_activity_id" long NULL, | 54 | "seckill_activity_id" long NULL, |
| 53 | "bargain_activity_id" long NULL, | 55 | "bargain_activity_id" long NULL, |
| 54 | "bargain_record_id" long NULL, | 56 | "bargain_record_id" long NULL, |
| 55 | "combination_activity_id" long NULL, | 57 | "combination_activity_id" long NULL, |
| 56 | "combination_head_id" long NULL, | 58 | "combination_head_id" long NULL, |
| 57 | "combination_record_id" long NULL, | 59 | "combination_record_id" long NULL, |
| 60 | + "point_activity_id" bigint NULL, | ||
| 58 | "creator" varchar DEFAULT '', | 61 | "creator" varchar DEFAULT '', |
| 59 | "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | 62 | "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| 60 | "updater" varchar DEFAULT '', | 63 | "updater" varchar DEFAULT '', |
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java
| @@ -39,7 +39,7 @@ public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransac | @@ -39,7 +39,7 @@ public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransac | ||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | default Integer selectPriceSum(Long walletId, Integer type, LocalDateTime[] createTime) { | 41 | default Integer selectPriceSum(Long walletId, Integer type, LocalDateTime[] createTime) { |
| 42 | - // SQL sum 查询;支出流水的 price 为负值,汇总取正值(与钱包 total_expense 的正值语义一致) | 42 | + // SQL sum 查询;支出流水的 price 为负值,取反后汇总为正值 |
| 43 | String sumColumn = Objects.equals(type, TYPE_EXPENSE) ? "SUM(-price) AS priceSum" : "SUM(price) AS priceSum"; | 43 | String sumColumn = Objects.equals(type, TYPE_EXPENSE) ? "SUM(-price) AS priceSum" : "SUM(price) AS priceSum"; |
| 44 | List<Map<String, Object>> result = selectMaps(new QueryWrapperX<PayWalletTransactionDO>() | 44 | List<Map<String, Object>> result = selectMaps(new QueryWrapperX<PayWalletTransactionDO>() |
| 45 | .select(sumColumn) | 45 | .select(sumColumn) |