每个店铺都可以发布优惠券,分为平价券和特价券(秒杀券)。平价券可以任意购买,而特价券需要秒杀抢购:

- tb_voucher:优惠券的基本信息,优惠金额、使用规则等
- tb_seckill_voucher:优惠券的基本信息、优惠券的库存、开始抢购时间,结束抢购时间。特价优惠券才需要填写这些信息
优惠卷对应数据库表
mysql
-- tb_voucher 优惠券表
create table tb_voucher
(
id bigint unsigned auto_increment comment '主键'
primary key,
shop_id bigint unsigned null comment '商铺id',
title varchar(255) not null comment '代金券标题',
sub_title varchar(255) null comment '副标题',
rules varchar(1024) null comment '使用规则',
pay_value bigint unsigned not null comment '支付金额,单位是分。例如200代表2元',
actual_value bigint not null comment '抵扣金额,单位是分。例如200代表2元',
type tinyint unsigned default '0' not null comment '0,普通券;1,秒杀券',
status tinyint unsigned default '1' not null comment '1,上架; 2,下架; 3,过期',
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)
collate = utf8mb4_general_ci;
-- tb_seckill_voucher 秒杀优惠券表,与优惠券是一对一关系
create table tb_seckill_voucher
(
voucher_id bigint unsigned not null comment '关联的优惠券的id'
primary key,
stock int not null comment '库存',
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
begin_time timestamp default CURRENT_TIMESTAMP not null comment '生效时间',
end_time timestamp default CURRENT_TIMESTAMP not null comment '失效时间',
update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)
comment '秒杀优惠券表,与优惠券是一对一关系' collate = utf8mb4_general_ci;平价券由于优惠力度并不是很大,所以是可以任意发放领取
而特价券由于优惠力度大,所以像第二种券,就得限制数量和抢购时间
秒杀优惠券表,与优惠券是一对一关系,通过优惠券的id关联优惠券表,相当于对优惠券表做了字段的扩展
VoucherController
java
/**
* 新增普通券
* @param voucher优惠券信息
* @return优惠券id
*/@PostMapping
/**
* 新增普通优惠券。
*
* @param voucher优惠券对象
* @return处理结果
*/
public Result addVoucher(@RequestBody Voucher voucher) {
voucherService.save(voucher);
return Result.ok(voucher.getId());
}
/**
* 新增秒杀券
* @param voucher优惠券信息,包含秒杀信息
* @return优惠券id
*/@PostMapping("seckill")
/**
* 新增秒杀优惠券。
*
* @param voucher优惠券对象
* @return处理结果
*/
public Result addSeckillVoucher(@RequestBody Voucher voucher) {
voucherService.addSeckillVoucher(voucher);
return Result.ok(voucher.getId());
}- VoucherServiceImpl:新增普通券和秒杀券共用同一个业务方法addSecKillVoucher()
java
/**
* 新增秒杀优惠券。
*
* 作用:
* 1.保存优惠券;
* 2.保存秒杀信息;
*
* @param voucher优惠券对象
* @return无返回值
*/
@Override
@Transactional
public void addSeckillVoucher(Voucher voucher) {
// 保存优惠券
save(voucher);
// 保存秒杀信息
SeckillVoucher seckillVoucher = new SeckillVoucher();
seckillVoucher.setVoucherId(voucher.getId());
seckillVoucher.setStock(voucher.getStock());
seckillVoucher.setBeginTime(voucher.getBeginTime());
seckillVoucher.setEndTime(voucher.getEndTime());
seckillVoucherService.save(seckillVoucher);
}想要秒杀优惠卷,需要先有普通优惠卷,,使用Postman请求接口,添加优惠卷保存到数据库
json
POST http://localhost:8081/voucher/seckill
{
"shopId": 1,
"title": "100元代金券",
"subTitle": "周一至周五均可使用",
"rules": "全场通用\\n无需预约\\n可无限叠加\\不兑现、不找零\\n仅限堂食",
"payValue": 8000,
"actualValue": 10000,
"type": 1,
"stock": 100,
"beginTime": "2022-01-25T10:09:17",
"endTime": "2037-01-26T12:09:04"
}
确保添加的优惠券没有过期,即 endTime > 当前时间 且 endTime > beginTime用户下单,触发请求:

下单时需要判断两点:
- 秒杀是否开始或结束,如果尚未开始或已经结束则无法下单
- 库存是否充足,不足则无法下单

- VoucherOrderServiceImpl
java
@Resource
private ISeckillVoucherService seckillVoucherService;
@Autowired
private RedisIdWorker redisIdWorker;
/**
* 处理优惠券秒杀下单请求。
*
* 作用:
* 1.查询优惠卷;
* 2.判断秒杀是否开始;
* 3.尚未开始;
* 4.判断秒杀是否已经结束;
* 5.已经结束;
*
* @param voucherId优惠券id
* @return处理结果
*/
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
//1.查询优惠卷
SeckillVoucher voucher=seckillVoucherService.getById(voucherId);
//2.判断秒杀是否开始
if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
//尚未开始
return Result.fail("秒杀尚未开始");
}
//3.判断秒杀是否已经结束
if(voucher.getEndTime().isBefore(LocalDateTime.now())){
//已经结束
return Result.fail("秒杀已结束");
}
//4.判断库存是否充足
if (voucher.getStock()<1) {
//库存不足
return Result.fail("库存不足");
}
//5.扣减库存
boolean success = seckillVoucherService.update()
.setSql("stock=stock-1") //set stock=stock-1
.eq("voucher_id", voucherId)
.update();
if(!success){
return Result.fail("库存不足");
}
//6.创建订单
VoucherOrder voucherOrder=new VoucherOrder();
//6.1.订单id
long orderId = redisIdWorker.nextId("order");
voucherOrder.setId(orderId);
//6.2.用户id
Long userId = UserHolder.getUser().getId(); //直接从ThreadLocal获取ID
voucherOrder.setUserId(userId);
//6.3.代金卷id
voucherOrder.setVoucherId(voucherId);
save(voucherOrder);
//7.返回订单id
return Result.ok(orderId);
}