基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七)

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

    • 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 产品原型

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

菜品分页原型:

在这里插入图片描述

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

业务规则:

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

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

在这里插入图片描述
在这里插入图片描述

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; //分类idprivate 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;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//更新时间private LocalDateTime updateTime;//分类名称private String categoryName;//菜品关联的口味private List<DishFlavor> flavors = new ArrayList<>();
}
1.2.3 Controller层

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

	/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("菜品分页查询")public Result<PageResult> 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<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);//后绪步骤实现return new PageResult(page.getTotal(), page.getResult());}
1.2.6 Mapper层

在 DishMapper 接口中声明 pageQuery 方法:

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

在 DishMapper.xml 中编写SQL:

<select id="pageQuery" resultType="com.sky.vo.DishVO">select d.* , c.name as categoryName from dish d left outer join category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%',#{name},'%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desc
</select>

1.3 功能测试

1.3.2 前后端联调测试

启动nginx,访问 http://localhost

点击菜品管理

在这里插入图片描述

数据成功查出。

2. 删除菜品

2.1 需求分析和设计

2.1.1 产品原型

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

删除菜品原型:
在这里插入图片描述

业务规则:

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

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

在这里插入图片描述
在这里插入图片描述

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

2.1.3 表设计

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

在这里插入图片描述

注意事项:

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

2.2 代码开发

2.1.2 Controller层

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

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

在DishService接口中声明deleteBatch方法:

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

在DishServiceImpl中实现deleteBatch方法:

    @Autowiredprivate SetmealDishMapper setmealDishMapper;/*** 菜品批量删除** @param ids*/@Transactional//事务public void deleteBatch(List<Long> ids) {//判断当前菜品是否能够删除---是否存在起售中的菜品??for (Long id : ids) {Dish dish = dishMapper.getById(id);//后绪步骤实现if (dish.getStatus() == StatusConstant.ENABLE) {//当前菜品处于起售中,不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}}//判断当前菜品是否能够删除---是否被套餐关联了??List<Long> 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<Long> getSetmealIdsByDishIds(List<Long> dishIds);
}

SetmealDishMapper.xml

<mapper namespace="com.sky.mapper.SetmealDishMapper"><select id="getSetmealIdsByDishIds" resultType="java.lang.Long">select setmeal_id from setmeal_dish where dish_id in<foreach collection="dishIds" item="dishId" separator="," open="(" close=")">#{dishId}</foreach></select>
</mapper>

在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接口文档进行测试,也可以通过前后端联调测试,接下来,我们直接使用前后端联调测试

进入到菜品列表查询页面

在这里插入图片描述

对测试菜品进行删除操作

在这里插入图片描述

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

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

在这里插入图片描述

点击批量删除

在这里插入图片描述

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

3. 修改菜品

3.1 需求分析和设计

3.1.1 产品原型

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

修改菜品原型:

在这里插入图片描述

3.1.2 接口设计

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

接口:

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

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

1). 根据id查询菜品

在这里插入图片描述
在这里插入图片描述

2). 修改菜品

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

3.2 代码开发

3.2.1 根据id查询菜品实现

1). Controller层

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

    /*** 根据id查询菜品** @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询菜品")public Result<DishVO> 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<DishFlavor> dishFlavors = dishFlavorMapper.getByDishId(id);//后绪步骤实现//将查询到的数据封装到VODishVO 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<DishFlavor> 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<DishFlavor> 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 id="update">update dish<set><if test="name != null">name = #{name},</if><if test="categoryId != null">category_id = #{categoryId},</if><if test="price != null">price = #{price},</if><if test="image != null">image = #{image},</if><if test="description != null">description = #{description},</if><if test="status != null">status = #{status},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{updateUser},</if></set>where id = #{id}
</update>

3.3 功能测试

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

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

在这里插入图片描述

点击修改,回显成功

在这里插入图片描述

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

在这里插入图片描述
修改成功。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/205295.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

可视化大屏时代的到来:智慧城市管理的新思路

随着科技的不断发展&#xff0c;智能芯片作为一种新型的电子元件&#xff0c;被广泛应用于各个领域&#xff0c;其中智慧芯片可视化大屏是一种重要的应用形式。 一、智慧芯片可视化大屏的优势 智慧芯片可视化大屏是一种将智能芯片与大屏幕显示技术相结合的产品&#xff0c;山海…

[pyqt5]pyqt5设置窗口背景图片后上面所有图片都会变成和背景图片一样

pyqt5的控件所有都是集成widget&#xff0c;窗体设置背景图片后控件背景也会跟着改变&#xff0c;此时有2个办法。第一个办法显然我们可以换成其他方式设置窗口背景图片&#xff0c;而不是使用styleSheet样式表&#xff0c;网上有很多其他方法。还有个办法就是仍然用styleSheet…

redis---主从复制及哨兵模式(高可用)

主从复制 主从复制&#xff1a;主从复制是redis实现高可用的基础&#xff0c;哨兵模式和集群都是在主从复制的基础之上实现高可用。 主从负责的工作原理 1、主节点&#xff08;master&#xff09; 从节点&#xff08;slave&#xff09;组成&#xff0c;数据复制是单向的&a…

IP-Adapter:文本兼容图像提示适配器,用于文本到图像扩散模型

IP-Adapter这是一种有效且轻量级的适配器&#xff0c;用于实现预训练文本到图像扩散模型的图像提示功能。只有 22M 参数的 IP 适配器可以实现与微调图像提示模型相当甚至更好的性能。IP-Adapter 不仅可以推广到从同一基本模型微调的其他自定义模型&#xff0c;还可以推广到使用…

Linux小程序之进度条

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;自己能实现进度条 > 毒鸡汤&#xff1a; > …

js双击修改元素内容并提交到后端封装实现

前面发过一个版本了&#xff0c;后来又追加了些功能。重新发一版。新版支持select和radio。 效果图&#xff1a; 右上角带有绿标的&#xff0c;是可以修改的单元格。如果不喜欢显示绿标&#xff0c;可以传递参数时指定不显示&#xff0c;如果想改为其它颜色&#xff0c;也可以…

[Java]JUC并发编程

JUC并发编程 一、什么是JUC 使用到 java.util 工具包、包、分类 二、线程和进程 进程&#xff1a;一个正在运行的程序&#xff0c;QQ.exe Music.exe 程序的集合&#xff1b; 一个进程往往可以包含多个线程&#xff0c;至少包含一个&#xff01; Java默认有两个线程&#x…

浅学指针(3)

系列文章目录 文章目录 系列文章目录前言系列文章目录前言1. 字符指针变量2. 数组指针变量那数组指针变量应该是&#xff1a;存放的应该是数组的地址&#xff0c;能够指向数组的指针变量。2.2 数组指针变量怎么初始化总结&#xff1a;函数名就是地址&#xff0c;&函数名和直…

ubuntu22.04在线安装redis,可选择版本

安装脚本7.0.5版本 在线安装脚本&#xff0c;默认版本号是7.0.5&#xff0c;可以根据需要选择需要的版本进行下载编译安装 sudo apt-get install gcc -y sudo apt-get install pkg-config -y sudo apt-get install build-essential -y#安装redis rm -rf ./tmp.log systemctl …

AI4S Cup学习赛-中枢神经系统药物研发:药物筛选与优化

赛题介绍 链接&#xff1a;Bohrium 案例广场 (dp.tech) 中枢神经系统类疾病长期以来存在着重要的临床未满足需求。据统计&#xff0c;在当前人口老龄化趋势下&#xff0c;阿兹海默&#xff08;AD&#xff09;、帕金森病&#xff08;PD&#xff09;等神经退行性疾病和脑癌、中…

MySQL主从复制架构

MySQL主从复制架构 一、MySQL集群概述 ##1、集群的主要类型 高可用集群&#xff08;High Available Cluster&#xff0c;HA Cluster&#xff09; 高可用集群是指通过特殊的软件把独立的服务器连接起来&#xff0c;组成一个能够提供故障切换&#xff08;Fail Over&#xff09…

【前端系列】前端存档术之keep-alive

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

电子学会C/C++编程等级考试2022年09月(三级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:课程冲突 小 A 修了 n 门课程, 第 i 门课程是从第 ai 天一直上到第 bi 天。 定义两门课程的冲突程度为 : 有几天是这两门课程都要上的。 例如 a1=1,b1=3,a2=2,b2=4 时, 这两门课的冲突程度为 2。 现在你需要求的是这 n 门课…

如何设置Linux终端提示信息

如何设置Linux终端提示信息 1 方法一&#xff1a;只能在VSCode或者Pycharm终端显示提示信息2 方法二&#xff1a;只能在MobaXterm等远程软件上显示提示3 方法三&#xff1a;避免用户没看到上面的提示&#xff0c;上面两种都设置一下 在使用远程终端时&#xff0c;由于多用户使用…

Qt 软件调试(一) Log日志调试

终于这段时间闲下来了&#xff0c;可以系统的编写Qt软件调试的整个系列。前面零零星星的也有部分输出&#xff0c;但终究没有形成体系。借此机会&#xff0c;做一下系统的总结。慎独、精进~ 日志是有效帮助我们快速定位&#xff0c;找到程序异常点的实用方法。但是好的日志才能…

MATLAB | 官方举办的动图绘制大赛 | 第三周赛情回顾

MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展&#xff01;&#xff01; 很荣幸前三周都成为了阶段性获奖者~&#xff1a; https://ww2.mathworks.cn/matlabcentral/communitycontests/contests/6/entries/13382 https://ww2.mathworks.cn/mat…

实验一 SAS 基本操作和数据表的导入 2023-11-29

一、上机目的 熟悉SAS的集成环境并掌握它的基本操作。理解SAS程序的结构&#xff0c;理解其中的过程&#xff0c;过程选项&#xff0c;语句&#xff0c;语句选项等概念&#xff0c;掌握SAS编程技术。 二、上机内容 主要有SAS操作界面、SAS窗口操作、SAS菜单操作、SAS按钮操作…

【Java】泛型的简单使用

文章目录 一、包装类1.基本数据类型和对应的包装类2.自动装箱和自动拆箱3.手动装箱和手动拆箱 二、什么是泛型三、泛型的使用四、裸类型&#xff08;Raw Type&#xff09;五、泛型是如何编译的六、泛型的上界七、泛型方法总结 一、包装类 在了解泛型之前我们先了解什么是包装类…

对称加密与非对称加密的区别是什么?

对称加密与非对称加密的区别是什么&#xff1f; 对称加密概念&#xff1a;好处和坏处&#xff1a;基本原理 非对称加密概念&#xff1a;工作原理&#xff1a; 两者区别安全性处理速度密钥管理通信双方数量 对称加密 概念&#xff1a; 同一个密钥可以同时用来对信息进行加密和…

Flutter:多线程Isolate的简单使用

在flutter中如果要使用线程&#xff0c;需要借助Isolate来实现。 简介 在Flutter中&#xff0c;Isolate是一种轻量级的线程解决方案&#xff0c;用于在应用程序中执行并发任务。Isolate可以被认为是独立于主线程的工作单元&#xff0c;它们可以在后台执行任务而不会阻塞应用程…