Commit 8811ce0200b291977a34635bb26fed8d143ffede

Authored by 芋道源码
Committed by Gitee
2 parents b9018baa b2c608e7

!637 完善优惠券功能

Merge pull request !637 from 疯狂的世界/feature/mall_product
Showing 40 changed files with 633 additions and 354 deletions
sql/mysql/ruoyi-vue-pro.sql
@@ -1188,8 +1188,8 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st @@ -1188,8 +1188,8 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
1188 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', b'0'); 1188 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', b'0');
1189 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', b'0'); 1189 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', b'0');
1190 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', b'0'); 1190 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', b'0');
1191 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1169, 1, '通用卷', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-01 23:42:49', b'0');  
1192 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1170, 2, '商品卷', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-01 23:42:54', b'0'); 1191 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1169, 1, '通用券', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-01 23:42:49', b'0');
  1192 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1170, 2, '商品券', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-01 23:42:54', b'0');
1193 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1171, 1, '已领取', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2022-11-04 19:16:04', b'0'); 1193 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1171, 1, '已领取', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2022-11-04 19:16:04', b'0');
1194 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', b'0'); 1194 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', b'0');
1195 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', b'0'); 1195 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', b'0');
@@ -1284,7 +1284,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st @@ -1284,7 +1284,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
1284 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0'); 1284 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0');
1285 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', b'0'); 1285 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', b'0');
1286 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', b'0'); 1286 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', b'0');
1287 -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1358, 3, '品类卷', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-01 23:43:07', b'0'); 1287 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1358, 3, '品类券', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-01 23:43:07', b'0');
1288 COMMIT; 1288 COMMIT;
1289 1289
1290 -- ---------------------------- 1290 -- ----------------------------
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/dto/RewardActivityMatchRespDTO.java
@@ -68,7 +68,7 @@ public class RewardActivityMatchRespDTO { @@ -68,7 +68,7 @@ public class RewardActivityMatchRespDTO {
68 */ 68 */
69 private List<Long> couponIds; 69 private List<Long> couponIds;
70 /** 70 /**
71 - * 赠送的优惠卷数量的数组 71 + * 赠送的优惠券数量的数组
72 */ 72 */
73 private List<Integer> couponCounts; 73 private List<Integer> couponCounts;
74 74
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java
@@ -15,9 +15,9 @@ import java.util.Arrays; @@ -15,9 +15,9 @@ import java.util.Arrays;
15 @AllArgsConstructor 15 @AllArgsConstructor
16 public enum PromotionProductScopeEnum implements IntArrayValuable { 16 public enum PromotionProductScopeEnum implements IntArrayValuable {
17 17
18 - ALL(1, "通用卷"), // 全部商品  
19 - SPU(2, "商品卷"), // 指定商品  
20 - CATEGORY(3, "品类卷"), // 指定商品 18 + ALL(1, "通用券"), // 全部商品
  19 + SPU(2, "商品券"), // 指定商品
  20 + CATEGORY(3, "品类券"), // 指定品类
21 ; 21 ;
22 22
23 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray(); 23 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray();
yudao-module-mall/yudao-module-promotion-biz/pom.xml
@@ -47,6 +47,10 @@ @@ -47,6 +47,10 @@
47 </dependency> 47 </dependency>
48 <dependency> 48 <dependency>
49 <groupId>cn.iocoder.boot</groupId> 49 <groupId>cn.iocoder.boot</groupId>
  50 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  51 + </dependency>
  52 + <dependency>
  53 + <groupId>cn.iocoder.boot</groupId>
50 <artifactId>yudao-spring-boot-starter-biz-weixin</artifactId> 54 <artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
51 </dependency> 55 </dependency>
52 56
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageReqVO.java
1 package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon; 1 package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 7 import lombok.Data;
6 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
@@ -8,6 +10,7 @@ import lombok.ToString; @@ -8,6 +10,7 @@ import lombok.ToString;
8 import org.springframework.format.annotation.DateTimeFormat; 10 import org.springframework.format.annotation.DateTimeFormat;
9 11
10 import java.time.LocalDateTime; 12 import java.time.LocalDateTime;
  13 +import java.util.Collection;
11 14
12 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 15 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
13 16
@@ -21,6 +24,7 @@ public class CouponPageReqVO extends PageParam { @@ -21,6 +24,7 @@ public class CouponPageReqVO extends PageParam {
21 private Long templateId; 24 private Long templateId;
22 25
23 @Schema(description = "优惠码状态", example = "1") 26 @Schema(description = "优惠码状态", example = "1")
  27 + @InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
24 private Integer status; 28 private Integer status;
25 29
26 @Schema(description = "创建时间") 30 @Schema(description = "创建时间")
@@ -30,4 +34,7 @@ public class CouponPageReqVO extends PageParam { @@ -30,4 +34,7 @@ public class CouponPageReqVO extends PageParam {
30 @Schema(description = "用户昵称", example = "芋艿") 34 @Schema(description = "用户昵称", example = "芋艿")
31 private String nickname; 35 private String nickname;
32 36
  37 + @Schema(description = "用户编号", example = "1")
  38 + private Collection<Long> userIds;
  39 +
33 } 40 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.validation.InEnum; 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
5 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 6 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
6 import io.swagger.v3.oas.annotations.media.Schema; 7 import io.swagger.v3.oas.annotations.media.Schema;
7 import lombok.Data; 8 import lombok.Data;
@@ -33,8 +34,14 @@ public class CouponTemplatePageReqVO extends PageParam { @@ -33,8 +34,14 @@ public class CouponTemplatePageReqVO extends PageParam {
33 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 34 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
34 private LocalDateTime[] createTime; 35 private LocalDateTime[] createTime;
35 36
36 - @Schema(description = "可以领取的类型", example = "[1,2, 3]") 37 + @Schema(description = "可以领取的类型", example = "[1, 2, 3]")
37 @InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}") 38 @InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}")
38 private List<Integer> canTakeTypes; 39 private List<Integer> canTakeTypes;
39 40
  41 + @Schema(description = "商品范围", example = "1")
  42 + @InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
  43 + private Integer productScope;
  44 +
  45 + @Schema(description = "商品范围编号", example = "1")
  46 + private Long productScopeValue;
40 } 47 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java
@@ -84,7 +84,7 @@ public class RewardActivityBaseVO { @@ -84,7 +84,7 @@ public class RewardActivityBaseVO {
84 @Schema(description = "赠送的优惠劵编号的数组", example = "1,2,3") 84 @Schema(description = "赠送的优惠劵编号的数组", example = "1,2,3")
85 private List<Long> couponIds; 85 private List<Long> couponIds;
86 86
87 - @Schema(description = "赠送的优惠卷数量的数组", example = "1,2,3") 87 + @Schema(description = "赠送的优惠券数量的数组", example = "1,2,3")
88 private List<Integer> couponCounts; 88 private List<Integer> couponCounts;
89 89
90 @AssertTrue(message = "优惠劵和数量必须一一对应") 90 @AssertTrue(message = "优惠劵和数量必须一一对应")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
1 package cn.iocoder.yudao.module.promotion.controller.app.coupon; 1 package cn.iocoder.yudao.module.promotion.controller.app.coupon;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 7 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
6 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;  
7 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;  
8 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;  
9 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;  
10 -import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO; 8 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.*;
  9 +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
  10 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  11 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
  12 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
11 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; 13 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
  14 +import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
12 import io.swagger.v3.oas.annotations.Operation; 15 import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
13 import io.swagger.v3.oas.annotations.tags.Tag; 17 import io.swagger.v3.oas.annotations.tags.Tag;
14 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
15 import org.springframework.web.bind.annotation.*; 19 import org.springframework.web.bind.annotation.*;
16 20
17 import javax.annotation.Resource; 21 import javax.annotation.Resource;
18 -import java.time.LocalDateTime;  
19 -import java.util.ArrayList; 22 +import javax.validation.Valid;
  23 +import java.util.Collections;
20 import java.util.List; 24 import java.util.List;
21 -import java.util.Random;  
22 25
23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
24 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 27 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -31,73 +34,40 @@ public class AppCouponController { @@ -31,73 +34,40 @@ public class AppCouponController {
31 34
32 @Resource 35 @Resource
33 private CouponService couponService; 36 private CouponService couponService;
  37 + @Resource
  38 + private CouponTemplateService couponTemplateService;
34 39
35 - // TODO 芋艿:待实现  
36 @PostMapping("/take") 40 @PostMapping("/take")
37 @Operation(summary = "领取优惠劵") 41 @Operation(summary = "领取优惠劵")
38 - public CommonResult<Long> takeCoupon(@RequestBody AppCouponTemplatePageReqVO pageReqVO) {  
39 - return success(1L); 42 + @Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024")
  43 + public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {
  44 + Long userId = getLoginUserId();
  45 + // 领取
  46 + couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
  47 + // 检查是否可以继续领取
  48 + CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());
  49 + boolean canTakeAgain = true;
  50 + if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {
  51 + Integer takeCount = MapUtil.getInt(couponService.getTakeCountMapByTemplateIds(
  52 + Collections.singleton(reqVO.getTemplateId()), userId), reqVO.getTemplateId(), 0);
  53 + canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();
  54 + }
  55 + return success(canTakeAgain);
40 } 56 }
41 57
42 - // TODO 芋艿:待实现  
43 @GetMapping("/match-list") 58 @GetMapping("/match-list")
44 @Operation(summary = "获得匹配指定商品的优惠劵列表") 59 @Operation(summary = "获得匹配指定商品的优惠劵列表")
45 public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) { 60 public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
46 - List<AppCouponMatchRespVO> list = new ArrayList<>();  
47 - Random random = new Random();  
48 - for (int i = 0; i < 10; i++) {  
49 - AppCouponMatchRespVO vo = new AppCouponMatchRespVO();  
50 - vo.setId(i + 1L);  
51 - vo.setName("优惠劵" + (i + 1));  
52 - vo.setUsePrice(random.nextInt(100) * 100);  
53 - vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));  
54 - vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));  
55 - vo.setDiscountType(random.nextInt(2) + 1);  
56 - if (vo.getDiscountType() == 1) {  
57 - vo.setDiscountPercent(null);  
58 - vo.setDiscountPrice(random.nextInt(50) * 100);  
59 - vo.setDiscountLimitPrice(null);  
60 - } else {  
61 - vo.setDiscountPercent(random.nextInt(90) + 10);  
62 - vo.setDiscountPrice(null);  
63 - vo.setDiscountLimitPrice(random.nextInt(200) * 100);  
64 - }  
65 - vo.setMatch(random.nextBoolean());  
66 - if (!vo.getMatch()) {  
67 - vo.setDescription("不符合条件噢");  
68 - }  
69 - list.add(vo);  
70 - }  
71 - return success(list); 61 + // todo: 优惠金额倒序
  62 + return success(CouponConvert.INSTANCE.convertList(couponService.getMatchCouponList(getLoginUserId(), matchReqVO)));
72 } 63 }
73 64
74 - // TODO 芋艿:待实现  
75 @GetMapping("/page") 65 @GetMapping("/page")
76 @Operation(summary = "优惠劵列表", description = "我的优惠劵") 66 @Operation(summary = "优惠劵列表", description = "我的优惠劵")
77 public CommonResult<PageResult<AppCouponRespVO>> takeCoupon(AppCouponPageReqVO pageReqVO) { 67 public CommonResult<PageResult<AppCouponRespVO>> takeCoupon(AppCouponPageReqVO pageReqVO) {
78 - List<AppCouponRespVO> list = new ArrayList<>();  
79 - Random random = new Random();  
80 - for (int i = 0; i < 10; i++) {  
81 - AppCouponRespVO vo = new AppCouponRespVO();  
82 - vo.setId(i + 1L);  
83 - vo.setName("优惠劵" + (i + 1));  
84 - vo.setStatus(pageReqVO.getStatus());  
85 - vo.setUsePrice(random.nextInt(100) * 100);  
86 - vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));  
87 - vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));  
88 - vo.setDiscountType(random.nextInt(2) + 1);  
89 - if (vo.getDiscountType() == 1) {  
90 - vo.setDiscountPercent(null);  
91 - vo.setDiscountPrice(random.nextInt(50) * 100);  
92 - vo.setDiscountLimitPrice(null);  
93 - } else {  
94 - vo.setDiscountPercent(random.nextInt(90) + 10);  
95 - vo.setDiscountPrice(null);  
96 - vo.setDiscountLimitPrice(random.nextInt(200) * 100);  
97 - }  
98 - list.add(vo);  
99 - }  
100 - return success(new PageResult<>(list, 20L)); 68 + PageResult<CouponDO> pageResult = couponService.getCouponPage(
  69 + CouponConvert.INSTANCE.convert(pageReqVO, Collections.singleton(getLoginUserId())));
  70 + return success(CouponConvert.INSTANCE.convertAppPage(pageResult));
101 } 71 }
102 72
103 @GetMapping(value = "/get-unused-count") 73 @GetMapping(value = "/get-unused-count")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponTemplateController.java
1 package cn.iocoder.yudao.module.promotion.controller.app.coupon; 1 package cn.iocoder.yudao.module.promotion.controller.app.coupon;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
6 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO; 9 import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;
  10 +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;
  11 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
  12 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  13 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
  14 +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
7 import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService; 15 import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
8 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
9 -import io.swagger.v3.oas.annotations.Parameter;  
10 -import io.swagger.v3.oas.annotations.Parameters;  
11 import io.swagger.v3.oas.annotations.tags.Tag; 17 import io.swagger.v3.oas.annotations.tags.Tag;
12 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
13 import org.springframework.web.bind.annotation.GetMapping; 19 import org.springframework.web.bind.annotation.GetMapping;
14 import org.springframework.web.bind.annotation.RequestMapping; 20 import org.springframework.web.bind.annotation.RequestMapping;
15 -import org.springframework.web.bind.annotation.RequestParam;  
16 import org.springframework.web.bind.annotation.RestController; 21 import org.springframework.web.bind.annotation.RestController;
17 22
18 import javax.annotation.Resource; 23 import javax.annotation.Resource;
19 -import java.time.LocalDateTime;  
20 -import java.util.ArrayList;  
21 -import java.util.List;  
22 -import java.util.Random; 24 +import java.util.*;
23 25
24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  28 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
25 29
26 @Tag(name = "用户 App - 优惠劵模板") 30 @Tag(name = "用户 App - 优惠劵模板")
27 @RestController 31 @RestController
@@ -31,84 +35,45 @@ public class AppCouponTemplateController { @@ -31,84 +35,45 @@ public class AppCouponTemplateController {
31 35
32 @Resource 36 @Resource
33 private CouponTemplateService couponTemplateService; 37 private CouponTemplateService couponTemplateService;
  38 + @Resource
  39 + private CouponService couponService;
34 40
35 - // TODO 芋艿:待实现  
36 - @GetMapping("/list")  
37 - @Operation(summary = "获得优惠劵模版列表")  
38 - @Parameters({  
39 - @Parameter(name = "spuId", description = "商品 SPU 编号"), // 目前主要给商品详情使用  
40 - @Parameter(name = "useType", description = "使用类型"),  
41 - @Parameter(name = "count", description = "数量", required = true)  
42 - })  
43 - public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(@RequestParam(value = "spuId", required = false) Long spuId,  
44 - @RequestParam(value = "useType", required = false) Integer useType,  
45 - @RequestParam(value = "count", required = false, defaultValue = "10") Integer count) {  
46 - List<AppCouponTemplateRespVO> list = new ArrayList<>();  
47 - Random random = new Random();  
48 - for (int i = 0; i < 10; i++) {  
49 - AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();  
50 - vo.setId(i + 1L);  
51 - vo.setName("优惠劵" + (i + 1));  
52 - vo.setTakeLimitCount(random.nextInt(10) + 1);  
53 - vo.setUsePrice(random.nextInt(100) * 100);  
54 - vo.setValidityType(random.nextInt(2) + 1);  
55 - if (vo.getValidityType() == 1) {  
56 - vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));  
57 - vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));  
58 - } else {  
59 - vo.setFixedStartTerm(random.nextInt(10));  
60 - vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);  
61 - }  
62 - vo.setDiscountType(random.nextInt(2) + 1);  
63 - if (vo.getDiscountType() == 1) {  
64 - vo.setDiscountPercent(null);  
65 - vo.setDiscountPrice(random.nextInt(50) * 100);  
66 - vo.setDiscountLimitPrice(null);  
67 - } else {  
68 - vo.setDiscountPercent(random.nextInt(90) + 10);  
69 - vo.setDiscountPrice(null);  
70 - vo.setDiscountLimitPrice(random.nextInt(200) * 100);  
71 - }  
72 - vo.setTakeStatus(random.nextBoolean());  
73 - list.add(vo);  
74 - }  
75 - return success(list);  
76 - } 41 + @Resource
  42 + private ProductSpuApi productSpuApi;
77 43
78 - // TODO 芋艿:待实现;和 getCouponTemplateList 类似  
79 @GetMapping("/page") 44 @GetMapping("/page")
80 @Operation(summary = "获得优惠劵模版分页") 45 @Operation(summary = "获得优惠劵模版分页")
81 public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) { 46 public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
82 - List<AppCouponTemplateRespVO> list = new ArrayList<>();  
83 - Random random = new Random();  
84 - for (int i = 0; i < 10; i++) {  
85 - AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();  
86 - vo.setId(i + 1L);  
87 - vo.setName("优惠劵" + (i + 1));  
88 - vo.setTakeLimitCount(random.nextInt(10) + 1);  
89 - vo.setUsePrice(random.nextInt(100) * 100);  
90 - vo.setValidityType(random.nextInt(2) + 1);  
91 - if (vo.getValidityType() == 1) {  
92 - vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));  
93 - vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));  
94 - } else {  
95 - vo.setFixedStartTerm(random.nextInt(10));  
96 - vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);  
97 - }  
98 - vo.setDiscountType(random.nextInt(2) + 1);  
99 - if (vo.getDiscountType() == 1) {  
100 - vo.setDiscountPercent(null);  
101 - vo.setDiscountPrice(random.nextInt(50) * 100);  
102 - vo.setDiscountLimitPrice(null);  
103 - } else {  
104 - vo.setDiscountPercent(random.nextInt(90) + 10);  
105 - vo.setDiscountPrice(null);  
106 - vo.setDiscountLimitPrice(random.nextInt(200) * 100);  
107 - }  
108 - vo.setTakeStatus(random.nextBoolean());  
109 - list.add(vo); 47 + // 1.1 处理查询条件:商品范围编号
  48 + Long productScopeValue = getaProductScopeValue(pageReqVO);
  49 + // 1.2 处理查询条件:领取方式=直接领取
  50 + List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
  51 + // 2. 分页查询
  52 + PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(
  53 + CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
  54 + // 3.1 领取数量
  55 + Map<Long, Integer> couponTakeCountMap = new HashMap<>(0);
  56 + Long userId = getLoginUserId();
  57 + if (userId != null) {
  58 + List<Long> templateIds = convertList(pageResult.getList(), CouponTemplateDO::getId,
  59 + t -> ObjUtil.notEqual(t.getTakeLimitCount(), -1)); // 只查有设置“每人限领个数”的
  60 + couponTakeCountMap = couponService.getTakeCountMapByTemplateIds(templateIds, userId);
  61 + }
  62 + // 3.2 拼接返回
  63 + return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap));
  64 + }
  65 +
  66 + private Long getaProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
  67 + Long productScopeValue = pageReqVO.getSpuId();
  68 + if (pageReqVO.getProductScope() == null || Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope())) {
  69 + // 通用券:清除商品范围
  70 + productScopeValue = null;
  71 + } else if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
  72 + // 品类券:查询商品的品类
  73 + productScopeValue = Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
  74 + .map(ProductSpuRespDTO::getCategoryId).orElse(null);
110 } 75 }
111 - return success(new PageResult<>(list, 20L)); 76 + return productScopeValue;
112 } 77 }
113 78
114 } 79 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponMatchReqVO.java
@@ -15,15 +15,15 @@ public class AppCouponMatchReqVO { @@ -15,15 +15,15 @@ public class AppCouponMatchReqVO {
15 @NotNull(message = "商品金额不能为空") 15 @NotNull(message = "商品金额不能为空")
16 private Integer price; 16 private Integer price;
17 17
18 - @Schema(description = "商品 SPU 编号的数组", required = true, example = "[1, 2]") 18 + @Schema(description = "商品 SPU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
19 @NotEmpty(message = "商品 SPU 编号不能为空") 19 @NotEmpty(message = "商品 SPU 编号不能为空")
20 private List<Long> spuIds; 20 private List<Long> spuIds;
21 21
22 - @Schema(description = "商品 SKU 编号的数组", required = true, example = "[1, 2]") 22 + @Schema(description = "商品 SKU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
23 @NotEmpty(message = "商品 SKU 编号不能为空") 23 @NotEmpty(message = "商品 SKU 编号不能为空")
24 private List<Long> skuIds; 24 private List<Long> skuIds;
25 25
26 - @Schema(description = "分类编号的数组", required = true, example = "[10, 20]") 26 + @Schema(description = "分类编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[10, 20]")
27 @NotEmpty(message = "分类编号不能为空") 27 @NotEmpty(message = "分类编号不能为空")
28 private List<Long> categoryIds; 28 private List<Long> categoryIds;
29 29
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/coupon/AppCouponPageReqVO.java
1 package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon; 1 package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 7 import lombok.Data;
6 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
@@ -13,6 +15,7 @@ import lombok.ToString; @@ -13,6 +15,7 @@ import lombok.ToString;
13 public class AppCouponPageReqVO extends PageParam { 15 public class AppCouponPageReqVO extends PageParam {
14 16
15 @Schema(description = "优惠劵状态", example = "1") 17 @Schema(description = "优惠劵状态", example = "1")
  18 + @InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
16 private Integer status; 19 private Integer status;
17 20
18 } 21 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplatePageReqVO.java
1 package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template; 1 package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 7 import lombok.Data;
6 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
@@ -12,8 +14,10 @@ import lombok.ToString; @@ -12,8 +14,10 @@ import lombok.ToString;
12 @ToString(callSuper = true) 14 @ToString(callSuper = true)
13 public class AppCouponTemplatePageReqVO extends PageParam { 15 public class AppCouponTemplatePageReqVO extends PageParam {
14 16
15 - @Schema(description = "使用类型", example = "1")  
16 - // TODO 芋艿:这里要限制下枚举的使用  
17 - private Integer useType; 17 + @Schema(description = "商品范围", example = "1")
  18 + @InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
  19 + private Integer productScope;
18 20
  21 + @Schema(description = "商品标号", example = "1")
  22 + private Long spuId;
19 } 23 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
@@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.promotion.convert.coupon; @@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.promotion.convert.coupon;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
  9 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
6 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 11 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
8 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 12 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
@@ -11,6 +15,8 @@ import org.mapstruct.Mapper; @@ -11,6 +15,8 @@ import org.mapstruct.Mapper;
11 import org.mapstruct.factory.Mappers; 15 import org.mapstruct.factory.Mappers;
12 16
13 import java.time.LocalDateTime; 17 import java.time.LocalDateTime;
  18 +import java.util.Collection;
  19 +import java.util.List;
14 20
15 /** 21 /**
16 * 优惠劵 Convert 22 * 优惠劵 Convert
@@ -49,4 +55,10 @@ public interface CouponConvert { @@ -49,4 +55,10 @@ public interface CouponConvert {
49 } 55 }
50 return couponDO; 56 return couponDO;
51 } 57 }
  58 +
  59 + CouponPageReqVO convert(AppCouponPageReqVO pageReqVO, Collection<Long> userIds);
  60 +
  61 + PageResult<AppCouponRespVO> convertAppPage(PageResult<CouponDO> pageResult);
  62 +
  63 + List<AppCouponMatchRespVO> convertList(List<CouponDO> list);
52 } 64 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java
1 package cn.iocoder.yudao.module.promotion.convert.coupon; 1 package cn.iocoder.yudao.module.promotion.convert.coupon;
2 2
  3 +import cn.hutool.core.map.MapUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO;
6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
  9 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
  10 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 11 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
8 import org.mapstruct.Mapper; 12 import org.mapstruct.Mapper;
9 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
10 14
  15 +import java.util.List;
  16 +import java.util.Map;
  17 +
11 /** 18 /**
12 * 优惠劵模板 Convert 19 * 优惠劵模板 Convert
13 * 20 *
@@ -26,4 +33,25 @@ public interface CouponTemplateConvert { @@ -26,4 +33,25 @@ public interface CouponTemplateConvert {
26 33
27 PageResult<CouponTemplateRespVO> convertPage(PageResult<CouponTemplateDO> page); 34 PageResult<CouponTemplateRespVO> convertPage(PageResult<CouponTemplateDO> page);
28 35
  36 + CouponTemplatePageReqVO convert(AppCouponTemplatePageReqVO pageReqVO, List<Integer> canTakeTypes, Integer productScope, Long productScopeValue);
  37 +
  38 + PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult);
  39 +
  40 + default PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult, Map<Long, Integer> couponTakeCountMap) {
  41 + PageResult<AppCouponTemplateRespVO> result = convertAppPage(pageResult);
  42 + if (MapUtil.isEmpty(couponTakeCountMap)) {
  43 + return result;
  44 + }
  45 +
  46 + for (AppCouponTemplateRespVO vo : result.getList()) {
  47 + // 每人领取数量无限制
  48 + if (vo.getTakeLimitCount() == -1) {
  49 + vo.setTakeStatus(false);
  50 + continue;
  51 + }
  52 + // 检查已领取数量是否超过限领数量
  53 + vo.setTakeStatus(MapUtil.getInt(couponTakeCountMap, vo.getId(), 0) >= vo.getTakeLimitCount());
  54 + }
  55 + return result;
  56 + }
29 } 57 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java
@@ -110,7 +110,7 @@ public class RewardActivityDO extends BaseDO { @@ -110,7 +110,7 @@ public class RewardActivityDO extends BaseDO {
110 */ 110 */
111 private List<Long> couponIds; 111 private List<Long> couponIds;
112 /** 112 /**
113 - * 赠送的优惠卷数量的数组 113 + * 赠送的优惠券数量的数组
114 */ 114 */
115 private List<Integer> couponCounts; 115 private List<Integer> couponCounts;
116 116
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.coupon; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
2 2
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  10 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  11 +import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
8 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  13 +import com.github.yulichang.toolkit.MPJWrappers;
9 import org.apache.ibatis.annotations.Mapper; 14 import org.apache.ibatis.annotations.Mapper;
10 15
  16 +import java.time.LocalDateTime;
11 import java.util.Collection; 17 import java.util.Collection;
12 import java.util.List; 18 import java.util.List;
  19 +import java.util.function.Function;
  20 +import java.util.stream.Collectors;
13 21
14 /** 22 /**
15 * 优惠劵 Mapper 23 * 优惠劵 Mapper
@@ -19,11 +27,11 @@ import java.util.List; @@ -19,11 +27,11 @@ import java.util.List;
19 @Mapper 27 @Mapper
20 public interface CouponMapper extends BaseMapperX<CouponDO> { 28 public interface CouponMapper extends BaseMapperX<CouponDO> {
21 29
22 - default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO, Collection<Long> userIds) { 30 + default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO) {
23 return selectPage(reqVO, new LambdaQueryWrapperX<CouponDO>() 31 return selectPage(reqVO, new LambdaQueryWrapperX<CouponDO>()
24 .eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId()) 32 .eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId())
25 .eqIfPresent(CouponDO::getStatus, reqVO.getStatus()) 33 .eqIfPresent(CouponDO::getStatus, reqVO.getStatus())
26 - .inIfPresent(CouponDO::getUserId, userIds) 34 + .inIfPresent(CouponDO::getUserId, reqVO.getUserIds())
27 .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime()) 35 .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime())
28 .orderByDesc(CouponDO::getId)); 36 .orderByDesc(CouponDO::getId));
29 } 37 }
@@ -62,4 +70,36 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; { @@ -62,4 +70,36 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; {
62 ); 70 );
63 } 71 }
64 72
  73 + default List<CouponTakeCountBO> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
  74 + return BeanUtil.copyToList(selectMaps(MPJWrappers.lambdaJoin(CouponDO.class)
  75 + .select(CouponDO::getTemplateId)
  76 + .selectCount(CouponDO::getId, CouponTakeCountBO::getCount)
  77 + .eq(CouponDO::getUserId, userId)
  78 + .in(CouponDO::getTemplateId, templateIds)
  79 + .groupBy(CouponDO::getTemplateId)), CouponTakeCountBO.class);
  80 + }
  81 +
  82 + default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
  83 + Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
  84 +
  85 + Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
  86 + .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
  87 + .collect(Collectors.joining(" OR "));
  88 + return selectList(new LambdaQueryWrapperX<CouponDO>()
  89 + .eq(CouponDO::getUserId, userId)
  90 + .eq(CouponDO::getStatus, status)
  91 + .le(CouponDO::getUsePrice, usePrice)
  92 + .and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope())
  93 + .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
  94 + .apply(productScopeValuesFindInSetFunc.apply(spuIds)))
  95 + .or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
  96 + .apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
  97 + }
  98 +
  99 + default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {
  100 + return selectList(new LambdaQueryWrapperX<CouponDO>()
  101 + .eq(CouponDO::getStatus, status)
  102 + .le(CouponDO::getValidEndTime, validEndTime)
  103 + );
  104 + }
65 } 105 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
@@ -39,6 +39,9 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; { @@ -39,6 +39,9 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
39 .eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus()) 39 .eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus())
40 .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType()) 40 .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())
41 .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime()) 41 .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())
  42 + .eqIfPresent(CouponTemplateDO::getProductScope, reqVO.getProductScope())
  43 + .and(reqVO.getProductScopeValue() != null, w -> w.apply("FIND_IN_SET({0}, product_scope_values)",
  44 + reqVO.getProductScopeValue()))
42 .and(canTakeConsumer != null, canTakeConsumer) 45 .and(canTakeConsumer != null, canTakeConsumer)
43 .orderByDesc(CouponTemplateDO::getId)); 46 .orderByDesc(CouponTemplateDO::getId));
44 } 47 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/job/CouponExpireJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.job;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  5 +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
  6 +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 优惠券过期 Job
  13 + *
  14 + * @author owen
  15 + */
  16 +@Component
  17 +@TenantJob
  18 +public class CouponExpireJob implements JobHandler {
  19 +
  20 + @Resource
  21 + private CouponService couponService;
  22 +
  23 + @Override
  24 + public String execute(String param) {
  25 + int count = couponService.expireCoupon();
  26 + return StrUtil.format("过期优惠券 {} 个", count);
  27 + }
  28 +
  29 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -3,12 +3,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon; @@ -3,12 +3,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
6 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
7 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 8 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
  9 +import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
8 10
  11 +import java.util.Collection;
9 import java.util.List; 12 import java.util.List;
  13 +import java.util.Map;
10 import java.util.Set; 14 import java.util.Set;
11 15
  16 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  17 +
12 /** 18 /**
13 * 优惠劵 Service 接口 19 * 优惠劵 Service 接口
14 * 20 *
@@ -18,11 +24,11 @@ public interface CouponService { @@ -18,11 +24,11 @@ public interface CouponService {
18 24
19 /** 25 /**
20 * 校验优惠劵,包括状态、有限期 26 * 校验优惠劵,包括状态、有限期
21 - * 27 + * <p>
22 * 1. 如果校验通过,则返回优惠劵信息 28 * 1. 如果校验通过,则返回优惠劵信息
23 * 2. 如果校验不通过,则直接抛出业务异常 29 * 2. 如果校验不通过,则直接抛出业务异常
24 * 30 *
25 - * @param id 优惠劵编号 31 + * @param id 优惠劵编号
26 * @param userId 用户编号 32 * @param userId 用户编号
27 * @return 优惠劵信息 33 * @return 优惠劵信息
28 */ 34 */
@@ -31,9 +37,8 @@ public interface CouponService { @@ -31,9 +37,8 @@ public interface CouponService {
31 /** 37 /**
32 * 校验优惠劵,包括状态、有限期 38 * 校验优惠劵,包括状态、有限期
33 * 39 *
34 - * @see #validCoupon(Long, Long) 逻辑相同,只是入参不同  
35 - *  
36 * @param coupon 优惠劵 40 * @param coupon 优惠劵
  41 + * @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
37 */ 42 */
38 void validCoupon(CouponDO coupon); 43 void validCoupon(CouponDO coupon);
39 44
@@ -124,4 +129,39 @@ public interface CouponService { @@ -124,4 +129,39 @@ public interface CouponService {
124 takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER); 129 takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER);
125 } 130 }
126 131
  132 + /**
  133 + * 统计会员领取优惠券的数量
  134 + *
  135 + * @param templateIds 优惠券模板编号列表
  136 + * @param userId 用户编号
  137 + * @return 领取优惠券的数量
  138 + */
  139 + default Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
  140 + return convertMap(getTakeCountListByTemplateIds(templateIds, userId), CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount);
  141 + }
  142 +
  143 + /**
  144 + * 统计会员领取优惠券的数量
  145 + *
  146 + * @param templateIds 优惠券模板编号列表
  147 + * @param userId 用户编号
  148 + * @return 领取优惠券的数量
  149 + */
  150 + List<CouponTakeCountBO> getTakeCountListByTemplateIds(Collection<Long> templateIds, Long userId);
  151 +
  152 + /**
  153 + * 获取用户匹配的优惠券列表
  154 + *
  155 + * @param userId 用户编号
  156 + * @param matchReqVO 匹配参数
  157 + * @return 优惠券列表
  158 + */
  159 + List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
  160 +
  161 + /**
  162 + * 过期优惠券
  163 + *
  164 + * @return 过期数量
  165 + */
  166 + int expireCoupon();
127 } 167 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -2,15 +2,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon; @@ -2,15 +2,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
2 2
3 import cn.hutool.core.collection.CollStreamUtil; 3 import cn.hutool.core.collection.CollStreamUtil;
4 import cn.hutool.core.collection.CollUtil; 4 import cn.hutool.core.collection.CollUtil;
  5 +import cn.hutool.core.collection.ListUtil;
5 import cn.hutool.core.map.MapUtil; 6 import cn.hutool.core.map.MapUtil;
6 import cn.hutool.core.util.ObjectUtil; 7 import cn.hutool.core.util.ObjectUtil;
7 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
  9 +import cn.hutool.extra.spring.SpringUtil;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 10 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 11 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
10 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 12 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
11 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 13 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
12 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 14 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
13 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 15 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  16 +import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
14 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; 17 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
15 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 18 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
16 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 19 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
@@ -18,12 +21,15 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; @@ -18,12 +21,15 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
18 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 21 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
19 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 22 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
20 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; 23 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
  24 +import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
  25 +import lombok.extern.slf4j.Slf4j;
21 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
22 import org.springframework.transaction.annotation.Transactional; 27 import org.springframework.transaction.annotation.Transactional;
23 import org.springframework.validation.annotation.Validated; 28 import org.springframework.validation.annotation.Validated;
24 29
25 import javax.annotation.Resource; 30 import javax.annotation.Resource;
26 import java.time.LocalDateTime; 31 import java.time.LocalDateTime;
  32 +import java.util.Collection;
27 import java.util.List; 33 import java.util.List;
28 import java.util.Map; 34 import java.util.Map;
29 import java.util.Set; 35 import java.util.Set;
@@ -39,6 +45,7 @@ import static java.util.Arrays.asList; @@ -39,6 +45,7 @@ import static java.util.Arrays.asList;
39 * 45 *
40 * @author 芋道源码 46 * @author 芋道源码
41 */ 47 */
  48 +@Slf4j
42 @Service 49 @Service
43 @Validated 50 @Validated
44 public class CouponServiceImpl implements CouponService { 51 public class CouponServiceImpl implements CouponService {
@@ -77,16 +84,16 @@ public class CouponServiceImpl implements CouponService { @@ -77,16 +84,16 @@ public class CouponServiceImpl implements CouponService {
77 @Override 84 @Override
78 public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) { 85 public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {
79 // 获得用户编号 86 // 获得用户编号
80 - Set<Long> userIds = null;  
81 if (StrUtil.isNotEmpty(pageReqVO.getNickname())) { 87 if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {
82 - userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()), 88 + Set<Long> userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()),
83 MemberUserRespDTO::getId); 89 MemberUserRespDTO::getId);
84 if (CollUtil.isEmpty(userIds)) { 90 if (CollUtil.isEmpty(userIds)) {
85 return PageResult.empty(); 91 return PageResult.empty();
86 } 92 }
  93 + pageReqVO.setUserIds(userIds);
87 } 94 }
88 // 分页查询 95 // 分页查询
89 - return couponMapper.selectPage(pageReqVO, userIds); 96 + return couponMapper.selectPage(pageReqVO);
90 } 97 }
91 98
92 @Override 99 @Override
@@ -175,6 +182,57 @@ public class CouponServiceImpl implements CouponService { @@ -175,6 +182,57 @@ public class CouponServiceImpl implements CouponService {
175 couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size()); 182 couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
176 } 183 }
177 184
  185 + @Override
  186 + public List<CouponTakeCountBO> getTakeCountListByTemplateIds(Collection<Long> templateIds, Long userId) {
  187 + if (CollUtil.isEmpty(templateIds)) {
  188 + return ListUtil.empty();
  189 + }
  190 + return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
  191 + }
  192 +
  193 + @Override
  194 + public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
  195 + return couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId, CouponStatusEnum.UNUSED.getStatus(),
  196 + matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
  197 + }
  198 +
  199 + @Override
  200 + public int expireCoupon() {
  201 + // 1. 查询待过期的优惠券
  202 + List<CouponDO> list = couponMapper.selectListByStatusAndValidEndTimeLe(
  203 + CouponStatusEnum.UNUSED.getStatus(), LocalDateTime.now());
  204 + if (CollUtil.isEmpty(list)) {
  205 + return 0;
  206 + }
  207 +
  208 + // 2. 遍历执行
  209 + int count = 0;
  210 + for (CouponDO coupon : list) {
  211 + try {
  212 + boolean success = getSelf().expireCoupon(coupon);
  213 + if (success) {
  214 + count++;
  215 + }
  216 + } catch (Exception e) {
  217 + log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId(), e);
  218 + }
  219 + }
  220 + return count;
  221 + }
  222 +
  223 + private boolean expireCoupon(CouponDO coupon) {
  224 + // 更新记录状态
  225 + CouponDO updateObj = new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus());
  226 + int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(), updateObj);
  227 + if (updateRows == 0) {
  228 + log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId());
  229 + return false;
  230 + }
  231 +
  232 + log.info("[expireCoupon][coupon({}) 更新为已过期成功]", coupon.getId());
  233 + return true;
  234 + }
  235 +
178 /** 236 /**
179 * 校验优惠券是否可以领取 237 * 校验优惠券是否可以领取
180 * 238 *
@@ -211,7 +269,7 @@ public class CouponServiceImpl implements CouponService { @@ -211,7 +269,7 @@ public class CouponServiceImpl implements CouponService {
211 /** 269 /**
212 * 过滤掉达到领取上线的用户 270 * 过滤掉达到领取上线的用户
213 * 271 *
214 - * @param userIds 用户编号数组 272 + * @param userIds 用户编号数组
215 * @param couponTemplate 优惠劵模版 273 * @param couponTemplate 优惠劵模版
216 */ 274 */
217 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) { 275 private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
@@ -228,4 +286,12 @@ public class CouponServiceImpl implements CouponService { @@ -228,4 +286,12 @@ public class CouponServiceImpl implements CouponService {
228 userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount()); 286 userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());
229 } 287 }
230 288
  289 + /**
  290 + * 获得自身的代理对象,解决 AOP 生效问题
  291 + *
  292 + * @return 自己
  293 + */
  294 + private CouponServiceImpl getSelf() {
  295 + return SpringUtil.getBean(getClass());
  296 + }
231 } 297 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/bo/CouponTakeCountBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.service.coupon.bo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 优惠券领取数量 BO
  7 + *
  8 + * @author owen
  9 + */
  10 +@Data
  11 +public class CouponTakeCountBO {
  12 + /**
  13 + * 优惠劵模板编号
  14 + */
  15 + private Long templateId;
  16 + /**
  17 + * 领取数量
  18 + */
  19 + private Integer count;
  20 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java
@@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO; @@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
10 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; 10 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
11 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; 11 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
12 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; 12 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
13 -import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;  
14 -import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;  
15 import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; 13 import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
16 import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService; 14 import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
  15 +import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
  16 +import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
17 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO; 17 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
18 import io.swagger.v3.oas.annotations.Operation; 18 import io.swagger.v3.oas.annotations.Operation;
19 import io.swagger.v3.oas.annotations.Parameter; 19 import io.swagger.v3.oas.annotations.Parameter;
@@ -92,20 +92,17 @@ public class BrokerageUserController { @@ -92,20 +92,17 @@ public class BrokerageUserController {
92 Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId); 92 Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
93 // 查询用户信息 93 // 查询用户信息
94 Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds); 94 Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
95 - // TODO @疯狂:看看下面两个 getBrokerageUserCountByBindUserId、getWithdrawSummaryByUserId 有没可能一次性出结果,不然 n 次有点太花性能了;  
96 // 合计分佣订单 95 // 合计分佣订单
97 - Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = convertMap(userIds,  
98 - userId -> userId,  
99 - userId -> brokerageRecordService.getUserBrokerageSummaryByUserId(userId,  
100 - BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus())); 96 + Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId(
  97 + userIds, BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus());
  98 + // TODO @芋艿:看看下面 getBrokerageUserCountByBindUserId 有没可能一次性出结果,不然 n 次有点太花性能了;
101 // 合计推广用户数量 99 // 合计推广用户数量
102 Map<Long, Long> brokerageUserCountMap = convertMap(userIds, 100 Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
103 userId -> userId, 101 userId -> userId,
104 userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null)); 102 userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null));
105 // 合计提现 103 // 合计提现
106 - Map<Long, UserWithdrawSummaryBO> withdrawMap = convertMap(userIds,  
107 - userId -> userId,  
108 - userId -> brokerageWithdrawService.getWithdrawSummaryByUserId(userId, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS)); 104 + Map<Long, UserWithdrawSummaryBO> withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId(userIds,
  105 + BrokerageWithdrawStatusEnum.AUDIT_SUCCESS);
109 // 拼接返回 106 // 拼接返回
110 return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap, 107 return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap,
111 userOrderSummaryMap, withdrawMap)); 108 userOrderSummaryMap, withdrawMap));
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java
@@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.*; @@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.*;
27 import javax.annotation.Resource; 27 import javax.annotation.Resource;
28 import javax.validation.Valid; 28 import javax.validation.Valid;
29 import java.time.LocalDateTime; 29 import java.time.LocalDateTime;
  30 +import java.util.Collections;
30 import java.util.Map; 31 import java.util.Map;
31 import java.util.Optional; 32 import java.util.Optional;
32 33
@@ -76,23 +77,25 @@ public class AppBrokerageUserController { @@ -76,23 +77,25 @@ public class AppBrokerageUserController {
76 @Operation(summary = "获得个人分销统计") 77 @Operation(summary = "获得个人分销统计")
77 @PreAuthenticated 78 @PreAuthenticated
78 public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() { 79 public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
79 - Long userId = getLoginUserId();  
80 - // TODO @疯狂:后面这种,要不也改成 convert;感觉 controller 这样更容易看到整体;核心其实是 86、8/87、9/90、9/91 这阶段  
81 - // 统计 yesterdayPrice、withdrawPrice、firstBrokerageUserCount、secondBrokerageUserCount 字段 80 + // 查询当前登录用户信息
  81 + BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(getLoginUserId());
  82 + // 统计用户昨日的佣金
82 LocalDateTime yesterday = LocalDateTime.now().minusDays(1); 83 LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
83 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday); 84 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday);
84 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday); 85 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday);
85 - AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()  
86 - .setYesterdayPrice(brokerageRecordService.getSummaryPriceByUserId(userId, BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime))  
87 - .setWithdrawPrice(Optional.ofNullable(brokerageWithdrawService.getWithdrawSummaryByUserId(userId, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS))  
88 - .map(UserWithdrawSummaryBO::getPrice).orElse(0))  
89 - .setBrokeragePrice(0).setFrozenPrice(0)  
90 - .setFirstBrokerageUserCount(brokerageUserService.getBrokerageUserCountByBindUserId(userId, 1))  
91 - .setSecondBrokerageUserCount(brokerageUserService.getBrokerageUserCountByBindUserId(userId, 2));  
92 - // 设置 brokeragePrice、frozenPrice 字段  
93 - Optional.ofNullable(brokerageUserService.getBrokerageUser(userId))  
94 - .ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice()));  
95 - return success(respVO); 86 + Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(brokerageUser.getId(),
  87 + BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime);
  88 + // 统计用户提现的佣金
  89 + Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryByUserId(Collections.singleton(brokerageUser.getId()),
  90 + BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream()
  91 + .findFirst().map(UserWithdrawSummaryBO::getPrice).orElse(0);
  92 + // 统计分销用户数量(一级)
  93 + Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 1);
  94 + // 统计分销用户数量(二级)
  95 + Long secondBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 2);
  96 +
  97 + // 拼接返回
  98 + return success(BrokerageUserConvert.INSTANCE.convert(yesterdayPrice, withdrawPrice, firstBrokerageUserCount, secondBrokerageUserCount, brokerageUser));
96 } 99 }
97 100
98 @GetMapping("/rank-page-by-user-count") 101 @GetMapping("/rank-page-by-user-count")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
@@ -37,6 +37,8 @@ public class AppTradeOrderSettlementRespVO { @@ -37,6 +37,8 @@ public class AppTradeOrderSettlementRespVO {
37 37
38 // ========== SPU 信息 ========== 38 // ========== SPU 信息 ==========
39 39
  40 + @Schema(description = "品类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  41 + private Long categoryId;
40 @Schema(description = "SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") 42 @Schema(description = "SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
41 private Long spuId; 43 private Long spuId;
42 @Schema(description = "SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "Apple iPhone 12") 44 @Schema(description = "SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "Apple iPhone 12")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/BrokerageUserConvert.java
1 package cn.iocoder.yudao.module.trade.convert.brokerage; 1 package cn.iocoder.yudao.module.trade.convert.brokerage;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
6 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserRespVO; 7 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserRespVO;
  8 +import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserMySummaryRespVO;
7 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO; 9 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
9 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO; 11 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
@@ -71,4 +73,18 @@ public interface BrokerageUserConvert { @@ -71,4 +73,18 @@ public interface BrokerageUserConvert {
71 73
72 void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByUserCountRespVO to); 74 void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByUserCountRespVO to);
73 75
  76 + default AppBrokerageUserMySummaryRespVO convert(Integer yesterdayPrice, Integer withdrawPrice,
  77 + Long firstBrokerageUserCount, Long secondBrokerageUserCount,
  78 + BrokerageUserDO brokerageUser) {
  79 + AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
  80 + .setYesterdayPrice(ObjUtil.defaultIfNull(yesterdayPrice, 0))
  81 + .setWithdrawPrice(ObjUtil.defaultIfNull(withdrawPrice, 0))
  82 + .setBrokeragePrice(0).setFrozenPrice(0)
  83 + .setFirstBrokerageUserCount(ObjUtil.defaultIfNull(firstBrokerageUserCount, 0L))
  84 + .setSecondBrokerageUserCount(ObjUtil.defaultIfNull(secondBrokerageUserCount, 0L));
  85 + // 设置 brokeragePrice、frozenPrice 字段
  86 + Optional.ofNullable(brokerageUser)
  87 + .ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice()));
  88 + return respVO;
  89 + }
74 } 90 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java
1 package cn.iocoder.yudao.module.trade.dal.mysql.brokerage; 1 package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
2 2
  3 +import cn.hutool.core.bean.BeanUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -9,12 +10,15 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO; @@ -9,12 +10,15 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
9 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO; 10 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
10 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 11 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
11 import com.baomidou.mybatisplus.core.metadata.IPage; 12 import com.baomidou.mybatisplus.core.metadata.IPage;
  13 +import com.github.yulichang.toolkit.MPJWrappers;
12 import org.apache.ibatis.annotations.Mapper; 14 import org.apache.ibatis.annotations.Mapper;
13 import org.apache.ibatis.annotations.Param; 15 import org.apache.ibatis.annotations.Param;
14 import org.apache.ibatis.annotations.Select; 16 import org.apache.ibatis.annotations.Select;
15 17
16 import java.time.LocalDateTime; 18 import java.time.LocalDateTime;
  19 +import java.util.Collection;
17 import java.util.List; 20 import java.util.List;
  21 +import java.util.Map;
18 22
19 /** 23 /**
20 * 佣金记录 Mapper 24 * 佣金记录 Mapper
@@ -52,11 +56,27 @@ public interface BrokerageRecordMapper extends BaseMapperX&lt;BrokerageRecordDO&gt; { @@ -52,11 +56,27 @@ public interface BrokerageRecordMapper extends BaseMapperX&lt;BrokerageRecordDO&gt; {
52 BrokerageRecordDO::getUserId, userId); 56 BrokerageRecordDO::getUserId, userId);
53 } 57 }
54 58
55 - @Select("SELECT COUNT(1), SUM(price) FROM trade_brokerage_record " +  
56 - "WHERE user_id = #{userId} AND biz_type = #{bizType} AND status = #{status} AND deleted = FALSE")  
57 - UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId,  
58 - @Param("bizType") Integer bizType,  
59 - @Param("status") Integer status); 59 + default List<UserBrokerageSummaryBO> selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection<Long> userIds,
  60 + Integer bizType,
  61 + Integer status) {
  62 + List<Map<String, Object>> list = selectMaps(MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
  63 + .select(BrokerageRecordDO::getUserId)
  64 + .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
  65 + .selectSum(BrokerageRecordDO::getPrice)
  66 + .in(BrokerageRecordDO::getUserId, userIds)
  67 + .eq(BrokerageRecordDO::getBizId, bizType)
  68 + .eq(BrokerageRecordDO::getStatus, status)
  69 + .groupBy(BrokerageRecordDO::getUserId));
  70 + return BeanUtil.copyToList(list, UserBrokerageSummaryBO.class);
  71 +// return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
  72 +// .select(BrokerageRecordDO::getUserId)
  73 +// .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
  74 +// .selectSum(BrokerageRecordDO::getPrice)
  75 +// .in(BrokerageRecordDO::getUserId, userIds)
  76 +// .eq(BrokerageRecordDO::getBizId, bizType)
  77 +// .eq(BrokerageRecordDO::getStatus, status)
  78 +// .groupBy(BrokerageRecordDO::getUserId));
  79 + }
60 80
61 @Select("SELECT SUM(price) FROM trade_brokerage_record " + 81 @Select("SELECT SUM(price) FROM trade_brokerage_record " +
62 "WHERE user_id = #{userId} AND biz_type = #{bizType} " + 82 "WHERE user_id = #{userId} AND biz_type = #{bizType} " +
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java
1 package cn.iocoder.yudao.module.trade.dal.mysql.brokerage; 1 package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
2 2
  3 +import cn.hutool.core.bean.BeanUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -7,9 +8,12 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.Brok @@ -7,9 +8,12 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.Brok
7 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
8 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO; 9 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  11 +import com.github.yulichang.wrapper.MPJLambdaWrapper;
10 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
11 -import org.apache.ibatis.annotations.Param;  
12 -import org.apache.ibatis.annotations.Select; 13 +
  14 +import java.util.Collection;
  15 +import java.util.List;
  16 +import java.util.Map;
13 17
14 /** 18 /**
15 * 佣金提现 Mapper 19 * 佣金提现 Mapper
@@ -37,9 +41,23 @@ public interface BrokerageWithdrawMapper extends BaseMapperX&lt;BrokerageWithdrawDO @@ -37,9 +41,23 @@ public interface BrokerageWithdrawMapper extends BaseMapperX&lt;BrokerageWithdrawDO
37 .eq(BrokerageWithdrawDO::getStatus, status)); 41 .eq(BrokerageWithdrawDO::getStatus, status));
38 } 42 }
39 43
40 - @Select("SELECT COUNT(1) AS count, SUM(price) AS price FROM trade_brokerage_withdraw " +  
41 - "WHERE user_id = #{userId} AND status = #{status} AND deleted = FALSE")  
42 - UserWithdrawSummaryBO selectCountAndSumPriceByUserIdAndStatus(@Param("userId") Long userId,  
43 - @Param("status") Integer status); 44 + default List<UserWithdrawSummaryBO> selectCountAndSumPriceByUserIdAndStatus(Collection<Long> userIds, Integer status) {
  45 + List<Map<String, Object>> list = selectMaps(new MPJLambdaWrapper<BrokerageWithdrawDO>()
  46 + .select(BrokerageWithdrawDO::getUserId)
  47 + .selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount)
  48 + .selectSum(BrokerageWithdrawDO::getPrice)
  49 + .in(BrokerageWithdrawDO::getUserId, userIds)
  50 + .eq(BrokerageWithdrawDO::getStatus, status)
  51 + .groupBy(BrokerageWithdrawDO::getUserId));
  52 + return BeanUtil.copyToList(list, UserWithdrawSummaryBO.class);
  53 + // selectJoinList有BUG,会与租户插件冲突:解析SQL时,发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
  54 +// return selectJoinList(UserWithdrawSummaryBO.class, new MPJLambdaWrapper<BrokerageWithdrawDO>()
  55 +// .select(BrokerageWithdrawDO::getUserId)
  56 +// .selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount)
  57 +// .selectSum(BrokerageWithdrawDO::getPrice)
  58 +// .in(BrokerageWithdrawDO::getUserId, userIds)
  59 +// .eq(BrokerageWithdrawDO::getStatus, status)
  60 +// .groupBy(BrokerageWithdrawDO::getUserId));
  61 + }
44 62
45 } 63 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java
@@ -85,7 +85,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa @@ -85,7 +85,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
85 85
86 @Override 86 @Override
87 public TradeAfterSaleDO getAfterSale(Long userId, Long id) { 87 public TradeAfterSaleDO getAfterSale(Long userId, Long id) {
88 - return tradeAfterSaleMapper.selectByIdAndUserId(id, userId); 88 + return tradeAfterSaleMapper.selectByIdAndUserId(id, userId);
89 } 89 }
90 90
91 @Override 91 @Override
@@ -167,9 +167,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa @@ -167,9 +167,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
167 tradeAfterSaleMapper.insert(afterSale); 167 tradeAfterSaleMapper.insert(afterSale);
168 168
169 // 更新交易订单项的售后状态 169 // 更新交易订单项的售后状态
170 - tradeOrderUpdateService.updateOrderItemAfterSaleStatus(orderItem.getId(),  
171 - TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),  
172 - afterSale.getId(), null); 170 + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCreate(orderItem.getId(), afterSale.getId());
173 171
174 // 记录售后日志 172 // 记录售后日志
175 createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(), 173 createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(),
@@ -219,8 +217,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa @@ -219,8 +217,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
219 // TODO 发送售后消息 217 // TODO 发送售后消息
220 218
221 // 更新交易订单项的售后状态为【未申请】 219 // 更新交易订单项的售后状态为【未申请】
222 - tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),  
223 - TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); 220 + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
224 } 221 }
225 222
226 /** 223 /**
@@ -313,8 +310,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa @@ -313,8 +310,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
313 // TODO 发送售后消息 310 // TODO 发送售后消息
314 311
315 // 更新交易订单项的售后状态为【未申请】 312 // 更新交易订单项的售后状态为【未申请】
316 - tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),  
317 - TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); 313 + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
318 } 314 }
319 315
320 /** 316 /**
@@ -360,9 +356,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa @@ -360,9 +356,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
360 // TODO 发送售后消息 356 // TODO 发送售后消息
361 357
362 // 更新交易订单项的售后状态为【已完成】 358 // 更新交易订单项的售后状态为【已完成】
363 - tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),  
364 - TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(),  
365 - null, afterSale.getRefundPrice()); 359 + tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice());
366 } 360 }
367 361
368 private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) { 362 private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
@@ -403,8 +397,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa @@ -403,8 +397,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
403 // TODO 发送售后消息 397 // TODO 发送售后消息
404 398
405 // 更新交易订单项的售后状态为【未申请】 399 // 更新交易订单项的售后状态为【未申请】
406 - tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),  
407 - TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); 400 + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
408 } 401 }
409 402
410 @Override 403 @Override
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordService.java
@@ -12,7 +12,11 @@ import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO @@ -12,7 +12,11 @@ import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO
12 12
13 import javax.validation.Valid; 13 import javax.validation.Valid;
14 import java.time.LocalDateTime; 14 import java.time.LocalDateTime;
  15 +import java.util.Collection;
15 import java.util.List; 16 import java.util.List;
  17 +import java.util.Map;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
16 20
17 /** 21 /**
18 * 佣金记录 Service 接口 22 * 佣金记录 Service 接口
@@ -76,12 +80,24 @@ public interface BrokerageRecordService { @@ -76,12 +80,24 @@ public interface BrokerageRecordService {
76 /** 80 /**
77 * 汇总用户佣金 81 * 汇总用户佣金
78 * 82 *
79 - * @param userId 用户编号 83 + * @param userIds 用户编号
  84 + * @param bizType 业务类型
  85 + * @param status 佣金状态
  86 + * @return 用户佣金汇总
  87 + */
  88 + List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status);
  89 +
  90 + /**
  91 + * 汇总用户佣金
  92 + *
  93 + * @param userIds 用户编号
80 * @param bizType 业务类型 94 * @param bizType 业务类型
81 * @param status 佣金状态 95 * @param status 佣金状态
82 * @return 用户佣金汇总 96 * @return 用户佣金汇总
83 */ 97 */
84 - UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status); 98 + default Map<Long, UserBrokerageSummaryBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
  99 + return convertMap(getUserBrokerageSummaryByUserId(userIds, bizType, status), UserBrokerageSummaryBO::getUserId);
  100 + }
85 101
86 /** 102 /**
87 * 获得用户佣金合计 103 * 获得用户佣金合计
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java
@@ -36,10 +36,7 @@ import org.springframework.validation.annotation.Validated; @@ -36,10 +36,7 @@ import org.springframework.validation.annotation.Validated;
36 36
37 import javax.annotation.Resource; 37 import javax.annotation.Resource;
38 import java.time.LocalDateTime; 38 import java.time.LocalDateTime;
39 -import java.util.ArrayList;  
40 -import java.util.List;  
41 -import java.util.Objects;  
42 -import java.util.Optional; 39 +import java.util.*;
43 40
44 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 41 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
45 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; 42 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
@@ -235,9 +232,8 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService { @@ -235,9 +232,8 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
235 } 232 }
236 233
237 @Override 234 @Override
238 - public UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status) {  
239 - UserBrokerageSummaryBO summaryBO = brokerageRecordMapper.selectCountAndSumPriceByUserIdAndBizTypeAndStatus(userId, bizType, status);  
240 - return summaryBO != null ? summaryBO : new UserBrokerageSummaryBO(0, 0); 235 + public List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
  236 + return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status);
241 } 237 }
242 238
243 @Override 239 @Override
@@ -338,7 +334,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService { @@ -338,7 +334,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
338 if (BooleanUtil.isTrue(spu.getSubCommissionType())) { 334 if (BooleanUtil.isTrue(spu.getSubCommissionType())) {
339 fixedMinPrice = getMinValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice); 335 fixedMinPrice = getMinValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
340 fixedMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice); 336 fixedMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
341 - // 3.2 全局分佣模式(根据商品价格比例计算) 337 + // 3.2 全局分佣模式(根据商品价格比例计算)
342 } else { 338 } else {
343 spuMinPrice = getMinValue(skuList, ProductSkuRespDTO::getPrice); 339 spuMinPrice = getMinValue(skuList, ProductSkuRespDTO::getPrice);
344 spuMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getPrice); 340 spuMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getPrice);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageUserService.java
1 package cn.iocoder.yudao.module.trade.service.brokerage; 1 package cn.iocoder.yudao.module.trade.service.brokerage;
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.framework.common.util.date.LocalDateTimeUtils;  
5 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO; 4 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
6 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO; 5 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
7 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO; 6 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
@@ -111,21 +110,7 @@ public interface BrokerageUserService { @@ -111,21 +110,7 @@ public interface BrokerageUserService {
111 * @param registerTime 用户注册时间 110 * @param registerTime 用户注册时间
112 * @return 是否绑定 111 * @return 是否绑定
113 */ 112 */
114 - default boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime) {  
115 - // 注册时间在30秒内的,都算新用户  
116 - boolean isNewUser = LocalDateTimeUtils.afterNow(registerTime.minusSeconds(30));  
117 - return bindBrokerageUser(userId, bindUserId, isNewUser);  
118 - }  
119 -  
120 - /**  
121 - * 【会员】绑定推广员  
122 - *  
123 - * @param userId 用户编号  
124 - * @param bindUserId 推广员编号  
125 - * @param isNewUser 是否为新用户  
126 - * @return 是否绑定  
127 - */  
128 - boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser); 113 + boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime);
129 114
130 /** 115 /**
131 * 获取用户是否有分销资格 116 * 获取用户是否有分销资格
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageUserServiceImpl.java
@@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.ArrayUtil; 5 import cn.hutool.core.util.ArrayUtil;
6 import cn.hutool.core.util.BooleanUtil; 6 import cn.hutool.core.util.BooleanUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
8 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; 9 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
9 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO; 10 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
10 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO; 11 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
@@ -154,7 +155,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService { @@ -154,7 +155,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
154 } 155 }
155 156
156 @Override 157 @Override
157 - public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) { 158 + public boolean bindBrokerageUser(Long userId, Long bindUserId, LocalDateTime registerTime) {
158 // 1. 获得分销用户 159 // 1. 获得分销用户
159 boolean isNewBrokerageUser = false; 160 boolean isNewBrokerageUser = false;
160 BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId); 161 BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
@@ -164,7 +165,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService { @@ -164,7 +165,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
164 } 165 }
165 166
166 // 2.1 校验是否能绑定用户 167 // 2.1 校验是否能绑定用户
167 - boolean validated = isUserCanBind(brokerageUser, isNewUser); 168 + boolean validated = isUserCanBind(brokerageUser, registerTime);
168 if (!validated) { 169 if (!validated) {
169 return false; 170 return false;
170 } 171 }
@@ -222,7 +223,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService { @@ -222,7 +223,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
222 return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); 223 return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
223 } 224 }
224 225
225 - private boolean isUserCanBind(BrokerageUserDO user, Boolean isNewUser) { 226 + private boolean isUserCanBind(BrokerageUserDO user, LocalDateTime registerTime) {
226 // 校验分销功能是否启用 227 // 校验分销功能是否启用
227 TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); 228 TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
228 if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) { 229 if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
@@ -236,8 +237,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService { @@ -236,8 +237,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
236 237
237 // 校验分销关系绑定模式 238 // 校验分销关系绑定模式
238 if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) { 239 if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
239 - // TODO @疯狂:是不是把 isNewUser 挪到这里好点呀?  
240 - if (!BooleanUtil.isTrue(isNewUser)) { 240 + // 判断是否为新用户:注册时间在30秒内的,都算新用户
  241 + boolean isNotNewUser = LocalDateTimeUtils.beforeNow(registerTime.plusSeconds(30));
  242 + if (isNotNewUser) {
241 throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定 243 throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定
242 } 244 }
243 } else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) { 245 } else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java
@@ -7,6 +7,13 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawD @@ -7,6 +7,13 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawD
7 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; 7 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
8 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO; 8 import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
9 9
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +import java.util.Set;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  16 +
10 /** 17 /**
11 * 佣金提现 Service 接口 18 * 佣金提现 Service 接口
12 * 19 *
@@ -51,10 +58,20 @@ public interface BrokerageWithdrawService { @@ -51,10 +58,20 @@ public interface BrokerageWithdrawService {
51 /** 58 /**
52 * 汇总用户提现 59 * 汇总用户提现
53 * 60 *
54 - * @param userId 用户编号  
55 - * @param status 提现状态 61 + * @param userIds 用户编号
  62 + * @param status 提现状态
56 * @return 用户提现汇总 63 * @return 用户提现汇总
57 */ 64 */
58 - UserWithdrawSummaryBO getWithdrawSummaryByUserId(Long userId, BrokerageWithdrawStatusEnum status); 65 + List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status);
59 66
  67 + /**
  68 + * 汇总用户提现
  69 + *
  70 + * @param userIds 用户编号
  71 + * @param status 提现状态
  72 + * @return 用户提现汇总
  73 + */
  74 + default Map<Long, UserWithdrawSummaryBO> getWithdrawSummaryMapByUserId(Set<Long> userIds, BrokerageWithdrawStatusEnum status) {
  75 + return convertMap(getWithdrawSummaryByUserId(userIds, status), UserWithdrawSummaryBO::getUserId);
  76 + }
60 } 77 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
@@ -26,6 +26,8 @@ import org.springframework.validation.annotation.Validated; @@ -26,6 +26,8 @@ import org.springframework.validation.annotation.Validated;
26 import javax.annotation.Resource; 26 import javax.annotation.Resource;
27 import javax.validation.Validator; 27 import javax.validation.Validator;
28 import java.time.LocalDateTime; 28 import java.time.LocalDateTime;
  29 +import java.util.Collection;
  30 +import java.util.List;
29 import java.util.Map; 31 import java.util.Map;
30 32
31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -142,9 +144,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -142,9 +144,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
142 } 144 }
143 145
144 @Override 146 @Override
145 - public UserWithdrawSummaryBO getWithdrawSummaryByUserId(Long userId, BrokerageWithdrawStatusEnum status) {  
146 - UserWithdrawSummaryBO summaryBO = brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userId, status.getStatus());  
147 - return summaryBO != null ? summaryBO : new UserWithdrawSummaryBO(0, 0); 147 + public List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status) {
  148 + return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());
148 } 149 }
149 150
150 /** 151 /**
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/bo/UserBrokerageSummaryBO.java
@@ -13,7 +13,10 @@ import lombok.NoArgsConstructor; @@ -13,7 +13,10 @@ import lombok.NoArgsConstructor;
13 @NoArgsConstructor 13 @NoArgsConstructor
14 @AllArgsConstructor 14 @AllArgsConstructor
15 public class UserBrokerageSummaryBO { 15 public class UserBrokerageSummaryBO {
16 - 16 + /**
  17 + * 用户编号
  18 + */
  19 + private Long userId;
17 /** 20 /**
18 * 佣金数量 21 * 佣金数量
19 */ 22 */
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/bo/UserWithdrawSummaryBO.java
@@ -15,6 +15,11 @@ import lombok.NoArgsConstructor; @@ -15,6 +15,11 @@ import lombok.NoArgsConstructor;
15 public class UserWithdrawSummaryBO { 15 public class UserWithdrawSummaryBO {
16 16
17 /** 17 /**
  18 + * 用户编号
  19 + */
  20 + private Long userId;
  21 +
  22 + /**
18 * 提现次数 23 * 提现次数
19 */ 24 */
20 private Integer count; 25 private Integer count;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
1 package cn.iocoder.yudao.module.trade.service.order; 1 package cn.iocoder.yudao.module.trade.service.order;
2 2
3 -import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;  
4 -import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;  
5 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; 3 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
6 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO; 4 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
  5 +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
  6 +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
7 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; 7 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
8 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; 8 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
9 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; 9 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
10 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; 10 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
12 12
  13 +import javax.validation.constraints.NotNull;
  14 +
13 /** 15 /**
14 * 交易订单【写】Service 接口 16 * 交易订单【写】Service 接口
15 * 17 *
@@ -116,27 +118,28 @@ public interface TradeOrderUpdateService { @@ -116,27 +118,28 @@ public interface TradeOrderUpdateService {
116 // =================== Order Item =================== 118 // =================== Order Item ===================
117 119
118 /** 120 /**
119 - * 更新交易订单项的售后状态 121 + * 当售后申请后,更新交易订单项的售后状态
  122 + *
  123 + * @param id 交易订单项编号
  124 + * @param afterSaleId 售后单编号
  125 + */
  126 + void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId);
  127 +
  128 +
  129 + /**
  130 + * 当售后完成后,更新交易订单项的售后状态
120 * 131 *
121 - * @param id 交易订单项编号  
122 - * @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常  
123 - * @param newAfterSaleStatus 目标售后状态 132 + * @param id 交易订单项编号
  133 + * @param refundPrice 退款金额
124 */ 134 */
125 - default void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {  
126 - updateOrderItemAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, null, null);  
127 - } 135 + void updateOrderItemWhenAfterSaleSuccess(@NotNull Long id, @NotNull Integer refundPrice);
128 136
129 /** 137 /**
130 - * 更新交易订单项的售后状态 138 + * 当售后取消(用户取消、管理员驳回、管理员拒绝收货)后,更新交易订单项的售后状态
131 * 139 *
132 - * @param id 交易订单项编号  
133 - * @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常  
134 - * @param newAfterSaleStatus 目标售后状态  
135 - * @param afterSaleId 售后单编号;当订单项发起售后时,必须传递该字段  
136 - * @param refundPrice 退款金额;当订单项退款成功时,必须传递该值 140 + * @param id 交易订单项编号
137 */ 141 */
138 - void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,  
139 - Long afterSaleId, Integer refundPrice); 142 + void updateOrderItemWhenAfterSaleCancel(@NotNull Long id);
140 143
141 /** 144 /**
142 * 【会员】创建订单项的评论 145 * 【会员】创建订单项的评论
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -74,10 +74,7 @@ import org.springframework.transaction.annotation.Transactional; @@ -74,10 +74,7 @@ import org.springframework.transaction.annotation.Transactional;
74 74
75 import javax.annotation.Resource; 75 import javax.annotation.Resource;
76 import java.time.LocalDateTime; 76 import java.time.LocalDateTime;
77 -import java.util.ArrayList;  
78 -import java.util.List;  
79 -import java.util.Objects;  
80 -import java.util.Set; 77 +import java.util.*;
81 78
82 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 79 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
83 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 80 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -363,7 +360,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -363,7 +360,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
363 360
364 /** 361 /**
365 * 校验交易订单满足被支付的条件 362 * 校验交易订单满足被支付的条件
366 - * 363 + * <p>
367 * 1. 交易订单未支付 364 * 1. 交易订单未支付
368 * 2. 支付单已支付 365 * 2. 支付单已支付
369 * 366 *
@@ -416,7 +413,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -416,7 +413,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
416 413
417 @Override 414 @Override
418 @Transactional(rollbackFor = Exception.class) 415 @Transactional(rollbackFor = Exception.class)
419 - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL) 416 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
420 public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) { 417 public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
421 // 1.1 校验并获得交易订单(可发货) 418 // 1.1 校验并获得交易订单(可发货)
422 TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId()); 419 TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
@@ -455,7 +452,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -455,7 +452,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
455 452
456 /** 453 /**
457 * 校验交易订单满足被发货的条件 454 * 校验交易订单满足被发货的条件
458 - * 455 + * <p>
459 * 1. 交易订单未发货 456 * 1. 交易订单未发货
460 * 457 *
461 * @param id 交易订单编号 458 * @param id 交易订单编号
@@ -521,7 +518,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -521,7 +518,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
521 for (TradeOrderDO order : orders) { 518 for (TradeOrderDO order : orders) {
522 try { 519 try {
523 getSelf().receiveOrderBySystem(order); 520 getSelf().receiveOrderBySystem(order);
524 - count ++; 521 + count++;
525 } catch (Throwable e) { 522 } catch (Throwable e) {
526 log.error("[receiveOrderBySystem][order({}) 自动收货订单异常]", order.getId(), e); 523 log.error("[receiveOrderBySystem][order({}) 自动收货订单异常]", order.getId(), e);
527 } 524 }
@@ -559,7 +556,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -559,7 +556,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
559 556
560 /** 557 /**
561 * 校验交易订单满足可售货的条件 558 * 校验交易订单满足可售货的条件
562 - * 559 + * <p>
563 * 1. 交易订单待收货 560 * 1. 交易订单待收货
564 * 561 *
565 * @param userId 用户编号 562 * @param userId 用户编号
@@ -612,7 +609,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -612,7 +609,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
612 for (TradeOrderDO order : orders) { 609 for (TradeOrderDO order : orders) {
613 try { 610 try {
614 getSelf().cancelOrderBySystem(order); 611 getSelf().cancelOrderBySystem(order);
615 - count ++; 612 + count++;
616 } catch (Throwable e) { 613 } catch (Throwable e) {
617 log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e); 614 log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e);
618 } 615 }
@@ -634,7 +631,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -634,7 +631,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
634 /** 631 /**
635 * 取消订单的核心实现 632 * 取消订单的核心实现
636 * 633 *
637 - * @param order 订单 634 + * @param order 订单
638 * @param cancelType 取消类型 635 * @param cancelType 取消类型
639 */ 636 */
640 private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) { 637 private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
@@ -754,68 +751,81 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -754,68 +751,81 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
754 751
755 // =================== Order Item =================== 752 // =================== Order Item ===================
756 753
757 - // TODO 疯狂:帮我重构下:  
758 - // 1. updateOrderItemAfterSaleStatus 拆分成三个方法:发起;同意;拒绝。原因是,职责更清晰,操作日志也更容易记录;  
759 -  
760 @Override 754 @Override
761 - @Transactional(rollbackFor = Exception.class)  
762 - public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,  
763 - Long afterSaleId, Integer refundPrice) {  
764 - // 如果退款成功,则 refundPrice 非空  
765 - if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())  
766 - && refundPrice == null) {  
767 - throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));  
768 - }  
769 - // 如果退款发起,则 afterSaleId 非空  
770 - if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus())  
771 - && afterSaleId == null) { 755 + public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
  756 + if (afterSaleId == null) {
772 throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id)); 757 throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id));
773 } 758 }
774 759
775 // 更新订单项 760 // 更新订单项
776 - int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);  
777 - if (updateCount <= 0) {  
778 - throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);  
779 - } 761 + updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
  762 + TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId);
  763 + }
780 764
781 - // 如果有退款金额,则需要更新订单 765 + @Override
  766 + @Transactional(rollbackFor = Exception.class)
  767 + public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
782 if (refundPrice == null) { 768 if (refundPrice == null) {
783 - return; 769 + throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
784 } 770 }
785 - // 计算总的退款金额 771 +
  772 + // 1. 更新订单项
  773 + updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
  774 + TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
  775 +
  776 + // 2. 计算总的退款金额、退回积分
786 TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id); 777 TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
787 TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId()); 778 TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
788 Integer orderRefundPrice = order.getRefundPrice() + refundPrice; 779 Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
789 - // TODO @疯狂:809 到 817 改成:cancelOrderByAfterSale:相当于全部售后成功后,就是要取消胆子; 780 + Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
790 if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单 781 if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
791 - tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())  
792 - .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice).setRefundPoint(order.getRefundPoint() + orderItem.getUsePoint())  
793 - .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));  
794 -  
795 - // TODO 芋艿:记录订单日志  
796 -  
797 - // TODO 芋艿:要不要退优惠劵  
798 - 782 + cancelOrderByAfterSale(order, orderRefundPrice, orderRefundPoint);
799 } else { // 如果部分售后,则更新退款金额 783 } else { // 如果部分售后,则更新退款金额
800 tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()) 784 tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
801 - .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice)); 785 + .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus())
  786 + .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
802 } 787 }
803 788
804 // TODO 芋艿:这块扣减规则,需要在考虑下 789 // TODO 芋艿:这块扣减规则,需要在考虑下
805 - // 售后成功后,执行数据回滚逻辑  
806 - if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {  
807 - // TODO @疯狂:这里库存也要扣减下;  
808 - // 扣减用户积分(赠送的)  
809 - reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, afterSaleId);  
810 - // 增加用户积分(返还抵扣)  
811 - addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, afterSaleId);  
812 - // 扣减用户经验  
813 - getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId);  
814 - // 更新分佣记录为已失效  
815 - getSelf().cancelBrokerageAsync(order.getUserId(), id); 790 + // 3.1 回滚数据:增加 SKU 库存
  791 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
  792 + // 3.2 回滚数据:扣减用户积分(赠送的)
  793 + reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
  794 + // 3.3 回滚数据:增加用户积分(返还抵扣)
  795 + addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());
  796 + // 3.4 回滚数据:扣减用户经验
  797 + getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, orderItem.getAfterSaleId());
  798 + // 3.5 回滚数据:更新分佣记录为已失效
  799 + getSelf().cancelBrokerageAsync(order.getUserId(), id);
  800 + }
  801 +
  802 + @Override
  803 + public void updateOrderItemWhenAfterSaleCancel(Long id) {
  804 + // 更新订单项
  805 + updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
  806 + TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
  807 + }
  808 +
  809 + private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
  810 + Long afterSaleId) {
  811 + // 更新订单项
  812 + int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
  813 + if (updateCount <= 0) {
  814 + throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
816 } 815 }
817 } 816 }
818 817
  818 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
  819 + private void cancelOrderByAfterSale(TradeOrderDO order, Integer orderRefundPrice, Integer refundPoint) {
  820 + // 1. 更新订单
  821 + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
  822 + .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus())
  823 + .setRefundPrice(orderRefundPrice).setRefundPoint(refundPoint)
  824 + .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
  825 + // 2. 退还优惠券
  826 + couponApi.returnUsedCoupon(order.getCouponId());
  827 + }
  828 +
819 829
820 /** 830 /**
821 * 判断指定订单的所有订单项,是不是都售后成功 831 * 判断指定订单的所有订单项,是不是都售后成功
@@ -879,7 +889,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -879,7 +889,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
879 for (TradeOrderDO order : orders) { 889 for (TradeOrderDO order : orders) {
880 try { 890 try {
881 getSelf().createOrderItemCommentBySystemBySystem(order); 891 getSelf().createOrderItemCommentBySystemBySystem(order);
882 - count ++; 892 + count++;
883 } catch (Throwable e) { 893 } catch (Throwable e) {
884 log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e); 894 log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e);
885 } 895 }
@@ -923,7 +933,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -923,7 +933,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
923 /** 933 /**
924 * 创建订单项的评论的核心实现 934 * 创建订单项的评论的核心实现
925 * 935 *
926 - * @param orderItem 订单项 936 + * @param orderItem 订单项
927 * @param createReqVO 评论内容 937 * @param createReqVO 评论内容
928 * @return 评论编号 938 * @return 评论编号
929 */ 939 */
@@ -979,14 +989,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -979,14 +989,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
979 989
980 /** 990 /**
981 * 创建分销记录 991 * 创建分销记录
982 - * 992 + * <p>
983 * 目前是支付成功后,就会创建分销记录。 993 * 目前是支付成功后,就会创建分销记录。
984 - * 994 + * <p>
985 * 业内还有两种做法,可以根据自己的业务调整: 995 * 业内还有两种做法,可以根据自己的业务调整:
986 - * 1. 确认收货后,才创建分销记录  
987 - * 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻 996 + * 1. 确认收货后,才创建分销记录
  997 + * 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
988 * 998 *
989 - * @param userId 用户编号 999 + * @param userId 用户编号
990 * @param orderId 订单编号 1000 * @param orderId 订单编号
991 */ 1001 */
992 @Async 1002 @Async
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointGiveCalculator.java
@@ -56,7 +56,6 @@ public class TradePointGiveCalculator implements TradePriceCalculator { @@ -56,7 +56,6 @@ public class TradePointGiveCalculator implements TradePriceCalculator {
56 TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); 56 TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i);
57 // 商品可能赠送了积分,所以这里要加上 57 // 商品可能赠送了积分,所以这里要加上
58 orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i)); 58 orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i));
59 - TradePriceCalculatorHelper.recountPayPrice(orderItem); // TODO @疯狂:这个应该不用调用哇?不影响支付金额  
60 } 59 }
61 // 3.3 更新订单赠送积分 60 // 3.3 更新订单赠送积分
62 TradePriceCalculatorHelper.recountAllGivePoint(result); 61 TradePriceCalculatorHelper.recountAllGivePoint(result);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.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.util.BooleanUtil; 3 import cn.hutool.core.util.BooleanUtil;
4 -import cn.hutool.core.util.NumberUtil;  
5 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.module.member.api.point.MemberPointApi; 5 import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
7 import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; 6 import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO;
@@ -15,10 +14,11 @@ import org.springframework.core.annotation.Order; @@ -15,10 +14,11 @@ import org.springframework.core.annotation.Order;
15 import org.springframework.stereotype.Component; 14 import org.springframework.stereotype.Component;
16 15
17 import javax.annotation.Resource; 16 import javax.annotation.Resource;
18 -import java.math.RoundingMode;  
19 import java.util.List; 17 import java.util.List;
20 18
  19 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; 20 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
  21 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
22 22
23 // TODO @疯狂:搞个单测,嘿嘿; 23 // TODO @疯狂:搞个单测,嘿嘿;
24 /** 24 /**
@@ -45,7 +45,7 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator { @@ -45,7 +45,7 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
45 } 45 }
46 // 1.2 校验积分抵扣是否开启 46 // 1.2 校验积分抵扣是否开启
47 MemberPointConfigRespDTO config = memberPointApi.getConfig(); 47 MemberPointConfigRespDTO config = memberPointApi.getConfig();
48 - if (!checkDeductPointEnable(config)) { 48 + if (!isDeductPointEnable(config)) {
49 return; 49 return;
50 } 50 }
51 // 1.3 校验用户积分余额 51 // 1.3 校验用户积分余额
@@ -55,7 +55,6 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator { @@ -55,7 +55,6 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
55 } 55 }
56 56
57 // 2.1 计算积分优惠金额 57 // 2.1 计算积分优惠金额
58 - // TODO @疯狂:如果计算出来,优惠金额为 0,那是不是不用执行后续逻辑哈  
59 int pointPrice = calculatePointPrice(config, user.getPoint(), result); 58 int pointPrice = calculatePointPrice(config, user.getPoint(), result);
60 // 2.2 计算分摊的积分、抵扣金额 59 // 2.2 计算分摊的积分、抵扣金额
61 List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); 60 List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected);
@@ -77,18 +76,10 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator { @@ -77,18 +76,10 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
77 TradePriceCalculatorHelper.recountAllPrice(result); 76 TradePriceCalculatorHelper.recountAllPrice(result);
78 } 77 }
79 78
80 - // TODO @疯狂:这个最好是 is 开头;因为 check 或者 validator,更多失败,会抛出异常;  
81 - private boolean checkDeductPointEnable(MemberPointConfigRespDTO config) {  
82 - // TODO @疯狂:这个要不直接写成 return config != null && config .... 多行这样一个形式;  
83 - if (config == null) {  
84 - return false;  
85 - }  
86 - if (!BooleanUtil.isTrue(config.getTradeDeductEnable())) {  
87 - return false;  
88 - }  
89 -  
90 - // 有没有配置:1 积分抵扣多少分  
91 - return config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0; 79 + private boolean isDeductPointEnable(MemberPointConfigRespDTO config) {
  80 + return config != null &&
  81 + !BooleanUtil.isTrue(config.getTradeDeductEnable()) && // 积分功能是否启用
  82 + config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0; // 有没有配置:1 积分抵扣多少分
92 } 83 }
93 84
94 private Integer calculatePointPrice(MemberPointConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) { 85 private Integer calculatePointPrice(MemberPointConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) {
@@ -98,14 +89,18 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator { @@ -98,14 +89,18 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
98 } 89 }
99 // 积分优惠金额(分) 90 // 积分优惠金额(分)
100 int pointPrice = usePoint * config.getTradeDeductUnitPrice(); 91 int pointPrice = usePoint * config.getTradeDeductUnitPrice();
101 - // 0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分  
102 - if (result.getPrice().getPayPrice() < pointPrice) {  
103 - pointPrice = result.getPrice().getPayPrice();  
104 - // 反推需要扣除的积分  
105 - usePoint = NumberUtil.toBigDecimal(pointPrice)  
106 - .divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP)  
107 - .intValue(); 92 + if (result.getPrice().getPayPrice() <= pointPrice) {
  93 + // 禁止0元购
  94 + throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
108 } 95 }
  96 +// // 允许0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分
  97 +// if (result.getPrice().getPayPrice() < pointPrice) {
  98 +// pointPrice = result.getPrice().getPayPrice();
  99 +// // 反推需要扣除的积分
  100 +// usePoint = NumberUtil.toBigDecimal(pointPrice)
  101 +// .divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP)
  102 +// .intValue();
  103 +// }
109 // 记录使用的积分 104 // 记录使用的积分
110 result.setUsePoint(usePoint); 105 result.setUsePoint(usePoint);
111 return pointPrice; 106 return pointPrice;