瑞吉外卖项目复盘(六)
课程内容
- 用户地址簿功能
- 菜品展示
- 购物车(使用ThreadLocal获取当前登录的用户ID,对响应的购物车数据进行操作)
- 下单(计算金额时使用AtomicInteger保证原子操作,实体类中金额的类型是BigDemical,订单号使用IdWorker实现)
用户地址簿功能
移动端消费者用户的地址信息。同一个用户可以有多个地址信息,但是只能有一个默认地址。
对于地址簿管理,我们需要实现以下几个功能:
- 新增地址
- 需要记录当前是哪个用户的地址(关联当前登录用户)
- 设置默认地址
- 每个用户可以有很多地址,但是默认地址只能有一个
- 先将该用户的所有地址的is_default更新为0,然后将当前是设置的默认地址的is_default设置为1
- 根据ID查询地址
- 查询默认地址
- 根据当前的登录用户ID以及is_default进行查询,查询当前登录用户is_default为1的地址信息
- 查询指定用户的全部地址
- 根据当前登录用户ID,查询所有的地址列表
菜品展示
在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息,需要展示
按钮,否则显示
按钮。
前端与后端的交互逻辑:
- 首先要获取分类数据(菜品分类和套餐分类)
- 获取第一个分类下的菜品或者套餐信息。
- 根据分类ID查询套餐信息
- 根据分类ID查询菜品列表(在实现添加套餐功能中已实现,但是之前查询的只是菜品的基本信息,不包含菜品的口味信息)
查询菜品方法修改:
- 根据分类ID查询,查询目前正在起售的菜品列表(已实现)
- 遍历菜品列表,并查询菜品的分类信息以及菜品的口味列表
- 组装DishDto并返回
根据分类ID查询套餐:
套餐的查询比较简单,需要根据CategoryId,Status查询,然后再根据UpdateTime降序排列即可。没有涉及其它表格。
购物车
需要实现的功能有:
- 添加购物车
- 查询购物车
- 清空购物车
对于同一个菜品/套餐,如果选择多份不需要添加多条记录,增加数量number即可
加入购物车实现逻辑:
- 获取当前用户,为购物车对象赋值
- 根据当前登录用户ID及本次添加的菜品ID/套餐ID,查询购物车中是否存在统一菜品/套餐。
- 如果已经存在,就在原本数量基础上加1
- 如果不存在,则添加到购物车,数量默认就是1
查询购物车:
根据当前登陆用户ID查询购物车列表,并对查询结果进行创建时间的倒序排序
清空购物车:
清空购物车,登录用户ID,删除购物车数据
下单
点击去结算按钮,跳到订单确认页面,点击去支付则完成下单操作。
orders订单表:
order_detail订单表:
当点击”去结算“按钮,需要获取用户的默认收货地址(已实现),购物车中的数据(已实现),进行下单操作(需要往订单表oreders中插入一条记录,并且需要往order_detail中插入一条或多条记录)
下单操作具体逻辑:
- 获得当前用户id,查询当前用户的购物车数据
- 根据当前登录用户id,查询用户数据
- 根据地址ID,查询地址数据
- 组装订单明细数据,批量保存订单明细
- 组装订单数据,批量保存订单明细
- 删除当前用户的购物车列表数据
上述逻辑中,计算购物车商品的总金额时,为保证我们每一次执行的累加计算是一个原子操作,我们这里用到了JDK中提供的原子类AtomicInteger。订单号使用Mybatisplus的IdWorker生成
@Autowired
private ShoppingCartService shoppingCartService;
@Autowired
private UserService userService;
@Autowired
private AddressBookService addressBookService;
@Autowired
private OrderDetailService orderDetailService;
/**
* 用户下单
* @param orders
*/
@Transactional
public void submit(Orders orders) {
//获得当前用户id
Long userId = BaseContext.getCurrentId();
//查询当前用户的购物车数据
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);
List<ShoppingCart> shoppingCarts = shoppingCartService.list(wrapper);
if(shoppingCarts == null || shoppingCarts.size() == 0){
throw new CustomException("购物车为空,不能下单");
}
//查询用户数据
User user = userService.getById(userId);
//查询地址数据
Long addressBookId = orders.getAddressBookId();
AddressBook addressBook = addressBookService.getById(addressBookId);
if(addressBook == null){
throw new CustomException("用户地址信息有误,不能下单");
}
long orderId = IdWorker.getId();//订单号
AtomicInteger amount = new AtomicInteger(0);
//组装订单明细信息
List<OrderDetail> orderDetails = shoppingCarts.stream().map((item) -> {
OrderDetail orderDetail = new OrderDetail();
orderDetail.setOrderId(orderId);
orderDetail.setNumber(item.getNumber());
orderDetail.setDishFlavor(item.getDishFlavor());
orderDetail.setDishId(item.getDishId());
orderDetail.setSetmealId(item.getSetmealId());
orderDetail.setName(item.getName());
orderDetail.setImage(item.getImage());
orderDetail.setAmount(item.getAmount());
amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
return orderDetail;
}).collect(Collectors.toList());
//组装订单数据
orders.setId(orderId);
orders.setOrderTime(LocalDateTime.now());
orders.setCheckoutTime(LocalDateTime.now());
orders.setStatus(2);
orders.setAmount(new BigDecimal(amount.get()));//总金额
orders.setUserId(userId);
orders.setNumber(String.valueOf(orderId));
orders.setUserName(user.getName());
orders.setConsignee(addressBook.getConsignee());
orders.setPhone(addressBook.getPhone());
orders.setAddress((addressBook.getProvinceName() == null ? "" : addressBook.getProvinceName())
+ (addressBook.getCityName() == null ? "" : addressBook.getCityName())
+ (addressBook.getDistrictName() == null ? "" : addressBook.getDistrictName())
+ (addressBook.getDetail() == null ? "" : addressBook.getDetail()));
//向订单表插入数据,一条数据
this.save(orders);
//向订单明细表插入数据,多条数据
orderDetailService.saveBatch(orderDetails);
//清空购物车数据
shoppingCartService.remove(wrapper);
}