菜品管理模块开发 -- 手把手教你做ssm+springboot入门后端项目黑马程序员瑞吉外卖(五)

news2024/11/28 2:53:26

文章目录

  • 前言
  • 一、文件上传下载
    • 1. 文件上传介绍
    • 2. 文件下载介绍
    • 3. 实现文件上传功能
    • 4. 实现文件下载功能
    • 5. 上传下载图片效果预览
  • 二、新增菜品
    • 1. 需求分析
    • 2. 数据模型
    • 3. 代码开发
    • 4. 功能测试
  • 三、菜品信息分页查询
    • 1. 需求分析
    • 2. 代码开发
    • 3. 功能测试
  • 四、修改菜品
    • 1. 需求分析
    • 2. 代码开发
    • 3. 功能测试
  • 总结


前言

为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

一、文件上传下载

1. 文件上传介绍

  • 文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程
  • 文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能
  • 文件上传时,对页面的form表单有如下要求
  1. method=“post” ---- 采用post方式提交数据
  2. enctype=“multipart/form-data” ---- 采用multipart格式上传文件
  3. type=“file” ---- 使用input的file控件上传
  4. 举例
<form method="post" action="/common/upload" enctype="multipart/form-data">
<input name="myFile" type="file"/>
<input type="submit" value="提交"/>
</form>
  • 目前一些前端组件库也提供了相应的上传组件,但是底层原理还是基于form表单的文件上传例如ElementUl中提供的upload上传组件

在这里插入图片描述

  • 服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
  1. commons-fileupload
  2. commons-io
  • Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如

在这里插入图片描述

2. 文件下载介绍

  • 文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程。
  • 通过浏览器进行文件下载,通常有两种表现形式:
  1. 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
  2. 直接在浏览器中打开
  • 通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程

3. 实现文件上传功能

1. 前端相关代码分析

在这里插入图片描述
2. 为了方便调试,将common/路径添加到过滤器

 //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**"
        };

3. 在application.yml文件中指定上传路径

reggie:
  path: D:\img\

4. 新建controller,编写相关方法

/**
 * 文件上传和下载
 */
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {

    @Value("${reggie.path}")
    private String basePath;

    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
        log.info(file.toString());

        //原始文件名
        String originalFilename = file.getOriginalFilename();//abc.jpg
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        //使用UUID重新生成文件名,防止文件名称重复造成文件覆盖
        String fileName = UUID.randomUUID().toString() + suffix;//dfsdfdfd.jpg

        //创建一个目录对象
        File dir = new File(basePath);
        //判断当前目录是否存在
        if(!dir.exists()){
            //目录不存在,需要创建
            dir.mkdirs();
        }

        try {
            //将临时文件转存到指定位置
            file.transferTo(new File(basePath + fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }
}

4. 实现文件下载功能

1. 前端相关代码分析

文件下载,页面端可以使用标签展示下载的图片

在这里插入图片描述

  • 在controller中编写下载方法的代码
/**
     * 文件下载
     * @param name
     * @param response
     */
    @GetMapping("/download")
    public void download(String name, HttpServletResponse response){

        try {
            //输入流,通过输入流读取文件内容
            FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));

            //输出流,通过输出流将文件写回浏览器
            ServletOutputStream outputStream = response.getOutputStream();

            response.setContentType("image/jpeg");

            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = fileInputStream.read(bytes)) != -1){
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }

            //关闭资源
            outputStream.close();
            fileInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

5. 上传下载图片效果预览

在这里插入图片描述

二、新增菜品

1. 需求分析

后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息

在这里插入图片描述

2. 数据模型

  • 新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish flavor表插入数据所以在新增菜品时,涉及到两个表:
  1. dish ---- 菜品表
  2. dish flavor ---- 菜品口味表

在这里插入图片描述

在这里插入图片描述

3. 代码开发

1. 在开发业务功能前,先将需要用到的类和接口基本结构创建好

  • 实体类DishFlavor
/**
菜品口味
 */
@Data
public class DishFlavor implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //菜品id
    private Long dishId;


    //口味名称
    private String name;


    //口味数据list
    private String value;


    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;


    //是否删除
    private Integer isDeleted;

}
  • Mapper接口 DishFlavorMapper
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}
  • 业务层接口 DishFlavorService
public interface DishFlavorService extends IService<DishFlavor> {
}

  • 业务层实现类 DishFlavorServicelmpl
@Service
public class DishFlavorServiceImpl extends ServiceImpl<DishFlavorMapper,DishFlavor> implements DishFlavorService {
}
  • 控制层 DishController
/**
 * 菜品管理
 */
@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {
     @Autowired
    private DishService dishService;

    @Autowired
    private DishFlavorService dishFlavorService;
}

2. 在开发代码之前,需要梳理一下新增菜品时前端页面和服务端的交互过程

  1. 页面 (backend/page/food/add.html) 发送 ajax 请求,请求服务端获取菜品分类数据并展示到下拉框中
  2. 页面发送请求进行图片上传,请求服务端将图片保存到服务器
  3. 页面发送请求进行图片下载,将上传的图片进行回显
  4. 点击保存按钮,发送ajax请求,将菜品相关数据以ison形式提交到服务端
  5. 开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可

3. 根据条件查询数据(CategoryController)

/**
     * 根据条件查询分类数据
     * @param category
     * @return
     */
    @GetMapping("/list")
    public R<List<Category>> list(Category category){
        //条件构造器
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        //添加条件
        queryWrapper.eq(category.getType() != null,Category::getType,category.getType());
        //添加排序条件
        queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);

        List<Category> list = categoryService.list(queryWrapper);
        return R.success(list);
    }

4. 前端相关页面解析
在这里插入图片描述

5. 创建编写 DishDto,用于封装页面提交的数据

DTO,全称为Data Transfer object,即数据传输对象,一般用于展示层与服务层之间的数据传输(与数据库表不是一 一对应)

@Data
public class DishDto extends Dish {
    //接收模拟提交数据中的flavors参数
    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

6. 在DishService接口方法

public interface DishService extends IService<Dish> {

    //新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish、dish_flavor
    public void saveWithFlavor(DishDto dishDto);
}

7. 具体实现类

@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品,同时保存对应的口味数据
     * @param dishDto
     */
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //保存菜品的基本信息到菜品表dish
        this.save(dishDto);

        Long dishId = dishDto.getId();//菜品id

        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());

        //保存菜品口味数据到菜品口味表dish_flavor
        dishFlavorService.saveBatch(flavors);

    } 
}

4. 功能测试

  1. 根据条件查询数据
    在这里插入图片描述
    2. 实现保存功能

在这里插入图片描述

在这里插入图片描述

三、菜品信息分页查询

1. 需求分析

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

在这里插入图片描述

2. 代码开发

  1. 在开发代码之前,需要梳理一下菜品分页查询时前端页面和服务端的交互过程
  1. 页面(backend/page/food/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据
  2. 页面发送请求,请求服务端进行图片下载,用于页面图片展示
  3. 开发菜品信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可

2. 开发对应的Controller方法

 /**
     * 菜品信息分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> page(int page,int pageSize,String name){

        //构造分页构造器对象
        Page<Dish> pageInfo = new Page<>(page,pageSize);
        Page<DishDto> dishDtoPage = new Page<>();

        //条件构造器
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        //添加过滤条件
        queryWrapper.like(name != null,Dish::getName,name);
        //添加排序条件
        queryWrapper.orderByDesc(Dish::getUpdateTime);

        //执行分页查询
        dishService.page(pageInfo,queryWrapper);

        //对象拷贝
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");

        List<Dish> records = pageInfo.getRecords();

        List<DishDto> list = records.stream().map((item) -> {
            DishDto dishDto = new DishDto();

            BeanUtils.copyProperties(item,dishDto);

            Long categoryId = item.getCategoryId();//分类id
            //根据id查询分类对象
            Category category = categoryService.getById(categoryId);

            if(category != null){
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }
            return dishDto;
        }).collect(Collectors.toList());

        dishDtoPage.setRecords(list);

        return R.success(dishDtoPage);
    }

3. 功能测试

在这里插入图片描述

四、修改菜品

1. 需求分析

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

在这里插入图片描述

2. 代码开发

1. 在开发代码之前,需要梳理一下修改菜品时前端页面 (add.html) 和服务端的交互过程

  1. 页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示
  2. 页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
  3. 页面发送请求,请求服务端进行图片下载,用于页图片回显
  4. 点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端
  5. 开发修改菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可

2. 在DishService中编写查询方法和更新菜品的接口方法

public interface DishService extends IService<Dish> {
    //...
    //根据id查询菜品信息和对应的口味信息
    public DishDto getByIdWithFlavor(Long id);

    //更新菜品信息,同时更新对应的口味信息
    public void updateWithFlavor(DishDto dishDto);
}

3. 实现类实现

public class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService {

    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品,同时保存对应的口味数据
     * @param dishDto
     */
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //保存菜品的基本信息到菜品表dish
        this.save(dishDto);

        Long dishId = dishDto.getId();//菜品id

        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());

        //保存菜品口味数据到菜品口味表dish_flavor
        dishFlavorService.saveBatch(flavors);

    }

    /**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    public DishDto getByIdWithFlavor(Long id) {
        //查询菜品基本信息,从dish表查询
        Dish dish = this.getById(id);

        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish,dishDto);

        //查询当前菜品对应的口味信息,从dish_flavor表查询
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dish.getId());
        List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(flavors);

        return dishDto;
    }

    @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        //更新dish表基本信息
        this.updateById(dishDto);

        //清理当前菜品对应口味数据---dish_flavor表的delete操作
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());

        dishFlavorService.remove(queryWrapper);

        //添加当前提交过来的口味数据---dish_flavor表的insert操作
        List<DishFlavor> flavors = dishDto.getFlavors();

        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);
    }
}

4.在controller中编写对应的查询方法和修改方法

/**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> get(@PathVariable Long id){

        DishDto dishDto = dishService.getByIdWithFlavor(id);

        return R.success(dishDto);
    }

    /**
     * 修改菜品
     * @param dishDto
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        dishService.updateWithFlavor(dishDto);

        return R.success("新增菜品成功");
    }

3. 功能测试

在这里插入图片描述

总结

欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.JAVA BIO深入剖析

highlight: arduino-light 1.JAVA BIO深入剖析 1.1 Java BIO 基本介绍 Java BIO 就是传统的 java io 编程&#xff0c;其相关的类和接口在 java.ioBIO(blocking I/O) &#xff1a; 同步阻塞&#xff0c;服务器实现模式为一个连接一个线程&#xff0c;即客户端有连接请求时服务器…

东莞-戴尔R540服务器故障告警处理方法

DELL PowerEdge R540服务器故障维修案例&#xff1a;&#xff08;看到文章就是缘分&#xff09; 客户名称&#xff1a;东莞市某街道管理中心 故障机型&#xff1a;DELL R540服务器 故障问题&#xff1a;DELL R540服务器无法开机&#xff0c;前面板亮黄灯&#xff0c;工程师通过…

PatchMatchNet运行dtu数据集、

文章目录 1 准备工作2 dtu数据集重建演示2.1 bash文件超参数解释2.2 lists/dtu解释1 准备工作 MVSNet、PatchMatchNet环境配置 数据集下载,准备好数据集,我的dtu数据集放在/PatchmatchNet-main/Data/testData/dtu/下,如图所示 进入mvs 环境:source activate mvs 2 dtu数据…

Appium 安卓环境的配置

目录 前言&#xff1a; 环境准备 写个脚本玩玩 前言&#xff1a; 在使用Appium进行安卓自动化测试之前&#xff0c;需要配置相应的安卓环境。 环境准备 为了避免走弯路&#xff0c;我们先要确保三点&#xff1a; Android SDK API > 17 (Additional features require …

14matlab数理统计 多项式的求根和根据根求多项式(matlab程序)

1.简述 分享一下通过多种不同的方法计算多项式的根。 数值根 使用代换法求根 特定区间内的根 符号根 数值根 roots 函数用于计算系数向量表示的单变量多项式的根。 例如&#xff0c;创建一个向量以表示多项式 x2−x−6&#xff0c;然后计算多项式的根。 p [1 -1 -6]; r …

利用Python和Selenium编程,实现定时自动检索特定网页,发现特定网页内容发生变化后,向管理员发送提醒邮件(一)

一、项目需求 要求爬取某单位网站&#xff0c;登录后台查看是否有新增“网友提问”&#xff0c;如果有新的提问&#xff0c;向特定邮箱发出提醒邮件。 二、项目分析 &#xff08;一&#xff09;判断是否可用爬虫爬取相关内容 首先查看该网站的robots.txt文件&#xff0c;发现…

SpringCloud(四)Hystrix服务降级、熔断、监控页面

一、服务熔断 官方文档&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.5.RELEASE/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients 我们知道&#xff0c;微服务之间是可以进行相互调用的&#xff0c;那么如果出现了…

YUM报错:Could not retrieve mirrorlist

​​​​​​背景说明 ESXI新安装CentOS7&#xff0c;无法执行yum命令 报错内容解决方案 1.报错说明&#xff1a;问题在于yum无法获取CentOS的软件仓库地址&#xff0c;导致无法找到合法的baseurl2.检查网络连接&#xff1a;确保服务器可以访问互联网&#xff0c;以及能够解析D…

C++ CEF库 源码编译及使用(VS2019)

源码编译 官网下载源码 CEF Automated Builds

linux下mmdetection安装

linux下mmdetection安装 使用 MIM 安装 MMEngine 和 MMCV。安装 MMDetection 前提条件是配置了pytorch18的linux环境 参考流程&#xff1a;配置pycharm环境 拷贝pycharm环境为mmdet conda create -n mmdet --clone torch18进入 conda activate mmdet使用 MIM 安装 MMEngine …

量化基础 PTQ QAT

简介 固定bit下的量化始终无法在Accuracy和 (FLOPs & Parameters)之间达到一个非常细粒度的trade-off&#xff0c;所以就需要混合精度量化(Mixed-Precision Quantization, MPQ)来对模型实现进一步的高效压缩 混合精度量化区别于混合精度训练这个概念&#xff0c;后者指的是…

c基本数据类型

关键字 charshort intintlong intfloatdouble 常量和变量 常量&#xff1a;在程序运行过程中&#xff0c;其值不可改变的量变量&#xff1a;其值可以改变的量称为变量 字符数据 字符常量 直接常量&#xff1a;用单引号括起来&#xff0c;如&#xff1a;‘a’,‘b’.转义字…

7.4Java EE——Bean的作用域

一、singleton作用域 Spring支持的5种作用域 作用域名城 描述 singleton 单例模式。在单例模式下&#xff0c;Spring 容器中只会存在一个共享的Bean实例&#xff0c; 所有对Bean的请求&#xff0c;只要请求的id&#xff08;或name&#xff09;与Bean的定义相匹配&#xff0…

msvc2017x64编译器编译项目报错”编译器的堆空间不足“错误 的解决方法

开发日常软件的时候&#xff0c;因为项目较大&#xff0c;模块较多&#xff0c;编译时&#xff0c;报错”编译器编译空间不足“&#xff0c;且常规方法无法消除的问题。有 opengl模块 占用内存大 尝试 尝试1   按照常规的&#xff0c;在pro里面加大资源配置&#xff1a; CONF…

记录一下uniapp开发中遇到的一些问题

概述 最近码代码的时候遇到一些问题&#xff0c;这里自己记录总结一下&#xff0c;供大家参考&#xff0c;说得不对的地方希望大家指出 大概介绍一下我用到的内容 用HbuilderX新建一个uni-app项目 &#xff0c;vue版本选的2&#xff0c;爬坑轻松一点移动端ui框架选了uView&…

C 知识积累 回车与换行 Linux C 语法分析

目录 回车与换行一.知其然二.知其所以然 关键字&#xff0c;操作符和函数区别1&#xff1a;关键字2&#xff1a;操作符3&#xff1a;函数 命令行参数argv原码补码补码加法 Linux C 语法分析结构体指针类型函数宏定义其他 const语法整理 回车与换行 一.知其然 \n是换行&#x…

智能电表远程抄表系统原理

智能电表远程抄表系统是现代智能电网建设的重要组成部分&#xff0c;它利用物联网技术实现电表数据的远程采集、传输和处理&#xff0c;提高了电力公司的抄表效率&#xff0c;同时也为用户提供了更加便捷、准确的用电服务。本文将从远程智能电表抄表系统的工作原理、特点、应用…

KDE项目近日发布了KDE Frameworks 5.108

导读KDE项目近日发布了KDE Frameworks 5.108&#xff0c;作为这个开源软件套件的最新版本&#xff0c;它由80多个Qt附加库组成&#xff0c;为KDE Plasma桌面环境和KDE应用程序提供常用功能。 KDE Frameworks 5.108在这里修复了Plasma桌面崩溃的问题&#xff0c;该问题发生在用中…

Openlayers layer 基础及重点内容讲解

图层就像是含有文字或图形等元素的图片,一张张按顺序叠放在一起,组合起来形成页面的最终效果。 在 openlayers 中,图层是使用 layer 对象表示的,主要有 WebGLPoints Layer、热度图(HeatMap Layer)、图片图层(Image Layer)、切片图层(Tile Layer)和 矢量图层(Vector Layer…

ShardingSphere分库分表实战之水平分表

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…