蜗牛学苑 Java 智能体学习 Day49|贯穿项目 5 订单下单业务 思维导图复盘

一、思维导图整体知识结构梳理

业务本质

数据库新增操作

  • order 订单主表:保存订单整体信息
  • orderItem 订单详情表:保存订单内每一件商品

数据库修改操作

  • book 书籍表:扣减商品库存
  • cart 购物车表:更新已选中购物项的结算状态

附加业务逻辑

  • 超时未支付自动取消订单,采用 RabbitMQ 延时消息实现

核心注解 @Transactional

  • 作用范围:标注在业务方法上
  • 事务特性:方法内所有数据库操作,要么全部提交成功,一旦抛出异常则全部回滚
  • 业务目的:防止部分 SQL 执行成功、部分失败,保障多表操作的数据一致性
  • 注意事项:默认只捕获 runtimeException 运行时异常进行回滚,受异常传播配置影响

①接收参数 & 查询购物车数据

  • 入参 CartDto
    • cartIds:用户勾选的购物车 id 集合,用来确定哪些商品要下单
    • addressId:用户选择的收货地址编号
  • 执行 SQL:cartMapper.findListByIds (cartIds)
    • 根据购物车 id 集合,批量查询多条购物车记录,封装为 CartVo 集合
    • CartVo 封装信息:书籍 id、书籍名称、单价、购买数量、当前库存

②库存校验逻辑

  • 需求:下单前校验每一件选中商品库存,库存不足直接拒绝下单
  • 实现方式:Stream 流式处理
    • filter 过滤:筛选出【购买数量 > 商品库存数量】的记录
    • map 映射:提取库存不足的书籍名称
    • collect 收集:把名称存入 List 集合
  • 分支判断
    • CollectionUtils 判断集合不为空:代表存在库存不足商品
      • 直接返回 ResponseUtil,携带 FAIL 失败枚举,携带库存不足的书籍名称集合返回前端
      • 终止后续下单流程,不会执行任何数据库写入操作
    • 集合为空:全部商品库存充足,继续往下执行下单

③雪花 ID 生成订单编号

  • 工具类:SnowflakeIdGenerator 雪花算法生成器
  • 调用 sfg.nextId () 生成全局唯一 ID,转为字符串作为 orderNum 订单编号
  • 结构:时间戳 + 机器 ID + 序列号
  • 雪花 ID 特点:分布式场景多台服务器生成 ID 不重复,天然有序
  • 作为订单主表唯一业务主键

④订单总金额计算

  • 类型选择 BigDecimal
    • 不能使用 double/float:浮点类型存在精度丢失,金额计算必须使用 BigDecimal
    • 初始值:BigDecimal.ZERO
  • 计算逻辑
    • 循环遍历 CartVo 购物项集合
    • 单项金额 = 商品单价 multiply (购买数量)
    • totalPrice = totalPrice.add (单项金额),累加得到订单总金额
  • 安全要点
    • 前端页面只展示金额,后端必须重新完整计算金额
    • 防止用户通过浏览器篡改前端提交的价格,造成资金漏洞

⑤封装并插入订单主表 order

  • Order 实体字段
    • orderNum:雪花算法生成的订单编号(业务唯一标识)
    • totalPrice:后端计算得到的订单总金额
    • userId:当前登录用户 ID,从 UserContext 获取
    • addressId:前端传入的收货地址 ID
    • createTime:订单创建时间 new Date ()
    • status 订单状态:1 代表【待付款】
  • UserContext 用户上下文
    • 底层 ThreadLocal 线程本地存储
    • 作用:在整个请求链路中,随时获取当前登录用户 id,不用每次都从头解析 token
  • 执行 SQL:orderMapper.addOrder (order)
    • 必须先插入订单主表,拿到 orderNum,才能给订单详情设置外键

⑥批量封装、新增订单详情 orderItem

  • 循环 CartVo 集合,逐个构建 OrderItem 订单详情对象
  • OrderItem 字段
    • bookId:书籍 id
    • bookName:书籍名称
    • price:下单时商品单价(保存快照,后续商品改价不影响历史订单)
    • buyCount:购买数量
    • sumPrice:该商品小计金额
    • orderId:关联订单编号 orderNum,绑定属于哪一笔订单
    • createTime:详情记录创建时间
  • 批量插入
    • 使用 MyBatis 的 foreach 标签一次性插入多条详情记录
    • orderItemMapper.batchAddOrderItem(orderItemList)
    • 优势:减少多次数据库连接 IO,提升性能,相比循环单次 insert 效率更高

⑦批量更新数据库

  • cartMapper.batchUpdateCartStatus(cartVoList)
    • 批量修改本次下单对应的购物项状态,标记为已结算
    • 业务效果:结算后的商品,不再展示在用户购物待结算列表
  • bookMapper.batchUpdateBookCount(cartVoList)
    • 批量扣减书籍库存,增加商品的已购买统计数量
    • MyBatis foreach 批量 update,避免循环逐条更新数据库

⑧超时取消订单(业务预留逻辑)

  • 业务需求:下单后 15 分钟未完成付款,自动取消订单,释放商品库存
  • 技术方案:RabbitMQ 延时消息队列
    • 下单成功发送延时消息,延迟 15 分钟投递
    • 消费者接收消息,查询订单状态;如果依旧是待付款,则执行订单取消逻辑,回滚库存

工具类与规范类

  • ResponseUtil:统一返回结果工具类,封装后端返回给前端的 JSON 结构
  • ResponseEnum:响应枚举,统一管理状态码、提示信息(成功、库存不足、失败等)
  • CollectionUtils:集合工具类,安全判断集合是否为空,避免空指针异常

雪花 ID(SnowflakeId)

概念

  • 推特开源分布式 ID 生成算法
  • 用于生成全局唯一的 ID,本项目用来生成订单号 orderNum

组成结构(64 位 Long 类型)

  • 时间戳(毫秒):毫秒级时间,从指定基准时间开始的差值,保证 ID 整体递增
  • 机器 ID:区分不同服务器 / 实例,集群环境防止重复
  • 序列号:同一毫秒内,生成多个 ID 的自增序号;同一毫秒最多可生成 4096 个 ID

项目使用方式

  • 工具类:SnowflakeIdGenerator
  • 调用方法:sfg.nextId (),返回 long 类型 Id,转为字符串作为订单编号

核心特点

  • 唯一性:分布式多台服务器生成 ID 不会重复
  • 有序性:大体上按时间递增,数据库索引性能更好
  • 高性能:内存生成,不依赖数据库,生成速度快

适用场景

  • 订单号、商品 ID 等分布式系统唯一主键
  • 替代数据库自增主键;自增 ID 在多库多表集群环境会冲突

优缺点

  • 优点:不依赖数据库,生成效率高;自带时间信息,可以从 ID 反推生成时间
  • 缺点:依赖服务器系统时间;服务器回拨时间,可能产生重复 ID

项目代码相关

复制代码
SnowflakeIdGenerator sfg = new SnowflakeIdGenerator();
String orderNum = sfg.nextId().toString();
  • 生成结果转为字符串,存入 order 订单表 orderNum 字段,作为业务订单编号

二、制作意图与规划

  1. 梳理订单下单完整业务链路:参数接收→库存校验→订单主表 + 详情入库→库存 & 购物车更新,完整掌握事务控制场景
  2. 重点理解分布式唯一 ID 雪花算法的原理、优缺点和业务落地,区分数据库自增主键的局限
  3. 掌握金额计算 BigDecimal 规范,理解前端传参不可信,后端必须重算金额的安全思想
  4. 熟悉 MyBatis 批量新增 / 更新,对比循环单条 SQL 的性能差异
  5. 了解延时消息 RabbitMQ 实现订单超时关闭的设计思路,为后续消息队列业务开发铺垫

三、当日学习总结

  1. 今天完整落地书城项目订单下单核心业务 ,打通从前端传入购物车 ID 到多表更新的全链路开发,理解电商下单场景是典型多表事务业务,必须使用@Transactional保证原子性,一旦中间任何步骤异常,全部数据库操作回滚。
  2. 掌握下单前置校验逻辑:通过 Stream 流式表达式批量校验库存,提前拦截库存不足场景,避免无效数据库写入,学会集合工具类判断空集合,规避空指针。
  3. 理解雪花算法的原理、结构与适用场景,对比数据库自增 ID 的短板,学会在项目中使用雪花 ID 生成业务订单号,满足分布式环境下 ID 唯一需求,同时知晓时间回拨带来的潜在缺陷。
  4. 夯实金额开发规范:金融 / 订单场景必须使用 BigDecimal 做计算,严禁 double 浮点类型;建立后端校验思想,不能信任前端传来的价格,后端要独立重新计算订单总价,防止恶意篡改价格漏洞。
  5. 熟练 MyBatis foreach 批量操作,批量新增订单详情、批量更新购物车和书籍库存,相比循环单条 SQL,减少 IO 交互,提升接口性能。
  6. 了解订单超时关闭的业务设计,使用 RabbitMQ 延时消息实现 15 分钟未支付自动取消订单,为后续消息队列编码实现铺垫业务思路。
  7. 巩固 ThreadLocal 的 UserContext 用户上下文,理解在请求链路中传递登录用户信息的实现方式,不用反复解析 token 获取用户 ID。
相关推荐
鬼手点金1 小时前
Claude Code示范案例-修复 Bug 工作流
java·服务器·前端·javascript·bug·openclaw
一条破秋裤1 小时前
Linux 进程通信介绍:内存隔离与 IPC 分类
java·linux·服务器
“AI国潮设计-小江”1 小时前
【AIGC实战】Python+SDXL打造潮汕国潮IP:英歌舞麻将的自动化生成与商用思路
开发语言·人工智能·python·prompt·aigc
别动我齐刘海1 小时前
ROS2 Jazzy + C++ 时间系统——Time / Clock / Duration
linux·c++·人工智能·学习·机器学习·机器人·自动驾驶
Elastic 中国社区官方博客1 小时前
两个依赖和一个配置块:通过 Prometheus 远程写入将 Spring Boot 指标发送到 Elasticsearch
大数据·数据库·spring boot·elasticsearch·搜索引擎·全文检索·prometheus
机核研创社1 小时前
polo 长袖短裤套装自动化产线:八台机器排队接力
android·java·自动化
小范的技术工坊1 小时前
大模型学习操作文档(13个核心概念串讲)
人工智能·学习·算法·大模型
索端阳1 小时前
I.MX6ULL 裸机开发:LCD Framebuffer 与 RGB888 学习笔记
笔记·vscode·嵌入式硬件·学习
Roadinforest2 小时前
Spring Boot 4 六步入门实录:从 Hello World 到一个能交付的 Todo API
java·开发语言·spring boot·spring