优惠券系统-第三章介绍
本文主要设计一个基于送券,送积分等的活动中心。
活动中心设计
活动中心主要是有各种活动,比如双十一活动,可能参加一个活动会送多个优惠券,某一个活动送大礼包还可能包含了积分(类似京豆),所有活动与优惠券(或者积分)属于一对多的关系。领券中心与活动中心有一点区别就是,领券中心每次只有一个券,活动中心可以是一个大礼包。如下面图所示,某电商618的h5活动页,一次送618礼包(多张券)。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9UEVPd3Z65UeRRVT3V1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxMjN1QTN1UTM0EDOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
在这里我们只讲活动心中的处理,领券中心的单个券处理比领活动礼包类似。我们需要两个表,一个是活动表activity_info,还需要一个活动与优惠券关联表activity_coupon,表示参与活动送哪一类券,需要参与活动送其他(京豆),直接加关联表即可。activity_code为活动的唯一标识code。
CREATE TABLE `activity` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`activity_code` varchar(50) NOT NULL COMMENT '活动码',
`activity_name` varchar(255) DEFAULT NULL COMMENT '活动名称',
`activity_desc` varchar(255) DEFAULT NULL COMMENT '活动描述',
`activity_status` int(11) NOT NULL DEFAULT '1' COMMENT '活动状态(上下架)1可用,0禁用',
`start_time` datetime DEFAULT NULL COMMENT '活动开始时间',
`end_time` datetime DEFAULT NULL COMMENT '活动结束时间',
`image_url` varchar(255) DEFAULT NULL COMMENT '列表图片',
`h5_url` varchar(255) DEFAULT NULL COMMENT 'h5跳转链接',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_activity_code` (`activity_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='活动表';
CREATE TABLE `activity_coupon` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`activity_code` varchar(50) NOT NULL COMMENT '活动code',
`coupon_act_sn` varchar(50) NOT NULL COMMENT '优惠券模板码',
`coupon_num` int(11) NOT NULL DEFAULT '1' COMMENT '优惠券数量',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_sendSn_couponSn` (`activity_code`,`coupon_act_sn`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='活动赠券配置表';
这里注意activity_coupon里面的coupon_act_sn字段是第一章里面优惠券主表coupon_act的act_sn,为某一类券的标识,coupon_num为这一类券在该活动中送多少张。
流程设计
当某一个活动发布时,会有一波领券高峰,领券需要通过MQ来异步处理,并且某一个用户参加活动的标志也要放在缓存中,同个用户的请求就会被缓存拦截下,缓存的失效时间,就可以设置为活动的结束时间。需要注意的是,出现了用户参与活动,但是没领到券的情况需要补救,这种情况是没有及时追加优惠券,导致券不够用,或者其他系统错误导致。
编码实现
有一些细节在流程图中未给出,比如第一步的参加活动需要用分布式锁控制一个用户的请求,不可以同时进入领券代码中,防止由于同一个用户并发参加两次活动。代码如下
//参加618活动APi接口
@RequestMapping(value = "/joinAct", method = RequestMethod.POST)
@ResponseBody
public ResponseDto joinAct(HttpServletRequest request) {
//验证是否登陆
Header header = getHeader(request);
if (header.getUserId() == null) {
return new ResponseDto("用户信息异常");
}
//取出用户id,用户验证登陆属于非通用代码,根据你的业务来
String userId = header.getUserId();
//验证是否参加过活动的key
String actKey = "618" + userId;
//防止同一用户并发的缓存key
String cacheKey = "CACHE_KEY" + userId;
try {
//判断同一个用户的并发,如果已经有请求进入,直接返回,同时设置超时60秒,防止死锁
long cacheResult = redisOperations.setnx(cacheKey,"1");
if (cacheResult != 1) {
return new ResponseDto("领券中,请稍后重试");
}
redisOperations.expire(cacheKey,60);
//判断是否参加过活动,参加过活动直接返回,并设置指定错误码
String result = redisOperations.get(actKey);
if (!Check.NuNStr(result)) {
ResponseDto responseDto = new ResponseDto("您已领取过优惠券");
responseDto.getMsg().setCode(PromotionErrorCode.ALREADY_CODE);
return responseDto;
}
//送券,performSendAct方法中发送MQ,MQ中包含用户id与活动code
sendActRequest.setPatientId(userId);
DataTransferObject<Void> sendDto = sendActivityService.performSendAct(sendActRequest);
if (sendDto.checkSuccess()) {
//送券成功,设置活动标志,这里的ACT_TIME是根据活动结束时间计算出来的,代码未给出
redisOperations.set(actKey,"1");
redisOperations.expire(actKey,ACT_TIME);
//送券成功,写入用户参加某活动的记录
UserActivityRecordEntity userActivityRecordEntity = new UserActivityRecordEntity();
userActivityRecordEntity.setBizCode("618"));
userActivityRecordEntity.setUserId(userId);
userActivityRecordService.saveUserActivityRecord(userActivityRecordEntity);
}
return new ResponseDto();
} catch (Exception e) {
LogUtil.error(LOGGER,"用户{},参与618领券异常,{}",userId,e);
return new ResponseDto("领券异常");
} finally {
//请求结束,删除防止并发的key
redisOperations.del(cacheKey);
}
}
以上代码中的这个方法 sendActivityService.performSendAct(sendActRequest) 是发送MQ的方法,具体代码未给出,读者能够理解整个流程的逻辑即可,MQ的代码,读者可以阅读其他资料。
活动相关介绍完毕后,第四章将会介绍优惠券的下单使用。