一、先看接口文档,明确需求
从你截图的接口文档可以看到:
POST /admin/employee/status/{status}
Path参数: status (1=启用,0=禁用)
Query参数: id (员工ID)
也就是说,前端发的请求长这样:
POST /admin/employee/status/0?id=2
意思是:把 id=2 的员工状态改成 0(禁用)。
二、完整开发流程(从外到内)
第 1 步:Controller 层 ------ 接收请求
你已经写了:
@PostMapping("/status/{status}")
@ApiOperation("启用禁用员工账号")
public Result startOrStop(@PathVariable Integer status, Long id) {
log.info("启用禁用员工账号:{},{}", status, id);
employeeService.startOrStop(status, id);
return Result.success();
}
关键点:
@PathVariable Integer status:从路径/status/0里取出0Long id:从 URL?id=2里自动取出(没有@RequestParam也能自动封装,因为参数名和 query 参数名一致)- 自己不写业务,调用 Service
第 2 步:Service 层 ------ 业务逻辑
// 接口
void startOrStop(Integer status, Long id);
// 实现
public void startOrStop(Integer status, Long id) {
// 1. 构建一个 Employee 对象,只设置需要更新的字段
Employee employee = new Employee();
employee.setId(id);
employee.setStatus(status);
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId()); // 当前登录用户
// 2. 调用通用的 update 方法
employeeMapper.update(employee);
}
为什么这样设计?
- 启用 / 禁用本质上就是更新员工的 status 字段
- 不需要把整个员工信息传进去,只需要 id + status + 审计字段
- 复用通用的
update方法,不用单独写 SQL
第 3 步:Mapper 层 ------ 动态更新 SQL
你在 XML 里写了通用的 update:
<update id="update" parameterType="Employee">
update employee
<set>
<if test="name != null">name=#{name},</if>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="phone != null">phone=#{phone},</if>
<if test="sex != null">sex=#{sex},</if>
<if test="idNumber != null">id_number=#{idNumber},</if>
<if test="updateTime != null">update_time=#{updateTime},</if>
<if test="updateUser != null">update_user=#{updateUser},</if>
<if test="status != null">status=#{status},</if>
</set>
where id=#{id}
</update>
<set> + <if> 的工作原理:
-
<set>自动加set关键字,自动去掉最后多余的逗号 -
每个
<if>判断字段不为 null 才更新这个字段 -
启用 / 禁用时,Employee 对象里只有 id/status/updateTime/updateUser 有值,其他都是 null
-
所以最终 SQL 是:
update employee set status=0, update_time=?, update_user=? where id=2
三、你 XML 里有个错误!
看你截图这行:
<if test="idNumber !=null">update_Time =#{updateTiem},</if>
<!-- ↑ ↑
条件是idNumber,但更新的是update_Time?
而且updateTiem拼写也错了(应该是updateTime)
这行明显是复制粘贴错了,应该改成:
<if test="idNumber != null">id_number=#{idNumber},</if>
这个错误会导致:编辑员工时修改身份证号不生效,反而可能错误更新时间。一定要改!
四、调用链路图
前端请求
POST /admin/employee/status/0?id=2
│
▼
【Controller】
@PathVariable status=0
自动封装 id=2
→ employeeService.startOrStop(0, 2)
│
▼
【Service】
构建 Employee{id=2, status=0, updateTime=now, updateUser=当前用户}
→ employeeMapper.update(employee)
│
▼
【Mapper XML】
<set> 动态判断,只更新非null字段
→ update employee set status=0, update_time=?, update_user=? where id=2
│
▼
数据库:员工id=2 的状态变成 0(禁用)
五、这个功能和 "编辑员工" 复用同一个 update
你写的这个通用 update 方法,两个功能都能用:
表格
| 功能 | 传什么字段 | 最终更新什么 |
|---|---|---|
| 启用 / 禁用 | id, status, updateTime, updateUser | 只更新状态 |
| 编辑员工 | id, name, username, phone, sex, idNumber, updateTime, updateUser | 更新基本信息 |
这就是动态 SQL 的好处:一个 update 方法,按需更新不同字段,不用为每个功能单独写 SQL。
六、开发顺序总结
1. 看接口文档,确定请求方式、路径、参数
2. 写 Controller(@PathVariable 接路径参数,普通参数接 query)
3. 写 Service 接口 + 实现(构建对象,调用 update)
4. 写 Mapper 接口方法声明
5. 写 Mapper XML(<set> + <if> 动态更新)
6. 启动测试:POST /admin/employee/status/0?id=2
7. 去数据库确认 status 变成 0
一句话总结
启用 / 禁用员工 = 更新 status 字段 ,Controller 用
@PathVariable取路径里的 status、用参数自动封装取 id,Service 构建只含必要字段的 Employee 对象,Mapper 用<set>+<if>动态更新非 null 字段。记得把 XML 里 idNumber 那行的错误改过来。
先把 XML 那个错误修了,然后启动测试一下启用 / 禁用功能,有问题继续问。
今天 22:08
怎么根据接口 进行设计
接口设计相关问题
这是后端开发最核心的能力 ------拿到接口文档就能设计出完整代码。我给你一套通用的方法论,配合你学过的功能举例。
第一步:从接口文档提取 4 个关键信息
拿到任何一个接口,先看这 4 样东西:
表格
| 看什么 | 决定什么 | 例子 |
|---|---|---|
| 请求方式 | Controller 用哪个注解 | POST → @PostMapping,GET → @GetMapping |
| 请求路径 | Controller 的 URL 映射 | /admin/employee → @RequestMapping |
| 入参(参数) | 用什么接收、DTO 怎么设计 | JSON 体 → @RequestBody,URL 参数 → 普通参数 |
| 出参(返回) | 返回什么类型 | Result / Result<T> / Result<PageResult> |
第二步:根据请求方式 + 路径,设计 Controller
规则
请求方式是 POST/PUT → 方法上用 @PostMapping / @PutMapping
请求方式是 GET → 方法上用 @GetMapping
路径里有 {xxx} → 方法参数用 @PathVariable
对照你学过的 3 个接口
接口 1:新增员工
POST /admin/employee
Body: {"name":"张三",...}
→ Controller 设计:
@PostMapping
public Result save(@RequestBody EmployeeDTO dto) { }
接口 2:员工分页查询
GET /admin/employee/page?name=李&page=1&pageSize=10
→ Controller 设计:
@GetMapping("/page")
public Result<PageResult> page(EmployeePageQueryDTO dto) { }
// 注意:没有 @RequestBody,GET请求参数自动封装
接口 3:启用禁用员工
POST /admin/employee/status/{status}?id=2
→ Controller 设计:
@PostMapping("/status/{status}")
public Result startOrStop(@PathVariable Integer status, Long id) { }
// status在路径里 → @PathVariable
// id在URL?后面 → 普通参数自动封装
第三步:根据入参,设计 DTO
规则
参数是JSON对象 → 建一个DTO类,字段和JSON的key对应
参数是URL拼接的 → 可以建DTO,也可以直接用零散参数
参数是路径里的 → 直接用 @PathVariable,不用建DTO
怎么判断要不要建 DTO?
表格
| 参数情况 | 做法 | 例子 |
|---|---|---|
| 1-2 个简单参数 | 直接写在方法参数里 | startOrStop(Integer status, Long id) |
| 3 个以上参数 | 封装成 DTO | page(EmployeePageQueryDTO dto) |
| JSON 请求体 | 必须封装成 DTO | save(@RequestBody EmployeeDTO dto) |
DTO 字段怎么定?
照着接口文档的参数列表写,一个都不能少:
接口文档入参:name, username, phone, sex, idNumber
↓
EmployeeDTO 字段:name, username, phone, sex, idNumber
字段名必须和接口文档一模一样,否则
@RequestBody或 query 自动封装时匹配不上。
第四步:根据业务逻辑,设计 Service
Service 要做什么?
Controller 只负责 "收发",所有业务逻辑都在 Service 里。问自己几个问题:
- 需要做数据转换吗? → DTO 转 Entity(
BeanUtils.copyProperties) - 需要补充默认值吗? → 状态、密码、时间、创建人
- 需要做校验吗? → 用户名是否重复、账号是否锁定
- 需要调用几个 Mapper? → 单表操作还是多表操作
对照你学过的功能
新增员工的 Service 逻辑:
1. DTO → Entity(属性拷贝)
2. 补充默认值:status=1, password=MD5(123456), createTime, updateTime, createUser, updateUser
3. 调用 mapper.insert
启用禁用的 Service 逻辑:
1. 构建一个 Employee 对象,只设置需要更新的字段
2. 设置 id, status, updateTime, updateUser
3. 调用 mapper.update(动态更新)
分页查询的 Service 逻辑:
1. PageHelper.startPage 开启分页
2. 调用 mapper.pageQuery
3. 从 Page 对象取出 total 和 records
4. 封装成 PageResult 返回
第五步:根据数据库操作,设计 Mapper
规则
简单 SQL(单表单条件)→ 注解方式 @Select/@Insert/@Update
复杂 SQL(动态条件、多表关联)→ XML 方式
怎么判断用注解还是 XML?
表格
| SQL 类型 | 方式 | 例子 |
|---|---|---|
| 简单插入 | 注解 @Insert |
新增员工 |
| 简单查询(按 id / 按用户名) | 注解 @Select |
根据用户名查员工 |
| 动态条件查询 | XML <where>+<if> |
分页查询(name 可选) |
| 动态更新 | XML <set>+<if> |
编辑员工、启用禁用 |
| 多表关联查询 | XML | 订单查询(后面学) |
Mapper 方法的入参和返回值怎么定?
插入 → 参数是 Entity,返回 void
更新 → 参数是 Entity,返回 void
根据id查 → 参数是 Long id,返回 Entity
分页查 → 参数是 DTO,返回 Page<Entity>
第六步:根据出参,设计返回结果
规则
所有接口统一返回 Result<T>
不需要返回数据 → Result
需要返回对象 → Result<Employee>
需要返回分页 → Result<PageResult>
需要返回列表 → Result<List<Employee>>
对照你学过的
表格
| 接口 | 返回什么 | 方法签名 |
|---|---|---|
| 新增员工 | 不需要数据 | Result save(...) |
| 启用禁用 | 不需要数据 | Result startOrStop(...) |
| 分页查询 | 分页数据 | Result<PageResult> page(...) |
| 根据 id 查员工 | 员工对象 | Result<Employee> getById(...) |
完整设计流程图
拿到接口文档
│
├── 看请求方式+路径 → 设计 Controller 映射
│
├── 看入参
│ ├── JSON体 → 建DTO + @RequestBody
│ ├── URL参数 → 建DTO 或 零散参数
│ └── 路径参数 → @PathVariable
│
├── 看出参 → 设计返回类型 Result<T>
│
├── 想业务逻辑 → 设计 Service(转换、补值、校验、调Mapper)
│
└── 想数据库操作 → 设计 Mapper
├── 简单SQL → 注解
└── 复杂SQL → XML动态标签
拿一个新接口练手:编辑员工
假设接口文档是:
PUT /admin/employee
Body: {"id":2, "name":"张三", "username":"zhangsan", "phone":"138...", "sex":"男", "idNumber":"..."}
返回:Result
按上面的方法设计:
1. Controller:
@PutMapping
public Result update(@RequestBody EmployeeDTO dto) {
employeeService.update(dto);
return Result.success();
}
2. DTO: 用已有的 EmployeeDTO(里面有 id、name、username 等字段)
3. Service:
public void update(EmployeeDTO dto) {
Employee employee = new Employee();
BeanUtils.copyProperties(dto, employee);
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.update(employee); // 复用你写的动态update
}
4. Mapper: 复用已有的 update 方法(XML 动态 SQL)
你看,编辑员工几乎不用写新代码,复用之前的 DTO 和 update 方法就行。
一句话总结
接口文档 = 设计蓝图:请求方式和路径决定 Controller,入参决定 DTO 和接收方式,出参决定返回类型,业务逻辑决定 Service,数据库操作决定 Mapper。按照这个顺序一步步来,任何接口都能设计出来。