文章目录
SpringBoot项目实战:难忘外卖开发 —— 下单功能实现【12】
引言
在本篇博客中,我们将深入探讨如何实现一个外卖系统中的下单功能。使用Spring Boot框架和一些常用的技术栈,如MyBatis,进行后端开发。在该功能中,用户能够提交订单,系统会进行一系列的业务处理,包括校验用户信息、购物车内容、地址信息等,最终生成订单并返回给前端。
功能概述
本示例实现了一个简化的外卖系统中的“下单”功能。用户通过提交包含地址、购物车信息的请求,系统会对请求进行验证,创建订单,并将购物车中的商品记录到订单明细表中,最后返回一个订单的简要信息。
主要模块
1. Controller 层
首先,创建一个 UserOrderController
,提供一个下单的接口供前端调用。
/**
* 用户下单
* @param ordersSubmitDTO
* @return
*/
@PostMapping("/submit")
public Result<OrderSubmitVO> userOrder(@RequestBody OrdersSubmitDTO ordersSubmitDTO) {
OrderSubmitVO orderSubmitVO = userOrderService.submitOrder(ordersSubmitDTO);
return Result.success(orderSubmitVO);
}
在这个接口中,我们通过 @PostMapping("/submit")
来处理用户的下单请求,接收前端传来的 ordersSubmitDTO
对象。接着调用 userOrderService.submitOrder
方法处理订单逻辑,并返回一个 OrderSubmitVO
作为响应结果。
2. Service 层
服务层的 UserOrderServiceImpl
实现了下单的核心业务逻辑。它包括以下几个步骤:
- 校验地址信息:判断用户传递的地址是否有效。
- 校验购物车:检查用户购物车是否为空。
- 创建订单:向
orders
表插入订单信息。 - 插入订单明细:向
order_detail
表插入订单明细。 - 清空购物车:下单成功后,清空用户的购物车。
@Service
public class UserOrderServiceImpl implements UserOrderService {
@Autowired
UserOrderMapper userOrderMapper;
@Autowired
UserOrderDetailMapper userOrderDetailMapper;
@Autowired
AddressBookMapper addressBookMapper;
@Autowired
ShoppingCartMapper shoppingCartMapper;
/**
* 用户下单
* @param ordersSubmitDTO
* @return
*/
@Override
public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
// 1. 校验地址信息
AddressBook address = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
if (address == null) {
throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
}
Long currentId = BaseContext.getCurrentId();
// 2. 校验购物车是否为空
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.setUserId(currentId);
List<ShoppingCart> shoppingCartList = shoppingCartMapper.installshopping_cart(shoppingCart);
if (shoppingCartList == null || shoppingCartList.isEmpty()) {
throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
}
// 3. 创建订单
Orders order = new Orders();
BeanUtils.copyProperties(ordersSubmitDTO, order);
order.setOrderTime(LocalDateTime.now());
order.setPayStatus(Orders.UN_PAID);
order.setStatus(Orders.PAID);
order.setNumber(String.valueOf(System.currentTimeMillis()));
order.setPhone(address.getPhone());
order.setConsignee(address.getConsignee());
order.setUserId(currentId);
Long orderId = userOrderMapper.insteOrder(order);
// 4. 插入订单明细
List<OrderDetail> orderDetails = new ArrayList<>();
for (ShoppingCart cart : shoppingCartList) {
OrderDetail orderDetail = new OrderDetail();
BeanUtils.copyProperties(cart, orderDetail);
orderDetail.setOrderId(orderId);
orderDetails.add(orderDetail);
}
userOrderDetailMapper.installall(orderDetails);
// 5. 清空购物车
shoppingCartMapper.deleteshoppingcartAll(currentId);
// 6. 封装返回结果
OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
.orderTime(order.getOrderTime())
.id(order.getId())
.orderNumber(order.getNumber())
.orderAmount(order.getAmount())
.build();
return orderSubmitVO;
}
}
3. 核心业务逻辑详解
- 校验地址信息
在下单过程中,首先要验证用户提供的收货地址是否有效。系统通过 addressBookMapper.getById
方法查询数据库中是否存在该地址。若不存在,抛出 AddressBookBusinessException
异常。
- 校验购物车
接着,系统会查询当前用户的购物车是否为空。如果为空,抛出 ShoppingCartBusinessException
异常,提示用户购物车为空。
- 创建订单
如果购物车有商品,系统会创建一个新的 Orders
实体,填充用户提交的订单信息。此时,订单的状态设置为“未支付”,并生成一个唯一的订单号。然后,插入到数据库的 orders
表中。
- 插入订单明细
系统会根据购物车中的商品列表,生成对应的 OrderDetail
记录,并批量插入到 order_detail
表中。这些明细记录包含了订单中商品的详细信息。
- 清空购物车
订单提交成功后,购物车中的商品会被清空,确保不会重复下单。
- 返回结果
最后,系统返回一个简化的订单信息 OrderSubmitVO
,供前端显示。该 VO(值对象)包含了订单的基本信息,如订单号、订单金额、订单时间等。
MyBatis 插入订单 SQL 语句解析在本篇博客中,我们将深入探讨MyBatis中的一个 insert
操作。这个操作将用户下单的相关信息插入到 orders
表中,关键点在于如何使用 useGeneratedKeys
和 keyProperty
来获取插入后生成的主键,并将其设置到实体对象中。
下面是插入订单的 SQL 语句部分:
<insert id="insteOrder" useGeneratedKeys="true" keyProperty="id">
insert into orders (number, status, user_id, address_book_id, order_time, checkout_time,
pay_method, pay_status, amount, remark, phone, address, consignee,
estimated_delivery_time, delivery_status, pack_amount, tableware_number, tableware_status)
values (#{number}, #{status}, #{userId}, #{addressBookId},
#{orderTime}, #{checkoutTime}, #{payMethod}, #{payStatus}, #{amount}, #{remark},
#{phone}, #{address}, #{consignee}, #{estimatedDeliveryTime},
#{deliveryStatus}, #{packAmount}, #{tablewareNumber},
#{tablewareStatus})
</insert>
关键属性详解
useGeneratedKeys="true"
:- 这个属性告诉 MyBatis 在执行插入操作后自动获取数据库生成的主键值。常用于自增主键的情况。
- 例如,在
orders
表中,id
字段通常是自增主键,插入新记录时数据库会自动生成这个id
。
keyProperty="id"
:keyProperty
指定插入操作后生成的主键会赋值到哪个 Java 对象的属性上。在这里,id
属性会被自动填充为数据库生成的主键值。- 在 Java 实体类中,
Order
对象的id
属性会被设置为插入订单后的自增主键。
SQL 语句解析
insert into orders (number, status, user_id, address_book_id, order_time, checkout_time,
pay_method, pay_status, amount, remark, phone, address, consignee,
estimated_delivery_time, delivery_status, pack_amount, tableware_number, tableware_status)
values (#{number}, #{status}, #{userId}, #{addressBookId},
#{orderTime}, #{checkoutTime}, #{payMethod}, #{payStatus}, #{amount}, #{remark},
#{phone}, #{address}, #{consignee}, #{estimatedDeliveryTime},
#{deliveryStatus}, #{packAmount}, #{tablewareNumber},
#{tablewareStatus})
insert into orders (...)
:- 向
orders
表中插入数据,指定了多个字段,这些字段将被填充来自Orders
对象的相应属性值。
- 向
values (...)
:- 这些值对应于表单中插入的字段。每个
#{}
语法表示从OrdersSubmitDTO
或实体对象中提取属性并映射到 SQL 中。 - 例如,
#{number}
会取ordersSubmitDTO
中的number
字段,#{status}
会取status
字段,以此类推。
- 这些值对应于表单中插入的字段。每个
使用 useGeneratedKeys
获取主键
通过使用 useGeneratedKeys="true"
,MyBatis 会自动获取数据库生成的主键值,并将其填充到 Order
对象的 id
属性中。在插入操作后,调用 orderMapper.insteOrder(order)
进行插入时,order
对象的 id
字段将被赋值为数据库自增生成的主键。
实际应用
在我们的 UserOrderServiceImpl
中,userOrderMapper.insteOrder(order)
调用就会触发这个 SQL 插入操作,并自动将 order
对象的 id
设置为数据库生成的主键值。
Long orderId = userOrderMapper.insteOrder(order);
这里,orderId
将被设置为新插入的订单的主键,并用于后续操作,例如插入订单明细,创建订单的关联数据等。
通过使用 MyBatis 的 useGeneratedKeys
和 keyProperty
配置,我们能够方便地在插入操作中获取数据库自动生成的主键,并将其赋值给 Java 对象的属性。在实际开发中,这对于处理订单和其他涉及主键生成的业务场景非常有帮助。希望这篇博客能够帮助你更好地理解 MyBatis 的插入操作及其如何自动处理主键。
效果展示
小结
通过本篇文章的学习,我们实现了外卖系统中的订单下单功能。在实现过程中,我们重点讨论了如何进行业务验证、创建订单、处理购物车数据以及返回订单信息等内容。这是一个典型的电商系统中的核心功能,在实际项目中,开发者可以根据实际需求扩展更多功能,如支付、优惠券、积分等。希望本篇文章能为你的开发提供一些思路和参考。