二刷 苍穹外卖day09
查询历史订单
service
public PageResult pageQuery4User(int pageNum, int pageSize, Integer status) {// 设置分页PageHelper.startPage(pageNum, pageSize);OrdersPageQueryDTO ordersPageQueryDTO = new OrdersPageQueryDTO();ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());ordersPageQueryDTO.setStatus(status);// 分页条件查询Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);List<OrderVO> list = new ArrayList();// 查询出订单明细,并封装入OrderVO进行响应if (page != null && page.getTotal() > 0) {for (Orders orders : page) {Long orderId = orders.getId();// 订单id// 查询订单明细List<OrderDetail> orderDetails = orderDetailMapper.getByOrderId(orderId);OrderVO orderVO = new OrderVO();BeanUtils.copyProperties(orders, orderVO);orderVO.setOrderDetailList(orderDetails);list.add(orderVO);}}return new PageResult(page.getTotal(), list);}
先创建了一个OrdersPageQueryDTO对象,用于封装查询条件
通过BaseContent.getCurrentId()获取当前用户id
在设置好状态后
用条件查询功能查询符合要求的订单数据,返回一个Page对象
创建OrderVO类型的list列表用于存储封装后的订单数据
将page对象中的每一个订单Orders,查询订单id,并通过订单id查询订单明细,通过BeanUtils工具将Orders对象的属性复制到orderVO中,并将查询到的订单明细列表也赋值到orderVO中
取消订单
public void userCancelById(Long id) throws Exception {// 根据id查询订单Orders ordersDB = orderMapper.getById(id);// 校验订单是否存在if (ordersDB == null) {throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);}//订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消if (ordersDB.getStatus() > 2) {throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}Orders orders = new Orders();orders.setId(ordersDB.getId());// 更新订单状态、取消原因、取消时间orders.setStatus(Orders.CANCELLED);orders.setCancelReason("用户取消");orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);}
先检查订单是否存在,再检查订单状态是否能够取消(这里省略了退款情况,因为没做支付功能)
然后创建一个orders对象,将部分信息修改,这里是Update,只会修改有的,而没提到的菜品信息等仍然会保留
再来一单
service
/*** 再来一单** @param id*/public void repetition(Long id) {// 查询当前用户idLong userId = BaseContext.getCurrentId();// 根据订单id查询当前订单详情List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(id);// 将订单详情对象转换为购物车对象List<ShoppingCart> shoppingCartList = orderDetailList.stream().map(x -> {ShoppingCart shoppingCart = new ShoppingCart();// 将原订单详情里面的菜品信息重新复制到购物车对象中BeanUtils.copyProperties(x, shoppingCart, "id");shoppingCart.setUserId(userId);shoppingCart.setCreateTime(LocalDateTime.now());return shoppingCart;}).collect(Collectors.toList());// 将购物车对象批量添加到数据库shoppingCartMapper.insertBatch(shoppingCartList);}
使用Stream(流)操作,对orderDetailList中的每个元素进行映射操作,map会对OrderDetailList中的每个元素执行括号内的逻辑,再将其转换为ShoppingCart类型的对象
最后通过collect(Collectors.toList())将转换后的对象收集到一个List类型的shoppingCartList中
流操作
流操作 = 流水线工厂
想象你是一个工厂老板,有一批订单详情(比如汉堡、薯条、可乐的订单)需要变成购物车商品(给顾客结账用)。流操作就像是一条流水线,每个订单在流水线上走一遍,出来就变成了购物车商品。
-
stream()
- 把一个列表(订单详情)变成 “可以逐个处理的东西”,就像把一堆零件放到传送带上。
-
map()
- 对流水线上的每个东西做转换(比如把订单→购物车商品)。
x -> {...}
是一个箭头函数,表示 “对每个x
做这些操作”。
- 对流水线上的每个东西做转换(比如把订单→购物车商品)。
-
collect(Collectors.toList())
- 流水线的终点,把处理好的东西重新收集回一个列表。
订单搜索
/*** 订单搜索** @param ordersPageQueryDTO* @return*/public PageResult conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);// 部分订单状态,需要额外返回订单菜品信息,将Orders转化为OrderVOList<OrderVO> orderVOList = getOrderVOList(page);return new PageResult(page.getTotal(), orderVOList);}private List<OrderVO> getOrderVOList(Page<Orders> page) {// 需要返回订单菜品信息,自定义OrderVO响应结果List<OrderVO> orderVOList = new ArrayList<>();List<Orders> ordersList = page.getResult();if (!CollectionUtils.isEmpty(ordersList)) {for (Orders orders : ordersList) {// 将共同字段复制到OrderVOOrderVO orderVO = new OrderVO();BeanUtils.copyProperties(orders, orderVO);String orderDishes = getOrderDishesStr(orders);// 将订单菜品信息封装到orderVO中,并添加到orderVOListorderVO.setOrderDishes(orderDishes);orderVOList.add(orderVO);}}return orderVOList;}/*** 根据订单id获取菜品信息字符串** @param orders* @return*/private String getOrderDishesStr(Orders orders) {// 查询订单菜品详情信息(订单中的菜品和数量)List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());// 将每一条订单菜品信息拼接为字符串(格式:宫保鸡丁*3;)List<String> orderDishList = orderDetailList.stream().map(x -> {String orderDish = x.getName() + "*" + x.getNumber() + ";";return orderDish;}).collect(Collectors.toList());// 将该订单对应的所有菜品信息拼接在一起return String.join("", orderDishList);}
conditionSearch是对外暴露的订单搜索接口,负责分页查询并返回订单列表
调用getOrderVOList将Orders对象转换为OrderVO对象,其中包含更多展示字段
在gerOrderVOList方法中通过调用page.getResult就能拿到List
先判断是否为空,然后将所有值都复制到OrderVO中,再调用getOrderDishesStr方法将所有菜品信息拼接成特定格式的字符串
而getOrderDishesStr方法使用了Stream流,将所有菜品详情都转换成了字符串的格式,最后通过Stream.join拼接
检验收货地址是否超出配送范围
/*** 检查客户的收货地址是否超出配送范围* @param address*/private void checkOutOfRange(String address) {Map map = new HashMap();map.put("address",shopAddress);map.put("output","json");map.put("ak",ak);//获取店铺的经纬度坐标String shopCoordinate = HttpClientUtil.doGet("https://api.map.baidu.com/geocoding/v3", map);JSONObject jsonObject = JSON.parseObject(shopCoordinate);if(!jsonObject.getString("status").equals("0")){throw new OrderBusinessException("店铺地址解析失败");}//数据解析JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");String lat = location.getString("lat");String lng = location.getString("lng");//店铺经纬度坐标String shopLngLat = lat + "," + lng;map.put("address",address);//获取用户收货地址的经纬度坐标String userCoordinate = HttpClientUtil.doGet("https://api.map.baidu.com/geocoding/v3", map);jsonObject = JSON.parseObject(userCoordinate);if(!jsonObject.getString("status").equals("0")){throw new OrderBusinessException("收货地址解析失败");}//数据解析location = jsonObject.getJSONObject("result").getJSONObject("location");lat = location.getString("lat");lng = location.getString("lng");//用户收货地址经纬度坐标String userLngLat = lat + "," + lng;map.put("origin",shopLngLat);map.put("destination",userLngLat);map.put("steps_info","0");//路线规划String json = HttpClientUtil.doGet("https://api.map.baidu.com/directionlite/v1/driving", map);jsonObject = JSON.parseObject(json);if(!jsonObject.getString("status").equals("0")){throw new OrderBusinessException("配送路线规划失败");}//数据解析JSONObject result = jsonObject.getJSONObject("result");JSONArray jsonArray = (JSONArray) result.get("routes");Integer distance = (Integer) ((JSONObject) jsonArray.get(0)).get("distance");if(distance > 5000){//配送距离超过5000米throw new OrderBusinessException("超出配送范围");}}
先通过调用百度地图api获取店铺的经纬度坐标
然后解析json数据得到店铺的经纬度坐标
然后同理获取用户的经纬度坐标
然后解析json数据得到用户收货地址的经纬度坐标
解析json数据后得到店铺的经纬度坐标
然后把两地的经纬度坐标放到map中让百度地图进行路径规划
把规划好后的距离解析查看距离是否大于5000米,大于回复超出配送范围
记得修改submitOrder方法,在其中调用这个函数
总结
1.为什么在订单查询中使用PageResult和OrderVO对象
PageResult用于封装分页查询结果(包括总记录数和当前页数据),便于前端展示分页信息
OrderVO是专为前端展示设计的对象,在Orders实体的基础上扩展了额外字段,避免返回数据,提升接口性能。
2.Stream流在订单处理中主要是什么应用场景
对象转换:将OrderDetail转换为ShoppingCart对象
数据拼接:将订单菜品信息拼接成字符串
3.取消订单时为什么要校验订单状态?
订单状态决定了业务流程的合法性:
仅当状态为代付款和代接单才能被允许取消,比较已配送或已完成的订单被误操作.
4.”再来一单“功能如何实现从订单到购物车的转换?
通过Stream.map()遍历整个订单详情,将每个OrderDetail转换为ShoppingCart对象
利用BeanUtils.copyProperties复制基础属性
最终通过collect(Collectors.toList())收集为购物车列表,批量存入数据库
5.## 配送范围校验中,百度地图 API 的核心调用流程是什么?
地址解析:将店铺和用户地址转换为经纬度坐标。
路线规划:调用 driving 接口计算两地驾车距离。
距离判断:若距离超过 5000 米,抛出 “超出配送范围” 异常。
关键是确保 API 请求参数正确,并正确解析 JSON 响应中的 distance
字段。