目录

  • 模块简介
  • 文件的上传与下载
    • 文件上传
    • 文件下载
    • 代码实现
  • 新增菜品
    • 需求分析
    • 代码分析
    • 代码实现
  • 菜品信息分页查询
    • 需求分析
    • 代码分析
    • 代码实现
  • 修改菜品
    • 需求分析
    • 代码分析
    • 代码开发
  • 总结

模块简介

本模块主要的开发任务分为以下几个方面

  • 文件的上传与下载
  • 新增菜品
  • 菜品信息分页查询
  • 修改菜品

模块内容大致与之前内容相似,只有一个新内容就是文件的上传与下载
那么什么是文件的上传与下载呢?

文件上传与下载相信生活中大家经常使用。在QQ、微信中,我们可以上传图片、音频等信息,而接收者可以对这些文件进行浏览和下载。这里我们就是要实现这个功能。

文件的上传与下载

文件上传

文件上传,也称为upload,是指将本地图片视频上传到服务器上,可以提供给其他用户浏览和下载的过程。

文件上传时,对于页面的form表单有如下要求

  • method = "post" 采用post方式提交表格数据
  • enctype = "multipart/form-data" 采用multipart格式上传文件
  • type = "file" 使用input的file控件上传

例如

<form method="post" action="/common/upload" enctype="mutipart/form-data"><input name="myFile" type="file"/><input type="submit" value="提交"/>
</form>

服务端要接收客户端页面上传的文件,通常会使用Apache的这两个组件

  • commons-fileupload
  • commons-io

spring框架在spring-web包中对文件上传进行了封装,只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件

@PostMapping("/upload")
public Result upload(MultipartFile file){System.out.println(file);return null;
}

文件下载

文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程

通过浏览器进行文件下载,通常有两种表现形式

  • 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
  • 直接在浏览器中打开

通过浏览器进行下载,本质就是服务端将文件以流的形式写回浏览器的过程

代码实现

1.在yml文件中设置上传文件的储存地址,实际开发应该储存到服务器
2. 在controller包下创建CommonController
3. 在类中编写上传和下载的实现代码

yml文件(添加)

#文件路径可以自己选择
reggie:path: D:\img\

CommonController

package com.cjgn.contorller;import com.cjgn.common.Code;
import com.cjgn.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;/*** 进行文件的上传和下载*/
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {//自定义的配置文件中的路径@Value("${reggie.path}")private String basePath;/*** 文件上传* @param file* @return*/@PostMapping("/upload")//file要和前端传回文件的name属性相同,例如前端传回文件name = "file"//file是一个临时文件,需要转存,否则本次请求完成后文件就会删除public Result upload(MultipartFile file){//获得原始文件名String originalFilename = file.getOriginalFilename();//abc.jpg//截取原始文件名的后缀String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));//使用UUID重新生成文件名字,防止文件重名覆盖文件,并加上后缀String fileName   = UUID.randomUUID().toString()+suffix;//创建一个目录对象File dir = new File(basePath);//判断目录是否存在if(!dir.exists()){//不存在就创建目录dir.mkdirs();}try {//临时文件转存到指定位置file.transferTo(new File(basePath+fileName));}catch (IOException e) {e.printStackTrace();}//返回文件名字return new Result(fileName, Code.OK,"上传成功");}/*** 下载文件* @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的返回格式为图片response.setContentType("image/jpeg");//使用bytes数组存数据int lens = 0;byte[] bytes = new byte[1024];while ((lens = fileInputStream.read(bytes))!=-1){outputStream.write(bytes,0,lens);//刷新outputStream.flush();}//关闭流fileInputStream.close();outputStream.close();}catch (Exception e) {e.printStackTrace();}}
}

新增菜品

需求分析

  • 通过新增功能来添加一个菜品,添加菜品时要选择菜品分类
  • 需要上传菜品的图片,移动端会根据分类展示菜品信息

代码分析

  • 点击新增菜品后,向CategoryController发送请求,查询菜品分类
  • 选择完分类,填完信息,上传图片后,点击新增菜品
  • 新增菜品后,发送ajax请求,调用DishController并传输数据,图片的数据传输为图片的名字
  • DishController调用DishService,处理数据信息,返回一个布尔值
  • DishService调用DishMapperDishFlavorMapper把信息保存到dish和dish_flavor表
  • 注意:因为在一个Controller中实现了向两个表中添加数据的操作,此时的形参是不能同时接受两个表的属性的。需要设置一个新类,同时包含两个表的属性。
  • 因为是对两张表的操作,还需要添加事务

代码实现

本次代码涉及的模块较多,采用按包分类来展示

  1. 启动类(加入如下注解)
@EnableTransactionManagement
  1. entity包

DishFlavor.java

package com.cjgn.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;/**菜品口味*/
@Data
public class DishFlavor implements Serializable {private static final long serialVersionUID = 1L;private Long id;//菜品idprivate Long dishId;//口味名称private String name;//口味数据listprivate 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;}
  1. dto包 (一个新的包,用来放新的实体类,包含两个实体类属性)

DishDto.java

package com.cjgn.dto;import com.cjgn.entity.Dish;
import com.cjgn.entity.DishFlavor;
import lombok.Data;import java.util.ArrayList;
import java.util.List;@Data
public class DishDto extends Dish {//DishFlavor的集合private List<DishFlavor> flavors = new ArrayList<>();private String categoryName;private Integer copies;}
  1. controller包

CategoryController.java (新增)

 /*** 获取分类的列表,用于在添加页面展示* @param category* @return*/@GetMapping("/list")public Result getList(Category category){List<Category> categories = categoryService.getList(category);Integer code = categories!=null? Code.OK:Code.ERR;String msg = categories!=null?"查询成功":"查询失败";return new Result(categories,code,msg);}

DishContorller.java

package com.cjgn.contorller;import com.cjgn.common.Code;
import com.cjgn.common.Result;
import com.cjgn.dto.DishDto;
import com.cjgn.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 用来处理对dish和dish_flavor表的操作*/
@RestController
@RequestMapping("/dish")
@Slf4j
public class DishContorller {@Autowiredprivate DishService dishService;/*** 新增菜品* @param dishDto* @return*/@PostMappingpublic Result insert(@RequestBody DishDto dishDto){log.info(dishDto.toString());dishService.insertWithFlavor(dishDto);return new Result(Code.OK,"添加成功");}
}
  1. mapper包

DishFlavorMapper.java

package com.cjgn.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cjgn.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {}
  1. service包

CategoryService.java (新增)

 /*** 获取分类的列表,用于在添加页面展示* @param category* @return*/public List<Category> getList(Category category);

CategoryServiceImpl.java(新增)

 /*** 获取分类的列表,用于在添加页面展示* @param category* @return*/@Overridepublic List<Category> getList(Category category) {LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<Category>();//获取type值Integer type = category.getType();//设置相等条件wrapper.eq(type!=null,Category::getType,type);//设置排序条件wrapper.orderByAsc(Category::getSort).orderByDesc(Category::getCreateTime);//查询数据List<Category> categories = categoryMapper.selectList(wrapper);return categories;}

DishService.java

package com.cjgn.service;import com.cjgn.dto.DishDto;
import org.springframework.transaction.annotation.Transactional;public interface DishService {/*** 新增菜品,同时插入菜品对应的口味数据,需要操作两张表dish和dish_flavor* 开启事务*/@Transactionalpublic void insertWithFlavor(DishDto dishDto);
}

DishServiceImpl.java

package com.cjgn.service.impl;import com.cjgn.dto.DishDto;
import com.cjgn.entity.DishFlavor;
import com.cjgn.mapper.DishFlavorMapper;
import com.cjgn.mapper.DishMapper;
import com.cjgn.service.DishService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.stream.Collectors;/*** 菜品的业务层*/
@Service
public class DishServiceImpl implements DishService {@Autowiredprivate CategoryMapper categoryMapper;@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;/*** 新增菜品,同时插入菜品对应的口味数据,需要操作两张表dish和dish_flavor* @param dishDto*/@Overridepublic void insertWithFlavor(DishDto dishDto) {//保存菜品的基本信息到菜品表dishMapper.insert(dishDto);//获取雪花算法自动生成的dish表的idLong id = dishDto.getId();//保存菜品口味信息到dish_flavorList<DishFlavor> flavors = dishDto.getFlavors();//向集合中注入id的值flavors.stream().map((item) -> {//注入iditem.setDishId(id);//保存到dish_flavordishFlavorMapper.insert(item);return item;}).collect(Collectors.toList());}
}

DishFlavorService.java

package com.cjgn.service;
public interface DishFlavorService {}

DishFlavorServiceImpl.java

package com.cjgn.service.impl;import com.cjgn.service.DishFlavorService;
import org.springframework.stereotype.Service;@Service
public class DishFlavorServiceImpl implements DishFlavorService {}

菜品信息分页查询

在查询之前,先把资料中的图片信息,复制到你设置的yml文件中保存图片的位置

需求分析

  • 分页查询已经实现很多次,本次需求主要提出一些特别的需求
  • 本次的分页查询,需要通过文件下载来展示图片
  • 要通过菜品表中的分类id,去查询此分类id对应的分类名称,并显示到前端

代码分析

  • 前端传输查询页面,页面大小等数据给服务端
  • 页面发送请求,请求服务端进行图片的下载
  • 服务端接收请求后,调用service向dish和dish_flavor查询数据
  • 按照菜品分类的id找出查询菜品分类的名称
  • 把查询的数据和图片展示到客户端

代码实现

DishController.java(添加)

 /***菜品信息分页查询* @param page 第几页* @param pageSize 页面大小* @param name 菜品的名称* @return*/@GetMapping("/page")public Result getByPage(int page,int pageSize,String name){//调用service进行查询Page byPage = dishService.getByPage(page, pageSize, name);return new Result(byPage,Code.OK,"查询成功");}

DishService.java

 /*** 分页查询菜品信息* @param page 第几页* @param pageSize 页面大小* @param name 菜品名称*/public Page getByPage(int page, int pageSize, String name);

DishServiceImpl.java

这段代码比较复杂,大致的思路为:

  1. 将菜品的信息查询到Dish泛型的Page1中
  2. 将Page1的信息除了records以外的值复制到DishDto泛型的Page2中
  3. 将Page1的records进行复制给DishDto泛型的List,并添加categoryName的值到集合中
  4. 将List注入到Page2中,将Page2返回给DishController
 /*** 分页查询菜品信息* @param page 第几页* @param pageSize 页面大小* @param name 菜品名称*/@Overridepublic Page getByPage(int page, int pageSize, String name) {//用来查询Page<Dish> iPage = new Page(page,pageSize);//用来输出Page<DishDto> dtoPage = new Page<>(page,pageSize);//设置菜品的分页查询的条件LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<Dish>();//设置模糊查询wrapper.like(name!=null,Dish::getName,name);//按照更新时间降序排序wrapper.orderByDesc(Dish::getUpdateTime);//查询dishMapper.selectPage(iPage,wrapper);//对象拷贝,忽视recordsBeanUtils.copyProperties(iPage,dtoPage,"records");List<Dish> records = iPage.getRecords();//注入dishdto的list集合的值List<DishDto> dishDtos = records.stream().map((item)->{//新建dto对象DishDto dishDto = new DishDto();//拷贝对象BeanUtils.copyProperties(item,dishDto);//获取菜品的idLong categoryId = item.getCategoryId();//获取菜品的nameCategory category = categoryMapper.selectById(categoryId);//判断是否有这个分类if(category!=null){//注入菜品namedishDto.setCategoryName(category.getName());}return dishDto;}).collect(Collectors.toList());//注入recordsdtoPage.setRecords(dishDtos);return dtoPage;}

修改菜品

需求分析

  • 点击修改按钮,可以修改菜品的信息,包括名字,口味,图片,价格,分类,描述等。

代码分析

  • 页面发送ajax请求,在服务端查询分类信息数据,在前端显示
  • 页面发送ajax请求,根据id查询当前菜品和口味信息,用于信息的回显
  • 页面请求服务端进行图片的下载
  • 点击保存后,将json数据传到服务端进行保存

代码开发

DishController.java(新增)

  /*** 修改菜品和口味的信息* @param dishDto 传回来的菜品和口味数据* @return*/@PutMappingpublic Result updateDishDto(@RequestBody DishDto dishDto){dishService.updateDishDto(dishDto);return new Result(Code.OK,"更新成功");}

DishService.java

 /*** 更新菜品和口味的信息* @param dishDto* @return*/@Transactionalpublic boolean updateDishDto(DishDto dishDto);

DishServiceImpl.java
菜品信息可以直接修改,口味因为有的删除有的修改,所以采取先全部删除,再重新添加的方式

  /*** 更新菜品和口味的信息* @param dishDto* @return*/@Overridepublic boolean updateDishDto(DishDto dishDto) {//更新菜品信息dishMapper.updateById(dishDto);//清理当前的口味数据LambdaQueryWrapper<DishFlavor> wrapper1 = new LambdaQueryWrapper<>();//获取菜品idLong id = dishDto.getId();//设置条件wrapper1.eq(DishFlavor::getDishId,id);//全部删除dishFlavorMapper.delete(wrapper1);//再次新增数据List<DishFlavor> flavors = dishDto.getFlavors();//给数据注入idflavors.stream().map((item)->{item.setDishId(id);//把口味添加进数据库dishFlavorMapper.insert(item);return item;}).collect(Collectors.toList());return true;}

总结

至此菜单模块的开发就全部完成,本模块的重点如下

  • 文件的上传和下载
  • 多表的添加、查询和删除

总体的过程比较复杂,需要详细的去看一下代码,才能弄清楚

瑞吉外卖-菜单模块开发相关推荐

  1. 瑞吉外卖项目 基于spring Boot+mybatis-plus开发 超详细笔记,有源码链接

    本项目是基于自学b站中 黑马程序员 的瑞吉外卖项目:视频链接: 黑马程序员Java项目实战<瑞吉外卖>,轻松掌握springboot + mybatis plus开发核心技术的真java实 ...

  2. 学习【瑞吉外卖⑥】SpringBoot单体项目_手机验证码登录业务开发

    视频:[黑马程序员]Java 项目实战<瑞吉外卖>,轻松掌握 SpringBoot + MybatisPlus 开发核心技术 资料:2022 最新版 Java学习 路线图>第 5 阶 ...

  3. 瑞吉外卖-Day01

    瑞吉外卖-Day01 课程内容 软件开发整体介绍 瑞吉外卖项目介绍 开发环境搭建 后台登录功能开发 后台退出功能开发 1. 软件开发整体介绍 作为一名软件开发工程师,我们需要了解在软件开发过程中的开发 ...

  4. Java实战项目《瑞吉外卖》

    目录 <瑞吉外卖>后台管理系统开发 软件开发整体介绍 1.软件开发流程 2.角色分工 3.软件环境 瑞吉外卖项目介绍 1.项目介绍 2.产品原型展示 3.技术选型 4.功能架构 5.角色 ...

  5. 学习【瑞吉外卖①】SpringBoot单体项目

    视频链接:黑马程序员[Java 项目实战<瑞吉外卖>,轻松掌握 SpringBoot + MybatisPlus 开发核心技术] 资料链接:2022 最新版 Java学习 路线图>第 ...

  6. 【SpringBoot项目实战+思维导图】瑞吉外卖①(项目介绍、开发环境搭建、后台登陆/退出功能开发)

    文章目录 软件开发整体介绍 软件开发流程 角色分工 软件环境 瑞吉外卖项目介绍 项目介绍 产品原型 技术选型 功能架构 角色 开发环境搭建 数据库环境搭建 创建数据库 数据库表导入 数据库表介绍 Ma ...

  7. 【瑞吉外卖开发笔记】

    瑞吉外卖开发笔记 源码地址 一.业务开发Day01 1.软件开发整体介绍 软件开发流程 角色分工 软件环境 2.瑞奇外卖项目介绍 项目介绍 产品原型展示 技术选型 功能架构 角色 3.环境搭建 开发环 ...

  8. 瑞吉外卖项目(一)软件开发流程设计及环境搭建

    第一章 软件开发整体介绍 软件开发流程 软件开发流程 需求分析:产品原型.需求规格说明书 设计:产品文档,ui界面设计,概要设计,详细设计,数据库设计 编码:项目代码,单元测试 测试:测试用例,测试报 ...

  9. 黑马瑞吉外卖项目开发笔记

    目录 软件开发整体介绍 开发流程 角色分工 软件环境 瑞吉外卖项目介绍 项目介绍 产品原型展示 技术选型 功能架构 角色 开发环境搭建 数据库环境搭建 Maven环境搭建 1.直接创建maven项目( ...

最新文章

  1. 使用JavaScript实现一个简单的编译器
  2. 【Linux】30.ssh不用手动输入密码登录终端sshpass 和 shell脚本后跟参数自动匹配case的用法
  3. 克拉克拉(KilaKila):大规模实时计算平台架构实战
  4. python 模块和包
  5. 如何让地面不起灰_什么是不发火地面,如何施工?
  6. P4859 已经没有什么好害怕的了
  7. Mysql高手系列 - 第20篇:异常捕获及处理详解(实战经验)
  8. 2017百度之星初赛
  9. 计算机网络性能(2)
  10. 隐藏nginx 版本号信息(转)
  11. CentOS7.3系统Tomcat无法正常启动解决(8005端口不能启动)
  12. SVN汉化包安装后,没有出现对应的语言选项问题解决(附SVN1.12.1汉化包下载地址)
  13. 一百多个实用ZBrush笔刷和Alpah,笔刷使用方法,让建模更简单!
  14. 网络安全工作及其配套法律法规和规范性文件汇总目录
  15. 用java异或的方式去实现简单的视频加密
  16. 陈彤离职,新浪在门户竞争中将继续被边缘化
  17. Audio in Windows Vista
  18. 以集成和管理为主要手段的企业报表中心架构设计
  19. 使用微软官方工具下载最新系统(win10为例)
  20. c# excel导入后处理不固定列数据

热门文章

  1. 肝了一夜,用 90 行代码打造最强 PDF 转换器,word、PPT、excel、markdown、html 一键转换...
  2. android launcher 日历图标显示日期
  3. LoRa开发1:LoRa设计10问
  4. kt和Java的几个有意思的地方
  5. 【四足机器人--控制指令输入及转换】(1)遥控手柄状态指令转换为机器人躯干状态输入代码解析
  6. killall 命令终止进程用法
  7. XtraBackup备份Mysql教程
  8. node中使用mysql模块的步骤
  9. jenkins+gitlab实现手动和自动同步
  10. Win10系统电脑下Airpods Pro等苹果耳机只能使用hands free无法使用stereo的解决办法_CSDN【调研后总结】