需求介绍
-
优惠券业务需求介绍
- 新用户注册-发放后端配置的新人优惠券
- 用户可以主动领取优惠券
- 下单可以选择对应的优惠券抵扣
- 支持满减优惠券-无门槛优惠券两种
- 多种元数据配置
- 类型:无门槛、满减等
- 每人领劵次数限制
- 发券总量控制
- 优惠券开始时间和结束时间
- 优惠券状态配置
-
核心知识:
- 高并发下扣减劵库存
- 超发
- 单人超领取
- 高并发下扣减劵库存
-
原生分布式锁+redisson框架分布锁使用
- 分布式锁+最佳实践
-
数据库表介绍
#优惠券表
CREATE TABLE `coupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`category` varchar(11) DEFAULT NULL COMMENT '优惠卷类型[NEW_USER注册赠券,TASK任务卷,PROMOTION促销劵]',
`publish` varchar(11) DEFAULT NULL COMMENT '发布状态, PUBLISH发布,DRAFT草稿,OFFLINE下线',
`coupon_img` varchar(524) DEFAULT NULL COMMENT '优惠券图片',
`coupon_title` varchar(128) DEFAULT NULL COMMENT '优惠券标题',
`price` decimal(16,2) DEFAULT NULL COMMENT '抵扣价格',
`user_limit` int(11) DEFAULT NULL COMMENT '每人限制张数',
`start_time` datetime DEFAULT NULL COMMENT '优惠券开始有效时间',
`end_time` datetime DEFAULT NULL COMMENT '优惠券失效时间',
`publish_count` int(11) DEFAULT NULL COMMENT '优惠券总量',
`stock` int(11) DEFAULT '0' COMMENT '库存',
`create_time` datetime DEFAULT NULL,
`condition_price` decimal(16,2) DEFAULT NULL COMMENT '满多少才可以使用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4;
#优惠券领劵记录
CREATE TABLE `coupon_record` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`coupon_id` bigint(11) DEFAULT NULL COMMENT '优惠券id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间获得时间',
`use_state` varchar(32) DEFAULT NULL COMMENT '使用状态 可用 NEW,已使用USED,过期 EXPIRED;',
`user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
`user_name` varchar(128) DEFAULT NULL COMMENT '用户昵称',
`coupon_title` varchar(128) DEFAULT NULL COMMENT '优惠券标题',
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
`order_id` bigint(11) DEFAULT NULL COMMENT '订单id',
`price` decimal(16,2) DEFAULT NULL COMMENT '抵扣价格',
`condition_price` decimal(16,2) DEFAULT NULL COMMENT '满多少才可以使用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=141 DEFAULT CHARSET=utf8mb4;
分页查询优惠券
思路:查询为发布状态并且是促销优惠券类型的优惠券信息
@Override
public Map<String, Object> pageCouponActivity(int page, int size) {
Page<CouponDO> pageInfo = new Page<>(page,size);
IPage<CouponDO> couponDOIPage = couponMapper.selectPage(pageInfo, new QueryWrapper<CouponDO>()
.eq("publish",CouponPublishEnum.PUBLISH)
.eq("category", CouponCategoryEnum.PROMOTION)
.orderByDesc("create_time"));
Map<String,Object> pageMap = new HashMap<>(3);
//总条数
pageMap.put("total_record", couponDOIPage.getTotal());
//总页数
pageMap.put("total_page",couponDOIPage.getPages());
pageMap.put("current_data",couponDOIPage.getRecords().stream().map(obj->beanProcess(obj)).collect(Collectors.toList()));
return pageMap;
}
private CouponVO beanProcess(CouponDO couponDO) {
CouponVO couponVO = new CouponVO();
BeanUtils.copyProperties(couponDO,couponVO);
return couponVO;
}
问题:HashMap初始化时为什么建议使用HashMap(int initialCapacity)
领取优惠券
思路:
* 领劵接口
* 1、获取优惠券是否存在
* 2、校验优惠券是否可以领取:时间、库存、超过限制
* 3、扣减库存
* 4、保存领劵记录
@Override
public JsonData addCoupon(long couponId, CouponCategoryEnum category) {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
CouponDO couponDO = couponMapper.selectOne(new QueryWrapper<CouponDO>()
.eq("id",couponId)
.eq("category",category.name()));
//优惠券是否可以领取
this.checkCoupon(couponDO,loginUser.getId());
//构建领劵记录
CouponRecordDO couponRecordDO = new CouponRecordDO();
BeanUtils.copyProperties(couponDO,couponRecordDO);
couponRecordDO.setCreateTime(new Date());
couponRecordDO.setUseState(CouponStateEnum.NEW.name());
couponRecordDO.setUserId(loginUser.getId());
couponRecordDO.setUserName(loginUser.getName());
couponRecordDO.setCouponId(couponId);
couponRecordDO.setId(null);
//扣减库存 TODO
int rows = 1; //couponMapper.reduceStock(couponId);
if(rows==1){
//库存扣减成功才保存记录
couponRecordMapper.insert(couponRecordDO);
}else {
log.warn("发放优惠券失败:{},用户:{}",couponDO,loginUser);
throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
}
return JsonData.buildSuccess();
}
private void checkCoupon(CouponDO couponDO, Long userId) {
if(couponDO==null){
throw new BizException(BizCodeEnum.COUPON_NO_EXITS);
}
//库存是否足够
if(couponDO.getStock()<=0){
throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
}
//判断是否是否发布状态
if(!couponDO.getPublish().equals(CouponPublishEnum.PUBLISH.name())){
throw new BizException(BizCodeEnum.COUPON_GET_FAIL);
}
//是否在领取时间范围
long time = CommonUtil.getCurrentTimestamp();
long start = couponDO.getStartTime().getTime();
long end = couponDO.getEndTime().getTime();
if(time<start || time>end){
throw new BizException(BizCodeEnum.COUPON_OUT_OF_TIME);
}
//用户是否超过限制
int recordNum = couponRecordMapper.selectCount(new QueryWrapper<CouponRecordDO>()
.eq("coupon_id",couponDO.getId())
.eq("user_id",userId));
if(recordNum >= couponDO.getUserLimit()){
throw new BizException(BizCodeEnum.COUPON_OUT_OF_LIMIT);
}
}
标签:COMMENT,优惠券,领取,DEFAULT,new,NULL,id
From: https://www.cnblogs.com/youngleesin/p/16808170.html