mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-23 05:00:25 +08:00
feat:优惠券基本业务代码开发
This commit is contained in:
parent
a677fa8749
commit
74b3d107b3
@ -1,8 +1,14 @@
|
||||
package com.youlai.mall.sms.pojo.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponStateEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@ -10,81 +16,40 @@ import java.util.Date;
|
||||
* @author
|
||||
*/
|
||||
@Data
|
||||
public class SmsCoupon implements Serializable {
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("sms_coupon")
|
||||
public class SmsCoupon {
|
||||
/**
|
||||
* ID
|
||||
* 用户优惠券ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 优惠券标题(有图片则显示图片):无门槛50元优惠券 | 单品最高减2000元
|
||||
* 关联优惠券模板ID
|
||||
*/
|
||||
private String title;
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 图片
|
||||
* 领取用户
|
||||
*/
|
||||
private String img;
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 1满减券 2叠加满减券 3无门槛券(需要限制大小)
|
||||
* 优惠券码
|
||||
*/
|
||||
private Integer type;
|
||||
private String couponCode;
|
||||
|
||||
/**
|
||||
* 发布状态, PUBLISH发布,DRAFT草稿,OFFLINE下线
|
||||
* 优惠券状态
|
||||
*/
|
||||
private String publish;
|
||||
private CouponStateEnum state;
|
||||
|
||||
/**
|
||||
* 满多少才可以使用(为0则不限制金额)
|
||||
* 使用时间
|
||||
*/
|
||||
private Long conditionPrice;
|
||||
|
||||
/**
|
||||
* 抵扣价格
|
||||
*/
|
||||
private Long price;
|
||||
|
||||
/**
|
||||
* 优惠券总量
|
||||
*/
|
||||
private Integer publishCount;
|
||||
|
||||
/**
|
||||
* 每张优惠券限领张数(默认为1,为0不限制)
|
||||
*/
|
||||
private Integer limitCount;
|
||||
|
||||
/**
|
||||
* 已领取的优惠券数量
|
||||
*/
|
||||
private Integer takeCount;
|
||||
|
||||
/**
|
||||
* 已使用的优惠券数量
|
||||
*/
|
||||
private Integer usedCount;
|
||||
|
||||
/**
|
||||
* 发放开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 发放结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 自领取之日起有效天数
|
||||
*/
|
||||
private Integer validDays;
|
||||
|
||||
/**
|
||||
* 逻辑删除使用
|
||||
*/
|
||||
private Integer status;
|
||||
private Date useTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
@ -96,5 +61,7 @@ public class SmsCoupon implements Serializable {
|
||||
*/
|
||||
private Date gmtModified;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@TableField(exist = false)
|
||||
private SmsCouponTemplate template;
|
||||
|
||||
}
|
@ -2,8 +2,8 @@ package com.youlai.mall.sms.pojo.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponCategoryEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponTemplateStateEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.DistributeTargetEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.ProductLineEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.TemplateRuleVO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@ -30,14 +30,9 @@ public class SmsCouponTemplate {
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 是否是可用状态
|
||||
* 优惠券模板状态(0:待审核、1:进行中、2:已过期)
|
||||
*/
|
||||
private Integer available;
|
||||
|
||||
/**
|
||||
* 是否过期
|
||||
*/
|
||||
private Integer expired;
|
||||
private CouponTemplateStateEnum state;
|
||||
|
||||
/**
|
||||
* 优惠券名称
|
||||
@ -59,11 +54,6 @@ public class SmsCouponTemplate {
|
||||
*/
|
||||
private CouponCategoryEnum category;
|
||||
|
||||
/**
|
||||
* 产品线
|
||||
*/
|
||||
private ProductLineEnum productLine;
|
||||
|
||||
/**
|
||||
* 总数
|
||||
*/
|
||||
@ -91,5 +81,4 @@ public class SmsCouponTemplate {
|
||||
private Date gmtModified;
|
||||
|
||||
private String gmtModifiedBy;
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,45 @@
|
||||
package com.youlai.mall.sms.pojo.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author huawei
|
||||
* @desc
|
||||
* @desc:用户优惠券状态
|
||||
* @email huawei_code@163.com
|
||||
* @date 2021/2/28
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CouponStateEnum {
|
||||
|
||||
NEW,
|
||||
USED,
|
||||
EXPIRED;
|
||||
USABLE("可用", 1),
|
||||
USED("已使用", 5),
|
||||
OCCUPY("占用中(下单未付款)", 3),
|
||||
EXPIRED("已过期(未被使用)", 10);
|
||||
|
||||
/**
|
||||
* 用户优惠券状态描述
|
||||
*/
|
||||
@JsonValue
|
||||
private String desc;
|
||||
|
||||
/**
|
||||
* 用户优惠券状态码
|
||||
*/
|
||||
@EnumValue
|
||||
private Integer code;
|
||||
|
||||
public static CouponStateEnum of(Integer code) {
|
||||
Objects.requireNonNull(code);
|
||||
return Stream.of(values())
|
||||
.filter(bean -> bean.code.equals(code))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalArgumentException(code + " Not Exist"));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package com.youlai.mall.sms.pojo.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc
|
||||
* @date 2021/7/10
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CouponTemplateStateEnum {
|
||||
|
||||
INIT("待审核", 0),
|
||||
USED("进行中", 1),
|
||||
FINISH("已过期(活动结束)", 2);
|
||||
|
||||
/**
|
||||
* 用户优惠券状态描述
|
||||
*/
|
||||
@JsonValue
|
||||
private String desc;
|
||||
|
||||
/**
|
||||
* 用户优惠券状态码
|
||||
*/
|
||||
@EnumValue
|
||||
private Integer code;
|
||||
|
||||
public static CouponTemplateStateEnum of(Integer code) {
|
||||
Objects.requireNonNull(code);
|
||||
return Stream.of(values())
|
||||
.filter(bean -> bean.code.equals(code))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalArgumentException(code + " Not Exist"));
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.youlai.mall.sms.pojo.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 结算规则类型枚举
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RuleFlagEnum {
|
||||
// 单类型优惠券定义
|
||||
MANJIAN("满减券计算规则"),
|
||||
|
||||
ZHEKOU("折扣卷计算规则"),
|
||||
|
||||
LIJIAN("立减券计算规则"),
|
||||
|
||||
// 多类别组合优惠券定义
|
||||
MANJIAN_ZHEKOU("满减券+折扣券计算规则"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 规则描述
|
||||
*/
|
||||
private String desc;
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package com.youlai.mall.sms.pojo.form;
|
||||
|
||||
import com.youlai.mall.sms.pojo.vo.TemplateRuleVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
@ -12,48 +15,52 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券模板创建请求对象
|
||||
* @desc: 优惠券模板表单模型
|
||||
* @date 2021/6/26
|
||||
*/
|
||||
@ApiModel("优惠券模板表单模型")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CouponTemplateForm {
|
||||
|
||||
private Long id;
|
||||
@ApiModelProperty("优惠券模板ID(修改使用)")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 优惠券模板名称
|
||||
*/
|
||||
@ApiModelProperty("优惠券模板名称(最大长度255)")
|
||||
@NotBlank(message = "请填写优惠券模板名称")
|
||||
@Length(max = 255,message = "长度在0到255个字符之间")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 优惠券 logo
|
||||
*/
|
||||
@ApiModelProperty("优惠券 LOGO(最大长度255)")
|
||||
@Length(max = 255,message = "长度在0到255个字符之间")
|
||||
private String logo;
|
||||
|
||||
/**
|
||||
* 优惠券描述
|
||||
*/
|
||||
@ApiModelProperty("优惠券描述(最大长度255)")
|
||||
@NotBlank(message = "请填写优惠券描述")
|
||||
private String desc;
|
||||
@Length(max = 255,message = "长度在0到255个字符之间")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 优惠券分类
|
||||
* 优惠券分类(满减券\折扣券\立减券)
|
||||
*/
|
||||
@ApiModelProperty("优惠券分类(满减券、折扣券、立减券 ...)")
|
||||
@NotBlank(message = "请选择优惠券分类")
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 优惠券产品线
|
||||
*/
|
||||
@NotBlank(message = "请选择优惠券产品线")
|
||||
private Integer productLine;
|
||||
|
||||
/**
|
||||
* 优惠券总数量
|
||||
*/
|
||||
@ApiModelProperty("优惠券总数量(范围1到65535)")
|
||||
@NotNull(message = "请输入优惠券总数量")
|
||||
@Min(value = 1, message = "优惠券数量必须大于1")
|
||||
@Max(value = Integer.MAX_VALUE, message = "优惠券数量不能大于 " + Integer.MAX_VALUE)
|
||||
@ -62,12 +69,13 @@ public class CouponTemplateForm {
|
||||
/**
|
||||
* 目标用户
|
||||
*/
|
||||
@ApiModelProperty("目标用户(单用户、多用户)")
|
||||
private Integer target;
|
||||
|
||||
/**
|
||||
* 优惠券规则
|
||||
*/
|
||||
@ApiModelProperty("优惠券规则")
|
||||
@NotNull(message = "优惠券规则不能为空")
|
||||
private TemplateRuleVO rule;
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.youlai.mall.sms.pojo.query;
|
||||
|
||||
import com.youlai.common.base.BasePageQuery;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券领取使用详情条件分页查询
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@ApiModel("优惠券领取使用详情条件分页查询")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CouponPageQuery extends BasePageQuery {
|
||||
|
||||
@ApiModelProperty("优惠券模板ID(必传)")
|
||||
private String templateId;
|
||||
|
||||
@ApiModelProperty("状态")
|
||||
private Integer state;
|
||||
|
||||
@ApiModelProperty("优惠券码")
|
||||
private String couponCode;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.youlai.mall.sms.pojo.query;
|
||||
|
||||
import com.youlai.common.base.BasePageQuery;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券模板条件分页查询
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@ApiModel("优惠券模板条件分页查询")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CouponTemplatePageQuery extends BasePageQuery {
|
||||
|
||||
/**
|
||||
* 优惠券模板名称(模糊匹配)
|
||||
*/
|
||||
@ApiModelProperty("优惠券模板名称(模糊匹配)")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty("优惠券模板状态")
|
||||
private Integer state;
|
||||
|
||||
@ApiModelProperty("优惠券模板目标发放用户")
|
||||
private Integer target;
|
||||
|
||||
@ApiModelProperty("优惠券模板类型")
|
||||
private Integer category;
|
||||
|
||||
@ApiModelProperty("优惠券模板创建范围开始时间")
|
||||
private Date startTime;
|
||||
|
||||
@ApiModelProperty("优惠券模板创建范围结束时间")
|
||||
private Date endTime;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.youlai.mall.sms.pojo.vo;
|
||||
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCouponTemplate;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponStateEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.PeriodTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券状态分类
|
||||
* @date 2021/7/7
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CouponClassify {
|
||||
|
||||
/**
|
||||
* 可用优惠券
|
||||
*/
|
||||
private List<SmsCoupon> usable;
|
||||
|
||||
/**
|
||||
* 已使用优惠券
|
||||
*/
|
||||
private List<SmsCoupon> used;
|
||||
|
||||
/**
|
||||
* 已过期优惠券
|
||||
*/
|
||||
private List<SmsCoupon> expired;
|
||||
|
||||
public static CouponClassify classify(List<SmsCoupon> coupons) {
|
||||
List<SmsCoupon> usable = new ArrayList<>(coupons.size());
|
||||
List<SmsCoupon> used = new ArrayList<>(coupons.size());
|
||||
List<SmsCoupon> expired = new ArrayList<>(coupons.size());
|
||||
Date time = new Date();
|
||||
|
||||
coupons.forEach(coupon -> {
|
||||
boolean isTimeExpire;
|
||||
SmsCouponTemplate template = coupon.getTemplate();
|
||||
// 判断优惠券是否过期
|
||||
if (template.getRule().getExpiration().getPeriod().equals(PeriodTypeEnum.REGULAR.getCode())) {
|
||||
isTimeExpire = template.getRule().getExpiration().getDeadline() <= time.getTime();
|
||||
} else {
|
||||
isTimeExpire = DateUtils.addDays(coupon.getGmtCreate(), template.getRule().getExpiration().getGap()).getTime() <= time.getTime();
|
||||
}
|
||||
|
||||
if (coupon.getState().equals(CouponStateEnum.USED)) {
|
||||
used.add(coupon);
|
||||
} else if (coupon.getState().equals(CouponStateEnum.EXPIRED) || isTimeExpire) {
|
||||
coupon.setState(CouponStateEnum.EXPIRED);
|
||||
expired.add(coupon);
|
||||
} else {
|
||||
usable.add(coupon);
|
||||
}
|
||||
|
||||
});
|
||||
return new CouponClassify(usable, used, expired);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.youlai.mall.sms.pojo.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券结算信息模型
|
||||
* @date 2021/7/4
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SettlementInfoVO {
|
||||
|
||||
private Long userId;
|
||||
|
||||
private List<GoodsInfo> goodsInfos;
|
||||
|
||||
|
||||
private List<CouponAndTemplateInfo> couponAndTemplateInfos;
|
||||
|
||||
private Boolean employ;
|
||||
private Long pay;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class CouponAndTemplateInfo {
|
||||
private Long id;
|
||||
|
||||
private CouponTemplateVO template;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class GoodsInfo {
|
||||
private Long id;
|
||||
|
||||
private Long price;
|
||||
|
||||
private Integer count;
|
||||
|
||||
private String type;
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package com.youlai.mall.sms.pojo.vo;
|
||||
|
||||
import com.youlai.mall.sms.pojo.enums.CouponStateEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
@ -10,91 +13,37 @@ import java.util.Date;
|
||||
* @author
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmsCouponVO implements Serializable {
|
||||
/**
|
||||
* ID
|
||||
* 用户优惠券ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 优惠券标题(有图片则显示图片):无门槛50元优惠券 | 单品最高减2000元
|
||||
* 关联优惠券模板ID
|
||||
*/
|
||||
private String title;
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 图片
|
||||
* 领取用户
|
||||
*/
|
||||
private String img;
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 1满减券 2叠加满减券 3无门槛券(需要限制大小)
|
||||
* 优惠券码
|
||||
*/
|
||||
private Integer type;
|
||||
private String couponCode;
|
||||
|
||||
/**
|
||||
* 发布状态, PUBLISH发布,DRAFT草稿,OFFLINE下线
|
||||
* 优惠券状态
|
||||
*/
|
||||
private String publish;
|
||||
|
||||
/**
|
||||
* 满多少才可以使用(为0则不限制金额)
|
||||
*/
|
||||
private Long conditionPrice;
|
||||
|
||||
/**
|
||||
* 抵扣价格
|
||||
*/
|
||||
private Long price;
|
||||
|
||||
/**
|
||||
* 优惠券总量
|
||||
*/
|
||||
private Integer publishCount;
|
||||
|
||||
/**
|
||||
* 每张优惠券限领张数(默认为1,为0不限制)
|
||||
*/
|
||||
private Integer limitCount;
|
||||
|
||||
/**
|
||||
* 已领取的优惠券数量
|
||||
*/
|
||||
private Integer takeCount;
|
||||
|
||||
/**
|
||||
* 已使用的优惠券数量
|
||||
*/
|
||||
private Integer usedCount;
|
||||
|
||||
/**
|
||||
* 发放开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 发放结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 自领取之日起有效天数
|
||||
*/
|
||||
private Integer validDays;
|
||||
|
||||
/**
|
||||
* 逻辑删除使用
|
||||
*/
|
||||
private Integer status;
|
||||
private CouponStateEnum state;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date gmtCreate;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date gmtModified;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
@ -1,16 +1,22 @@
|
||||
package com.youlai.mall.sms.pojo.vo;
|
||||
|
||||
import com.youlai.mall.sms.pojo.enums.PeriodTypeEnum;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券规则对象定义
|
||||
* @date 2021/6/26
|
||||
*/
|
||||
@ApiModel("优惠券领取使用规则模型")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ -19,31 +25,41 @@ public class TemplateRuleVO {
|
||||
/**
|
||||
* 优惠券过期规则
|
||||
*/
|
||||
@ApiModelProperty("优惠券过期规则")
|
||||
@NotNull(message = "优惠券过期规则不能为空")
|
||||
private Expiration expiration;
|
||||
|
||||
/**
|
||||
* 折扣
|
||||
* 优惠券折扣规则
|
||||
*/
|
||||
@ApiModelProperty("优惠券折扣规则")
|
||||
@NotNull(message = "优惠券折扣规则不能为空")
|
||||
private Discount discount;
|
||||
|
||||
/**
|
||||
* 限领张数限制
|
||||
* 单个限领张数限制
|
||||
*/
|
||||
@ApiModelProperty("单个限领张数限制")
|
||||
@Min(value = 1,message = "单个限领张数限制不能小于1")
|
||||
private Integer limitation;
|
||||
|
||||
/**
|
||||
* 优惠券使用范围规则
|
||||
* 使用范围:地域 + 商品类型
|
||||
*/
|
||||
@ApiModelProperty("优惠券使用范围规则")
|
||||
private Usage usage;
|
||||
|
||||
/**
|
||||
* 权重(可以和哪些优惠券叠加使用,同一类的优惠券一定不能叠加)
|
||||
*/
|
||||
@ApiModelProperty("优惠券使用权重规则")
|
||||
private String weight;
|
||||
|
||||
/**
|
||||
* 有效期规则定义
|
||||
*/
|
||||
@ApiModel("有效期规则定义模型")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ -52,16 +68,22 @@ public class TemplateRuleVO {
|
||||
/**
|
||||
* 有效期规则,对应 PeriodType 的 code 字段
|
||||
*/
|
||||
@ApiModelProperty("有效期规则(固定日期、以领取之日开始计算)")
|
||||
@NotNull(message = "有效期规则不能为空")
|
||||
private Integer period;
|
||||
|
||||
/**
|
||||
* 有效间隔:只对变动性有效期有效
|
||||
*/
|
||||
@ApiModelProperty("有效间隔(单位小时,只对变动性有效期有效)")
|
||||
@Min(value = 0,message = "有效期间隔不能小于0(单位小时)")
|
||||
private Integer gap;
|
||||
|
||||
/**
|
||||
* 优惠券模板的失效日期,两类规则都有效
|
||||
*/
|
||||
@ApiModelProperty("优惠券模板的失效日期(两类规则都有效)")
|
||||
@Min(value = 0,message = "失效日期不能小于0(时间毫秒值)")
|
||||
private Long deadline;
|
||||
|
||||
boolean validate() {
|
||||
@ -73,6 +95,7 @@ public class TemplateRuleVO {
|
||||
/**
|
||||
* 折扣的规则
|
||||
*/
|
||||
@ApiModel("折扣规则定义模型")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ -81,46 +104,34 @@ public class TemplateRuleVO {
|
||||
/**
|
||||
* 额度:满减(20),折扣(85),立减(10)
|
||||
*/
|
||||
private Integer quota;
|
||||
@ApiModelProperty("额度:满减(20),折扣(85),立减(10)")
|
||||
@Min(value = 0,message = "折扣额度最小值为0")
|
||||
private Long quota;
|
||||
|
||||
/**
|
||||
* 基准:需要满多少才可用
|
||||
*/
|
||||
private Integer base;
|
||||
@ApiModelProperty("折扣基准(需要满多少才可用,为0表示无基准)")
|
||||
@Min(value = 0,message = "折扣基准最小值为0")
|
||||
private Long base;
|
||||
|
||||
boolean validate() {
|
||||
// 最简化校验
|
||||
return quota > 0 && base > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 优惠券使用范围
|
||||
*/
|
||||
@ApiModel("优惠券使用范围")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Usage {
|
||||
|
||||
/**
|
||||
* 省份
|
||||
*/
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 城市
|
||||
*/
|
||||
private String city;
|
||||
|
||||
/**
|
||||
* 商品类型,list[文娱,生鲜,家居,全品类]
|
||||
* 可根据业务扩展,增加商品现在,地域限制,下单手机号限制。。。
|
||||
*/
|
||||
private String goodsType;
|
||||
@ApiModelProperty("允许使用优惠券的商品类型(为空则表示不限制)")
|
||||
private List<String> goodsType;
|
||||
|
||||
boolean validate() {
|
||||
return StringUtils.isNotEmpty(province)
|
||||
&& StringUtils.isNotEmpty(province)
|
||||
&& StringUtils.isNotEmpty(province);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,31 @@
|
||||
package com.youlai.mall.sms.controller.admin;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCouponTemplate;
|
||||
import com.youlai.mall.sms.pojo.form.CouponTemplateForm;
|
||||
import com.youlai.mall.sms.pojo.query.CouponPageQuery;
|
||||
import com.youlai.mall.sms.pojo.query.CouponTemplatePageQuery;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.service.ISmsCouponService;
|
||||
import com.youlai.mall.sms.service.ISmsCouponTemplateService;
|
||||
import com.youlai.mall.sms.service.ITemplateBaseService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc:优惠券模板API接口
|
||||
* @desc:优惠券模板(管理台API接口)
|
||||
* @date 2021/6/26
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "优惠券模板API接口")
|
||||
@Api(tags = "优惠券模板(管理台API接口)")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/coupon_template")
|
||||
public class CouponTemplateController {
|
||||
@ -27,14 +36,32 @@ public class CouponTemplateController {
|
||||
@Autowired
|
||||
private ISmsCouponTemplateService couponTemplateService;
|
||||
|
||||
@Autowired
|
||||
private ISmsCouponService couponService;
|
||||
|
||||
@ApiOperation(value = "优惠券模板条件分页查询")
|
||||
@GetMapping("/template/page")
|
||||
public Result page(@ApiParam(value = "优惠券模板条件分页查询") CouponTemplatePageQuery query) {
|
||||
IPage<SmsCouponTemplate> pageResult = couponTemplateService.pageQuery(query);
|
||||
return Result.success(pageResult.getRecords(), pageResult.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建优惠券模板
|
||||
* 1、优惠券模板基础数据校验
|
||||
* 2、生成优惠券模板实体数据
|
||||
* 3、初始由运营人员创建的优惠券模板为待审核状态
|
||||
* 3.1、待审核状态优惠券可以修改,删除
|
||||
*
|
||||
* @param form 提交表单
|
||||
* @return result
|
||||
*/
|
||||
@ApiOperation(value = "创建优惠券模板")
|
||||
@PostMapping("/template")
|
||||
public Result<Object> createTemplate(@RequestBody CouponTemplateForm form) {
|
||||
public Result<Object> createTemplate(@ApiParam(value = "优惠券模板构建表单")
|
||||
@Validated
|
||||
@RequestBody CouponTemplateForm form) {
|
||||
|
||||
log.info("Create Coupon Template , form:{}", form);
|
||||
couponTemplateService.createTemplate(form);
|
||||
return Result.success();
|
||||
@ -42,26 +69,58 @@ public class CouponTemplateController {
|
||||
|
||||
/**
|
||||
* 修改优惠券模板
|
||||
* 待审核状态优惠券模板,允许修改
|
||||
* 1、优惠券模板基础数据校验
|
||||
* 2、修改优惠券模板实体数据
|
||||
*
|
||||
* @param form 提交表单
|
||||
* @return result
|
||||
*/
|
||||
@ApiOperation(value = "修改优惠券模板")
|
||||
@PutMapping("/template")
|
||||
public Result<Object> updateTemplate(@RequestBody CouponTemplateForm form){
|
||||
public Result<Object> updateTemplate(@ApiParam(value = "优惠券模板构建表单")
|
||||
@Validated @RequestBody CouponTemplateForm form) {
|
||||
log.info("Update Coupon Template,form:{}", form);
|
||||
couponTemplateService.updateTemplate(form);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询优惠券模板详情
|
||||
* @param id 优惠券模板ID
|
||||
* @return result
|
||||
* 优惠券模板审核功能
|
||||
* 优惠券创建完成后,更高级别人员审核通过,在系统中批量生成优惠券码
|
||||
* 审核完成后的优惠券无法修改,删除
|
||||
*
|
||||
* @param id 优惠券模板id
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation(value = "优惠券模板审核")
|
||||
@GetMapping("/template/confirm")
|
||||
public Result<Object> templateConfirm(@ApiParam(value = "优惠券模板ID") @RequestParam("id") String id) {
|
||||
couponTemplateService.confirmTemplate(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取优惠券模板详情")
|
||||
@GetMapping("/template/info")
|
||||
public Result<CouponTemplateVO> getTemplateInfo(@RequestParam("id") Long id){
|
||||
log.info("Query Coupon Template Info , id:{}",id);
|
||||
CouponTemplateVO templateVO = templateBaseService.queryTemplateInfo(id);
|
||||
public Result<CouponTemplateVO> info(@ApiParam(value = "优惠券模板ID", defaultValue = "1")
|
||||
@RequestParam("id") String id) {
|
||||
CouponTemplateVO templateVO = couponTemplateService.info(id);
|
||||
return Result.success(templateVO);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "优惠券领取使用条件分页查询")
|
||||
@GetMapping("/template/coupon/page")
|
||||
public Result couponPage(@ApiParam(value = "优惠券领取使用条件分页查询") CouponPageQuery query) {
|
||||
IPage<SmsCoupon> iPage = couponService.pageQuery(query);
|
||||
return Result.success(iPage.getRecords(), iPage.getTotal());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "优惠券模板下优惠券领取详情")
|
||||
@GetMapping("/template/coupon/info")
|
||||
public Result couponInfo(@ApiParam(value = "优惠券码CODE", defaultValue = "0000000")
|
||||
@RequestParam("code") String code) {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package com.youlai.mall.sms.controller.app;
|
||||
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.web.util.BeanMapperUtils;
|
||||
import com.youlai.common.web.util.JwtUtils;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponStateEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.pojo.vo.SmsCouponVO;
|
||||
import com.youlai.mall.sms.service.ISmsCouponService;
|
||||
import com.youlai.mall.sms.service.ISmsCouponTemplateService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 用户优惠券
|
||||
* @date 2021/7/7
|
||||
*/
|
||||
@Api(tags = "优惠券(移动端接口)")
|
||||
@RestController
|
||||
@RequestMapping("/api-app/v1/coupon/")
|
||||
@Slf4j
|
||||
public class CouponController {
|
||||
|
||||
@Autowired
|
||||
private ISmsCouponService couponService;
|
||||
|
||||
@Autowired
|
||||
private ISmsCouponTemplateService couponTemplateService;
|
||||
|
||||
@ApiOperation("查看可领取优惠券模板列表")
|
||||
@GetMapping("/template")
|
||||
public Result<List<CouponTemplateVO>> findAvailableTemplate() {
|
||||
couponService.findAvailableTemplate(JwtUtils.getUserId());
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@ApiOperation("用户领取优惠券")
|
||||
@GetMapping("receive")
|
||||
public Result receive(@ApiParam(value = "优惠券模板ID")
|
||||
@RequestParam("templateId") String templateId) {
|
||||
couponService.receive(JwtUtils.getUserId(), templateId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@ApiOperation("查询用户已领取优惠券列表")
|
||||
@GetMapping("/list")
|
||||
public Result<List<SmsCouponVO>> list() {
|
||||
List<SmsCoupon> coupons = couponService.findCouponsByState(JwtUtils.getUserId(), CouponStateEnum.USABLE.getCode());
|
||||
return Result.success(BeanMapperUtils.mapList(coupons, SmsCouponVO.class));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.youlai.mall.sms.controller.app;
|
||||
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.mall.sms.executor.ExecutorManager;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@Api(tags = "优惠券核销(移动端接口)")
|
||||
@RestController
|
||||
@RequestMapping("/api-app/v1/coupon_settlement/")
|
||||
@Slf4j
|
||||
public class CouponSettlementController {
|
||||
|
||||
@Autowired
|
||||
private ExecutorManager executorManager;
|
||||
|
||||
@ApiOperation("优惠券结算")
|
||||
@PostMapping("/compute_rule")
|
||||
public Result computeRule(@ApiParam("优惠券结算请求") @RequestBody SettlementInfoVO settlement){
|
||||
SettlementInfoVO settlementInfoVO = executorManager.computeRule(settlement);
|
||||
return Result.success(settlementInfoVO);
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.youlai.mall.sms.executor;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc:规则执行器抽象类,定义通用方法
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
public class AbstractExecutor {
|
||||
|
||||
/**
|
||||
* 校验商品类型与优惠券是匹配
|
||||
* 1、这里实现的是但品类优惠券校验,多品类优惠券自行实现
|
||||
* 2、商品只需要有一个优惠券要求的商品类型进行匹配
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean isGoodsTypeSatisfy(SettlementInfoVO settlement) {
|
||||
List<String> goodsType = settlement.getGoodsInfos().stream().map(SettlementInfoVO.GoodsInfo::getType).collect(Collectors.toList());
|
||||
List<String> templateGoodsType = settlement.getCouponAndTemplateInfos().get(0).getTemplate().getRule().getUsage().getGoodsType();
|
||||
if (CollUtil.isEmpty(templateGoodsType)){
|
||||
return true;
|
||||
}
|
||||
return CollectionUtils.isNotEmpty(CollectionUtils.intersection(goodsType, templateGoodsType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理商品类型与优惠券限制不匹配情况
|
||||
* @param settlement 用户传递的结算信息
|
||||
* @param goodsSum 商品总价
|
||||
* @return 返回已经修改过的结算信息
|
||||
*/
|
||||
protected SettlementInfoVO processGoodsTypeNotSatisfy(SettlementInfoVO settlement,Long goodsSum){
|
||||
boolean isGoodsTypeSatisfy = isGoodsTypeSatisfy(settlement);
|
||||
if (!isGoodsTypeSatisfy){
|
||||
// 当商品类型不满足时,直接返回总价,并清空优惠券
|
||||
settlement.setPay(goodsSum);
|
||||
settlement.setCouponAndTemplateInfos(CollUtil.empty(List.class));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前两张优惠券是否可以够用
|
||||
* 即校验 TemplateRule 中的 weight 是否满足条件
|
||||
*
|
||||
* @param c1
|
||||
* @param c2
|
||||
* @return
|
||||
*/
|
||||
protected boolean isTemplateCanShared(SettlementInfoVO.CouponAndTemplateInfo c1, SettlementInfoVO.CouponAndTemplateInfo c2) {
|
||||
String c1Key = c1.getTemplate().getCode() + String.format("%04d", c1.getTemplate().getId());
|
||||
String c2Key = c2.getTemplate().getCode() + String.format("%04d", c2.getTemplate().getId());
|
||||
|
||||
List<String> allSharedKeysForC1 = new ArrayList<>();
|
||||
allSharedKeysForC1.add(c1Key);
|
||||
allSharedKeysForC1.addAll(JSON.parseObject(c1.getTemplate().getRule().getWeight(), List.class));
|
||||
|
||||
List<String> allSharedKeysForC2 = new ArrayList<>();
|
||||
allSharedKeysForC2.add(c2Key);
|
||||
allSharedKeysForC2.addAll(JSON.parseObject(c2.getTemplate().getRule().getWeight(), List.class));
|
||||
|
||||
return CollectionUtils.isSubCollection(Arrays.asList(c1Key, c2Key), allSharedKeysForC1)
|
||||
|| CollectionUtils.isSubCollection(Arrays.asList(c1Key, c2Key), allSharedKeysForC2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算商品总价
|
||||
* @param goodsInfos
|
||||
* @return
|
||||
*/
|
||||
protected Long goodsCostSum(List<SettlementInfoVO.GoodsInfo> goodsInfos){
|
||||
return goodsInfos.stream().mapToLong(g -> g.getCount()*g.getCount()).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小支付费用(1分钱)
|
||||
* @return
|
||||
*/
|
||||
protected Long minCost(){
|
||||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package com.youlai.mall.sms.executor;
|
||||
|
||||
import com.youlai.common.web.exception.BizException;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponCategoryEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.RuleFlagEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券结算规则执行管理器
|
||||
* @date 2021/7/13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ExecutorManager implements BeanPostProcessor {
|
||||
|
||||
/**
|
||||
* 规则执行器映射
|
||||
*/
|
||||
private static Map<RuleFlagEnum, RuleExecutor> executorIndex = new HashMap<>(RuleFlagEnum.values().length);
|
||||
|
||||
public SettlementInfoVO computeRule(SettlementInfoVO settlement){
|
||||
SettlementInfoVO result = null;
|
||||
|
||||
// 单类优惠券
|
||||
if (settlement.getCouponAndTemplateInfos().size() == 1){
|
||||
CouponCategoryEnum category = settlement.getCouponAndTemplateInfos().get(0).getTemplate().getCategory();
|
||||
switch (category){
|
||||
case MANJIAN:
|
||||
settlement = executorIndex.get(RuleFlagEnum.MANJIAN).computeRule(settlement);
|
||||
break;
|
||||
case LIJIAN:
|
||||
settlement = executorIndex.get(RuleFlagEnum.LIJIAN).computeRule(settlement);
|
||||
break;
|
||||
case ZHEKOU:
|
||||
settlement = executorIndex.get(RuleFlagEnum.ZHEKOU).computeRule(settlement);
|
||||
break;
|
||||
}
|
||||
}else {
|
||||
List<CouponCategoryEnum> categories = settlement.getCouponAndTemplateInfos().stream().map(obj -> {
|
||||
return obj.getTemplate().getCategory();
|
||||
}).collect(Collectors.toList());
|
||||
if (categories.size() > 2){
|
||||
throw new BizException("Not Support Form More Template Category");
|
||||
}
|
||||
if (categories.contains(CouponCategoryEnum.MANJIAN) && categories.contains(CouponCategoryEnum.ZHEKOU)){
|
||||
settlement = executorIndex.get(RuleFlagEnum.MANJIAN_ZHEKOU).computeRule(settlement);
|
||||
}else {
|
||||
throw new BizException("Not Support Form Other Template Category");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 在bean初始化之前去执行
|
||||
*
|
||||
* @param bean
|
||||
* @param beanName
|
||||
* @return
|
||||
* @throws BeansException
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
if (!(bean instanceof RuleExecutor)) {
|
||||
return bean;
|
||||
}
|
||||
RuleExecutor executor = (RuleExecutor) bean;
|
||||
RuleFlagEnum ruleFlag = executor.ruleConfig();
|
||||
if (executorIndex.containsKey(ruleFlag)) {
|
||||
throw new IllegalStateException("There is already an executor for rule flag: " + ruleFlag);
|
||||
}
|
||||
log.info("Load executor {} for rule flag {}.", executor.getClass(), ruleFlag);
|
||||
executorIndex.put(ruleFlag, executor);
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在bean初始化之后去执行
|
||||
*
|
||||
* @param bean
|
||||
* @param beanName
|
||||
* @return
|
||||
* @throws BeansException
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.youlai.mall.sms.executor;
|
||||
|
||||
import com.youlai.mall.sms.pojo.enums.RuleFlagEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券模板规则处理器接口
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
public interface RuleExecutor {
|
||||
|
||||
/**
|
||||
* 规则类型标记
|
||||
* @return {@RuleFlagEnum}
|
||||
*/
|
||||
RuleFlagEnum ruleConfig();
|
||||
|
||||
/**
|
||||
* 优惠券规则计算
|
||||
* @param settlement
|
||||
* @return
|
||||
*/
|
||||
SettlementInfoVO computeRule(SettlementInfoVO settlement);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.youlai.mall.sms.executor.impl;
|
||||
|
||||
import com.youlai.mall.sms.executor.AbstractExecutor;
|
||||
import com.youlai.mall.sms.executor.RuleExecutor;
|
||||
import com.youlai.mall.sms.pojo.enums.RuleFlagEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 立减优惠券结算规则执行器
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LiJianExecutor extends AbstractExecutor implements RuleExecutor {
|
||||
@Override
|
||||
public RuleFlagEnum ruleConfig() {
|
||||
return RuleFlagEnum.LIJIAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettlementInfoVO computeRule(SettlementInfoVO settlement) {
|
||||
Long goodsCostSum = goodsCostSum(settlement.getGoodsInfos());
|
||||
// 判断商品类型与优惠券是否满足条件
|
||||
SettlementInfoVO probability = processGoodsTypeNotSatisfy(settlement, goodsCostSum);
|
||||
if (probability != null) {
|
||||
log.debug("LiJian Coupon Is Not Match To GoodsType.");
|
||||
return probability;
|
||||
}
|
||||
CouponTemplateVO template = settlement.getCouponAndTemplateInfos().get(0).getTemplate();
|
||||
Long quota = template.getRule().getDiscount().getQuota();
|
||||
//计算使用优惠券之后的价格
|
||||
Long pay = goodsCostSum - quota;
|
||||
settlement.setPay(pay > minCost() ? pay : minCost());
|
||||
log.debug("Use LiJian Coupon Make Goods Cost From {} To {}",goodsCostSum,settlement.getPay());
|
||||
return settlement;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.youlai.mall.sms.executor.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.youlai.mall.sms.executor.AbstractExecutor;
|
||||
import com.youlai.mall.sms.executor.RuleExecutor;
|
||||
import com.youlai.mall.sms.pojo.enums.RuleFlagEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc:满减优惠券结算规则执行器
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ManJianExecutor extends AbstractExecutor implements RuleExecutor {
|
||||
@Override
|
||||
public RuleFlagEnum ruleConfig() {
|
||||
return RuleFlagEnum.MANJIAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettlementInfoVO computeRule(SettlementInfoVO settlement) {
|
||||
Long goodsCostSum = goodsCostSum(settlement.getGoodsInfos());
|
||||
// 判断商品类型与优惠券是否满足条件
|
||||
SettlementInfoVO probability = processGoodsTypeNotSatisfy(settlement, goodsCostSum);
|
||||
if (probability != null) {
|
||||
log.debug("Manjian Coupon Is Not Match To GoodsType.");
|
||||
return probability;
|
||||
}
|
||||
CouponTemplateVO template = settlement.getCouponAndTemplateInfos().get(0).getTemplate();
|
||||
Long base = template.getRule().getDiscount().getBase();
|
||||
Long quota = template.getRule().getDiscount().getQuota();
|
||||
if (goodsCostSum < base) {
|
||||
log.debug("Current Goods Cost Sum < Manjian Coupon Base");
|
||||
settlement.setPay(goodsCostSum);
|
||||
settlement.setCouponAndTemplateInfos(CollUtil.empty(List.class));
|
||||
return settlement;
|
||||
}
|
||||
//计算使用优惠券之后的价格
|
||||
settlement.setPay((goodsCostSum - quota) > minCost() ? (goodsCostSum - quota) : minCost());
|
||||
log.debug("Use ManJian Coupon Make Goods Cost From {} To {}",goodsCostSum,settlement.getPay());
|
||||
return settlement;
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package com.youlai.mall.sms.executor.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.youlai.mall.sms.executor.AbstractExecutor;
|
||||
import com.youlai.mall.sms.executor.RuleExecutor;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponCategoryEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.RuleFlagEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 满减 + 折扣优惠券结算规则执行器
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ManJianZheKouExecutor extends AbstractExecutor implements RuleExecutor {
|
||||
@Override
|
||||
public RuleFlagEnum ruleConfig() {
|
||||
return RuleFlagEnum.MANJIAN_ZHEKOU;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGoodsTypeSatisfy(SettlementInfoVO settlement) {
|
||||
log.debug("Check ManJian And ZheKou Is Match Or Not.");
|
||||
List<String> goodsType = settlement.getGoodsInfos().stream().map(SettlementInfoVO.GoodsInfo::getType).collect(Collectors.toList());
|
||||
List<String> templateGoodsType = new ArrayList<>();
|
||||
|
||||
for (SettlementInfoVO.CouponAndTemplateInfo couponAndTemplateInfo : settlement.getCouponAndTemplateInfos()) {
|
||||
List<String> type = couponAndTemplateInfo.getTemplate().getRule().getUsage().getGoodsType();
|
||||
templateGoodsType.addAll(type);
|
||||
}
|
||||
|
||||
// 如果想要使用多品类优惠券,则必须要所有的商品类型都包含在内,即差集为空
|
||||
return CollectionUtils.isEmpty(CollectionUtils.subtract(goodsType, templateGoodsType));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettlementInfoVO computeRule(SettlementInfoVO settlement) {
|
||||
long goodsCostSum = settlement.getGoodsInfos().stream().mapToLong(g -> g.getCount() * g.getPrice()).sum();
|
||||
SettlementInfoVO probaility = processGoodsTypeNotSatisfy(settlement, goodsCostSum);
|
||||
if (probaility != null) {
|
||||
log.debug("Manjian And ZheKou Coupon Is Not Match To GoodsType.");
|
||||
return probaility;
|
||||
}
|
||||
|
||||
SettlementInfoVO.CouponAndTemplateInfo manJian = null;
|
||||
SettlementInfoVO.CouponAndTemplateInfo zheKou = null;
|
||||
|
||||
for (SettlementInfoVO.CouponAndTemplateInfo couponAndTemplateInfo : settlement.getCouponAndTemplateInfos()) {
|
||||
|
||||
if (couponAndTemplateInfo.getTemplate().getCategory() == CouponCategoryEnum.MANJIAN) {
|
||||
manJian = couponAndTemplateInfo;
|
||||
} else {
|
||||
zheKou = couponAndTemplateInfo;
|
||||
}
|
||||
}
|
||||
|
||||
assert null != manJian;
|
||||
assert null != zheKou;
|
||||
|
||||
if (!isTemplateCanShared(manJian, zheKou)) {
|
||||
settlement.setPay(goodsCostSum);
|
||||
settlement.setCouponAndTemplateInfos(CollUtil.empty(List.class));
|
||||
return settlement;
|
||||
}
|
||||
|
||||
List<SettlementInfoVO.CouponAndTemplateInfo> ctInfos = new ArrayList<>();
|
||||
Long manJianBase = manJian.getTemplate().getRule().getDiscount().getBase();
|
||||
Long manJianQuota = manJian.getTemplate().getRule().getDiscount().getQuota();
|
||||
|
||||
Long pay = goodsCostSum;
|
||||
if (goodsCostSum >= manJianBase) {
|
||||
pay -= manJianQuota;
|
||||
ctInfos.add(manJian);
|
||||
}
|
||||
|
||||
Long zheKouBase = zheKou.getTemplate().getRule().getDiscount().getBase();
|
||||
double zheKouQuota = zheKou.getTemplate().getRule().getDiscount().getQuota();
|
||||
if (goodsCostSum >= zheKouBase) {
|
||||
pay = Long.parseLong(goodsCostSum * (zheKouQuota * 1.0 / 100) + "");
|
||||
ctInfos.add(manJian);
|
||||
}
|
||||
//计算使用优惠券之后的价格
|
||||
settlement.setPay(pay > minCost() ? pay : minCost());
|
||||
settlement.setCouponAndTemplateInfos(ctInfos);
|
||||
log.debug("Use ManJian And ZheKou Coupon Make Goods Cost From {} To {}", goodsCostSum, settlement.getPay());
|
||||
return settlement;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.youlai.mall.sms.executor.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.youlai.mall.sms.executor.AbstractExecutor;
|
||||
import com.youlai.mall.sms.executor.RuleExecutor;
|
||||
import com.youlai.mall.sms.pojo.enums.RuleFlagEnum;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.pojo.vo.SettlementInfoVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 折扣优惠券结算规则执行器
|
||||
* @date 2021/7/11
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ZheKouExecutor extends AbstractExecutor implements RuleExecutor {
|
||||
@Override
|
||||
public RuleFlagEnum ruleConfig() {
|
||||
return RuleFlagEnum.ZHEKOU;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettlementInfoVO computeRule(SettlementInfoVO settlement) {
|
||||
Long goodsCostSum = goodsCostSum(settlement.getGoodsInfos());
|
||||
// 判断商品类型与优惠券是否满足条件
|
||||
SettlementInfoVO probability = processGoodsTypeNotSatisfy(settlement, goodsCostSum);
|
||||
if (probability != null) {
|
||||
log.debug("ZheKou Coupon Is Not Match To GoodsType.");
|
||||
return probability;
|
||||
}
|
||||
|
||||
CouponTemplateVO template = settlement.getCouponAndTemplateInfos().get(0).getTemplate();
|
||||
Long base = template.getRule().getDiscount().getBase();
|
||||
double quota = template.getRule().getDiscount().getQuota();
|
||||
if (goodsCostSum < base) {
|
||||
log.debug("Current Goods Cost Sum < ZheKou Coupon Base.");
|
||||
settlement.setPay(goodsCostSum);
|
||||
settlement.setCouponAndTemplateInfos(CollUtil.empty(List.class));
|
||||
return settlement;
|
||||
}
|
||||
//计算使用优惠券之后的价格
|
||||
Long pay = Long.parseLong(goodsCostSum * (quota * 1.0 / 100) + "");
|
||||
settlement.setPay(pay > minCost() ? pay : minCost());
|
||||
|
||||
log.debug("Use ZheKou Coupon Make Goods Cost From {} To {}",goodsCostSum,settlement.getPay());
|
||||
return settlement;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.youlai.mall.sms.service;
|
||||
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券 Cache 业务接口
|
||||
* @date 2021/7/4
|
||||
*/
|
||||
public interface ICouponRedisService {
|
||||
|
||||
/**
|
||||
* 从缓存中获取用户优惠券列表
|
||||
* @param userId 用户ID
|
||||
* @param state 优惠券状态
|
||||
* @return 优惠券列表
|
||||
*/
|
||||
List<SmsCoupon> getCachedCoupons(Long userId, Integer state);
|
||||
|
||||
/**
|
||||
* 保存空的优惠券列表到缓存中
|
||||
* 目的:避免缓存穿透
|
||||
* @param userId 用户ID
|
||||
* @param states 优惠券状态列表
|
||||
*/
|
||||
void saveEmptyCouponListToCache(Long userId, List<Integer> states);
|
||||
|
||||
/**
|
||||
* 尝试从 Cache 中获取一个优惠券码
|
||||
* @param templateId 优惠券模板ID
|
||||
* @return 优惠券码
|
||||
*/
|
||||
String tryToAcquireCouponCodeFromCache(Long templateId);
|
||||
|
||||
/**
|
||||
* 向缓存中添加用户可用优惠券
|
||||
* @param userId 用户ID
|
||||
* @param coupons 优惠券列表
|
||||
* @param state 状态
|
||||
* @return
|
||||
*/
|
||||
Integer addCouponToCache(Long userId,List<SmsCoupon> coupons,Integer state);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.youlai.mall.sms.service;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc: 优惠券结算服务接口
|
||||
* @date 2021/7/7
|
||||
*/
|
||||
public interface ICouponSettlementService {
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
package com.youlai.mall.sms.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.common.base.BasePageQuery;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
import com.youlai.mall.sms.pojo.form.CouponForm;
|
||||
import com.youlai.mall.sms.pojo.query.CouponPageQuery;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.pojo.vo.SmsCouponVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author huawei
|
||||
* @desc
|
||||
@ -16,6 +18,31 @@ import com.youlai.mall.sms.pojo.vo.SmsCouponVO;
|
||||
*/
|
||||
public interface ISmsCouponService extends IService<SmsCoupon> {
|
||||
|
||||
/**
|
||||
* 查询用户可用优惠券列表
|
||||
* @param userId 用户ID
|
||||
* @return {@link CouponTemplateVO} 优惠券模板列表
|
||||
*/
|
||||
List<CouponTemplateVO> findAvailableTemplate(Long userId);
|
||||
|
||||
|
||||
/**
|
||||
* 领取优惠券
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param templateId 优惠券模板ID
|
||||
*/
|
||||
void receive(Long userId, String templateId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和优惠券状态查询优惠券列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param state 优惠券状态
|
||||
* @return {@link SmsCouponVO} 优惠券列表
|
||||
*/
|
||||
List<SmsCoupon> findCouponsByState(Long userId, Integer state);
|
||||
|
||||
/**
|
||||
* 获取优惠券详情
|
||||
*
|
||||
@ -37,10 +64,11 @@ public interface ISmsCouponService extends IService<SmsCoupon> {
|
||||
*/
|
||||
void modify(CouponForm form);
|
||||
|
||||
IPage<SmsCoupon> pageQuery(Page<SmsCoupon> page, BasePageQuery query);
|
||||
IPage<SmsCoupon> pageQuery(CouponPageQuery query);
|
||||
|
||||
/**
|
||||
* 根据已领券数量
|
||||
*
|
||||
* @param couponId
|
||||
* @return
|
||||
*/
|
||||
|
@ -1,8 +1,11 @@
|
||||
package com.youlai.mall.sms.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCouponTemplate;
|
||||
import com.youlai.mall.sms.pojo.form.CouponTemplateForm;
|
||||
import com.youlai.mall.sms.pojo.query.CouponTemplatePageQuery;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -13,6 +16,9 @@ import java.util.List;
|
||||
*/
|
||||
public interface ISmsCouponTemplateService extends IService<SmsCouponTemplate> {
|
||||
|
||||
|
||||
IPage<SmsCouponTemplate> pageQuery(CouponTemplatePageQuery query);
|
||||
|
||||
/**
|
||||
* 创建优惠券模板
|
||||
*
|
||||
@ -29,6 +35,13 @@ public interface ISmsCouponTemplateService extends IService<SmsCouponTemplate> {
|
||||
*/
|
||||
SmsCouponTemplate updateTemplate(CouponTemplateForm form);
|
||||
|
||||
|
||||
/**
|
||||
* 优惠券模板审核
|
||||
* @param id
|
||||
*/
|
||||
void confirmTemplate(String id);
|
||||
|
||||
/**
|
||||
* 查询所有可用优惠券模板列表
|
||||
*
|
||||
@ -36,7 +49,7 @@ public interface ISmsCouponTemplateService extends IService<SmsCouponTemplate> {
|
||||
* @param expired 是否过期
|
||||
* @return 优惠券模板
|
||||
*/
|
||||
List<SmsCouponTemplate> findAllUsableTemplate(boolean available, boolean expired);
|
||||
List<SmsCouponTemplate> findAllUsableTemplate(Integer available, Integer expired);
|
||||
|
||||
/**
|
||||
* 查询未过期的优惠券模板列表
|
||||
@ -46,4 +59,10 @@ public interface ISmsCouponTemplateService extends IService<SmsCouponTemplate> {
|
||||
*/
|
||||
List<SmsCouponTemplate> findAllNotExpiredTemplate(Integer expired);
|
||||
|
||||
/**
|
||||
* 查询优惠券模板详情
|
||||
* @param id 优惠券模板ID
|
||||
* @return 优惠券模板详情
|
||||
*/
|
||||
CouponTemplateVO info(String id);
|
||||
}
|
||||
|
@ -36,18 +36,16 @@ public class AsyncServiceImpl implements IAsyncService {
|
||||
public void asyncConstructCouponByTemplate(SmsCouponTemplate template) {
|
||||
Stopwatch watch = Stopwatch.createStarted();
|
||||
Set<String> couponCodes = buildCouponCode(template);
|
||||
String couponCodeKey = String.format("%s%s", RedisConstants.SMS_COUPON_TEMPLATE_CODE_KEY, String.valueOf(template.getId()));
|
||||
String couponCodeKey = String.format("%s%s", RedisConstants.SMS_COUPON_TEMPLATE_CODE_KEY, template.getId());
|
||||
redisUtils.lSet(couponCodeKey, couponCodes);
|
||||
log.info("Push CouponCode To Redis, Coupon Template " +
|
||||
"ID:{}, Name:{}, TOTAL:{}", template.getId(), template.getName(), template.getTotal());
|
||||
template.setAvailable(1);
|
||||
couponTemplateMapper.updateById(template);
|
||||
|
||||
watch.stop();
|
||||
log.info("Construct CouponCode By Coupon Template Use:{}ms", watch.elapsed(TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构造优惠券码
|
||||
* 优惠券码对应于每一张优惠券,一共18位
|
||||
@ -62,7 +60,7 @@ public class AsyncServiceImpl implements IAsyncService {
|
||||
|
||||
Stopwatch watch = Stopwatch.createStarted();
|
||||
Set<String> result = new HashSet<>(template.getTotal());
|
||||
String prefix4 = template.getProductLine().getCode() + template.getCategory().getCode();
|
||||
String prefix4 = template.getCategory().getCode();
|
||||
|
||||
String date = FastDateFormat.getInstance("yyMMdd").format(new Date());
|
||||
for (Integer i = 0; i < template.getTotal(); i++) {
|
||||
|
@ -0,0 +1,204 @@
|
||||
package com.youlai.mall.sms.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.youlai.common.web.exception.BizException;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponStateEnum;
|
||||
import com.youlai.mall.sms.service.ICouponRedisService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.SessionCallback;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.youlai.common.constant.RedisConstants.*;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc:优惠券 Redis 相关操作服务接口实现
|
||||
* @date 2021/7/4
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CouponRedisServiceImpl implements ICouponRedisService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
@Override
|
||||
public List<SmsCoupon> getCachedCoupons(Long userId, Integer state) {
|
||||
log.info("Get Coupons From Cache, User:{}, State:{}", userId, state);
|
||||
String redisKey = state2RedisKey(userId, state);
|
||||
List<String> couponCaches = (List<String>) redisTemplate.opsForHash()
|
||||
.values(redisKey)
|
||||
.stream().map(obj -> Objects.toString(obj, null))
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(couponCaches)) {
|
||||
// 在缓存中放一个无效的优惠券,防止缓存穿透
|
||||
saveEmptyCouponListToCache(userId, Collections.singletonList(state));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return couponCaches.stream()
|
||||
.map(obj -> JSON.parseObject(obj, SmsCoupon.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveEmptyCouponListToCache(Long userId, List<Integer> states) {
|
||||
log.info("Save Empty Coupon List To Cache For User:{}, State:{}", userId, JSON.toJSONString(states));
|
||||
Map<String, String> invalidCouponMap = new HashMap<>();
|
||||
// 构造无效的coupon
|
||||
invalidCouponMap.put("-1", JSON.toJSONString(new SmsCoupon()));
|
||||
|
||||
SessionCallback<Object> sessionCallback = new SessionCallback<Object>() {
|
||||
@Override
|
||||
public Object execute(RedisOperations operations) throws DataAccessException {
|
||||
for (Integer state : states) {
|
||||
String redisKey = state2RedisKey(userId, state);
|
||||
operations.opsForHash().putAll(redisKey, invalidCouponMap);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
log.info("Pipeline Exe Result: {}", redisTemplate.executePipelined(sessionCallback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tryToAcquireCouponCodeFromCache(Long templateId) {
|
||||
String redisKey = String.format("%s%s", SMS_COUPON_TEMPLATE_CODE_KEY, templateId);
|
||||
String couponCode = (String) redisTemplate.opsForList().leftPop(redisKey);
|
||||
log.info("Acquire Coupon Code, {} {} {}", templateId, redisKey, couponCode);
|
||||
return couponCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer addCouponToCache(Long userId, List<SmsCoupon> coupons, Integer state) {
|
||||
log.info("Add Coupon To Cache: {}, {}, {}", userId, coupons, state);
|
||||
int result = -1;
|
||||
|
||||
CouponStateEnum couponStateEnum = CouponStateEnum.of(state);
|
||||
switch (couponStateEnum) {
|
||||
case USABLE:
|
||||
result = addCouponToCacheForUsable(userId, coupons);
|
||||
break;
|
||||
case USED:
|
||||
result = addCouponToCacheForUsed(userId, coupons);
|
||||
break;
|
||||
case EXPIRED:
|
||||
result = addCouponToCacheForExpired(userId, coupons);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Integer addCouponToCacheForUsable(Long userId, List<SmsCoupon> coupons) {
|
||||
|
||||
log.debug("Add Coupon To Cache For Usable.");
|
||||
Map<String, String> needCacheForUsable = new HashMap<>(coupons.size());
|
||||
coupons.forEach(coupon -> {
|
||||
needCacheForUsable.put(coupon.getId().toString(), JSON.toJSONString(coupon));
|
||||
});
|
||||
String redisKey = state2RedisKey(userId, CouponStateEnum.USABLE.getCode());
|
||||
redisTemplate.opsForHash().putAll(redisKey, needCacheForUsable);
|
||||
log.info("Add Coupons To Cache For Usable: {}, {}, {}", userId, redisKey, needCacheForUsable.size());
|
||||
|
||||
redisTemplate.expire(redisKey, getRandomExpirationTime(1, 2), TimeUnit.SECONDS);
|
||||
return needCacheForUsable.size();
|
||||
}
|
||||
|
||||
private Integer addCouponToCacheForUsed(Long userId, List<SmsCoupon> coupons) {
|
||||
|
||||
log.debug("Add Coupon To Cache For Used.");
|
||||
Map<String, String> needCacheForUsed = new HashMap<>();
|
||||
|
||||
String redisKeyForUsable = state2RedisKey(userId, CouponStateEnum.USABLE.getCode());
|
||||
String redisKeyForUsed = state2RedisKey(userId, CouponStateEnum.USED.getCode());
|
||||
// TODO 待开发
|
||||
return coupons.size();
|
||||
}
|
||||
|
||||
private int addCouponToCacheForExpired(Long userId, List<SmsCoupon> coupons) {
|
||||
log.debug("Add Coupon To Cache For Expired.");
|
||||
Map<String, String> needCacheForExpired = new HashMap<>(coupons.size());
|
||||
String redisKeyForUsable = state2RedisKey(userId, CouponStateEnum.USABLE.getCode());
|
||||
String redisKeyForExpired = state2RedisKey(userId, CouponStateEnum.EXPIRED.getCode());
|
||||
// 从缓存中获取当前可用优惠券列表和无效优惠券列表
|
||||
List<SmsCoupon> curUsableCoupons = getCachedCoupons(userId, CouponStateEnum.USABLE.getCode());
|
||||
List<SmsCoupon> curExpiredCoupons = getCachedCoupons(userId, CouponStateEnum.EXPIRED.getCode());
|
||||
|
||||
assert curUsableCoupons.size() > coupons.size();
|
||||
coupons.forEach(coupon -> {
|
||||
needCacheForExpired.put(coupon.getId().toString(), JSON.toJSONString(coupon));
|
||||
});
|
||||
|
||||
List<Long> curUsableIds = curUsableCoupons.stream().map(SmsCoupon::getId).collect(Collectors.toList());
|
||||
List<Long> paramIds = coupons.stream().map(SmsCoupon::getId).collect(Collectors.toList());
|
||||
if (!CollectionUtils.isSubCollection(curUsableIds, paramIds)) {
|
||||
log.error("CurCoupons Is Not Equal To Cache: {}, {}, {}", userId, JSON.toJSONString(curUsableIds), JSON.toJSONString(paramIds));
|
||||
throw new BizException("CurCoupon Is Not Equal To Cache.");
|
||||
}
|
||||
List<String> needCleanKey = paramIds.stream().map(String::valueOf).collect(Collectors.toList());
|
||||
|
||||
SessionCallback<Objects> sessionCallback = new SessionCallback<Objects>() {
|
||||
@Override
|
||||
public Objects execute(RedisOperations operations) throws DataAccessException {
|
||||
|
||||
// 1、已过期优惠券缓存
|
||||
operations.opsForHash().putAll(redisKeyForExpired, needCacheForExpired);
|
||||
// 2、可用的优惠券需要清理
|
||||
operations.opsForHash().delete(redisKeyForUsable, needCleanKey);
|
||||
//3、重置过期时间
|
||||
operations.expire(redisKeyForUsable, getRandomExpirationTime(1, 2), TimeUnit.SECONDS);
|
||||
operations.expire(redisKeyForExpired, getRandomExpirationTime(1, 2), TimeUnit.SECONDS);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
log.info("Pipeline Exe Result: {}", redisTemplate.executePipelined(sessionCallback));
|
||||
|
||||
return coupons.size();
|
||||
}
|
||||
|
||||
private String state2RedisKey(Long userId, Integer state) {
|
||||
String redisKey = null;
|
||||
CouponStateEnum couponStateEnum = CouponStateEnum.of(state);
|
||||
switch (couponStateEnum) {
|
||||
case USABLE:
|
||||
redisKey = String.format("%s%s", SMS_USER_COUPON_USABLE_KEY, userId);
|
||||
break;
|
||||
case USED:
|
||||
redisKey = String.format("%s%s", SMS_USER_COUPON_USED_KEY, userId);
|
||||
break;
|
||||
case EXPIRED:
|
||||
redisKey = String.format("%s%s", SMS_USER_COUPON_EXPIRED_KEY, userId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return redisKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个随机的过期时间
|
||||
* 目的:解决Key在同一时间失效
|
||||
*
|
||||
* @param min 最小的小时数
|
||||
* @param max 最大的小时数
|
||||
* @return 返回 [min,max] 之间的随机秒数
|
||||
*/
|
||||
private Long getRandomExpirationTime(Integer min, Integer max) {
|
||||
return RandomUtils
|
||||
.nextLong(min * 60 * 60, max * 60 * 60);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.youlai.mall.sms.service.impl;
|
||||
|
||||
import com.youlai.mall.sms.service.ICouponSettlementService;
|
||||
|
||||
/**
|
||||
* @author xinyi
|
||||
* @desc
|
||||
* @date 2021/7/7
|
||||
*/
|
||||
public class CouponSettlementServiceImpl implements ICouponSettlementService {
|
||||
}
|
@ -1,19 +1,35 @@
|
||||
package com.youlai.mall.sms.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.common.base.BasePageQuery;
|
||||
import com.youlai.common.web.util.BeanMapperUtils;
|
||||
import com.youlai.mall.sms.mapper.SmsCouponMapper;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCoupon;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCouponTemplate;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponStateEnum;
|
||||
import com.youlai.mall.sms.pojo.form.CouponForm;
|
||||
import com.youlai.mall.sms.pojo.query.CouponPageQuery;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponClassify;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.pojo.vo.SmsCouponVO;
|
||||
import com.youlai.mall.sms.service.ICouponRedisService;
|
||||
import com.youlai.mall.sms.service.ISmsCouponService;
|
||||
import com.youlai.mall.sms.service.ISmsCouponTemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author huawei
|
||||
* @desc 优惠券服务业务实现类
|
||||
@ -23,6 +39,88 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SmsCouponServiceImpl extends ServiceImpl<SmsCouponMapper, SmsCoupon> implements ISmsCouponService {
|
||||
|
||||
@Autowired
|
||||
private ICouponRedisService couponRedisService;
|
||||
|
||||
@Autowired
|
||||
private ISmsCouponTemplateService couponTemplateService;
|
||||
|
||||
@Override
|
||||
public List<CouponTemplateVO> findAvailableTemplate(Long userId) {
|
||||
|
||||
// 1、查询所有可用优惠券模板
|
||||
List<SmsCouponTemplate> templates = couponTemplateService.findAllUsableTemplate(1, 1);
|
||||
Date nowTime = new Date();
|
||||
templates = templates.stream()
|
||||
.filter(template -> template.getRule().getExpiration().getDeadline() < nowTime.getTime())
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(templates)) {
|
||||
log.info("All Usable Template is Empty.");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
log.info("All Usable Coupon Template Size={}", templates.size());
|
||||
|
||||
// 2、查询用户已领取优惠券数量
|
||||
List<SmsCoupon> coupons = findCouponsByState(userId, 0);
|
||||
Map<Long, List<SmsCoupon>> couponMap = coupons.stream().collect(Collectors.groupingBy(SmsCoupon::getTemplateId));
|
||||
log.info("All User Receive Coupon Template Size={}", couponMap.size());
|
||||
|
||||
// 3、过滤用户已领取优惠券模板
|
||||
templates = templates.stream().filter(template -> {
|
||||
List<SmsCoupon> receivedCoupons = couponMap.getOrDefault(template.getId(), new ArrayList<>());
|
||||
return template.getRule().getLimitation() > receivedCoupons.size();
|
||||
}).collect(Collectors.toList());
|
||||
log.info("Find All Available Template Size={}", templates.size());
|
||||
|
||||
return BeanMapperUtils.mapList(templates, CouponTemplateVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Long userId, String templateId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SmsCoupon> findCouponsByState(Long userId, Integer state) {
|
||||
log.info("根据查询优惠券列表,UserId={}, State={}", userId, state);
|
||||
List<SmsCoupon> cachedCoupons = couponRedisService.getCachedCoupons(userId, state);
|
||||
List<SmsCoupon> targetCoupon = new ArrayList<>();
|
||||
if (!CollUtil.isEmpty(cachedCoupons)) {
|
||||
// 如果缓存中有数据直接取缓存中的数据
|
||||
targetCoupon = cachedCoupons;
|
||||
} else {
|
||||
// 如果缓存中没有数据,从数据库取数据
|
||||
QueryWrapper<SmsCoupon> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId);
|
||||
queryWrapper.eq("state", state);
|
||||
List<SmsCoupon> dbCoupon = this.list(queryWrapper);
|
||||
if (CollUtil.isEmpty(dbCoupon)) {
|
||||
log.info("Current User Do Not Has Coupon: {} {}", userId, state);
|
||||
return dbCoupon;
|
||||
}
|
||||
List<SmsCouponTemplate> templates = couponTemplateService.listByIds(dbCoupon.stream().map(SmsCoupon::getTemplateId).collect(Collectors.toList()));
|
||||
Map<Long, SmsCouponTemplate> templateMap = templates.stream().collect(Collectors.toMap(SmsCouponTemplate::getId, Function.identity()));
|
||||
dbCoupon.forEach(db -> db.setTemplate(templateMap.get(db.getTemplateId())));
|
||||
targetCoupon = dbCoupon;
|
||||
couponRedisService.addCouponToCache(userId, targetCoupon, state);
|
||||
log.info("Add User Coupons To Cache, {} {}", userId, state);
|
||||
}
|
||||
|
||||
// 将无效的优惠券剔除
|
||||
targetCoupon = targetCoupon.stream().filter(c -> c.getState().getCode() != -1).collect(Collectors.toList());
|
||||
if (CouponStateEnum.of(state) == CouponStateEnum.USABLE) {
|
||||
// 如果当前获取的是可用优惠券,还需要对已过期优惠券进行延时处理
|
||||
CouponClassify classify = CouponClassify.classify(targetCoupon);
|
||||
if (!CollUtil.isEmpty(classify.getExpired())) {
|
||||
log.info("Add User Expired Coupons To Cache, {} {}", userId, state);
|
||||
couponRedisService.addCouponToCache(userId, classify.getExpired(), CouponStateEnum.EXPIRED.getCode());
|
||||
this.updateBatchById(classify.getExpired());
|
||||
}
|
||||
}
|
||||
return targetCoupon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsCouponVO detail(String couponId) {
|
||||
log.info("根据优惠券ID获取优惠券详情,couponId={}", couponId);
|
||||
@ -48,10 +146,15 @@ public class SmsCouponServiceImpl extends ServiceImpl<SmsCouponMapper, SmsCoupon
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<SmsCoupon> pageQuery(Page<SmsCoupon> page, BasePageQuery query) {
|
||||
public IPage<SmsCoupon> pageQuery(CouponPageQuery query) {
|
||||
log.info("Page Query Coupons,TemplateID={},query={}", query.getTemplateId(), query);
|
||||
Page<SmsCoupon> page = new Page<>(query.getPageNum(), query.getPageSize());
|
||||
QueryWrapper<SmsCoupon> queryWrapper = new QueryWrapper<>();
|
||||
IPage<SmsCoupon> iPage = this.page(page,queryWrapper);
|
||||
return iPage;
|
||||
queryWrapper.eq("template_id", query.getTemplateId());
|
||||
queryWrapper.eq(StrUtil.isNotBlank(query.getCouponCode()), "coupon_code", query.getCouponCode());
|
||||
queryWrapper.eq(query.getState() != null, "state", query.getState());
|
||||
queryWrapper.orderByDesc("gmt_create");
|
||||
return this.page(page, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,16 +1,25 @@
|
||||
package com.youlai.mall.sms.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.common.web.exception.BizException;
|
||||
import com.youlai.common.web.util.BeanMapperUtils;
|
||||
import com.youlai.mall.sms.mapper.SmsCouponTemplateMapper;
|
||||
import com.youlai.mall.sms.pojo.domain.SmsCouponTemplate;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponCategoryEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.CouponTemplateStateEnum;
|
||||
import com.youlai.mall.sms.pojo.enums.DistributeTargetEnum;
|
||||
import com.youlai.mall.sms.pojo.form.CouponTemplateForm;
|
||||
import com.youlai.mall.sms.pojo.query.CouponTemplatePageQuery;
|
||||
import com.youlai.mall.sms.pojo.vo.CouponTemplateVO;
|
||||
import com.youlai.mall.sms.service.IAsyncService;
|
||||
import com.youlai.mall.sms.service.ISmsCouponTemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -26,16 +35,27 @@ import java.util.List;
|
||||
public class SmsCouponTemplateServiceImpl extends ServiceImpl<SmsCouponTemplateMapper, SmsCouponTemplate>
|
||||
implements ISmsCouponTemplateService {
|
||||
|
||||
|
||||
@Autowired
|
||||
private IAsyncService asyncService;
|
||||
|
||||
@Override
|
||||
public IPage<SmsCouponTemplate> pageQuery(CouponTemplatePageQuery query) {
|
||||
Page<SmsCouponTemplate> page = new Page<>(query.getPageNum(), query.getPageSize());
|
||||
QueryWrapper<SmsCouponTemplate> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.like(StrUtil.isNotBlank(query.getName()), "name", query.getName());
|
||||
queryWrapper.eq(query.getCategory() != null, "category", query.getCategory());
|
||||
queryWrapper.eq(query.getState() != null, "state", query.getState());
|
||||
queryWrapper.eq(query.getTarget() != null, "target", query.getTarget());
|
||||
queryWrapper.gt(query.getEndTime() != null, "gmt_create", query.getEndTime());
|
||||
queryWrapper.gt(query.getStartTime() != null, "gmt_create", query.getStartTime());
|
||||
return this.page(page, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsCouponTemplate createTemplate(CouponTemplateForm form) {
|
||||
log.info("Create Coupon Template , form={}", form);
|
||||
// 1、form 表单参数校验
|
||||
// 2、不允许出现同名的模板
|
||||
if (null == findByCouponTemplateName(form.getName())) {
|
||||
if (null != findByCouponTemplateName(form.getName())) {
|
||||
throw new BizException("Coupon Template Name Exist");
|
||||
}
|
||||
|
||||
@ -43,19 +63,55 @@ public class SmsCouponTemplateServiceImpl extends ServiceImpl<SmsCouponTemplateM
|
||||
SmsCouponTemplate template = formToTemplate(form);
|
||||
this.save(template);
|
||||
|
||||
// 根据优惠券模板异步生成优惠券码
|
||||
asyncService.asyncConstructCouponByTemplate(template);
|
||||
return template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsCouponTemplate updateTemplate(CouponTemplateForm form) {
|
||||
// TODO 开发中。。。
|
||||
return null;
|
||||
log.info("Update Coupon Template, ID={}, form={}", form.getId(), form);
|
||||
// 1、判断优惠券的名称有没有变化
|
||||
SmsCouponTemplate template = findByTemplateId(form.getId());
|
||||
if (CouponTemplateStateEnum.INIT != template.getState()) {
|
||||
throw new BizException("Coupon Template State Not Init");
|
||||
}
|
||||
if (!StringUtils.equals(template.getName(), form.getName())) {
|
||||
// 如果form提交的名称不一致,需要判断名称是否重复
|
||||
if (null != findByCouponTemplateName(form.getName())) {
|
||||
throw new BizException("Coupon Template Name Exist");
|
||||
}
|
||||
}
|
||||
|
||||
// 2、将修改后的数据,覆盖原数据
|
||||
BeanMapperUtils.copy(form, template);
|
||||
this.updateById(template);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SmsCouponTemplate> findAllUsableTemplate(boolean available, boolean expired) {
|
||||
public void confirmTemplate(String id) {
|
||||
// 1、校验优惠券状态
|
||||
SmsCouponTemplate template = findByTemplateId(id);
|
||||
if (CouponTemplateStateEnum.INIT != template.getState()) {
|
||||
log.info("Finish Confirm Coupon Template State Not Init, TemplateID={}.", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2、修改优惠券模板状态,生成优惠券模板编码
|
||||
template.setState(CouponTemplateStateEnum.USED);
|
||||
template.setCode(template.getCategory() +
|
||||
DateUtil.today().replace("-", "") +
|
||||
template.getId());
|
||||
this.save(template);
|
||||
|
||||
// 3、根据优惠券模板异步生成优惠券码,放入Redis 缓存中,等待用户领取
|
||||
asyncService.asyncConstructCouponByTemplate(template);
|
||||
log.info("Finish Confirm Coupon Template, TemplateID={}.", id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<SmsCouponTemplate> findAllUsableTemplate(Integer available, Integer expired) {
|
||||
QueryWrapper<SmsCouponTemplate> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("available", available)
|
||||
.eq("expired", expired);
|
||||
@ -69,6 +125,26 @@ public class SmsCouponTemplateServiceImpl extends ServiceImpl<SmsCouponTemplateM
|
||||
return this.list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CouponTemplateVO info(String id) {
|
||||
SmsCouponTemplate template = findByTemplateId(id);
|
||||
return BeanMapperUtils.map(template,CouponTemplateVO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找优惠券模板
|
||||
*
|
||||
* @param id 优惠券模板ID
|
||||
* @return 优惠券模板
|
||||
*/
|
||||
private SmsCouponTemplate findByTemplateId(String id) {
|
||||
SmsCouponTemplate template = this.getById(id);
|
||||
if (template == null) {
|
||||
throw new BizException("Template Not Exist, ID=" + id);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板名称查询实体类
|
||||
*
|
||||
@ -89,7 +165,7 @@ public class SmsCouponTemplateServiceImpl extends ServiceImpl<SmsCouponTemplateM
|
||||
template.setTotal(form.getTotal());
|
||||
template.setTarget(DistributeTargetEnum.of(form.getTarget()));
|
||||
template.setRule(form.getRule());
|
||||
template.setCode("");
|
||||
template.setState(CouponTemplateStateEnum.INIT);
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
8
pom.xml
8
pom.xml
@ -51,6 +51,7 @@
|
||||
<ip2region.version>1.7.2</ip2region.version>
|
||||
<dozer.version>6.2.0</dozer.version>
|
||||
<swagger.version>1.6.2</swagger.version>
|
||||
<fastjson.version>1.2.73</fastjson.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -209,6 +210,13 @@
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
|
@ -42,6 +42,12 @@
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
Loading…
Reference in New Issue
Block a user