Day04 学习文档:项目实战——套餐管理(管理端)
对照原型 + 接口 + 表结构 + 业务规则,把套餐管理完整跑通。
1) 你今天要完成的功能清单(做到这些就算 Day04 过关)
管理端「套餐管理」一般包含(day01 里也明确把“套餐管理”作为管理端核心模块之一):
- 套餐分页查询(列表页)
- 新增套餐(包含“套餐基本信息 + 套餐包含哪些菜品/份数”)
- 查询套餐详情(用于回显编辑页)
- 修改套餐
- 批量删除套餐
- 起售/停售套餐
你可以把它理解为:维护两张表的数据一致性:
setmeal(套餐主表) +setmeal_dish(套餐-菜品关系表)。
2) 先把数据模型想清楚
day01 的库表清单里就有:setmeal、setmeal_dish 。
setmeal:套餐本身(名字、分类、价格、图片、描述、状态…)setmeal_dish:套餐包含哪些菜品、每个菜品几份(copies),属于典型的“一对多”(一个套餐对应多条明细)
你在 day03 删除菜品时已经遇到过“被套餐关联不能删”的规则,本质就是检查 setmeal_dish 这张关系表 。Day04 反过来:你要维护/清理这张关系表的数据。
3) 接口清单
POST /admin/setmeal:新增套餐(Body:SetmealDTO)GET /admin/setmeal/page:分页查询(Query:name/categoryId/status/page/pageSize…)GET /admin/setmeal/{id}:查询套餐详情(用于回显)PUT /admin/setmeal:修改套餐(Body:SetmealDTO)DELETE /admin/setmeal?ids=1,2,3:批量删除POST /admin/setmeal/status/{status}?id=xx:起售/停售
4) 项目里你该看哪些代码(按这个顺序看,最省时间)
- Controller:
com.sky.controller.admin.SetmealController - Service:
com.sky.service.SetmealService、com.sky.service.impl.SetmealServiceImpl - Mapper:
com.sky.mapper.SetmealMapper、SetmealDishMapper、必要时DishMapper - XML:
resources/mapper/SetmealMapper.xml、SetmealDishMapper.xml
建议你学习方式是:先从 Controller 看“有哪些接口” → 再进 Service 看“业务规则” → 最后看 Mapper/XML 明白“SQL 怎么落库”。
5) 关键业务怎么写(每个功能你都要能讲清楚“数据怎么动”)
A. 新增套餐(最核心:一次写两张表)
目标:插入 setmeal 一条 + 插入 setmeal_dish 多条
思路:
SetmealDTO拿到套餐基本信息 +setmealDishes列表- 先 insert
setmeal,拿到生成的setmealId - 遍历
setmealDishes,给每条设置setmealId - 批量 insert 到
setmeal_dish
你要重点理解:为什么必须先插 setmeal?
因为 setmeal_dish.setmeal_id 需要主表 id,主表 id 是自增生成的。
✅ 自检点:
- 只新增主表、不新增关系表:前端回显/用户端展示必坏
- 新增关系表但没设置 setmealId:会出现“明细全是 null 外键”或直接插入失败
B. 套餐分页查询(列表页)
目标:按条件分页查 setmeal,一般还要带出分类名(categoryName)
思路:
- 用 PageHelper + mapper XML 里 join
category的写法(类似 day03 菜品分页 join 分类) - 返回
PageResult(total, records)
✅ 自检点:
page、pageSize是否生效(SQL 有没有 limit)- 条件过滤:name 模糊、categoryId 精确、status 精确
C. 查询套餐详情(编辑页回显)
目标:查出:
- 套餐基本信息(setmeal)
- 套餐包含的菜品明细(setmeal_dish 列表)
思路:
setmealMapper.getById(id)拿主表setmealDishMapper.getBySetmealId(id)拿明细- 组装成
SetmealVO返回(VO 里包含setmealDishes)
✅ 自检点:
- 回显页能否显示“已选菜品 + 份数”
- 如果套餐没有菜品(理论不应出现),接口也要稳,不要 NPE
D. 修改套餐(本质:先清空旧明细,再插入新明细)
目标:更新 setmeal 一条 + 重建 setmeal_dish 多条
推荐做法(最稳):
- update
setmeal - delete
setmeal_dish where setmeal_id = ? - 遍历新
setmealDishesset 外键 - batch insert 新明细
✅ 自检点:
- “删旧明细”必须做,否则会出现“旧菜品还在 + 新菜品又加进来”的脏数据
- 最好加事务:update 成功但明细插入失败会导致数据不一致(真实项目里这是事故点)
E. 批量删除套餐(业务规则通常更严格)
常见规则:
- 起售中的套餐不能删除(避免用户侧还能买到)
- 删除套餐时必须删除关系表数据(否则孤儿数据)
- 批量删除要么全成功要么全失败(建议事务)
✅ 自检点:
- 先查 status 再删,别直接 delete
- 删除顺序:通常先删关系表再删主表,或主表删完再删关系表都行,但要保证最终一致
F. 起售/停售套餐(容易考:联动校验)
常见规则:
- 起售套餐前,要确保“套餐里所有菜品都在售”(否则套餐上架但里面菜品下架,逻辑矛盾)
实现上通常会:
- 根据 setmealId 查出其菜品列表(通过
setmeal_dish关联 dish) - 若存在停售菜品:抛业务异常
- 校验通过:更新 setmeal.status
✅ 自检点:
- 你能用断点看到:为什么能查到这个套餐包含哪些菜品?
- 报错信息要“业务可读”(不是 SQL 异常)
6) 测试路线
你 day03 已经用过接口文档测试:http://localhost:8080/doc.html,并且提醒过 token 失效要重新登录获取 。day04 继续沿用这个节奏:
建议测试顺序:
- 分页查询:先确保列表能出数据
- 新增套餐:新增完回到分页确认能看到
- 查询详情:拿新增的 id 回显确认 setmealDishes 正确
- 修改套餐:改套餐里的菜品组合,回显确认旧明细已替换
- 起售/停售:验证规则是否生效(尤其“含停售菜品不能起售”)
- 删除套餐:先尝试删起售套餐(应失败),再停售后删除(应成功)
联调测试(可选但推荐):
- 启动 nginx → 进“套餐管理”页面 → 跟着 UI 点一遍(这一步能暴露很多字段映射问题)
7) 常见坑
- 事务没加导致数据不一致
修改/删除这类“一次动多张表”的操作,推荐都加@Transactional(至少:新增、修改、删除) - MyBatis 单参数命名坑(特别常见)
Mapper 方法如果只有一个参数但 XML 用了别的名字,可能出现:Parameter not found
解决方式:要么 XML 用#{param1}/#{id}对齐,要么在 Mapper 参数上加@Param("xxx") - BeanUtils.copyProperties 把不该覆盖的字段覆盖了
修改时注意:是否需要保留 createTime/createUser 等(项目里一般用公共字段自动填充解决) - 前端传参字段名对不上
典型:categoryId、image、setmealDishes 这类字段,一旦名字不一致,后端就收不到