feat:优惠券基本业务代码开发

This commit is contained in:
dongtiandexue 2021-07-13 23:17:07 +08:00
parent a677fa8749
commit 74b3d107b3
33 changed files with 1566 additions and 211 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"));
}
}

View File

@ -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"));
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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){
log.info("Update Coupon Templateform:{}",form);
public Result<Object> updateTemplate(@ApiParam(value = "优惠券模板构建表单")
@Validated @RequestBody CouponTemplateForm form) {
log.info("Update Coupon Templateform:{}", 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();
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -0,0 +1,9 @@
package com.youlai.mall.sms.service;
/**
* @author xinyi
* @desc: 优惠券结算服务接口
* @date 2021/7/7
*/
public interface ICouponSettlementService {
}

View File

@ -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
*/

View File

@ -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);
}

View File

@ -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++) {

View File

@ -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);
}
}

View File

@ -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 {
}

View File

@ -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 CouponsTemplateID={}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

View File

@ -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);
// 1form 表单参数校验
// 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;
}
}

View File

@ -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>

View File

@ -42,6 +42,12 @@
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>