Skip to content
DAILY QUOTE

“ ”

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

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