文章目录
前言
套餐管理这部分主要涉及到的数据库表包括套餐表(setmeal)和菜品表(dish)以及一种套餐关联的所有菜品表(setmeal_dish),业务功能包括新增套餐、套餐分页查询、删除套餐、修改套餐、套餐起售停售。
一、新增套餐
1. 业务规则
- 套餐名称唯一
- 套餐必须属于某个分类
- 套餐必须包含菜品
- 名称、分类、价格、图片为必填项
- 添加菜品窗口需要根据分类类型来展示菜品
- 新增的套餐默认为停售状态
2. 接口设计
- 根据套餐类型查询分类(已完成)
- 根据分类id查询菜品
- 图片上传(已完成)
- 新增套餐
3.数据库设计
setmeal表为套餐表,用于存储套餐的信息。具体表结构如下:
| 字段名 | 数据类型 | 说明 | 备注 |
| ----------- | ------------- | ------------| -----------|
| id | bigint | 主键 | 自增 |
| name | varchar(32) | 套餐名称 | 唯一 |
| category_id | bigint | 分类id | 逻辑外键 |
| price | decimal(10,2) | 套餐价格 | |
| image | varchar(255) | 图片路径 | |
| description | varchar(255) | 套餐描述 | |
| status | int | 售卖状态 | 1起售 0停售 |
| create_time | datetime | 创建时间 | |
| update_time | datetime | 最后修改时间 | |
| create_user | bigint | 创建人id | |
| update_user | bigint | 最后修改人id | |
setmeal_dish表为套餐菜品关系表,用于存储套餐和菜品的关联关系。具体表结构如下:
| 字段名 | 数据类型 | 说明 | 备注 |
| ---------- | ------------- | -------- | -------- |
| id | bigint | 主键 | 自增 |
| setmeal_id | bigint | 套餐id | 逻辑外键 |
| dish_id | bigint | 菜品id | 逻辑外键 |
| name | varchar(32) | 菜品名称 | 冗余字段 |
| price | decimal(10,2) | 菜品单价 | 冗余字段 |
| copies | int | 菜品份数 | |
4. 代码实现
4.1 根据套餐类型查询菜品
1、在DishController中定义方法,用于为套餐添加菜品时使用
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<Dish>> list(Long categoryId){
List<Dish> list = dishService.list(categoryId);
return Result.success(list);
}
2、在DishService中定义list方法,在DishServiceImpl中实现业务逻辑
public List<Dish> list(Long categoryId) {
Dish dish = Dish.builder()
.categoryId(categoryId)
.status(StatusConstant.ENABLE)
.build();
return dishMapper.list(dish);
}
3、在DishMapper中定义方法,动态条件查询菜品
<select id="list" resultType="Dish" parameterType="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>
4.2 新增套餐
1、在SetmealController中定义方法
@PostMapping
@ApiOperation("新增套餐")
public Result saveSetmealwithDish(@RequestBody SetmealDTO setmealDTO){
setmealService.saveSetmealwithDish(setmealDTO);
return Result.success();
}
2、在SetmealService中定义saveSetmealwithDish方法,新增套餐,同时需要保存套餐和菜品的关联关系,在SetmealServiceImpl中实现方法
@Transactional //多表操作,开启事务注解
public void saveSetmealwithDish(SetmealDTO setmealDTO) {
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO, setmeal);
//1、向套餐表插入数据
setmealMapper.insert(setmeal);
//2、获取生成的套餐id
Long setmealId = setmeal.getId();
//获取这个套餐下所关联的所有菜品
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {
//把套餐id存入套餐_菜品表中
setmealDish.setSetmealId(setmealId);
});
//保存套餐和菜品的关联关系
setmealDishMapper.insertBatch(setmealDishes);
}
3、在SetmealMapper中编写方法新增套餐,在SetmealMapper.xml中编写sql语句
@AutoFill(OperationType.INSERT)
void insert(Setmeal setmeal);
<insert id="insert" 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>
4、在SetmealDishMapper中编写方法批量保存套餐和菜品的关联关系,在SetmealDishMapper.xml中编写sql语句
<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>
二、套餐分页查询
1. 业务规则
- 根据页码进行分页展示
- 每页展示10条数据
- 可以根据需要,按照套餐名称、分类、售卖状态进行查询
2. 接口设计
3.代码实现
1、在SetmealController中编写方法
@GetMapping("/page")
@ApiOperation("分页查询套餐信息")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){
log.info("套餐分页查询:{}",setmealPageQueryDTO);
PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);
return Result.success(pageResult);
}
2、在SetmealService定义pageQuery方法,在SetmealServiceImpl中实现
public PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {
PageHelper.startPage(setmealPageQueryDTO.getPage(),setmealPageQueryDTO.getPageSize());
Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
3、在SetmealMapper中定义方法,在SetmealMapper.xml中编写sql语句查询套餐信息。返回的参数类型是setmealvo对象,关联了两张表的信息,要进行多表联查。
<select id="pageQuery" resultType="com.sky.vo.SetmealVO">
select s.*,c.name categoryName from sky_take_out.setmeal s
left join sky_take_out.category c on s.category_id = c.id
<where>
<if test="name != null">and s.name like concat('%',#{name},'%') </if>
<if test="status != null">and s.status = #{status} </if>
<if test="categoryId != null">and s.category_id = #{categoryId}</if>
</where>
order by s.create_time desc
</select>
三、删除套餐
1. 业务规则
- 可以一次删除一个套餐,也可以批量删除套餐
- 起售中的套餐不能删除
2. 接口设计
3. 代码实现
1、在SetmealController中编写方法,根据前端响应过来的套餐id
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List<Long> ids){
setmealService.deleteBatch(ids);
return Result.success();
}
2、在SetmealService定义方法,在SetmealServiceImpl中实现方法
@Transactional
public void deleteBatch(List<Long> ids) {
//判断当前套餐是否能够删除--是否存在起售中的套餐 status
for (Long id : ids) {
Setmeal setmeal = setmealMapper.getById(id);
if(setmeal.getStatus() == StatusConstant.ENABLE){//当前菜品处于起售中,不能删除
throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
}
}
//批量删除套餐
setmealMapper.deleteByIds(ids);
//还要删除套餐关联的菜品信息
setmealDishMapper.deleteBysetmealIds(ids);
}
3、在SetmealMapper定义getByI方法,根据id查询套餐
@Select("select * from setmeal where id = #{id}")
Setmeal getById(Long id);
4、在SetmealMapper定义deleteByIds方法,在SetmealMappe.xml中编写sql语句批量删除套餐
<delete id="deleteByIds">
delete from sky_take_out.setmeal where id in
<foreach collection="ids" item="id" open="(" close=")">#{id}</foreach>
</delete>
5、在SetmealDishMapper定义deleteBysetmealIds方法,在SetmealMappe.xml中编写sql语句批量删除套餐关联的菜品信息
<delete id="deleteBysetmealIds">
delete from sky_take_out.setmeal_dish where setmeal_id in
<foreach collection="setmealIds" item="sid" open="(" close=")">#{sid}</foreach>
</delete>
四、修改套餐
1. 接口设计
- 根据id查询套餐(信息回显)
- 根据类型查询分类(已完成)
- 根据分类id查询菜品(已完成)
- 图片上传(已完成)
- 修改套餐
2. 代码实现
2.1 根据id查询套餐
1、在SetmealController定义getById方法
@GetMapping("/{id}")
@ApiOperation("根据套餐id查询套餐")
public Result<SetmealVO> getById(@PathVariable Long id){
SetmealVO setmealVO = setmealService.getByIdWithDish(id);
return Result.success(setmealVO);
}
2、在SetmealService中定义getByIdWithDish方法,在getByIdWithDishImpl中实现
public SetmealVO getByIdWithDish(Long id) {
//根据id查询套餐信息
Setmeal setmeal = setmealMapper.getById(id);
SetmealVO setmealVO = new SetmealVO();
BeanUtils.copyProperties(setmeal,setmealVO);
//根据套餐id查询对应的菜品信息
List<SetmealDish> setmealDishes = setmealDishMapper.getBySetmealId(setmeal.getId());
setmealVO.setSetmealDishes(setmealDishes);
return setmealVO;
}
3、在SetmealMapper中定义getById方法
@Select("select * from sky_take_out.setmeal where id = #{id}")
Setmeal getById(Long id);
4、在SetmealDishMapper中定义getBySetmealId方法
@Select("select * from sky_take_out.setmeal_dish where setmeal_id=#{setmealId}")
List<SetmealDish> getBySetmealId(Long setmealId);
2.2 修改套餐
1、在SetmealController定义update方法
@PutMapping
@ApiOperation("修改套餐")
public Result update(@RequestBody SetmealDTO setmealDTO) {
setmealService.updateWithDish(setmealDTO);
return Result.success();
2、在SetmealService中定义updateWithDish方法,在getByIdWithDishImpl中实现
@Transactional
public void updateWithDish(SetmealDTO setmealDTO) {
//1.修改套餐的基本信息
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO,setmeal);
setmealMapper.update(setmeal);
//2 删除套餐和菜品的关联关系,操作setmeal_dish表,执行delete
Long setmealId = setmeal.getId(); //得到套餐id
setmealDishMapper.deleteBysetmealId(setmealId);
//3 插入新的套餐和菜品的关联关系,操作setmeal_dish表,执行insert
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {
setmealDish.setSetmealId(setmealId);
});
setmealDishMapper.insertBatch(setmealDishes);
}
3、在SetmealMapper中定义update方法,在SetmealMapper.xml中编写sql语句
<update id="update">
update sky_take_out.setmeal
<set>
<if test="name != null">name = #{name},</if>
<if test="categoryId != null">category_id = #{categoryId},</if>
<if test="price != null">price = #{price},</if>
<if test="image != null">image = #{image},</if>
<if test="description != null">description = #{description},</if>
<if test="status != null">status = #{status},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUser != null">update_user = #{updateUser},</if>
</set>
where id = #{id}
</update>
3、在SetmealDishMapper中定义deleteBysetmealId方法和insertBatch方法,在SetmealDishMapper.xml中编写sql语句
//删除单个与套餐有关联的菜品信息
@Delete("delete from sky_take_out.setmeal_dish where setmeal_id = #{setmealId};")
void deleteBysetmealId(Long setmealId);
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
insert into sky_take_out.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>
五、起售停售套餐
1. 业务规则
- 可以对状态为起售的套餐进行停售操作,可以对状态为停售的套餐进行起售操作
- 起售的套餐可以展示在用户端,停售的套餐不能展示在用户端
- 起售套餐时,如果套餐内包含停售的菜品,则不能起售
2. 接口设计
3. 代码实现
1、在SetmealController编写startOrStop方法
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
public Result startOrStop(@PathVariable Integer status,Long id ){
setmealService.startOrStop(id,status);
return Result.success();
}
2、在SetmealService中定义startOrStop方法,在startOrStopImpl中实现
public void startOrStop(Long id, Integer status) {
//起售套餐时,判断套餐内是否有停售的菜品,有停售的菜品提示"套餐内包含未启售菜品,无法启售
if(status == StatusConstant.ENABLE){
//通过套餐id得到菜品信息
List<Dish> dishList = dishMapper.getBySetmealId(id);
if(dishList != null && dishList.size() > 0){
dishList.forEach(dish -> {
if(StatusConstant.DISABLE == dish.getStatus()){
throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
}
});
}
}
Setmeal setmeal = Setmeal.builder()
.id(id)
.status(status)
.build();
setmealMapper.update(setmeal);
}
3、在DishMapper中定义getBySetmealId方法,根据套餐id查询菜品
@Select("select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = #{setmealId}")
List<Dish> getBySetmealId(Long setmealId);