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 @ 2022-05-24 23:16  Ember00  阅读(64)  评论(0)    收藏  举报