# 新增套餐# 需求分析和设计产品原型:
业务规则:
套餐名称唯一 套餐必须属于某个分类 套餐必须包含菜品 名称、分类、价格、图片为必填项 添加菜品窗口需要根据分类类型来展示菜品 新增的套餐默认为停售状态 接口设计(共涉及到 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 菜品份数
# 代码实现# DishController
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/list") @ApiOperation("根据分类id查询菜品") public Result<List<Dish>> list (Long categoryId) { List<Dish> list = dishService.list(categoryId); return Result.success(list); }
# DishService
1 2 3 4 5 6 List<Dish> list (Long categoryId) ;
# DishServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 public List<Dish> list (Long categoryId) { Dish dish = Dish.builder() .categoryId(categoryId) .status(StatusConstant.ENABLE) .build(); return dishMapper.list(dish); }
# DishMapper
1 2 3 4 5 6 List<Dish> list (Dish dish) ;
# DishMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <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 >
# 1.2.6 SetmealController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @RestController @RequestMapping("/admin/setmeal") @Api(tags = "套餐相关接口") @Slf4j public class SetmealController { @Autowired private SetmealService setmealService; @PostMapping @ApiOperation("新增套餐") public Result save (@RequestBody SetmealDTO setmealDTO) { setmealService.saveWithDish(setmealDTO); return Result.success(); } }
# SetmealService
1 2 3 4 5 6 7 8 public interface SetmealService { void saveWithDish (SetmealDTO setmealDTO) ; }
# SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Service @Slf4j public class SetmealServiceImpl implements SetmealService { @Autowired private SetmealMapper setmealMapper; @Autowired private SetmealDishMapper setmealDishMapper; @Autowired private DishMapper dishMapper; @Transactional public void saveWithDish (SetmealDTO setmealDTO) { Setmeal setmeal = new Setmeal (); BeanUtils.copyProperties(setmealDTO, setmeal); setmealMapper.insert(setmeal); Long setmealId = setmeal.getId(); List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes(); setmealDishes.forEach(setmealDish -> { setmealDish.setSetmealId(setmealId); }); setmealDishMapper.insertBatch(setmealDishes); } }
[!IMPORTANT]
为什么要先插入再获取套餐 id,不能直接获取吗
自增主键:很多数据库使用自增主键来自动生成 ID 值。在插入数据之前,这个自增主键还没有被赋值,所以无法直接获取。只有在插入数据后,数据库才会给这个自增主键赋值,此时才能获取到这个 ID。 数据库事务:在插入数据的过程中,可能会涉及到数据库事务。如果先获取 ID, 然后插入数据失败,那么这个 ID 就被 "浪费" 了。所以先插入数据,确保数据插入成功后,再获取 ID 更加合理和安全。 其他情况:有时候,ID 的生成可能不是自增的,而是通过某种算法或者其他机制生成的。在这种情况下,插入数据后获取 ID 也是一种常见的做法。 # SetmealMapper
1 2 3 4 5 6 @AutoFill(OperationType.INSERT) void insert (Setmeal setmeal) ;
# SetmealMapper.xml
1 2 3 4 5 6 <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 >
# SetmealDishMapper
1 2 3 4 5 void insertBatch (List<SetmealDish> setmealDishes) ;
# SetmealDishMapper.xml
1 2 3 4 5 6 7 8 <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 >
# 功能测试略
# 套餐分页查询# 需求分析和设计产品原型:
业务规则:
根据页码进行分页展示 每页展示 10 条数据 可以根据需要,按照套餐名称、分类、售卖状态进行查询 接口设计:
# 代码实现# SetmealController
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/page") @ApiOperation("分页查询") public Result<PageResult> page (SetmealPageQueryDTO setmealPageQueryDTO) { PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO); return Result.success(pageResult); }
# SetmealService
1 2 3 4 5 6 PageResult pageQuery (SetmealPageQueryDTO setmealPageQueryDTO) ;
# SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 public PageResult pageQuery (SetmealPageQueryDTO setmealPageQueryDTO) { int pageNum = setmealPageQueryDTO.getPage(); int pageSize = setmealPageQueryDTO.getPageSize(); PageHelper.startPage(pageNum, pageSize); Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO); return new PageResult (page.getTotal(), page.getResult()); }
# SetmealMapper
1 2 3 4 5 6 Page<SetmealVO> pageQuery (SetmealPageQueryDTO setmealPageQueryDTO) ;
# SetmealMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <select id ="pageQuery" 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 ="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 >
# 功能测试略
# 删除套餐# 需求分析和设计产品原型:
业务规则:
可以一次删除一个套餐,也可以批量删除套餐 起售中的套餐不能删除 接口设计:
# 代码实现# SetmealController
1 2 3 4 5 6 7 8 9 10 11 @DeleteMapping @ApiOperation("批量删除套餐") public Result delete (@RequestParam List<Long> ids) { setmealService.deleteBatch(ids); return Result.success(); }
# SetmealService
1 2 3 4 5 void deleteBatch (List<Long> ids) ;
# SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Transactional public void deleteBatch (List<Long> 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); }); }
# SetmealMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Select("select * from setmeal where id = #{id}") Setmeal getById (Long id) ; @Delete("delete from setmeal where id = #{id}") void deleteById (Long setmealId) ;
# SetmealDishMapper
1 2 3 4 5 6 @Delete("delete from setmeal_dish where setmeal_id = #{setmealId}") void deleteBySetmealId (Long setmealId) ;
# 功能测试略
# 修改套餐# 需求分析和设计产品原型:
接口设计(共涉及到 5 个接口):
根据 id 查询套餐 根据类型查询分类(已完成) 根据分类 id 查询菜品(已完成) 图片上传(已完成) 修改套餐
# 代码实现# SetmealController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @GetMapping("/{id}") @ApiOperation("根据id查询套餐") public Result<SetmealVO> getById (@PathVariable Long id) { SetmealVO setmealVO = setmealService.getByIdWithDish(id); return Result.success(setmealVO); } @PutMapping @ApiOperation("修改套餐") public Result update (@RequestBody SetmealDTO setmealDTO) { setmealService.update(setmealDTO); return Result.success(); }
# SetmealService
1 2 3 4 5 6 7 8 9 10 11 12 SetmealVO getByIdWithDish (Long id) ; void update (SetmealDTO setmealDTO) ;
# SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public SetmealVO getByIdWithDish (Long id) { Setmeal setmeal = setmealMapper.getById(id); List<SetmealDish> setmealDishes = setmealDishMapper.getBySetmealId(id); SetmealVO setmealVO = new SetmealVO (); BeanUtils.copyProperties(setmeal, setmealVO); setmealVO.setSetmealDishes(setmealDishes); return setmealVO; } @Transactional public void update (SetmealDTO setmealDTO) { Setmeal setmeal = new Setmeal (); BeanUtils.copyProperties(setmealDTO, setmeal); setmealMapper.update(setmeal); Long setmealId = setmealDTO.getId(); setmealDishMapper.deleteBySetmealId(setmealId); List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes(); setmealDishes.forEach(setmealDish -> { setmealDish.setSetmealId(setmealId); }); setmealDishMapper.insertBatch(setmealDishes); }
# SetmealDishMapper
1 2 3 4 5 6 7 @Select("select * from setmeal_dish where setmeal_id = #{setmealId}") List<SetmealDish> getBySetmealId (Long setmealId) ;
# SetmealMapper
1 2 3 4 5 6 @AutoFill(OperationType.UPDATE) void update (Setmeal setmeal) ;
# SetmealMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <update id ="update" parameterType ="Setmeal" > 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 ="updateTime != null" > update_time = #{updateTime},</if > <if test ="updateUser != null" > update_user = #{updateUser}</if > </set > where id = #{id} </update >
# 功能测试略
# 起售停售套餐# 需求分析和设计产品原型:
业务规则:
可以对状态为起售的套餐进行停售操作,可以对状态为停售的套餐进行起售操作 起售的套餐可以展示在用户端,停售的套餐不能展示在用户端 起售套餐时,如果套餐内包含停售的菜品,则不能起售 接口设计:
# 代码实现# SetmealController
1 2 3 4 5 6 7 8 9 10 11 12 @PostMapping("/status/{status}") @ApiOperation("套餐起售停售") public Result startOrStop (@PathVariable Integer status, Long id) { setmealService.startOrStop(status, id); return Result.success(); }
# SetmealService
1 2 3 4 5 6 void startOrStop (Integer status, Long id) ;
# SetmealServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public void startOrStop (Integer status, Long id) { if (status == StatusConstant.ENABLE){ 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); }
# DishMapper
1 2 3 4 5 6 7 @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) ;
# 功能测试略