目录

12、菜品管理业务开发--文件上传下载

12.1、需求分析

12.2、代码开发(上传、下载)

13、新增菜品

13.1、需求分析

13.2、代码开发

14、菜品信息分页查询

14.1、需求分析

14.2、代码开发

15、修改菜品

15.1、问题分析

15.2、代码开发

16、停售/起售菜品,删除菜品

16.1、需求分析

16.2、代码开发


12、菜品管理业务开发--文件上传下载

12.1、需求分析

--在我们进行添加菜品时,需要提供图片,此时就涉及到了文件上传。从客户端上传文件至服务器。

  • 发QQ、微博、微信朋友圈都用到了文件上传功能,将用户数据上传到服务器保存

--在我们进行分页查询菜品数据时,会在页面显示图片资源,此时就涉及到了文件下载。从服务器下载文件至客户端。通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。

  • 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
  • 直接在浏览器中打开(也是下载的一种)

12.2、代码开发(上传、下载)

--上传:Spring框架在spring-web包中对文件上传进行了封装,在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件。

--根据前端检查信息:此name值即为MultipartFile声明的参数名称

复杂java需求的截面 java需求文档例子_开发语言

--操作如下:

-上传时,需要指定磁盘路径。我们在yml文件中配置

reggie:
  path: E:\BaiduNetdiskDownload\7、黑马程序员瑞吉外卖平台实战开发\瑞吉外卖-资料\1 瑞吉外卖项目\资料\图片资源\

-上传业务代码:

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

    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<String>upload(MultipartFile file){ //MultipartFile将客户端的文件传送到服务器
        String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); //截取后缀
        String fileName = UUID.randomUUID().toString() + suffix;

        //创建包路径的目录对象
        File dir = new File(basePath);
        //如果包路径不存在,创建
        if (!dir.exists()){
            dir.mkdir();
        }
        //将临时文件转存到指定位置
        try {
            file.transferTo(new File(basePath + fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }

-下载:流的读写操作

@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){
                //只要len里面有内容(由输入流读取的)就一直用输出流读
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }

            fileInputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

13、新增菜品

13.1、需求分析

--通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。

--新增菜品时包含口味属性,所以我们要添加新的数据模型dish_flavor表。所以在新增菜品时,涉及到两个表:dish表(菜品表),dish_flavor表(菜品口味表)。多表操作

13.2、代码开发

--配置dish_flavor的相关配置:实体类、mapper接口、service接口和实现类

新增菜品时前端页面和服务端的交互过程:

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

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

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

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

开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可。

--1、我们在添加菜品时,需要选择当前菜品所属的菜品分类,所以我们要回显菜品分类到下拉框。

复杂java需求的截面 java需求文档例子_复杂java需求的截面_02

-从前端检查请求路径得知,需要在CategoryController中操作

/**
     * 根据条件查询菜品的分类数据
     * @param category
     * @return
     */

    @GetMapping("/list") //返回的为菜品的分类集合
    public R<List<Category>>list(Category category){

        LambdaQueryWrapper<Category> qw = new LambdaQueryWrapper<>();
        //若菜品没有分类,则不在此页面显示,此页面只显示菜品的分类
        qw.eq(category.getType()!=null,Category::getType,category.getType());
        //根据sort和修改时间排序
        qw.orderByAsc(Category::getSort).orderByAsc(Category::getUpdateTime);

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

--2和3我们在做过公用的上传和下载

--4、涉及到两个表:dish表(菜品表),dish_flavor表(菜品口味表)。多表操作,我们需要中级体(Dto)来协助。MP提供的添加操作无法满足,需自定义

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

-操作如下:我们在DishService中编写添加方法saveWithFlavor,在实现类中重写

********此处涉及到Stream流操作***********

//          菜品新增----因新增的内容含有口味等属性,此属性在菜品口味表中DishFlavor
//        * MP提供的方法只能操作Dish表中属性,所以要自定义方法DishServiceImpl--利用Dto进行多表操作
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;
    @Override
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //客户端传的数据dishDto中有部分内容在dish实体类(数据库dish表),先将dish中有的内容添加进去
        this.save(dishDto);
        /**
         * "flavors":[{"name":"甜味","value":"[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]","showOption":false}
         * //因前端传送的flavors里并没有dishId,所以需要利用stream联合map进行映射,将dishId手动添加至集合中的每一条数据中
         */
        //先在dishDto中手动获取dishId
        Long dishId = dishDto.getId();
        //创建flavors集合
        List<DishFlavor> flavors = dishDto.getFlavors();
        //flavors.stream()创建flavors集合的stream流
        //stream().map将集合里面的每一条数据映射出来
        flavors.stream().map((item) -> {
            //对集合中的每条数据进行操作-----加入dishId
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList()); //将刚刚单独取出来的每条数据在转化为集合
//        for (DishFlavor item:flavors){ //增强for
//            item.setDishId(dishId);
//        }
        dishFlavorService.saveBatch(flavors);
    }
}

-在Controller中调用

/**
     * 菜品新增----因新增的内容含有口味等属性,此属性在菜品口味表中DishFlavor
     * MP提供的方法只能操作Dish表中属性,所以要自定义方法--利用Dto进行多表操作
     * @param dishDto
     * @return
     */
    @PostMapping
    public R<String>save(@RequestBody DishDto  dishDto ){
//        dishService.save(dish);
        dishService.saveWithFlavor(dishDto );
        return R.success("菜品新增成功");
    }

14、菜品信息分页查询

14.1、需求分析

--分页显示数据,涉及到客户端返回类型中有个别属性在前端页面无法显示,需要后端进行操作


* 前端菜品分类需要显示名称,但是前端查询时,只能查到菜品的id, * 我们需要在后端根据传来的id来查找对应的名称然后返回给前端


14.2、代码开发

--1、页面(backend/page/food/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据。2、页面发送请求,请求服务端进行图片下载,用于页面图片展示

************涉及到Stream流和对象拷贝**************

/**
     * 前端菜品分类需要显示名称,但是前端查询时,只能查到菜品的id,
     * 我们需要在后端根据传来的id来查找对应的名称然后返回给前端
     * @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> qw = new LambdaQueryWrapper<>();
        //添加查询条件
        qw.like(StringUtils.isNotEmpty(name),Dish::getName,name);
        qw.orderByDesc(Dish::getSort);
        //开始查询分页
        dishService.page(pageInfo,qw);
        //将得到的分页数据进行处理
        //对象对象拷贝----其中records为页面集合数据因缺少categoryName不需要复制
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");
        List<Dish> records = pageInfo.getRecords();
        //stream().map将集合里面的每一条数据映射出来
//        List<DishDto> nowrecords = records.stream().map((item)->{
        //对集合中的每条数据进行操作
        //获取dishDto用于存放属性
//            DishDto dishDto = new DishDto();
        //将集合中每条数据已有属性拷贝到dishDto
//            BeanUtils.copyProperties(item,dishDto);
        //获取菜品的id
//            Long categoryId = item.getCategoryId();
        //根据id查对应的菜品
//            Category category = categoryService.getById(categoryId);
        //获取对应菜品的名称
//            String categoryName = category.getName();
        //将得到的名称set到dishDto中
//            dishDto.setCategoryName(categoryName);
//            return dishDto;
//        }).collect(Collectors.toList());

        ArrayList<DishDto> list = new ArrayList<>();
        for (Dish newRecords:records){
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(newRecords,dishDto);
            Long categoryId = newRecords.getCategoryId();
            Category category = categoryService.getById(categoryId);
            String categoryName = category.getName();
            dishDto.setCategoryName(categoryName);
            list.add(dishDto);
        }
//        dishDtoPage.setRecords(nowrecords);
        dishDtoPage.setRecords(list);
        return R.success(dishDtoPage);
    }

15、修改菜品

15.1、问题分析

--在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,最后点击确定按钮完成修改操作

-所以,我们需要做的事分两步,先回显,再修改

15.2、代码开发

--1、在修改页面回显数据:

        根据检查消息得:回显数据为根据id进行get请求查询信息

复杂java需求的截面 java需求文档例子_开发语言_03

        查询内容分两块:基本信息Dish表和口味信息DishFlavor表

复杂java需求的截面 java需求文档例子_复杂java需求的截面_04

--2、点击保存提交数据:

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

--操作如下(多表操作,自定义service方法):


DishController:


/**
     * 修改菜品--1回显数据
     * @param ids
     * @return
     */
    @GetMapping("/{ids}")
    public R<DishDto> update(@PathVariable Long ids){
        DishDto dishDto = dishService.getWithFlavor(ids);
        return R.success(dishDto);
    }

    /**
     * 修改菜品--2保存数据
     * @param dishDto
     * @return
     */
    @PutMapping
    public R<String>put(@RequestBody DishDto dishDto){
        dishService.updateWithFlavor(dishDto);
        return R.success("修改成功");
    }


DishService


//修改菜品--1回显数据
        DishDto getWithFlavor (Long ids);
        //修改菜品--2保存数据
        void updateWithFlavor (DishDto dishDto);


DishServiceImpl


/**
     * //修改菜品--1回显数据
     * @param ids
     * @return
     */
    @Override
    public DishDto getWithFlavor(Long ids) {
        //查询菜品的基本信息
        Dish dish = this.getById(ids);
        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish,dishDto);
        //查询口味信息
        LambdaQueryWrapper<DishFlavor> qw = new LambdaQueryWrapper<>();
        qw.eq(DishFlavor::getDishId,dish.getId());
        List<DishFlavor> list = dishFlavorService.list(qw);
        dishDto.setFlavors(list);
        return dishDto;
    }

    /**
     * //修改菜品--2保存数据
     * @param dishDto
     */
    @Override
    public void updateWithFlavor(DishDto dishDto) {
        //更改基本信息
        this.updateById(dishDto);

        //删除原有口味
        LambdaQueryWrapper<DishFlavor> qw = new LambdaQueryWrapper<>();
        qw.eq(DishFlavor::getDishId,dishDto.getId());
        dishFlavorService.remove(qw);

        //添加更改之后的口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors.stream().map((item) -> {
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());
        dishFlavorService.saveBatch(flavors);
    }

16、停售/起售菜品,删除菜品

16.1、需求分析

--批量进行商品停售,起售以及删除方法

16.2、代码开发

因批量操作,我们客户端提交的数据为数组类型的ids

--1、商品停售,起售:

-从检查得知请求路径为"/status/{status}",客户端提交给服务器的数据有status和ids的数组

复杂java需求的截面 java需求文档例子_复杂java需求的截面_05

 --操作如下:

/**
     * 批量起售/停售
     * @param status
     * @param ids
     * @return
     */
    @PostMapping("/status/{status}")
    public R<String>stop(@PathVariable int status, String[] ids){
        for (String id:ids) {
            Dish dish = dishService.getById(id);
            dish.setStatus(status);
            dishService.updateById(dish);
        }return R.success("菜品状态更改成功");
    }
}

--2、删除操作:

-删除菜品同时要删除口味,多表操作

DishController:调用自定义的deleteWithFlavor

/**
     * 删除菜品-连带口味一起删除
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String>delete(String[] ids){
        dishService.deleteWithFlavor(ids);
            return R.success("菜品删除成功");
    }


DishService:


//删除菜品-连带口味一起删除
        void deleteWithFlavor (String[]ids);


DishServiceImpl


/**
     * 删除菜品-连带口味一起删除
     * @param ids
     */
    @Override
    public void deleteWithFlavor(String[] ids) {
        for (String id:ids) {
            this.removeById(id);
            LambdaQueryWrapper<DishFlavor> qw = new LambdaQueryWrapper<>();
            qw.eq(DishFlavor::getDishId,id);
            dishFlavorService.remove(qw);
        }
    }