基于Springboot和MybatisPlus的外卖项目 瑞吉外卖Day4

瑞吉外卖Day4

以下所有笔记均是个人学习总结的,希望大家点赞关注支持一下

文件上传与下载

一、前端代码要求

​ **文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上.**文件上传时,对页面的form表单有如下要求:method=“post” enctype=“multipart/form-data” type=“file”

<form method="post" action="/common/upload" enctype="multipart/form-data">
<input name-"myFile" type="file" />
<input type="submit" value="提交"/>
form>
  1. 采用post方式提交数据
  2. 采用multipart格式上传文件
  3. 使用input的file控件上传

二、文件上传介绍

​ 服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件: commons-fileupload commons-io
​ Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如:

三、文件下载介绍

文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程。通过浏览器进行文件下载,通常有两种表现形式:

  1. 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
  2. 直接在浏览器中打开通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程

四、代码实现

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.web.bind.annotation.*;
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;

/**
 * 文件上传和下载
 */
@RestController
@RequestMapping("/common")
@Slf4j
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;//dfsdfdfd.jpg

        //创建一个目录对象
        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();
        }

    }
}

新增菜品

一、类和接口基本结构

  1. 实体类DishFlavor和数据传输对象DishDto
  2. Mapper接口 DishFlavorMapper
  3. 业务层接口 DishFlavorService
  4. 业务层实现类 DishFlavorServicelmpl
  5. 控制层 DishController

二、交互过程

​ 1、页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中

​ 2、页面发送请求进行图片上传,请求服务端将图片保存到服务器

​ 3、页面发送请求进行图片下载,将上传的图片进行回显

​ 4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端

三、完善功能

1.完善DishService菜品口味功能
package com.itheima.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Dish;

public interface DishService extends IService {

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

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

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

2.完善DishServiceimpl类
package com.itheima.reggie.service.impl;

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

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

@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);

    }

    /**
     * 根据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;
    }

    @Override
    @Transactional
    public void updateWithFlavor(DishDto 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();

        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);
    }
}

四、代码实现

1.导入实体类DishFlavor和数据传输对象DishDto
package com.study.pojo;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
菜品口味
 */
@Data
public class DishFlavor implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //菜品id
    private Long dishId;


    //口味名称
    private String name;


    //口味数据list
    private String value;


    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;


    //是否删除
    private Integer isDeleted;

}


package com.study.Dto;


import com.study.pojo.Dish;
import com.study.pojo.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
public class DishDto extends Dish {

    private List flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}


2.Mapper接口 DishFlavorMapper
package com.study.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.study.pojo.DishFlavor;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}

3.业务层接口 DishFlavorService
package com.study.Service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.study.pojo.DishFlavor;

public interface DishFlavorService extends IService<DishFlavor> {
}

4.业务层实现类 DishFlavorServicelmpl
package com.study.Service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.study.Service.DishFlavorService;
import com.study.mapper.DishFlavorMapper;
import com.study.pojo.DishFlavor;
import org.springframework.stereotype.Service;

@Service
public class DishFlavorServiceimpl extends ServiceImpl<DishFlavorMapper, DishFlavor>implements DishFlavorService {
}

5.控制层 DishController
package com.study.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.study.Dto.DishDto;
import com.study.Service.CategoryService;
import com.study.Service.DishFlavorService;
import com.study.Service.DishService;
import com.study.common.R;
import com.study.pojo.Category;
import com.study.pojo.Dish;
import com.study.pojo.DishFlavor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {
    @Autowired
    private DishService dishService;

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private CategoryService categoryService;

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

  
}

菜品分页查询

一、梳理交互过程

1、页面(backend/page/food/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据

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,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);
    }

    /**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> get(@PathVariable Long id){

        DishDto dishDto = dishService.getByIdWithFlavor(id);

        return R.success(dishDto);
    }

修改菜品

一、代码开发-梳理交互过程

1、页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示

2、页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显

3、页面发送请求,请求服务端进行图片下载,用于页图片回显

4、点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端

二、代码开发

  /**
     * 修改菜品
     * @param dishDto
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        dishService.updateWithFlavor(dishDto);

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

删除与禁用菜品

一、前端代码

页面代码
<el-button
              type="text"
              size="small"
              class="blueBug"
              @click="addFoodtype(scope.row.id)"
            >
              修改
            el-button>
            <el-button
              type="text"
              size="small"
              class="blueBug"
              @click="statusHandle(scope.row)"
            >
              {{ scope.row.status == '0' ? '启售' : '停售' }}
            el-button>
vue代码
 // 删除
          deleteHandle (type, id) {
            if (type === '批量' && id === null) {
              if (this.checkList.length === 0) {
                return this.$message.error('请选择删除对象')
              }
            }
            this.$confirm('确认删除该菜品, 是否继续?', '确定删除', {
              'confirmButtonText': '确定',
              'cancelButtonText': '取消',
            }).then(() => {
              deleteDish(type === '批量' ? this.checkList.join(',') : id).then(res => {
                if (res.code === 1) {
                  this.$message.success('删除成功!')
                  this.handleQuery()
                } else {
                  this.$message.error(res.msg || '操作失败')
                }
              }).catch(err => {
                this.$message.error('请求出错了:' + err)
              })
            })
          },

          //状态更改
          statusHandle (row) {
            let params = {}
            if (typeof row === 'string' ) {
              if (this.checkList.length === 0) {
                this.$message.error('批量操作,请先勾选操作菜品!')
                return false
              }
              params.id = this.checkList.join(',')
              params.status = row
            } else {
              params.id = row.id
              params.status = row.status ? '0' : '1'
            }
            this.dishState = params
            this.$confirm('确认更改该菜品状态?', '提示', {
              'confirmButtonText': '确定',
              'cancelButtonText': '取消',
              'type': 'warning'
            }).then(() => {
              // 起售停售---批量起售停售接口
              dishStatusByStatus(this.dishState).then(res => {
                if (res.code === 1) {
                  this.$message.success('菜品状态已经更改成功!')
                  this.handleQuery()
                } else {
                  this.$message.error(res.msg || '操作失败')
                }
              }).catch(err => {
                  this.$message.error('请求出错了:' + err)
              })
            })
          },

二、后端代码

@PostMapping("/status/{st}")
    public R<String> setStatus(@PathVariable int st, String ids){
        //处理string 转成Long
        String[] split = ids.split(",");
        List<Long> idList = Arrays.stream(split).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());

        //将每个id new出来一个Dish对象,并设置状态
        List<Dish> dishes = idList.stream().map((item) -> {
            Dish dish = new Dish();
            dish.setId(item);
            dish.setStatus(st);
            return dish;
        }).collect(Collectors.toList()); //Dish集合

        log.info("status ids : {}",ids);
        dishService.updateBatchById(dishes);//批量操作
        return R.success("操作成功");
    }


@DeleteMapping
    public R<String> delete(String ids){
        String[] split = ids.split(","); //将每个id分开
        //每个id还是字符串,转成Long
        List<Long> idList = Arrays.stream(split).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
        dishService.removeByIds(idList);//执行批量删除
        log.info("删除的ids: {}",ids);
        return R.success("删除成功"); //返回成功
    }

三、代码分析

1、停售与启售:客户端点击停售或者启售修改服务端res.code的值,将修改的值发送post指令传输到status/code中,并调用了setStatus方法

2、删除:客户端点击删除或者批量删除将id转为字符串传入给到前端代码ids(此处使用id接收不到数据)中,最终将调用delete(String ids)方法,将参数转为long型并删除。

你可能感兴趣的:(开发学习,spring,boot,java,后端,intellij-idea)