瑞吉外卖(4)—菜品管理业务开发

目录

一、文件上传和下载

1.1 文件上传和下载介绍

1.2 代码实现

二、新增菜品

2.1 需求分析

2.2 数据模型

2.3 代码开发

三、菜品信息分页查询

3.1 需求分析

3.2 代码开发

四、修改菜品

4.1 需求分析

4.2 代码开发


一、文件上传和下载

1.1 文件上传和下载介绍

 

 

1.2 代码实现

yml配置文件:配置上传图片的存储位置

reggie:
  path: D:\img\
package com.learn.reggie.controller;

import com.learn.reggie.common.R;
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.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;

/**
 * 文件上传和下载
 */
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {

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

    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
        log.info(file.toString());

        //原始文件名
        String originalFilename = file.getOriginalFilename(); //abc.jpg
        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);
    }

    /**
     * 文件下载
     * @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.flush();
            }

            //关闭资源
            outputStream.close();
            fileInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

注意:这里上传的文件的文件名要和这个地方的一样,接收文件的参数的名不能随便定义,要和下面的name的值一致

二、新增菜品

2.1 需求分析

2.2 数据模型

2.3 代码开发

 创建相关的mapper和service层:

package com.learn.reggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.learn.reggie.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author 咕咕猫
 * @version 1.0
 */
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}
package com.learn.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.learn.reggie.entity.DishFlavor;

/**
 * @author 咕咕猫
 * @version 1.0
 */
public interface DishFlavorService extends IService<DishFlavor> {
}
package com.learn.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.learn.reggie.entity.DishFlavor;
import com.learn.reggie.mapper.DishFlavorMapper;
import com.learn.reggie.service.DishFlavorService;
import com.learn.reggie.service.DishService;
import org.springframework.stereotype.Service;

/**
 * @author 咕咕猫
 * @version 1.0
 */
@Service
public class DishFlavorServiceImpl extends ServiceImpl<DishFlavorMapper, DishFlavor> implements DishFlavorService {
}

编写controller:

  先获取和返回菜品分类列表; 前端主要的代码;

// 获取菜品分类列表
const getCategoryList = (params) => {
  return $axios({
    url: '/category/list',
    method: 'get',
    params
  })
}
 
if (res.code === 1) {
     this.dishList = res.data   //这里就相当于把所有的category对象的数据赋值给dishList
   }
 
这是菜品分类和数据双向绑定的前端代码:  我们返回的是一个集合,
</el-form-item>
          <el-form-item
            label="菜品分类:"
            prop="categoryId"
          >
            <el-select
              v-model="ruleForm.categoryId"
              placeholder="请选择菜品分类"
            >
              <el-option v-for="(item,index) in dishList" :key="index" :label="item.name" :value="item.id" />
            </el-select>
          </el-form-item>

在CategoryController书写查询代码,不过这里的返回值和参数接收值可能和自己想的有点不一样。。。这个的返回值和参数值 值得多思考一下; 这里之所以返回list集合,是因为这个要展示的数据是引用类型的数据集,集合可以存放任意类型的数据;

    /**
     * 根据条件查询分类数据
     * @param category
     * @return
     */
    @GetMapping("/list")
    //这个接口接收到参数其实就是一个前端传过来的type,这里之所以使用Category这个类来接受前端的数据,是为了以后方便
    //因为这个Category类里面包含了type这个数据,返回的数据多了,你自己用啥取啥就行
    public R<List<Category>> list(Category category){
        //条件构造器
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        //添加条件
        queryWrapper.eq(category.getType() != null, Category::getType,category.getType());
        //添加排序条件    使用两个排序条件,如果sort相同的情况下就使用更新时间进行排序
        queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);

        List<Category> list = categoryService.list(queryWrapper);

        return R.success(list);
    }

测试的返回数据:

接收页面提交的数据(涉及两张表)

点击保存按钮的时候,把前端的json数据提交到后台,后台接收数据,对数据进行处理;要与两张表打交道,一个是dish一个是dish_flavor表;

先用前端页面向后端发一次请求,看看前端具体的请求是什么,我们好写controller;然后再看前端提交携带的参数是什么,我们好选择用什么类型的数据来接收!!!

看下图:这是前端传过来的具体参数,我们需要什么参数类型来接收这些数据就大概知道了;因为这里传过来的参数比较复杂,所以这里有两种方式进行封装,第一:创建与这些数据对应的实体类(dto) ,第二使用map来接收;

 这里我们选择使用第一种方式;

package com.learn.reggie.dto;

import com.learn.reggie.entity.Dish;
import com.learn.reggie.entity.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
public class DishDto extends Dish {

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

    private String categoryName;

    private Integer copies;
}

  前端关键代码:

<el-button
  type="primary"
  @click="submitForm('ruleForm')"
>
  保存
</el-button>
 
let params = {...this.ruleForm}
// params.flavors = this.dishFlavors
params.status = this.ruleForm ? 1 : 0
params.price *= 100   //存到数据库的时候是以分为单位,所以这里x100
params.categoryId = this.ruleForm.categoryId
params.flavors = this.dishFlavors.map(obj => ({ ...obj, value: JSON.stringify(obj.value) }))
 
 
if (this.actionType == 'add') {
     delete params.id
     addDish(params).then(res => {
     if (res.code === 1) {
     this.$message.success('菜品添加成功!')
     if (!st) {
     this.goBack()
      } else {   ....
 
 
// 新增接口
const addDish = (params) => {
  return $axios({
    url: '/dish',
    method: 'post',
    data: { ...params }
  })
}

后端代码:

在DishService中新增一个方法:

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

相关的实现:

package com.learn.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.learn.reggie.dto.DishDto;
import com.learn.reggie.entity.Dish;
import com.learn.reggie.entity.DishFlavor;
import com.learn.reggie.mapper.DishMapper;
import com.learn.reggie.service.DishFlavorService;
import com.learn.reggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 咕咕猫
 * @version 1.0
 */
@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {

    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品,同时保存对应的口味数据
     * @param dishDto
     */
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //保存菜品的基本信息到菜品表dish
        this.save(dishDto);

        Long dishId = dishDto.getId();//菜品id

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


        //保存菜品的口味数据到菜品口味表dish_flavor
        dishFlavorService.saveBatch(flavors);

    }
}

在启动类开启事务: 加上这个注解就行 @EnableTransactionManagement

controller 层的代码:

package com.learn.reggie.controller;

import com.learn.reggie.common.R;
import com.learn.reggie.dto.DishDto;
import com.learn.reggie.entity.Dish;
import com.learn.reggie.service.DishFlavorService;
import com.learn.reggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 菜品管理
 */
@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品
     * @param dishDto
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){  //前端提交的是json数据的话,我们在后端就要使用这个注解来接收参数,否则接收到的数据全是null
        log.info(dishDto.toString());

        dishService.saveWithFlavor(dishDto);

        return R.success("新增菜品成功");
    }
}

测试功能

三、菜品信息分页查询

3.1 需求分析

3.2 代码开发

图片下载的请求前面已经写好了,前端也写好了相关的请求,所以第二步的图片下载和展示就不需要管了;

controller层的代码(DishController):不过这里是有bug的,后面会改善;

    /**
     * 菜品信息分页查询
     *
     * @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);

        //对象拷贝
        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
            //根据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);
    }

功能完善:引入了DishDto

package com.learn.reggie.dto;

import com.learn.reggie.entity.Dish;
import com.learn.reggie.entity.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
public class DishDto extends Dish {

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

    private String categoryName;

    private Integer copies;
}

功能测试

这边输入流error没关系,是io异常,图片查找的是位置在xml文件里配置,默认是D:/img

四、修改菜品

4.1 需求分析

4.2 代码开发

第一次交互的后端代码已经完成了;菜品分类的信息前面做新增菜品的时候就已经完成了,这里前端发一个相关接口的请求就行;

第三次交互,图片的下载前面也已经写了,所以前端直接发生请求就行;

菜品信息的回显

在service添加自己要实现的方法

//根据id来查询菜品信息和对应的口味信息
DishDto getByIdWithFlavor(Long id);

方法的实现:

    /**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    public DishDto getByIdWithFlavor(Long id) {
        //查询菜品基本信息,从dish表查询
        Dish dish = this.getById(id);

        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish,dishDto);

        //查询当前菜品对应的口味信息,从dish_flavor
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dish.getId());
        List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(flavors);

        return dishDto;
    }

controller 层的编写:

/**
 * 根据id来查询菜品信息和对应的口味信息
 * @param id
 * @return
 */
@GetMapping("/{id}")
public R<DishDto> get(@PathVariable Long id){  //这里返回什么数据是要看前端需要什么数据,不能直接想当然的就返回Dish对象
    DishDto dishDto = dishService.getByIdWithFlavor(id);
    return R.success(dishDto);
}

相关的实现:
 

   @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        //更新dish表的基本信息  因为这里的dishDto是dish的子类
        this.updateById(dishDto);
 
        //更新口味信息---》先清理再重新插入口味信息
        //清理当前菜品对应口味数据---dish_flavor表的delete操作
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
        dishFlavorService.remove(queryWrapper);
 
        //添加当前提交过来的口味数据---dish_flavor表的insert操作
        List<DishFlavor> flavors = dishDto.getFlavors();
 
        //下面这段流的代码我注释,然后测试,发现一次是报dishId没有默认值(先测),两次可以得到结果(后测,重新编译过,清除缓存过),相隔半个小时
        //因为这里拿到的flavorsz只有name和value(这是在设计数据封装的问题),不过debug测试的时候发现有时候可以拿到全部数据,有时候又不可以...  所以还是加上吧。。。。。
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());
 
        dishFlavorService.saveBatch(flavors);
 
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值