优惠券秒杀的核心业务逻辑

这是一套 SpringBoot + MyBatis-Plus 实现的优惠券秒杀下单功能,前端用户点击抢购后,后端校验时间、库存,然后扣库存、生成订单并返回结果。

这是一个典型的 Java Spring Boot 后端业务代码 ,实现了优惠券秒杀(Seckill)的核心业务逻辑。我们可以将这段代码拆解为 3 个核心层面来理解:

(1)校验:检查秒杀是否开始、是否结束,以及库存是否足够。

(2)扣减:如果校验通过,减少数据库中的优惠券库存。

(3)创建:在订单表中生成一条订单记录。

(4)返回:告诉用户是否成功,并返回订单 ID。

Controller

复制代码
@RestController
@RequestMapping("/voucher-order")
public class VoucherOrderController {

    @Resource
    private IVoucherOrderService voucherOrderService;

    @PostMapping("seckill/{id}")
    public Result seckillVoucher(@PathVariable("id") Long voucherId) {
        return voucherOrderService.seckillVoucher(voucherId);
    }
}

Service

复制代码
public interface IVoucherOrderService extends IService<VoucherOrder> {
    Result seckillVoucher(Long voucherId);
}

ServiceImpl

复制代码
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIdWorker redisIdWorker;

    @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")
                .eq("voucher_id", voucherId)
                .update();
        if (!success) {
            return Result.fail("库存不足!");
        }

        // 6.创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        // 订单ID
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        // 用户ID
        Long userId = UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
        // 代金券ID
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);

        // 7.返回订单号
        return Result.ok(orderId);
    }
}

整体结构(三层)

  1. Controller 层

    • 接收前端 HTTP 请求
    • 只做转发,不写业务逻辑
    • 接口地址:/voucher-order/seckill/{优惠券ID}
  2. Service 接口

    • 定义秒杀方法
    • 方便解耦、扩展、测试
  3. Service 实现类(核心)

    • 所有业务逻辑都在这里
    • 加了事务 @Transactional,保证扣库存和生成订单要么都成功,要么都失败

整体执行流程(完整逻辑)

  1. 前端传入优惠券 ID,请求秒杀接口
  2. 后端根据 ID 查询优惠券信息
  3. 校验秒杀时间:
    • 没开始 → 拒绝
    • 已结束 → 拒绝
  4. 校验库存:
    • 库存不足 → 拒绝
  5. 库存充足 → 执行数据库扣减库存
  6. 扣减成功 → 生成全局唯一订单 ID
  7. 创建订单记录(包含用户 ID、优惠券 ID)
  8. 保存订单到数据库
  9. 返回订单号给前端,表示秒杀成功

涉及的数据库表

1.tb_seckill_voucher:秒杀优惠券表,存库存、开始结束时间

2.tb_voucher_order:用户秒杀成功后的订单表


关键点总结

1.这是标准后端秒杀业务

2.用了 Spring 事务 保证数据安全

3.用了 Redis 生成唯一订单号

4.用 MyBatis-Plus 简化数据库操作

5.流程:校验时间 → 校验库存 → 扣库存 → 生成订单

相关推荐
做个文艺程序员34 分钟前
第04篇:K8s 弹性伸缩实战:HPA、VPA、KEDA——Java SaaS 应对流量洪峰的秘密武器
java·容器·kubernetes·弹性伸缩·自动扩容·ai 推理伸缩
漂流瓶jz4 小时前
Webpack如何实现万物皆可import?loader的使用/配置/手写实践
前端·javascript·webpack
ZC跨境爬虫4 小时前
跟着 MDN 学CSS day_41:显式轨道、隐式网格与区域命名放置
前端·javascript·css·ui·交互
weelinking4 小时前
【产品】12_接入数据库——让数据永久保存
jvm·数据库·python·react.js·数据挖掘·前端框架·产品经理
稳联技术老娜4 小时前
DeviceNet主站怎么连接西门子PLC,Profinet网关配置手册(那智机器人)
服务器·网络·数据库
石山代码4 小时前
ArrayList / HashMap / ConcurrentHashMap
java·开发语言
这个DBA有点耶5 小时前
云上运维新挑战:当数据库不再“看得见摸得着”
数据库·sql·程序人生·云原生·运维开发·学习方法·dba
修己xj5 小时前
告别手动存图!这款叫 Fatkun 的浏览器插件,简直是素材收集神器
前端
袋鼠云数栈5 小时前
从前端到基础设施,ACOS 如何打通企业全链路可观测
运维·前端·人工智能·数据治理·数据智能
AskHarries6 小时前
系统提示词、开发者指令和用户输入的优先级
java·前端·数据库