瑞吉外卖-菜单模块开发
目录
- 模块简介
- 文件的上传与下载
- 文件上传
- 文件下载
- 代码实现
- 新增菜品
- 需求分析
- 代码分析
- 代码实现
- 菜品信息分页查询
- 需求分析
- 代码分析
- 代码实现
- 修改菜品
- 需求分析
- 代码分析
- 代码开发
- 总结
模块简介
本模块主要的开发任务分为以下几个方面
- 文件的上传与下载
- 新增菜品
- 菜品信息分页查询
- 修改菜品
模块内容大致与之前内容相似,只有一个新内容就是文件的上传与下载
那么什么是文件的上传与下载呢?
文件上传与下载相信生活中大家经常使用。在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
调用DishMapper
和DishFlavorMapper
把信息保存到dish和dish_flavor表- 注意:因为在一个Controller中实现了向两个表中添加数据的操作,此时的形参是不能同时接受两个表的属性的。需要设置一个新类,同时包含两个表的属性。
- 因为是对两张表的操作,还需要添加事务
代码实现
本次代码涉及的模块较多,采用按包分类来展示
- 启动类(加入如下注解)
@EnableTransactionManagement
- 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;}
- 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;}
- 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,"添加成功");}
}
- 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> {}
- 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
这段代码比较复杂,大致的思路为:
- 将菜品的信息查询到Dish泛型的Page1中
- 将Page1的信息除了records以外的值复制到DishDto泛型的Page2中
- 将Page1的records进行复制给DishDto泛型的List,并添加categoryName的值到集合中
- 将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;}
总结
至此菜单模块的开发就全部完成,本模块的重点如下
- 文件的上传和下载
- 多表的添加、查询和删除
总体的过程比较复杂,需要详细的去看一下代码,才能弄清楚
瑞吉外卖-菜单模块开发相关推荐
- 瑞吉外卖项目 基于spring Boot+mybatis-plus开发 超详细笔记,有源码链接
本项目是基于自学b站中 黑马程序员 的瑞吉外卖项目:视频链接: 黑马程序员Java项目实战<瑞吉外卖>,轻松掌握springboot + mybatis plus开发核心技术的真java实 ...
- 学习【瑞吉外卖⑥】SpringBoot单体项目_手机验证码登录业务开发
视频:[黑马程序员]Java 项目实战<瑞吉外卖>,轻松掌握 SpringBoot + MybatisPlus 开发核心技术 资料:2022 最新版 Java学习 路线图>第 5 阶 ...
- 瑞吉外卖-Day01
瑞吉外卖-Day01 课程内容 软件开发整体介绍 瑞吉外卖项目介绍 开发环境搭建 后台登录功能开发 后台退出功能开发 1. 软件开发整体介绍 作为一名软件开发工程师,我们需要了解在软件开发过程中的开发 ...
- Java实战项目《瑞吉外卖》
目录 <瑞吉外卖>后台管理系统开发 软件开发整体介绍 1.软件开发流程 2.角色分工 3.软件环境 瑞吉外卖项目介绍 1.项目介绍 2.产品原型展示 3.技术选型 4.功能架构 5.角色 ...
- 学习【瑞吉外卖①】SpringBoot单体项目
视频链接:黑马程序员[Java 项目实战<瑞吉外卖>,轻松掌握 SpringBoot + MybatisPlus 开发核心技术] 资料链接:2022 最新版 Java学习 路线图>第 ...
- 【SpringBoot项目实战+思维导图】瑞吉外卖①(项目介绍、开发环境搭建、后台登陆/退出功能开发)
文章目录 软件开发整体介绍 软件开发流程 角色分工 软件环境 瑞吉外卖项目介绍 项目介绍 产品原型 技术选型 功能架构 角色 开发环境搭建 数据库环境搭建 创建数据库 数据库表导入 数据库表介绍 Ma ...
- 【瑞吉外卖开发笔记】
瑞吉外卖开发笔记 源码地址 一.业务开发Day01 1.软件开发整体介绍 软件开发流程 角色分工 软件环境 2.瑞奇外卖项目介绍 项目介绍 产品原型展示 技术选型 功能架构 角色 3.环境搭建 开发环 ...
- 瑞吉外卖项目(一)软件开发流程设计及环境搭建
第一章 软件开发整体介绍 软件开发流程 软件开发流程 需求分析:产品原型.需求规格说明书 设计:产品文档,ui界面设计,概要设计,详细设计,数据库设计 编码:项目代码,单元测试 测试:测试用例,测试报 ...
- 黑马瑞吉外卖项目开发笔记
目录 软件开发整体介绍 开发流程 角色分工 软件环境 瑞吉外卖项目介绍 项目介绍 产品原型展示 技术选型 功能架构 角色 开发环境搭建 数据库环境搭建 Maven环境搭建 1.直接创建maven项目( ...
最新文章
- 使用JavaScript实现一个简单的编译器
- 【Linux】30.ssh不用手动输入密码登录终端sshpass 和 shell脚本后跟参数自动匹配case的用法
- 克拉克拉(KilaKila):大规模实时计算平台架构实战
- python 模块和包
- 如何让地面不起灰_什么是不发火地面,如何施工?
- P4859 已经没有什么好害怕的了
- Mysql高手系列 - 第20篇:异常捕获及处理详解(实战经验)
- 2017百度之星初赛
- 计算机网络性能(2)
- 隐藏nginx 版本号信息(转)
- CentOS7.3系统Tomcat无法正常启动解决(8005端口不能启动)
- SVN汉化包安装后,没有出现对应的语言选项问题解决(附SVN1.12.1汉化包下载地址)
- 一百多个实用ZBrush笔刷和Alpah,笔刷使用方法,让建模更简单!
- 网络安全工作及其配套法律法规和规范性文件汇总目录
- 用java异或的方式去实现简单的视频加密
- 陈彤离职,新浪在门户竞争中将继续被边缘化
- Audio in Windows Vista
- 以集成和管理为主要手段的企业报表中心架构设计
- 使用微软官方工具下载最新系统(win10为例)
- c# excel导入后处理不固定列数据
热门文章
- 肝了一夜,用 90 行代码打造最强 PDF 转换器,word、PPT、excel、markdown、html 一键转换...
- android launcher 日历图标显示日期
- LoRa开发1:LoRa设计10问
- kt和Java的几个有意思的地方
- 【四足机器人--控制指令输入及转换】(1)遥控手柄状态指令转换为机器人躯干状态输入代码解析
- killall 命令终止进程用法
- XtraBackup备份Mysql教程
- node中使用mysql模块的步骤
- jenkins+gitlab实现手动和自动同步
- Win10系统电脑下Airpods Pro等苹果耳机只能使用hands free无法使用stereo的解决办法_CSDN【调研后总结】