# 用户端历史订单模块# 查询历史订单# 需求分析和设计产品原型:
业务规则
分页查询历史订单 可以根据订单状态查询 展示订单数据时,需要展示的数据包括:下单时间、订单状态、订单金额、订单明细(商品名称、图片) 接口设计:参见接口文档
# 代码实现# user/OrderController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @GetMapping("/historyOrders") @ApiOperation("历史订单查询") public Result<PageResult> page (int page, int pageSize, Integer status) { PageResult pageResult = orderService.pageQuery4User(page, pageSize, status); return Result.success(pageResult); }
# OrderService
1 2 3 4 5 6 7 8 PageResult pageQuery4User (int page, int pageSize, Integer status) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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 (); if (page != null && page.getTotal() > 0 ) { for (Orders orders : page) { Long orderId = orders.getId(); 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); }
# OrderMapper
1 2 3 4 5 Page<Orders> pageQuery (OrdersPageQueryDTO ordersPageQueryDTO) ;
# OrderMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <select id ="pageQuery" resultType ="Orders" > select * from orders <where > <if test ="number != null and number!=''" > and number like concat('%',#{number},'%') </if > <if test ="phone != null and phone!=''" > and phone like concat('%',#{phone},'%') </if > <if test ="userId != null" > and user_id = #{userId} </if > <if test ="status != null" > and status = #{status} </if > <if test ="beginTime != null" > and order_time > = #{beginTime} </if > <if test ="endTime != null" > and order_time < = #{endTime} </if > </where > order by order_time desc </select >
# OrderDetailMapper
1 2 3 4 5 6 7 @Select("select * from order_detail where order_id = #{orderId}") List<OrderDetail> getByOrderId (Long orderId) ;
# 查询订单详情# 需求分析和设计产品原型:
接口设计:参见接口文档
# 代码实现# user/OrderController
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/orderDetail/{id}") @ApiOperation("查询订单详情") public Result<OrderVO> details (@PathVariable("id") Long id) { OrderVO orderVO = orderService.details(id); return Result.success(orderVO); }
# OrderService
1 2 3 4 5 6 OrderVO details (Long id) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public OrderVO details (Long id) { Orders orders = orderMapper.getById(id); List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId()); OrderVO orderVO = new OrderVO (); BeanUtils.copyProperties(orders, orderVO); orderVO.setOrderDetailList(orderDetailList); return orderVO; }
# OrderMapper
1 2 3 4 5 6 @Select("select * from orders where id=#{id}") Orders getById (Long id) ;
# 取消订单# 需求分析和设计产品原型:
业务规则:
待支付和待接单状态下,用户可直接取消订单 商家已接单状态下,用户取消订单需电话沟通商家 派送中状态下,用户取消订单需电话沟通商家 如果在待接单状态下取消订单,需要给用户退款 取消订单后需要将订单状态修改为 “已取消” 接口设计:参见接口文档
# 代码实现# user/OrderController
1 2 3 4 5 6 7 8 9 10 11 @PutMapping("/cancel/{id}") @ApiOperation("取消订单") public Result cancel (@PathVariable("id") Long id) throws Exception { orderService.userCancelById(id); return Result.success(); }
# OrderService
1 2 3 4 5 void userCancelById (Long id) throws Exception;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public void userCancelById (Long id) throws Exception { Orders ordersDB = orderMapper.getById(id); if (ordersDB == null ) { throw new OrderBusinessException (MessageConstant.ORDER_NOT_FOUND); } if (ordersDB.getStatus() > 2 ) { throw new OrderBusinessException (MessageConstant.ORDER_STATUS_ERROR); } Orders orders = new Orders (); orders.setId(ordersDB.getId()); if (ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)) { weChatPayUtil.refund( ordersDB.getNumber(), ordersDB.getNumber(), new BigDecimal (0.01 ), new BigDecimal (0.01 )); orders.setPayStatus(Orders.REFUND); } orders.setStatus(Orders.CANCELLED); orders.setCancelReason("用户取消" ); orders.setCancelTime(LocalDateTime.now()); orderMapper.update(orders); }
# 再来一单# 需求分析和设计产品原型:
接口设计:参见接口文档
业务规则:
# 代码实现# user/OrderController
1 2 3 4 5 6 7 8 9 10 11 12 @PostMapping("/repetition/{id}") @ApiOperation("再来一单") public Result repetition (@PathVariable Long id) { orderService.repetition(id); return Result.success(); }
# OrderService
1 2 3 4 5 6 void repetition (Long id) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void repetition (Long id) { Long userId = BaseContext.getCurrentId(); 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); }
1 2 3 4 5 6 void insertBatch (List<ShoppingCart> shoppingCartList) ;
1 2 3 4 5 6 7 8 <insert id ="insertBatch" parameterType ="list" > insert into shopping_cart (name, image, user_id, dish_id, setmeal_id, dish_flavor, number, amount, create_time) values <foreach collection ="shoppingCartList" item ="sc" separator ="," > (#{sc.name},#{sc.image},#{sc.userId},#{sc.dishId},#{sc.setmealId},#{sc.dishFlavor},#{sc.number},#{sc.amount},#{sc.createTime}) </foreach > </insert >
# 商家端订单管理模块# 订单搜索# 需求分析和设计产品原型:
业务规则:
输入订单号 / 手机号进行搜索,支持模糊搜索 根据订单状态进行筛选 下单时间进行时间筛选 搜索内容为空,提示未找到相关订单 搜索结果页,展示包含搜索关键词的内容 分页展示搜索到的订单数据 接口设计:参见接口文档
# 代码实现# admin/OrderController在 admin 包下创建 OrderController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @RestController("adminOrderController") @RequestMapping("/admin/order") @Slf4j @Api(tags = "订单管理接口") public class OrderController { @Autowired private OrderService orderService; @GetMapping("/conditionSearch") @ApiOperation("订单搜索") public Result<PageResult> conditionSearch (OrdersPageQueryDTO ordersPageQueryDTO) { PageResult pageResult = orderService.conditionSearch(ordersPageQueryDTO); return Result.success(pageResult); } }
# OrderService
1 2 3 4 5 6 PageResult conditionSearch (OrdersPageQueryDTO ordersPageQueryDTO) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public PageResult conditionSearch (OrdersPageQueryDTO ordersPageQueryDTO) { PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize()); Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO); List<OrderVO> orderVOList = getOrderVOList(page); return new PageResult (page.getTotal(), orderVOList); } private List<OrderVO> getOrderVOList (Page<Orders> page) { List<OrderVO> orderVOList = new ArrayList <>(); List<Orders> ordersList = page.getResult(); if (!CollectionUtils.isEmpty(ordersList)) { for (Orders orders : ordersList) { OrderVO orderVO = new OrderVO (); BeanUtils.copyProperties(orders, orderVO); String orderDishes = getOrderDishesStr(orders); orderVO.setOrderDishes(orderDishes); orderVOList.add(orderVO); } } return orderVOList; } private String getOrderDishesStr (Orders orders) { List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId()); List<String> orderDishList = orderDetailList.stream().map(x -> { String orderDish = x.getName() + "*" + x.getNumber() + ";" ; return orderDish; }).collect(Collectors.toList()); return String.join("" , orderDishList); }
# 各个状态的订单数量统计# 需求分析和设计产品原型:
接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/statistics") @ApiOperation("各个状态的订单数量统计") public Result<OrderStatisticsVO> statistics () { OrderStatisticsVO orderStatisticsVO = orderService.statistics(); return Result.success(orderStatisticsVO); }
# OrderService
1 2 3 4 5 OrderStatisticsVO statistics () ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public OrderStatisticsVO statistics () { Integer toBeConfirmed = orderMapper.countStatus(Orders.TO_BE_CONFIRMED); Integer confirmed = orderMapper.countStatus(Orders.CONFIRMED); Integer deliveryInProgress = orderMapper.countStatus(Orders.DELIVERY_IN_PROGRESS); OrderStatisticsVO orderStatisticsVO = new OrderStatisticsVO (); orderStatisticsVO.setToBeConfirmed(toBeConfirmed); orderStatisticsVO.setConfirmed(confirmed); orderStatisticsVO.setDeliveryInProgress(deliveryInProgress); return orderStatisticsVO; }
# OrderMapper
1 2 3 4 5 6 @Select("select count(id) from orders where status = #{status}") Integer countStatus (Integer status) ;
# 查询订单详情# 需求分析和设计产品原型:
业务规则:
订单详情页面需要展示订单基本信息(状态、订单号、下单时间、收货人、电话、收货地址、金额等) 订单详情页面需要展示订单明细数据(商品名称、数量、单价) 接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/details/{id}") @ApiOperation("查询订单详情") public Result<OrderVO> details (@PathVariable("id") Long id) { OrderVO orderVO = orderService.details(id); return Result.success(orderVO); }
# 接单# 需求分析和设计产品原型:
业务规则:
接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 @PutMapping("/confirm") @ApiOperation("接单") public Result confirm (@RequestBody OrdersConfirmDTO ordersConfirmDTO) { orderService.confirm(ordersConfirmDTO); return Result.success(); }
# OrderService
1 2 3 4 5 6 void confirm (OrdersConfirmDTO ordersConfirmDTO) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 public void confirm (OrdersConfirmDTO ordersConfirmDTO) { Orders orders = Orders.builder() .id(ordersConfirmDTO.getId()) .status(Orders.CONFIRMED) .build(); orderMapper.update(orders); }
# 拒单# 需求分析和设计产品原型:
业务规则:
商家拒单其实就是将订单状态修改为 “已取消” 只有订单处于 “待接单” 状态时可以执行拒单操作 商家拒单时需要指定拒单原因 商家拒单时,如果用户已经完成了支付,需要为用户退款 接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 @PutMapping("/rejection") @ApiOperation("拒单") public Result rejection (@RequestBody OrdersRejectionDTO ordersRejectionDTO) throws Exception { orderService.rejection(ordersRejectionDTO); return Result.success(); }
# OrderService
1 2 3 4 5 6 void rejection (OrdersRejectionDTO ordersRejectionDTO) throws Exception;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public void rejection (OrdersRejectionDTO ordersRejectionDTO) throws Exception { Orders ordersDB = orderMapper.getById(ordersRejectionDTO.getId()); if (ordersDB == null || !ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)) { throw new OrderBusinessException (MessageConstant.ORDER_STATUS_ERROR); } Integer payStatus = ordersDB.getPayStatus(); if (payStatus == Orders.PAID) { String refund = weChatPayUtil.refund( ordersDB.getNumber(), ordersDB.getNumber(), new BigDecimal (0.01 ), new BigDecimal (0.01 )); log.info("申请退款:{}" , refund); } Orders orders = new Orders (); orders.setId(ordersDB.getId()); orders.setStatus(Orders.CANCELLED); orders.setRejectionReason(ordersRejectionDTO.getRejectionReason()); orders.setCancelTime(LocalDateTime.now()); orderMapper.update(orders); }
# 取消订单# 需求分析和设计产品原型:
业务规则:
取消订单其实就是将订单状态修改为 “已取消” 商家取消订单时需要指定取消原因 商家取消订单时,如果用户已经完成了支付,需要为用户退款 接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 @PutMapping("/cancel") @ApiOperation("取消订单") public Result cancel (@RequestBody OrdersCancelDTO ordersCancelDTO) throws Exception { orderService.cancel(ordersCancelDTO); return Result.success(); }
# OrderService
1 2 3 4 5 6 void cancel (OrdersCancelDTO ordersCancelDTO) throws Exception;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public void cancel (OrdersCancelDTO ordersCancelDTO) throws Exception { Orders ordersDB = orderMapper.getById(ordersCancelDTO.getId()); Integer payStatus = ordersDB.getPayStatus(); if (payStatus == 1 ) { String refund = weChatPayUtil.refund( ordersDB.getNumber(), ordersDB.getNumber(), new BigDecimal (0.01 ), new BigDecimal (0.01 )); log.info("申请退款:{}" , refund); } Orders orders = new Orders (); orders.setId(ordersCancelDTO.getId()); orders.setStatus(Orders.CANCELLED); orders.setCancelReason(ordersCancelDTO.getCancelReason()); orders.setCancelTime(LocalDateTime.now()); orderMapper.update(orders); }
# 派送订单# 需求分析和设计产品原型:
业务规则:
派送订单其实就是将订单状态修改为 “派送中” 只有状态为 “待派送” 的订单可以执行派送订单操作 接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 @PutMapping("/delivery/{id}") @ApiOperation("派送订单") public Result delivery (@PathVariable("id") Long id) { orderService.delivery(id); return Result.success(); }
# OrderService
1 2 3 4 5 6 void delivery (Long id) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void delivery (Long id) { Orders ordersDB = orderMapper.getById(id); if (ordersDB == null || !ordersDB.getStatus().equals(Orders.CONFIRMED)) { throw new OrderBusinessException (MessageConstant.ORDER_STATUS_ERROR); } Orders orders = new Orders (); orders.setId(ordersDB.getId()); orders.setStatus(Orders.DELIVERY_IN_PROGRESS); orderMapper.update(orders); }
# 完成订单# 需求分析和设计产品原型:
业务规则:
完成订单其实就是将订单状态修改为 “已完成” 只有状态为 “派送中” 的订单可以执行订单完成操作 接口设计:参见接口文档
# 代码实现# admin/OrderController
1 2 3 4 5 6 7 8 9 10 11 @PutMapping("/complete/{id}") @ApiOperation("完成订单") public Result complete (@PathVariable("id") Long id) { orderService.complete(id); return Result.success(); }
# OrderService
1 2 3 4 5 6 void complete (Long id) ;
# OrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void complete (Long id) { Orders ordersDB = orderMapper.getById(id); if (ordersDB == null || !ordersDB.getStatus().equals(Orders.DELIVERY_IN_PROGRESS)) { throw new OrderBusinessException (MessageConstant.ORDER_STATUS_ERROR); } Orders orders = new Orders (); orders.setId(ordersDB.getId()); orders.setStatus(Orders.COMPLETED); orders.setDeliveryTime(LocalDateTime.now()); orderMapper.update(orders); }
# 校验收货地址是否超出配送范围# 环境准备注册账号:https://passport.baidu.com/v2/?reg&tt=1671699340600&overseas=&gid=CF954C2-A3D2-417F-9FE6-B0F249ED7E33&tpl=pp&u=https%3A%2F%2Flbsyun.baidu.com%2Findex.php%3Ftitle%3D 首页
登录百度地图开放平台:https://lbsyun.baidu.com/
进入控制台,创建应用,获取 AK:
相关接口:
https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding
https://lbsyun.baidu.com/index.php?title=webapi/directionlite-v1
# 代码开发# application.yml配置外卖商家店铺地址和百度地图的 AK:
# OrderServiceImpl改造 OrderServiceImpl,注入上面的配置项:
1 2 3 4 5 @Value("${sky.shop.address}") private String shopAddress;@Value("${sky.baidu.ak}") private String ak;
在 OrderServiceImpl 中提供校验方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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 ){ throw new OrderBusinessException ("超出配送范围" ); } }
在 OrderServiceImpl 的 submitOrder 方法中调用上面的校验方法: