Commit 4126e37d910e2cbd80f7f5a5571cc97ef8aef0f2

Authored by YunaiV
2 parents f75f4e88 0e817b19

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

# Conflicts:
#	yudao-module-mall/yudao-module-product-biz/pom.xml
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
Showing 40 changed files with 861 additions and 289 deletions
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
@@ -103,6 +103,15 @@ public class ProductSpuRespDTO { @@ -103,6 +103,15 @@ public class ProductSpuRespDTO {
103 */ 103 */
104 private Integer stock; 104 private Integer stock;
105 105
  106 + // ========== 物流相关字段 =========
  107 +
  108 + /**
  109 + * 物流配置模板编号
  110 + *
  111 + * 对应 TradeDeliveryExpressTemplateDO 的 id 编号
  112 + */
  113 + private Long deliveryTemplateId;
  114 +
106 // ========== 统计相关字段 ========= 115 // ========== 统计相关字段 =========
107 116
108 /** 117 /**
yudao-module-mall/yudao-module-product-biz/pom.xml
@@ -23,7 +23,12 @@ @@ -23,7 +23,12 @@
23 <artifactId>yudao-module-product-api</artifactId> 23 <artifactId>yudao-module-product-api</artifactId>
24 <version>${revision}</version> 24 <version>${revision}</version>
25 </dependency> 25 </dependency>
26 - 26 + <!-- TODO 芋艿:看看~~~ -->
  27 + <dependency>
  28 + <groupId>cn.iocoder.boot</groupId>
  29 + <artifactId>yudao-module-trade-api</artifactId>
  30 + <version>${revision}</version>
  31 + </dependency>
27 <dependency> 32 <dependency>
28 <groupId>cn.iocoder.boot</groupId> 33 <groupId>cn.iocoder.boot</groupId>
29 <artifactId>yudao-module-member-api</artifactId> 34 <artifactId>yudao-module-member-api</artifactId>
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppCommentController.java
@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentCreat @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentCreat
9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; 9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
10 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO; 10 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
11 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert; 11 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
12 -import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;  
13 import cn.iocoder.yudao.module.product.service.comment.ProductCommentService; 12 import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
14 import io.swagger.v3.oas.annotations.Operation; 13 import io.swagger.v3.oas.annotations.Operation;
15 import io.swagger.v3.oas.annotations.tags.Tag; 14 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -18,6 +17,7 @@ import org.springframework.web.bind.annotation.*; @@ -18,6 +17,7 @@ import org.springframework.web.bind.annotation.*;
18 17
19 import javax.annotation.Resource; 18 import javax.annotation.Resource;
20 import javax.validation.Valid; 19 import javax.validation.Valid;
  20 +import java.util.Map;
21 21
22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
23 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 23 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -37,8 +37,13 @@ public class AppCommentController { @@ -37,8 +37,13 @@ public class AppCommentController {
37 @GetMapping("/page") 37 @GetMapping("/page")
38 @Operation(summary = "获得商品评价分页") 38 @Operation(summary = "获得商品评价分页")
39 public CommonResult<PageResult<AppCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) { 39 public CommonResult<PageResult<AppCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
40 - PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(pageVO, Boolean.TRUE);  
41 - return success(ProductCommentConvert.INSTANCE.convertPage02(pageResult)); 40 + return success(productCommentService.getCommentPage(pageVO, Boolean.TRUE));
  41 + }
  42 +
  43 + @GetMapping("/get-count")
  44 + @Operation(summary = "获得商品评价分页 tab count")
  45 + public CommonResult<Map<String, Long>> getCommentPage(@Valid Long spuId) {
  46 + return success(productCommentService.getCommentPageTabsCount(spuId, Boolean.TRUE));
42 } 47 }
43 48
44 @PostMapping(value = "/create") 49 @PostMapping(value = "/create")
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppCommentPageReqVO.java
@@ -14,8 +14,57 @@ import javax.validation.constraints.NotNull; @@ -14,8 +14,57 @@ import javax.validation.constraints.NotNull;
14 @ToString(callSuper = true) 14 @ToString(callSuper = true)
15 public class AppCommentPageReqVO extends PageParam { 15 public class AppCommentPageReqVO extends PageParam {
16 16
  17 + /**
  18 + * 所有
  19 + */
  20 + public static final Integer ALL = 0;
  21 +
  22 + /**
  23 + * 所有数量 key
  24 + */
  25 + public static final String ALL_COUNT = "allCount";
  26 +
  27 + /**
  28 + * 好评
  29 + */
  30 + public static final Integer FAVOURABLE_COMMENT = 1;
  31 +
  32 + /**
  33 + * 好评数量 key
  34 + */
  35 + public static final String FAVOURABLE_COMMENT_COUNT = "favourableCommentCount";
  36 +
  37 + /**
  38 + * 中评
  39 + */
  40 + public static final Integer MEDIOCRE_COMMENT = 2;
  41 +
  42 + /**
  43 + * 中评数量 key
  44 + */
  45 + public static final String MEDIOCRE_COMMENT_COUNT = "mediocreCommentCount";
  46 +
  47 + /**
  48 + * 差评
  49 + */
  50 + public static final Integer NEGATIVE_COMMENT = 3;
  51 +
  52 + /**
  53 + * 差评数量 key
  54 + */
  55 + public static final String NEGATIVE_COMMENT_COUNT = "negativeCommentCount";
  56 +
  57 + /**
  58 + * 默认匿名昵称
  59 + */
  60 + public static final String ANONYMOUS_NICKNAME = "匿名用户";
  61 +
17 @Schema(description = "商品SPU编号", example = "29502") 62 @Schema(description = "商品SPU编号", example = "29502")
18 @NotNull(message = "商品SPU编号不能为空") 63 @NotNull(message = "商品SPU编号不能为空")
19 private Long spuId; 64 private Long spuId;
20 65
  66 + @Schema(description = "app 评论页 tab 类型 (0 全部、1 好评、2 中评、3 差评)", example = "0")
  67 + @NotNull(message = "商品SPU编号不能为空")
  68 + private Integer type;
  69 +
21 } 70 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppCommentRespVO.java
@@ -5,7 +5,6 @@ import lombok.Data; @@ -5,7 +5,6 @@ import lombok.Data;
5 import lombok.EqualsAndHashCode; 5 import lombok.EqualsAndHashCode;
6 import lombok.ToString; 6 import lombok.ToString;
7 7
8 -import javax.validation.constraints.NotNull;  
9 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
10 import java.util.List; 9 import java.util.List;
11 10
@@ -27,7 +26,7 @@ public class AppCommentRespVO extends AppCommentBaseVO { @@ -27,7 +26,7 @@ public class AppCommentRespVO extends AppCommentBaseVO {
27 @Schema(description = "订单项编号", required = true, example = "24965") 26 @Schema(description = "订单项编号", required = true, example = "24965")
28 private Long id; 27 private Long id;
29 28
30 - @Schema(description = "是否匿名:[0:不匿名 1:匿名]", required = true) 29 + @Schema(description = "是否匿名", required = true)
31 private Boolean anonymous; 30 private Boolean anonymous;
32 31
33 @Schema(description = "交易订单编号", required = true, example = "24428") 32 @Schema(description = "交易订单编号", required = true, example = "24428")
@@ -36,7 +35,7 @@ public class AppCommentRespVO extends AppCommentBaseVO { @@ -36,7 +35,7 @@ public class AppCommentRespVO extends AppCommentBaseVO {
36 @Schema(description = "交易订单项编号", required = true, example = "8233") 35 @Schema(description = "交易订单项编号", required = true, example = "8233")
37 private Long orderItemId; 36 private Long orderItemId;
38 37
39 - @Schema(description = "商家是否回复:[1:回复 0:未回复]", required = true) 38 + @Schema(description = "商家是否回复", required = true)
40 private Boolean replied; 39 private Boolean replied;
41 40
42 @Schema(description = "回复管理员编号", example = "22212") 41 @Schema(description = "回复管理员编号", example = "22212")
@@ -60,4 +59,6 @@ public class AppCommentRespVO extends AppCommentBaseVO { @@ -60,4 +59,6 @@ public class AppCommentRespVO extends AppCommentBaseVO {
60 @Schema(description = "创建时间", required = true) 59 @Schema(description = "创建时间", required = true)
61 private LocalDateTime createTime; 60 private LocalDateTime createTime;
62 61
  62 + @Schema(description = "最终评分", required = true)
  63 + private Integer finalScore;
63 } 64 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
@@ -6,11 +6,7 @@ import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetail @@ -6,11 +6,7 @@ import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetail
6 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO; 6 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
7 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; 7 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
8 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; 8 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
9 -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;  
10 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 9 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
11 -import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;  
12 -import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;  
13 -import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;  
14 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; 10 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
15 import io.swagger.v3.oas.annotations.Operation; 11 import io.swagger.v3.oas.annotations.Operation;
16 import io.swagger.v3.oas.annotations.Parameter; 12 import io.swagger.v3.oas.annotations.Parameter;
@@ -23,12 +19,8 @@ import org.springframework.web.bind.annotation.RestController; @@ -23,12 +19,8 @@ import org.springframework.web.bind.annotation.RestController;
23 19
24 import javax.annotation.Resource; 20 import javax.annotation.Resource;
25 import javax.validation.Valid; 21 import javax.validation.Valid;
26 -import java.util.List;  
27 22
28 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
30 -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_ENABLE;  
31 -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;  
32 24
33 @Tag(name = "用户 APP - 商品 SPU") 25 @Tag(name = "用户 APP - 商品 SPU")
34 @RestController 26 @RestController
@@ -38,10 +30,6 @@ public class AppProductSpuController { @@ -38,10 +30,6 @@ public class AppProductSpuController {
38 30
39 @Resource 31 @Resource
40 private ProductSpuService productSpuService; 32 private ProductSpuService productSpuService;
41 - @Resource  
42 - private ProductSkuService productSkuService;  
43 - @Resource  
44 - private ProductPropertyValueService productPropertyValueService;  
45 33
46 @GetMapping("/page") 34 @GetMapping("/page")
47 @Operation(summary = "获得商品 SPU 分页") 35 @Operation(summary = "获得商品 SPU 分页")
@@ -50,23 +38,12 @@ public class AppProductSpuController { @@ -50,23 +38,12 @@ public class AppProductSpuController {
50 return success(ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult)); 38 return success(ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult));
51 } 39 }
52 40
  41 + // TODO 芋艿:等会看看
53 @GetMapping("/get-detail") 42 @GetMapping("/get-detail")
54 @Operation(summary = "获得商品 SPU 明细") 43 @Operation(summary = "获得商品 SPU 明细")
55 @Parameter(name = "id", description = "编号", required = true) 44 @Parameter(name = "id", description = "编号", required = true)
56 public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) { 45 public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
57 - // 获得商品 SPU  
58 - ProductSpuDO spu = productSpuService.getSpu(id);  
59 - if (spu == null) {  
60 - throw exception(SPU_NOT_EXISTS);  
61 - }  
62 - if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {  
63 - throw exception(SPU_NOT_ENABLE);  
64 - }  
65 -  
66 - // 查询商品 SKU  
67 - List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());  
68 - // 拼接  
69 - return success(ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus)); 46 + return success(productSpuService.getAppProductSpuDetail(id));
70 } 47 }
71 48
72 } 49 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java
@@ -22,6 +22,15 @@ import lombok.*; @@ -22,6 +22,15 @@ import lombok.*;
22 public class ProductPropertyDO extends BaseDO { 22 public class ProductPropertyDO extends BaseDO {
23 23
24 /** 24 /**
  25 + * 默认属性id
  26 + */
  27 + public static final Long PROPERTY_ID = 0L;
  28 + /**
  29 + * 默认属性名字
  30 + */
  31 + public static final String PROPERTY_NAME = "默认";
  32 +
  33 + /**
25 * 主键 34 * 主键
26 */ 35 */
27 @TableId 36 @TableId
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java
@@ -23,6 +23,15 @@ import lombok.*; @@ -23,6 +23,15 @@ import lombok.*;
23 public class ProductPropertyValueDO extends BaseDO { 23 public class ProductPropertyValueDO extends BaseDO {
24 24
25 /** 25 /**
  26 + * 默认属性值id
  27 + */
  28 + public static final Long VALUE_ID = 0L;
  29 + /**
  30 + * 默认属性值名字
  31 + */
  32 + public static final String VALUE_NAME = "默认";
  33 +
  34 + /**
26 * 主键 35 * 主键
27 */ 36 */
28 @TableId 37 @TableId
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java
1 package cn.iocoder.yudao.module.product.dal.mysql.comment; 1 package cn.iocoder.yudao.module.product.dal.mysql.comment;
2 2
3 3
  4 +import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -34,11 +35,33 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; { @@ -34,11 +35,33 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; {
34 .orderByDesc(ProductCommentDO::getId)); 35 .orderByDesc(ProductCommentDO::getId));
35 } 36 }
36 37
  38 + static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
  39 + // 构建好评查询语句
  40 + if (ObjectUtil.equal(type, AppCommentPageReqVO.FAVOURABLE_COMMENT)) {
  41 + // 好评计算 (商品评分星级+服务评分星级) >= 8
  42 + queryWrapper.apply("(scores + benefit_scores) >= 8");
  43 + }
  44 + // 构建中评查询语句
  45 + if (ObjectUtil.equal(type, AppCommentPageReqVO.MEDIOCRE_COMMENT)) {
  46 + // 中评计算 (商品评分星级+服务评分星级) > 4 且 (商品评分星级+服务评分星级) < 8
  47 + queryWrapper.apply("(scores + benefit_scores) > 4 and (scores + benefit_scores) < 8");
  48 + }
  49 + // 构建差评查询语句
  50 + if (ObjectUtil.equal(type, AppCommentPageReqVO.NEGATIVE_COMMENT)) {
  51 + // 差评计算 (商品评分星级+服务评分星级) <= 4
  52 + queryWrapper.apply("(scores + benefit_scores) <= 4");
  53 + }
  54 + }
  55 +
37 default PageResult<ProductCommentDO> selectPage(AppCommentPageReqVO reqVO, Boolean visible) { 56 default PageResult<ProductCommentDO> selectPage(AppCommentPageReqVO reqVO, Boolean visible) {
38 - return selectPage(reqVO, new LambdaQueryWrapperX<ProductCommentDO>() 57 + LambdaQueryWrapperX<ProductCommentDO> queryWrapper = new LambdaQueryWrapperX<ProductCommentDO>()
39 .eqIfPresent(ProductCommentDO::getSpuId, reqVO.getSpuId()) 58 .eqIfPresent(ProductCommentDO::getSpuId, reqVO.getSpuId())
40 - .eqIfPresent(ProductCommentDO::getVisible, visible)  
41 - .orderByDesc(ProductCommentDO::getId)); 59 + .eqIfPresent(ProductCommentDO::getVisible, visible);
  60 + // 构建评价查询语句
  61 + appendTabQuery(queryWrapper, reqVO.getType());
  62 + // 按评价时间排序最新的显示在前面
  63 + queryWrapper.orderByDesc(ProductCommentDO::getCreateTime);
  64 + return selectPage(reqVO, queryWrapper);
42 } 65 }
43 66
44 default void updateCommentVisible(Long id, Boolean visible) { 67 default void updateCommentVisible(Long id, Boolean visible) {
@@ -74,4 +97,13 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; { @@ -74,4 +97,13 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; {
74 update(null, lambdaUpdateWrapper); 97 update(null, lambdaUpdateWrapper);
75 } 98 }
76 99
  100 + default Long selectTabCount(Long spuId, Boolean visible, Integer type) {
  101 + LambdaQueryWrapperX<ProductCommentDO> queryWrapper = new LambdaQueryWrapperX<ProductCommentDO>()
  102 + .eqIfPresent(ProductCommentDO::getSpuId, spuId)
  103 + .eqIfPresent(ProductCommentDO::getVisible, visible);
  104 + // 构建评价查询语句
  105 + appendTabQuery(queryWrapper, type);
  106 + return selectCount(queryWrapper);
  107 + }
  108 +
77 } 109 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java
1 package cn.iocoder.yudao.module.product.dal.mysql.sku; 1 package cn.iocoder.yudao.module.product.dal.mysql.sku;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
5 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 6 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
9 -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;  
10 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 7 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 8 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
12 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
@@ -63,23 +60,4 @@ public interface ProductSkuMapper extends BaseMapperX&lt;ProductSkuDO&gt; { @@ -63,23 +60,4 @@ public interface ProductSkuMapper extends BaseMapperX&lt;ProductSkuDO&gt; {
63 return selectList(new QueryWrapper<ProductSkuDO>().apply("stock <= warn_stock")); 60 return selectList(new QueryWrapper<ProductSkuDO>().apply("stock <= warn_stock"));
64 } 61 }
65 62
66 - // TODO @puhui999:貌似 IN 不出来数据哈。直接全部查询出来,处理就好列;  
67 - /**  
68 - * 更新 sku 属性值时使用的分页查询  
69 - *  
70 - * @param pageParam 页面参数  
71 - * @return {@link PageResult}<{@link ProductSkuDO}>  
72 - */  
73 - default PageResult<ProductSkuDO> selectPage(PageParam pageParam) {  
74 - return selectPage(pageParam, new LambdaQueryWrapper<ProductSkuDO>().isNotNull(ProductSkuDO::getProperties));  
75 - }  
76 -  
77 - /**  
78 - * 查询 sku properties 不等于 null 的数量  
79 - *  
80 - * @return {@link Long}  
81 - */  
82 - default Long selectCountByPropertyNotNull() {  
83 - return selectCount(new LambdaQueryWrapper<ProductSkuDO>().isNotNull(ProductSkuDO::getProperties));  
84 - }  
85 } 63 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java
@@ -68,7 +68,10 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { @@ -68,7 +68,10 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
68 throw exception(CATEGORY_EXISTS_CHILDREN); 68 throw exception(CATEGORY_EXISTS_CHILDREN);
69 } 69 }
70 // 校验分类是否绑定了 SPU 70 // 校验分类是否绑定了 SPU
71 - validateProductCategoryIsHaveBindSpu(id); 71 + Long count = productSpuService.getSpuCountByCategoryId(id);
  72 + if (0 != count) {
  73 + throw exception(CATEGORY_HAVE_BIND_SPU);
  74 + }
72 // 删除 75 // 删除
73 productCategoryMapper.deleteById(id); 76 productCategoryMapper.deleteById(id);
74 } 77 }
@@ -96,14 +99,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { @@ -96,14 +99,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
96 } 99 }
97 } 100 }
98 101
99 - // TODO @puhui999:不用抽方法,因为不太会复用这个方法哈。  
100 - private void validateProductCategoryIsHaveBindSpu(Long id) {  
101 - Long count = productSpuService.getSpuCountByCategoryId(id);  
102 - if (0 != count) {  
103 - throw exception(CATEGORY_HAVE_BIND_SPU);  
104 - }  
105 - }  
106 -  
107 @Override 102 @Override
108 public ProductCategoryDO getCategory(Long id) { 103 public ProductCategoryDO getCategory(Long id) {
109 return productCategoryMapper.selectById(id); 104 return productCategoryMapper.selectById(id);
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java
@@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen @@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen
7 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; 7 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
8 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO; 8 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO;
9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; 9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
  10 +import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
10 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; 11 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
11 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
12 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
13 14
  15 +import java.util.Map;
  16 +
14 /** 17 /**
15 * 商品评论 Service 接口 18 * 商品评论 Service 接口
16 * 19 *
@@ -50,7 +53,7 @@ public interface ProductCommentService { @@ -50,7 +53,7 @@ public interface ProductCommentService {
50 * @param visible 是否可见 53 * @param visible 是否可见
51 * @return 商品评价分页 54 * @return 商品评价分页
52 */ 55 */
53 - PageResult<ProductCommentDO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible); 56 + PageResult<AppCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
54 57
55 /** 58 /**
56 * 创建商品评论 59 * 创建商品评论
@@ -68,4 +71,12 @@ public interface ProductCommentService { @@ -68,4 +71,12 @@ public interface ProductCommentService {
68 */ 71 */
69 void additionalComment(MemberUserRespDTO user, AppCommentAdditionalReqVO createReqVO); 72 void additionalComment(MemberUserRespDTO user, AppCommentAdditionalReqVO createReqVO);
70 73
  74 + /**
  75 + * 评论页面标签数
  76 + *
  77 + * @param spuId spu id
  78 + * @param visible 是否可见
  79 + * @return 获得商品评价分页 tab count
  80 + */
  81 + Map<String, Long> getCommentPageTabsCount(Long spuId, Boolean visible);
71 } 82 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.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.util.ObjectUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 5 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
5 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO; 6 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentPageReqVO;
@@ -7,17 +8,28 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen @@ -7,17 +8,28 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen
7 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; 8 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
8 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO; 9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO;
9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; 10 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
  11 +import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
  12 +import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
10 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; 13 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
  14 +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
11 import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; 15 import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
  16 +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
  17 +import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
  18 +import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
12 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
13 import org.springframework.util.StringUtils; 20 import org.springframework.util.StringUtils;
14 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
15 22
16 import javax.annotation.Resource; 23 import javax.annotation.Resource;
  24 +import java.math.BigDecimal;
  25 +import java.math.RoundingMode;
  26 +import java.util.HashMap;
  27 +import java.util.Map;
17 import java.util.Objects; 28 import java.util.Objects;
18 29
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; 31 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
  32 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;
21 33
22 /** 34 /**
23 * 商品评论 Service 实现类 35 * 商品评论 Service 实现类
@@ -30,6 +42,11 @@ public class ProductCommentServiceImpl implements ProductCommentService { @@ -30,6 +42,11 @@ public class ProductCommentServiceImpl implements ProductCommentService {
30 42
31 @Resource 43 @Resource
32 private ProductCommentMapper productCommentMapper; 44 private ProductCommentMapper productCommentMapper;
  45 + @Resource
  46 + private TradeOrderApi tradeOrderApi;
  47 +
  48 + @Resource
  49 + private ProductSpuService productSpuService;
33 50
34 @Override 51 @Override
35 public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) { 52 public PageResult<ProductCommentDO> getCommentPage(ProductCommentPageReqVO pageReqVO) {
@@ -53,13 +70,48 @@ public class ProductCommentServiceImpl implements ProductCommentService { @@ -53,13 +70,48 @@ public class ProductCommentServiceImpl implements ProductCommentService {
53 } 70 }
54 71
55 @Override 72 @Override
56 - public PageResult<ProductCommentDO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {  
57 - return productCommentMapper.selectPage(pageVO, visible); 73 + public Map<String, Long> getCommentPageTabsCount(Long spuId, Boolean visible) {
  74 + Map<String, Long> countMap = new HashMap<>(4);
  75 + // 查询商品 id = spuId 的所有评论数量
  76 + countMap.put(AppCommentPageReqVO.ALL_COUNT, productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.ALL));
  77 + // 查询商品 id = spuId 的所有好评数量
  78 + countMap.put(AppCommentPageReqVO.FAVOURABLE_COMMENT_COUNT, productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.FAVOURABLE_COMMENT));
  79 + // 查询商品 id = spuId 的所有中评数量
  80 + countMap.put(AppCommentPageReqVO.MEDIOCRE_COMMENT_COUNT, productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.MEDIOCRE_COMMENT));
  81 + // 查询商品 id = spuId 的所有差评数量
  82 + countMap.put(AppCommentPageReqVO.NEGATIVE_COMMENT_COUNT, productCommentMapper.selectTabCount(spuId, visible, AppCommentPageReqVO.NEGATIVE_COMMENT));
  83 + return countMap;
  84 + }
  85 +
  86 + @Override
  87 + public PageResult<AppCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {
  88 + PageResult<AppCommentRespVO> result = ProductCommentConvert.INSTANCE.convertPage02(productCommentMapper.selectPage(pageVO, visible));
  89 + result.getList().forEach(item -> {
  90 + // 判断用户是否选择匿名
  91 + if (ObjectUtil.equal(item.getAnonymous(), true)) {
  92 + item.setUserNickname(AppCommentPageReqVO.ANONYMOUS_NICKNAME);
  93 + }
  94 + // 计算评价最终综合评分 最终星数 = (商品评星 + 服务评星) / 2
  95 + BigDecimal sumScore = new BigDecimal(item.getScores() + item.getBenefitScores());
  96 + BigDecimal divide = sumScore.divide(BigDecimal.valueOf(2L), 0, RoundingMode.DOWN);
  97 + item.setFinalScore(divide.intValue());
  98 + });
  99 + return result;
58 } 100 }
59 101
60 @Override 102 @Override
61 public void createComment(ProductCommentDO productComment, Boolean system) { 103 public void createComment(ProductCommentDO productComment, Boolean system) {
62 if (!system) { 104 if (!system) {
  105 + // TODO 判断订单是否存在 fix
  106 + TradeOrderRespDTO order = tradeOrderApi.getOrder(productComment.getOrderId());
  107 + if (null == order) {
  108 + throw exception(ORDER_NOT_FOUND);
  109 + }
  110 + // TODO 判断 SPU 是否存在 fix
  111 + ProductSpuDO spu = productSpuService.getSpu(productComment.getSpuId());
  112 + if (null == spu) {
  113 + throw exception(SPU_NOT_EXISTS);
  114 + }
63 // 判断当前订单的当前商品用户是否评价过 115 // 判断当前订单的当前商品用户是否评价过
64 ProductCommentDO exist = productCommentMapper.findByUserIdAndOrderIdAndSpuId(productComment.getId(), productComment.getOrderId(), productComment.getSpuId()); 116 ProductCommentDO exist = productCommentMapper.findByUserIdAndOrderIdAndSpuId(productComment.getId(), productComment.getOrderId(), productComment.getSpuId());
65 if (null != exist) { 117 if (null != exist) {
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java
@@ -71,8 +71,8 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { @@ -71,8 +71,8 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
71 // 更新 71 // 更新
72 ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO); 72 ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO);
73 productPropertyMapper.updateById(updateObj); 73 productPropertyMapper.updateById(updateObj);
74 - // TODO @puhui:是不是只要传递变量,不传递整个 updateObj 变量哈  
75 - productSkuService.updateSkuProperty(updateObj); 74 + // 更新 sku 相关属性
  75 + productSkuService.updateSkuProperty(updateObj.getId(), updateObj.getName());
76 } 76 }
77 77
78 @Override 78 @Override
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java
@@ -73,10 +73,8 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ @@ -73,10 +73,8 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
73 // 更新 73 // 更新
74 ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO); 74 ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO);
75 productPropertyValueMapper.updateById(updateObj); 75 productPropertyValueMapper.updateById(updateObj);
76 -  
77 - // TODO 芋艿:更新时,需要看看 sku 表 fix  
78 - // TODO @puhui:是不是只要传递变量,不传递整个 updateObj 变量哈  
79 - productSkuService.updateSkuPropertyValue(updateObj); 76 + // 更新 sku 相关属性
  77 + productSkuService.updateSkuPropertyValue(updateObj.getId(), updateObj.getName());
80 } 78 }
81 79
82 @Override 80 @Override
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java
@@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.product.service.sku; @@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.product.service.sku;
2 2
3 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; 3 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
4 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; 4 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
5 -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;  
6 -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;  
7 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 5 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
8 6
9 import java.util.Collection; 7 import java.util.Collection;
@@ -111,16 +109,18 @@ public interface ProductSkuService { @@ -111,16 +109,18 @@ public interface ProductSkuService {
111 /** 109 /**
112 * 更新 sku 属性 110 * 更新 sku 属性
113 * 111 *
114 - * @param updateObj 属性对象 112 + * @param propertyId 属性 id
  113 + * @param propertyName 属性名
115 * @return int 影响的行数 114 * @return int 影响的行数
116 */ 115 */
117 - int updateSkuProperty(ProductPropertyDO updateObj); 116 + int updateSkuProperty(Long propertyId, String propertyName);
118 117
119 /** 118 /**
120 * 更新 sku 属性值 119 * 更新 sku 属性值
121 * 120 *
122 - * @param updateObj 属性值对象 121 + * @param propertyValueId 属性值 id
  122 + * @param propertyValueName 属性值名字
123 * @return int 影响的行数 123 * @return int 影响的行数
124 */ 124 */
125 - int updateSkuPropertyValue(ProductPropertyValueDO updateObj); 125 + int updateSkuPropertyValue(Long propertyValueId, String propertyValueName);
126 } 126 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
@@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.product.service.sku; @@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.product.service.sku;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
5 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
6 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
7 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; 5 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
  6 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
8 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; 7 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
9 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; 8 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
10 import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; 9 import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
@@ -80,16 +79,25 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -80,16 +79,25 @@ public class ProductSkuServiceImpl implements ProductSkuService {
80 79
81 @Override 80 @Override
82 public void validateSkuList(List<ProductSkuCreateOrUpdateReqVO> skus, Boolean specType) { 81 public void validateSkuList(List<ProductSkuCreateOrUpdateReqVO> skus, Boolean specType) {
83 - // 非多规格,不需要校验  
84 - if (ObjectUtil.notEqual(specType, true)) {  
85 - return;  
86 - }  
87 -  
88 // 0、校验skus是否为空 82 // 0、校验skus是否为空
89 if (CollUtil.isEmpty(skus)) { 83 if (CollUtil.isEmpty(skus)) {
90 throw exception(SKU_NOT_EXISTS); 84 throw exception(SKU_NOT_EXISTS);
91 } 85 }
92 - 86 + // 单规格处理
  87 + if (ObjectUtil.equal(specType, false)) {
  88 + ProductSkuCreateOrUpdateReqVO skuVO = skus.get(0);
  89 + // 赋予单规格默认属性
  90 + List<ProductSkuBaseVO.Property> properties = new ArrayList<>();
  91 + ProductSkuBaseVO.Property property = new ProductSkuBaseVO.Property();
  92 + property.setPropertyId(ProductPropertyDO.PROPERTY_ID);
  93 + property.setPropertyName(ProductPropertyDO.PROPERTY_NAME);
  94 + property.setValueId(ProductPropertyValueDO.VALUE_ID);
  95 + property.setValueName(ProductPropertyValueDO.VALUE_NAME);
  96 + properties.add(property);
  97 + skuVO.setProperties(properties);
  98 + // 单规格不需要后续的校验
  99 + return;
  100 + }
93 // 1、校验属性项存在 101 // 1、校验属性项存在
94 Set<Long> propertyIds = skus.stream().filter(p -> p.getProperties() != null) 102 Set<Long> propertyIds = skus.stream().filter(p -> p.getProperties() != null)
95 // 遍历多个 Property 属性 103 // 遍历多个 Property 属性
@@ -156,81 +164,51 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -156,81 +164,51 @@ public class ProductSkuServiceImpl implements ProductSkuService {
156 } 164 }
157 165
158 @Override 166 @Override
159 - public int updateSkuProperty(ProductPropertyDO updateObj) {  
160 - // TODO 看了一下数据库有关于 json 字符串的处理,怕数据库出现兼容问题这里还是用数据库常规操作来实现  
161 - // TODO @puhui999:直接全部查询处理,批量处理就好列;一般项目的商品不会超过几十万的哈。  
162 - Long count = productSkuMapper.selectCountByPropertyNotNull();  
163 - int currentPage = 1; 167 + public int updateSkuProperty(Long propertyId, String propertyName) {
  168 + // 获取所有的 sku
  169 + List<ProductSkuDO> skuDOList = productSkuMapper.selectList();
  170 + // 处理后需要更新的 sku
164 List<ProductSkuDO> updateSkus = new ArrayList<>(); 171 List<ProductSkuDO> updateSkus = new ArrayList<>();
165 - if (count == 0) { 172 + if (CollUtil.isEmpty(skuDOList)) {
166 return 0; 173 return 0;
167 } 174 }
168 - int pageSize = 100;  
169 - for (int i = 0; i <= count / 100; i++) {  
170 - PageParam pageParam = new PageParam().setPageNo(currentPage + i).setPageSize(pageSize);  
171 - // 分页查找出 sku 属性不为 null 的  
172 - PageResult<ProductSkuDO> skuPage = productSkuMapper.selectPage(pageParam);  
173 - List<ProductSkuDO> records = skuPage.getList();  
174 - if (CollUtil.isEmpty(records)) {  
175 - break;  
176 - }  
177 - records.stream().filter(sku -> sku.getProperties() != null)  
178 - .forEach(sku -> sku.getProperties().forEach(property -> {  
179 - if (property.getPropertyId().equals(updateObj.getId())) {  
180 - property.setPropertyName(updateObj.getName());  
181 - updateSkus.add(sku);  
182 - }  
183 - }));  
184 - } 175 + skuDOList.stream().filter(sku -> sku.getProperties() != null)
  176 + .forEach(sku -> sku.getProperties().forEach(property -> {
  177 + if (property.getPropertyId().equals(propertyId)) {
  178 + property.setPropertyName(propertyName);
  179 + updateSkus.add(sku);
  180 + }
  181 + }));
185 if (CollUtil.isEmpty(updateSkus)) { 182 if (CollUtil.isEmpty(updateSkus)) {
186 return 0; 183 return 0;
187 } 184 }
188 - // TODO @puhui999:貌似 updateBatch 自己会拆分批次,这里不用再拆分了  
189 - // 每批处理的大小  
190 - int batchSize = 1000;  
191 - for (int i = 0; i < updateSkus.size(); i += batchSize) {  
192 - List<ProductSkuDO> batchSkuDOs = updateSkus.subList(i, Math.min(i + batchSize, updateSkus.size()));  
193 - productSkuMapper.updateBatch(batchSkuDOs, batchSize);  
194 - } 185 +
  186 + productSkuMapper.updateBatch(updateSkus);
195 return updateSkus.size(); 187 return updateSkus.size();
196 } 188 }
197 189
198 @Override 190 @Override
199 - public int updateSkuPropertyValue(ProductPropertyValueDO updateObj) {  
200 - // TODO 看了一下数据库有关于 json 字符串的处理,怕数据库出现兼容问题这里还是用数据库常规操作来实现  
201 - Long count = productSkuMapper.selectCountByPropertyNotNull();  
202 - int currentPage = 1; 191 + public int updateSkuPropertyValue(Long propertyValueId, String propertyValueName) {
  192 + // 获取所有的 sku
  193 + List<ProductSkuDO> skuDOList = productSkuMapper.selectList();
  194 + // 处理后需要更新的 sku
203 List<ProductSkuDO> updateSkus = new ArrayList<>(); 195 List<ProductSkuDO> updateSkus = new ArrayList<>();
204 - if (count == 0) { 196 + if (CollUtil.isEmpty(skuDOList)) {
205 return 0; 197 return 0;
206 } 198 }
207 - int pageSize = 100;  
208 - for (int i = 0; i <= count / 100; i++) {  
209 - PageParam pageParam = new PageParam().setPageNo(currentPage + i).setPageSize(pageSize);  
210 - // 分页查找出 sku 属性不为 null 的  
211 - PageResult<ProductSkuDO> skuPage = productSkuMapper.selectPage(pageParam);  
212 - List<ProductSkuDO> records = skuPage.getList();  
213 - if (CollUtil.isEmpty(records)) {  
214 - break;  
215 - }  
216 - records.stream()  
217 - .filter(sku -> sku.getProperties() != null)  
218 - .forEach(sku -> sku.getProperties().forEach(property -> {  
219 - if (property.getValueId().equals(updateObj.getId())) {  
220 - property.setValueName(updateObj.getName());  
221 - updateSkus.add(sku);  
222 - }  
223 - }));  
224 - } 199 + skuDOList.stream()
  200 + .filter(sku -> sku.getProperties() != null)
  201 + .forEach(sku -> sku.getProperties().forEach(property -> {
  202 + if (property.getValueId().equals(propertyValueId)) {
  203 + property.setValueName(propertyValueName);
  204 + updateSkus.add(sku);
  205 + }
  206 + }));
225 if (CollUtil.isEmpty(updateSkus)) { 207 if (CollUtil.isEmpty(updateSkus)) {
226 return 0; 208 return 0;
227 } 209 }
228 - // 每批处理的大小  
229 - int batchSize = 1000;  
230 - for (int i = 0; i < updateSkus.size(); i += batchSize) {  
231 - List<ProductSkuDO> batchSkuDOs = updateSkus.subList(i, Math.min(i + batchSize, updateSkus.size()));  
232 - productSkuMapper.updateBatch(batchSkuDOs, batchSize);  
233 - } 210 +
  211 + productSkuMapper.updateBatch(updateSkus);
234 return updateSkus.size(); 212 return updateSkus.size();
235 } 213 }
236 214
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.service.spu; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.service.spu;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; 4 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  5 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
5 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; 6 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
6 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 7 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
7 8
@@ -135,4 +136,11 @@ public interface ProductSpuService { @@ -135,4 +136,11 @@ public interface ProductSpuService {
135 */ 136 */
136 Long getSpuCountByCategoryId(Long id); 137 Long getSpuCountByCategoryId(Long id);
137 138
  139 + /**
  140 + * 通过 spu id 获取商品 SPU 明细
  141 + *
  142 + * @param id id
  143 + * @return 用户 App - 商品 SPU 明细
  144 + */
  145 + AppProductSpuDetailRespVO getAppProductSpuDetail(Long id);
138 } 146 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
@@ -7,15 +7,21 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -7,15 +7,21 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; 7 import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;
8 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; 8 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
9 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; 9 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
  10 +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
10 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; 11 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
  12 +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
11 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; 13 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
12 import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; 14 import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
  15 +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
  16 +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
13 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 17 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
14 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 18 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
15 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; 19 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
16 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; 20 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
17 import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; 21 import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
18 import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; 22 import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
  23 +import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
  24 +import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
19 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; 25 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
20 import com.google.common.collect.Maps; 26 import com.google.common.collect.Maps;
21 import org.springframework.context.annotation.Lazy; 27 import org.springframework.context.annotation.Lazy;
@@ -51,6 +57,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -51,6 +57,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
51 private ProductBrandService brandService; 57 private ProductBrandService brandService;
52 @Resource 58 @Resource
53 private ProductCategoryService categoryService; 59 private ProductCategoryService categoryService;
  60 + @Resource
  61 + @Lazy // 循环依赖,避免报错
  62 + private ProductPropertyValueService productPropertyValueService;
54 63
55 @Override 64 @Override
56 @Transactional(rollbackFor = Exception.class) 65 @Transactional(rollbackFor = Exception.class)
@@ -141,7 +150,11 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -141,7 +150,11 @@ public class ProductSpuServiceImpl implements ProductSpuService {
141 // 校验存在 150 // 校验存在
142 validateSpuExists(id); 151 validateSpuExists(id);
143 // 校验商品状态不是回收站不能删除 152 // 校验商品状态不是回收站不能删除
144 - validateSpuStatus(id); 153 + ProductSpuDO spuDO = productSpuMapper.selectById(id);
  154 + // 判断 SPU 状态是否为回收站
  155 + if (ObjectUtil.notEqual(spuDO.getStatus(), ProductSpuStatusEnum.RECYCLE.getStatus())) {
  156 + throw exception(SPU_NOT_RECYCLE);
  157 + }
145 158
146 // 删除 SPU 159 // 删除 SPU
147 productSpuMapper.deleteById(id); 160 productSpuMapper.deleteById(id);
@@ -155,20 +168,6 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -155,20 +168,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
155 } 168 }
156 } 169 }
157 170
158 - /**  
159 - * 验证 SPU 状态是否为回收站  
160 - *  
161 - * @param id id  
162 - */  
163 - // TODO puhui999:感觉不用独立出来一个方法,直接在 deleteSpu 方法中校验即可  
164 - private void validateSpuStatus(Long id) {  
165 - ProductSpuDO spuDO = productSpuMapper.selectById(id);  
166 - // 判断 SPU 状态是否为回收站  
167 - if (ObjectUtil.notEqual(spuDO.getStatus(), ProductSpuStatusEnum.RECYCLE.getStatus())) {  
168 - throw exception(SPU_NOT_RECYCLE);  
169 - }  
170 - }  
171 -  
172 @Override 171 @Override
173 public ProductSpuDO getSpu(Long id) { 172 public ProductSpuDO getSpu(Long id) {
174 return productSpuMapper.selectById(id); 173 return productSpuMapper.selectById(id);
@@ -263,4 +262,35 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -263,4 +262,35 @@ public class ProductSpuServiceImpl implements ProductSpuService {
263 return productSpuMapper.selectCount(ProductSpuDO::getCategoryId, id); 262 return productSpuMapper.selectCount(ProductSpuDO::getCategoryId, id);
264 } 263 }
265 264
  265 + @Override
  266 + public AppProductSpuDetailRespVO getAppProductSpuDetail(Long id) {
  267 + // 获得商品 SPU
  268 + ProductSpuDO spu = getSpu(id);
  269 + if (spu == null) {
  270 + throw exception(SPU_NOT_EXISTS);
  271 + }
  272 + if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
  273 + throw exception(SPU_NOT_ENABLE);
  274 + }
  275 +
  276 + // 查询商品 SKU
  277 + List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
  278 + List<ProductPropertyValueDetailRespBO> propertyValues = new ArrayList<>();
  279 + // 单规格商品 赋予默认属性值
  280 + if (ObjectUtil.equal(spu.getSpecType(), false)) {
  281 + ProductPropertyValueDetailRespBO respBO = new ProductPropertyValueDetailRespBO();
  282 + respBO.setPropertyId(ProductPropertyDO.PROPERTY_ID);
  283 + respBO.setPropertyName(ProductPropertyDO.PROPERTY_NAME);
  284 + respBO.setValueId(ProductPropertyValueDO.VALUE_ID);
  285 + respBO.setValueName(ProductPropertyValueDO.VALUE_NAME);
  286 + propertyValues.add(respBO);
  287 + } else {
  288 + // 多规格商品则查询商品属性
  289 + propertyValues = productPropertyValueService
  290 + .getPropertyValueDetailList(ProductSkuConvert.INSTANCE.convertPropertyValueIds(skus));
  291 + }
  292 + // 拼接
  293 + return ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus, propertyValues);
  294 + }
  295 +
266 } 296 }
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java
@@ -11,15 +11,22 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen @@ -11,15 +11,22 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen
11 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; 11 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
12 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO; 12 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentAdditionalReqVO;
13 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; 13 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
  14 +import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentRespVO;
14 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert; 15 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
15 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; 16 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
16 import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; 17 import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
17 import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum; 18 import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;
  19 +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
  20 +import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
18 import org.junit.jupiter.api.Test; 21 import org.junit.jupiter.api.Test;
  22 +import org.springframework.boot.test.mock.mockito.MockBean;
19 import org.springframework.context.annotation.Import; 23 import org.springframework.context.annotation.Import;
  24 +import org.springframework.context.annotation.Lazy;
20 25
21 import javax.annotation.Resource; 26 import javax.annotation.Resource;
  27 +import java.time.LocalDateTime;
22 import java.util.Date; 28 import java.util.Date;
  29 +import java.util.Map;
23 30
24 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 31 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
25 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 32 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@@ -39,8 +46,14 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { @@ -39,8 +46,14 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
39 private ProductCommentMapper productCommentMapper; 46 private ProductCommentMapper productCommentMapper;
40 47
41 @Resource 48 @Resource
  49 + @Lazy
42 private ProductCommentServiceImpl productCommentService; 50 private ProductCommentServiceImpl productCommentService;
43 51
  52 + @MockBean
  53 + private TradeOrderApi tradeOrderApi;
  54 + @MockBean
  55 + private ProductSpuService productSpuService;
  56 +
44 public String generateNo() { 57 public String generateNo() {
45 return DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomInt(100000, 999999); 58 return DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomInt(100000, 999999);
46 } 59 }
@@ -70,6 +83,23 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { @@ -70,6 +83,23 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
70 o.setScores(ProductCommentScoresEnum.FOUR.getScores()); 83 o.setScores(ProductCommentScoresEnum.FOUR.getScores());
71 o.setReplied(Boolean.TRUE); 84 o.setReplied(Boolean.TRUE);
72 o.setVisible(Boolean.TRUE); 85 o.setVisible(Boolean.TRUE);
  86 + o.setId(generateId());
  87 + o.setUserId(generateId());
  88 + o.setAnonymous(Boolean.TRUE);
  89 + o.setOrderId(generateId());
  90 + o.setOrderItemId(generateId());
  91 + o.setSpuId(generateId());
  92 + o.setSkuId(generateId());
  93 + o.setDescriptionScores(ProductCommentScoresEnum.FOUR.getScores());
  94 + o.setBenefitScores(ProductCommentScoresEnum.FOUR.getScores());
  95 + o.setDeliveryScores(ProductCommentScoresEnum.FOUR.getScores());
  96 + o.setContent("真好吃");
  97 + o.setReplyUserId(generateId());
  98 + o.setReplyContent("确实");
  99 + o.setReplyTime(LocalDateTime.now());
  100 + o.setAdditionalTime(LocalDateTime.now());
  101 + o.setCreateTime(LocalDateTime.now());
  102 + o.setUpdateTime(LocalDateTime.now());
73 }); 103 });
74 productCommentMapper.insert(productComment); 104 productCommentMapper.insert(productComment);
75 105
@@ -77,7 +107,7 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { @@ -77,7 +107,7 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
77 Long spuId = productComment.getSpuId(); 107 Long spuId = productComment.getSpuId();
78 108
79 // 测试 userNickname 不匹配 109 // 测试 userNickname 不匹配
80 - productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setUserNickname("王三"))); 110 + productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setUserNickname("王三").setScores(ProductCommentScoresEnum.ONE.getScores())));
81 // 测试 orderId 不匹配 111 // 测试 orderId 不匹配
82 productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setOrderId(generateId()))); 112 productCommentMapper.insert(cloneIgnoreId(productComment, o -> o.setOrderId(generateId())));
83 // 测试 spuId 不匹配 113 // 测试 spuId 不匹配
@@ -107,8 +137,25 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { @@ -107,8 +137,25 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
107 PageResult<ProductCommentDO> all = productCommentService.getCommentPage(new ProductCommentPageReqVO()); 137 PageResult<ProductCommentDO> all = productCommentService.getCommentPage(new ProductCommentPageReqVO());
108 assertEquals(8, all.getTotal()); 138 assertEquals(8, all.getTotal());
109 139
110 - PageResult<ProductCommentDO> visible = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE);  
111 - assertEquals(7, visible.getTotal()); 140 + // 测试获取所有商品分页评论数据
  141 + PageResult<AppCommentRespVO> result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE);
  142 + assertEquals(7, result1.getTotal());
  143 +
  144 + // 测试获取所有商品分页中评数据
  145 + PageResult<AppCommentRespVO> result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
  146 + assertEquals(2, result2.getTotal());
  147 +
  148 + // 测试获取指定 spuId 商品分页中评数据
  149 + PageResult<AppCommentRespVO> result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
  150 + assertEquals(2, result3.getTotal());
  151 +
  152 + // 测试分页 tab count
  153 + Map<String, Long> tabsCount = productCommentService.getCommentPageTabsCount(spuId, Boolean.TRUE);
  154 + assertEquals(6, tabsCount.get(AppCommentPageReqVO.ALL_COUNT));
  155 + assertEquals(4, tabsCount.get(AppCommentPageReqVO.FAVOURABLE_COMMENT_COUNT));
  156 + assertEquals(2, tabsCount.get(AppCommentPageReqVO.MEDIOCRE_COMMENT_COUNT));
  157 + assertEquals(0, tabsCount.get(AppCommentPageReqVO.NEGATIVE_COMMENT_COUNT));
  158 +
112 } 159 }
113 160
114 @Test 161 @Test
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.api.order;
  2 +
  3 +import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
  4 +
  5 +/**
  6 + * 订单 API 接口
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +public interface TradeOrderApi {
  11 +
  12 + /**
  13 + * 获取订单通过订单 id
  14 + *
  15 + * @param id id
  16 + * @return 订单信息 Response DTO
  17 + */
  18 + TradeOrderRespDTO getOrder(Long id);
  19 +
  20 +}
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/dto/TradeOrderRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.api.order.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
  4 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;
  5 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
  6 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  7 +import lombok.Data;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +/**
  12 + * 订单信息 Response DTO
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Data
  17 +public class TradeOrderRespDTO {
  18 +
  19 + // ========== 订单基本信息 ==========
  20 + /**
  21 + * 订单编号,主键自增
  22 + */
  23 + private Long id;
  24 + /**
  25 + * 订单流水号
  26 + * <p>
  27 + * 例如说,1146347329394184195
  28 + */
  29 + private String no;
  30 + /**
  31 + * 订单类型
  32 + * <p>
  33 + * 枚举 {@link TradeOrderTypeEnum}
  34 + */
  35 + private Integer type;
  36 + /**
  37 + * 订单来源
  38 + * <p>
  39 + * 枚举 {@link TerminalEnum}
  40 + */
  41 + private Integer terminal;
  42 + /**
  43 + * 用户编号
  44 + * <p>
  45 + * 关联 MemberUserDO 的 id 编号
  46 + */
  47 + private Long userId;
  48 + /**
  49 + * 用户 IP
  50 + */
  51 + private String userIp;
  52 + /**
  53 + * 用户备注
  54 + */
  55 + private String userRemark;
  56 + /**
  57 + * 订单状态
  58 + * <p>
  59 + * 枚举 {@link TradeOrderStatusEnum}
  60 + */
  61 + private Integer status;
  62 + /**
  63 + * 购买的商品数量
  64 + */
  65 + private Integer productCount;
  66 + /**
  67 + * 订单完成时间
  68 + */
  69 + private LocalDateTime finishTime;
  70 + /**
  71 + * 订单取消时间
  72 + */
  73 + private LocalDateTime cancelTime;
  74 + /**
  75 + * 取消类型
  76 + * <p>
  77 + * 枚举 {@link TradeOrderCancelTypeEnum}
  78 + */
  79 + private Integer cancelType;
  80 + /**
  81 + * 商家备注
  82 + */
  83 + private String remark;
  84 + /**
  85 + * 是否评价
  86 + * <p>
  87 + * true - 已评价
  88 + * false - 未评价
  89 + */
  90 + private Boolean commentStatus;
  91 +
  92 +}
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.api;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-mall/yudao-module-trade-biz/pom.xml
@@ -23,7 +23,6 @@ @@ -23,7 +23,6 @@
23 <artifactId>yudao-module-trade-api</artifactId> 23 <artifactId>yudao-module-trade-api</artifactId>
24 <version>${revision}</version> 24 <version>${revision}</version>
25 </dependency> 25 </dependency>
26 -  
27 <dependency> 26 <dependency>
28 <groupId>cn.iocoder.boot</groupId> 27 <groupId>cn.iocoder.boot</groupId>
29 <artifactId>yudao-module-product-api</artifactId> 28 <artifactId>yudao-module-product-api</artifactId>
@@ -44,6 +43,11 @@ @@ -44,6 +43,11 @@
44 <artifactId>yudao-module-member-api</artifactId> 43 <artifactId>yudao-module-member-api</artifactId>
45 <version>${revision}</version> 44 <version>${revision}</version>
46 </dependency> 45 </dependency>
  46 + <dependency>
  47 + <groupId>cn.iocoder.boot</groupId>
  48 + <artifactId>yudao-module-system-api</artifactId>
  49 + <version>${revision}</version>
  50 + </dependency>
47 51
48 <!-- 业务组件 --> 52 <!-- 业务组件 -->
49 <dependency> 53 <dependency>
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.api.order;
  2 +
  3 +import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
  4 +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
  5 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
  6 +import org.springframework.stereotype.Service;
  7 +import org.springframework.validation.annotation.Validated;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 订单 API 接口实现类
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Service
  17 +@Validated
  18 +public class TradeOrderApiImpl implements TradeOrderApi {
  19 +
  20 + @Resource
  21 + private TradeOrderService tradeOrderService;
  22 +
  23 + @Override
  24 + public TradeOrderRespDTO getOrder(Long id) {
  25 + return TradeOrderConvert.INSTANCE.convert(tradeOrderService.getOrder(id));
  26 + }
  27 +
  28 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.api;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java
@@ -70,14 +70,14 @@ public class DeliveryExpressTemplateController { @@ -70,14 +70,14 @@ public class DeliveryExpressTemplateController {
70 return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list)); 70 return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list));
71 } 71 }
72 72
73 - // TODO @puhui999:DeliveryExpressTemplateRespVO 搞个 simple 的哈 73 + // TODO @puhui999:DeliveryExpressTemplateRespVO 搞个 simple 的哈 fix
74 @GetMapping("/list-all-simple") 74 @GetMapping("/list-all-simple")
75 @Operation(summary = "获取快递模版精简信息列表", description = "主要用于前端的下拉选项") 75 @Operation(summary = "获取快递模版精简信息列表", description = "主要用于前端的下拉选项")
76 - public CommonResult<List<DeliveryExpressTemplateRespVO>> getSimpleTemplateList() { 76 + public CommonResult<List<DeliveryExpressTemplateSimpleRespVO>> getSimpleTemplateList() {
77 // 获取运费模版列表,只要开启状态的 77 // 获取运费模版列表,只要开启状态的
78 List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(); 78 List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList();
79 // 排序后,返回给前端 79 // 排序后,返回给前端
80 - return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list)); 80 + return success(DeliveryExpressTemplateConvert.INSTANCE.convertList1(list));
81 } 81 }
82 82
83 @GetMapping("/page") 83 @GetMapping("/page")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +
  9 +@Schema(description = "管理后台 - 模版精简信息 Response VO")
  10 +@Data
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +public class DeliveryExpressTemplateSimpleRespVO {
  14 +
  15 + @Schema(description = "模版编号", required = true, example = "1024")
  16 + private Long id;
  17 +
  18 + @Schema(description = "模板名称", required = true, example = "测试模版")
  19 + private String name;
  20 +
  21 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java
@@ -28,11 +28,13 @@ public interface DeliveryExpressTemplateConvert { @@ -28,11 +28,13 @@ public interface DeliveryExpressTemplateConvert {
28 28
29 List<DeliveryExpressTemplateRespVO> convertList(List<DeliveryExpressTemplateDO> list); 29 List<DeliveryExpressTemplateRespVO> convertList(List<DeliveryExpressTemplateDO> list);
30 30
  31 + List<DeliveryExpressTemplateSimpleRespVO> convertList1(List<DeliveryExpressTemplateDO> list);
  32 +
31 PageResult<DeliveryExpressTemplateRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page); 33 PageResult<DeliveryExpressTemplateRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page);
32 34
33 default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean, 35 default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean,
34 List<DeliveryExpressTemplateChargeDO> chargeList, 36 List<DeliveryExpressTemplateChargeDO> chargeList,
35 - List<DeliveryExpressTemplateFreeDO> freeList){ 37 + List<DeliveryExpressTemplateFreeDO> freeList) {
36 DeliveryExpressTemplateDetailRespVO respVO = convert2(bean); 38 DeliveryExpressTemplateDetailRespVO respVO = convert2(bean);
37 respVO.setTemplateCharge(convertTemplateChargeList(chargeList)); 39 respVO.setTemplateCharge(convertTemplateChargeList(chargeList));
38 respVO.setTemplateFree(convertTemplateFreeList(freeList)); 40 respVO.setTemplateFree(convertTemplateFreeList(freeList));
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
11 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; 11 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
12 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; 12 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
13 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; 13 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
  14 +import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
14 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO; 15 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
15 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; 16 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
16 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO; 17 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
@@ -62,6 +63,8 @@ public interface TradeOrderConvert { @@ -62,6 +63,8 @@ public interface TradeOrderConvert {
62 TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, 63 TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,
63 TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address); 64 TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address);
64 65
  66 + TradeOrderRespDTO convert(TradeOrderDO orderDO);
  67 +
65 default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) { 68 default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
66 return CollectionUtils.convertList(calculateRespBO.getItems(), item -> { 69 return CollectionUtils.convertList(calculateRespBO.getItems(), item -> {
67 TradeOrderItemDO orderItem = convert(item); 70 TradeOrderItemDO orderItem = convert(item);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java
@@ -6,10 +6,12 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat @@ -6,10 +6,12 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat
6 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; 6 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
7 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO; 7 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
  9 +import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
9 10
10 import javax.validation.Valid; 11 import javax.validation.Valid;
11 import java.util.Collection; 12 import java.util.Collection;
12 import java.util.List; 13 import java.util.List;
  14 +import java.util.Map;
13 15
14 /** 16 /**
15 * 快递运费模板 Service 接口 17 * 快递运费模板 Service 接口
@@ -73,11 +75,21 @@ public interface DeliveryExpressTemplateService { @@ -73,11 +75,21 @@ public interface DeliveryExpressTemplateService {
73 75
74 /** 76 /**
75 * 校验快递运费模板 77 * 校验快递运费模板
76 - * 78 + * <p>
77 * 如果校验不通过,抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常 79 * 如果校验不通过,抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常
78 * 80 *
79 * @param templateId 模板编号 81 * @param templateId 模板编号
80 * @return 快递运费模板 82 * @return 快递运费模板
81 */ 83 */
82 DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); 84 DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId);
  85 +
  86 + /**
  87 + * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板
  88 + *
  89 + * @param ids SPU 编号列表
  90 + * @param areaId 区域编号
  91 + * @return Map (spuId -> 运费模板设置)
  92 + */
  93 + Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateBySpuIdsAndArea(Collection<Long> ids, Integer areaId);
  94 +
83 } 95 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java
1 package cn.iocoder.yudao.module.trade.service.delivery; 1 package cn.iocoder.yudao.module.trade.service.delivery;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  7 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
5 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*; 8 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; 9 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
@@ -9,6 +12,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemp @@ -9,6 +12,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemp
9 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper; 12 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;
10 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper; 13 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;
11 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper; 14 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper;
  15 +import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
12 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
13 import org.springframework.transaction.annotation.Transactional; 17 import org.springframework.transaction.annotation.Transactional;
14 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
@@ -17,6 +21,7 @@ import javax.annotation.Resource; @@ -17,6 +21,7 @@ import javax.annotation.Resource;
17 import java.util.*; 21 import java.util.*;
18 22
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
20 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
21 import static cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressTemplateConvert.INSTANCE; 26 import static cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressTemplateConvert.INSTANCE;
22 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NAME_DUPLICATE; 27 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NAME_DUPLICATE;
@@ -37,6 +42,8 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla @@ -37,6 +42,8 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
37 private DeliveryExpressTemplateChargeMapper expressTemplateChargeMapper; 42 private DeliveryExpressTemplateChargeMapper expressTemplateChargeMapper;
38 @Resource 43 @Resource
39 private DeliveryExpressTemplateFreeMapper expressTemplateFreeMapper; 44 private DeliveryExpressTemplateFreeMapper expressTemplateFreeMapper;
  45 + @Resource
  46 + private ProductSpuApi productSpuApi;
40 47
41 @Override 48 @Override
42 @Transactional(rollbackFor = Exception.class) 49 @Transactional(rollbackFor = Exception.class)
@@ -216,4 +223,50 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla @@ -216,4 +223,50 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
216 return template; 223 return template;
217 } 224 }
218 225
  226 + @Override
  227 + public Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId) {
  228 + Assert.notNull(areaId, "区域编号 {} 不能为空", areaId);
  229 + List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
  230 + if (CollUtil.isEmpty(spuList)) {
  231 + return Collections.emptyMap();
  232 + }
  233 + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getDeliveryTemplateId);
  234 + List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet());
  235 + Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size());
  236 + templateList.forEach(item -> {
  237 + if (spuMap.containsKey(item.getId())) {
  238 + ProductSpuRespDTO spuDTO = spuMap.get(item.getId());
  239 + SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO()
  240 + .setSpuId(spuDTO.getId()).setAreaId(areaId)
  241 + .setChargeMode(item.getChargeMode())
  242 + .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId))
  243 + .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId));
  244 + result.put(spuDTO.getId(), bo);
  245 + }
  246 + });
  247 + return result;
  248 + }
  249 +
  250 + private DeliveryExpressTemplateChargeDO findMatchExpressTemplateCharge(Long templateId, Integer areaId) {
  251 + List<DeliveryExpressTemplateChargeDO> list = expressTemplateChargeMapper.selectListByTemplateId(templateId);
  252 + for (DeliveryExpressTemplateChargeDO item : list) {
  253 + // 第一个匹配的返回。 areaId 不能重复
  254 + if (item.getAreaIds().contains(areaId)) {
  255 + return item;
  256 + }
  257 + }
  258 + return null;
  259 + }
  260 +
  261 + private DeliveryExpressTemplateFreeDO findMatchExpressTemplateFree(Long templateId, Integer areaId) {
  262 + List<DeliveryExpressTemplateFreeDO> list = expressTemplateFreeMapper.selectListByTemplateId(templateId);
  263 + for (DeliveryExpressTemplateFreeDO item : list) {
  264 + // 第一个匹配的返回。 areaId 不能重复
  265 + if (item.getAreaIds().contains(areaId)) {
  266 + return item;
  267 + }
  268 + }
  269 + return null;
  270 + }
  271 +
219 } 272 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/SpuDeliveryExpressTemplateRespBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.delivery.bo;
  2 +
  3 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
  4 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
  5 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
  6 +import lombok.Data;
  7 +
  8 +/**
  9 + * SPU 运费模板配置 Resp BO
  10 + *
  11 + * @author jason
  12 + */
  13 +@Data
  14 +public class SpuDeliveryExpressTemplateRespBO {
  15 +
  16 + /**
  17 + * 配送计费方式
  18 + * <p>
  19 + * 枚举 {@link DeliveryExpressChargeModeEnum}
  20 + */
  21 + private Integer chargeMode;
  22 +
  23 + /**
  24 + * 运费模板快递运费设置
  25 + */
  26 + private DeliveryExpressTemplateChargeDO templateCharge;
  27 +
  28 + /**
  29 + * 运费模板包邮设置
  30 + */
  31 + private DeliveryExpressTemplateFreeDO templateFree;
  32 +
  33 + /**
  34 + * SPU 编号
  35 + * <p>
  36 + * 关联 ProductSpuDO 的 id 编号
  37 + */
  38 + private Long spuId;
  39 +
  40 + /**
  41 + * 区域编号
  42 + */
  43 + private Integer areaId;
  44 +
  45 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
@@ -6,7 +6,9 @@ import cn.hutool.core.util.IdUtil; @@ -6,7 +6,9 @@ import cn.hutool.core.util.IdUtil;
6 import cn.hutool.core.util.ObjectUtil; 6 import cn.hutool.core.util.ObjectUtil;
7 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
8 import cn.iocoder.yudao.framework.common.core.KeyValue; 8 import cn.iocoder.yudao.framework.common.core.KeyValue;
  9 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
9 import cn.iocoder.yudao.framework.common.enums.TerminalEnum; 10 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
  11 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
10 import cn.iocoder.yudao.framework.common.pojo.PageResult; 12 import cn.iocoder.yudao.framework.common.pojo.PageResult;
11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 13 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
12 import cn.iocoder.yudao.module.member.api.address.AddressApi; 14 import cn.iocoder.yudao.module.member.api.address.AddressApi;
@@ -21,6 +23,11 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; @@ -21,6 +23,11 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
21 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; 23 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
22 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; 24 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
23 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 25 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
  26 +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
  27 +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
  28 +import cn.iocoder.yudao.module.system.api.notify.dto.NotifyTemplateReqDTO;
  29 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  30 +import cn.iocoder.yudao.module.system.enums.notify.NotifyTemplateTypeEnum;
24 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; 31 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
25 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; 32 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
26 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; 33 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
@@ -29,6 +36,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettle @@ -29,6 +36,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettle
29 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; 36 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
30 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 37 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
31 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO; 38 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
  39 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
32 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 40 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
33 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 41 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
34 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; 42 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
@@ -38,6 +46,7 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; @@ -38,6 +46,7 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
38 import cn.iocoder.yudao.module.trade.enums.order.*; 46 import cn.iocoder.yudao.module.trade.enums.order.*;
39 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 47 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
40 import cn.iocoder.yudao.module.trade.service.cart.TradeCartService; 48 import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
  49 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
41 import cn.iocoder.yudao.module.trade.service.price.TradePriceService; 50 import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
42 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 51 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
43 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 52 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
@@ -74,6 +83,8 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -74,6 +83,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
74 private TradeCartService tradeCartService; 83 private TradeCartService tradeCartService;
75 @Resource 84 @Resource
76 private TradePriceService tradePriceService; 85 private TradePriceService tradePriceService;
  86 + @Resource
  87 + private DeliveryExpressService deliveryExpressService;
77 88
78 @Resource 89 @Resource
79 private ProductSkuApi productSkuApi; 90 private ProductSkuApi productSkuApi;
@@ -85,7 +96,10 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -85,7 +96,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
85 private CouponApi couponApi; 96 private CouponApi couponApi;
86 @Resource 97 @Resource
87 private MemberUserApi memberUserApi; 98 private MemberUserApi memberUserApi;
88 - 99 + @Resource
  100 + private AdminUserApi adminUserApi;
  101 + @Resource
  102 + private NotifyMessageSendApi notifyMessageSendApi;
89 @Resource 103 @Resource
90 private TradeOrderProperties tradeOrderProperties; 104 private TradeOrderProperties tradeOrderProperties;
91 105
@@ -123,7 +137,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -123,7 +137,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
123 /** 137 /**
124 * 计算订单价格 138 * 计算订单价格
125 * 139 *
126 - * @param userId 用户编号 140 + * @param userId 用户编号
127 * @param settlementReqVO 结算信息 141 * @param settlementReqVO 结算信息
128 * @return 订单价格 142 * @return 订单价格
129 */ 143 */
@@ -162,7 +176,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -162,7 +176,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
162 /** 176 /**
163 * 校验收件地址是否存在 177 * 校验收件地址是否存在
164 * 178 *
165 - * @param userId 用户编号 179 + * @param userId 用户编号
166 * @param addressId 收件地址编号 180 * @param addressId 收件地址编号
167 * @return 收件地址 181 * @return 收件地址
168 */ 182 */
@@ -181,7 +195,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -181,7 +195,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
181 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); 195 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
182 order.setType(TradeOrderTypeEnum.NORMAL.getType()); 196 order.setType(TradeOrderTypeEnum.NORMAL.getType());
183 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); 197 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
184 - order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); 198 + order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
185 order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源? 199 order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
186 // 支付信息 200 // 支付信息
187 order.setAdjustPrice(0).setPayed(false); 201 order.setAdjustPrice(0).setPayed(false);
@@ -201,12 +215,12 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -201,12 +215,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
201 215
202 /** 216 /**
203 * 执行创建完创建完订单后的逻辑 217 * 执行创建完创建完订单后的逻辑
204 - * 218 + * <p>
205 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 219 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
206 * 220 *
207 - * @param userId 用户编号  
208 - * @param createReqVO 创建订单请求  
209 - * @param tradeOrderDO 交易订单 221 + * @param userId 用户编号
  222 + * @param createReqVO 创建订单请求
  223 + * @param tradeOrderDO 交易订单
210 * @param calculateRespBO 订单价格计算结果 224 * @param calculateRespBO 订单价格计算结果
211 */ 225 */
212 private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, 226 private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
@@ -265,11 +279,11 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -265,11 +279,11 @@ public class TradeOrderServiceImpl implements TradeOrderService {
265 279
266 /** 280 /**
267 * 校验交易订单满足被支付的条件 281 * 校验交易订单满足被支付的条件
268 - * 282 + * <p>
269 * 1. 交易订单未支付 283 * 1. 交易订单未支付
270 * 2. 支付单已支付 284 * 2. 支付单已支付
271 * 285 *
272 - * @param id 交易订单编号 286 + * @param id 交易订单编号
273 * @param payOrderId 支付订单编号 287 * @param payOrderId 支付订单编号
274 * @return 交易订单 288 * @return 交易订单
275 */ 289 */
@@ -324,8 +338,11 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -324,8 +338,11 @@ public class TradeOrderServiceImpl implements TradeOrderService {
324 public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) { 338 public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
325 // 校验并获得交易订单(可发货) 339 // 校验并获得交易订单(可发货)
326 TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId()); 340 TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
327 -  
328 - // TODO 芋艿:logisticsId 校验存在 341 + // TODO 芋艿:logisticsId 校验存在 发货物流公司 fix
  342 + DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
  343 + if (deliveryExpress == null) {
  344 + throw exception(DELIVERY_EXPRESS_NOT_EXISTS);
  345 + }
329 346
330 // 更新 TradeOrderDO 状态为已发货,等待收货 347 // 更新 TradeOrderDO 状态为已发货,等待收货
331 int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), 348 int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
@@ -338,8 +355,32 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -338,8 +355,32 @@ public class TradeOrderServiceImpl implements TradeOrderService {
338 355
339 // TODO 芋艿:发送订单变化的消息 356 // TODO 芋艿:发送订单变化的消息
340 357
341 - // TODO 芋艿:发送站内信  
342 - 358 + // TODO 芋艿:发送站内信 fix
  359 + // 1、获取模版编码为 order_delivery 的模版,判断是否存在 存在放回 true
  360 + if (!notifyMessageSendApi.validateNotifyTemplate("order_delivery")) {
  361 + // 1、1 站内信模版不存在则创建模版
  362 + NotifyTemplateReqDTO templateReqDTO = new NotifyTemplateReqDTO();
  363 + templateReqDTO.setName("订单发货通知模版");
  364 + templateReqDTO.setCode("order_delivery");
  365 + templateReqDTO.setType(NotifyTemplateTypeEnum.NOTIFICATION_MESSAGE.getType()); // 系统消息
  366 + // 获取操作用户
  367 + // AdminUserRespDTO user = adminUserApi.getUser(userId);
  368 + // templateReqDTO.setNickname(user.getNickname());
  369 + templateReqDTO.setNickname(UserTypeEnum.ADMIN.getName());
  370 + templateReqDTO.setContent("订单:{orderId}{msg}");
  371 + templateReqDTO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  372 + notifyMessageSendApi.createNotifyTemplate(templateReqDTO);
  373 + }
  374 + // 2、构造消息
  375 + Map<String, Object> msgMap = new HashMap<>();
  376 + msgMap.put("orderId", deliveryReqVO.getId());
  377 + msgMap.put("msg", TradeOrderStatusEnum.DELIVERED.getStatus());
  378 + // 2、发送站内信
  379 + notifyMessageSendApi.sendSingleMessageToAdmin(
  380 + new NotifySendSingleToUserReqDTO()
  381 + .setUserId(userId)
  382 + .setTemplateCode("order_delivery")
  383 + .setTemplateParams(msgMap));
343 // TODO 芋艿:OrderLog 384 // TODO 芋艿:OrderLog
344 385
345 // TODO 设计:like:是否要单独一个 delivery 发货单表??? 386 // TODO 设计:like:是否要单独一个 delivery 发货单表???
@@ -349,7 +390,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -349,7 +390,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
349 390
350 /** 391 /**
351 * 校验交易订单满足被发货的条件 392 * 校验交易订单满足被发货的条件
352 - * 393 + * <p>
353 * 1. 交易订单未发货 394 * 1. 交易订单未发货
354 * 395 *
355 * @param id 交易订单编号 396 * @param id 交易订单编号
@@ -363,7 +404,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -363,7 +404,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
363 } 404 }
364 // 校验订单是否是待发货状态 405 // 校验订单是否是待发货状态
365 if (!TradeOrderStatusEnum.isUndelivered(order.getStatus()) 406 if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())
366 - || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) { 407 + || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
367 throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED); 408 throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
368 } 409 }
369 return order; 410 return order;
@@ -397,11 +438,11 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -397,11 +438,11 @@ public class TradeOrderServiceImpl implements TradeOrderService {
397 438
398 /** 439 /**
399 * 校验交易订单满足可售货的条件 440 * 校验交易订单满足可售货的条件
400 - * 441 + * <p>
401 * 1. 交易订单待收货 442 * 1. 交易订单待收货
402 * 443 *
403 * @param userId 用户编号 444 * @param userId 用户编号
404 - * @param id 交易订单编号 445 + * @param id 交易订单编号
405 * @return 交易订单 446 * @return 交易订单
406 */ 447 */
407 private TradeOrderDO validateOrderReceivable(Long userId, Long id) { 448 private TradeOrderDO validateOrderReceivable(Long userId, Long id) {
@@ -476,7 +517,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -476,7 +517,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
476 public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, Integer refundPrice) { 517 public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, Integer refundPrice) {
477 // 如果退款成功,则 refundPrice 非空 518 // 如果退款成功,则 refundPrice 非空
478 if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()) 519 if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())
479 - && refundPrice == null) { 520 + && refundPrice == null) {
480 throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id)); 521 throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
481 } 522 }
482 523
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java
1 package cn.iocoder.yudao.module.trade.service.price.bo; 1 package cn.iocoder.yudao.module.trade.service.price.bo;
2 2
3 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;  
4 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 3 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
5 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 4 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
6 import lombok.Data; 5 import lombok.Data;
@@ -54,14 +53,6 @@ public class TradePriceCalculateReqBO { @@ -54,14 +53,6 @@ public class TradePriceCalculateReqBO {
54 private Integer deliveryType; 53 private Integer deliveryType;
55 54
56 /** 55 /**
57 - * 配送模板编号  
58 - *  
59 - * 关联 {@link DeliveryExpressTemplateDO#getId()}  
60 - */  
61 - // TODO @jason:运费模版,是不是每个 SKU 传入哈  
62 - private Long templateId;  
63 -  
64 - /**  
65 * 商品 SKU 数组 56 * 商品 SKU 数组
66 */ 57 */
67 @NotNull(message = "商品数组不能为空") 58 @NotNull(message = "商品数组不能为空")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
1 package cn.iocoder.yudao.module.trade.service.price.calculator; 1 package cn.iocoder.yudao.module.trade.service.price.calculator;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
3 import cn.iocoder.yudao.module.member.api.address.AddressApi; 5 import cn.iocoder.yudao.module.member.api.address.AddressApi;
4 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; 6 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
5 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 7 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
6 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 8 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; 9 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
8 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;  
9 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
10 -import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;  
11 -import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;  
12 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; 11 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
13 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 12 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
14 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; 13 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
  14 +import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
16 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 16 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
17 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem; 17 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
@@ -19,7 +19,6 @@ import org.springframework.core.annotation.Order; @@ -19,7 +19,6 @@ import org.springframework.core.annotation.Order;
19 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
20 20
21 import javax.annotation.Resource; 21 import javax.annotation.Resource;
22 -import java.util.HashMap;  
23 import java.util.List; 22 import java.util.List;
24 import java.util.Map; 23 import java.util.Map;
25 import java.util.Set; 24 import java.util.Set;
@@ -39,14 +38,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -39,14 +38,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
39 private AddressApi addressApi; 38 private AddressApi addressApi;
40 @Resource 39 @Resource
41 private ProductSkuApi productSkuApi; 40 private ProductSkuApi productSkuApi;
42 -  
43 @Resource 41 @Resource
44 private DeliveryExpressTemplateService deliveryExpressTemplateService; 42 private DeliveryExpressTemplateService deliveryExpressTemplateService;
45 - // TODO @jason:走 Service 哈。Mapper 只允许自己的 Service 调用,保护好数据结构;  
46 - @Resource  
47 - private DeliveryExpressTemplateChargeMapper templateChargeMapper;  
48 - @Resource  
49 - private DeliveryExpressTemplateFreeMapper templateFreeMapper;  
50 43
51 @Override 44 @Override
52 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { 45 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
@@ -54,66 +47,45 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -54,66 +47,45 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
54 if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) { 47 if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
55 return; 48 return;
56 } 49 }
57 -  
58 - if (param.getTemplateId() == null || param.getAddressId() == null) { 50 + // 1.2 得到收件地址区域
  51 + if (param.getAddressId() == null) {
59 return; 52 return;
60 } 53 }
61 - // 1.2 校验运费模板是否存在  
62 - DeliveryExpressTemplateDO template = deliveryExpressTemplateService.validateDeliveryExpressTemplate(param.getTemplateId());  
63 -  
64 - // 得到包邮配置  
65 - List<DeliveryExpressTemplateFreeDO> expressTemplateFreeList = templateFreeMapper.selectListByTemplateId(template.getId());  
66 - Map<Integer, DeliveryExpressTemplateFreeDO> areaTemplateFreeMap = new HashMap<>();  
67 - expressTemplateFreeList.forEach(item -> {  
68 - for (Integer areaId : item.getAreaIds()) {  
69 - // TODO 需要保证 areaId 不能重复  
70 - if (!areaTemplateFreeMap.containsKey(areaId)) {  
71 - areaTemplateFreeMap.put(areaId, item);  
72 - }  
73 - }  
74 - });  
75 - // 得到快递运费配置  
76 - List<DeliveryExpressTemplateChargeDO> expressTemplateChargeList = templateChargeMapper.selectListByTemplateId(template.getId());  
77 - Map<Integer, DeliveryExpressTemplateChargeDO> areaTemplateChargeMap = new HashMap<>();  
78 - expressTemplateChargeList.forEach(item -> {  
79 - for (Integer areaId : item.getAreaIds()) {  
80 - // areaId 不能重复  
81 - if (!areaTemplateChargeMap.containsKey(areaId)) {  
82 - areaTemplateChargeMap.put(areaId, item);  
83 - }  
84 - }  
85 - });  
86 - // 得到收件地址区域  
87 AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); 54 AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
88 - // 1.3 计算快递费用  
89 - calculateDeliveryPrice(address.getAreaId(), template.getChargeMode(),  
90 - areaTemplateFreeMap, areaTemplateChargeMap, result); 55 + Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId());
  56 +
  57 + //1.3 过滤出已选中的商品SKU
  58 + List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
  59 +
  60 + Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap =
  61 + deliveryExpressTemplateService.getExpressTemplateBySpuIdsAndArea(
  62 + convertSet(selectedItem, OrderItem::getSpuId), address.getAreaId());
  63 +
  64 + // 1.4 计算配送费用
  65 + if (CollUtil.isNotEmpty(spuExpressTemplateMap)) {
  66 + calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result);
  67 + }
  68 +
91 } 69 }
92 70
93 - /**  
94 - * 校验订单是否满足包邮条件  
95 - *  
96 - * @param receiverAreaId 收件人地区的区域编号  
97 - * @param chargeMode 配送计费方式  
98 - * @param areaTemplateFreeMap 运费模板包邮区域设置 Map  
99 - * @param areaTemplateChargeMap 运费模板快递费用设置 Map  
100 - */  
101 - private void calculateDeliveryPrice(Integer receiverAreaId,  
102 - Integer chargeMode,  
103 - Map<Integer, DeliveryExpressTemplateFreeDO> areaTemplateFreeMap,  
104 - Map<Integer, DeliveryExpressTemplateChargeDO> areaTemplateChargeMap, 71 + private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
  72 + Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap,
105 TradePriceCalculateRespBO result) { 73 TradePriceCalculateRespBO result) {
106 - // 过滤出已选中的商品SKU  
107 - List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);  
108 - Set<Long> skuIds = convertSet(selectedItem, OrderItem::getSkuId); 74 + Set<Long> skuIds = convertSet(selectedSkus, OrderItem::getSkuId);
109 // 得到SKU 详情。得到 重量体积 75 // 得到SKU 详情。得到 重量体积
110 Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); 76 Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId);
111 - // 一个 spuId 可能对应多条订单商品 SKU  
112 - // TODO @jason:得确认下,按照 sku 算,还是 spu 算;  
113 - Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedItem, OrderItem::getSpuId); 77 + // 按spu 来计算商品的运费 一个 spuId 可能对应多条订单商品 SKU,
  78 + Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId);
  79 +
114 // 依次计算每个 SPU 的快递运费 80 // 依次计算每个 SPU 的快递运费
115 for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { 81 for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) {
  82 + Long spuId = entry.getKey();
116 List<OrderItem> orderItems = entry.getValue(); 83 List<OrderItem> orderItems = entry.getValue();
  84 + SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId);
  85 + if (templateBO == null) {
  86 + // 记录错误日志
  87 + continue;
  88 + }
117 // 总件数, 总金额, 总重量, 总体积 89 // 总件数, 总金额, 总重量, 总体积
118 int totalCount = 0; 90 int totalCount = 0;
119 int totalPrice = 0; 91 int totalPrice = 0;
@@ -121,51 +93,67 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -121,51 +93,67 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
121 double totalVolume = 0; 93 double totalVolume = 0;
122 for (OrderItem orderItem : orderItems) { 94 for (OrderItem orderItem : orderItems) {
123 totalCount += orderItem.getCount(); 95 totalCount += orderItem.getCount();
124 - totalPrice += orderItem.getPrice(); // TODO jason:应该按照 payPrice? 96 + totalPrice += orderItem.getPayPrice(); // 先按应付总金额来算,后面确认一下
125 ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); 97 ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId());
126 if (skuResp != null) { 98 if (skuResp != null) {
127 - totalWeight = totalWeight + skuResp.getWeight(); // TODO @jason:* 数量  
128 - totalVolume = totalVolume + skuResp.getVolume(); 99 + totalWeight = totalWeight + skuResp.getWeight() * orderItem.getCount();
  100 + totalVolume = totalVolume + skuResp.getVolume() * orderItem.getCount();
129 } 101 }
130 } 102 }
131 // 优先判断是否包邮. 如果包邮不计算快递运费 103 // 优先判断是否包邮. 如果包邮不计算快递运费
132 - if (areaTemplateFreeMap.containsKey(receiverAreaId) &&  
133 - checkExpressFree(chargeMode, totalCount, totalWeight,  
134 - totalVolume, totalPrice, areaTemplateFreeMap.get(receiverAreaId))) { 104 + if (checkExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
  105 + totalVolume, totalPrice, templateBO.getTemplateFree())) {
135 continue; 106 continue;
136 } 107 }
137 - // 计算快递运费  
138 - // TODO @jason:貌似也可以抽成 checkExpressFree 类似方法  
139 - if (areaTemplateChargeMap.containsKey(receiverAreaId)) {  
140 - DeliveryExpressTemplateChargeDO templateCharge = areaTemplateChargeMap.get(receiverAreaId);  
141 - DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);  
142 - switch (chargeModeEnum) {  
143 - case PIECE: {  
144 - calculateExpressFeeBySpu(totalCount, templateCharge, orderItems);  
145 - break;  
146 - }  
147 - case WEIGHT: {  
148 - calculateExpressFeeBySpu(totalWeight, templateCharge, orderItems);  
149 - break;  
150 - }  
151 - case VOLUME: {  
152 - calculateExpressFeeBySpu(totalVolume, templateCharge, orderItems);  
153 - break;  
154 - }  
155 - } 108 + if (templateBO.getTemplateCharge() == null) {
  109 + continue;
156 } 110 }
  111 + // 计算快递运费
  112 + calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume,
  113 + templateBO.getChargeMode(), templateBO.getTemplateCharge(), orderItems);
  114 +
157 } 115 }
158 TradePriceCalculatorHelper.recountAllPrice(result); 116 TradePriceCalculatorHelper.recountAllPrice(result);
159 } 117 }
160 118
161 /** 119 /**
162 - * 按 spu 来计算快递费用 120 + * 按配送方式来计算运费
  121 + *
  122 + * @param totalCount 总件数
  123 + * @param totalWeight 总重量
  124 + * @param totalVolume 总体积
  125 + * @param chargeMode 配送计费方式
  126 + * @param templateCharge 快递运费配置
  127 + * @param orderItems SKU 商品项目
  128 + */
  129 + private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume,
  130 + int chargeMode, DeliveryExpressTemplateChargeDO templateCharge,
  131 + List<OrderItem> orderItems) {
  132 + DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
  133 + switch (chargeModeEnum) {
  134 + case PIECE: {
  135 + calculateExpressFee(totalCount, templateCharge, orderItems);
  136 + break;
  137 + }
  138 + case WEIGHT: {
  139 + calculateExpressFee(totalWeight, templateCharge, orderItems);
  140 + break;
  141 + }
  142 + case VOLUME: {
  143 + calculateExpressFee(totalVolume, templateCharge, orderItems);
  144 + break;
  145 + }
  146 + }
  147 + }
  148 +
  149 + /**
  150 + * 计算 SKU 商品快递费用
163 * 151 *
164 * @param total 总件数/总重量/总体积 152 * @param total 总件数/总重量/总体积
165 * @param templateCharge 快递运费配置 153 * @param templateCharge 快递运费配置
166 * @param orderItems SKU 商品项目 154 * @param orderItems SKU 商品项目
167 */ 155 */
168 - private void calculateExpressFeeBySpu(double total, DeliveryExpressTemplateChargeDO templateCharge, List<OrderItem> orderItems) { 156 + private void calculateExpressFee(double total, DeliveryExpressTemplateChargeDO templateCharge, List<OrderItem> orderItems) {
169 int deliveryPrice; 157 int deliveryPrice;
170 if (total <= templateCharge.getStartCount()) { 158 if (total <= templateCharge.getStartCount()) {
171 deliveryPrice = templateCharge.getStartPrice(); 159 deliveryPrice = templateCharge.getStartPrice();
@@ -176,8 +164,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -176,8 +164,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
176 int extraPrice = templateCharge.getExtraPrice() * extraNum; 164 int extraPrice = templateCharge.getExtraPrice() * extraNum;
177 deliveryPrice = templateCharge.getStartPrice() + extraPrice; 165 deliveryPrice = templateCharge.getStartPrice() + extraPrice;
178 } 166 }
179 - // TODO @芋艿 分摊快递费用到 SKU. 是不是搞复杂了;  
180 - // TODO @jason:因为退费的时候,可能按照 SKU 考虑退费金额 167 + // 分摊快递费用到 SKU. 退费的时候,可能按照 SKU 考虑退费金额
181 divideDeliveryPrice(deliveryPrice, orderItems); 168 divideDeliveryPrice(deliveryPrice, orderItems);
182 } 169 }
183 170
@@ -207,6 +194,9 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -207,6 +194,9 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
207 */ 194 */
208 private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight, 195 private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight,
209 double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) { 196 double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) {
  197 + if (templateFree == null) {
  198 + return false;
  199 + }
210 DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode); 200 DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
211 switch (chargeModeEnum) { 201 switch (chargeModeEnum) {
212 case PIECE: 202 case PIECE:
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java
1 package cn.iocoder.yudao.module.system.api.notify; 1 package cn.iocoder.yudao.module.system.api.notify;
2 2
3 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; 3 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
  4 +import cn.iocoder.yudao.module.system.api.notify.dto.NotifyTemplateReqDTO;
4 5
5 import javax.validation.Valid; 6 import javax.validation.Valid;
6 7
@@ -27,4 +28,8 @@ public interface NotifyMessageSendApi { @@ -27,4 +28,8 @@ public interface NotifyMessageSendApi {
27 */ 28 */
28 Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO); 29 Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO);
29 30
  31 +
  32 + boolean validateNotifyTemplate(String orderDelivery);
  33 +
  34 + void createNotifyTemplate(NotifyTemplateReqDTO templateReqDTO);
30 } 35 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.notify.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +@Data
  11 +public class NotifyTemplateReqDTO {
  12 +
  13 + @NotEmpty(message = "模版名称不能为空")
  14 + private String name;
  15 +
  16 + @NotNull(message = "模版编码不能为空")
  17 + private String code;
  18 +
  19 + @NotNull(message = "模版类型不能为空")
  20 + private Integer type;
  21 +
  22 + @NotEmpty(message = "发送人名称不能为空")
  23 + private String nickname;
  24 +
  25 + @NotEmpty(message = "模版内容不能为空")
  26 + private String content;
  27 +
  28 + @NotNull(message = "状态不能为空")
  29 + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
  30 + private Integer status;
  31 +
  32 + private String remark;
  33 +
  34 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.enums.notify;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 通知模板类型枚举
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum NotifyTemplateTypeEnum {
  14 +
  15 + /**
  16 + * 系统消息
  17 + */
  18 + SYSTEM_MESSAGE(2),
  19 + /**
  20 + * 通知消息
  21 + */
  22 + NOTIFICATION_MESSAGE(1);
  23 +
  24 + private final Integer type;
  25 +
  26 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java
1 package cn.iocoder.yudao.module.system.api.notify; 1 package cn.iocoder.yudao.module.system.api.notify;
2 2
3 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; 3 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
4 -import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService; 4 +import cn.iocoder.yudao.module.system.api.notify.dto.NotifyTemplateReqDTO;
5 import cn.iocoder.yudao.module.system.service.notify.NotifySendService; 5 import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
6 import org.springframework.stereotype.Service; 6 import org.springframework.stereotype.Service;
7 7
@@ -30,4 +30,14 @@ public class NotifyMessageSendApiImpl implements NotifyMessageSendApi { @@ -30,4 +30,14 @@ public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
30 reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); 30 reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
31 } 31 }
32 32
  33 + @Override
  34 + public boolean validateNotifyTemplate(String orderDelivery) {
  35 + return false;
  36 + }
  37 +
  38 + @Override
  39 + public void createNotifyTemplate(NotifyTemplateReqDTO templateReqDTO) {
  40 +
  41 + }
  42 +
33 } 43 }