黑马瑞吉外卖之菜品信息的修改
首先来看前端页面。 首先这个按钮从这里开始
然后转到下面的调用
到这里其实就是转到了另外的一个界面,就是我们的add.html。 那这个页面其实就是大致长这样的一个样子。
然后这里面其实可以需要的是进行一些数据的回显。其实这里的分类是已经调用到了。因为我们之前写过有关分类的后端代码。 我们首先去看前端给到后端的请求。其实我们可以大致浏览一下这个add.html的大致构造。
如下,看前面这一部分就是我们的页面的表单框的主要设计。这里还是进行了数据绑定。
具体的还是绑定到了属性数据。具体得属性数据还是定义在我们的vue组件的data下面,下面我么来看。
那么我们可以定位到下面这里的属性。 可以看到这里分类的展示,其实是遍历这个dishList里面的数据。这个dishList定义在vue的data数据模型里面。我们可以找到它。
然后我们在下面看,其实这里挂载了方法吗,也就是当我们打开这个页面的时候,这个方法会自动执行。我们先来看第一个。
然后这个方法其实就是具体写道了这里。我们可以看到这里还是传入了参数tyoe=1,这是比较熟悉的,传入1就是要找的就是菜品的分类。
回忆一下,所以我们打开数据库的这个分类表去看看,你看这里就是我们的分类,根据type的值来区分是套餐的分类还是菜品的分类。所以这样就十分清楚了。
然后我们点开这个方法去看看。 还是主要写在一个js里面,这里其实我们之前看过,就是分类的后端代码。
再次简单摘录下来。
代码语言:javascript复制 @GetMapping("list")
public R_<List<Category>> list(Category category)
{
// 条件构造器
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 R_.success(list);
}
所以我们的这个分类的其实已经完成了,所以它可以正常的展示
然后我们还是去看前端页面调用的方法,来看看这些数据的会先的情况要求。
然后紧接着划到下面,我们看到下面的init这里面的方法,其实这里还是调用了方法,然后将id传进去,
我们点进去这个方法。 所以还是写在来一个js里面,再看上面的其实这里请求是要求我们获取到数据模型中的属性。我们应该注意到除了基本的菜品数据还有具体的口味数据。这些数据都赋给了这些数据模型,然后其实这些数据模型还是和上面我们找到的dishlist一样,和表单那里进行了数据模型的绑定。
那么现在我们思路明确了,然后就可以进行后端逻辑代码的开发。 于是我们应该想到,我们应该自己定义一个查询的方法,将菜品和具体的口味信息的数据都封装进去。注意这里我们做的是查询到具体的信息然后回显。
那么我们班现在可以在service这里重新定义方法,作为扩展的方法。
代码语言:javascript复制package com.jgdabc.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.jgdabc.entity.dto.DishDto;
import com.jgdabc.entity.Dish;
public interface DishService extends IService<Dish> {
//新增菜品,同时插入口味数据
/**
* 这个是扩展功能,在我们保存菜品数据的时候,在口味表里面也插入改变的数据
* @param dishDto
*/
// 根据id来查询菜品信息和对应的口味信息
public DishDto getByIdWithFlavor(Long id);
}
然后呢,我们具体的去实现。 先把这个注入进来。
这里我们还是需要dto来进行属性的拷贝。
代码语言:javascript复制 @Override
public DishDto getByIdWithFlavor(Long id) {
// 先查询菜品的基本信息(根据id查询基本的菜品信息)
Dish dish = this.getById(id);
//引入dto用于扩展属性
DishDto dishDto = new DishDto();
//这里进行了属性拷贝
BeanUtils.copyProperties(dish,dishDto);
// 查询菜品对应的口味信息
//添加条件
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,dish.getId());
List<DishFlavor> list = dishFlavorService.list(queryWrapper);
//赋值口味数据
dishDto.setFlavors(list);
return dishDto;
}
然后我们就去Controller层调用这个方法
代码语言:javascript复制 @GetMapping("/{id}")
public R_<DishDto> get(@PathVariable Long id)
{
DishDto dishDto = dishService.getByIdWithFlavor(id);
if(dishDto!=null)
{
return R_.success(dishDto);
}
else
{
return R_.error("没有找到菜品信息");
}
}
这样就做到了菜品信息和口味数据的回显。
其实还需要将图片的数据回显,这样就涉及到下载图片的后端功能。其实下载图片我们之前已经开发过了。 前端页面就进行了对图片下载的方法的请求,其实还是调用到我们之前下载图片的代码。
文件下载的代码
代码语言:javascript复制 /**
* 文件下载
* @param name
* @param response
*/
@GetMapping("/download")
public void download(String name, HttpServletResponse response) throws IOException {
// 输入流,通过输入流读取文件内容
FileInputStream fileInputStream = new FileInputStream(new File(basepath) name);
// 输出流,通过输出流将文件写回到浏览器
// 要向浏览器写回数据,所以通过response响应对象进行写回
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();
}
// 关闭资源
outputStream.close();
fileInputStream.close();
}
这样我们的菜品和口味的数据就可以进行完整的回显了。 需要注意的就是图片的这个回显就是我们之前写过的,要结合到文件的上传,文件上传的其中的一个转储,这个路径一个要写对。路径写在了yml文件配置当中。
下面还是给出文件上传的代码截图
然后就是后端的对修改保存的功能的开发,其实和数据回显哪里一样,我们一样需要考虑到口味数据,我们需要在保存菜品的时候同时还需要保存到口味数据。
我们再去看一下前端的这个请求过程。我们主要关注到数据的修改保存提交,所以我们直接看到这里。
这个方法其实还是写在了一个js里面。
通过这两段其实我们就基本明白这个请求的来路。其实我们在浏览器或者代码简单尝试接收参数的调试的话,我们也可以看到前端提交过来哪些数据,已经请求方法和路径这些关键信息。
所以我们现在还是去自定义一个修改保存的方法。我们还是在service去扩展。 我们首先在dishservice里面库欧战一个自定义的方法。就是下面这样。
然后我们具体去实现。 我们在保存菜品口味的时候需要将对应的菜品口味关联到菜品。 我们尝试一次提交在浏览器的控制台观察数据,这个口味数据这里我们是可以提交未空列表的,所以我们后面做出一个处理就是如果提交口味数据为null的话,那么就不更新口味数据就好了。其实这取决于自己的处理逻辑。值得注意的是我们的口味数据需要关联到菜品。我们看菜品的基本信息是有id的,我们可以先将菜品添加保存,然后给DishFlavor设置对应的菜品id,那么我们就可以去用流的方式去遍历处理口味,然后将每个口味对应的关联菜品的id set上去。然后呢,我们对对应的口味数据进行批量保存就可以了,就是这样的一个处理逻辑。
还需要注意的是,我们要求的是需要的保存菜品的时候同时保存口味,那么这里其实应该去用到事务。我们必须去开启事务支持。万一在保存菜品和口味的这两个操作中某个过程挂掉或者出现问题的话,那么如果只有一个操作正常进行的话,这样其实是不符合逻辑的。所以这样就需要开启事务了。 其实类似这样的操作,都应该去开启事务。 启动类
代码如下。
代码语言:javascript复制 @Override
@Transactional
public void updateWithFlavor(DishDto dishDto) {
// 更新dish表
this.updateById(dishDto);
// 更新口味表dish_flavor
/**
* 可以有这样一个操作逻辑
* 就是先清理掉口味表数据,然后在重新插入更新的数据
*/
// 获取到口味数据的条件表达式
LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
//添加到新的数据
// 获取到前端提交过来的口味数据
List<DishFlavor> flavors = dishDto.getFlavors();
//
if (flavors!=null)
{
dishFlavorService.remove(dishFlavorLambdaQueryWrapper);
flavors = flavors.stream().map((item)->
{
item.setDishId(dishDto.getId());
return item;
}).collect(Collectors.toList());
dishFlavorService.saveBatch(flavors);
}
}
这是我们serviceimpl开发的方法。
接下来我们去Controller里面去调用这个方法。
代码语言:javascript复制 修改菜品
@PutMapping
public R_<String> update(@RequestBody DishDto dishDto) {
log.info(dishDto.toString());
dishService.updateWithFlavor(dishDto);
return R_.success("修改菜品成功");
}
这样就可以了。