菜品管理
- 文件上传下载
- 新增菜品
- 菜品信息分页
- 修改菜品
文件上传下载
1、文件上传
upload
form 表单参数:
method=post
enctype=multipart/form-data
type=file
服务端
组件
- commons-fileupload
- commons-io
spring-web对文件上传进行了封装
前端浏览需要配置静态资源路由
java
// class WebMvcConfig
// upload
registry.addResourceHandler("/upload/**")
.addResourceLocations("file:upload/");
2、文件下载
download
下载形式
- 附件形式下载,保存对话框
- 直接在浏览器中打开
3、文件上传代码实现
前端代码
html
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
4、文件下载代码实现
java
package com.github.mouday.reggie.controller;
import com.github.mouday.reggie.common.R;
import com.github.mouday.reggie.common.UploadFile;
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.IOException;
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {
@Value("${application.public-dirname}")
private String publicDirname;
@Value("${application.upload-dirname}")
private String uploadDirname;
/**
* 文件上传
*
* @param file
* @return
* @url http://127.0.0.1:8080/backend/page/common/upload.html
*/
@PostMapping("/upload")
public R<String> uploadFile(MultipartFile file) throws IOException {
// file 是一个临时文件
log.info("file: {}", file);
String originalFilename = file.getOriginalFilename();
String filename = UploadFile.getUploadFilename(originalFilename);
// 转存文件
file.transferTo(new File(UploadFile.getUploadDirectory(), filename));
return R.success(filename);
}
/**
* 文件下载
*
*/
@GetMapping("/download")
public void downloadFile(String name, HttpServletResponse response) throws IOException {
log.info("name: {}", name);
// 输入流 读取文件
FileInputStream inputStream = new FileInputStream(new File(UploadFile.getUploadDirectory(), name));
// 输出流 将文件写回浏览器
ServletOutputStream outputStream = response.getOutputStream();
response.setContentType("image/jpeg");
int len = 0;
byte[] bytes = new byte[1024];
while (true) {
len = inputStream.read(bytes);
if (len == -1) {
break;
}
outputStream.write(bytes, 0, len);
outputStream.flush();
}
// 关闭资源
outputStream.close();
inputStream.close();
}
}
公共类
java
package com.github.mouday.reggie.common;
import java.io.File;
import java.util.UUID;
public class UploadFile {
/**
* 获取文件保存路径
* 参考:https://www.bbsmax.com/A/GBJrE67Wz0/
*
* @return File
*/
public static File getUploadDirectory() {
// 获取目录
File path = new File("");
System.out.println("path:" + path.getAbsolutePath());
File upload = new File(path.getAbsolutePath(), "upload");
// 不存在则创建
if (!upload.exists()) {
upload.mkdirs();
}
return upload;
}
public static String getUploadFilename(String originalFilename) {
String suffix = UploadFile.getFilenameSuffix(originalFilename);
// 使用uuid生成文件名,防止文件名重复造成文件覆盖
String filename = UUID.randomUUID().toString() + suffix;
return filename;
}
/**
* 获取文件名后缀,带有.
*
* @param filename
* @return
*/
public static String getFilenameSuffix(String filename) {
if (filename == null) {
return null;
}
return filename.substring(filename.lastIndexOf("."));
}
}
新增菜品
需求分析
- 添加菜品,选择菜品分类
- 上传菜品图片
数据模型
- dish 菜品表
- dish_flavor 菜品口味表
代码开发
- 实体类 DishFlavor
- Mapper接口 DishFlavorMapper
- 业务层接口 DishFlavorService
- 业务层实现类 DishFlavorServiceImpl
- 控制层 DishFlavorController
DTO
- Data Transfer Object 数据传输对象
- 用于展示层与服务层之间数据传输
开启事务支持
java
@EnableTransactionManagement
public class ReggieApplication {}
java
/**
* 添加菜品
*
* @param dishDto
* @return
*/
@PostMapping
public R<String> addDish(@RequestBody DishDto dishDto) {
log.info(dishDto.toString());
dishService.saveDishWithDishFlavor(dishDto);
return R.success(null);
}
java
/**
* 保存菜品 和 对应的口味数据
* @param dishDto
* @return
*/
@Transactional
@Override
public void saveDishWithDishFlavor(DishDto dishDto) {
// 保存菜品
this.save(dishDto);
// 保存菜品对应的口味数据
Long dishId = dishDto.getId();
List<DishFlavor> list = dishDto.getFlavors().stream().map(item -> {
item.setDishId(dishId);
return item;
}).collect(Collectors.toList());
dishFlavorService.saveBatch(list);
}
功能测试
菜品分页查询
需求分析
- 菜品基本信息和分类名称
代码开发
java
/**
* 菜品分页
*/
@GetMapping("/page")
public R<Page> getDishList(int page, int pageSize, String name) {
// 条件构造器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(name)) {
queryWrapper.like(Dish::getName, name);
}
// 排序
queryWrapper.orderByDesc(Dish::getUpdateTime)
.orderByDesc(Dish::getId);
// 分页
Page<Dish> pageInfo = new Page<>(page, pageSize);
// 查询
dishService.page(pageInfo, queryWrapper);
// 查询分类名称
Page<DishDto> dishDtoPageInfo = new Page<>();
// 对象拷贝
BeanUtils.copyProperties(pageInfo, dishDtoPageInfo, "records");
List<Dish> dishRecords = pageInfo.getRecords();
// N + 1 次查询
// List<DishDto> dishDtoRecords = dishRecords.stream().map(item -> {
//
// DishDto dishDto = new DishDto();
// BeanUtils.copyProperties(item, dishDto);
//
// Long categoryId = item.getCategoryId();
// Category category = categoryService.getById(categoryId);
//
// if (category != null) {
// dishDto.setCategoryName(category.getName());
// }
//
// return dishDto;
// }).collect(Collectors.toList());
// 优化 2次 查询
// 取 Category.id
Set<Long> categoryIds = dishRecords.stream()
.map(Dish::getCategoryId).filter(item -> {
return item != null;
}).collect(Collectors.toSet());
// 取映射:Category.id => Category
List<Category> categories = categoryService.listByIds(categoryIds);
Map<Long, Category> categorieMap = categories.stream()
.collect(Collectors.toMap(Category::getId, o -> o));
// 类型转换 Dish => DishDto
List<DishDto> dishDtoRecords = dishRecords.stream().map(item -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
Long categoryId = item.getCategoryId();
Category category = categorieMap.get(categoryId);
if (category != null) {
dishDto.setCategoryName(category.getName());
}
return dishDto;
}).collect(Collectors.toList());
dishDtoPageInfo.setRecords(dishDtoRecords);
return R.success(dishDtoPageInfo);
}
功能测试
修改菜品
需求分析
代码开发
java
/**
* 根据id获取菜品
* @param id
* @return
*/
@GetMapping("/{id}")
public R<DishDto> getDishById(@PathVariable long id){
DishDto dishDto = dishService.getDishByIdWithDishFlavor(id);
return R.success(dishDto);
}
/**
* 更新菜品
* @param dishDto
* @return
*/
@PutMapping
public R<String> updateDishById(@RequestBody DishDto dishDto){
dishService.updateDishWithDishFlavor(dishDto);
return R.success("更新成功");
}
java
/**
* 获取菜品 和 对应的口味数据
*
* @param id Long
* @return
*/
@Override
public DishDto getDishByIdWithDishFlavor(Long id) {
// 查询菜品信息
Dish dish = this.getById(id);
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(dish, dishDto);
// 查询口味信息
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
List<DishFlavor> list = dishFlavorService.list(queryWrapper);
dishDto.setFlavors(list);
return dishDto;
}
/**
* 更新菜品 和 对应的口味数据
* @param dishDto
*/
@Override
@Transactional
public void updateDishWithDishFlavor(DishDto dishDto) {
// 更新菜品基本信息
this.updateById(dishDto);
// 清理对应的口味数据 delete
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
dishFlavorService.remove(queryWrapper);
// 更新对应的口味数据 insert
List<DishFlavor> dishFlavors = dishDto.getFlavors()
.stream()
.map(item -> {
item.setDishId(dishDto.getId());
return item;
}).collect(Collectors.toList());
dishFlavorService.saveBatch(dishFlavors);
}
功能测试