Commit 9d5d8c9527ca81c1a41633565d7cb62ee55d92c2
1 parent
6ed09714
fix(product): 修正虚拟评价创建契约并启用商品评价单测
Showing
4 changed files
with
311 additions
and
135 deletions
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentCreateReqVO.java
| @@ -11,12 +11,6 @@ import java.util.List; | @@ -11,12 +11,6 @@ import java.util.List; | ||
| 11 | @Data | 11 | @Data |
| 12 | public class ProductCommentCreateReqVO { | 12 | public class ProductCommentCreateReqVO { |
| 13 | 13 | ||
| 14 | - @Schema(description = "评价人", requiredMode = Schema.RequiredMode.REQUIRED, example = "16868") | ||
| 15 | - private Long userId; | ||
| 16 | - | ||
| 17 | - @Schema(description = "评价订单项", requiredMode = Schema.RequiredMode.REQUIRED, example = "19292") | ||
| 18 | - private Long orderItemId; | ||
| 19 | - | ||
| 20 | @Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小姑凉") | 14 | @Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小姑凉") |
| 21 | @NotNull(message = "评价人名称不能为空") | 15 | @NotNull(message = "评价人名称不能为空") |
| 22 | private String userNickname; | 16 | private String userNickname; |
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java
| @@ -40,6 +40,7 @@ public interface ProductCommentConvert { | @@ -40,6 +40,7 @@ public interface ProductCommentConvert { | ||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | default ProductCommentDO convert(ProductCommentCreateReqVO createReq, ProductSpuDO spu, ProductSkuDO sku) { | 42 | default ProductCommentDO convert(ProductCommentCreateReqVO createReq, ProductSpuDO spu, ProductSkuDO sku) { |
| 43 | + // 管理后台添加的是虚拟评价,不关联会员和订单,userId 固定为 0:https://github.com/YunaiV/ruoyi-vue-pro/issues/1277 | ||
| 43 | ProductCommentDO comment = BeanUtils.toBean(createReq, ProductCommentDO.class) | 44 | ProductCommentDO comment = BeanUtils.toBean(createReq, ProductCommentDO.class) |
| 44 | .setVisible(true).setUserId(0L).setAnonymous(false) | 45 | .setVisible(true).setUserId(0L).setAnonymous(false) |
| 45 | .setScores(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores())); | 46 | .setScores(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores())); |
yudao-module-mall/yudao-module-product/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java
| 1 | package cn.iocoder.yudao.module.product.service.comment; | 1 | package cn.iocoder.yudao.module.product.service.comment; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.date.DateUtil; | ||
| 4 | -import cn.hutool.core.util.RandomUtil; | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 4 | +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; |
| 7 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 6 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | ||
| 7 | +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; | ||
| 8 | +import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; | ||
| 9 | +import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO; | ||
| 8 | import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; | 10 | import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; |
| 9 | import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyReqVO; | 11 | import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentReplyReqVO; |
| 10 | -import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentRespVO; | ||
| 11 | import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; | 12 | import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; |
| 12 | import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; | 13 | import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; |
| 13 | import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; | 14 | import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; |
| 15 | +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; | ||
| 16 | +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; | ||
| 14 | import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; | 17 | import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; |
| 15 | import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum; | 18 | import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum; |
| 16 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; | 19 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; |
| 17 | import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; | 20 | import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; |
| 18 | import jakarta.annotation.Resource; | 21 | import jakarta.annotation.Resource; |
| 19 | -import org.junit.jupiter.api.Disabled; | ||
| 20 | import org.junit.jupiter.api.Test; | 22 | import org.junit.jupiter.api.Test; |
| 21 | import org.springframework.context.annotation.Import; | 23 | import org.springframework.context.annotation.Import; |
| 22 | -import org.springframework.context.annotation.Lazy; | 24 | +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; |
| 23 | import org.springframework.test.context.bean.override.mockito.MockitoBean; | 25 | import org.springframework.test.context.bean.override.mockito.MockitoBean; |
| 24 | 26 | ||
| 25 | -import java.time.LocalDateTime; | ||
| 26 | -import java.util.Date; | 27 | +import java.util.List; |
| 28 | +import java.util.function.Consumer; | ||
| 27 | 29 | ||
| 30 | +import static cn.hutool.core.util.RandomUtil.randomEle; | ||
| 31 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | ||
| 32 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | ||
| 28 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | 33 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 29 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 34 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 30 | -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||
| 31 | -import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| 32 | -import static org.junit.jupiter.api.Assertions.assertFalse; | 35 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 36 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||
| 37 | +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; | ||
| 38 | +import static java.util.Collections.singletonList; | ||
| 39 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 40 | +import static org.mockito.ArgumentMatchers.any; | ||
| 41 | +import static org.mockito.ArgumentMatchers.eq; | ||
| 42 | +import static org.mockito.Mockito.*; | ||
| 33 | 43 | ||
| 34 | // TODO 芋艿:单测详细 review 下 | 44 | // TODO 芋艿:单测详细 review 下 |
| 35 | /** | 45 | /** |
| @@ -37,159 +47,328 @@ import static org.junit.jupiter.api.Assertions.assertFalse; | @@ -37,159 +47,328 @@ import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| 37 | * | 47 | * |
| 38 | * @author wangzhs | 48 | * @author wangzhs |
| 39 | */ | 49 | */ |
| 40 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | 50 | +// DONE 芋艿:后续 fix 补充的单测 |
| 41 | @Import(ProductCommentServiceImpl.class) | 51 | @Import(ProductCommentServiceImpl.class) |
| 42 | public class ProductCommentServiceImplTest extends BaseDbUnitTest { | 52 | public class ProductCommentServiceImplTest extends BaseDbUnitTest { |
| 43 | 53 | ||
| 44 | @Resource | 54 | @Resource |
| 45 | - private ProductCommentMapper productCommentMapper; | 55 | + private ProductCommentServiceImpl productCommentService; |
| 46 | 56 | ||
| 47 | @Resource | 57 | @Resource |
| 48 | - @Lazy | ||
| 49 | - private ProductCommentServiceImpl productCommentService; | 58 | + private ProductCommentMapper productCommentMapper; |
| 50 | 59 | ||
| 51 | @MockitoBean | 60 | @MockitoBean |
| 52 | private ProductSpuService productSpuService; | 61 | private ProductSpuService productSpuService; |
| 53 | @MockitoBean | 62 | @MockitoBean |
| 54 | private ProductSkuService productSkuService; | 63 | private ProductSkuService productSkuService; |
| 64 | + @MockitoBean | ||
| 65 | + private MemberUserApi memberUserApi; | ||
| 66 | + | ||
| 67 | + // ========== 管理后台添加虚拟评价 ========== | ||
| 68 | + | ||
| 69 | + @Test | ||
| 70 | + public void testCreateComment_success() { | ||
| 71 | + // 准备参数 | ||
| 72 | + ProductCommentCreateReqVO reqVO = randomPojo(ProductCommentCreateReqVO.class, o -> o | ||
| 73 | + .setDescriptionScores(ProductCommentScoresEnum.FIVE.getScores()) | ||
| 74 | + .setBenefitScores(ProductCommentScoresEnum.FOUR.getScores())); | ||
| 75 | + // mock 方法 | ||
| 76 | + ProductSkuDO sku = mockSku(reqVO.getSkuId()); | ||
| 77 | + ProductSpuDO spu = mockSpu(sku.getSpuId()); | ||
| 55 | 78 | ||
| 56 | - public String generateNo() { | ||
| 57 | - return DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomInt(100000, 999999); | 79 | + // 调用 |
| 80 | + productCommentService.createComment(reqVO); | ||
| 81 | + // 断言:虚拟评价不关联会员和订单 | ||
| 82 | + List<ProductCommentDO> comments = productCommentMapper.selectList(); | ||
| 83 | + assertEquals(1, comments.size()); | ||
| 84 | + ProductCommentDO comment = comments.get(0); | ||
| 85 | + assertPojoEquals(reqVO, comment); | ||
| 86 | + assertEquals(0L, comment.getUserId()); | ||
| 87 | + assertNull(comment.getOrderId()); | ||
| 88 | + assertNull(comment.getOrderItemId()); | ||
| 89 | + assertTrue(comment.getVisible()); | ||
| 90 | + assertFalse(comment.getAnonymous()); | ||
| 91 | + assertEquals(4, comment.getScores()); // (5 + 4) / 2 向下取整 | ||
| 92 | + // 断言:商品信息 | ||
| 93 | + assertEquals(spu.getId(), comment.getSpuId()); | ||
| 94 | + assertEquals(spu.getName(), comment.getSpuName()); | ||
| 95 | + assertEquals(sku.getPicUrl(), comment.getSkuPicUrl()); | ||
| 96 | + assertEquals(sku.getProperties(), comment.getSkuProperties()); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + @Test | ||
| 100 | + public void testCreateComment_ignoreUserIdAndOrderItemId() throws Exception { | ||
| 101 | + // 准备参数:旧调用方仍传入 userId、orderItemId,使用 Spring MVC 默认的 Jackson2ObjectMapperBuilder 反序列化 | ||
| 102 | + String json = "{\"userId\":968,\"orderItemId\":1024,\"skuId\":1,\"userNickname\":\"芋艿\"," | ||
| 103 | + + "\"userAvatar\":\"https://www.iocoder.cn/1.png\",\"descriptionScores\":5,\"benefitScores\":5," | ||
| 104 | + + "\"content\":\"好评\",\"picUrls\":[]}"; | ||
| 105 | + ProductCommentCreateReqVO reqVO = Jackson2ObjectMapperBuilder.json().build() | ||
| 106 | + .readValue(json, ProductCommentCreateReqVO.class); | ||
| 107 | + // mock 方法 | ||
| 108 | + mockSpu(mockSku(reqVO.getSkuId()).getSpuId()); | ||
| 109 | + | ||
| 110 | + // 调用 | ||
| 111 | + productCommentService.createComment(reqVO); | ||
| 112 | + // 断言 | ||
| 113 | + ProductCommentDO comment = productCommentMapper.selectOne(ProductCommentDO::getSkuId, 1L); | ||
| 114 | + assertEquals("芋艿", comment.getUserNickname()); | ||
| 115 | + assertEquals(0L, comment.getUserId()); | ||
| 116 | + assertNull(comment.getOrderItemId()); | ||
| 58 | } | 117 | } |
| 59 | 118 | ||
| 60 | - public Long generateId() { | ||
| 61 | - return RandomUtil.randomLong(100000, 999999); | 119 | + @Test |
| 120 | + public void testCreateComment_skuNotExists() { | ||
| 121 | + // 准备参数 | ||
| 122 | + ProductCommentCreateReqVO reqVO = randomPojo(ProductCommentCreateReqVO.class); | ||
| 123 | + | ||
| 124 | + // 调用,并断言异常 | ||
| 125 | + assertServiceException(() -> productCommentService.createComment(reqVO), SKU_NOT_EXISTS); | ||
| 126 | + assertEquals(0L, productCommentMapper.selectCount()); | ||
| 62 | } | 127 | } |
| 63 | 128 | ||
| 129 | + // ========== 会员评价订单项 ========== | ||
| 130 | + | ||
| 64 | @Test | 131 | @Test |
| 65 | - public void testCreateCommentAndGet_success() { | ||
| 66 | - // mock 测试 | ||
| 67 | - ProductCommentDO productComment = randomPojo(ProductCommentDO.class); | ||
| 68 | - productCommentMapper.insert(productComment); | 132 | + public void testCreateCommentByMember_success() { |
| 133 | + // 准备参数 | ||
| 134 | + ProductCommentCreateReqDTO reqDTO = randomPojo(ProductCommentCreateReqDTO.class, o -> o | ||
| 135 | + .setDescriptionScores(ProductCommentScoresEnum.THREE.getScores()) | ||
| 136 | + .setBenefitScores(ProductCommentScoresEnum.FOUR.getScores())); | ||
| 137 | + // mock 数据:同一 SKU 已有虚拟评价,不影响会员评价 | ||
| 138 | + productCommentMapper.insert(randomCommentDO(o -> o.setUserId(0L).setOrderId(null).setOrderItemId(null) | ||
| 139 | + .setSkuId(reqDTO.getSkuId()))); | ||
| 140 | + // mock 方法 | ||
| 141 | + ProductSkuDO sku = mockSku(reqDTO.getSkuId()); | ||
| 142 | + ProductSpuDO spu = mockSpu(sku.getSpuId()); | ||
| 143 | + MemberUserRespDTO user = randomPojo(MemberUserRespDTO.class, o -> o.setId(reqDTO.getUserId())); | ||
| 144 | + when(memberUserApi.getUser(eq(reqDTO.getUserId()))).thenReturn(user); | ||
| 69 | 145 | ||
| 146 | + // 调用 | ||
| 147 | + Long commentId = productCommentService.createComment(reqDTO); | ||
| 70 | // 断言 | 148 | // 断言 |
| 71 | - // 校验记录的属性是否正确 | ||
| 72 | - ProductCommentDO comment = productCommentMapper.selectById(productComment.getId()); | ||
| 73 | - assertPojoEquals(productComment, comment); | 149 | + ProductCommentDO comment = productCommentMapper.selectById(commentId); |
| 150 | + assertPojoEquals(reqDTO, comment); | ||
| 151 | + assertEquals(user.getNickname(), comment.getUserNickname()); | ||
| 152 | + assertEquals(user.getAvatar(), comment.getUserAvatar()); | ||
| 153 | + assertEquals(3, comment.getScores()); // (3 + 4) / 2 向下取整 | ||
| 154 | + assertEquals(spu.getId(), comment.getSpuId()); | ||
| 155 | + assertEquals(spu.getName(), comment.getSpuName()); | ||
| 156 | + assertEquals(sku.getPicUrl(), comment.getSkuPicUrl()); | ||
| 157 | + assertEquals(sku.getProperties(), comment.getSkuProperties()); | ||
| 74 | } | 158 | } |
| 75 | 159 | ||
| 76 | @Test | 160 | @Test |
| 77 | - public void testGetCommentPage_success() { | 161 | + public void testCreateCommentByMember_orderExists() { |
| 162 | + // mock 数据:该会员已评价该订单项 | ||
| 163 | + ProductCommentDO dbComment = randomCommentDO(); | ||
| 164 | + productCommentMapper.insert(dbComment); | ||
| 78 | // 准备参数 | 165 | // 准备参数 |
| 79 | - ProductCommentDO productComment = randomPojo(ProductCommentDO.class, o -> { | 166 | + ProductCommentCreateReqDTO reqDTO = randomPojo(ProductCommentCreateReqDTO.class, o -> o |
| 167 | + .setUserId(dbComment.getUserId()).setOrderItemId(dbComment.getOrderItemId())); | ||
| 168 | + // mock 方法 | ||
| 169 | + mockSpu(mockSku(reqDTO.getSkuId()).getSpuId()); | ||
| 170 | + | ||
| 171 | + // 调用,并断言异常 | ||
| 172 | + assertServiceException(() -> productCommentService.createComment(reqDTO), COMMENT_ORDER_EXISTS); | ||
| 173 | + assertEquals(1L, productCommentMapper.selectCount()); | ||
| 174 | + verify(memberUserApi, never()).getUser(any()); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + // ========== 查询评价 ========== | ||
| 178 | + | ||
| 179 | + @Test | ||
| 180 | + public void testGetComment_success() { | ||
| 181 | + // mock 数据 | ||
| 182 | + ProductCommentDO dbComment = randomCommentDO(); | ||
| 183 | + productCommentMapper.insert(dbComment); | ||
| 184 | + | ||
| 185 | + // 调用 | ||
| 186 | + ProductCommentDO comment = productCommentService.getComment(dbComment.getId()); | ||
| 187 | + // 断言 | ||
| 188 | + assertPojoEquals(dbComment, comment); | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + @Test | ||
| 192 | + public void testGetComment_notExists() { | ||
| 193 | + // 调用,并断言异常 | ||
| 194 | + assertServiceException(() -> productCommentService.getComment(randomLongId()), COMMENT_NOT_EXISTS); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + @Test | ||
| 198 | + public void testGetCommentPage_success() { | ||
| 199 | + // mock 数据 | ||
| 200 | + ProductCommentDO dbComment = randomCommentDO(o -> { // 等会查询到 | ||
| 80 | o.setUserNickname("王二狗"); | 201 | o.setUserNickname("王二狗"); |
| 202 | + o.setOrderId(1L); | ||
| 203 | + o.setSpuId(2L); | ||
| 81 | o.setSpuName("感冒药"); | 204 | o.setSpuName("感冒药"); |
| 82 | o.setScores(ProductCommentScoresEnum.FOUR.getScores()); | 205 | o.setScores(ProductCommentScoresEnum.FOUR.getScores()); |
| 83 | o.setReplyStatus(Boolean.TRUE); | 206 | o.setReplyStatus(Boolean.TRUE); |
| 84 | - o.setVisible(Boolean.TRUE); | ||
| 85 | - o.setId(generateId()); | ||
| 86 | - o.setUserId(generateId()); | ||
| 87 | - o.setAnonymous(Boolean.TRUE); | ||
| 88 | - o.setOrderId(generateId()); | ||
| 89 | - o.setOrderItemId(generateId()); | ||
| 90 | - o.setSpuId(generateId()); | ||
| 91 | - o.setSkuId(generateId()); | ||
| 92 | - o.setDescriptionScores(ProductCommentScoresEnum.FOUR.getScores()); | ||
| 93 | - o.setBenefitScores(ProductCommentScoresEnum.FOUR.getScores()); | ||
| 94 | - o.setContent("真好吃"); | ||
| 95 | - o.setReplyUserId(generateId()); | ||
| 96 | - o.setReplyContent("确实"); | ||
| 97 | - o.setReplyTime(LocalDateTime.now()); | ||
| 98 | - o.setCreateTime(LocalDateTime.now()); | ||
| 99 | - o.setUpdateTime(LocalDateTime.now()); | 207 | + o.setCreateTime(buildTime(2023, 2, 1)); |
| 208 | + o.setSkuProperties(null); // ProductSkuDO.Property 不可序列化,cloneIgnoreId 无法深拷贝 | ||
| 100 | }); | 209 | }); |
| 101 | - productCommentMapper.insert(productComment); | ||
| 102 | - | ||
| 103 | - Long orderId = productComment.getOrderId(); | ||
| 104 | - Long spuId = productComment.getSpuId(); | ||
| 105 | - | 210 | + productCommentMapper.insert(dbComment); |
| 106 | // 测试 userNickname 不匹配 | 211 | // 测试 userNickname 不匹配 |
| 107 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setUserNickname("王三").setScores(ProductCommentScoresEnum.ONE.getScores()))); | 212 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setUserNickname("王三"))); |
| 108 | // 测试 orderId 不匹配 | 213 | // 测试 orderId 不匹配 |
| 109 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setOrderId(generateId()))); | 214 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setOrderId(10L))); |
| 110 | // 测试 spuId 不匹配 | 215 | // 测试 spuId 不匹配 |
| 111 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setSpuId(generateId()))); | 216 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setSpuId(20L))); |
| 112 | // 测试 spuName 不匹配 | 217 | // 测试 spuName 不匹配 |
| 113 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setSpuName("感康"))); | 218 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setSpuName("感康"))); |
| 114 | // 测试 scores 不匹配 | 219 | // 测试 scores 不匹配 |
| 115 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setScores(ProductCommentScoresEnum.ONE.getScores()))); | ||
| 116 | - // 测试 replied 不匹配 | ||
| 117 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setReplyStatus(Boolean.FALSE))); | ||
| 118 | - // 测试 visible 不匹配 | ||
| 119 | - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setVisible(Boolean.FALSE))); | 220 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setScores(ProductCommentScoresEnum.ONE.getScores()))); |
| 221 | + // 测试 replyStatus 不匹配 | ||
| 222 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setReplyStatus(Boolean.FALSE))); | ||
| 223 | + // 测试 createTime 不匹配 | ||
| 224 | + productCommentMapper.insert(cloneIgnoreId(dbComment, o -> o.setCreateTime(buildTime(2023, 3, 1)))); | ||
| 225 | + // 准备参数 | ||
| 226 | + ProductCommentPageReqVO reqVO = new ProductCommentPageReqVO(); | ||
| 227 | + reqVO.setUserNickname("王二"); | ||
| 228 | + reqVO.setOrderId(1L); | ||
| 229 | + reqVO.setSpuId(2L); | ||
| 230 | + reqVO.setSpuName("感冒"); | ||
| 231 | + reqVO.setScores(ProductCommentScoresEnum.FOUR.getScores()); | ||
| 232 | + reqVO.setReplyStatus(Boolean.TRUE); | ||
| 233 | + reqVO.setCreateTime(buildBetweenTime(2023, 1, 1, 2023, 2, 28)); | ||
| 120 | 234 | ||
| 121 | // 调用 | 235 | // 调用 |
| 122 | - ProductCommentPageReqVO productCommentPageReqVO = new ProductCommentPageReqVO(); | ||
| 123 | - productCommentPageReqVO.setUserNickname("王二"); | ||
| 124 | - productCommentPageReqVO.setOrderId(orderId); | ||
| 125 | - productCommentPageReqVO.setSpuId(spuId); | ||
| 126 | - productCommentPageReqVO.setSpuName("感冒药"); | ||
| 127 | - productCommentPageReqVO.setScores(ProductCommentScoresEnum.FOUR.getScores()); | ||
| 128 | - productCommentPageReqVO.setReplyStatus(Boolean.TRUE); | ||
| 129 | - | ||
| 130 | - PageResult<ProductCommentDO> commentPage = productCommentService.getCommentPage(productCommentPageReqVO); | ||
| 131 | - PageResult<ProductCommentRespVO> result = BeanUtils.toBean(productCommentMapper.selectPage(productCommentPageReqVO), | ||
| 132 | - ProductCommentRespVO.class); | ||
| 133 | - assertEquals(result.getTotal(), commentPage.getTotal()); | 236 | + PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(reqVO); |
| 237 | + // 断言 | ||
| 238 | + assertEquals(1, pageResult.getTotal()); | ||
| 239 | + assertEquals(1, pageResult.getList().size()); | ||
| 240 | + assertPojoEquals(dbComment, pageResult.getList().get(0)); | ||
| 241 | + } | ||
| 134 | 242 | ||
| 135 | - PageResult<ProductCommentDO> all = productCommentService.getCommentPage(new ProductCommentPageReqVO()); | ||
| 136 | - assertEquals(8, all.getTotal()); | 243 | + @Test |
| 244 | + public void testGetAppCommentPage_success() { | ||
| 245 | + // mock 数据 | ||
| 246 | + ProductCommentDO goodComment = randomCommentDO(o -> o.setSpuId(1L).setVisible(Boolean.TRUE) | ||
| 247 | + .setScores(ProductCommentScoresEnum.FIVE.getScores()) | ||
| 248 | + .setSkuProperties(null)); // ProductSkuDO.Property 不可序列化,cloneIgnoreId 无法深拷贝 | ||
| 249 | + productCommentMapper.insert(goodComment); | ||
| 250 | + ProductCommentDO mediocreComment = cloneIgnoreId(goodComment, o -> o.setScores(ProductCommentScoresEnum.THREE.getScores())); | ||
| 251 | + productCommentMapper.insert(mediocreComment); | ||
| 252 | + ProductCommentDO negativeComment = cloneIgnoreId(goodComment, o -> o.setScores(ProductCommentScoresEnum.ONE.getScores())); | ||
| 253 | + productCommentMapper.insert(negativeComment); | ||
| 254 | + // 测试 visible 不匹配 | ||
| 255 | + productCommentMapper.insert(cloneIgnoreId(mediocreComment, o -> o.setVisible(Boolean.FALSE))); | ||
| 256 | + // 测试 spuId 不匹配 | ||
| 257 | + productCommentMapper.insert(cloneIgnoreId(mediocreComment, o -> o.setSpuId(2L))); | ||
| 258 | + | ||
| 259 | + // 调用 + 断言:全部可见评价 | ||
| 260 | + PageResult<ProductCommentDO> allResult = productCommentService.getCommentPage( | ||
| 261 | + new AppCommentPageReqVO().setSpuId(1L), Boolean.TRUE); | ||
| 262 | + assertEquals(3, allResult.getTotal()); | ||
| 263 | + // 调用 + 断言:好评 | ||
| 264 | + PageResult<ProductCommentDO> goodResult = productCommentService.getCommentPage( | ||
| 265 | + new AppCommentPageReqVO().setSpuId(1L).setType(AppCommentPageReqVO.GOOD_COMMENT), Boolean.TRUE); | ||
| 266 | + assertEquals(1, goodResult.getTotal()); | ||
| 267 | + assertPojoEquals(goodComment, goodResult.getList().get(0)); | ||
| 268 | + // 调用 + 断言:中评 | ||
| 269 | + PageResult<ProductCommentDO> mediocreResult = productCommentService.getCommentPage( | ||
| 270 | + new AppCommentPageReqVO().setSpuId(1L).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); | ||
| 271 | + assertEquals(1, mediocreResult.getTotal()); | ||
| 272 | + assertPojoEquals(mediocreComment, mediocreResult.getList().get(0)); | ||
| 273 | + // 调用 + 断言:差评 | ||
| 274 | + PageResult<ProductCommentDO> negativeResult = productCommentService.getCommentPage( | ||
| 275 | + new AppCommentPageReqVO().setSpuId(1L).setType(AppCommentPageReqVO.NEGATIVE_COMMENT), Boolean.TRUE); | ||
| 276 | + assertEquals(1, negativeResult.getTotal()); | ||
| 277 | + assertPojoEquals(negativeComment, negativeResult.getList().get(0)); | ||
| 278 | + // 调用 + 断言:不过滤可见性时,包含隐藏的中评 | ||
| 279 | + PageResult<ProductCommentDO> mediocreAllResult = productCommentService.getCommentPage( | ||
| 280 | + new AppCommentPageReqVO().setSpuId(1L).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), null); | ||
| 281 | + assertEquals(2, mediocreAllResult.getTotal()); | ||
| 282 | + } | ||
| 137 | 283 | ||
| 138 | - // 测试获取所有商品分页评论数据 | ||
| 139 | - PageResult<ProductCommentDO> result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE); | ||
| 140 | - assertEquals(7, result1.getTotal()); | 284 | + // ========== 更新评价 ========== |
| 141 | 285 | ||
| 142 | - // 测试获取所有商品分页中评数据 | ||
| 143 | - PageResult<ProductCommentDO> result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); | ||
| 144 | - assertEquals(2, result2.getTotal()); | 286 | + @Test |
| 287 | + public void testUpdateCommentVisible_success() { | ||
| 288 | + // mock 数据 | ||
| 289 | + ProductCommentDO dbComment = randomCommentDO(o -> o.setVisible(Boolean.TRUE)); | ||
| 290 | + productCommentMapper.insert(dbComment); | ||
| 291 | + // 准备参数 | ||
| 292 | + ProductCommentUpdateVisibleReqVO reqVO = new ProductCommentUpdateVisibleReqVO(); | ||
| 293 | + reqVO.setId(dbComment.getId()); | ||
| 294 | + reqVO.setVisible(Boolean.FALSE); | ||
| 145 | 295 | ||
| 146 | - // 测试获取指定 spuId 商品分页中评数据 | ||
| 147 | - PageResult<ProductCommentDO> result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); | ||
| 148 | - assertEquals(2, result3.getTotal()); | 296 | + // 调用 |
| 297 | + productCommentService.updateCommentVisible(reqVO); | ||
| 298 | + // 断言 | ||
| 299 | + ProductCommentDO comment = productCommentMapper.selectById(dbComment.getId()); | ||
| 300 | + assertFalse(comment.getVisible()); | ||
| 301 | + assertPojoEquals(dbComment, comment, "visible", "updater", "updateTime"); | ||
| 302 | + } | ||
| 149 | 303 | ||
| 150 | - // 测试分页 tab count | ||
| 151 | - //AppCommentStatisticsRespVO tabsCount = productCommentService.getCommentStatistics(spuId, Boolean.TRUE); | ||
| 152 | - //assertEquals(4, tabsCount.getGoodCount()); | ||
| 153 | - //assertEquals(2, tabsCount.getMediocreCount()); | ||
| 154 | - //assertEquals(0, tabsCount.getNegativeCount()); | 304 | + @Test |
| 305 | + public void testUpdateCommentVisible_notExists() { | ||
| 306 | + // 准备参数 | ||
| 307 | + ProductCommentUpdateVisibleReqVO reqVO = new ProductCommentUpdateVisibleReqVO(); | ||
| 308 | + reqVO.setId(randomLongId()); | ||
| 309 | + reqVO.setVisible(Boolean.FALSE); | ||
| 155 | 310 | ||
| 311 | + // 调用,并断言异常 | ||
| 312 | + assertServiceException(() -> productCommentService.updateCommentVisible(reqVO), COMMENT_NOT_EXISTS); | ||
| 156 | } | 313 | } |
| 157 | 314 | ||
| 158 | @Test | 315 | @Test |
| 159 | - public void testUpdateCommentVisible_success() { | ||
| 160 | - // mock 测试 | ||
| 161 | - ProductCommentDO productComment = randomPojo(ProductCommentDO.class, o -> { | ||
| 162 | - o.setVisible(Boolean.TRUE); | ||
| 163 | - }); | ||
| 164 | - productCommentMapper.insert(productComment); | 316 | + public void testReplyComment_success() { |
| 317 | + // mock 数据 | ||
| 318 | + ProductCommentDO dbComment = randomCommentDO(o -> o.setReplyStatus(Boolean.FALSE) | ||
| 319 | + .setReplyUserId(null).setReplyContent(null).setReplyTime(null)); | ||
| 320 | + productCommentMapper.insert(dbComment); | ||
| 321 | + // 准备参数 | ||
| 322 | + ProductCommentReplyReqVO reqVO = new ProductCommentReplyReqVO(); | ||
| 323 | + reqVO.setId(dbComment.getId()); | ||
| 324 | + reqVO.setReplyContent("测试"); | ||
| 325 | + Long userId = randomLongId(); | ||
| 165 | 326 | ||
| 166 | - Long productCommentId = productComment.getId(); | 327 | + // 调用 |
| 328 | + productCommentService.replyComment(reqVO, userId); | ||
| 329 | + // 断言 | ||
| 330 | + ProductCommentDO comment = productCommentMapper.selectById(dbComment.getId()); | ||
| 331 | + assertTrue(comment.getReplyStatus()); | ||
| 332 | + assertEquals(userId, comment.getReplyUserId()); | ||
| 333 | + assertEquals("测试", comment.getReplyContent()); | ||
| 334 | + assertNotNull(comment.getReplyTime()); | ||
| 335 | + } | ||
| 167 | 336 | ||
| 168 | - ProductCommentUpdateVisibleReqVO updateReqVO = new ProductCommentUpdateVisibleReqVO(); | ||
| 169 | - updateReqVO.setId(productCommentId); | ||
| 170 | - updateReqVO.setVisible(Boolean.FALSE); | ||
| 171 | - productCommentService.updateCommentVisible(updateReqVO); | 337 | + @Test |
| 338 | + public void testReplyComment_notExists() { | ||
| 339 | + // 准备参数 | ||
| 340 | + ProductCommentReplyReqVO reqVO = new ProductCommentReplyReqVO(); | ||
| 341 | + reqVO.setId(randomLongId()); | ||
| 342 | + reqVO.setReplyContent("测试"); | ||
| 172 | 343 | ||
| 173 | - ProductCommentDO productCommentDO = productCommentMapper.selectById(productCommentId); | ||
| 174 | - assertFalse(productCommentDO.getVisible()); | 344 | + // 调用,并断言异常 |
| 345 | + assertServiceException(() -> productCommentService.replyComment(reqVO, randomLongId()), COMMENT_NOT_EXISTS); | ||
| 175 | } | 346 | } |
| 176 | 347 | ||
| 348 | + // ========== 随机对象 ========== | ||
| 177 | 349 | ||
| 178 | - @Test | ||
| 179 | - public void testCommentReply_success() { | ||
| 180 | - // mock 测试 | ||
| 181 | - ProductCommentDO productComment = randomPojo(ProductCommentDO.class); | ||
| 182 | - productCommentMapper.insert(productComment); | ||
| 183 | - | ||
| 184 | - Long productCommentId = productComment.getId(); | 350 | + private ProductSkuDO mockSku(Long skuId) { |
| 351 | + ProductSkuDO sku = randomPojo(ProductSkuDO.class, o -> o.setId(skuId) | ||
| 352 | + .setProperties(singletonList(new ProductSkuDO.Property(1L, "颜色", 2L, "红色")))); | ||
| 353 | + when(productSkuService.getSku(eq(skuId), eq(true))).thenReturn(sku); | ||
| 354 | + return sku; | ||
| 355 | + } | ||
| 185 | 356 | ||
| 186 | - ProductCommentReplyReqVO replyVO = new ProductCommentReplyReqVO(); | ||
| 187 | - replyVO.setId(productCommentId); | ||
| 188 | - replyVO.setReplyContent("测试"); | ||
| 189 | - productCommentService.replyComment(replyVO, 1L); | 357 | + private ProductSpuDO mockSpu(Long spuId) { |
| 358 | + ProductSpuDO spu = randomPojo(ProductSpuDO.class, o -> o.setId(spuId)); | ||
| 359 | + when(productSpuService.getSpu(eq(spuId), eq(true))).thenReturn(spu); | ||
| 360 | + return spu; | ||
| 361 | + } | ||
| 190 | 362 | ||
| 191 | - ProductCommentDO productCommentDO = productCommentMapper.selectById(productCommentId); | ||
| 192 | - assertEquals("测试", productCommentDO.getReplyContent()); | 363 | + @SafeVarargs |
| 364 | + private static ProductCommentDO randomCommentDO(Consumer<ProductCommentDO>... consumers) { | ||
| 365 | + Consumer<ProductCommentDO> consumer = (o) -> { | ||
| 366 | + o.setScores(randomEle(ProductCommentScoresEnum.values()).getScores()); // 保证 scores 的范围 | ||
| 367 | + o.setDescriptionScores(randomEle(ProductCommentScoresEnum.values()).getScores()); | ||
| 368 | + o.setBenefitScores(randomEle(ProductCommentScoresEnum.values()).getScores()); | ||
| 369 | + o.setSkuProperties(singletonList(new ProductSkuDO.Property(1L, "颜色", 2L, "红色"))); | ||
| 370 | + }; | ||
| 371 | + return randomPojo(ProductCommentDO.class, ArrayUtils.append(consumer, consumers)); | ||
| 193 | } | 372 | } |
| 194 | 373 | ||
| 195 | } | 374 | } |
yudao-module-mall/yudao-module-product/src/test/resources/sql/create_tables.sql
| @@ -126,32 +126,34 @@ CREATE TABLE IF NOT EXISTS `product_property_value` ( | @@ -126,32 +126,34 @@ CREATE TABLE IF NOT EXISTS `product_property_value` ( | ||
| 126 | PRIMARY KEY("id") | 126 | PRIMARY KEY("id") |
| 127 | ) COMMENT '规格值'; | 127 | ) COMMENT '规格值'; |
| 128 | 128 | ||
| 129 | -DROP TABLE IF EXISTS `product_comment` ( | ||
| 130 | - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '评论编号,主键自增', | 129 | +CREATE TABLE IF NOT EXISTS `product_comment` ( |
| 130 | + `id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||
| 131 | `user_id` bigint DEFAULT NULL COMMENT '评价人的用户编号关联 MemberUserDO 的 id 编号', | 131 | `user_id` bigint DEFAULT NULL COMMENT '评价人的用户编号关联 MemberUserDO 的 id 编号', |
| 132 | `user_nickname` varchar(255) DEFAULT NULL COMMENT '评价人名称', | 132 | `user_nickname` varchar(255) DEFAULT NULL COMMENT '评价人名称', |
| 133 | - `user_avatar` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '评价人头像', | ||
| 134 | - `anonymous` bit(1) DEFAULT NULL COMMENT '是否匿名', | 133 | + `user_avatar` varchar(1024) DEFAULT NULL COMMENT '评价人头像', |
| 134 | + `anonymous` bit DEFAULT NULL COMMENT '是否匿名', | ||
| 135 | `order_id` bigint DEFAULT NULL COMMENT '交易订单编号关联 TradeOrderDO 的 id 编号', | 135 | `order_id` bigint DEFAULT NULL COMMENT '交易订单编号关联 TradeOrderDO 的 id 编号', |
| 136 | `order_item_id` bigint DEFAULT NULL COMMENT '交易订单项编号关联 TradeOrderItemDO 的 id 编号', | 136 | `order_item_id` bigint DEFAULT NULL COMMENT '交易订单项编号关联 TradeOrderItemDO 的 id 编号', |
| 137 | `spu_id` bigint DEFAULT NULL COMMENT '商品 SPU 编号关联 ProductSpuDO 的 id', | 137 | `spu_id` bigint DEFAULT NULL COMMENT '商品 SPU 编号关联 ProductSpuDO 的 id', |
| 138 | `spu_name` varchar(255) DEFAULT NULL COMMENT '商品 SPU 名称', | 138 | `spu_name` varchar(255) DEFAULT NULL COMMENT '商品 SPU 名称', |
| 139 | `sku_id` bigint DEFAULT NULL COMMENT '商品 SKU 编号关联 ProductSkuDO 的 id 编号', | 139 | `sku_id` bigint DEFAULT NULL COMMENT '商品 SKU 编号关联 ProductSkuDO 的 id 编号', |
| 140 | - `visible` bit(1) DEFAULT NULL COMMENT '是否可见true:显示false:隐藏', | 140 | + `sku_pic_url` varchar(256) DEFAULT NULL COMMENT '商品 SKU 图片地址', |
| 141 | + `sku_properties` varchar(512) DEFAULT NULL COMMENT '属性数组,JSON 格式', | ||
| 142 | + `visible` bit DEFAULT NULL COMMENT '是否可见true:显示false:隐藏', | ||
| 141 | `scores` tinyint DEFAULT NULL COMMENT '评分星级1-5分', | 143 | `scores` tinyint DEFAULT NULL COMMENT '评分星级1-5分', |
| 142 | `description_scores` tinyint DEFAULT NULL COMMENT '描述星级1-5 星', | 144 | `description_scores` tinyint DEFAULT NULL COMMENT '描述星级1-5 星', |
| 143 | `benefit_scores` tinyint DEFAULT NULL COMMENT '服务星级1-5 星', | 145 | `benefit_scores` tinyint DEFAULT NULL COMMENT '服务星级1-5 星', |
| 144 | - `content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '评论内容', | 146 | + `content` varchar(1024) DEFAULT NULL COMMENT '评论内容', |
| 145 | `pic_urls` varchar(4096) DEFAULT NULL COMMENT '评论图片地址数组', | 147 | `pic_urls` varchar(4096) DEFAULT NULL COMMENT '评论图片地址数组', |
| 146 | - `reply_status` bit(1) DEFAULT NULL COMMENT '商家是否回复', | 148 | + `reply_status` bit DEFAULT NULL COMMENT '商家是否回复', |
| 147 | `reply_user_id` bigint DEFAULT NULL COMMENT '回复管理员编号关联 AdminUserDO 的 id 编号', | 149 | `reply_user_id` bigint DEFAULT NULL COMMENT '回复管理员编号关联 AdminUserDO 的 id 编号', |
| 148 | - `reply_content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商家回复内容', | ||
| 149 | - `reply_time` datetime DEFAULT NULL COMMENT '商家回复时间', | ||
| 150 | - `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者', | ||
| 151 | - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 152 | - `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者', | ||
| 153 | - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 154 | - `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | ||
| 155 | - `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号', | ||
| 156 | - PRIMARY KEY (`id`) USING BTREE | ||
| 157 | -) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '商品评论'; | ||
| 158 | \ No newline at end of file | 150 | \ No newline at end of file |
| 151 | + `reply_content` varchar(1024) DEFAULT NULL COMMENT '商家回复内容', | ||
| 152 | + `reply_time` timestamp DEFAULT NULL COMMENT '商家回复时间', | ||
| 153 | + "creator" varchar(64) DEFAULT '', | ||
| 154 | + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| 155 | + "updater" varchar(64) DEFAULT '', | ||
| 156 | + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| 157 | + "deleted" bit NOT NULL DEFAULT FALSE, | ||
| 158 | + "tenant_id" bigint not null default '0', | ||
| 159 | + PRIMARY KEY("id") | ||
| 160 | +) COMMENT '商品评论'; | ||
| 159 | \ No newline at end of file | 161 | \ No newline at end of file |