目录
1. 新增套餐
1.1 需求分析和设计
1.2 代码实现
1.2.1 DishController
1.2.2 DishService
1.2.3 DishServiceImpl
1.2.4 DishMapper
1.2.5 DishMapper.xml
1.2.6 SetmealController
1.2.7 SetmealService
1.2.8 SetmealServiceImpl
1.2.9 SetmealMapper
1.2.10 SetmealMapper.xml
1.2.11 SetmealDishMapper
1.2.12 SetmealDishMapper.xml
2. 套餐分页查询
2.1 需求分析和设计
2.2 代码实现
2.2.1 SetmealController
2.2.2 SetmealService
2.2.3 SetmealServiceImpl
2.2.4 SetmealMapper
2.2.5 SetmealMapper.xml
3. 删除套餐
3.1 需求分析和设计
3.2 代码实现
3.2.1 SetmealController
3.2.2 SetmealService
3.2.3 SetmealServiceImpl
3.2.4 SetmealMapper
3.2.5 SetmealDishMapper
4. 修改套餐
4.1 需求分析和设计
4.2 代码实现
4.2.1 SetmealController
4.2.2 SetmealService
4.2.3 SetmealServiceImpl
4.2.4 SetmealDishMapper
5. 起售停售套餐
5.1 需求分析和设计
5.2 代码实现
5.2.1 SetmealController
5.2.2 SetmealService
5.2.3 SetmealServiceImpl
5.2.4 DishMapper
完成套餐管理模块所有业务功能,包括:
新增套餐
套餐分页查询
删除套餐
修改套餐
起售停售套餐
要求:
根据产品原型进行需求分析,分析出业务规则
设计接口
梳理表之间的关系(分类表、菜品表、套餐表、口味表、套餐菜品关系表)
根据接口设计进行代码实现
分别通过swagger接口文档和前后端联调进行功能测试
业务规则:
套餐名称唯一
套餐必须属于某个分类
套餐必须包含菜品
名称、分类、价格、图片为必填项
添加菜品窗口需要根据分类类型来展示菜品
新增的套餐默认为停售状态
接口设计(共涉及到4个接口):
根据类型查询分类(已完成)
根据分类id查询菜品
图片上传(已完成)
新增套餐
数据库设计:
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 | 菜品份数 |
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result> list(Long categoryId){
List list = dishService.list(categoryId);
return Result.success(list);
}
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
List list(Long categoryId);
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
public List list(Long categoryId) {
Dish dish = Dish.builder()
.categoryId(categoryId)
.status(StatusConstant.ENABLE)
.build();
return dishMapper.list(dish);
}
/**
* 动态条件查询菜品
* @param dish
* @return
*/
List list(Dish dish);
/**
* 套餐管理
*/
@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐相关接口")
@Slf4j
public class SetmealController {
@Autowired
private SetmealService setmealService;
/**
* 新增套餐
* @param setmealDTO
* @return
*/
@PostMapping
@ApiOperation("新增套餐")
public Result save(@RequestBody SetmealDTO setmealDTO) {
setmealService.saveWithDish(setmealDTO);
return Result.success();
}
}
public interface SetmealService {
/**
* 新增套餐,同时需要保存套餐和菜品的关联关系
* @param setmealDTO
*/
void saveWithDish(SetmealDTO setmealDTO);
}
/**
* 套餐业务实现
*/
@Service
@Slf4j
public class SetmealServiceImpl implements SetmealService {
@Autowired
private SetmealMapper setmealMapper;
@Autowired
private SetmealDishMapper setmealDishMapper;
@Autowired
private DishMapper dishMapper;
/**
* 新增套餐,同时需要保存套餐和菜品的关联关系
* @param setmealDTO
*/
@Transactional
public void saveWithDish(SetmealDTO setmealDTO) {
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO, setmeal);
//向套餐表插入数据
setmealMapper.insert(setmeal);
//获取生成的套餐id
Long setmealId = setmeal.getId();
List setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {
setmealDish.setSetmealId(setmealId);
});
//保存套餐和菜品的关联关系
setmealDishMapper.insertBatch(setmealDishes);
}
}
/**
* 新增套餐
* @param setmeal
*/
@AutoFill(OperationType.INSERT)
void insert(Setmeal setmeal);
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})
/**
* 批量保存套餐和菜品的关联关系
* @param setmealDishes
*/
void insertBatch(List setmealDishes);
insert into setmeal_dish
(setmeal_id,dish_id,name,price,copies)
values
(#{sd.setmealId},#{sd.dishId},#{sd.name},#{sd.price},#{sd.copies})
产品原型:
业务规则:
根据页码进行分页展示
每页展示10条数据
可以根据需要,按照套餐名称、分类、售卖状态进行查询
接口设计:
/**
* 分页查询
* @param setmealPageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("分页查询")
public Result page(SetmealPageQueryDTO setmealPageQueryDTO) {
PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);
return Result.success(pageResult);
}
/**
* 分页查询
* @param setmealPageQueryDTO
* @return
*/
PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
/**
* 分页查询
* @param setmealPageQueryDTO
* @return
*/
public PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {
int pageNum = setmealPageQueryDTO.getPage();
int pageSize = setmealPageQueryDTO.getPageSize();
PageHelper.startPage(pageNum, pageSize);
Page page = setmealMapper.pageQuery(setmealPageQueryDTO);
return new PageResult(page.getTotal(), page.getResult());
}
/**
* 分页查询
* @param setmealPageQueryDTO
* @return
*/
Page pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
业务规则:
可以一次删除一个套餐,也可以批量删除套餐
起售中的套餐不能删除
接口设计:
/**
* 批量删除套餐
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List ids){
setmealService.deleteBatch(ids);
return Result.success();
}
/**
* 批量删除套餐
* @param ids
*/
void deleteBatch(List ids);
/**
* 批量删除套餐
* @param ids
*/
@Transactional
public void deleteBatch(List ids) {
ids.forEach(id -> {
Setmeal setmeal = setmealMapper.getById(id);
if(StatusConstant.ENABLE == setmeal.getStatus()){
//起售中的套餐不能删除
throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
}
});
ids.forEach(setmealId -> {
//删除套餐表中的数据
setmealMapper.deleteById(setmealId);
//删除套餐菜品关系表中的数据
setmealDishMapper.deleteBySetmealId(setmealId);
});
}
/**
* 根据id查询套餐
* @param id
* @return
*/
@Select("select * from setmeal where id = #{id}")
Setmeal getById(Long id);
/**
* 根据id删除套餐
* @param setmealId
*/
@Delete("delete from setmeal where id = #{id}")
void deleteById(Long setmealId);
/**
* 根据套餐id删除套餐和菜品的关联关系
* @param setmealId
*/
@Delete("delete from setmeal_dish where setmeal_id = #{setmealId}")
void deleteBySetmealId(Long setmealId);
产品原型:
接口设计(共涉及到5个接口):
根据id查询套餐
根据类型查询分类(已完成)
根据分类id查询菜品(已完成)
图片上传(已完成)
修改套餐
/**
* 根据id查询套餐,用于修改页面回显数据
*
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
public Result getById(@PathVariable Long id) {
SetmealVO setmealVO = setmealService.getByIdWithDish(id);
return Result.success(setmealVO);
}
/**
* 修改套餐
*
* @param setmealDTO
* @return
*/
@PutMapping
@ApiOperation("修改套餐")
public Result update(@RequestBody SetmealDTO setmealDTO) {
setmealService.update(setmealDTO);
return Result.success();
}
/**
* 根据id查询套餐和关联的菜品数据
* @param id
* @return
*/
SetmealVO getByIdWithDish(Long id);
/**
* 修改套餐
* @param setmealDTO
*/
void update(SetmealDTO setmealDTO);
/**
* 根据id查询套餐和套餐菜品关系
*
* @param id
* @return
*/
public SetmealVO getByIdWithDish(Long id) {
Setmeal setmeal = setmealMapper.getById(id);
List setmealDishes = setmealDishMapper.getBySetmealId(id);
SetmealVO setmealVO = new SetmealVO();
BeanUtils.copyProperties(setmeal, setmealVO);
setmealVO.setSetmealDishes(setmealDishes);
return setmealVO;
}
/**
* 修改套餐
*
* @param setmealDTO
*/
@Transactional
public void update(SetmealDTO setmealDTO) {
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO, setmeal);
//1、修改套餐表,执行update
setmealMapper.update(setmeal);
//套餐id
Long setmealId = setmealDTO.getId();
//2、删除套餐和菜品的关联关系,操作setmeal_dish表,执行delete
setmealDishMapper.deleteBySetmealId(setmealId);
List setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {
setmealDish.setSetmealId(setmealId);
});
//3、重新插入套餐和菜品的关联关系,操作setmeal_dish表,执行insert
setmealDishMapper.insertBatch(setmealDishes);
}
/**
* 根据套餐id查询套餐和菜品的关联关系
* @param setmealId
* @return
*/
@Select("select * from setmeal_dish where setmeal_id = #{setmealId}")
List getBySetmealId(Long setmealId);
update setmeal
name=#{name},
category_id=#{categoryId},
price=#{price},
image=#{image},
description=#{description},
status=#{status},
update_time=#{updateTime},
update_user=#{updateUser},
where id=#{id}
业务规则:
可以对状态为起售的套餐进行停售操作,可以对状态为停售的套餐进行起售操作
起售的套餐可以展示在用户端,停售的套餐不能展示在用户端
起售套餐时,如果套餐内包含停售的菜品,则不能起售
接口设计:
/**
* 套餐起售停售
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
public Result startOrStop(@PathVariable Integer status, Long id) {
setmealService.startOrStop(status, id);
return Result.success();
}
/**
* 套餐起售、停售
* @param status
* @param id
*/
void startOrStop(Integer status, Long id);
/**
* 套餐起售、停售
* @param status
* @param id
*/
public void startOrStop(Integer status, Long id) {
//起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"
if(status == StatusConstant.ENABLE){
//select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = ?
List 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);
}
/**
* 根据套餐id查询菜品
* @param setmealId
* @return
*/
@Select("select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = #{setmealId}")
List getBySetmealId(Long setmealId);