mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-23 07:41:53 +08:00
commit
8811ce0200
@ -1188,8 +1188,8 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -68,7 +68,7 @@ public class RewardActivityMatchRespDTO {
|
||||
*/
|
||||
private List<Long> couponIds;
|
||||
/**
|
||||
* 赠送的优惠卷数量的数组
|
||||
* 赠送的优惠券数量的数组
|
||||
*/
|
||||
private List<Integer> couponCounts;
|
||||
|
||||
|
@ -15,9 +15,9 @@ import java.util.Arrays;
|
||||
@AllArgsConstructor
|
||||
public enum PromotionProductScopeEnum implements IntArrayValuable {
|
||||
|
||||
ALL(1, "通用卷"), // 全部商品
|
||||
SPU(2, "商品卷"), // 指定商品
|
||||
CATEGORY(3, "品类卷"), // 指定商品
|
||||
ALL(1, "通用券"), // 全部商品
|
||||
SPU(2, "商品券"), // 指定商品
|
||||
CATEGORY(3, "品类券"), // 指定品类
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray();
|
||||
|
@ -45,6 +45,10 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@ -8,6 +10,7 @@ import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ -21,6 +24,7 @@ public class CouponPageReqVO extends PageParam {
|
||||
private Long templateId;
|
||||
|
||||
@Schema(description = "优惠码状态", example = "1")
|
||||
@InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@ -30,4 +34,7 @@ public class CouponPageReqVO extends PageParam {
|
||||
@Schema(description = "用户昵称", example = "芋艿")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Collection<Long> userIds;
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@ -33,8 +34,14 @@ public class CouponTemplatePageReqVO extends PageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
@Schema(description = "可以领取的类型", example = "[1,2, 3]")
|
||||
@Schema(description = "可以领取的类型", example = "[1, 2, 3]")
|
||||
@InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}")
|
||||
private List<Integer> canTakeTypes;
|
||||
|
||||
@Schema(description = "商品范围", example = "1")
|
||||
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
|
||||
private Integer productScope;
|
||||
|
||||
@Schema(description = "商品范围编号", example = "1")
|
||||
private Long productScopeValue;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public class RewardActivityBaseVO {
|
||||
@Schema(description = "赠送的优惠劵编号的数组", example = "1,2,3")
|
||||
private List<Long> couponIds;
|
||||
|
||||
@Schema(description = "赠送的优惠卷数量的数组", example = "1,2,3")
|
||||
@Schema(description = "赠送的优惠券数量的数组", example = "1,2,3")
|
||||
private List<Integer> couponCounts;
|
||||
|
||||
@AssertTrue(message = "优惠劵和数量必须一一对应")
|
||||
|
@ -1,24 +1,27 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.app.coupon;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.*;
|
||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
@ -31,73 +34,40 @@ public class AppCouponController {
|
||||
|
||||
@Resource
|
||||
private CouponService couponService;
|
||||
@Resource
|
||||
private CouponTemplateService couponTemplateService;
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@PostMapping("/take")
|
||||
@Operation(summary = "领取优惠劵")
|
||||
public CommonResult<Long> takeCoupon(@RequestBody AppCouponTemplatePageReqVO pageReqVO) {
|
||||
return success(1L);
|
||||
@Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {
|
||||
Long userId = getLoginUserId();
|
||||
// 领取
|
||||
couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
|
||||
// 检查是否可以继续领取
|
||||
CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());
|
||||
boolean canTakeAgain = true;
|
||||
if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {
|
||||
Integer takeCount = MapUtil.getInt(couponService.getTakeCountMapByTemplateIds(
|
||||
Collections.singleton(reqVO.getTemplateId()), userId), reqVO.getTemplateId(), 0);
|
||||
canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();
|
||||
}
|
||||
return success(canTakeAgain);
|
||||
}
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping("/match-list")
|
||||
@Operation(summary = "获得匹配指定商品的优惠劵列表")
|
||||
public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
|
||||
List<AppCouponMatchRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponMatchRespVO vo = new AppCouponMatchRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
vo.setMatch(random.nextBoolean());
|
||||
if (!vo.getMatch()) {
|
||||
vo.setDescription("不符合条件噢");
|
||||
}
|
||||
list.add(vo);
|
||||
}
|
||||
return success(list);
|
||||
// todo: 优惠金额倒序
|
||||
return success(CouponConvert.INSTANCE.convertList(couponService.getMatchCouponList(getLoginUserId(), matchReqVO)));
|
||||
}
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "优惠劵列表", description = "我的优惠劵")
|
||||
public CommonResult<PageResult<AppCouponRespVO>> takeCoupon(AppCouponPageReqVO pageReqVO) {
|
||||
List<AppCouponRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponRespVO vo = new AppCouponRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setStatus(pageReqVO.getStatus());
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
list.add(vo);
|
||||
}
|
||||
return success(new PageResult<>(list, 20L));
|
||||
PageResult<CouponDO> pageResult = couponService.getCouponPage(
|
||||
CouponConvert.INSTANCE.convert(pageReqVO, Collections.singleton(getLoginUserId())));
|
||||
return success(CouponConvert.INSTANCE.convertAppPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/get-unused-count")
|
||||
|
@ -1,27 +1,31 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.app.coupon;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 App - 优惠劵模板")
|
||||
@RestController
|
||||
@ -31,84 +35,45 @@ public class AppCouponTemplateController {
|
||||
|
||||
@Resource
|
||||
private CouponTemplateService couponTemplateService;
|
||||
@Resource
|
||||
private CouponService couponService;
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得优惠劵模版列表")
|
||||
@Parameters({
|
||||
@Parameter(name = "spuId", description = "商品 SPU 编号"), // 目前主要给商品详情使用
|
||||
@Parameter(name = "useType", description = "使用类型"),
|
||||
@Parameter(name = "count", description = "数量", required = true)
|
||||
})
|
||||
public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(@RequestParam(value = "spuId", required = false) Long spuId,
|
||||
@RequestParam(value = "useType", required = false) Integer useType,
|
||||
@RequestParam(value = "count", required = false, defaultValue = "10") Integer count) {
|
||||
List<AppCouponTemplateRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setTakeLimitCount(random.nextInt(10) + 1);
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidityType(random.nextInt(2) + 1);
|
||||
if (vo.getValidityType() == 1) {
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
} else {
|
||||
vo.setFixedStartTerm(random.nextInt(10));
|
||||
vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);
|
||||
}
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
vo.setTakeStatus(random.nextBoolean());
|
||||
list.add(vo);
|
||||
}
|
||||
return success(list);
|
||||
}
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
|
||||
// TODO 芋艿:待实现;和 getCouponTemplateList 类似
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得优惠劵模版分页")
|
||||
public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
|
||||
List<AppCouponTemplateRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setTakeLimitCount(random.nextInt(10) + 1);
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidityType(random.nextInt(2) + 1);
|
||||
if (vo.getValidityType() == 1) {
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
} else {
|
||||
vo.setFixedStartTerm(random.nextInt(10));
|
||||
vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);
|
||||
}
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
vo.setTakeStatus(random.nextBoolean());
|
||||
list.add(vo);
|
||||
// 1.1 处理查询条件:商品范围编号
|
||||
Long productScopeValue = getaProductScopeValue(pageReqVO);
|
||||
// 1.2 处理查询条件:领取方式=直接领取
|
||||
List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
|
||||
// 2. 分页查询
|
||||
PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(
|
||||
CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
|
||||
// 3.1 领取数量
|
||||
Map<Long, Integer> couponTakeCountMap = new HashMap<>(0);
|
||||
Long userId = getLoginUserId();
|
||||
if (userId != null) {
|
||||
List<Long> templateIds = convertList(pageResult.getList(), CouponTemplateDO::getId,
|
||||
t -> ObjUtil.notEqual(t.getTakeLimitCount(), -1)); // 只查有设置“每人限领个数”的
|
||||
couponTakeCountMap = couponService.getTakeCountMapByTemplateIds(templateIds, userId);
|
||||
}
|
||||
return success(new PageResult<>(list, 20L));
|
||||
// 3.2 拼接返回
|
||||
return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap));
|
||||
}
|
||||
|
||||
private Long getaProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
|
||||
Long productScopeValue = pageReqVO.getSpuId();
|
||||
if (pageReqVO.getProductScope() == null || Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope())) {
|
||||
// 通用券:清除商品范围
|
||||
productScopeValue = null;
|
||||
} else if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
|
||||
// 品类券:查询商品的品类
|
||||
productScopeValue = Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
|
||||
.map(ProductSpuRespDTO::getCategoryId).orElse(null);
|
||||
}
|
||||
return productScopeValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,15 +15,15 @@ public class AppCouponMatchReqVO {
|
||||
@NotNull(message = "商品金额不能为空")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "商品 SPU 编号的数组", required = true, example = "[1, 2]")
|
||||
@Schema(description = "商品 SPU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotEmpty(message = "商品 SPU 编号不能为空")
|
||||
private List<Long> spuIds;
|
||||
|
||||
@Schema(description = "商品 SKU 编号的数组", required = true, example = "[1, 2]")
|
||||
@Schema(description = "商品 SKU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotEmpty(message = "商品 SKU 编号不能为空")
|
||||
private List<Long> skuIds;
|
||||
|
||||
@Schema(description = "分类编号的数组", required = true, example = "[10, 20]")
|
||||
@Schema(description = "分类编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[10, 20]")
|
||||
@NotEmpty(message = "分类编号不能为空")
|
||||
private List<Long> categoryIds;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@ -13,6 +15,7 @@ import lombok.ToString;
|
||||
public class AppCouponPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "优惠劵状态", example = "1")
|
||||
@InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@ -12,8 +14,10 @@ import lombok.ToString;
|
||||
@ToString(callSuper = true)
|
||||
public class AppCouponTemplatePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "使用类型", example = "1")
|
||||
// TODO 芋艿:这里要限制下枚举的使用
|
||||
private Integer useType;
|
||||
@Schema(description = "商品范围", example = "1")
|
||||
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
|
||||
private Integer productScope;
|
||||
|
||||
@Schema(description = "商品标号", example = "1")
|
||||
private Long spuId;
|
||||
}
|
||||
|
@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.promotion.convert.coupon;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
@ -11,6 +15,8 @@ import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 优惠劵 Convert
|
||||
@ -49,4 +55,10 @@ public interface CouponConvert {
|
||||
}
|
||||
return couponDO;
|
||||
}
|
||||
|
||||
CouponPageReqVO convert(AppCouponPageReqVO pageReqVO, Collection<Long> userIds);
|
||||
|
||||
PageResult<AppCouponRespVO> convertAppPage(PageResult<CouponDO> pageResult);
|
||||
|
||||
List<AppCouponMatchRespVO> convertList(List<CouponDO> list);
|
||||
}
|
||||
|
@ -1,13 +1,20 @@
|
||||
package cn.iocoder.yudao.module.promotion.convert.coupon;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 优惠劵模板 Convert
|
||||
*
|
||||
@ -26,4 +33,25 @@ public interface CouponTemplateConvert {
|
||||
|
||||
PageResult<CouponTemplateRespVO> convertPage(PageResult<CouponTemplateDO> page);
|
||||
|
||||
CouponTemplatePageReqVO convert(AppCouponTemplatePageReqVO pageReqVO, List<Integer> canTakeTypes, Integer productScope, Long productScopeValue);
|
||||
|
||||
PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult);
|
||||
|
||||
default PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult, Map<Long, Integer> couponTakeCountMap) {
|
||||
PageResult<AppCouponTemplateRespVO> result = convertAppPage(pageResult);
|
||||
if (MapUtil.isEmpty(couponTakeCountMap)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (AppCouponTemplateRespVO vo : result.getList()) {
|
||||
// 每人领取数量无限制
|
||||
if (vo.getTakeLimitCount() == -1) {
|
||||
vo.setTakeStatus(false);
|
||||
continue;
|
||||
}
|
||||
// 检查已领取数量是否超过限领数量
|
||||
vo.setTakeStatus(MapUtil.getInt(couponTakeCountMap, vo.getId(), 0) >= vo.getTakeLimitCount());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public class RewardActivityDO extends BaseDO {
|
||||
*/
|
||||
private List<Long> couponIds;
|
||||
/**
|
||||
* 赠送的优惠卷数量的数组
|
||||
* 赠送的优惠券数量的数组
|
||||
*/
|
||||
private List<Integer> couponCounts;
|
||||
|
||||
|
@ -1,15 +1,23 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.github.yulichang.toolkit.MPJWrappers;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 优惠劵 Mapper
|
||||
@ -19,11 +27,11 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface CouponMapper extends BaseMapperX<CouponDO> {
|
||||
|
||||
default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO, Collection<Long> userIds) {
|
||||
default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<CouponDO>()
|
||||
.eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId())
|
||||
.eqIfPresent(CouponDO::getStatus, reqVO.getStatus())
|
||||
.inIfPresent(CouponDO::getUserId, userIds)
|
||||
.inIfPresent(CouponDO::getUserId, reqVO.getUserIds())
|
||||
.betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(CouponDO::getId));
|
||||
}
|
||||
@ -62,4 +70,36 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
|
||||
);
|
||||
}
|
||||
|
||||
default List<CouponTakeCountBO> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
|
||||
return BeanUtil.copyToList(selectMaps(MPJWrappers.lambdaJoin(CouponDO.class)
|
||||
.select(CouponDO::getTemplateId)
|
||||
.selectCount(CouponDO::getId, CouponTakeCountBO::getCount)
|
||||
.eq(CouponDO::getUserId, userId)
|
||||
.in(CouponDO::getTemplateId, templateIds)
|
||||
.groupBy(CouponDO::getTemplateId)), CouponTakeCountBO.class);
|
||||
}
|
||||
|
||||
default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
|
||||
Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
|
||||
|
||||
Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
|
||||
.map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
|
||||
.collect(Collectors.joining(" OR "));
|
||||
return selectList(new LambdaQueryWrapperX<CouponDO>()
|
||||
.eq(CouponDO::getUserId, userId)
|
||||
.eq(CouponDO::getStatus, status)
|
||||
.le(CouponDO::getUsePrice, usePrice)
|
||||
.and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope())
|
||||
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
|
||||
.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
|
||||
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
|
||||
.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
|
||||
}
|
||||
|
||||
default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {
|
||||
return selectList(new LambdaQueryWrapperX<CouponDO>()
|
||||
.eq(CouponDO::getStatus, status)
|
||||
.le(CouponDO::getValidEndTime, validEndTime)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,9 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
|
||||
.eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())
|
||||
.betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())
|
||||
.eqIfPresent(CouponTemplateDO::getProductScope, reqVO.getProductScope())
|
||||
.and(reqVO.getProductScopeValue() != null, w -> w.apply("FIND_IN_SET({0}, product_scope_values)",
|
||||
reqVO.getProductScopeValue()))
|
||||
.and(canTakeConsumer != null, canTakeConsumer)
|
||||
.orderByDesc(CouponTemplateDO::getId));
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.promotion.job;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 优惠券过期 Job
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Component
|
||||
@TenantJob
|
||||
public class CouponExpireJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private CouponService couponService;
|
||||
|
||||
@Override
|
||||
public String execute(String param) {
|
||||
int count = couponService.expireCoupon();
|
||||
return StrUtil.format("过期优惠券 {} 个", count);
|
||||
}
|
||||
|
||||
}
|
@ -3,12 +3,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 优惠劵 Service 接口
|
||||
*
|
||||
@ -18,11 +24,11 @@ public interface CouponService {
|
||||
|
||||
/**
|
||||
* 校验优惠劵,包括状态、有限期
|
||||
*
|
||||
* <p>
|
||||
* 1. 如果校验通过,则返回优惠劵信息
|
||||
* 2. 如果校验不通过,则直接抛出业务异常
|
||||
*
|
||||
* @param id 优惠劵编号
|
||||
* @param id 优惠劵编号
|
||||
* @param userId 用户编号
|
||||
* @return 优惠劵信息
|
||||
*/
|
||||
@ -31,9 +37,8 @@ public interface CouponService {
|
||||
/**
|
||||
* 校验优惠劵,包括状态、有限期
|
||||
*
|
||||
* @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
|
||||
*
|
||||
* @param coupon 优惠劵
|
||||
* @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
|
||||
*/
|
||||
void validCoupon(CouponDO coupon);
|
||||
|
||||
@ -124,4 +129,39 @@ public interface CouponService {
|
||||
takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计会员领取优惠券的数量
|
||||
*
|
||||
* @param templateIds 优惠券模板编号列表
|
||||
* @param userId 用户编号
|
||||
* @return 领取优惠券的数量
|
||||
*/
|
||||
default Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
|
||||
return convertMap(getTakeCountListByTemplateIds(templateIds, userId), CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计会员领取优惠券的数量
|
||||
*
|
||||
* @param templateIds 优惠券模板编号列表
|
||||
* @param userId 用户编号
|
||||
* @return 领取优惠券的数量
|
||||
*/
|
||||
List<CouponTakeCountBO> getTakeCountListByTemplateIds(Collection<Long> templateIds, Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户匹配的优惠券列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param matchReqVO 匹配参数
|
||||
* @return 优惠券列表
|
||||
*/
|
||||
List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
|
||||
|
||||
/**
|
||||
* 过期优惠券
|
||||
*
|
||||
* @return 过期数量
|
||||
*/
|
||||
int expireCoupon();
|
||||
}
|
||||
|
@ -2,15 +2,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
|
||||
|
||||
import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
@ -18,12 +21,15 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -39,6 +45,7 @@ import static java.util.Arrays.asList;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class CouponServiceImpl implements CouponService {
|
||||
@ -77,16 +84,16 @@ public class CouponServiceImpl implements CouponService {
|
||||
@Override
|
||||
public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {
|
||||
// 获得用户编号
|
||||
Set<Long> userIds = null;
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {
|
||||
userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()),
|
||||
Set<Long> userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()),
|
||||
MemberUserRespDTO::getId);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
pageReqVO.setUserIds(userIds);
|
||||
}
|
||||
// 分页查询
|
||||
return couponMapper.selectPage(pageReqVO, userIds);
|
||||
return couponMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -175,6 +182,57 @@ public class CouponServiceImpl implements CouponService {
|
||||
couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CouponTakeCountBO> getTakeCountListByTemplateIds(Collection<Long> templateIds, Long userId) {
|
||||
if (CollUtil.isEmpty(templateIds)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
|
||||
return couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId, CouponStatusEnum.UNUSED.getStatus(),
|
||||
matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expireCoupon() {
|
||||
// 1. 查询待过期的优惠券
|
||||
List<CouponDO> list = couponMapper.selectListByStatusAndValidEndTimeLe(
|
||||
CouponStatusEnum.UNUSED.getStatus(), LocalDateTime.now());
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 遍历执行
|
||||
int count = 0;
|
||||
for (CouponDO coupon : list) {
|
||||
try {
|
||||
boolean success = getSelf().expireCoupon(coupon);
|
||||
if (success) {
|
||||
count++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean expireCoupon(CouponDO coupon) {
|
||||
// 更新记录状态
|
||||
CouponDO updateObj = new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus());
|
||||
int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(), updateObj);
|
||||
if (updateRows == 0) {
|
||||
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("[expireCoupon][coupon({}) 更新为已过期成功]", coupon.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验优惠券是否可以领取
|
||||
*
|
||||
@ -211,7 +269,7 @@ public class CouponServiceImpl implements CouponService {
|
||||
/**
|
||||
* 过滤掉达到领取上线的用户
|
||||
*
|
||||
* @param userIds 用户编号数组
|
||||
* @param userIds 用户编号数组
|
||||
* @param couponTemplate 优惠劵模版
|
||||
*/
|
||||
private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
|
||||
@ -228,4 +286,12 @@ public class CouponServiceImpl implements CouponService {
|
||||
userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
* @return 自己
|
||||
*/
|
||||
private CouponServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.coupon.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 优惠券领取数量 BO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
public class CouponTakeCountBO {
|
||||
/**
|
||||
* 优惠劵模板编号
|
||||
*/
|
||||
private Long templateId;
|
||||
/**
|
||||
* 领取数量
|
||||
*/
|
||||
private Integer count;
|
||||
}
|
@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -92,20 +92,17 @@ public class BrokerageUserController {
|
||||
Set<Long> userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
|
||||
// 查询用户信息
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
|
||||
// TODO @疯狂:看看下面两个 getBrokerageUserCountByBindUserId、getWithdrawSummaryByUserId 有没可能一次性出结果,不然 n 次有点太花性能了;
|
||||
// 合计分佣订单
|
||||
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageRecordService.getUserBrokerageSummaryByUserId(userId,
|
||||
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus()));
|
||||
Map<Long, UserBrokerageSummaryBO> userOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId(
|
||||
userIds, BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus());
|
||||
// TODO @芋艿:看看下面 getBrokerageUserCountByBindUserId 有没可能一次性出结果,不然 n 次有点太花性能了;
|
||||
// 合计推广用户数量
|
||||
Map<Long, Long> brokerageUserCountMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null));
|
||||
// 合计提现
|
||||
Map<Long, UserWithdrawSummaryBO> withdrawMap = convertMap(userIds,
|
||||
userId -> userId,
|
||||
userId -> brokerageWithdrawService.getWithdrawSummaryByUserId(userId, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS));
|
||||
Map<Long, UserWithdrawSummaryBO> withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId(userIds,
|
||||
BrokerageWithdrawStatusEnum.AUDIT_SUCCESS);
|
||||
// 拼接返回
|
||||
return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap,
|
||||
userOrderSummaryMap, withdrawMap));
|
||||
|
@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -76,23 +77,25 @@ public class AppBrokerageUserController {
|
||||
@Operation(summary = "获得个人分销统计")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
|
||||
Long userId = getLoginUserId();
|
||||
// TODO @疯狂:后面这种,要不也改成 convert;感觉 controller 这样更容易看到整体;核心其实是 86、8/87、9/90、9/91 这阶段
|
||||
// 统计 yesterdayPrice、withdrawPrice、firstBrokerageUserCount、secondBrokerageUserCount 字段
|
||||
// 查询当前登录用户信息
|
||||
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(getLoginUserId());
|
||||
// 统计用户昨日的佣金
|
||||
LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday);
|
||||
LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday);
|
||||
AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
|
||||
.setYesterdayPrice(brokerageRecordService.getSummaryPriceByUserId(userId, BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime))
|
||||
.setWithdrawPrice(Optional.ofNullable(brokerageWithdrawService.getWithdrawSummaryByUserId(userId, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS))
|
||||
.map(UserWithdrawSummaryBO::getPrice).orElse(0))
|
||||
.setBrokeragePrice(0).setFrozenPrice(0)
|
||||
.setFirstBrokerageUserCount(brokerageUserService.getBrokerageUserCountByBindUserId(userId, 1))
|
||||
.setSecondBrokerageUserCount(brokerageUserService.getBrokerageUserCountByBindUserId(userId, 2));
|
||||
// 设置 brokeragePrice、frozenPrice 字段
|
||||
Optional.ofNullable(brokerageUserService.getBrokerageUser(userId))
|
||||
.ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice()));
|
||||
return success(respVO);
|
||||
Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(brokerageUser.getId(),
|
||||
BrokerageRecordBizTypeEnum.ORDER.getType(), beginTime, endTime);
|
||||
// 统计用户提现的佣金
|
||||
Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryByUserId(Collections.singleton(brokerageUser.getId()),
|
||||
BrokerageWithdrawStatusEnum.AUDIT_SUCCESS).stream()
|
||||
.findFirst().map(UserWithdrawSummaryBO::getPrice).orElse(0);
|
||||
// 统计分销用户数量(一级)
|
||||
Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 1);
|
||||
// 统计分销用户数量(二级)
|
||||
Long secondBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(brokerageUser.getId(), 2);
|
||||
|
||||
// 拼接返回
|
||||
return success(BrokerageUserConvert.INSTANCE.convert(yesterdayPrice, withdrawPrice, firstBrokerageUserCount, secondBrokerageUserCount, brokerageUser));
|
||||
}
|
||||
|
||||
@GetMapping("/rank-page-by-user-count")
|
||||
|
@ -37,6 +37,8 @@ public class AppTradeOrderSettlementRespVO {
|
||||
|
||||
// ========== SPU 信息 ==========
|
||||
|
||||
@Schema(description = "品类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long categoryId;
|
||||
@Schema(description = "SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long spuId;
|
||||
@Schema(description = "SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "Apple iPhone 12")
|
||||
|
@ -1,9 +1,11 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.brokerage;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserMySummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
@ -71,4 +73,18 @@ public interface BrokerageUserConvert {
|
||||
|
||||
void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByUserCountRespVO to);
|
||||
|
||||
default AppBrokerageUserMySummaryRespVO convert(Integer yesterdayPrice, Integer withdrawPrice,
|
||||
Long firstBrokerageUserCount, Long secondBrokerageUserCount,
|
||||
BrokerageUserDO brokerageUser) {
|
||||
AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
|
||||
.setYesterdayPrice(ObjUtil.defaultIfNull(yesterdayPrice, 0))
|
||||
.setWithdrawPrice(ObjUtil.defaultIfNull(withdrawPrice, 0))
|
||||
.setBrokeragePrice(0).setFrozenPrice(0)
|
||||
.setFirstBrokerageUserCount(ObjUtil.defaultIfNull(firstBrokerageUserCount, 0L))
|
||||
.setSecondBrokerageUserCount(ObjUtil.defaultIfNull(secondBrokerageUserCount, 0L));
|
||||
// 设置 brokeragePrice、frozenPrice 字段
|
||||
Optional.ofNullable(brokerageUser)
|
||||
.ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice()));
|
||||
return respVO;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
@ -9,12 +10,15 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.yulichang.toolkit.MPJWrappers;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 佣金记录 Mapper
|
||||
@ -52,11 +56,27 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
|
||||
BrokerageRecordDO::getUserId, userId);
|
||||
}
|
||||
|
||||
@Select("SELECT COUNT(1), SUM(price) FROM trade_brokerage_record " +
|
||||
"WHERE user_id = #{userId} AND biz_type = #{bizType} AND status = #{status} AND deleted = FALSE")
|
||||
UserBrokerageSummaryBO selectCountAndSumPriceByUserIdAndBizTypeAndStatus(@Param("userId") Long userId,
|
||||
@Param("bizType") Integer bizType,
|
||||
@Param("status") Integer status);
|
||||
default List<UserBrokerageSummaryBO> selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection<Long> userIds,
|
||||
Integer bizType,
|
||||
Integer status) {
|
||||
List<Map<String, Object>> list = selectMaps(MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
|
||||
.select(BrokerageRecordDO::getUserId)
|
||||
.selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
|
||||
.selectSum(BrokerageRecordDO::getPrice)
|
||||
.in(BrokerageRecordDO::getUserId, userIds)
|
||||
.eq(BrokerageRecordDO::getBizId, bizType)
|
||||
.eq(BrokerageRecordDO::getStatus, status)
|
||||
.groupBy(BrokerageRecordDO::getUserId));
|
||||
return BeanUtil.copyToList(list, UserBrokerageSummaryBO.class);
|
||||
// return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class)
|
||||
// .select(BrokerageRecordDO::getUserId)
|
||||
// .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
|
||||
// .selectSum(BrokerageRecordDO::getPrice)
|
||||
// .in(BrokerageRecordDO::getUserId, userIds)
|
||||
// .eq(BrokerageRecordDO::getBizId, bizType)
|
||||
// .eq(BrokerageRecordDO::getStatus, status)
|
||||
// .groupBy(BrokerageRecordDO::getUserId));
|
||||
}
|
||||
|
||||
@Select("SELECT SUM(price) FROM trade_brokerage_record " +
|
||||
"WHERE user_id = #{userId} AND biz_type = #{bizType} " +
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
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
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 佣金提现 Mapper
|
||||
@ -37,9 +41,23 @@ public interface BrokerageWithdrawMapper extends BaseMapperX<BrokerageWithdrawDO
|
||||
.eq(BrokerageWithdrawDO::getStatus, status));
|
||||
}
|
||||
|
||||
@Select("SELECT COUNT(1) AS count, SUM(price) AS price FROM trade_brokerage_withdraw " +
|
||||
"WHERE user_id = #{userId} AND status = #{status} AND deleted = FALSE")
|
||||
UserWithdrawSummaryBO selectCountAndSumPriceByUserIdAndStatus(@Param("userId") Long userId,
|
||||
@Param("status") Integer status);
|
||||
default List<UserWithdrawSummaryBO> selectCountAndSumPriceByUserIdAndStatus(Collection<Long> userIds, Integer status) {
|
||||
List<Map<String, Object>> list = selectMaps(new MPJLambdaWrapper<BrokerageWithdrawDO>()
|
||||
.select(BrokerageWithdrawDO::getUserId)
|
||||
.selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount)
|
||||
.selectSum(BrokerageWithdrawDO::getPrice)
|
||||
.in(BrokerageWithdrawDO::getUserId, userIds)
|
||||
.eq(BrokerageWithdrawDO::getStatus, status)
|
||||
.groupBy(BrokerageWithdrawDO::getUserId));
|
||||
return BeanUtil.copyToList(list, UserWithdrawSummaryBO.class);
|
||||
// selectJoinList有BUG,会与租户插件冲突:解析SQL时,发生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW
|
||||
// return selectJoinList(UserWithdrawSummaryBO.class, new MPJLambdaWrapper<BrokerageWithdrawDO>()
|
||||
// .select(BrokerageWithdrawDO::getUserId)
|
||||
// .selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount)
|
||||
// .selectSum(BrokerageWithdrawDO::getPrice)
|
||||
// .in(BrokerageWithdrawDO::getUserId, userIds)
|
||||
// .eq(BrokerageWithdrawDO::getStatus, status)
|
||||
// .groupBy(BrokerageWithdrawDO::getUserId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
|
||||
@Override
|
||||
public TradeAfterSaleDO getAfterSale(Long userId, Long id) {
|
||||
return tradeAfterSaleMapper.selectByIdAndUserId(id, userId);
|
||||
return tradeAfterSaleMapper.selectByIdAndUserId(id, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,9 +167,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
tradeAfterSaleMapper.insert(afterSale);
|
||||
|
||||
// 更新交易订单项的售后状态
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(orderItem.getId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
afterSale.getId(), null);
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCreate(orderItem.getId(), afterSale.getId());
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(),
|
||||
@ -219,8 +217,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,8 +310,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -360,9 +356,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【已完成】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(),
|
||||
null, afterSale.getRefundPrice());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice());
|
||||
}
|
||||
|
||||
private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
|
||||
@ -403,8 +397,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderUpdateService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -12,7 +12,11 @@ import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 佣金记录 Service 接口
|
||||
@ -76,12 +80,24 @@ public interface BrokerageRecordService {
|
||||
/**
|
||||
* 汇总用户佣金
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userIds 用户编号
|
||||
* @param bizType 业务类型
|
||||
* @param status 佣金状态
|
||||
* @return 用户佣金汇总
|
||||
*/
|
||||
UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status);
|
||||
List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status);
|
||||
|
||||
/**
|
||||
* 汇总用户佣金
|
||||
*
|
||||
* @param userIds 用户编号
|
||||
* @param bizType 业务类型
|
||||
* @param status 佣金状态
|
||||
* @return 用户佣金汇总
|
||||
*/
|
||||
default Map<Long, UserBrokerageSummaryBO> getUserBrokerageSummaryMapByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
|
||||
return convertMap(getUserBrokerageSummaryByUserId(userIds, bizType, status), UserBrokerageSummaryBO::getUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户佣金合计
|
||||
|
@ -36,10 +36,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
|
||||
@ -235,9 +232,8 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserBrokerageSummaryBO getUserBrokerageSummaryByUserId(Long userId, Integer bizType, Integer status) {
|
||||
UserBrokerageSummaryBO summaryBO = brokerageRecordMapper.selectCountAndSumPriceByUserIdAndBizTypeAndStatus(userId, bizType, status);
|
||||
return summaryBO != null ? summaryBO : new UserBrokerageSummaryBO(0, 0);
|
||||
public List<UserBrokerageSummaryBO> getUserBrokerageSummaryByUserId(Collection<Long> userIds, Integer bizType, Integer status) {
|
||||
return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -338,7 +334,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
if (BooleanUtil.isTrue(spu.getSubCommissionType())) {
|
||||
fixedMinPrice = getMinValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
|
||||
fixedMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
|
||||
// 3.2 全局分佣模式(根据商品价格比例计算)
|
||||
// 3.2 全局分佣模式(根据商品价格比例计算)
|
||||
} else {
|
||||
spuMinPrice = getMinValue(skuList, ProductSkuRespDTO::getPrice);
|
||||
spuMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getPrice);
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO;
|
||||
@ -111,21 +110,7 @@ public interface BrokerageUserService {
|
||||
* @param registerTime 用户注册时间
|
||||
* @return 是否绑定
|
||||
*/
|
||||
default boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime) {
|
||||
// 注册时间在30秒内的,都算新用户
|
||||
boolean isNewUser = LocalDateTimeUtils.afterNow(registerTime.minusSeconds(30));
|
||||
return bindBrokerageUser(userId, bindUserId, isNewUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【会员】绑定推广员
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param bindUserId 推广员编号
|
||||
* @param isNewUser 是否为新用户
|
||||
* @return 是否绑定
|
||||
*/
|
||||
boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
|
||||
boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime);
|
||||
|
||||
/**
|
||||
* 获取用户是否有分销资格
|
||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO;
|
||||
@ -154,7 +155,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
|
||||
public boolean bindBrokerageUser(Long userId, Long bindUserId, LocalDateTime registerTime) {
|
||||
// 1. 获得分销用户
|
||||
boolean isNewBrokerageUser = false;
|
||||
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
|
||||
@ -164,7 +165,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
}
|
||||
|
||||
// 2.1 校验是否能绑定用户
|
||||
boolean validated = isUserCanBind(brokerageUser, isNewUser);
|
||||
boolean validated = isUserCanBind(brokerageUser, registerTime);
|
||||
if (!validated) {
|
||||
return false;
|
||||
}
|
||||
@ -222,7 +223,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
|
||||
}
|
||||
|
||||
private boolean isUserCanBind(BrokerageUserDO user, Boolean isNewUser) {
|
||||
private boolean isUserCanBind(BrokerageUserDO user, LocalDateTime registerTime) {
|
||||
// 校验分销功能是否启用
|
||||
TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
|
||||
if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) {
|
||||
@ -236,8 +237,9 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||
|
||||
// 校验分销关系绑定模式
|
||||
if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
|
||||
// TODO @疯狂:是不是把 isNewUser 挪到这里好点呀?
|
||||
if (!BooleanUtil.isTrue(isNewUser)) {
|
||||
// 判断是否为新用户:注册时间在30秒内的,都算新用户
|
||||
boolean isNotNewUser = LocalDateTimeUtils.beforeNow(registerTime.plusSeconds(30));
|
||||
if (isNotNewUser) {
|
||||
throw exception(BROKERAGE_BIND_MODE_REGISTER); // 只有在注册时可以绑定
|
||||
}
|
||||
} else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) {
|
||||
|
@ -7,6 +7,13 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawD
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserWithdrawSummaryBO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 佣金提现 Service 接口
|
||||
*
|
||||
@ -51,10 +58,20 @@ public interface BrokerageWithdrawService {
|
||||
/**
|
||||
* 汇总用户提现
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param status 提现状态
|
||||
* @param userIds 用户编号
|
||||
* @param status 提现状态
|
||||
* @return 用户提现汇总
|
||||
*/
|
||||
UserWithdrawSummaryBO getWithdrawSummaryByUserId(Long userId, BrokerageWithdrawStatusEnum status);
|
||||
List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status);
|
||||
|
||||
/**
|
||||
* 汇总用户提现
|
||||
*
|
||||
* @param userIds 用户编号
|
||||
* @param status 提现状态
|
||||
* @return 用户提现汇总
|
||||
*/
|
||||
default Map<Long, UserWithdrawSummaryBO> getWithdrawSummaryMapByUserId(Set<Long> userIds, BrokerageWithdrawStatusEnum status) {
|
||||
return convertMap(getWithdrawSummaryByUserId(userIds, status), UserWithdrawSummaryBO::getUserId);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import org.springframework.validation.annotation.Validated;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Validator;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@ -142,9 +144,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserWithdrawSummaryBO getWithdrawSummaryByUserId(Long userId, BrokerageWithdrawStatusEnum status) {
|
||||
UserWithdrawSummaryBO summaryBO = brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userId, status.getStatus());
|
||||
return summaryBO != null ? summaryBO : new UserWithdrawSummaryBO(0, 0);
|
||||
public List<UserWithdrawSummaryBO> getWithdrawSummaryByUserId(Collection<Long> userIds, BrokerageWithdrawStatusEnum status) {
|
||||
return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,10 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserBrokerageSummaryBO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 佣金数量
|
||||
*/
|
||||
|
@ -14,6 +14,11 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class UserWithdrawSummaryBO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 提现次数
|
||||
*/
|
||||
|
@ -1,15 +1,17 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 交易订单【写】Service 接口
|
||||
*
|
||||
@ -116,27 +118,28 @@ public interface TradeOrderUpdateService {
|
||||
// =================== Order Item ===================
|
||||
|
||||
/**
|
||||
* 更新交易订单项的售后状态
|
||||
* 当售后申请后,更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
|
||||
* @param newAfterSaleStatus 目标售后状态
|
||||
* @param id 交易订单项编号
|
||||
* @param afterSaleId 售后单编号
|
||||
*/
|
||||
default void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {
|
||||
updateOrderItemAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, null, null);
|
||||
}
|
||||
void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId);
|
||||
|
||||
|
||||
/**
|
||||
* 更新交易订单项的售后状态
|
||||
* 当售后完成后,更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
|
||||
* @param newAfterSaleStatus 目标售后状态
|
||||
* @param afterSaleId 售后单编号;当订单项发起售后时,必须传递该字段
|
||||
* @param refundPrice 退款金额;当订单项退款成功时,必须传递该值
|
||||
* @param id 交易订单项编号
|
||||
* @param refundPrice 退款金额
|
||||
*/
|
||||
void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
||||
Long afterSaleId, Integer refundPrice);
|
||||
void updateOrderItemWhenAfterSaleSuccess(@NotNull Long id, @NotNull Integer refundPrice);
|
||||
|
||||
/**
|
||||
* 当售后取消(用户取消、管理员驳回、管理员拒绝收货)后,更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
*/
|
||||
void updateOrderItemWhenAfterSaleCancel(@NotNull Long id);
|
||||
|
||||
/**
|
||||
* 【会员】创建订单项的评论
|
||||
|
@ -74,10 +74,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
@ -363,7 +360,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
/**
|
||||
* 校验交易订单满足被支付的条件
|
||||
*
|
||||
* <p>
|
||||
* 1. 交易订单未支付
|
||||
* 2. 支付单已支付
|
||||
*
|
||||
@ -416,7 +413,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
|
||||
public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
|
||||
// 1.1 校验并获得交易订单(可发货)
|
||||
TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
|
||||
@ -455,7 +452,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
/**
|
||||
* 校验交易订单满足被发货的条件
|
||||
*
|
||||
* <p>
|
||||
* 1. 交易订单未发货
|
||||
*
|
||||
* @param id 交易订单编号
|
||||
@ -521,7 +518,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
for (TradeOrderDO order : orders) {
|
||||
try {
|
||||
getSelf().receiveOrderBySystem(order);
|
||||
count ++;
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[receiveOrderBySystem][order({}) 自动收货订单异常]", order.getId(), e);
|
||||
}
|
||||
@ -559,7 +556,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
/**
|
||||
* 校验交易订单满足可售货的条件
|
||||
*
|
||||
* <p>
|
||||
* 1. 交易订单待收货
|
||||
*
|
||||
* @param userId 用户编号
|
||||
@ -612,7 +609,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
for (TradeOrderDO order : orders) {
|
||||
try {
|
||||
getSelf().cancelOrderBySystem(order);
|
||||
count ++;
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e);
|
||||
}
|
||||
@ -634,7 +631,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
/**
|
||||
* 取消订单的核心实现
|
||||
*
|
||||
* @param order 订单
|
||||
* @param order 订单
|
||||
* @param cancelType 取消类型
|
||||
*/
|
||||
private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
|
||||
@ -754,66 +751,79 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
// =================== Order Item ===================
|
||||
|
||||
// TODO 疯狂:帮我重构下:
|
||||
// 1. updateOrderItemAfterSaleStatus 拆分成三个方法:发起;同意;拒绝。原因是,职责更清晰,操作日志也更容易记录;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
||||
Long afterSaleId, Integer refundPrice) {
|
||||
// 如果退款成功,则 refundPrice 非空
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())
|
||||
&& refundPrice == null) {
|
||||
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
|
||||
}
|
||||
// 如果退款发起,则 afterSaleId 非空
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus())
|
||||
&& afterSaleId == null) {
|
||||
public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
|
||||
if (afterSaleId == null) {
|
||||
throw new IllegalArgumentException(StrUtil.format("id({}) 退款发起,售后单编号不能为空", id));
|
||||
}
|
||||
|
||||
// 更新订单项
|
||||
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
|
||||
if (refundPrice == null) {
|
||||
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
|
||||
}
|
||||
|
||||
// 1. 更新订单项
|
||||
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
|
||||
|
||||
// 2. 计算总的退款金额、退回积分
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
|
||||
Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
|
||||
Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
|
||||
if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
|
||||
cancelOrderByAfterSale(order, orderRefundPrice, orderRefundPoint);
|
||||
} else { // 如果部分售后,则更新退款金额
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus())
|
||||
.setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
|
||||
}
|
||||
|
||||
// TODO 芋艿:这块扣减规则,需要在考虑下
|
||||
// 3.1 回滚数据:增加 SKU 库存
|
||||
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
|
||||
// 3.2 回滚数据:扣减用户积分(赠送的)
|
||||
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
|
||||
// 3.3 回滚数据:增加用户积分(返还抵扣)
|
||||
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());
|
||||
// 3.4 回滚数据:扣减用户经验
|
||||
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, orderItem.getAfterSaleId());
|
||||
// 3.5 回滚数据:更新分佣记录为已失效
|
||||
getSelf().cancelBrokerageAsync(order.getUserId(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderItemWhenAfterSaleCancel(Long id) {
|
||||
// 更新订单项
|
||||
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
|
||||
}
|
||||
|
||||
private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
|
||||
Long afterSaleId) {
|
||||
// 更新订单项
|
||||
int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
|
||||
if (updateCount <= 0) {
|
||||
throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有退款金额,则需要更新订单
|
||||
if (refundPrice == null) {
|
||||
return;
|
||||
}
|
||||
// 计算总的退款金额
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
|
||||
Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
|
||||
// TODO @疯狂:809 到 817 改成:cancelOrderByAfterSale:相当于全部售后成功后,就是要取消胆子;
|
||||
if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice).setRefundPoint(order.getRefundPoint() + orderItem.getUsePoint())
|
||||
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
|
||||
|
||||
// TODO 芋艿:记录订单日志
|
||||
|
||||
// TODO 芋艿:要不要退优惠劵
|
||||
|
||||
} else { // 如果部分售后,则更新退款金额
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
|
||||
}
|
||||
|
||||
// TODO 芋艿:这块扣减规则,需要在考虑下
|
||||
// 售后成功后,执行数据回滚逻辑
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
|
||||
// TODO @疯狂:这里库存也要扣减下;
|
||||
// 扣减用户积分(赠送的)
|
||||
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, afterSaleId);
|
||||
// 增加用户积分(返还抵扣)
|
||||
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, afterSaleId);
|
||||
// 扣减用户经验
|
||||
getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId);
|
||||
// 更新分佣记录为已失效
|
||||
getSelf().cancelBrokerageAsync(order.getUserId(), id);
|
||||
}
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
|
||||
private void cancelOrderByAfterSale(TradeOrderDO order, Integer orderRefundPrice, Integer refundPoint) {
|
||||
// 1. 更新订单
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus())
|
||||
.setRefundPrice(orderRefundPrice).setRefundPoint(refundPoint)
|
||||
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
|
||||
// 2. 退还优惠券
|
||||
couponApi.returnUsedCoupon(order.getCouponId());
|
||||
}
|
||||
|
||||
|
||||
@ -879,7 +889,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
for (TradeOrderDO order : orders) {
|
||||
try {
|
||||
getSelf().createOrderItemCommentBySystemBySystem(order);
|
||||
count ++;
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e);
|
||||
}
|
||||
@ -923,7 +933,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
/**
|
||||
* 创建订单项的评论的核心实现
|
||||
*
|
||||
* @param orderItem 订单项
|
||||
* @param orderItem 订单项
|
||||
* @param createReqVO 评论内容
|
||||
* @return 评论编号
|
||||
*/
|
||||
@ -979,14 +989,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
/**
|
||||
* 创建分销记录
|
||||
*
|
||||
* <p>
|
||||
* 目前是支付成功后,就会创建分销记录。
|
||||
*
|
||||
* <p>
|
||||
* 业内还有两种做法,可以根据自己的业务调整:
|
||||
* 1. 确认收货后,才创建分销记录
|
||||
* 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
|
||||
* 1. 确认收货后,才创建分销记录
|
||||
* 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userId 用户编号
|
||||
* @param orderId 订单编号
|
||||
*/
|
||||
@Async
|
||||
|
@ -56,7 +56,6 @@ public class TradePointGiveCalculator implements TradePriceCalculator {
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i);
|
||||
// 商品可能赠送了积分,所以这里要加上
|
||||
orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i));
|
||||
TradePriceCalculatorHelper.recountPayPrice(orderItem); // TODO @疯狂:这个应该不用调用哇?不影响支付金额
|
||||
}
|
||||
// 3.3 更新订单赠送积分
|
||||
TradePriceCalculatorHelper.recountAllGivePoint(result);
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
|
||||
import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO;
|
||||
@ -15,10 +14,11 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
|
||||
|
||||
// TODO @疯狂:搞个单测,嘿嘿;
|
||||
/**
|
||||
@ -45,7 +45,7 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
}
|
||||
// 1.2 校验积分抵扣是否开启
|
||||
MemberPointConfigRespDTO config = memberPointApi.getConfig();
|
||||
if (!checkDeductPointEnable(config)) {
|
||||
if (!isDeductPointEnable(config)) {
|
||||
return;
|
||||
}
|
||||
// 1.3 校验用户积分余额
|
||||
@ -55,7 +55,6 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
}
|
||||
|
||||
// 2.1 计算积分优惠金额
|
||||
// TODO @疯狂:如果计算出来,优惠金额为 0,那是不是不用执行后续逻辑哈
|
||||
int pointPrice = calculatePointPrice(config, user.getPoint(), result);
|
||||
// 2.2 计算分摊的积分、抵扣金额
|
||||
List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected);
|
||||
@ -77,18 +76,10 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
}
|
||||
|
||||
// TODO @疯狂:这个最好是 is 开头;因为 check 或者 validator,更多失败,会抛出异常;
|
||||
private boolean checkDeductPointEnable(MemberPointConfigRespDTO config) {
|
||||
// TODO @疯狂:这个要不直接写成 return config != null && config .... 多行这样一个形式;
|
||||
if (config == null) {
|
||||
return false;
|
||||
}
|
||||
if (!BooleanUtil.isTrue(config.getTradeDeductEnable())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 有没有配置:1 积分抵扣多少分
|
||||
return config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0;
|
||||
private boolean isDeductPointEnable(MemberPointConfigRespDTO config) {
|
||||
return config != null &&
|
||||
!BooleanUtil.isTrue(config.getTradeDeductEnable()) && // 积分功能是否启用
|
||||
config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0; // 有没有配置:1 积分抵扣多少分
|
||||
}
|
||||
|
||||
private Integer calculatePointPrice(MemberPointConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) {
|
||||
@ -98,14 +89,18 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
}
|
||||
// 积分优惠金额(分)
|
||||
int pointPrice = usePoint * config.getTradeDeductUnitPrice();
|
||||
// 0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分
|
||||
if (result.getPrice().getPayPrice() < pointPrice) {
|
||||
pointPrice = result.getPrice().getPayPrice();
|
||||
// 反推需要扣除的积分
|
||||
usePoint = NumberUtil.toBigDecimal(pointPrice)
|
||||
.divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP)
|
||||
.intValue();
|
||||
if (result.getPrice().getPayPrice() <= pointPrice) {
|
||||
// 禁止0元购
|
||||
throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
|
||||
}
|
||||
// // 允许0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分
|
||||
// if (result.getPrice().getPayPrice() < pointPrice) {
|
||||
// pointPrice = result.getPrice().getPayPrice();
|
||||
// // 反推需要扣除的积分
|
||||
// usePoint = NumberUtil.toBigDecimal(pointPrice)
|
||||
// .divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP)
|
||||
// .intValue();
|
||||
// }
|
||||
// 记录使用的积分
|
||||
result.setUsePoint(usePoint);
|
||||
return pointPrice;
|
||||
|
Loading…
Reference in New Issue
Block a user