瑞吉外卖 —— 5、菜品管理

目录

1、文件上传和下载

1.1、文件上传介绍

1.2、文件下载介绍

1.3、文件上传实现

1.3.1、分析 

1.3.2、代码

1.4、文件下载实现

2、新增菜品

2.1、分析

2.1.1、需求分析

2.1.2、数据模型给

2.1.3、准备工作

2.1.4、页面与服务端的交互过程

2.1.5、前端代码分析

2.2、代码

2.2.1、获取菜品分类信息

2.2.2、DishDTO

2.2.3、保存页面发来的数据

3、菜品信息分页查询

3.1、分析

3.1.1、需求分析

3.1.2、页面与服务端的交互过程

3.2、代码

4、修改菜品

4.1、分析

4.1.1、需求分析

4.1.2、页面与服务端的交互过程

4.2、代码

4.2.1、菜品信息回显

4.2.2、修改商品实现

5、菜品停售与起售(批量)

5.1、分析

5.2、代码

6、(批量)删除

6.1、分析

6.2、代码

6.2.1、添加逻辑删除注解


1、文件上传和下载

1.1、文件上传介绍

1.2、文件下载介绍

1.3、文件上传实现

1.3.1、分析 

将 upload.html 放在 backend/page/demo 目录下,上传图片的接口如下

1.3.2、代码

① 将文件上传和下载页面取消检查登录:在 LoginCheckFilter 中将对应路径加入免查数组

        // 定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",  // 登录请求
                "/employee/logout",  // 登出请求
                "/backend/**",       // 前端资源
                "/front/**",       // 前端资源
                "/common/**"        // 文件上传和下载
                //"/employee/page"       // 前端资源
        };

② 在 application.yml 中设置文件存放位置

reggie:
  path: C:\Users\zhang\Desktop\2\

③ 创建 CommonController 处理文件上传请求

package com.itheima.reggie.controller;

import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
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 java.io.File;
import java.io.IOException;
import java.util.UUID;

/**
 * @Author zhang
 * @Date 2022/9/2 - 21:54
 * @Version 1.0
 */
// 文件上传和下载
@RestController
@Slf4j
@RequestMapping("/common")
public class CommonController {

    @Value("${reggie.path}")
    private String basePath;

    /**
     * 文件上传
     * @param file 这个参数名字必须与前端保持一直
     * @return
     */
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //log.info(file.toString());
        // file是一个临时文件,需要转存到指定位置,否则本次请求完后临时文件会删除
        // 原始文件名
        String originalFilename = file.getOriginalFilename();
        // 获取原始文件的后缀名
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        // 使用UUID生成文件名
        String fileName = UUID.randomUUID().toString() + suffix;
        // 创建一个目录对象
        File dir = new File(basePath);
        if(!dir.exists()){
            // 目录不存在,需要创建
            dir.mkdirs();
        }
        try {
            // 将临时文件转存到指定位置
            file.transferTo(new File(basePath + fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }

}

1.4、文件下载实现

前端在文件上传成功后,通过 handleAvatarSuccess 方法使自定义的参数 imageUrl 存储服务器保存图片的路径,再通过 imageUrl 发送请求下载文件

    /**
     * 文件下载
     * @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");  // 设置文件格式
            byte[] bytes = new byte[1024];
            int len = 0;
            while((len = fileInputStream.read(bytes)) != -1){
                outputStream.write(bytes, 0, len);
                outputStream.flush();
            }
            // 关闭资源
            outputStream.close();
            fileInputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2、新增菜品

2.1、分析

2.1.1、需求分析

2.1.2、数据模型给

 dish 表

dish_flavor 表

2.1.3、准备工作

2.1.4、页面与服务端的交互过程

获取菜品分类数据:

图片上传与下载与上面使用的接口相同,所以已经实现

保存菜品信息:

2.1.5、前端代码分析

在 created 生命周期,先通过 getDishList 方法获取菜品分类信息并存储到自定义数据 dishList

2.2、代码

2.2.1、获取菜品分类信息

在 CategoryController 中添加方法

    /**
     * 根据条件查询分类数据
     * @param category
     * @return
     */
    @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> categoryList = categoryService.list(queryWrapper);
        return R.success(categoryList);
    }

2.2.2、DishDTO

DTO,全称为 Data Transfer Object,即数据传输对象,一般用于展示层与服务层之间的数据传输。

由于保存菜品信息时,页面发送了 flavors 信息,而在 Dish 中没有该属性,无法匹配,所以导入 DishDto(位置:资料/dto) ,用于封装页面提交的数据

@Data
public class DishDto extends Dish {

    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

2.2.3、保存页面发来的数据

页面发来的数据不仅有菜品信息,还有对应的口味信息

① 在 DishSerivce 中添加同时存储两种信息的方法

public interface DishSerivce extends IService<Dish> {

    // 新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish、dish_flavor
    public void saveWithFlavor(DishDto dishDto);

}

② 在 DishServiceImpl 添加实现方法

@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishSerivce {

    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish、dish_flavor
     * @param dishDto
     */
    @Transactional
    @Override
    public void saveWithFlavor(DishDto dishDto) {
        // 保存菜品的基本信息
        this.save(dishDto);

        Long dishId = dishDto.getId();  // 菜品id
        List<DishFlavor> flavors = dishDto.getFlavors();    // 菜品口味
        flavors.stream().map((item) -> {
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());

        // 保存菜品口味信息到dish_flavor
        dishFlavorService.saveBatch(flavors);

    }

}

③ 在启动类添加注解开启事务

@EnableTransactionManagement    // 开启事务

④ 在 DishController 中添加方法

@RestController
@Slf4j
@RequestMapping("/dish")
public class DishController {

    @Autowired
    private DishSerivce dishSerivce;

    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品
     * @param dishDto
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        //log.info(dishDto.toString());
        dishSerivce.saveWithFlavor(dishDto);
        return R.success("新增菜品成功");
    }

}

3、菜品信息分页查询

3.1、分析

3.1.1、需求分析

3.1.2、页面与服务端的交互过程

 分页获取菜品信息请求:

3.2、代码

    /**
     * 菜品信息分页查询
     * @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 && !"".equals(name), Dish::getName, name);
        queryWrapper.orderByDesc(Dish::getUpdateTime);

        // 分页查询
        dishSerivce.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);
            Long categoryId = item.getCategoryId(); // 分类id
            Category category = categoryService.getById(categoryId);
            if(category != null) {
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }
            return dishDto;
        }).collect(Collectors.toList());
        dishDtoPage.setRecords(list);

        return R.success(dishDtoPage);
    }

4、修改菜品

4.1、分析

4.1.1、需求分析

4.1.2、页面与服务端的交互过程

根据 id 查询当前菜品信息的请求:

保存修改后的信息的请求:

4.2、代码

4.2.1、菜品信息回显

菜品信息的回显需要获取菜品的基本信息及其口味

① 在 DishSerivce 添加方法

    // 根据id查询菜品信息及其口味信息
    public DishDto getByIdWithFlavor(Long id);

② 在 DishServiceImpl 实现方法

    /**
     * 根据id查询菜品信息及其口味信息
     * @param id
     * @return
     */
    @Override
    public DishDto getByIdWithFlavor(Long id) {
        DishDto dishDto = new DishDto();

        // 查询菜品基本信息
        Dish dish = this.getById(id);

        // 查询菜品对应的口味信息
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId, id);
        List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);

        // 将查询到的数据封装
        BeanUtils.copyProperties(dish, dishDto);
        dishDto.setFlavors(flavors);

        return dishDto;
    }

③ 在 DishController 中使用方法

    /**
     * 根据id查询菜品信息及其口味信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> get(@PathVariable Long id){
        DishDto dishDto = dishSerivce.getByIdWithFlavor(id);
        return R.success(dishDto);
    }

4.2.2、修改商品实现

① 在 DishSerivce 添加方法

    // 更新菜品,同时更新对应的口味信息
    public void updateWithFlavor(DishDto dishDto);

② 在 DishServiceImpl 实现方法

    /**
     * 更新菜品,同时更新对应的口味信息
     * @param dishDto
     */
    @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        // 更新dish菜品表信息
        this.updateById(dishDto);

        // 清理当前菜品对应的口味
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId, dishDto.getCategoryId());
        dishFlavorService.remove(queryWrapper);

        // 添加当前提交过来的口味信息
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishDto.getCategoryId());
            return item;
        }).collect(Collectors.toList());
        dishFlavorService.saveBatch(flavors);
    }

③ 在 DishController 中使用方法

    /**
     * 修改菜品
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        dishSerivce.updateWithFlavor(dishDto);
        return R.success("修改菜品成功");
    }

5、菜品停售与起售(批量)

5.1、分析

首先查看单个停售或起售的请求

再看一下批量的请求

可以将他们写为一个方法

5.2、代码

在 DishController 添加方法 

    @PostMapping("/status/{status}")
    public R<String> updateStatus(
            @PathVariable("status") int status,
            @RequestParam List<Long> ids
    ){
        List<Dish> dishes = new ArrayList<>();
        for (Long id : ids) {
            Dish dish = new Dish();
            dish.setStatus(status);
            dish.setId(id);
            dishes.add(dish);
        }
        dishSerivce.updateBatchById(dishes, dishes.size());
        return R.success("菜品状态修改成功");
    }

6、(批量)删除

6.1、分析

首先看一下单个删除的请求

再来看一下批量删除的请求

可以看到与修改售卖状态的请求大致相同。

流程:
1、获取请求中要删除的 id,并获取他们的基本信息
2、逐个判断他们的售卖状态,若在启售状态,则无法删除

6.2、代码

6.2.1、添加逻辑删除注解

由于 Dish 和 DishFlavor 中有逻辑删除的字段,所以这里使用逻辑删除。

在 Dish 和 DishFlavor 类的 isDeleted 字段加上 @TableLogic 注解

    //是否删除
    @TableLogic // 逻辑删除
    private Integer isDeleted;

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值