苍穹外卖【day8| 用户下单】 个人主页:一条泥憨鱼(欢迎各位大佬莅临)精选专栏传送门:❄️《数据结构》 ❄️《AI与Agent那些事》❄️《从0开始学计算机网络》 ❄️《后端开发》需求分析和设计代码开发第1层Controller — 接收请求OrderController.javaRestController(userOrderController) RequestMapping(/user/order) Api(tags 用户端订单相关接口) Slf4j public class OrderController { Autowired private OrderService orderService; PostMapping(/submit) ApiOperation(用户下单) public ResultOrderSubmitVO submit(RequestBody OrdersSubmitDTO ordersSubmitDTO){ log.info(用户下单参数为{}, ordersSubmitDTO); OrderSubmitVO orderSubmitVO orderService.submitOrder(ordersSubmitDTO); return Result.success(orderSubmitVO); } }PostMapping(/submit)— 接口路径为POST /user/order/submitRequestBody OrdersSubmitDTO— 前端 JSON 自动反序列化为 DTO 对象Slf4j— Lombok 自动生成log对象用于记录日志Result.success(...)— 统一响应格式包裹返回数据第2层DTO — 前端提交的数据结构OrdersSubmitDTO.javaData public class OrdersSubmitDTO implements Serializable { private Long addressBookId; // 地址簿id private int payMethod; // 付款方式1微信 2支付宝 private String remark; // 备注 private LocalDateTime estimatedDeliveryTime; // 预计送达时间 private Integer deliveryStatus; // 配送状态1立即送出 0选择具体时间 private Integer tablewareNumber; // 餐具数量 private Integer tablewareStatus; // 餐具数量状态 private Integer packAmount; // 打包费 private BigDecimal amount; // 总金额 }这里前端提交的是用户下单时选择的信息不包含具体菜品菜品从购物车取。第3层Service 核心业务逻辑 — 分步详解OrderServiceImpl.java 的submitOrder方法整个流程分为6 个步骤步骤 ①校验地址簿是否存在AddressBook addressBook addressBookMapper.getById(ordersSubmitDTO.getAddressBookId()); if (addressBook null) { throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL); }根据前端传来的addressBookId查询地址表如果地址不存在抛出业务异常阻止下单同时这个addressBook对象后面还要用来提取收货人姓名、手机号步骤 ②获取当前用户ID 校验购物车Long userId BaseContext.getCurrentId(); ShoppingCart shoppingCart new ShoppingCart(); shoppingCart.setUserId(userId); ListShoppingCart shoppingCartList shoppingCartMapper.list(shoppingCart); if (shoppingCartList null || shoppingCartList.size() 0) { throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL); }BaseContext.getCurrentId()是从ThreadLocal中取出当前登录用户的 ID由 JWT 拦截器在请求进入时存入用userId查询购物车表得到该用户所有购物车商品购物车为空则抛出异常阻止下单这个shoppingCartList后面要用来生成订单明细步骤 ③组装 Orders 对象并插入订单表Orders orders new Orders(); BeanUtils.copyProperties(ordersSubmitDTO, orders); // 拷贝DTO同名属性 orders.setOrderTime(LocalDateTime.now()); // 下单时间 当前时间 orders.setPayStatus(Orders.UN_PAID); // 支付状态 未支付(0) orders.setStatus(Orders.PENDING_PAYMENT); // 订单状态 待付款(1) orders.setNumber(String.valueOf(System.currentTimeMillis())); // 订单号 时间戳 orders.setPhone(addressBook.getPhone()); // 从地址中取手机号 orders.setConsignee(addressBook.getConsignee()); // 从地址中取收货人 orders.setUserId(userId); // 设置用户ID orderMapper.insert(orders); // 插入数据库BeanUtils.copyProperties(ordersSubmitDTO, orders)— Spring 的工具类将 DTO 中同名属性自动拷贝到 Orders 实体如payMethod、amount、remark等setNumber(String.valueOf(System.currentTimeMillis()))— 用时间戳作为订单号简单但有碰撞风险生产环境应用雪花算法orderMapper.insert(orders)对应的 XML 中配置了useGeneratedKeystrue keyPropertyid插入后orders.getId()会自动回填主键值后续步骤要用步骤 ④遍历购物车生成订单明细列表ListOrderDetail orderDetailList new ArrayList(); for (ShoppingCart cart : shoppingCartList) { OrderDetail orderDetail new OrderDetail(); BeanUtils.copyProperties(cart, orderDetail); // 拷贝购物车属性 orderDetail.setOrderId(orders.getId()); // 关联订单ID orderDetailList.add(orderDetail); } orderDetailMapper.insertBatch(orderDetailList); // 批量插入BeanUtils.copyProperties(cart, orderDetail)自动拷贝同名属性orderDetail.setOrderId(orders.getId())是手动设置的因为购物车没有orderId需要关联到刚创建的订单insertBatch使用 MyBatis 的foreach动态拼接多行 VALUES一条 SQL 插入所有明细步骤 ⑤清空购物车shoppingCartMapper.deleteByUserId(userId);下单成功后删除该用户购物车中的所有数据。对应的 SQLDELETE FROM shopping_cart WHERE user_id #{userId}步骤 ⑥封装 VO 返回结果OrderSubmitVO orderSubmitVO OrderSubmitVO.builder() .id(orders.getId()) // 订单ID .orderTime(orders.getOrderTime()) // 下单时间 .orderNumber(orders.getNumber()) // 订单号 .orderAmount(orders.getAmount()) // 订单金额 .build(); return orderSubmitVO;OrderSubmitVO 返回给前端 4 个字段字段说明id订单IDorderNumber订单号时间戳orderAmount订单总金额orderTime下单时间前端拿到这些数据后跳转到支付页面。四、完整数据流图┌─────────────────────────────────────────────────────────────────┐ │ 前端 POST /user/order/submit │ │ Body: { addressBookId, payMethod, amount, remark, ... } │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ OrderController.submit(OrdersSubmitDTO) │ │ → orderService.submitOrder(ordersSubmitDTO) │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ OrderServiceImpl.submitOrder() [Transactional 事务保护] │ │ │ │ ① 查地址 → addressBookMapper.getById(...) │ │ ├─ null → 抛异常 AddressBookBusinessException │ │ └─ 有值 → 继续 │ │ │ │ ② 查购物车 → shoppingCartMapper.list(userId) │ │ ├─ 空 → 抛异常 ShoppingCartBusinessException │ │ └─ 有数据 → 继续 │ │ │ │ ③ 组装 Orders → orderMapper.insert(orders) │ │ └─ orders.id 自动回填自增主键 │ │ │ │ ④ 遍历购物车 → 生成 OrderDetail 列表 │ │ └─ orderDetailMapper.insertBatch(list) │ │ │ │ ⑤ 清空购物车 → shoppingCartMapper.deleteByUserId(userId) │ │ │ │ ⑥ 封装 OrderSubmitVO → return │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 前端收到 ResultOrderSubmitVO │ │ → 跳转支付页面 │ └─────────────────────────────────────────────────────────────────┘五、关键设计要点总结要点说明Transactional整个方法在一个事务中步骤③④⑤任意一步失败都会回滚保证数据一致性useGeneratedKeystrue插入 orders 后自动回填主键 ID供步骤④使用BeanUtils.copyProperties减少手写 setterDTO→Entity、Cart→OrderDetail 都复用了此工具BaseContext(ThreadLocal)从 JWT 令牌解析出的用户 ID 存入 ThreadLocalService 层直接取用无需层层传参批量插入insertBatch用一条 SQL 插入所有订单明细避免循环单条插入的性能问题Builder 模式OrderSubmitVO.builder()...build()链式构建对象代码清晰可读

相关新闻

最新新闻

蓝桥杯国赛电子秤项目实战:从传感器原理到高精度滤波算法全解析

蓝桥杯国赛电子秤项目实战:从传感器原理到高精度滤波算法全解析

1. 从“电子秤”到“高精度称重系统”:国赛赛题的实战解读 第九届蓝桥杯国赛的“电子秤”题目,对于很多备赛的同学来说,可能第一反应是“这不就是个ADC采样和数据处理吗?”。但如果你真这么想,那可能就错过了这道题的精…

2026/8/28 7:49:46
top指令详解

top指令详解

top指令详解 top指令详解

2026/8/28 7:49:46
基于Flask与YOLO的RTSP视频流实时AI分析服务构建指南

基于Flask与YOLO的RTSP视频流实时AI分析服务构建指南

简介:目标检测是计算机视觉的核心任务之一,它通过算法识别并定位图像或视频中的特定物体。其原理通常基于深度学习模型,如YOLO系列,对输入图像进行特征提取与分类回归,输出边界框与类别信息。这项技术的价值在于将AI感…

2026/8/28 7:49:46
网络nat-技术笔记

网络nat-技术笔记

什么是NAT(网络地址转换)? NAT是为了让我们对外访问用的: (1)静态NAT 一个内网地址对应一个公网地址,是固定的。 (2)动态NAT 有一个公网IP池,内网IP随机从公网…

2026/8/28 7:49:46
别让LLM直接写邮件主题行:工程边界与规则兜底

别让LLM直接写邮件主题行:工程边界与规则兜底

让 LLM 生成邮件主题行,为什么是件危险的事? 先说结论:在 LLM 应用落地的过程中, 最需要警惕的不是模型能力不够,而是职责边界没划清 。把“写邮件主题行”这类看似简单的任务完全交给 LLM,表面上是提效&…

2026/8/28 7:49:46
汽车制造业生产经营分析指标体系与公式【附全文阅读】

汽车制造业生产经营分析指标体系与公式【附全文阅读】

这份汽车制造经营指标 PPT 是新能源车企经营分析、数字化 BI、供应链咨询项目推介核心实战素材,复用价值极高。文档构建完整车企多层级指标体系,涵盖盈利、产能、供应链、人效、研发、现金流、ESG 七大板块,配套全套标准计算公式,…

2026/8/28 7:44:46