新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmealdish表插入套餐和菜品关联数据。
所以在新增套餐时,涉及到两个表:
●setmeal套餐表
●setmeal_dish套餐菜品关系表
1.3代码开发5-31.3.1代码开发-准备工作5-3在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类SetmealDish(直接从课程资料中导入即可,Setmeal实体前面课程中已经导入过了)
●DTOSetmealDto(直接从课程资料中导入即可)
●Mapper接口SetmealDishMapper
●业务层接口SetmealDishService
●业务层实现类SetmealDishServicelmpl
●控制层SetmealController
套餐菜品关系实体类SetmealDishpackage com.itheima.reggie.entity;import com.baomidou.mybatisplus.annotation.FieldFill;import com.baomidou.mybatisplus.annotation.TableField;import lombok.Data;import java.io.Serializable;import java.math.BigDecimal;import java.time.LocalDateTime;/** * 套餐菜品关系 5-3 */@Datapublic class SetmealDish implements Serializable { private static final long serialVersionUID = 1L; private Long id; //套餐id private Long setmealId; //菜品id private Long dishId; //菜品名称 (冗余字段) private String name; //菜品原价 private BigDecimal price; //份数 private Integer copies; //排序 private Integer sort; @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;}
数据传输对象DTOSetmealDtopackage com.itheima.reggie.dto;import com.itheima.reggie.entity.Setmeal;import com.itheima.reggie.entity.SetmealDish;import lombok.Data;import java.util.List;//数据传输对象DTO 5-3@Datapublic class SetmealDto extends Setmeal { private List<SetmealDish> setmealDishes; private String categoryName;}
套餐管理持久层接口SetmealDishMapperpackage com.itheima.reggie.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.itheima.reggie.entity.SetmealDish;import org.apache.ibatis.annotations.Mapper;//套餐管理持久层接口 5-3@Mapperpublic interface SetmealDishMapper extends BaseMapper<SetmealDish> {}
套餐管理业务层接口SetmealDishServicepackage com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;import com.itheima.reggie.entity.SetmealDish;//套餐管理业务层接口 5-3public interface SetmealDishService extends IService<SetmealDish> {}
套餐管理业务层接口实现类SetmealDishServiceImplpackage com.itheima.reggie.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itheima.reggie.entity.SetmealDish;import com.itheima.reggie.mapper.SetmealDishMapper;import com.itheima.reggie.service.SetmealDishService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;//套餐管理业务层接口实现类 5-3@Service@Slf4jpublic class SetmealDishServiceImpl extends ServiceImpl<SetmealDishMapper,SetmealDish> implements SetmealDishService {}
套餐管理控制层SetmealControllerpackage com.itheima.reggie.controller;import com.itheima.reggie.service.SetmealDishService;import com.itheima.reggie.service.SetmealService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;//套餐管理控制层 5-3@RestController@RequestMapping("/setmeal")@Slf4jpublic class SetmealController { @Autowired private SetmealService setmealService; @Autowired private SetmealDishService setmealDishService;}
1.3.2业务开发在开发代码之前,需要梳理一下新增套餐时前端页面和服务端的交互过程:
1、页面(backend/page/combo/add.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中
2、页面发送ajax请求,请求服务端获取菜品分类数据并展示到添加菜品窗口中
3、页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中
4、页面发送请求进行图片上传,请求服务端将图片保存到服务器
5、页面发送请求进行图片下载,将上传的图片进行回显
6、点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端
开发新增套餐功能,其实就是在服务端编写代码去处理前端页面发送的这6次请求即可。
根据菜品分类的id查询菜品5-4菜品管理DishController //根据菜品分类的id查询菜品 5-4 @GetMapping("/list") public R<List<Dish>> list(Dish dish){ //构造查询条件对象 LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId()); //添加条件,查询状态为1的也就是起售的菜品 queryWrapper.eq(Dish::getStatus,1); //添加排序条件 queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); return R.success(list); }
新增套餐业务开发5-55-6注意setmealId没有赋值所以代码中我们需要手动赋值
套餐管理业务层接口SetmealService/** * 新增套餐,同时需要保存套餐和菜品的关联关系 5-6 * @param setmealDto */ public void saveWithDish(SetmealDto setmealDto);
套餐管理业务层接口实现类SetmealServiceImpl@Autowired private SetmealDishService setmealDishService; //新增套餐,同时需要保存套餐和菜品的关联关系 5-6 @Override @Transactional //事务 public void saveWithDish(SetmealDto setmealDto) { //保存套餐的基本信息,操作setmeal,执行insert操作 this.save(setmealDto); //保存套餐和菜品的关联信息,操作setmeal_dish,执行insert操作 List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes(); //因为通过debug看到套餐id setmealId没有赋值,所以我们需要手动添加 setmealDishes = setmealDishes.stream().map((item)->{ item.setSetmealId(setmealDto.getId()); return item; }).collect(Collectors.toList()); setmealDishService.saveBatch(setmealDishes); }
套餐管理控制层SetmealController //新增套餐 5-5 5-6 @PostMapping public R<String> save(@RequestBody SetmealDto setmealDto){ log.info("套餐信息 {}",setmealDto); setmealService.saveWithDish(setmealDto); return R.success("新增套餐成功"); }
测试成功
2.套餐信息的分页查询5-82.1需求分析5-82.2代码开发-梳理交互过程5-8在开发代码之前,需要梳理一下套餐分页查询时前端页面和服务端的交互过程:
1、页面(backend/page/combo/list.html)发送ajax请求,将分页查询参数(page、pageSize、
name)提交到服务端,获取分页数据
2、页面发送请求,请求服务端进行图片下载,用于页面图片展示
开发套餐信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
2.3代码开发5-9依然存在和菜品分页查询一样的问题,解决方法也是一样的
套餐管理控制层SetmealController//方便我们做套餐分页查询时使用 5-9 @Autowired private CategoryService categoryService;
套餐管理控制层SetmealController//套餐信息分页查询 5-9 @GetMapping("/page") public R<Page> page(int page,int pageSize,String name){ //分页构造器 Page<Setmeal> pageInfo = new Page<>(); Page<SetmealDto> setmealDtoPage = new Page<>(); //添加查询条件 LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>(); //添加查询条件 根据名字进行like模糊查询 queryWrapper.like(name!=null,Setmeal::getName,name); //添加排序条件,根据更新时间降序排列 queryWrapper.orderByDesc(Setmeal::getUpdateTime); //执行分页查询 //这条语句会将我们查询到的数据进行封装 全部封装进Page的对象pageInfo setmealService.page(pageInfo,queryWrapper); //此时这个情况和菜品的分页查询一样的原因,我们这里套餐分类的名字显示不出来因为 // 我们传的是id,而前端需要的是name 解决办法和菜品的分页查询一样 //拷贝pageInfo给setmealDtoPage,但是不拷贝records,因为pageInfo对应的泛型是Setmeal //而我们需要的是SetmealDto //这个拷贝也只是拷贝page对象的普通属性不包括records属性集合 BeanUtils.copyProperties(pageInfo,setmealDtoPage,"records"); List<Setmeal> records = pageInfo.getRecords(); List<SetmealDto> list = records.stream().map((item)->{ //这个setmealDto是我们新new出来的,里面的属性都是空的,需要我们给赋值 SetmealDto setmealDto = new SetmealDto(); //将Setmeal的普通属性拷贝给setmealDto BeanUtils.copyProperties(item,setmealDto); //得到套餐分类id Long categoryId = item.getCategoryId(); //根据套餐分类id查询出分类对象 Category category = categoryService.getById(categoryId); if(category!=null){ //得到套餐分类的名称 String categoryName = category.getName(); //将分类名赋值给setmealDto对象 setmealDto.setCategoryName(categoryName); } return setmealDto; }).collect(Collectors.toList());//将这些setmealDto对象收集起来封装成集合 //经过上面一番操作我们就得到了带有 套餐分类名称 的 泛型为setmealDto 的集合 setmealDtoPage.setRecords(list);//给setmealDtoPage的records赋值 return R.success(setmealDtoPage); }
3.删除套餐5-103.1业务需求5-103.2代码开发-梳理交互过程5-103.3代码开发5-11删除单个套餐
删除多个套餐
套餐管理业务层接口SetmealService /** * 删除套餐,同时需要删除套餐和菜品的关联数据 5-11 * @param ids */ public void removeWithDish(List<Long> ids);
套餐管理业务层接口实现类SetmealServiceImpl/** * 删除套餐,同时需要删除套餐和菜品的关联数据 5-11 * @param ids */ @Transactional public void removeWithDish(List<Long> ids) { //查询套餐状态,确定是否可用删除 LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper(); //select count(*) from setmeal where id in (1,2,3) and status = 1 queryWrapper.in(Setmeal::getId,ids); queryWrapper.eq(Setmeal::getStatus,1); //这里的思想是根据id和status查询,如果查询出来的数据大于0,就证明处于售卖状态,否则为停售状态 int count = this.count(queryWrapper); if(count > 0){ //如果不能删除,抛出一个业务异常 throw new CustomException("套餐正在售卖中,不能删除"); } //如果可以删除,先删除套餐表中的数据---setmeal this.removeByIds(ids); LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>(); //delete from setmeal_dish where setmeal_id in (1,2,3) lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids); //删除关系表中的数据----setmeal_dish setmealDishService.remove(lambdaQueryWrapper); }
套餐管理控制层SetmealController/** * 删除套餐 * @param ids * @return */ @DeleteMapping public R<String> delete(@RequestParam List<Long> ids){ log.info("ids:{}",ids); setmealService.removeWithDish(ids); return R.success("套餐数据删除成功"); }
测试
4.拓展,修改套餐自己实现代码在reggie_take_out5_1中分两步实现
4.1首先是将数据回显这个和菜品修改原理一模一样
套餐管理业务层接口SetmealService//根据套餐id来查询菜品信息和口味信息 自己实现 public SetmealDto getByIdWithDish(Long id);
套餐管理业务层接口实现类SetmealServiceImpl//根据套餐id查询套餐数据和对应的菜品 回显数据 自己实现 @Override public SetmealDto getByIdWithDish(Long id) { //因为没有SetmealDto这张表,所以我们分两张表查询,一个是Setmeal表一个是SetmealDish表 //首先查询Setmeal表,查到套餐的基本数据不包括套餐对应的菜品 Setmeal setmeal = this.getById(id); //new一个SetmealDto出来因为我们最终要返回的是SetmealDto因为它里面包括了套餐 // 的基本数据还有对应的菜品 //我们刚new出来的SetmealDto是空的,所以需要将查询到的数据设置进去 SetmealDto setmealDto = new SetmealDto(); BeanUtils.copyProperties(setmeal,setmealDto); //接着查询套餐菜品关系表,查出对应的菜品 //因为我们的参数id数套餐的id不是套餐菜品关系表的id,所以自己构造查询条件 LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper<>(); //使用套餐表id查询套餐菜品关系表 queryWrapper.eq(SetmealDish::getSetmealId,id); List<SetmealDish> list = setmealDishService.list(queryWrapper);//查出来肯定是集合 //将查出来的对应菜品放进setmealDto setmealDto.setSetmealDishes(list); return setmealDto; }
SetmealController//修改套餐 之回显数据 自己实现的 //根据套餐id来查询套餐信息和菜品信息 @GetMapping("/{id}") public R<SetmealDto> get(@PathVariable Long id){ //调用我们自己实现的方法,因为修改套餐涉及两张表 SetmealDto setmealDto = setmealService.getByIdWithDish(id); return R.success(setmealDto); }
4.2保存修改这个也是和修改菜品原理一样
套餐管理业务层接口SetmealService//更新套餐信息 同时更新菜品信息 自己实现 public void updateWithDish(SetmealDto setmealDto);
套餐管理业务层接口实现类SetmealServiceImpl//更新套餐信息同时更新对应菜品 自己实现 @Override @Transactional public void updateWithDish(SetmealDto setmealDto) { //还是一样分两张表来做 //首先更新套餐表 this.updateById(setmealDto); //更新套餐菜品关系表 //这里我们直接根据套餐id删除原来的菜品,在添加即可 LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SetmealDish::getSetmealId,setmealDto.getId()); setmealDishService.remove(queryWrapper); //往餐菜品关系表添加新数据 List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes(); //这里和添加一样的问题 继续为餐菜品关系表添加套餐id setmealDishes = setmealDishes.stream().map((item)->{ item.setSetmealId(setmealDto.getId()); return item; }).collect(Collectors.toList()); setmealDishService.saveBatch(setmealDishes); }
SetmealController//更新套餐信息 自己实现 @PutMapping public R<String> update(@RequestBody SetmealDto setmealDto){ //调用我们自己实现的方法,因为修改套餐涉及两张表 setmealService.updateWithDish(setmealDto); return R.success("套餐修改成功"); }
5.拓展停售起售状态修改自己实现代码在reggie_take_out5_1停售
起售
批量停售
批量起售
由上图可以这四种功能的请求路径都是一样的,且均使用了restful风格
套餐管理业务层接口SetmealService //批量停售起售 自己实现 public void stop(int status,List<Long> ids);
套餐管理业务层接口实现类SetmealServiceImpl @Autowired private SetmealService setmealService; //批量停售起售 自己实现 @Override public void stop(int status,List<Long> ids) { LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.in(Setmeal::getId,ids);//先构造id(可能一个也可能多个,因为我们单独停售起售和批量写一起了) //在构造售卖状态 Setmeal setmeal = new Setmeal(); setmeal.setStatus(status); setmealService.update(setmeal,queryWrapper); }
SetmealController//套餐的起售停售 批量停售 批量起售 自己实现 @PostMapping("/status/{status}") public R<String> update(@PathVariable int status,@RequestParam List<Long> ids){ //调用我们的方法 setmealService.stop(status,ids); return R.success("套餐售卖修改成功"); }
测试成功