瑞吉外卖业务开发(3)

简介: 瑞吉外卖业务开发

瑞吉外卖业务开发(2)https://developer.aliyun.com/article/1530403

需求分析

  • 员工列表页面可以对员工账号进行启用和禁用处理,账号禁用的员工不能登录系统,启用后方可登录系统
  • 需要注意的是,只有管理员(admin用户)可以对其他用户进行启用,禁用操作,所以普通用户登录系统后启用,禁用按钮不显示.


    前端有一个名字为user的模型数据来之前存储的用户信息,如果user是admin也就是登录的用户是管理与,那么将显示禁用与启用信息.

代码开发

在开发业务之前需要把用到的类和接口基本结构准备好

  • 实体类category
  • Mapper接口CategoryMapper
  • 业务层接口CategoryService
  • 业务层实现类CategoryServiceImpl
  • 控制层CategoryController

操作流程

1、页面(backend/page/category/list.html)发送ajax请求,将新增分类窗口输入的数据以json形式提交到服务端

2、服务端Controller接收页面提交的数据并调用Service将数据进行保存

3、Service调用Mapper操作数据库,保存数据

可以发现新增菜品分类和新增套餐分类的请求服务端地址和JSon格式相同,所以服务端只要提供一个方法处理即可

@RestController  
@RequestMapping("/category")  
public class CategoryController {  
    @Autowired  
    private CategoryService categoryService;  
   @PostMapping  
    public R<String > save(@RequestBody Category category){  
    log.info("category{}",category);  
    categoryService.save(category);  
    return R.success("新增分类成功");  
}  
}

删除分类

需求分析

在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时,此分类不允许删除。

代码开发

在开发代码之前,需要梳理一下整个程序的执行过程:

1、页面发送ajax请求,将参数(id)提交到服务端

2、服务端Controller接收页面提交的数据并调用Service删除数据

3、Service调用Mapper操作数据库

@DeleteMapping  
public R<String> delete(Long id){  
    categoryService.removeById(id);  
    return R.success("删除成功")  
}

完善

和菜品和套餐已经连接的分类不能进行删除,因为我们没有设置外键,所以要用代码实现

@DeleteMapping  
public R<String> delete(Long ids){  
   categoryService.remove(ids);  
   return R.success("删除成功");  
}

[!note]

要在service中自己定义方法,而且可以调用别的service的方法

public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {  
    @Autowired  
    private DishService dishService;  
    @Autowired  
    private SetmealService setmealService;  
    @Override  
    public void remove(Long id) {  
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();  
        queryWrapper.eq(Dish::getCategoryId,id);  
        int count = dishService.count(queryWrapper);  
        if (count>0){  
            throw new CustomException("当期分类下关联了菜品,不能删除");  
        }  
        LambdaQueryWrapper<Setmeal> queryWrapper1 = new LambdaQueryWrapper<>();  
        queryWrapper1.eq(Setmeal::getCategoryId,id);  
        int count1 = setmealService.count(queryWrapper1);  
  
        if (count1>0){  
            throw new CustomException("当期分类下关联了套餐,不能删除");  
  
        }  
        //查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常  
        //查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常  
//正常删除分类  
        super.removeById(id);  
  
    }  
}

文件上传与下载

文件上传介绍

文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。

form表单的要求

  • method=“post” 采用post的方式提交数据
  • enctype=multpart/form-data" 采用multipart格式上传文件
  • type=“file” 使用input的file控件上传

举例

<form method="post" action="/common/upload" enctype="multipart/form-data"><input name="myFile" type="file" />
<input type="submit" value="提交" /></form>

服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:

  • commons-fileuplolad
  • commons-io
    Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如:
@Slf4j  
@RestController  
@RequestMapping("/common")  
public class CommonController {  
    @Value("/${reggie.path}")  
    private String basePath;  
    @PostMapping("/upload")  
    public R<String> upload(MultipartFile file) throws IOException {  
  
        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();  
        }  
        log.info(basePath+fileName);  
        //file是一个临时文件需要指定保存的位置,否则本次请求完成后文件会被自动删除  
        file.transferTo(new File(basePath+ fileName));  
        return R.success(fileName);  
  
    }  

文件下载

@GetMapping("/download")  
public void download(String  name, HttpServletResponse response){  
  
    try {  
        //输入流,通过输入流读取文件内容  
        FileInputStream fileInputStream = new FileInputStream(basePath+name);  
        //输出流,通过输出流把文件写会浏览器,在浏览器展示图片  
        ServletOutputStream outputStream = response.getOutputStream();  
        int len=0;  
        byte[] bytes = new byte[1024];  
        while ((len=fileInputStream.read(bytes))!=-1){  
            outputStream.write(bytes,0,len);  
            outputStream.flush();  
        }  
        response.setContentType("image/jpeg");  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
}

新增菜品

需求分析

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

数据模型

新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。所以在新增菜品时,涉及到两个表:

  • dish菜品表
  • dish_flavor菜品口味表

代码开发

准备工作

实体类DishFlavor(直接从课程资料中导入即可,Dish实体前面课程中已经导入过了)

Mapper接口DishFlavorMapper

业务层接口DishFlavorService

业务层实现类DishFlavorServicelmpl

控制层DishController

在开发代码之前,需要梳理一下新增菜品时前端页面和服务端的交互过程:

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

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

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

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

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

导入dto

@Data  
public class DishDto extends Dish {  
  
    private List<DishFlavor> flavors = new ArrayList<>();  
  
    private String categoryName;  
  
    private Integer copies;  
}

[!danger]

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

service

@Autowired  
private DishFlavorService dishFlavorService;  
//添加事务控制  
@Transactional  
@Override  
public void saveWithFlavor(DishDto dishDto) {  
//使用本身的save保存菜品
    this.save(dishDto);  
    Long id = dishDto.getId();  
    List<DishFlavor> flavors = dishDto.getFlavors();  
   flavors=  flavors.stream().map((item)->{  
        item.setDishId(id);  
        return item;  
    }).collect(Collectors.toList());  
  //保存口味信息
    dishFlavorService.saveBatch(flavors);  
}

controller

[[springmvc#9.1、@RequestBody]]

@Autowired  
    private DishService dishService;  
    @Autowired  
    private DishFlavorService dishFlavorService;  
@PostMapping  
    public R<String> save(@RequestBody DishDto dishDto){  
        dishService.saveWithFlavor(dishDto);  
        return R.success("新增菜品成功");  
    }

菜品信息分页查询

系统中的菜品数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。

代码开发

梳理交互流程

在开发代码之前,需要梳理一下菜品分页查询时前端页面和服务端的交互过程:

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

2、页面发送请求,请求服务端进行图片下载,用于页面图片展示

开发菜品信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。

controller

@GetMapping("/page")  
public R<Page> page(int page,int pageSize,String name){  
Page<Dish> pageInfo = new Page<>(page,pageSize);  
 LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();  
 queryWrapper.like(name!=null,Dish::getName,name);  
 queryWrapper.orderByDesc(Dish::getCreateTime);  
 dishService.page(pageInfo,queryWrapper);  
 return R.success(pageInfo);  
}

但是有个问题

菜品表中的菜品分类字段是分类的id,前端要求的是菜品分类名称,这样才能展示

所以我们要去处理一下

[!note]

因为原本的Dish里面没有categoryName这个属性,所以要借助DishDto,但是如何根据条件把Dto中的categoryName设置成我们需要的值,并且其余Dish有的我们都要有呢,需要先拷贝pageinfo中除了records的数据,(records)s是查询出来的数据列表,并不是我们需要的,需要把records处理好然后放回去,最后返回的是DishDto类型的Page

#工具类/对象拷贝

BeanUtils.copyProperties

controller

@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::getCreateTime);  
     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();  
            //拷贝Dish里面的属性到DishDto  
            BeanUtils.copyProperties(item,dishDto);  
            //查询到分类id  
            Long categoryId = item.getCategoryId();  
            Category category = categoryService.getById(categoryId);  
            //通过分类id查询到分类名称  
            String categoryName = category.getName();  
            dishDto.setCategoryName(categoryName);  
            return  dishDto;  
//最后通过collect收集成集合  
        }).collect(Collectors.toList());  
  
        dishDtoPage.setRecords(list);  
        //最后传回去的DishDto类型的分页信息  
        return R.success(dishDtoPage);  
  
    }

修改菜品

需求分析

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

代码开发

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

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

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

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

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

service中自定义查询来完成查询的功能

public DishDto getByIdWithFlavor(Long id) {  
  
    //查询菜品基本信息  
    Dish dish = this.getById(id);  
  
    DishDto dishDto = new DishDto();  
    BeanUtils.copyProperties(dish,dishDto);  
    //查询当前菜品的口味信息  
    LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();  
    queryWrapper.eq(DishFlavor::getDishId,dish.getId());  
      
    List<DishFlavor> list = dishFlavorService.list(queryWrapper);  
    dishDto.setFlavors(list);  
    return dishDto;  
}

controller

因为我们要同时返回菜品信息和口味信息,但是这两个表是单独的,我们的dto中包含了我们需要的属性,完成dto的封装需要查询到dish信息和flavor信息然后封装进去返回

@GetMapping("/{id}")  
public R<DishDto> get(@PathVariable Long id){  
    DishDto byIdWithFlavor = dishService.getByIdWithFlavor(id);  
    return R.success(byIdWithFlavor);  
}


瑞吉外卖业务开发(4)https://developer.aliyun.com/article/1530409

相关文章
|
运维 前端开发 测试技术
瑞吉外卖业务开发(1)
瑞吉外卖业务开发
252 3
|
存储 关系型数据库 MySQL
[重磅更新]PolarDB-X V2.3 集中式和分布式一体化开源发布
2023年云栖大会,PolarDB-X 正式发布 2.3.0版本,重点推出PolarDB-X标准版(集中式形态),将PolarDB-X分布式中的DN节点提供单独服务,支持paxos协议的多副本模式、lizard分布式事务引擎,可以100%兼容MySQL。同时在性能场景上,采用生产级部署和参数(开启双1 + Paxos多副本强同步),相比于开源MySQL 8.0.34,PolarDB-X在读写混合场景上有30~40%的性能提升,可以作为开源MySQL的最佳替代选择。
|
JSON 前端开发 Java
瑞吉外卖业务开发(2)
瑞吉外卖业务开发
219 3
|
前端开发 容器
CSS【详解】定位 position (静态定位 static -- 文档流排布 、相对定位 relative、绝对定位 absolute、固定定位 fixed、黏性定位 sticky)
CSS【详解】定位 position (静态定位 static -- 文档流排布 、相对定位 relative、绝对定位 absolute、固定定位 fixed、黏性定位 sticky)
1143 0
|
机器学习/深度学习 算法 语音技术
自适应线性单元| 学习笔记
快速学习自适应线性单元。
自适应线性单元| 学习笔记
|
Web App开发 存储 JavaScript
WebAssembly 初体验
WebAssembly 初体验
601 0
WebAssembly 初体验
|
机器学习/深度学习 存储 算法
【基础算法训练】—— 01背包 + 排序
【基础算法训练】—— 01背包 + 排序
399 0
【基础算法训练】—— 01背包 + 排序
|
存储 分布式计算 关系型数据库
【ZooKeeper】① 分布式的基本概念
一个 SpringBoot 项目(apple.jar)被部署到了服务器上运行。可向其发送网络请求获取网络资源。随着请求数量的逐渐增多,服务器宕机(死机)的可能性也越来越高。 若一个服务器宕机会导致该服务器上的某个系统直接无法被访问,则不是高可用的项目,便产生了单点故障。单点故障:服务器与项目共生。服务器生,项目活;服务器挂,项目死。
321 0
【ZooKeeper】① 分布式的基本概念
|
存储 NoSQL 安全
Spring Security系列教程21--会话管理之实现集群会话
前言 现在我们已经掌握了如何防御会话固定攻击,处理会话过期,对会话进行并发控制等,但是这些会话处理手段都是针对单机环境下的,在现在的大型项目中,很多时候都是采用分布式开发方案。一旦涉及到分布式方案,就意味着我们的服务器可能会有多台,而我们的项目也可能会根据业务被拆分成了若干个子服务,每个服务又可能被部署在不同的服务器上。这时候问题就来了,以前单台服务器的时候,我们的会话很好管理,现在有多台服务器,那会话岂不是有多个了?这时候我们把服务器集群环境下的会话和单个用户关联起来? 啊啊啊...是不是感觉很复杂! 别害怕!Spring Security其实给我们提供了对应的解决方案,就是 一一哥 今
849 0
|
存储 SQL 分布式计算
Hologres兼容PostgreSQL生态
Hologres兼容PostgreSQL生态
629 0