当前位置: 首页 > news >正文

【项目实战 Day9】springboot + vue 苍穹外卖系统(用户端订单模块 + 商家端订单管理模块 完结)

一、用户端订单模块

1、查询历史订单 - GET接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 历史订单查询* @param page* @param pageSize* @param status 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消* @return*/@GetMapping("/historyOrders")@ApiOperation("历史订单查询")public Result<PageResult> page(int page,int pageSize,Integer status){PageResult pageResult = orderService.pageQueryHistory(page,pageSize,status);return Result.success(pageResult);}

【2】service层

  • 使用分页插件PageHelper设置分页参数
  • 创建OrdersPageQueryDTO,并绑定相应参数(userId、status),传入mapper层进行条件分页查询,获得相应的所有订单信息
  • 判断返回的订单信息是否为空,若不为空,遍历每一个订单信息order
    • 通过订单id在ordersDetailMapper中获取对应订单明细列表
    • 创建OrderVO,并将订单信息order赋值给OrderVO,再加上之前获取的对应订单明细列表
    • 接着将每一个封装好的OrderVO加入list列表
  • 最后返回PageResult(page.getTotal()订单总数,list)
    /*** 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消* @param pageNum* @param pageSize* @param status* @return*/public PageResult pageQueryHistory(int pageNum, int pageSize, Integer status) {//1.设置分页参数,使用分页插件开始分页PageHelper.startPage(pageNum,pageSize);//条件分页查询OrdersPageQueryDTO ordersPageQueryDTO = new OrdersPageQueryDTO();ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());ordersPageQueryDTO.setStatus(status);//返回分页结果(相当于返回所有历史订单数据)Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);//创建OrderVO列表用于封装返回数据List<OrderVO> list = new ArrayList<>();if(page != null && page.getTotal() > 0){//遍历分页中每一个订单对象for (Orders orders : page) {//获取当前订单idLong orderId = orders.getId();//用订单id去获取其对应的订单明细List<OrderDetail> dlist = orderDetailMapper.getByOrderId(orderId);//封装到VO//OrderVO继承了Orders中的所有数据(在OrderVO中存在extend Orders),因此可以将orders赋值给ordervoOrderVO orderVO = new OrderVO();BeanUtils.copyProperties(orders,orderVO);orderVO.setOrderDetailList(dlist);//封装好的VO加入结果列表中list.add(orderVO);}}return new PageResult(page.getTotal(),list);}

【3】mapper层

1)OrderMapper

    /*** 订单分页查询* @param ordersPageQueryDTO* @return*/Page<Orders> pageQuery(OrdersPageQueryDTO ordersPageQueryDTO);

2)OrderDetailMapper

    /*** 根据订单id查询对应订单明细* @param orderId* @return*/@Select("select * from sky_take_out.order_detail where order_id = #{orderId}")List<OrderDetail> getByOrderId(Long orderId);

【4】mybatis层

1)OrderMapper

    <select id="pageQuery" resultType="com.sky.entity.Orders">select * from sky_take_out.orders<where><if test="status != null">and status = #{status}</if><if test="userId != null">and user_id = #{userId}</if><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="beginTime != null">and order_time &gt;= #{beginTime}</if><if test="endTime != null">and order_time &lt;= #{endTime}</if></where>order by order_time desc</select>

2、查询订单详情 - GET接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 查询订单详情* @param id* @return*/@GetMapping("/orderDetail/{id}")@ApiOperation("查询订单详情")public Result<OrderVO> details(@PathVariable Long id){OrderVO orderVO = orderService.getById(id);return Result.success(orderVO);}

【2】service层

    /*** 查询订单详情* @param id* @return*/public OrderVO getById(Long id) {OrderVO orderVO = new OrderVO();Orders order = orderMapper.getById(id);BeanUtils.copyProperties(order,orderVO);List<OrderDetail> list = orderDetailMapper.getByOrderId(id);orderVO.setOrderDetailList(list);return orderVO;}

【3】mapper层

    /*** 根据订单id查询订单* @param id* @return*/@Select("select * from sky_take_out.orders where id = #{id}")Orders getById(Long id);

3、取消订单 - PUT接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 取消订单* @param id* @return*/@PutMapping("/cancel/{id}")@ApiOperation("取消订单")public Result cancel(@PathVariable Long id) throws Exception {orderService.cancel(id);return Result.success();}

【2】service层

取消订单的实质是修改订单信息(订单状态、取消原因、取消时间)

  • 判断订单是否存在
  • 判断订单状态是否可以取消【3已接单、4派送中、5已完成、6已取消】不可以取消
  • 如果订单处于待接单状态取消,需要进行退款并更新支付状态
  • 最后更新订单状态、取消原因、取消时间(用OrderMapper的update方法)
    /*** 取消订单* @param id*/public void cancel(Long id) throws Exception {//1.校验订单是否存在Orders od = orderMapper.getById(id);if(od == null){throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);}//2.订单若为【3已接单、4派送中、5已完成、6已取消】则不能取消if(od.getStatus() > 2){throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}//3.若订单处于待接单状态下取消,需要进行退款if(od.getStatus().equals(Orders.TO_BE_CONFIRMED)){
//            //调用微信支付退款接口
//            weChatPayUtil.refund(
//                    od.getNumber(), //商户订单号
//                    od.getNumber(), //商户退款单号
//                    new BigDecimal(0.01),//退款金额,单位 元
//                    new BigDecimal(0.01));//原订单金额od.setPayStatus(Orders.REFUND);}//4.更新订单状态、取消原因、取消时间od.setStatus(Orders.CANCELLED);od.setCancelReason("用户取消");od.setCancelTime(LocalDateTime.now());orderMapper.update(od);}

4、再来一单 - POST接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 再来一单* @param id* @return*/@PostMapping("/repetition/{id}")@ApiOperation("再来一单")public Result repetition(@PathVariable Long id){orderService.repetition(id);return Result.success();}

【2】service层

再来一单的业务逻辑就是把订单商品全部重新插入购物车中

  • 通过订单id获取订单详情列表
  • 循环将【订单中的每一个商品】赋值到【单个购物车记录对象】中
  • 循环将【单个购物车记录对象】加入购物车列表中
  • 把购物车列表通过shoppingCartMapper的insertBatch批量插入进购物车表
    /*** 再来一单* @param id*/public void repetition(Long id) {//再来一单逻辑就是原订单商品重新加入购物车Long currentId = BaseContext.getCurrentId();//1.获取该订单的订单详情列表List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(id);List<ShoppingCart> shoppingCartList = new ArrayList<>();//2.生成若干购物车商品对象,并加入购物车列表for (OrderDetail orderDetail : orderDetailList) {ShoppingCart shoppingCart = new ShoppingCart();// 将订单详情中的菜品信息重新复制到购物车对象中,"id" 参数表示忽略复制 id 字段(因为购物车需要生成新的id)BeanUtils.copyProperties(orderDetail,shoppingCart,"id");shoppingCart.setUserId(currentId);shoppingCart.setCreateTime(LocalDateTime.now());shoppingCartList.add(shoppingCart);}//第2部分等价写法:
//        // 将订单详情对象转换为购物车对象
//        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());//3.将购物车列表中的内容批量插入购物车表shoppingCartMapper.insertBatch(shoppingCartList);}

【3】mapper层

1)ShoppingCartMapper

    /*** 批量插入购物车表* @param shoppingCartList*/void insertBatch(List<ShoppingCart> shoppingCartList);

【4】mybatis层

1)ShoppingCartMapper

    <insert id="insertBatch" parameterType="list">insert into sky_take_out.shopping_cart(name, image, user_id, dish_id, setmeal_id, dish_flavor, amount, create_time)VALUES<foreach collection="shoppingCartList" item="sc" separator=",">(#{sc.name},#{sc.image},#{sc.userId},#{sc.dishId},#{sc.setmealId},#{sc.dishFlavor},#{sc.amount},#{sc.createTime})</foreach></insert>

二、商家端订单管理模块

1、订单搜索 - GET接口

(1)需求分析

  • 通过需求分析中【返回数据】我们可以知道,要返回的结果为【Orders】+【OrderVO中的orderDishes字符串】
  • 而且已知OrderVO中已经继承了Orders的数据,所以只需要把Orders的数据赋值给OrderVO,再加上订单包含菜品的字符串即可
  • 因此我们Service层需要封装好这部分返回数据

(2)代码开发

【1】controller层

    /*** 订单搜索* @param ordersPageQueryDTO* @return*/@GetMapping("/conditionSearch")@ApiOperation("订单搜索")public Result<PageResult> conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO){PageResult pageResult = orderService.conditionSearch(ordersPageQueryDTO);return Result.success(pageResult);}

【2】service层

这里处理逻辑其实就是分页查询的常规思路,只是将【打包数据】和【把订单菜品列表转换为字符串】两部分提取成两个函数

    /*** 订单搜索* @param ordersPageQueryDTO* @return*/public PageResult conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {//开始分页,查询分页参数PageHelper.startPage(ordersPageQueryDTO.getPage(),ordersPageQueryDTO.getPageSize());//Page<Orders> 对象包含了分页查询的完整结果Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);//把order + ordervo数据打包成list列表List<OrderVO> orderVOList = getOrderVOList(page);return new PageResult(page.getTotal(),orderVOList);}//把order + ordervo数据打包成list列表public List<OrderVO> getOrderVOList(Page<Orders> page){List<OrderVO> orderVOList = new ArrayList<>();List<Orders> ordersList = page.getResult();//ordersList != null && ordersList.size() > 0if(!CollectionUtils.isEmpty(ordersList)) {for (Orders orders : ordersList) {//把每一个订单取出来赋值给OrderVOOrderVO orderVO = new OrderVO();BeanUtils.copyProperties(orders,orderVO);//创建订单菜品字符串并加入OrderVOString str = getOrderDishesStr(orders);orderVO.setOrderDishes(str);//加入orderVO列表orderVOList.add(orderVO);}}return orderVOList;}//创建订单菜品字符串并加入OrderVOpublic String getOrderDishesStr(Orders orders){List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());// 将每一条订单菜品信息拼接为字符串(格式:宫保鸡丁*3;)List<String> orderDishList = orderDetailList.stream().map(x->{String str = x.getName() + "*" + x.getNumber();return str;}).collect(Collectors.toList());return String.join(" ",orderDishList);}

2、各个状态的订单数量统计 - GET接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 各个状态的订单数量统计* @return*/@GetMapping("/statistics")@ApiOperation("各个状态的订单数量统计")public Result<OrderStatisticsVO> statistics(){OrderStatisticsVO orderStatisticsVO = orderService.statistics();return Result.success(orderStatisticsVO);}

【2】service层

    /*** 各个状态的订单数量统计* 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消* @return*/public OrderStatisticsVO statistics() {OrderStatisticsVO orderStatisticsVO = new OrderStatisticsVO();Integer confirmed = orderMapper.getStatus(Orders.COMPLETED);Integer deliver = orderMapper.getStatus(Orders.DELIVERY_IN_PROGRESS);Integer tobeconfirmed = orderMapper.getStatus(Orders.TO_BE_CONFIRMED);orderStatisticsVO.setConfirmed(confirmed);orderStatisticsVO.setDeliveryInProgress(deliver);orderStatisticsVO.setToBeConfirmed(tobeconfirmed);return orderStatisticsVO;}

【3】mapper层

    /*** 根据状态查询订单数量* @param status* @return*/@Select("select count(id) from sky_take_out.orders where status = #{status}")int getStatus(Integer status);

3、查询订单详情 - GET接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 查询订单详情* @param id* @return*/@GetMapping("/details/{id}")@ApiOperation("查询订单详情")public Result<OrderVO> orderDetails(@PathVariable Long id){OrderVO orderVO = orderService.getDetailById(id);return Result.success(orderVO);}

【2】service层

    /*** 根据id查询订单详情* @param id* @return*/public OrderVO getDetailById(Long id) {OrderVO orderVO = new OrderVO();Orders orders = orderMapper.getById(id);List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(id);BeanUtils.copyProperties(orders,orderVO);orderVO.setOrderDetailList(orderDetailList);String str = getOrderDishesStr(orders);orderVO.setOrderDishes(str);return orderVO;}

4、接单 - PUT接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 接单* @param ordersConfirmDTO* @return*/@PutMapping("/confirm")@ApiOperation("接单")public Result confirm(@RequestBody OrdersConfirmDTO ordersConfirmDTO){orderService.confirm(ordersConfirmDTO);return Result.success();}

【2】service层

接单业务逻辑就是:修改订单状态

    /*** 接单* 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消* @param ordersConfirmDTO*/public void confirm(OrdersConfirmDTO ordersConfirmDTO) {Orders orders = Orders.builder().id(ordersConfirmDTO.getId()).status(Orders.CONFIRMED).build();orderMapper.update(orders);}

5、拒单 - PUT接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 拒单* @param ordersRejectionDTO* @return*/@PutMapping("/rejection")@ApiOperation("拒单")public Result reject(@RequestBody OrdersRejectionDTO ordersRejectionDTO) throws Exception {orderService.reject(ordersRejectionDTO);return Result.success();}

【2】service层

如果【订单不存在】或【订单状态不为“待接单”】,则需要抛出异常,其他业务逻辑也是修改订单信息 

    /*** 拒单* @param ordersRejectionDTO*/public void reject(OrdersRejectionDTO ordersRejectionDTO) throws Exception {Orders ordersDB = orderMapper.getById(ordersRejectionDTO.getId());//只有订单存在且状态为2(待接单)才可以拒单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 = Orders.builder().id(ordersRejectionDTO.getId()).status(Orders.CANCELLED).cancelTime(LocalDateTime.now()).rejectionReason(ordersRejectionDTO.getRejectionReason()).build();orderMapper.update(orders);}

6、取消订单 - PUT接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 商家取消订单* @param ordersCancelDTO* @return*/@PutMapping("/cancel")@ApiOperation("取消订单")public Result cancel(@RequestBody OrdersCancelDTO ordersCancelDTO) throws Exception {orderService.shopCancel(ordersCancelDTO);return Result.success();}

【2】service层

    /*** 商家取消订单* @param ordersCancelDTO*/public void shopCancel(OrdersCancelDTO ordersCancelDTO) {
//        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 = Orders.builder().id(ordersCancelDTO.getId()).status(Orders.CANCELLED).cancelReason(ordersCancelDTO.getCancelReason()).cancelTime(LocalDateTime.now()).build();orderMapper.update(orders);}

7、派送订单 - PUT接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 派送订单* @param id* @return*/@PutMapping("/delivery/{id}")@ApiOperation("派送订单")public Result delivery(@PathVariable Long id){orderService.delivery(id);return Result.success();}

【2】service层

    /*** 派送订单* @param id*/public void delivery(Long id) {Orders orderDB = orderMapper.getById(id);if(orderDB == null || !orderDB.getStatus().equals(Orders.CONFIRMED)){throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}Orders orders = Orders.builder().id(id).status(Orders.DELIVERY_IN_PROGRESS).build();orderMapper.update(orders);}

8、完成订单 - PUT接口

(1)需求分析

(2)代码开发

【1】controller层

    /*** 完成订单* @param id* @return*/@PutMapping("/complete/{id}")@ApiOperation("完成订单")public Result complete(@PathVariable Long id){orderService.complete(id);return Result.success();}

【2】service层

    /*** 完成订单* @param id*/public void complete(Long id) {Orders orderDB = orderMapper.getById(id);if(orderDB == null || !orderDB.getStatus().equals(Orders.DELIVERY_IN_PROGRESS)){throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}Orders orders = Orders.builder().id(id).status(Orders.COMPLETED).deliveryTime(LocalDateTime.now()).build();orderMapper.update(orders);}

三、校验收货地址是否超出配送范围

1、环境准备

  • 注册账号: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%E9%A6%96%E9%A1%B5
  • 登录百度地图开放平台:https://lbsyun.baidu.com/

进入控制台,创建应用,获取AK:

2、代码开发

(1)application.yml

(2)OrderServiceImpl

OrderServiceImpl中注入下面的配置项

    @Value("${sky.shop.address}")private String shopAddress;@Value("${sky.baidu.ak}")private String ak;

在OrderServiceImpl中提供校验方法

/*** 检查客户的收货地址是否超出配送范围* @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("超出配送范围");}}

在OrderServiceImpl的submitOrder方法中调用上面的校验方法

//检查用户的收货地址是否超出配送范围checkOutOfRange(addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail());

http://www.dtcms.com/a/438010.html

相关文章:

  • Mac 安装Neo4j教程
  • blender 解决shift快捷键和中英切换重复的问题
  • 网站动态图怎么做阳明拍卖公司网站
  • 01_Docker 部署 Ollama 模型(支持 NVIDIA GPU)
  • 苏州新区网站制作wordpress视频格式
  • 一位Android用户的科技漫游手记
  • android中调用相册
  • 安卓基础组件031-Retrofit 网络请求框架
  • Redis 黑马点评-商户查询缓存
  • Android geckoview 集成,JS交互,官方demo
  • 【APK安全】Android 权限校验核心风险与防御指南
  • 单调队列与单调栈
  • 设计与优化Java API:构建高效、可维护的接口
  • Locality Sensitive Hashing (LSH) 详解:高效检测语言语句重复的利器
  • 阿里云网站开发零起步如何做设计师
  • 后端开发基础概念MVC以及Entity,DAO,DO,DTO,VO等概念
  • 七大排序算法的基本原理
  • Gateway-过滤器
  • 科普:Python 中,字典的“动态创建键”特性
  • Java 21 或 JavaFX 打包 exe 之 GraalVM Native Image 方案
  • 1.2.3 MCP(Model Context Protocol)
  • dede网站栏目管理网络科技是做什么的
  • 《Gdb 调试实战指南:不同风格于VS下的一种调试模式》
  • lua虚拟机的垃圾回收机制
  • 网站建设需要的材料wordpress自带的404
  • MPAndroidChart 用法解析和性能优化 - Kotlin Java 双版本
  • Qt中使用日志---Log4Qt
  • linux centos 7 解决终端提示符出现-bash-4.2的问题
  • MCP模型上下文协议实战:个人应用项目如何集成MCP?
  • Vue--Vue基础(一)