前言
本篇博客用于记录苍穹外卖Day04套餐管理的学习。由于本章节课程中直接跳过,所以我会全过程进行编码(service接口的代码就省略了)。有什么问题欢迎在评论区指出,可以一起探讨一下
注意:许多思路在代码块使用注释写出来了可以参考,建议自己先按自己的思路编码再来参考我的。
新增套餐
该接口有两个接口需要我们完成开发:1.根据分类id查询菜品 2.新增套餐
根据分类id查询菜品
在该接口,要求我们点击新增套餐->添加菜品后出现以下界面:
如图所示,左边展示出菜品分类(菜品分类只展示type=1同时status=1的菜品分类),中间展示出该分类下的菜品。
分析接口文档,我们可知传递的参数为categoryId,返回数据为一个菜品集合
编码如下:
DishController:
//根据分类id查询菜品
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<Dish>> list(Long categoryId){//这里的参数记得要是categoryId
log.info("根据分类id查询菜品:{}",categoryId);
List<Dish> list=dishService.getByCategoryId(categoryId);
return Result.success(list);
}
DishServiceImpl:
//根据分类id查询菜品
@Override
public List<Dish> getByCategoryId(Long categoryId) {
//封装好一个Dish对象
Dish dish=new Dish();
dish.setCategoryId(categoryId);//根据categoryId查询菜品
dish.setStatus(StatusConstant.ENABLE);//只查询status=1的菜品
List<Dish> list=dishMapper.list(dish);
return list;
}
DishMapper:
List<Dish> list(Dish dish);
DishMapper.xml:
<!--根据分类id查询菜品-->
<select id="list" resultType="com.sky.entity.Dish">
select * from dish
<where>
<if test="name!=null">
and name like concat('%',#{name},'%')
</if>
<if test="categoryId!=null">
and category_id=#{categoryId}
</if>
<if test="status!=null">
and status=#{status}
</if>
</where>
order by create_time desc
</select>
新增套餐
在该接口,我们不仅要保存套餐信息,还要保存套餐与菜品间的关联信息。
分析接口文档,我们可知传递的参数为SetmealDto,返回插入成功即可
SetmealController:
@RestController
@RequestMapping("/admin/setmeal")
@Slf4j
@Api(tags = "套餐相关接口")
public class SetmealController {
@Autowired
private SetmealService setmealService;
//新增套餐
@PostMapping
@ApiOperation("新增套餐")
private Result save(@RequestBody SetmealDTO setmealDTO){
log.info("新增套餐:{}",setmealDTO);
setmealService.save(setmealDTO);
return Result.success();
}
}
SetmealServiceImpl:
@Service
public class SetmealServiceImpl implements SetmealService {
@Autowired
private SetmealMapper setmealMapper;
@Autowired
private SetmealDishMapper setmealDishMapper;
//新增套餐
@Override
@Transactional//保证两次插入操作要么同时成功,要么同时失败
public void save(SetmealDTO setmealDTO) {
//同时保存套餐与菜品的关联信息
//1.新增套餐
Setmeal setmeal=new Setmeal();
BeanUtils.copyProperties(setmealDTO,setmeal);
setmealMapper.save(setmeal);
//获取插入套餐后生成的主键值(id)
Long setmealId=setmeal.getId();
//2.保存套餐与菜品的关联信息
//获取套餐中的菜品
List<SetmealDish> setmealDishes=setmealDTO.getSetmealDishes();
for (SetmealDish setmealDish : setmealDishes) {
//将套餐id设置进SetmelDish对象中
setmealDish.setSetmealId(setmealId);
}
//将套餐id、菜品id等信息保存进setmeal_dish表
setmealDishMapper.insertBatch(setmealDishes);
}
}
新增套餐:
SetmealMapper:
@AutoFill(OperationType.INSERT)
void save(Setmeal setmeal);
SetmealMapper.xml:
<!-- 记得插入之后要将主键值(id)返回-->
<insert id="save" parameterType="Setmeal" useGeneratedKeys="true" keyProperty="id">
insert into setmeal
(category_id,name,price,status,description,image,create_time,update_time,create_user,update_user)
values
(#{categoryId},#{name},#{price},#{status},#{description},#{image},#{createTime},#{updateTime},#{createUser},#{updateUser})
</insert>
保存套餐与菜品的关联信息:
SetmealDishMapper:
void insertBatch(List<SetmealDish> setmealDishes);
SetmealMapper.xml:
<!-- 插入套餐与菜品关联信息-->
<insert id="insertBatch" parameterType="list">
insert into setmeal_dish
(setmeal_id,dish_id,name,price,copies)
values
<foreach collection="setmealDishes" item="sd" separator=",">
(#{sd.setmealId},#{sd.dishId},#{sd.name},#{sd.price},#{sd.copies})
</foreach>
</insert>
分页查询
该接口与前面菜品管理的接口差不多
SetmealController:
@GetMapping("/page")
@ApiOperation("套餐分页查询")
private Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){
log.info("套餐分页查询:{}",setmealPageQueryDTO);
PageResult pageResult=setmealService.page(setmealPageQueryDTO);
return Result.success(pageResult);
}
SetmealServiceImpl:
@Override
public PageResult page(SetmealPageQueryDTO setmealPageQueryDTO) {
//开启分页查询
PageHelper.startPage(setmealPageQueryDTO.getPage(),setmealPageQueryDTO.getPageSize());
Page<SetmealVO> page=setmealMapper.page(setmealPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
SetmealMapper:
Page<SetmealVO> page(SetmealPageQueryDTO setmealPageQueryDTO);
SetmealMapper.xml:
<!--分页查询-->
<select id="page" resultType="com.sky.vo.SetmealVO">
select s.*,c.name categoryName from setmeal s left join category c on s.category_id=c.id
<where>
<if test="name!=null">
and s.name like concat('%',#{name},'%')
</if>
<if test="categoryId!=null">
and s.category_id=#{categoryId}
</if>
<if test="status!=null">
and s.status=#{status}
</if>
</where>
order by create_time desc
</select>
删除套餐
删除套餐与上面删除类似,可以一个一个删,也可以批量删除。还有一个就是起售状态的套餐不能删除。
SetmealController:
@DeleteMapping
@ApiOperation("删除套餐")
private Result delete(@RequestParam List<Long> ids){
log.info("删除套餐:{}",ids);
setmealService.delete(ids);
return Result.success();
}
SetmealServiceImpl:
@Override
@Transactional
public void delete(List<Long> ids) {
//判断套餐起售状态
for (Long id : ids) {
Setmeal setmeal=setmealMapper.getById(id);
if(setmeal.getStatus()== StatusConstant.ENABLE){
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//删除套餐同时删除套餐与菜品的关联信息
for (Long id : ids) {
//删除套餐表中的数据
setmealMapper.deleteById(id);
//删除套餐菜品表中的关联数据
setmealDishMapper.deleteBySetmealId(id);
}
}
SetmealMapper:
@Select("select * from setmeal where id=#{id}")
Setmeal getById(Long id);
@Delete("delete from setmeal where id=#{id}")
void deleteById(Long id);
SetmealDishMapper:
@Delete("delete from setmeal_dish where setmeal_id=#{setmealId}")
void deleteBySetmealId(Long id);
修改套餐
与之前的修改操作也差不多,简单介绍一下。在该接口,我们需要完成两步操作:1.根据id查询套餐,将原来的套餐信息展示出来先 2.修改套餐
SetmealController:
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
private Result<SetmealVO> getById(@PathVariable Long id){
log.info("根据id查询套餐:{}",id);
SetmealVO setmealVO=setmealService.getSetmealById(id);
return Result.success(setmealVO);
}
@PutMapping
@ApiOperation("更新套餐")
private Result update(@RequestBody SetmealDTO setmealDTO){
log.info("更新套餐:{}",setmealDTO);
setmealService.update(setmealDTO);
return Result.success();
}
SetmealServiceImpl:
查询套餐时,我们不仅要查套餐信息,还要查套餐关联的菜品的信息。将查到的套餐信息和菜品信息封装成一个SetmealVO对象返回给前端。修改套餐信息时,我们先要修改套餐的信息,然后要将套餐原来关联的菜品信息删除,重新关联新的菜品信息,以此达到一个修改套餐关联菜品的操作
//根据id查询套餐及套餐菜品信息
@Override
public SetmealVO getSetmealById(Long id) {
//查询套餐
Setmeal setmeal=setmealMapper.getById(id);
//获取套餐菜品关联信息
List<SetmealDish> setmealDishes=setmealDishMapper.getBySetmealId(id);
//封装SetMeal对象
SetmealVO setmealVO=new SetmealVO();
BeanUtils.copyProperties(setmeal,setmealVO);
setmealVO.setSetmealDishes(setmealDishes);
//将套餐信息以及套餐菜品信息返回
return setmealVO;
}
//修改套餐信息
@Override
public void update(SetmealDTO setmealDTO) {
Setmeal setmeal=new Setmeal();
BeanUtils.copyProperties(setmealDTO,setmeal);
//1.修改套餐信息
setmealMapper.update(setmeal);
Long setmealId=setmealDTO.getId();
//2.删除套餐与菜品间的联系
setmealDishMapper.deleteBySetmealId(setmealId);
//3.重新建立套餐与菜品间的联系
List<SetmealDish> setmealDishes=setmealDTO.getSetmealDishes();
for (SetmealDish setmealDish : setmealDishes) {
setmealDish.setSetmealId(setmealId);
}
setmealDishMapper.insertBatch(setmealDishes);
}
SetmealDishMapper: 查询套餐关联菜品的信息
@Select("select * from setmeal_dish where setmeal_id=#{id}")
List<SetmealDish> getBySetmealId(Long id);
SetmealMapper:
@AutoFill(OperationType.UPDATE)
void update(Setmeal setmeal);
SetmealMapper.xml:
<update id="update">
update setmeal
<set>
<if test="categoryId!=null">
category_id=#{categoryId},
</if>
<if test="name!=null">
name=#{name},
</if>
<if test="price!=null">
price=#{price},
</if>
<if test="status!=null">
status=#{status},
</if>
<if test="description!=null">
description=#{description},
</if>
<if test="image!=null">
image=#{image},
</if>
<if test="createTime!=null">
create_time=#{createTime},
</if>
<if test="updateTime!=null">
update_time=#{updateTime},
</if>
<if test="createUser!=null">
create_user=#{createUser},
</if>
<if test="updateTime!=null">
update_time=#{updateTime}
</if>
</set>
where id=#{id}
</update>
起售停售
在此接口主要要注意一个问题:当套餐状态由停售改为起售时,如果套餐内包含停售菜品时,无法起售
SetmealController:
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
private Result status(@PathVariable Integer status,Long id){
log.info("套餐起售停售:{}{}",status,id);
setmealService.status(status,id);
return Result.success();
}
SetmealServiceImpl:
@Override
public void status(Integer status, Long id) {
//将停售套餐变为起售时,先判断套餐内是否包含停售菜品,如果包含,则无法起售
if(status==StatusConstant.ENABLE){
//根据套餐id查菜品表,返回一个集合
List<Dish> dishes = dishMapper.getDish(id);
if(dishes!=null&&dishes.size()>0){
for (Dish dish : dishes) {
//菜品集合中包含停售菜品时
if(dish.getStatus()==StatusConstant.DISABLE){
throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
}
}
}
}
//修改状态
Setmeal setmeal=new Setmeal();
setmeal.setStatus(status);
setmeal.setId(id);
setmealMapper.update(setmeal);
}
DishMapper:
分析一下这个SQL语句,我们要根据套餐的id查询菜品数据,很显然这涉及到多表查询。因为我们传递进来的id是套餐的id,所以查询条件是sd.setmeal_id=#{id}",dish表和setmeal_dish表的连接条件是菜品表的id等于套餐菜品表的菜品id,所以在SQL语句中dish.id=setmeal_dish.dish_id
@Select("select d.* from dish d left join setmeal_dish sd on d.id=sd.dish_id where sd.setmeal_id=#{id}")
List<Dish> getDish(Long id);