【瑞吉外卖day04】

【瑞吉外卖day04】,第1张

菜品管理业务 1.文件上传下载 1.1 文件上传介绍



1.2 文件下载介绍

1.3 文件上传代码实现


前端页面直接使用现成的,源码这里:

DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>文件上传title>
  
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css" />
  <link rel="stylesheet" href="../../styles/page.css" />
head>
<body>
   <div class="addBrand-container" id="food-add-app">
    <div class="container">
        <el-upload class="avatar-uploader"
                action="/common/upload"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
                :before-upload="beforeUpload"
                ref="upload">
            <img v-if="imageUrl" :src="imageUrl" class="avatar">img>
            <i v-else class="el-icon-plus avatar-uploader-icon">i>
        el-upload>
    div>
  div>
    
    <script src="../../plugins/vue/vue.js">script>
    
    <script src="../../plugins/element-ui/index.js">script>
    
    <script src="../../plugins/axios/axios.min.js">script>
    <script src="../../js/index.js">script>
    <script>
      new Vue({
        el: '#food-add-app',
        data() {
          return {
            imageUrl: ''
          }
        },
        methods: {
          handleAvatarSuccess (response, file, fileList) {
              this.imageUrl = `/common/download?name=${response.data}`
          },
          beforeUpload (file) {
            if(file){
              const suffix = file.name.split('.')[1]
              const size = file.size / 1024 / 1024 < 2
              if(['png','jpeg','jpg'].indexOf(suffix) < 0){
                this.$message.error('上传图片只支持 png、jpeg、jpg 格式!')
                this.$refs.upload.clearFiles()
                return false
              }
              if(!size){
                this.$message.error('上传文件大小不能超过 2MB!')
                return false
              }
              return file
            }
          }
        }
      })
    script>
body>
html>

然后项目在terminal中输入
mvn clean compile
mvn install
清空浏览器缓存,访问http://localhost:8080/backend/page/demo/upload.html,就嫩实现图片上传。

但是看控制台信息就会发现,虽然显示结果是200,但是其实请求是被拦截的:


我们编写controller来正确处理这次请求:
新建CommonController处理文件上传下载。

其中我们文件本地转存的文件夹路径可以在applicaiton.yml中指定:

详细执行过程可以看代码注释,很多讲究

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



    //使用value注解从application.yml配置文件中读取的路径
    @Value("${reggie.path}")
    private String pathValue;

    /**
     * 文件上传
     * @param file 参数名要和前端页面请求的name参数的名称保持一致
     * @return
     */
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //此时的file是临时文件,需要转存到指定位置,否则本次请求完成后文件会自动消失
        log.info(file.toString());
        //原始文件名
        String originalFilename = file.getOriginalFilename();
        //截取文件后缀名
        String suffix=originalFilename.substring(originalFilename.lastIndexOf('.'));
        //使用UUID重新生成文件名称,防止同名文件覆盖
        String filename= UUID.randomUUID().toString()+suffix;

        //创建一个目录对象
        File dir=new File(pathValue);
        //判断当前目录是否存在
        if(!dir.exists()){
            //如果目录不存在,则创建目录
            dir.mkdir();
        }

        try{
            //将我们的临时文件转存到指定位置
            file.transferTo(new File(pathValue+filename));
        }catch (IOException e){
            e.printStackTrace();
        }

        return null;
    }
}

上传图片,我们发图片成功转存在了本地指定目录下:

1.4 文件下载代码实现


img标签可以展示下载的图片

对应的请求在这里:

需要把文件传进去

我们编写controller代码

/**
     * 文件下载
     * @param name
     * @param response
     */
    @GetMapping("/download")
    public void download(String name, HttpServletResponse response){

        try {
            //输入流,通过输入流读取文件内容
            FileInputStream inputStream=new FileInputStream(new File(pathValue+name));
            //输出流,将文件写回浏览器,从而可以展示图片
            ServletOutputStream outputStream = response.getOutputStream();

            response.setContentType("imag/jpeg");

            int len=0;
            byte[] bytes=new byte[1024];
            while((len=inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }
            //关闭资源
            outputStream.close();
            inputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

测试,运行:图片能成功回显了。

我们发现,整个过程浏览器一共提交了两个请求,处理第一个请求时将图片转存到本地,处理第二个请求将本地图片回显到浏览器上。

2.新增菜品 2.1需求分析


2.2 代码开发



其中图片上传下载功能之前已经开发好了,能直接使用。

首先是根据条件获取菜品分类数据(下拉框数据)。我们先看前端代码add.html:
直接看钩子函数created:



找到了发出的获取菜品分类的ajax请求:“/category/list”,get方法

然后我们在categoryController中编写按条件获取分类的请求代码

 /**
     * 根据条件查询分类数据
     * @return
     */
    @GetMapping("/list")
    public R<List<Category>> getCatList(Category category){
        //查询条件
        LambdaQueryWrapper<Category> queryWrapper=new LambdaQueryWrapper<>();
        //添加条件
        queryWrapper.eq(category.getType()!=null,Category::getType,category.getType());
        //排序条件
        queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getCreateTime);

        List<Category> list = categoryService.list(queryWrapper);
        return R.success(list);
    }

这里要强调一下为什么这个方法的参数没有加@RequestBody注解,是因为此方法是Get 请求,参数在请求头,而不是请求体。

测试:成功在下拉菜单中获取菜品分类。

然后我们试着新增一个菜,看看发出的dish请求:

看看前端的代码:点击保存调用submitForm方法


addDish方法:

我们要做的有两件事:一是保存菜品在dish表中,还有需要保存口味信息在dish_flavor表中。

那么我们请求体的参数类型要写Dish吗?这样是不行的,因为前端的请求参数和Dish的字段不完全一致,前端实际的请求参数多一个flavors。

我们的解决方法就是引入一个新的类DishDto(继承自Dish类),用来封装页面提交的数据。


我们在DishService接口中定义这样一个方法,用来同时保存菜品和口味:

public interface DishService extends IService<Dish> {
    /**
     * 新增菜品,同时保存菜品和口味数据
     * @param dishDto 
     */
    public void saveWithFlavor(DishDto dishDto);
}

然后我们在DishServiceImpl中实现方法:
其中有两点需要注意:
1.flavors需要通过stream流的方式来设置dishId,
2. 因为涉及到 *** 作多张表,所以需要事务,其注解为:@Transactional
3. 启动类上需要添加注解支持:@EnableTransactionManagement

@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 存口味和菜品
     * @param dishDto
     */
    //因为涉及到 *** 作多张表,所以需要事务
    @Transactional
    public void saveWithFlavor(DishDto dishDto){
        //存基本信息到菜品表
        this.save(dishDto);
        //菜品id
        Long dishId = dishDto.getId();
        List<DishFlavor> flavors = dishDto.getFlavors();
        //使用stream流处理
        flavors=flavors.stream().map((item)->{
            item.setDishId(dishId);
            return item; }).collect(Collectors.toList());
        //保存口味信息到口味表
        dishFlavorService.saveBatch(flavors);
    }
}

完成后,我们编写dishController的方法:

 /**
     * 新增菜品,同时保存对应的口味数据
     * @param dishDto
     * @return
     */
    @PostMapping
    public R<String> addDish(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());
        dishService.saveWithFlavor(dishDto);
        return R.success("新增菜品成功");
    }

测试,刷新数据库表,发现菜品和口味数据插入成功。

3.菜品信息分页查询 3.1需求分析

3.2 代码开发




ajax请求:

页面发送的请求:

于是我们编写controller方法:

/**
     * 菜品信息分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> getDishPage(int page,int pageSize,String name){

        //分页查询构造器
        Page<Dish> pageInfo=new Page<>(page,pageSize);
        //查询条件构造器
        LambdaQueryWrapper<Dish> queryWrapper=new LambdaQueryWrapper<>();
        //添加查询条件(模糊查询)
        queryWrapper.like(StringUtils.isNotEmpty(name),Dish::getName,name);
        //排序条件(根据更新时间降序排)
        queryWrapper.orderByDesc(Dish::getUpdateTime);
        //执行查询
        dishService.page(pageInfo,queryWrapper);

        return R.success(pageInfo);
    }

测试,发现问题

我们发现,菜品分类这一栏信息没有显示,是为什么呢?
我们浏览器调试,发现,我们响应回去的菜品数据,里面只有菜品分类的id,而没有菜品分类名称:

而人家前端需要的是categoryName,我们响应回去的数据没有这个字段。

怎么办呢?我们想到了我们的DishDto:

继承自Dish类,又扩展了categoryName属性,符合我们的需要。
于是我们对代码进行修改:

/**
     * 菜品信息分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> getDishPage(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(StringUtils.isNotEmpty(name),Dish::getName,name);
        //排序条件(根据更新时间降序排)
        queryWrapper.orderByDesc(Dish::getUpdateTime);
        //执行查询
        dishService.page(pageInfo,queryWrapper);

        //对象拷贝
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");
        List<Dish> records = pageInfo.getRecords();

        List<DishDto> dishDtoList;
        //改变类型
        dishDtoList=records.stream().map((item)->{
            DishDto dishDto=new DishDto();
            //对象(属性)拷贝
            BeanUtils.copyProperties(item,dishDto);
            //setCategoryName
            Long categoryId = item.getCategoryId();
            Category category = categoryService.getById(categoryId);
            if(category!=null){
                dishDto.setCategoryName(category.getName());
            }

            return dishDto;

        }).collect(Collectors.toList());

        dishDtoPage.setRecords(dishDtoList);

        return R.success(dishDtoPage);
    }

测试,发现能够正常查询了

4.修改菜品 4.1需求分析

4.2 代码开发


我们的第一个请求、第三个请求已经做好了。我们第二个请求(根据id查询当前菜品信息)在这里:

下面我们在DishService中定义好接口,然后在DishServiceImpl类中编写根据id查询菜品&口味信息的具体方法:
注意: BeanUtils.copyProperties此方法只能将父类对象的属性拷贝到子类对象中!其他的属性还是老老实实用set方法吧

/**
     * 根据id查询菜品和口味信息
     * @param id
     * @return
     */
    @Override
    public DishDto queryDishById(Long id) {
        //先查出对应id的菜品信息
        Dish dishbyId = this.getById(id);
        //属性拷贝到dishDto对象
        DishDto dishDto=new DishDto();
        BeanUtils.copyProperties(dishbyId,dishDto);// BeanUtils.copyProperties此方法只能将父类对象的属性拷贝到子类对象中
        //然后根据菜品id查询口味List
        //条件构造器
        LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dishbyId.getId());

        List<DishFlavor> flavorList = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(flavorList);

        return dishDto;
    }

controller代码

/**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> getDishWhenUpdate(@PathVariable  Long id){
        DishDto dishDto = dishService.queryDishById(id);
        return R.success(dishDto);
    }

编写完成,测试:回显成功。

下一步就是修改信息后,点击保存,修改菜品和口味信息表。还是先在DishService中定义好接口,然后在DishServiceImpl中编写新增方法。因为涉及到多张表的 *** 作,所以加上事务注解**@Transactional**
具体的逻辑是:先更新菜品表,然后先清理对应菜品的口味数据,最后在增加对应菜品的口味数据

/**
     * 根据传进来的dishDto修改菜品和口味表
     * @param dishDto
     */
    @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        //更新菜品表
        this.updateById(dishDto);
        //先清理对应菜品的口味数据
        LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());

        dishFlavorService.remove(queryWrapper);
        //再增加对应菜品的口味数据
        List<DishFlavor> flavors = dishDto.getFlavors();

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

    }

controller代码

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

测试,修改成功。

5.停售/起售菜品(视频没有,自己写的,供参考) 5.1代码开发


停售/起售 controller,注意这里为了支持批量 *** 作,传入的ids为long数组

 /**
     * (批量)停售/起售
     * @param status
     * @param ids
     * @return
     */
    @PostMapping("/status/{status}")
    public R<String> beginEnd(@PathVariable Integer status,Long[] ids){
//        log.info("id为{}的菜品要修改的状态为{}",ids,status);

        for (Long oneids : ids) {
            Dish dish = dishService.getById(oneids);
            dish.setStatus(status);
            dishService.updateById(dish);
        }

        return R.success("修改成功");
    }
6.删除菜品(视频没有,自己写的,供参考) 6.1代码开发

/**
     *(批量) 删除菜品,同时删除对应的口味信息
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String> deleteDish(Long[] ids){
        for (Long id : ids) {
            dishService.removeById(id);

            LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(DishFlavor::getDishId,id);
            dishFlavorService.remove(queryWrapper);
        }
        return R.success("删除成功");
    }

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1322677.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存