点评plus---异步消费之后可靠的生成订单
异步消费之后可靠的生成订单来自阿星不是程序员开源项目点评plus之前已经对秒杀系统进行了异步化升级。用户的购券请求会先通过 Lua 脚本完成原子性校验库存校验、一人一单校验以及 Redis 预扣库存。校验成功后系统会将订单信息封装成 Kafka 消息由生产者发送到 Kafka Broker再由 Kafka 消费者异步创建订单实现流量削峰与业务解耦。代码:/** * 创建秒杀订单 V2版本 * * 功能处理秒杀消息创建订单扣减库存记录订单路由信息缓存订单数据记录对账日志 * * param message Kafka消息对象里面包装了秒杀消息体 SeckillVoucherMessage * return boolean 是否创建成功 * * 注解说明 * - Override重写父类方法 * - RepeatExecuteLimit自定义注解防止重复执行基于消息的uuid避免消息重复消费 * - Transactional开启事务任何异常都会回滚 */Override/* *RepeatExecuteLimit 是一个防重复执行注解 主要用于Kafka消息消费幂等控制。 */RepeatExecuteLimit(nameSECKILL_VOUCHER_ORDER,keys{#message.uuid})Transactional(rollbackForException.class)publicbooleancreateVoucherOrderV2(MessageExtendSeckillVoucherMessagemessage){// 从消息扩展对象中取出真正的业务消息体SeckillVoucherMessagemessageBodymessage.getMessageBody();// 获取用户ID谁在抢购LonguserIdmessageBody.getUserId();// 查询数据库该用户是否已经成功购买过这个优惠券且订单状态是正常状态VoucherOrdernormalVoucherOrderlambdaQuery().eq(VoucherOrder::getVoucherId,messageBody.getVoucherId())// 优惠券ID相同.eq(VoucherOrder::getUserId,userId)// 用户ID相同.eq(VoucherOrder::getStatus,OrderStatus.NORMAL.getCode())// 订单状态正常未取消/未退款.one();// 查询一条记录// 如果查询到了说明用户已经买过了不允许重复购买if(Objects.nonNull(normalVoucherOrder)){log.warn(已存在此订单voucherId{},userId{},normalVoucherOrder.getVoucherId(),userId);thrownewHmdpFrameException(BaseCode.VOUCHER_ORDER_EXIST);// 抛出订单已存在异常}// 使用乐观锁扣减库存// - setSql(stock stock - 1)库存减1// - eq(voucher_id, ...)定位到指定的优惠券// - gt(stock, 0)保证库存大于0才扣减防止超卖// update() 返回 true 表示更新成功扣减成功false 表示失败库存不足或优惠券不存在booleansuccessseckillVoucherService.update().setSql(stock stock - 1).eq(voucher_id,messageBody.getVoucherId()).gt(stock,0).update();// 扣减失败说明库存不足抛异常触发事务回滚if(!success){thrownewHmdpFrameException(优惠券库存不足优惠券id:messageBody.getVoucherId());}VoucherOrdervoucherOrdernewVoucherOrder();voucherOrder.setId(messageBody.getOrderId());// 订单ID雪花算法生成voucherOrder.setUserId(messageBody.getUserId());// 用户IDvoucherOrder.setVoucherId(messageBody.getVoucherId());// 优惠券IDvoucherOrder.setCreateTime(LocalDateTimeUtil.now());// 创建时间// 保存订单到 voucher_order 表save(voucherOrder);// 订单路由表作用可能是用于后续的订单查询、分库分表、或者消息追踪VoucherOrderRoutervoucherOrderRouternewVoucherOrderRouter();voucherOrderRouter.setId(snowflakeIdGenerator.nextId());// 路由表自己的IDvoucherOrderRouter.setOrderId(voucherOrder.getId());// 关联订单IDvoucherOrderRouter.setUserId(userId);// 用户IDvoucherOrderRouter.setVoucherId(voucherOrder.getVoucherId());// 优惠券IDvoucherOrderRouter.setCreateTime(LocalDateTimeUtil.now());// 创建时间voucherOrderRouter.setUpdateTime(LocalDateTimeUtil.now());// 更新时间// 保存路由记录voucherOrderRouterService.save(voucherOrderRouter);// 将订单信息存入Redis过期时间60秒// 用途可能用于快速查询用户刚刚创建的订单或者用于后续流程的快速访问redisCache.set(RedisKeyBuild.createRedisKey(RedisKeyManage.DB_SECKILL_ORDER_KEY,// 缓存key前缀messageBody.getOrderId()// 订单ID作为key后缀),voucherOrder,// 缓存的值订单对象60,// 过期时间60秒TimeUnit.SECONDS// 时间单位秒);// 记录库存扣减日志用于后续对账保证数据一致性// LogType.DEDUCT扣减类型// BusinessType.SUCCESS成功状态voucherReconcileLogService.saveReconcileLog(LogType.DEDUCT.getCode(),// 日志类型扣减BusinessType.SUCCESS.getCode(),// 业务结果成功order created,// 描述信息message// 原始消息方便追踪);returntrue;}