boot实战-菜品管理

菜品管理

文件上传下载

因为菜品中要上传对应的图片所以要用到java中的io流,这里复习下文件的上传下载

以下是前端代码对文件上传的要求

image-20220521194017776

目前这种文件上传一般直接使用开源的组件库(element-ui)

而我们服务端就是使用开源的基于io流的组件

image-20220521194453647

只是目前Spring给我做了简化

文件上传

image-20220521205048332

这里要注意我们写的方法参数要和表单对应的名字一样才能接收

image-20220521205232392

/*
* 文件上传和下载
* */
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${reggie.path}")
private String basePath;
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
// 注意这里的参数要和表单中对应的名字一样
public R<String> upload(MultipartFile file){
// 获取原始文件名
String originalFilename=file.getOriginalFilename();
// 截取文件后缀
String houzui = originalFilename.substring(originalFilename.lastIndexOf("."));
System.out.println(houzui);
// 使用uuid随机生成文件名称:防止文件名重复照成文件覆盖
String fileName = UUID.randomUUID().toString()+houzui;
//file是一个临时文件需要转存到指定位置,否者请求完成则删除
try {
file.transferTo(new File(basePath+fileName));
} catch (IOException e) {
e.printStackTrace();
}
return R.success(fileName);
}
}

因为这里要存到指定的位置所以我们要在springboot配置文件中配置自定义配置

reggie:
path: D:\image\

那如果我们指定的目录不存在呢?我们可以在转存之前判断目录是否存在,不存在就创建该目录

// 判断指定目录是否存在,不存在则创建目录
File dir=new File(basePath);
// 判断目录是否存在
if(!dir.exists()){
// 目录不存在,创建
dir.mkdirs();
}

文件下载

在我们上传完之后,页面还会发起请求用于下载刚刚的图片使其展示出来

image-20220521210529739

/**
* 文件下载
* @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.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}

测试

image-20220521212238058

image-20220521212256491

新增菜品

需求分析

image-20220521212528137

数据模型

及数据库中对应的表

image-20220521213011561

image-20220521213418613

image-20220521213453304

image-20220521213521526

image-20220521213539161

框架搭建

搭建实体类,mapper,service,serviceimpl,(与前面的主要代码类似)controller

/**
* 菜品管理
*/
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private DishFlavorService dishFlavorService;
}

image-20220521215747359

查询分类数据

就是个条件查询

image-20220521220629283

/**
* 根据条件查询分类数据
* @param category
* @return
*/
@GetMapping("/list")
public R<List<Category>> list(Category category){
// 条件构造器(SELECT * FROM category WHERE type=?)
LambdaQueryWrapper<Category> queryWrapper=new LambdaQueryWrapper<>();
// 添加条件
queryWrapper.eq(category.getType()!=null,Category::getType,category.getType());
// 添加排序条件(优先使用sort字段排序,相同情况再使用updatetime排序)
queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);
List<Category> list = categoryService.list(queryWrapper);
return R.success(list);
}

image-20220521222027427

菜品图片上传

其实就是我们之前写好了的文件上传下载的代码

接收页面提交数据

image-20220523200351607

image-20220523200449749

根据分析我们就是要接受数据保存到dish(菜品表)和dish-flavor(口味表)

这里要注意因为里面有两个实体类的数据,所以我们选哪个实体类都不好,直接创造个新的实体类,使其能够封装所有参数(继承原实体类补充新的属性用于接收)

image-20220523201238588

因为我们在dish的基础上要插入对菜品口味的保存,所以我们要在DishServic中添加下自定义的保存方法,随后完成实现类,这里因为是同时操作两张表的方法所以我们要开启事务 @Transactional(注意要到运行类中开启事务功能)

//开启事务支持
@EnableTransactionManagement

同时通过前端提交的数据你会发现flavor中没有对应的id所以我们要用for循环或者stream为flavor添加id

public interface DishService extends IService<Dish> {
//新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish dish_flavor
public void saveWithFlavor(DishDto dishDto);
}
@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
@Autowired
private DishFlavorService dishFlavorService;
/**
* 新增菜品,同时保存口味数据
* @param dishDto
*/
// 因为要同时操作多表,开启事务支持
@Transactional
@Override
public void saveWithFlavor(DishDto dishDto) {
// 保存菜品基本信息到菜品表
this.save(dishDto);
// 获取菜品id
Long id = dishDto.getId();
//保存菜品口味数据到口味表
List<DishFlavor> flavors = dishDto.getFlavors();
for(Object flavor:flavors){
DishFlavor dishFlavor=(DishFlavor) flavor;
dishFlavor.setDishId(id);
}
dishFlavorService.saveBatch(flavors);
}
}
//可用此代替上面的for
flavors=flavors.stream().map((item)->{
item.setDishId(id);
return item;
}).collect(Collectors.toList());

最后完成controller进行测试

菜品信息分页查询

需求分析

image-20220523211845840

有两个难点一是要显示图片(用到之前的图片下载),二是要显示菜品分类,要通过id从分类表中查询数据

代码开发

image-20220523210823471

显示已有的菜品,分页查询和之前的基本一样

image-20220523212228131

我们来模仿之前的完成分页查询

@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
// 构造分页构造器
Page<Dish> pageInfo=new Page<>(page,pageSize);
// 条件构造器
LambdaQueryWrapper<Dish> queryWrapper=new LambdaQueryWrapper<>();
// 添加条件
queryWrapper.like(name!=null,Dish::getName,name);
queryWrapper.orderByDesc(Dish::getUpdateTime);
dishService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}

可是我们直接返回pageInfo可以发现分类的列没有数据

image-20220523213932055

其实这是因为我们返回的直接是分类表中对应的id而没有查到字段,我们需要返回的是categoryName

image-20220523214039330

这就是page里面的泛型Dish不含有这个属性

最后处理完成代码

/**
* 分页查询菜品
* @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);
// 对象拷贝,排除records属性
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);
// 获取每一项的分类id
Long categoryId = item.getCategoryId();
// 根据id查询分类对象
Category category = categoryService.getById(categoryId);
// 获取每一项对应分类名称
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
return dishDto;
}).collect(Collectors.toList());
dishDtoPage.setRecords(list);
return R.success(dishDtoPage);
}

修改菜品

需求分析

image-20220523222024032

交互过程

image-20220523222143506

代码开发

image-20220524155648104

这里是result风格但是要注意,因为需要返回的信息不是只有dish一张表的所以我们要联合口味表查,不能简单封装成Dish对象

image-20220524155828081

DishDto和Dish不同的就是多了几个参数,我们正好需要List来存对应菜品的口味

image-20220524163245348

我们不妨模仿之前新增菜品在DishService中添加一个自定义的查询方法,实现类中根据dishId查出DishFlavor的list集合,新定义一个DishDto对象,将查出的dish对象复制给dishdto,同时将dishflavors赋值给disdto

service&serviceImpl

// 根据id查询,查询同时查出对应的口味
public List<DishFlavor> getWithFlavor(Long dishId);
----------------------
/**
* 根据dish_id查出dish&List<dishflavor>将其封装成dishdto
* @param dishId
* @return
*/
@Override
public List<DishFlavor> getWithFlavor(Long dishId) {
DishDto dishDto=new DishDto();
LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,dishId);
List<DishFlavor> dishFlavor = dishFlavorService.list(queryWrapper);
System.out.println(dishFlavor);
return dishFlavor;
}

controller

@GetMapping("/{id}")
public R<DishDto> GetById(@PathVariable Long id){
DishDto dishDto=new DishDto();
List<DishFlavor> withFlavor = dishService.getWithFlavor(id);
Dish dish = dishService.getById(id);
BeanUtils.copyProperties(dish,dishDto);
dishDto.setFlavors(withFlavor);
return R.success(dishDto);
}

这里只是完成了菜品信息的显示

以下来完成菜品信息的更新,因为是多表操作,所以别忘了使用事务

image-20220524164728820

可见前端直接返回的是DishDto对象,我们更新口味的表有个思路是先删除对应的dish_id对应的口味数据,随后在存入,因为我们修改可能是增加或者删除操作,所以我们直接更新其口味,可以把现有口味清空在存入信息中的口味。

/**
* 根据提供的DishDto对象同时更新菜品和口味信息
* @param dishDto
* @return
*/
@Transactional
@PutMapping
public R<String> Update(@RequestBody DishDto dishDto){
//直接更新菜品信息
dishService.updateById(dishDto);
//查询出菜品对应的口味,清空数据
LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper();
queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
dishFlavorService.remove(queryWrapper);
//获取更改后的口味信息全部保存
List<DishFlavor> flavors = dishDto.getFlavors();
for (Object item:flavors){
DishFlavor dishFlavor=(DishFlavor) item;
dishFlavor.setDishId(dishDto.getId());
dishFlavorService.save(dishFlavor);
}
return R.success("更新菜品成功!");
}

删除菜品(批量)

需求分析

点击删除按钮,删除对应的菜品以及其口味信息(添加事务),可以同时选择多个id传入完成批量删除

image-20220524213008536

这里代码和修改菜品里面的部分代码类似

代码开发

/**
* 批量删除菜品及对应口味
* @param ids
* @return
*/
@Transactional
@DeleteMapping
public R<String> deleteByIds(Long[] ids){
List<Long> idList= Arrays.asList(ids);
dishService.removeByIds(idList);
for (Long id:idList){
LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,id);
dishFlavorService.remove(queryWrapper);
}
return R.success("删除菜品成功!");
}

停售菜品

需求分析

可以单击菜品完成停售/启售,也可以批量停售和批量起售

image-20220524222912760

请求路径都是一样的

代码开发

这里注意看我们传入的status就是我们要改的值,这里我要禁用菜品,他传入的是0,正好我们需要将1改成0!!

image-20220524231420167

那代码巨简单了

/**
* 批量启售禁售菜品
* @param ids
* @param status
* @return
*/
@PostMapping("/status/{status}")
public R<String> updateStatus(Long[] ids,@PathVariable int status){
for (Long id:ids){
Dish dish = dishService.getById(id);
dish.setStatus(status);
dishService.updateById(dish);
}
return R.success("更新状态成功");
}
posted @   Ember00  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示