新增套餐
需求分析
接口定义

请求方式:POST
路径:/admin/setmeal
涉及到多表操作 setmeal(套餐主表)& setmeal_dish(套餐-菜品关系表)
请求参数

Body 是一个 JSON数据内容
套餐主信息(对应 setmeal 表)
-
categoryId(必填):分类 id(如早餐、午餐...) -
name(必填):套餐名称 -
price(必填):套餐价格 -
image(必填):套餐图片路径 -
description(可选):套餐描述 -
status(必填):0 停售,1 起售(很多系统新增默认 0) -
id(可选):新增时通常不传/不需要(数据库生成)
套餐菜品列表(对应 setmeal_dish 表)
-
setmealDishes(必填):数组(至少 1 条)-
dishId:菜品 id(关键字段) -
copies:份数 -
name:菜品名(冗余字段,快照) -
price:菜品价格(冗余字段,快照) -
id:关系表主键(新增不需要) -
setmealId:新增时前端一般也不需要传(后端补)
-
响应结果


代码开发
controller

@PostMapping
-
表示这是一个 POST 请求
-
因为 Controller 类一般已经写了
@RequestMapping("/admin/setmeal") -
所以完整路径就是:
POST /admin/setmeal
@RequestBody SetmealDTO setmealDTO
-
从请求体 JSON 中解析出一个
SetmealDTO将其封装为Java对象内容 -
对应你接口文档 Body 参数那一堆字段(categoryId、name、price、status、setmealDishes...)
setmealService.saveWithDish(setmealDTO);
调用service其中核心逻辑
return Result.success();
新增完成后返回统一响应

service

-
saveWithDish:强调"新增套餐 + 保存菜品关系"
-
参数是 DTO:说明来自前端 JSON 的整体对象

主要内容是:
-
DTO → Entity(拷贝套餐主表字段)
-
insert 主表 setmeal
-
拿到新生成的 setmealId
-
给每条 setmealDish 填 setmealId → 批量插入 setmeal_dish
@Transactional
涉及到多表操作使用此注解,要么一起成功,要么一起失败
BeanUtils.copyProperties
属性拷贝,前端传递的是DTO对象数据,而我们新增套餐需要的是setMeal套餐实体类对象内容
setmealMapper.insert(setmeal)
插入主表,并生成主键
给每条 setmealDish 填 setmealId
insertBatch(setmealDishes) ------ 批量插入关系表
Mapper


useGeneratedKeys="true" keyProperty="id"
插入成功后,把数据库生成的主键 id 回填到 Java 对象的 setmeal.id 字段中
<insert id="insert">
这是一个 MyBatis 的"插入语句",对应 Java Mapper 接口里的
void insert(Setmeal setmeal);
parameterType="Setmeal"
对应参数是一个对应的Setmeal对象


parameterType="list"
参数接受一个List集合
<foreach> 的作用 ------ 批量生成 values
对集合中的每一个元素,生成一组 values(...),中间用逗号隔开。
item="sd",对集合中的每一个元素都起一个临时变量叫sd
套餐分页查询
需求分析
接口定义

套餐分页查询接口:GET /admin/setmeal/page
请求参数



SetmealPageQueryDTO:对应分页+条件查询参数封装对象内容
响应信息

第一层:统一响应字段

code业务状态码 msg提示信息 data封装返回的业务数据
第二层:分页对象 data

records:当前页的数据列表
"records": [
{ 套餐1 },
{ 套餐2 },
{ 套餐3 }
]
每个元素就是一行套餐数据,对应页面表格中的一行。
第三层:每一条套餐记录字段含义

代码开发
cointroller

@GetMapping("/page"):接口路径与请求方式
方法签名

前端传递DTO对象内容,将多个query参数封装成一个对象来传入方法其中
调用service来进行查询,返回响应封装对应的pageResult对象内容
service


Service 从 DTO 中取出页码和页大小 → 使用 PageHelper 开启分页 → 调用 Mapper 查询数据库 → 得到分页对象 Page → 从中取出 total 和 records → 封装成 PageResult 返回。
PageHelper.startPage
它会拦截紧接着执行的那条 SQL 查询,自动帮你加上:
limit (pageNum-1)*pageSize, pageSize
执行 Mapper 查询
封装成 PageResult 返回
Mapper



此处返回VO是因为前端需要展示额外字段
<where> 标签
-
自动帮你生成
where -
自动处理多余的
and
<if>筛选查询条件内容
删除套餐
需求分析
接口定义

DELETE → 删除请求
请求参数

我们要根据多个 id 批量删除数据库记录
返回响应

Code:业务状态码
Data:返回数据
Msg:提示信息
代码开发
controller

功能:批量删除套餐
方法:DELETE
路径:/admin/setmeal
参数:Query 参数 ids
返回:统一响应 Result
方法签名:delete(@RequestParam List<Long> ids)
从 URL 里拿:
- /admin/setmeal?ids=1,2,3
核心业务调用:setmealService.deleteBatch(ids)
返回:Result.success()
Service



@Transactional
注意:
涉及到多表操作时,我们通过此注解来对应保持要么事务操作全部成功,要么全部失败
ids.forEach
遍历id,如果查询当前的setmeal套餐的状态信息为售卖状态,我们禁止删除这个内容
forEach其中来具体执行删除操作 主表 + 关联表 同步删除
Mapper
删除套餐表数据内容

删除套餐与菜品关联表

根据id查询套餐设计
需求分析
接口定义
接口标题:根据 id 查询套餐
路径:GET /admin/setmeal/{id}
请求参数

对应是Path参数。这代表:id 不在 query 里,而是在 URL 路径里。
需要通过对应的@PathVariable注解来进行获取
返回响应

Code:业务状态码
Data:返回数据内容
Msg:提示信息内容
注意:
此处的data其中返回的是SetmealVO对象,因为其中的setmeal其中字段无法满足前端展示的需求,比如额外字段的展示因此需要VO对象
代码开发
controller

✅ 根据 id 查询套餐
- 方法:GET
- 路径:/admin/setmeal/{id}
- 参数:Path 参数 id
- 返回:Result<SetmealVO>
@GetMapping("/{id}"):对应表示Get请求方式,且路径其中包含变量{id}信息内容
方法签名:
返回值:Result<SetmealVO>
@PathVariable Long id:对于Path参数我们通过注解来对应的获取值即可
具体根据id来查询值调用service来实际实现
返回成功:Result.success(setmealVO)
service

接口定义:根据 id 查询套餐 + 带上菜品信息(withDish)

主表查询
↓
子表查询
↓
VO 组装
↓
返回
修改套餐
需求分析
接口分析

✅ PUT /admin/setmeal(修改套餐) 更新一个资源
路径:/admin/setmeal
请求参数

顶层字段(套餐本身信息)

setmealDishes:套餐包含的菜品数组

返回响应

Code:业务状态码
Data:返回数据
Msg:提示信息
代码开发
controller

@PutMapping PUT 请求接口,语义是"修改数据"
方法签名:
形参@RequestBody来接受对应前端所传递的DTO对象内容,Spring来自动的将JSON格式数据来转换为SetmealDTO对象
调用 Service 层,修改内容,并且返回结果
service


根据前端传来的套餐信息:
-
修改套餐基本信息(setmeal 表)
-
删除旧的套餐-菜品关系(setmeal_dish 表)
-
重新插入新的套餐-菜品关系
@Transactional:涉及多表操作时注解。整个方法要么全部成功,要么全部失败。
DTO → Entity 转换,给Mapper函数方法来进行操作
更新套餐主表,获取套餐id,用id来删除旧的套餐信息,更新新的信息
Mapper
deleteBySetmealId ------ "清空旧关联"

根据关联的套餐id信息去进行删除旧关系,然后再插入新关系
insertBatch ------ "重新插入新关联(批量)


通过在Service中,来拿到对应的SetmealDTO其中拿到新的菜品列表内容
XML其中来进行定义复杂SQL
id="insertBatch" 必须和 Mapper 接口的方法名一致
insertBatch(...) ↔ <insert id="insertBatch">
parameterType="list" 表示这个方法传入的参数是一个List集合
启售/停售
需求分析
接口分析

方法:POST
路径:/admin/setmeal/status/{status}
请求参数

status → 放在 Path 路径里
id → 放在 Query 参数里
Body → 空对象 {}(不使用)
Header → 需要 Content-Type: application/json

/status/1 → 把套餐状态改成 起售
/status/0 → 把套餐状态改成 停售
Query 参数:id

返回响应


代码开发
controller

POST /admin/setmeal/status/{status}
功能:根据 status 和 id 切换套餐状态(起售 / 停售)
@PostMapping("/status/{status}"):对应是post请求,且路径其中包含一个路径参数{status}
@PathVariable Integer status:用于来获取对应的Path参数信息内容
id 参数:
调用service层其中功能来实现逻辑
返回统一成功结果
service



第一段:启售前的业务校验
我们只针对对应的启售的商品来进行查看一下其中是否有已经停售的菜品信息内容
Mapper

查出某个套餐下,关联的所有菜品

启售套餐前,先查出套餐内的所有菜品,看有没有停售的。


