SpringBoot 项目实战 | 瑞吉外卖 Day04
该系列将记录一份完整的实战项目的完成过程,该篇属于第四天
案例来自B站黑马程序员Java项目实战《瑞吉外卖》,请结合课程资料阅读以下内容
该篇我们将完成以下内容:
- 文件上传下载
- 新增菜品
- 菜品信息分页查询
- 修改菜品
文件上传下载
由于是第一次接触文件上传下载,我们分为五个小阶段讲解
文件上传介绍
文件上传,也称为upload,是指将本地图片,视频,音频等文件上传到服务器上,可以供其他用户浏览下载的过程
首先我们介绍文件上传对前端的要求:
- 以POST方法提交数据
- 采用Multipart格式上传文件
- 使用input的file空间上传
尽管前端组件库提供了相应的上传组件,但这些组件底层仍旧采用上述要求的格式构造
然后我们介绍文件上传在后端的实现:
- 通常采用Apache的两个组件:commons-fileupload 和 commons-io
目前我们的Spring框架在Spring-web包下对文件上传进行了封装,简化了服务端代码
我们只需要在Controller中的方法声明一个MultipartFile类型的参数即可接收上传的数据
文件下载介绍
文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程
通过浏览器进行文件下载,通常有两种表现形式:
- 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
- 直接在浏览器中打开
通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程
文件上传代码实现
测试用例:
- 资料中为我们提供了一个文件上传下载的示例页面:upload.html
- 可以复制在backend/page/demo下,在我们的代码实现过程中进行调试
我们先来思考一下文件上传的逻辑:
- 我们会获得一个文件File,但这个文件是临时文件,所以我们需要将他储存在电脑中
- 我们的储存位置需要固定,但是这个文件的名称和后缀名需要重新书写,但还要保证名称随机不会重复,后缀名和原文件相同
接下来我们正式开始文件上传代码的实现:
- 书写一个CommonController服务层并构造基本框架:
package com.qiuluo.reggie.controller;
import com.qiuluo.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.CoyoteOutputStream;
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.*;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
/**
* 下载操作
* @param MultipartFile file 是文件上传的唯一必备代码,代表上传的数据
* 注意:file只是一个临时文件,当我们的request请求结束时,file也会消失,所以我们需要将它保存起来
* @return
*/
@PostMapping("/upload")
public Result<String> upload(MultipartFile file){
// 我们可以直接记录日志查看是否收集到file
log.info(file.toString());
}
}
- 书写内部代码,阐明书写思路:
package com.qiuluo.reggie.controller;
import com.qiuluo.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.CoyoteOutputStream;
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.*;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${reggie.path}")
private String BasePath;
/**
* 下载操作
* @param file 注意需要与前端传来的数据名一致
* @return
*/
@PostMapping("/upload")
public Result<String> upload(MultipartFile file){
// 由于我们的file是临时文件,我们需要将该文件保存在计算机上
try {
// 这个方法可以转载文件到指定目录
file.transferTo(new File("D:/hello.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
// 注意:我们需要返回文件名,因为我们还需要让图片回显,回显就需要再次传入文件名来查询文件
return Result.success(fileName);
}
}
- 在yaml配置文件中书写全局变量,使存放路径固定:
# 我们定义了一个reggie.path的参数来存放我们存放文件的地址
reggie:
path: E:\imgs\
- 回到主函数中书写代码:
package com.qiuluo.reggie.controller;
import com.qiuluo.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.CoyoteOutputStream;
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.*;
import java.util.UUID;
/*
目前我们的文件存放已经有了固定的位置
但是我们的文件名以及后缀文件名还没有获得,我们将把它们变为动态的
*/
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
// 定义主路径
@Value("${reggie.path}")
private String BasePath;
/**
* 下载操作
* @param file 注意需要与前端传来的数据名一致
* @return
*/
@PostMapping("/upload")
public Result<String> upload(MultipartFile file){
// 注意:file只是一个临时文件,当我们的request请求结束时,file也会消失,所以我们需要将它保存起来
// 这个方法可以获得文件的原名称,但不推荐设置为文件名保存(因为可能出现重复名称导致文件覆盖)
String originalFilename = file.getOriginalFilename();
// 将原始文件的后缀截取下来
String substring = originalFilename.substring(originalFilename.lastIndexOf("."));
// UUID生成随机名称,文件名设置为 UUID随机值+源文件后缀
String fileName = UUID.randomUUID().toString() + substring;
// 判断文件夹是否存在,若不存在需创建一个
File dir = new File(BasePath);
if (!dir.exists()){
dir.mkdirs();
}
// 这个方法可以转载文件到指定目录
try {
file.transferTo(new File(BasePath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return Result.success(fileName);
}
}
文件下载代码实现
同样我们来思考一下文件下载的逻辑:
- 首先我们根据前端传来的名字与我们设置的固定路径得到文件
- 然后我们直接将文件的内容采用输出流的形式输出到前端即可
我们的文件下载同时在CommonController中实现:
- 书写主体框架
package com.qiuluo.reggie.controller;
import com.qiuluo.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.CoyoteOutputStream;
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.*;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${reggie.path}")
private String BasePath;
/**
*
* @param name
* @param response
* @return
*/
@GetMapping("/download")
public void download(String name, HttpServletResponse response){
// 我们将在里面实现,读取文件,再返回文件的操作
}
}
- 实现内部逻辑
package com.qiuluo.reggie.controller;
import com.qiuluo.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.CoyoteOutputStream;
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.*;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${reggie.path}")
private String BasePath;
/**
* 文件下载
* @param name
* @param response
* @return
*/
@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();
}
// 关闭数据
fileInputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
实际测试
我们打开之前转载过来的upload.html页面,传进照片后照片显示在页面中即为成功
新增菜品
我们的功能开发通常分为三部分
需求分析
当我们点击页面的菜品的新添时,会弹出页面,我们根据这个页面进行分析:
我们需要注意,这里还有一个菜品分类的下拉框,其中菜品分类的数据是通过请求到后台最后到数据库查询所得到的:
此外我们点击保存时,还会将一个菜品的相关信息请求返回后台:
我们需要注意的是:这里返回的并不仅仅只有菜品Dish的数据表,还包括了调料Dish Flavor的数据表
所以我们得到数据时不能采用Dish来获得数据
这里我们提出一个新的概念:
- DTO(Data Transfer Object)
这是一种设计模式之间传输数据的软件应用系统,它用于储存一些不属于同一个数据库中的一些信息
我们会新创一种DishDto实体类来继承Dish实体类并且新添一些关于DishFlavor的属性或方法来接收整个数据
在最后我们简单查看一下Dish和DishFlavor的数据表:
其中category_id表示所属分类的id
其中dish_id表示该调味的菜的id
代码实现
首先我们来实现一个简单的功能,获得菜品分类并返回前端页面:
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryServiceImpl categoryService;
/**
* 返回所有分类名称
*/
@GetMapping("/list")
public Result<List<Category>> list(Category category){
// 创造一个LambdaQueryWrapper来判断Type=1并设置排序顺序
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 Result.success(list);
}
}
接下来我们来实现存储双数据表到数据库的操作:
- 基本准备
# 首先我们来完成一些基本准备(我们的DishFlavor的操作也会放在DishController中实现)
实体类DishFlavor
数据层DishFlavorMapper
业务层接口DishFlavorService
业务层DishFlavorServiceImpl
服务层DishController
- 创建实体类DishDto(资料已提供)
// 我们通常单独创建一个包dto来装在DTO类
package com.qiuluo.reggie.dto;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class DishDto extends Dish {
// 在里面设置了List<DishFlavor> flavors 来装载 flavors
private List<DishFlavor> flavors = new ArrayList<>();
// categoryName 表示属于的分类名
private String categoryName;
private Integer copies;
}
- 新方法业务层接口实现
package com.qiuluo.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import com.qiuluo.reggie.dto.DishDto;
public interface DishService extends IService<Dish> {
// 由于当前的默认方法无法满足我们的需求,我们创建新的方法来实现该操作
//新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish、dish_flavor
public void saveWithFlavor(DishDto dishDto);
}
- 新方法业务层实现
package com.qiuluo.reggie.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.mapper.DishMapper;
import com.qiuluo.reggie.service.DishService;
import org.springframework.beans.BeanUtils;
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 extends ServiceImpl<DishMapper, Dish> implements DishService {
// 调入dishFlavor的业务层实现类
@Autowired
private DishFlavorServiceImpl dishFlavorService;
public void saveWithFlavor(DishDto dishDto){
// 1. 将菜品数据导入
this.save(dishDto);
// 2. 将Flavor导入(注意:Flavor传入时没有传入dishID,需要我们手动设置)
List<DishFlavor> flavors = dishDto.getFlavors();
for (DishFlavor flavor:flavors) {
flavor.setDishId(dishDto.getId());
}
dishFlavorService.saveBatch(flavors);
}
}
- 服务层实现
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import com.qiuluo.reggie.service.impl.DishServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishServiceImpl dishService;
/**
* 新增菜品
* @param dishDto
* @return
*/
@PostMapping
public Result<String> save(@RequestBody DishDto dishDto){
dishService.saveWithFlavor(dishDto);
return Result.success("新创成功");
}
}
实际测试
点击打开新建菜品,填写数据点击后数据库出现相关菜品即可
菜品信息分页查询
我们的功能开发通常分为三部分
需求分析
系统中菜品很多时,我们需要采用分页查询,使菜品多批出现防止拥挤导致页面阅读不方便
这里我们先给出完成操作后的图片:
我们需要注意的是这里的菜品分类直接出了分类的名称
但是我们的Dish中只给出了菜品分类的id,所以我们还需要借用id去查出所属分类的名称并重新赋值
代码实现
首先我们来完成简单的分页查询操作:
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import com.qiuluo.reggie.service.impl.DishServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishServiceImpl dishService;
@Autowired
private CategoryServiceImpl categoryService;
/**
* 分页查询
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
public Result<Page> page(int page,int pageSize,String name){
// 构造基本Page
Page<Dish> pageImpl = new Page<>(page,pageSize);
// 进行查询
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(name != null,Dish::getName,name);
queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
// 查询赋值,此时pageImpl里有值
dishService.page(pageImpl,queryWrapper);
return Result.success(pageImpl);
}
}
此时我们出来的页面中是无法查看到分类所属的:
所以我们需要设置包含有菜品分类名称的实体类作为Page的实现类参数才可以将菜品分类的名称传递到前端
我们只需要到前端代码中查看就可以注意到,商品分类这行上的数据属性名称为categoryName,所以我们采用DishDto来完成操作
我们将对代码进行修改:
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import com.qiuluo.reggie.service.impl.DishServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishServiceImpl dishService;
@Autowired
private CategoryServiceImpl categoryService;
/**
* 分页查询
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
public Result<Page> page(int page,int pageSize,String name){
// 构造基本Page
Page<Dish> pageImpl = new Page<>(page,pageSize);
// 因为返回类型中多了一个categoryName,我们还需要构造一个DishDto类型的Page
Page<DishDto> dishDtoPage = new Page<>();
// 进行查询
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(name != null,Dish::getName,name);
queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
// 查询赋值,此时pageImpl里有值
dishService.page(pageImpl,queryWrapper);
// 但是我们需要返回DishDto类型的Page,我们将pageImpl的值赋值到dishDtoPage中(不要赋值records,这个值是数据,我们需要单独处理)
// 我们借助工具类实现
BeanUtils.copyProperties(pageImpl,dishDtoPage,"records");
// 然后我们来处理dishDtoPage中的records值,我们首先将pageImpl的records提取出来
List<Dish> records = pageImpl.getRecords();
// 将该值除了CategoryName全都赋值给dishDtoPage的records(我们这里使用stream流进行局内单个赋值,采用foreach方法也相同)
List<DishDto> dishDtoList = records.stream().map((item) -> {
// 创建一个新dishDto作为返回实体
DishDto dishDto = new DishDto();
// 将正常属性赋值进去
BeanUtils.copyProperties(item,dishDto);
// 将CategoryName复制进去
Long categoryId = item.getCategoryId();
Category category = categoryService.getById(categoryId);
if(category != null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
}
return dishDto;
}).collect(Collectors.toList());
// 完成dishDtoPage的results的内容封装
dishDtoPage.setRecords(dishDtoList);
return Result.success(dishDtoPage);
}
}
实际测试
当我们打开页面时,所有数据均出现在页面即为代码实现成功
修改菜品
我们的功能开发通常分为三部分
需求分析
首先我们点击菜品后面的修改来查看修改页面:
首先我们会发现我们的菜品数据会展现在页面中,这是因为在打开时页面就发送了一个请求来获得该菜品的相关信息并回显:
然后我们会发现菜品分类下拉框的数据又出来了,这里我们在前面的新填菜品中已经实现了
最后我们只需要点击提交查看相关信息即可:
代码实现
我们这次需要实现两个流程
首先我们来实现根据id返回数据的代码实现:
- 业务层接口实现
package com.qiuluo.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import com.qiuluo.reggie.dto.DishDto;
public interface DishService extends IService<Dish> {
//查询菜品,并查询对应口味
public DishDto getByIdWithFlavors(Long id);
}
- 业务层实现
package com.qiuluo.reggie.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.mapper.DishMapper;
import com.qiuluo.reggie.service.DishService;
import org.springframework.beans.BeanUtils;
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 extends ServiceImpl<DishMapper, Dish> implements DishService {
// 调入dishFlavor的业务层实现类
@Autowired
private DishFlavorServiceImpl dishFlavorService;
/**
* 查询菜品,并查询对应口味
* @param id
* @return
*/
public DishDto getByIdWithFlavors(Long id){
// 创造返回对象
DishDto dishDto = new DishDto();
// 首先根据id获得菜品信息
Dish dish = this.getById(id);
// 根据id获得调料信息
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,id);
List<DishFlavor> list = dishFlavorService.list(queryWrapper);
// 将数据传入
BeanUtils.copyProperties(dish,dishDto);
dishDto.setFlavors(list);
return dishDto;
}
}
- 服务层实现
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import com.qiuluo.reggie.service.impl.DishServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishServiceImpl dishService;
/**
* 得到数据
* @param id
* @return
*/
@GetMapping("/{id}")
public Result<DishDto> get(@PathVariable Long id){
DishDto dishDto = dishService.getByIdWithFlavors(id);
return Result.success(dishDto);
}
}
接下来我们来实现修改操作:
- 业务层接口实现
package com.qiuluo.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import com.qiuluo.reggie.dto.DishDto;
public interface DishService extends IService<Dish> {
// 修改菜品
public void updateWithFlavor(DishDto dishDto);
}
- 业务层实现
package com.qiuluo.reggie.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.DishFlavor;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.mapper.DishMapper;
import com.qiuluo.reggie.service.DishService;
import org.springframework.beans.BeanUtils;
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 extends ServiceImpl<DishMapper, Dish> implements DishService {
// 调入dishFlavor的业务层实现类
@Autowired
private DishFlavorServiceImpl dishFlavorService;
// 修改菜品
public void updateWithFlavor(DishDto dishDto){
// Dish修改
this.updateById(dishDto);
// Flavor修改(我们先全部删除,再全部重新添加)
// 删除操作
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
dishFlavorService.remove(queryWrapper);
// 添加操作
List<DishFlavor> flavors = dishDto.getFlavors();
flavors = flavors.stream().map((item) -> {
item.setDishId(dishDto.getId());
return item;
}).collect(Collectors.toList());
}
}
- 服务层实现
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import com.qiuluo.reggie.service.impl.DishServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishServiceImpl dishService;
@Autowired
private CategoryServiceImpl categoryService;
/**
* 修改数据
* @param
* @return
*/
@PutMapping
public Result<String> update(@RequestBody DishDto dishDto){
dishService.updateWithFlavor(dishDto);
return Result.success("修改完成");
}
}
实际测试
回到菜品页面,修改相关信息点击修改,若修改成功,代码即为成功
批量操作
我们的功能开发通常分为三部分
需求分析
该节属于视频未提及的简单操作,只需要调用简单的业务层函数处理即可
我们需要完成批量删除,批量启售,批量停售,我们点击相关菜品后,依次点击方法,F12查看url以及传递的参数:
# 批量删除
url:http://localhost:8080/dish?ids=1583025958761824258
Method:DELETE
# 批量启售
url:http://localhost:8080/dish/status/1?ids=1413384757047271425,1413385247889891330
Method:POST
# 批量停售
url:http://localhost:8080/dish/status/0?ids=1413384757047271425,1413385247889891330
Method:POST
代码实现
我们直接给出代码展示:
package com.qiuluo.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.dto.DishDto;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import com.qiuluo.reggie.service.impl.DishServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishServiceImpl dishService;
/**
* 批量删除
* @param ids
* @return
*/
@DeleteMapping
public Result<String> deleteByIds(Long[] ids){
for (Long id:ids
) {
dishService.removeById(id);
}
return Result.success("删除成功");
}
/**
* 批量启售
* @param ids
* @return
*/
@PostMapping("/status/1")
public Result<String> openStatus(Long[] ids) {
for (Long id : ids
) {
Dish dish = dishService.getById(id);
dish.setStatus(1);
dishService.updateById(dish);
}
return Result.success("修改成功");
}
/**
* 批量停售
* @param ids
* @return
*/
@PostMapping("/status/0")
public Result<String> closeStatus(Long[] ids) {
for (Long id : ids
) {
Dish dish = dishService.getById(id);
dish.setStatus(0);
dishService.updateById(dish);
}
return Result.success("修改成功");
}
}
实际测试
返回菜品页面,点击若干菜品,进行三个方法的测试,实现即可
易错点
在这里我们会点出该项目目前容易出错的位置
DTO实体类
我们在这里重新强调一下DTO:
- 数据传输对象(DTO)(Data Transfer Object),是一种设计模式之间传输数据的软件应用系统。
DTO的作用我们在实例中已经很清楚了:
- 当我们目前的实体类不足以接收请求的数据或者请求数据包含了多个数据表的属性时使用DTO
DTO的原理实际上很简单:
- DTO只是在继承原本实体类的基础上新添一些所需要的属性来接收数据
总而言之DTO在我们的实际开发中是很有用的哦~
业务层方法实现
我们在前面的文章中基本都是借助SpringBoot和MyBaits所提供的方法来进行开发
但在实际业务中,我们遇到的查询语法或查询条件或者需要操作的步骤不是简单的方法所能实现的
这时我们就需要采用原始的方法重新进行业务层的方法实现:
- 业务层接口声明
首先在业务层接口声明该方法,在这时要想要返回的类型,想要需要什么参数
- 业务层实现
这里主要负责方法的实现,我们要根据自己的逻辑思路来一步一步完成方法
- 服务层实现
到了这层基本就是直接调用业务层实现的方法即可
结束语
该篇内容到这里就结束了,希望能为你带来帮助~
SpringBoot 项目实战 | 瑞吉外卖 Day04的更多相关文章
- Centos8.3、docker部署springboot项目实战记录
引言 目前k8s很是火热,我也特意买了本书去学习了一下,但是k8s动辄都是成百上千的服务器运维,对只有几台服务器的应用来说使用k8s就有点像大炮打蚊子.只有几台服务器的应用运维使用传统的tomc ...
- Vue+SpringBoot项目实战(一) 搭建环境
GitHub 地址: https://github.com/dongfanger/sprint-backend https://github.com/dongfanger/sprint-fronten ...
- 数据量大了一定要分表,分库分表组件Sharding-JDBC入门与项目实战
最近项目中不少表的数据量越来越大,并且导致了一些数据库的性能问题.因此想借助一些分库分表的中间件,实现自动化分库分表实现.调研下来,发现Sharding-JDBC目前成熟度最高并且应用最广的Java分 ...
- SpringBoot电商项目实战 — Redis实现分布式锁
最近有小伙伴发消息说,在Springboot系列文第二篇,zookeeper是不是漏掉了?关于这个问题,其实我在写第二篇的时候已经考虑过,但基于本次系列文章是实战练习,在项目里你能看到Zookeepe ...
- SpringBoot电商项目实战 — ElasticSearch接入实现
如今在一些中大型网站中,搜索引擎已是必不可少的内容了.首先我们看看搜索引擎到底是什么呢?搜索引擎,就是根据用户需求与一定算法,运用特定策略从互联网检索出制定信息反馈给用户的一门检索技术.搜索引擎依托于 ...
- SpringBoot电商项目实战 — 前后端分离后的优雅部署及Nginx部署实现
在如今的SpringBoot微服务项目中,前后端分离已成为业界标准使用方式,通过使用nginx等代理方式有效的进行解耦,并且前后端分离会为以后的大型分布式架构.弹性计算架构.微服务架构.多端化服务(多 ...
- SpringBoot电商项目实战 — 商品的SPU/SKU实现
最近事情有点多,所以系列文章已停止好多天了.今天我们继续Springboot电商项目实战系列文章.到目前为止,整个项目的架构和基础服务已经全部实现,分布式锁也已经讲过了.那么,现在应该到数据库设计及代 ...
- SpringBoot电商项目实战 — Zookeeper的分布式锁实现
上一篇演示了基于Redis的Redisson分布式锁实现,那今天我要再来说说基于Zookeeper的分布式现实. Zookeeper分布式锁实现 要用Zookeeper实现分布式锁,我就不得不说说zo ...
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_汇总
2018年Spring Boot 2.x整合微信支付在线教育网站高级项目实战视频课程 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-1.SpringBoot整合微信支付开发在 ...
- 小D课堂 - 零基础入门SpringBoot2.X到实战_第14节 高级篇幅之SpringBoot多环境配置_59、SpringBoot多环境配置介绍和项目实战
笔记 1.SpringBoot多环境配置介绍和项目实战(核心知识) 简介:SpringBoot介绍多环境配置和使用场景 1.不同环境使用不同配置 例如数据库配置,在开发的时候, ...
随机推荐
- 一文秒懂|Linux字符设备驱动
1.前言 众所周知,Linux内核主要包括三种驱动模型,字符设备驱动,块设备驱动以及网络设备驱动. 其中,Linux字符设备驱动,可以说是Linux驱动开发中最常见的一种驱动模型. 我们该系列文章,主 ...
- 【JSOI2008】火星人 (哈希+Splay)
题目 这种含有修改操作的就难以用后缀数组实现了,求LCP这种区间相等的类型可以想到用hash判断,同时LCP的答案大小符合二分条件可以二分求出,如果只有修改可以用线段树维护,因为还有有插入操作所以想到 ...
- .net中优秀依赖注入框架Autofac看一篇就够了
Autofac 是一个功能丰富的 .NET 依赖注入容器,用于管理对象的生命周期.解决依赖关系以及进行属性注入.本文将详细讲解 Autofac 的使用方法,包括多种不同的注册方式,属性注入,以及如何使 ...
- 【UniApp】-uni-app-内置组件
前言 好,经过上个章节的介绍完毕之后,了解了一下 uni-app-全局数据和局部数据 那么了解完了uni-app-全局数据和局部数据之后,这篇文章来给大家介绍一下 UniApp 中内置组件 首先不管三 ...
- [ABC267F] Exactly K Steps
Problem Statement You are given a tree with $N$ vertices. The vertices are numbered $1, \dots, N$, a ...
- Kafka核心逻辑介绍
1.概念 Kafka是最初由Linkedin公司开发,是一个分布式.支持分区的(partition).多副本的(replica)分布式消息系统(kafka2.8.0版本之后接触了对zk的依赖,使用自己 ...
- Pulsar3.0 升级指北
Pulsar3.0 介绍 Pulsar3.0 是 Pulsar 社区推出的第一个 LTS 长期支持版本. 如图所示,LTS 版本会最长支持到 36 个月,而 Feature 版本最多只有六个月:类似于 ...
- Linux卸载与安装JDK
安装 一.yum安装JDK 1.查看可安装的Java版本 yum -y list java* 2.选择一个自己要安装的版本 我安装的是java-11-openjdk.x86_64 sudo yum i ...
- GPT-4多模态大型语言模型发布
GPT-4 模型是OpenAI开发的第四代大型语言模型(LLM),它将是一个多模态模型,会提供完全不同的可能性-例如文字转图像.音乐甚至视频.GPT 全称为 Generative Pre-traine ...
- Guava Cache 异步刷新技巧,你值得拥有!
Guava Cache是一款非常优秀的本地缓存框架,提供简洁易用的 API 供开发者使用. 这篇文章,我们聊聊如何使用 Guava Cache 异步刷新技巧带飞系统性能 . 1 基本用法 首先,在 J ...