中企动力网站开发,wordpress中dw是什么,百度做自己的网站,重庆网站seo苍穹外卖-day04
课程内容
新增套餐套餐分页查询删除套餐修改套餐起售停售套餐
要求#xff1a;
根据产品原型进行需求分析#xff0c;分析出业务规则设计接口梳理表之间的关系#xff08;分类表、菜品表、套餐表、口味表、套餐菜品关系表#xff09;根据接口设计进行代…苍穹外卖-day04
课程内容
新增套餐套餐分页查询删除套餐修改套餐起售停售套餐
要求
根据产品原型进行需求分析分析出业务规则设计接口梳理表之间的关系分类表、菜品表、套餐表、口味表、套餐菜品关系表根据接口设计进行代码实现分别通过swagger接口文档和前后端联调进行功能测试
1. 新增套餐
1.1 需求分析和设计
产品原型 业务规则
套餐名称唯一套餐必须属于某个分类套餐必须包含菜品名称、分类、价格、图片为必填项添加菜品窗口需要根据分类类型来展示菜品新增的套餐默认为停售状态
接口设计共涉及到4个接口
根据类型查询分类已完成根据分类id查询菜品图片上传已完成新增套餐 数据库设计
setmeal表为套餐表用于存储套餐的信息。具体表结构如下
字段名数据类型说明备注idbigint主键自增namevarchar(32)套餐名称唯一category_idbigint分类id逻辑外键pricedecimal(10,2)套餐价格imagevarchar(255)图片路径descriptionvarchar(255)套餐描述statusint售卖状态1起售 0停售create_timedatetime创建时间update_timedatetime最后修改时间create_userbigint创建人idupdate_userbigint最后修改人id
setmeal_dish表为套餐菜品关系表用于存储套餐和菜品的关联关系。具体表结构如下
字段名数据类型说明备注idbigint主键自增setmeal_idbigint套餐id逻辑外键dish_idbigint菜品id逻辑外键namevarchar(32)菜品名称冗余字段pricedecimal(10,2)菜品单价冗余字段copiesint菜品份数
1.2 代码实现
1.2.1 DishController根据分类id查询菜品 /*** 根据分类id查询菜品* param categoryId* return
*/
GetMapping(/list)
ApiOperation(根据分类id查询菜品)
public ResultListDish list(Long categoryId){ListDish list dishService.list(categoryId);return Result.success(list);
}1.2.2 DishService
/*** 根据分类id查询菜品* param categoryId* return
*/
ListDish list(Long categoryId);1.2.3 DishServiceImpl请求参数封装的不同情况
/*** 根据分类id查询菜品* param categoryId* return
*/
public ListDish list(Long categoryId) {Dish dish Dish.builder().categoryId(categoryId).status(StatusConstant.ENABLE).build();return dishMapper.list(dish);
}1.2.4 DishMapper
/*** 动态条件查询菜品* param dish* return
*/
ListDish list(Dish dish);1.2.5 DishMapper.xml
select idlist resultTypeDish parameterTypeDishselect * from dishwhereif testname ! nulland name like concat(%,#{name},%)/ifif testcategoryId ! nulland category_id #{categoryId}/ifif teststatus ! nulland status #{status}/if/whereorder by create_time desc
/select前后端联调 1.2.6 SetmealController新增套餐 /*** 套餐管理*/
RestController
RequestMapping(/admin/setmeal)
Api(tags 套餐相关接口)
Slf4j
public class SetmealController {Autowiredprivate SetmealService setmealService;/*** 新增套餐* param setmealDTO* return*/PostMappingApiOperation(新增套餐)public Result save(RequestBody SetmealDTO setmealDTO) {setmealService.saveWithDish(setmealDTO);return Result.success();}
}1.2.7 SetmealService
public interface SetmealService {/*** 新增套餐同时需要保存套餐和菜品的关联关系* param setmealDTO*/void saveWithDish(SetmealDTO setmealDTO);
}1.2.8 SetmealServiceImpl新增业务逻辑 类似新增菜品逻辑
/*** 套餐业务实现*/
Service
Slf4j
public class SetmealServiceImpl implements SetmealService {Autowiredprivate SetmealMapper setmealMapper;Autowiredprivate SetmealDishMapper setmealDishMapper;Autowiredprivate DishMapper dishMapper;/*** 新增套餐同时需要保存套餐和菜品的关联关系* param setmealDTO*/TransactionalOverridepublic void saveWithDish(SetmealDTO setmealDTO) {//请求参数用的是SetmealDTO类封装的包含套餐数据以及套餐菜品关系表数据,// 这个地方只需要插入套餐的基本信息所以进行属性拷贝。Setmeal setmeal new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);//向套餐表插入数据setmealMapper.insert(setmeal);//获取生成的套餐id 通过sql中的useGeneratedKeystrue keyPropertyid获取插入后生成的主键值//套餐菜品关系表的setmealId页面不能传递它是向套餐表插入数据之后生成的主键值也就是套餐菜品关系表的逻辑外键setmealIdLong setmealId setmeal.getId();//获取页面传来的套餐和菜品关系表数据ListSetmealDish setmealDishes setmealDTO.getSetmealDishes();//遍历关系表数据为关系表中的每一条数据(每一个对象)的setmealId赋值// 这个地方不需要像之前写新增菜品时多写个if判断因为之前的口味数据是非必须的// 这个地方要求套餐必须包含菜品是必须的所以不需要if判断不存在套餐不包含菜品得情况setmealDishes.forEach(setmealDish - {//将Setmeal套餐类的id属性赋值给SetmealDish套餐关系类的setmealId//套餐表的id保存在套餐关系表充当外键为setmealIdsetmealDish.setSetmealId(setmealId);});//保存套餐和菜品的关联关系 动态sql批量插入setmealDishMapper.insertBatch(setmealDishes);}
}1.2.9 SetmealMapper
/*** 新增套餐* param setmeal
*/
AutoFill(OperationType.INSERT)
void insert(Setmeal setmeal);1.2.10 SetmealMapper.xml
insert idinsert parameterTypeSetmeal useGeneratedKeystrue keyPropertyidinsert 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})
/insert1.2.11 SetmealDishMapper
/*** 批量保存套餐和菜品的关联关系* param setmealDishes
*/
void insertBatch(ListSetmealDish setmealDishes);1.2.12 SetmealDishMapper.xml
insert idinsertBatch parameterTypelistinsert into setmeal_dish(setmeal_id,dish_id,name,price,copies)valuesforeach collectionsetmealDishes itemsd separator,(#{sd.setmealId},#{sd.dishId},#{sd.name},#{sd.price},#{sd.copies})/foreach
/insert1.3 功能测试
略还没有写分页查询所以暂时不进行前后端联调。
2. 套餐分页查询字段和属性不一致问题
2.1 需求分析和设计
产品原型 业务规则
根据页码进行分页展示每页展示10条数据可以根据需要按照套餐名称、分类、售卖状态进行查询
接口设计 2.2 代码实现
2.2.1 SetmealController /*** 分页查询* param setmealPageQueryDTO* return
*/
GetMapping(/page)
ApiOperation(分页查询)
public ResultPageResult page(SetmealPageQueryDTO setmealPageQueryDTO) {PageResult pageResult setmealService.pageQuery(setmealPageQueryDTO);return Result.success(pageResult);
}2.2.2 SetmealService
/*** 分页查询* param setmealPageQueryDTO* return
*/
PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);2.2.3 SetmealServiceImpl /*** 分页查询* param setmealPageQueryDTO* return*/Overridepublic PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {int pageNum setmealPageQueryDTO.getPage();int pageSize setmealPageQueryDTO.getPageSize();//需要在查询功能之前开启分页功能当前页的页码 每页显示的条数PageHelper.startPage(pageNum, pageSize);//这个方法有返回值为Page对象里面保存的是分页之后的相关数据PageSetmealVO page setmealMapper.pageQuery(setmealPageQueryDTO);//封装到PageResult中:总记录数 当前页数据集合return new PageResult(page.getTotal(), page.getResult());}2.2.4 SetmealMapper
/*** 分页查询* param setmealPageQueryDTO* return
*/
PageSetmealVO pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);2.2.5 SetmealMapper.xml 问题类似于菜品分页查询在套餐分页查询中套餐表setmeal保存的字段是category_id分类id而接口文档要求返回的数据是分类名称。 解决使用左外连接查询。查询套餐表以及套餐对应的分类名称。 注意此时查询出套餐表中的字段 套餐名称为name分类表中的字段分类名称也是name那这样的话我们在封装数据的时候就会出现问题通过mybatis框架去封装数据的时候由于这2个字段名相同封装VO这个数据的时候就会对应错分类表中的字段分类名称是name字段SetmealVO是categoryName属性字段名和属性名不一致所以封装不了数据。------------通过起字段别名方式解决c.name as categoryName 名称习惯使用模糊查询而不是等号。
select idpageQuery resultTypecom.sky.vo.SetmealVOselects.*,c.name categoryNamefromsetmeal sleft joincategory cons.category_id c.idwhereif testname ! nulland s.name like concat(%,#{name},%)/ifif teststatus ! nulland s.status #{status}/ifif testcategoryId ! nulland s.category_id #{categoryId}/if/whereorder by s.create_time desc
/select2.3 功能测试
测试分页查询
测试新增套餐 3. 删除套餐删除的业务逻辑
3.1 需求分析和设计
产品原型 业务规则
可以一次删除一个套餐也可以批量删除套餐起售中的套餐不能删除
接口设计 3.2 代码实现
3.2.1 SetmealController /*** 批量删除套餐* param ids* return
*/
DeleteMapping
ApiOperation(批量删除套餐)
public Result delete(RequestParam ListLong ids){setmealService.deleteBatch(ids);return Result.success();
}3.2.2 SetmealService
/*** 批量删除套餐* param ids
*/
void deleteBatch(ListLong ids);3.2.3 SetmealServiceImpl /*** 批量删除套餐* param ids*/TransactionalOverridepublic void deleteBatch(ListLong ids) {//判断当前套餐是否能够删除---是否存在起售中的套餐//思路遍历获取传入的id根据id查询套餐setmeal中的status字段0 停售 1 起售// 如果是1代表是起售状态不能删除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);});}类似于删除菜品删除套餐的sql存在缺点
上述代码每一次for循环遍历都会发生2条sql删除套餐、删除套餐菜品关系表数据如果遍历的次数比较多那么发出的sql数量也很多可能会引发性能一些方面的问题。解决减少sql语句的数量使用动态sql批量删除只需要1条sql就可以把需要删除的菜品删除掉同样1条sql删除口味表的数据这里不在演示详情查看day03-删除菜品。这里使用的是有缺点的方式做的删除。
3.2.4 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);3.2.5 SetmealDishMapper
/*** 根据套餐id删除套餐和菜品的关联关系* param setmealId
*/
Delete(delete from setmeal_dish where setmeal_id #{setmealId})
void deleteBySetmealId(Long setmealId);3.3 功能测试 4. 修改套餐修改的业务逻辑
4.1 需求分析和设计
产品原型 接口设计共涉及到5个接口
根据id查询套餐根据类型查询分类已完成根据分类id查询菜品已完成图片上传已完成修改套餐 4.2 代码实现
4.2.1 SetmealController /*** 根据id查询套餐用于修改页面回显数据** param id* return
*/
GetMapping(/{id})
ApiOperation(根据id查询套餐)
public ResultSetmealVO 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();
}4.2.2 SetmealService /*** 根据id查询套餐和关联的菜品数据* param id* return*/SetmealVO getByIdWithDish(Long id);/*** 修改套餐* param setmealDTO*/void update(SetmealDTO setmealDTO);4.2.3 SetmealServiceImpl /*** 根据id查询套餐和套餐菜品关系** param id* return*/Overridepublic SetmealVO getByIdWithDish(Long id) {//根据id查询套餐表数据Setmeal setmeal setmealMapper.getById(id);//删除菜品时写过了//根据id查询餐菜品关系表数据ListSetmealDish setmealDishes setmealDishMapper.getBySetmealId(id);//封装返回结果SetmealVO setmealVO new SetmealVO();BeanUtils.copyProperties(setmeal, setmealVO);setmealVO.setSetmealDishes(setmealDishes);return setmealVO;}/*** 修改套餐** 思路分析套餐表修改直接使用update语句即可对于这个套餐菜品关系表* 套餐和菜品关系的修改比较复杂因为它的情况有很多 有可能关系没有修改 有可能* 关系是追加的 也有可能关系是删除了那么这个地方我们有没有一种比较* 简单的处理方式呢* 可以先把你当前这个套餐菜品关系数据全都统一删掉然后在按照你当前* 传过来的这个套餐菜品关系重新再来插入一遍这个数据就可以了。** param setmealDTO*/Transactionalpublic void update(SetmealDTO setmealDTO) {//说明SetmealDTO含有套餐菜品关系表数据当前只是修改套餐的基本信息所以直接传递SetmealDTO不合适// 可以把SetmealDTO的数据拷贝到套餐的基本信息类Setmeal中更合适。Setmeal setmeal new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);//1、修改套餐表执行updatesetmealMapper.update(setmeal);//获取生成的套餐id 通过sql中的useGeneratedKeystrue keyPropertyid获取插入后生成的主键值//套餐菜品关系表的setmealId页面不能传递它是向套餐表插入数据之后生成的主键值也就是套餐菜品关系表的逻辑外键setmealIdLong setmealId setmealDTO.getId();//新增套餐时的sql获取主键值//2、删除套餐和菜品的关联关系操作setmeal_dish表执行deletesetmealDishMapper.deleteBySetmealId(setmealId); //删除套餐时已经实现了//获取页面传来的套餐和菜品关系表数据ListSetmealDish setmealDishes setmealDTO.getSetmealDishes();//遍历关系表数据为关系表中的每一条数据(每一个对象)的setmealId赋值// 这个地方不需要像之前写新增菜品时多写个if判断因为之前的口味数据是非必须的// 这个地方要求套餐必须包含菜品是必须的所以不需要if判断不存在套餐不包含菜品得情况setmealDishes.forEach(setmealDish - {setmealDish.setSetmealId(setmealId);});//3、重新插入套餐和菜品的关联关系操作setmeal_dish表执行insert// 动态sql批量插入setmealDishMapper.insertBatch(setmealDishes);//新增套餐时已经实现了}4.2.4 SetmealMapper /*** 根据id查询套餐* param id* return*/Select(select * from setmeal where id #{id})Setmeal getById(Long id);//修改套餐表执行updateAutoFill(OperationType.UPDATE)void update(Setmeal setmeal);4.2.5 SetmealMapper.xml update idupdateupdate setmealsetif testcategoryId ! nullcategory_id #{categoryId},/ifif testname ! nullname #{name},/ifif testprice ! nullprice #{price},/ifif teststatus ! nullstatus #{status},/ifif testdescription ! nulldescription #{description},/ifif testimage ! nullimage #{image},/ifif testupdateTime ! nullupdate_time #{updateTime},/ifif testupdateUser ! nullupdate_user #{updateUser},/if/setwhere id #{id}/update4.2.6 SetmealDishMapper /*** 根据套餐id查询套餐和菜品的关联关系* param setmealId* return*/Select(select * from setmeal_dish where setmeal_id #{setmealId})ListSetmealDish getBySetmealId(Long setmealId);/*** 根据套餐id删除套餐和菜品的关联关系* param setmealId*/Delete(delete from setmeal_dish where setmeal_id #{setmealId})void deleteBySetmealId(Long setmealId);/*** 批量保存套餐和菜品的关联关系* param setmealDishes*/void insertBatch(ListSetmealDish setmealDishes);4.2.7 SetmealDishMapper insert idinsertBatch parameterTypelistinsert into setmeal_dish(setmeal_id,dish_id,name,price,copies)valuesforeach collectionsetmealDishes itemsd separator,(#{sd.setmealId},#{sd.dishId},#{sd.name},#{sd.price},#{sd.copies})/foreach/insert4.3 功能测试 5. 起售停售套餐业务逻辑
5.1 需求分析和设计
产品原型 业务规则
可以对状态为起售的套餐进行停售操作可以对状态为停售的套餐进行起售操作起售的套餐可以展示在用户端停售的套餐不能展示在用户端起售套餐时如果套餐内包含停售的菜品则不能起售
接口设计 5.2 代码实现
5.2.1 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();
}5.2.2 SetmealService
/*** 套餐起售、停售* param status* param id
*/
void startOrStop(Integer status, Long id);5.2.3 SetmealServiceImpl /*** 套餐起售、停售* param status* param id*/Overridepublic void startOrStop(Integer status, Long id) {//起售套餐时判断套餐内是否有停售菜品有停售菜品提示套餐内包含未启售菜品无法启售if(status.equals(StatusConstant.ENABLE)){ //1 启用//select a.* from dish a left join setmeal_dish b on a.id b.dish_id where b.setmeal_id ?//左外连接查询根据套餐id查询菜品以及对应的菜品套餐关系数据a.*所以返回所有菜品数据ListDish dishList dishMapper.getBySetmealId(id);if(dishList ! null dishList.size() 0){//判断套餐中是否包含的有菜品有才走if判断dishList.forEach(dish - {//套餐中包含菜品如果这个菜品的状态为禁用则抛出异常if(StatusConstant.DISABLE.equals(dish.getStatus())){throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);}});}}//执行流程 如果是起售套餐套餐内有停售菜品则抛出异常 不能起售// 如果是起售套餐套餐内没有停售菜品if执行完后跳出继续向下执行执行更新// 如果是停售套餐不走上面的if直接进行更新状态。Setmeal setmeal Setmeal.builder().id(id).status(status).build();setmealMapper.update(setmeal);//修改套餐时写了通用的修改sql}5.2.4 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})
ListDish getBySetmealId(Long setmealId);5.2.5 SetmealMapper //修改套餐表执行updateAutoFill(OperationType.UPDATE)void update(Setmeal setmeal);5.2.6 SetmealMapper.xml update idupdateupdate setmealsetif testcategoryId ! nullcategory_id #{categoryId},/ifif testname ! nullname #{name},/ifif testprice ! nullprice #{price},/ifif teststatus ! nullstatus #{status},/ifif testdescription ! nulldescription #{description},/ifif testimage ! nullimage #{image},/ifif testupdateTime ! nullupdate_time #{updateTime},/ifif testupdateUser ! nullupdate_user #{updateUser},/if/setwhere id #{id}/update5.3 功能测试