基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七)
作者:mmseoamin日期:2023-12-05

分页查询、删除和修改菜品

    • 1. 菜品分页查询
      • 1.1 需求分析和设计
        • 1.1.1 产品原型
        • 1.1.2 接口设计
        • 1.2 代码开发
          • 1.2.1 设计DTO类
          • 1.2.2 设计VO类
          • 1.2.3 Controller层
          • 1.2.4 Service层接口
          • 1.2.5 Service层实现类
          • 1.2.6 Mapper层
          • 1.3 功能测试
            • 1.3.2 前后端联调测试
            • 2. 删除菜品
              • 2.1 需求分析和设计
                • 2.1.1 产品原型
                • 2.1.2 接口设计
                • 2.1.3 表设计
                • 2.2 代码开发
                  • 2.1.2 Controller层
                  • 2.2.2 Service层接口
                  • 2.2.3 Service层实现类
                  • 2.2.4 Mapper层
                  • 2.3 功能测试
                  • 3. 修改菜品
                    • 3.1 需求分析和设计
                      • 3.1.1 产品原型
                      • 3.1.2 接口设计
                      • 3.2 代码开发
                        • 3.2.1 根据id查询菜品实现
                        • 3.2.1 修改菜品实现
                        • 3.3 功能测试

1. 菜品分页查询

1.1 需求分析和设计

1.1.1 产品原型

系统中的菜品数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。

菜品分页原型:

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第1张

在菜品列表展示时,除了菜品的基本信息(名称、售价、售卖状态、最后操作时间)外,还有两个字段略微特殊,第一个是图片字段 ,我们从数据库查询出来的仅仅是图片的名字,图片要想在表格中回显展示出来,就需要下载这个图片。第二个是菜品分类,这里展示的是分类名称,而不是分类ID,此时我们就需要根据菜品的分类ID,去分类表中查询分类信息,然后在页面展示。

业务规则:

  • 根据页码展示菜品信息
  • 每页展示10条数据
  • 分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询
    1.1.2 接口设计

    根据上述原型图,设计出相应的接口

    基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第2张

    基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第3张

    1.2 代码开发

    1.2.1 设计DTO类

    根据菜品分页查询接口定义设计对应的DTO:

    在sky-pojo模块中,已定义

    package com.sky.dto;
    @Data
    public class DishPageQueryDTO implements Serializable {
        private int page;
        private int pageSize;
        private String name;
        private Integer categoryId; //分类id
        private Integer status; //状态 0表示禁用 1表示启用
    }
    
    1.2.2 设计VO类

    根据菜品分页查询接口定义设计对应的VO:

    在sky-pojo模块中,已定义

    package com.sky.vo;
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class DishVO implements Serializable {
        private Long id;
        //菜品名称
        private String name;
        //菜品分类id
        private Long categoryId;
        //菜品价格
        private BigDecimal price;
        //图片
        private String image;
        //描述信息
        private String description;
        //0 停售 1 起售
        private Integer status;
        //更新时间
        private LocalDateTime updateTime;
        //分类名称
        private String categoryName;
        //菜品关联的口味
        private List flavors = new ArrayList<>();
    }
    
    1.2.3 Controller层

    根据接口定义创建DishController的page分页查询方法:

    	/**
         * 菜品分页查询
         *
         * @param dishPageQueryDTO
         * @return
         */
        @GetMapping("/page")
        @ApiOperation("菜品分页查询")
        public Result page(DishPageQueryDTO dishPageQueryDTO) {
            log.info("菜品分页查询:{}", dishPageQueryDTO);
            PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);//后绪步骤定义
            return Result.success(pageResult);
        }
    
    1.2.4 Service层接口

    在 DishService 中扩展分页查询方法:

    	/**
         * 菜品分页查询
         *
         * @param dishPageQueryDTO
         * @return
         */
        PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);
    
    1.2.5 Service层实现类

    在 DishServiceImpl 中实现分页查询方法:

    	/**
         * 菜品分页查询
         *
         * @param dishPageQueryDTO
         * @return
         */
        public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
            PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
            Page page = dishMapper.pageQuery(dishPageQueryDTO);//后绪步骤实现
            return new PageResult(page.getTotal(), page.getResult());
        }
    
    1.2.6 Mapper层

    在 DishMapper 接口中声明 pageQuery 方法:

    	/**
         * 菜品分页查询
         *
         * @param dishPageQueryDTO
         * @return
         */
        Page pageQuery(DishPageQueryDTO dishPageQueryDTO);
    

    在 DishMapper.xml 中编写SQL:

    
    

    1.3 功能测试

    1.3.2 前后端联调测试

    启动nginx,访问 http://localhost

    点击菜品管理

    基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第4张

    数据成功查出。

    2. 删除菜品

    2.1 需求分析和设计

    2.1.1 产品原型

    在菜品列表页面,每个菜品后面对应的操作分别为修改、删除、停售,可通过删除功能完成对菜品及相关的数据进行删除。

    删除菜品原型:

    基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第5张

    业务规则:

    • 可以一次删除一个菜品,也可以批量删除菜品
    • 起售中的菜品不能删除
    • 被套餐关联的菜品不能删除
    • 删除菜品后,关联的口味数据也需要删除掉
      2.1.2 接口设计

      根据上述原型图,设计出相应的接口。

      基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第6张

      基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第7张

      注意:删除一个菜品和批量删除菜品共用一个接口,故ids可包含多个菜品id,之间用逗号分隔。

      2.1.3 表设计

      在进行删除菜品操作时,会涉及到以下三张表。

      基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第8张

      注意事项:

      • 在dish表中删除菜品基本数据时,同时,也要把关联在dish_flavor表中的数据一块删除。
      • setmeal_dish表为菜品和套餐关联的中间表。
      • 若删除的菜品数据关联着某个套餐,此时,删除失败。
      • 若要删除套餐关联的菜品数据,先解除两者关联,再对菜品进行删除。

        2.2 代码开发

        2.1.2 Controller层

        根据删除菜品的接口定义在DishController中创建方法:

        	/**
             * 菜品批量删除
             *
             * @param ids
             * @return
             */
            @DeleteMapping
            @ApiOperation("菜品批量删除")
            public Result delete(@RequestParam List ids) {
                log.info("菜品批量删除:{}", ids);
                dishService.deleteBatch(ids);//后绪步骤实现
                return Result.success();
            }
        
        2.2.2 Service层接口

        在DishService接口中声明deleteBatch方法:

        	/**
             * 菜品批量删除
             *
             * @param ids
             */
            void deleteBatch(List ids);
        
        2.2.3 Service层实现类

        在DishServiceImpl中实现deleteBatch方法:

            @Autowired
            private SetmealDishMapper setmealDishMapper;
        	/**
             * 菜品批量删除
             *
             * @param ids
             */
            @Transactional//事务
            public void deleteBatch(List ids) {
                //判断当前菜品是否能够删除---是否存在起售中的菜品??
                for (Long id : ids) {
                    Dish dish = dishMapper.getById(id);//后绪步骤实现
                    if (dish.getStatus() == StatusConstant.ENABLE) {
                        //当前菜品处于起售中,不能删除
                        throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
                    }
                }
                //判断当前菜品是否能够删除---是否被套餐关联了??
                List setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);
                if (setmealIds != null && setmealIds.size() > 0) {
                    //当前菜品被套餐关联了,不能删除
                    throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
                }
                //删除菜品表中的菜品数据
                for (Long id : ids) {
                    dishMapper.deleteById(id);//后绪步骤实现
                    //删除菜品关联的口味数据
                    dishFlavorMapper.deleteByDishId(id);//后绪步骤实现
                }
            }
        
        2.2.4 Mapper层

        在DishMapper中声明getById方法,并配置SQL:

        	/**
             * 根据主键查询菜品
             *
             * @param id
             * @return
             */
            @Select("select * from dish where id = #{id}")
            Dish getById(Long id);
        

        创建SetmealDishMapper,声明getSetmealIdsByDishIds方法,并在xml文件中编写SQL:

        package com.sky.mapper;
        @Mapper
        public interface SetmealDishMapper {
            /**
             * 根据菜品id查询对应的套餐id
             *
             * @param dishIds
             * @return
             */
            //select setmeal_id from setmeal_dish where dish_id in (1,2,3,4)
            List getSetmealIdsByDishIds(List dishIds);
        }
        

        SetmealDishMapper.xml

        
            
        
        

        在DishMapper.java中声明deleteById方法并配置SQL:

        	/**
             * 根据主键删除菜品数据
             *
             * @param id
             */
            @Delete("delete from dish where id = #{id}")
            void deleteById(Long id);
        

        在DishFlavorMapper中声明deleteByDishId方法并配置SQL:

            /**
             * 根据菜品id删除对应的口味数据
             * @param dishId
             */
            @Delete("delete from dish_flavor where dish_id = #{dishId}")
            void deleteByDishId(Long dishId);
        

        2.3 功能测试

        既可以通过Swagger接口文档进行测试,也可以通过前后端联调测试,接下来,我们直接使用前后端联调测试。

        进入到菜品列表查询页面

        基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第9张

        对测试菜品进行删除操作

        基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第10张

        同时,进到dish表和dish_flavor两个表查看测试菜品的相关数据都已被成功删除。

        再次,删除状态为启售的菜品

        基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第11张

        点击批量删除

        基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第12张

        删除失败,因为起售中的菜品不能删除。

        3. 修改菜品

        3.1 需求分析和设计

        3.1.1 产品原型

        在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,最后点击保存按钮完成修改操作。

        修改菜品原型:

        基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第13张

        3.1.2 接口设计

        通过对上述原型图进行分析,该页面共涉及4个接口。

        接口:

        • 根据id查询菜品
        • 根据类型查询分类(已实现)
        • 文件上传(已实现)
        • 修改菜品

          我们只需要实现根据id查询菜品和修改菜品两个接口,接下来,我们来重点分析这两个接口。

          1). 根据id查询菜品

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第14张

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第15张

          2). 修改菜品

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第16张

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第17张

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第18张

          注:因为是修改功能,请求方式可设置为PUT。

          3.2 代码开发

          3.2.1 根据id查询菜品实现

          1). Controller层

          根据id查询菜品的接口定义在DishController中创建方法:

              /**
               * 根据id查询菜品
               *
               * @param id
               * @return
               */
              @GetMapping("/{id}")
              @ApiOperation("根据id查询菜品")
              public Result getById(@PathVariable Long id) {
                  log.info("根据id查询菜品:{}", id);
                  DishVO dishVO = dishService.getByIdWithFlavor(id);//后绪步骤实现
                  return Result.success(dishVO);
              }
          

          2). Service层接口

          在DishService接口中声明getByIdWithFlavor方法:

          	/**
               * 根据id查询菜品和对应的口味数据
               *
               * @param id
               * @return
               */
              DishVO getByIdWithFlavor(Long id);
          

          3). Service层实现类

          在DishServiceImpl中实现getByIdWithFlavor方法:

          	/**
               * 根据id查询菜品和对应的口味数据
               *
               * @param id
               * @return
               */
              public DishVO getByIdWithFlavor(Long id) {
                  //根据id查询菜品数据
                  Dish dish = dishMapper.getById(id);
                  //根据菜品id查询口味数据
                  List dishFlavors = dishFlavorMapper.getByDishId(id);//后绪步骤实现
                  //将查询到的数据封装到VO
                  DishVO dishVO = new DishVO();
                  BeanUtils.copyProperties(dish, dishVO);
                  dishVO.setFlavors(dishFlavors);
                  return dishVO;
              }
          

          4). Mapper层

          在DishFlavorMapper中声明getByDishId方法,并配置SQL:

              /**
               * 根据菜品id查询对应的口味数据
               * @param dishId
               * @return
               */
              @Select("select * from dish_flavor where dish_id = #{dishId}")
              List getByDishId(Long dishId);
          
          3.2.1 修改菜品实现

          1). Controller层

          根据修改菜品的接口定义在DishController中创建方法:

          	/**
               * 修改菜品
               *
               * @param dishDTO
               * @return
               */
              @PutMapping
              @ApiOperation("修改菜品")
              public Result update(@RequestBody DishDTO dishDTO) {
                  log.info("修改菜品:{}", dishDTO);
                  dishService.updateWithFlavor(dishDTO);
                  return Result.success();
              }
          

          2). Service层接口

          在DishService接口中声明updateWithFlavor方法:

          	/**
               * 根据id修改菜品基本信息和对应的口味信息
               *
               * @param dishDTO
               */
              void updateWithFlavor(DishDTO dishDTO);
          

          3). Service层实现类

          在DishServiceImpl中实现updateWithFlavor方法:

          	/**
               * 根据id修改菜品基本信息和对应的口味信息
               *
               * @param dishDTO
               */
              public void updateWithFlavor(DishDTO dishDTO) {
                  Dish dish = new Dish();
                  BeanUtils.copyProperties(dishDTO, dish);
                  //修改菜品表基本信息
                  dishMapper.update(dish);
                  //删除原有的口味数据
                  dishFlavorMapper.deleteByDishId(dishDTO.getId());
                  //重新插入口味数据
                  List flavors = dishDTO.getFlavors();
                  if (flavors != null && flavors.size() > 0) {
                      flavors.forEach(dishFlavor -> {
                          dishFlavor.setDishId(dishDTO.getId());
                      });
                      //向口味表插入n条数据
                      dishFlavorMapper.insertBatch(flavors);
                  }
              }
          

          4). Mapper层

          在DishMapper中,声明update方法:

          	/**
               * 根据id动态修改菜品数据
               *
               * @param dish
               */
              @AutoFill(value = OperationType.UPDATE)
              void update(Dish dish);
          

          并在DishMapper.xml文件中编写SQL:

          
                  update dish
                  
                      name = #{name},
                      category_id = #{categoryId},
                      price = #{price},
                      image = #{image},
                      description = #{description},
                      status = #{status},
                      update_time = #{updateTime},
                      update_user = #{updateUser},
                  
                  where id = #{id}
          
          

          3.3 功能测试

          通过前后端联调测试 ,可使用Debug方式启动项目,观察运行中步骤。

          进入菜品列表查询页面,对第一个菜品的价格进行修改

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第19张

          点击修改,回显成功

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第20张

          菜品价格修改后,点击保存

          基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七),在这里插入图片描述,第21张

          修改成功。

          后记

          👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹

上一篇:spring6概述

下一篇:9 Go的结构体