4. 功能模块完善之套餐模块
4.1 新增套餐
4.1.1 需求分析与设计
产品原型
后台系统中可以管理套餐信息,通过 新增功能来添加一个新的套餐,在添加套餐时需要添加套餐对应菜品的信息,并且需要上传套餐图片。
新增套餐原型:
当填写完表单信息, 点击"保存"按钮后, 会提交该表单的数据到服务端, 在服务端中需要接受数据, 然后将数据保存至数据库中。
业务规则:
- 套餐名称必须是唯一的
- 套餐必须属于某个分类
- 套餐必须包含菜品
- 名称、分类、价格、图片为必填项
- 添加菜品窗口需要根据分类类型来展示菜品
- 新增的套餐默认为停售状态
接口设计
- 根据类型查询分类(已完成)
- 根据分类id查询菜品
- 文件上传(已完成)
- 新增套餐
根据分类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 菜品份数
4.1.2 代码开发
DishController
/*** 根据分类id查询菜品* @param categoryId* @return */ @GetMapping("/list") @ApiOperation("根据分类id查询菜品") public Result<List<Dish>> list(Long categoryId){List<Dish> list = dishService.list(categoryId);return Result.success(list); }
DishService
/*** 根据分类id查询菜品* @param categoryId* @return */ List<Dish> list(Long categoryId);
DishServiceImpl
/*** 根据分类id查询菜品* @param categoryId* @return */ public List<Dish> list(Long categoryId) {Dish dish = Dish.builder().categoryId(categoryId).status(StatusConstant.ENABLE).build();return dishMapper.list(dish); }
DishMapper
/*** 动态条件查询菜品* @param dish* @return */ List<Dish> list(Dish dish);
DishMapper.xml
<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>
SetmealController
/*** 套餐管理*/ @RestController @RequestMapping("/admin/setmeal") @Api(tags = "套餐相关接口") @Slf4j public class SetmealController {@Autowiredprivate SetmealService setmealService;/*** 新增套餐* @param setmealDTO* @return*/@PostMapping@ApiOperation("新增套餐")public Result save(@RequestBody SetmealDTO setmealDTO) {setmealService.saveWithDish(setmealDTO);return Result.success();} }
SetmealService
public interface SetmealService {/*** 新增套餐,同时需要保存套餐和菜品的关联关系* @param setmealDTO*/void saveWithDish(SetmealDTO setmealDTO); }
SetmealServiceImpl
/*** 套餐业务实现*/ @Service @Slf4j public class SetmealServiceImpl implements SetmealService {@Autowiredprivate SetmealMapper setmealMapper;@Autowiredprivate SetmealDishMapper setmealDishMapper;@Autowiredprivate DishMapper dishMapper;/*** 新增套餐,同时需要保存套餐和菜品的关联关系* @param setmealDTO*/@Transactionalpublic void saveWithDish(SetmealDTO setmealDTO) {Setmeal setmeal = new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);//向套餐表插入数据setmealMapper.insert(setmeal);//获取生成的套餐idLong setmealId = setmeal.getId();List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmealId);});//保存套餐和菜品的关联关系setmealDishMapper.insertBatch(setmealDishes);} }
SetmealMapper
/*** 新增套餐* @param setmeal */ @AutoFill(OperationType.INSERT) void insert(Setmeal setmeal);
SetmealMapper.xml
<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
/*** 批量保存套餐和菜品的关联关系* @param setmealDishes */ void insertBatch(List<SetmealDish> setmealDishes);
SetmealDishMapper.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>
4.1.3 功能测试
接口文档测试
根据分类id查询菜品
新增套餐
前后端联调
根据分类id查询菜品
新增套餐
4.1.4 提交代码
commit—>describe—>push
4.2 套餐分页查询
4.2.1 需求分析与设计
产品原型
系统中的套餐数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。
套餐分页原型:
在套餐列表展示时,除了套餐的基本信息(名称、售价、售卖状态、最后操作时间)外,还有两个字段略微特殊,第一个是图片字段 ,从数据库查询出来的仅仅是图片的名字,图片要想在表格中回显展示出来,就需要下载这个图片。第二个是套餐分类,这里展示的是分类名称,而不是分类ID,此时就需要根据套餐的分类ID,去分类表中查询分类信息,然后在页面展示。
业务规则:
- 根据页码进行分页展示
- 每页展示10条数据
- 可以根据需要,按照套餐名称、分类、售卖状态进行查询
接口设计
4.2.2 代码开发
SetmealController
/*** 分页查询* @param setmealPageQueryDTO* @return */ @GetMapping("/page") @ApiOperation("分页查询") public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO) {PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);return Result.success(pageResult); }
SetmealService
/*** 分页查询* @param setmealPageQueryDTO* @return */ PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
SetmealServiceImpl
/*** 分页查询* @param setmealPageQueryDTO* @return */ 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
/*** 分页查询* @param setmealPageQueryDTO* @return */ Page<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
SetmealMapper.xml
<select id="pageQuery" resultType="com.sky.vo.SetmealVO">selects.*,c.name categoryNamefromsetmeal sleft joincategory cons.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>
4.2.3 功能测试
接口文档测试和前后端联调测试
4.2.4 提交代码
commit—>describe—>push
4.3 删除套餐
4.3.1 需求分析与设计
产品原型:
业务规则:
- 可以一次删除一个套餐,也可以批量删除套餐
- 起售中的套餐不能删除
接口设计:
4.3.2 代码开发
SetmealController
/*** 批量删除套餐* @param ids* @return */ @DeleteMapping @ApiOperation("批量删除套餐") public Result delete(@RequestParam List<Long> ids){setmealService.deleteBatch(ids);return Result.success(); }
SetmealService
/*** 批量删除套餐* @param ids */ void deleteBatch(List<Long> ids);
SetmealServiceImpl
/*** 批量删除套餐* @param ids */ @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
/*** 根据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);
SetmealDishMapper
/*** 根据套餐id删除套餐和菜品的关联关系* @param setmealId */ @Delete("delete from setmeal_dish where setmeal_id = #{setmealId}") void deleteBySetmealId(Long setmealId);
4.3.3 功能测试
接口文档测试
删除起售中的套餐
删除停售的套餐
前后端联调
4.3.4 提交代码
commit—>describe—>push
4.4 修改套餐
4.4.1 需求分析与设计
产品原型:
接口设计(共涉及到5个接口):
- 根据id查询套餐
- 根据类型查询分类(已完成)
- 根据分类id查询菜品(已完成)
- 图片上传(已完成)
- 修改套餐
根据id查询套餐
修改套餐
4.4.2 代码开发
SetmealController
/*** 根据id查询套餐,用于修改页面回显数据** @param id* @return */ @GetMapping("/{id}") @ApiOperation("根据id查询套餐") public Result<SetmealVO> 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(); }
SetmealService
/*** 根据id查询套餐和关联的菜品数据* @param id* @return */ SetmealVO getByIdWithDish(Long id);/*** 修改套餐* @param setmealDTO */ void update(SetmealDTO setmealDTO);
SetmealServiceImpl
/*** 根据id查询套餐和套餐菜品关系** @param id* @return */ public SetmealVO getByIdWithDish(Long id) {SetmealVO setmealVO = setmealMapper.getByIdWithDish(id);return setmealVO; }/*** 修改套餐** @param setmealDTO */ @Transactional public void update(SetmealDTO setmealDTO) {Setmeal setmeal = new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);//1、修改套餐表,执行updatesetmealMapper.update(setmeal);//套餐idLong setmealId = setmealDTO.getId();//2、删除套餐和菜品的关联关系,操作setmeal_dish表,执行deletesetmealDishMapper.deleteBySetmealId(setmealId);List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmealId);});//3、重新插入套餐和菜品的关联关系,操作setmeal_dish表,执行insertsetmealDishMapper.insertBatch(setmealDishes); }
SetmealMapper
/*** 根据id查询套餐和套餐菜品关系* @param id* @return */ SetmealVO getByIdWithDish(Long id);
SetmealMapper.xml
<resultMap id="setmealAndDishMap" type="com.sky.vo.SetmealVO" autoMapping="true"><result column="id" property="id"/><collection property="setmealDishes" ofType="SetmealDish"><result column="sd_id" property="id"/><result column="setmeal_id" property="setmealId"/><result column="dish_id" property="dishId"/><result column="sd_name" property="name"/><result column="sd_price" property="price"/><result column="copies" property="copies"/></collection> </resultMap> <select id="getByIdWithDish" parameterType="long" resultMap="setmealAndDishMap">select a.*,b.id sd_id,b.setmeal_id,b.dish_id,b.name sd_name,b.price sd_price,b.copiesfrom setmeal aleft joinsetmeal_dish bona.id = b.setmeal_idwhere a.id = #{id} </select>
4.4.3 功能测试
接口文档测试
根据id查询套餐
修改套餐
前后端联调
数据回显
修改套餐
4.4.4 提交代码
commit—>describe—>push
4.5 起售停售套餐
4.5.1 需求分析与设计
产品原型:
业务规则:
- 可以对状态为起售的套餐进行停售操作,可以对状态为停售的套餐进行起售操作
- 起售的套餐可以展示在用户端,停售的套餐不能展示在用户端
- 起售套餐时,如果套餐内包含停售的菜品,则不能起售
接口设计:
4.5.2 代码开发
SetmealController
/*** 套餐起售停售* @param status* @param id* @return */ @PostMapping("/status/{status}") @ApiOperation("套餐起售停售") public Result startOrStop(@PathVariable Integer status, Long id) {setmealService.startOrStop(status, id);return Result.success(); }
SetmealService
/*** 套餐起售、停售* @param status* @param id */ void startOrStop(Integer status, Long id);
SetmealServiceImpl
/*** 套餐起售、停售* @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<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
/*** 根据套餐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<Dish> getBySetmealId(Long setmealId);
4.5.3 功能测试
起售、停售套餐
起售:套餐内含未启用菜品
停售
前后端联调
起售
停售
4.5.4 提交代码
commit—>describe—>push