任务 CRUD 业务实现¶
把前面学的分层、校验、统一响应、MyBatis-Plus 串起来,实现任务的增删改查。
接口一览¶
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | /tasks?current=1&size=10&status=0 |
分页查询当前用户的任务 |
| POST | /tasks |
新建任务 |
| PUT | /tasks/{id} |
修改任务 |
| DELETE | /tasks/{id} |
删除任务(逻辑删除) |
业务实现¶
完整的 Service 实现——注意每个方法里的归属校验:
package com.javaglm.task.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.javaglm.task.common.BizException;
import com.javaglm.task.dto.TaskRequest;
import com.javaglm.task.entity.Task;
import com.javaglm.task.mapper.TaskMapper;
import com.javaglm.task.service.TaskService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* 任务服务实现(第 28 章)。
* 继承 ServiceImpl<TaskMapper, Task>:自动拥有 CRUD,Mapper 也自动注入。
* 业务逻辑写在这里(校验归属、组装数据),Controller 保持"瘦"。
*/
@Service
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements TaskService {
@Override
public IPage<Task> pageTasks(long current, long size, Long userId, Integer status) {
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Task::getUserId, userId);
if (status != null) {
wrapper.eq(Task::getStatus, status);
}
wrapper.orderByDesc(Task::getCreateTime);
return this.page(new Page<>(current, size), wrapper);
}
@Override
public Task createTask(TaskRequest req, Long userId) {
Task task = new Task();
task.setTitle(req.getTitle());
task.setDescription(req.getDescription());
task.setStatus(req.getStatus() == null ? 0 : req.getStatus());
task.setUserId(userId);
task.setCreateTime(LocalDateTime.now());
task.setUpdateTime(LocalDateTime.now());
this.save(task);
return task;
}
@Override
public Task updateTask(Long id, TaskRequest req, Long userId) {
Task task = this.getById(id);
if (task == null || !task.getUserId().equals(userId)) {
throw new BizException(404, "任务不存在或无权操作");
}
task.setTitle(req.getTitle());
task.setDescription(req.getDescription());
if (req.getStatus() != null) {
task.setStatus(req.getStatus());
}
task.setUpdateTime(LocalDateTime.now());
this.updateById(task);
return task;
}
@Override
public void deleteTask(Long id, Long userId) {
Task task = this.getById(id);
if (task == null || !task.getUserId().equals(userId)) {
throw new BizException(404, "任务不存在或无权操作");
}
this.removeById(id); // @TableLogic:实际执行逻辑删除
}
}
关键点:
- pageTasks:用
LambdaQueryWrapper拼user_id = ?(只查自己的)、可选status、按创建时间倒序,调this.page(...)分页。 - createTask:组装 Task 对象(userId 来自 JWT,不是前端传的),
this.save(task)插入。 - updateTask:先
getById查出来,校验userId归属(防越权改别人任务),再更新。 - deleteTask:同样校验归属,
this.removeById逻辑删除。
Controller¶
接参数、调 Service、包 Result:
package com.javaglm.task.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.javaglm.task.common.Result;
import com.javaglm.task.dto.TaskRequest;
import com.javaglm.task.entity.Task;
import com.javaglm.task.service.TaskService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
/**
* 任务接口(第 28 章):增删改查 + 分页。
* 注意 userId 来自 JWT 拦截器放进请求上下文的值(不是前端传的,防伪造)。
*/
@RestController
@RequestMapping("/tasks")
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping
public Result<IPage<Task>> list(
@RequestParam(defaultValue = "1") long current,
@RequestParam(defaultValue = "10") long size,
@RequestParam(required = false) Integer status,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return Result.success(taskService.pageTasks(current, size, userId, status));
}
@PostMapping
public Result<Task> create(@RequestBody @Valid TaskRequest req,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return Result.success(taskService.createTask(req, userId));
}
@PutMapping("/{id}")
public Result<Task> update(@PathVariable Long id,
@RequestBody @Valid TaskRequest req,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return Result.success(taskService.updateTask(id, req, userId));
}
@DeleteMapping("/{id}")
public Result<?> delete(@PathVariable Long id, HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
taskService.deleteTask(id, userId);
return Result.success();
}
}
注意 userId 的来源:
它不是前端传的,而是 JWT 拦截器(第 29 章)解析 token 后放进请求上下文的。这样绝对防伪造——前端没法假装自己是别人。
完整调用链¶
sequenceDiagram
participant F as 前端
participant I as JwtInterceptor
participant C as TaskController
participant S as TaskServiceImpl
participant DB as MySQL
F->>I: GET /tasks (带 Authorization)
I->>I: 解析 token,取出 userId
I->>C: 放行,userId 存入 request
C->>S: pageTasks(current,size,userId,status)
S->>DB: SELECT ... WHERE user_id=? AND deleted=0 LIMIT
DB-->>S: 数据
S-->>C: IPage<Task>
C-->>F: Result.success(page)
用 Postman 测试¶
- 先调
POST /auth/login拿到 token; - 后续请求 Header 加
Authorization: Bearer <token>; POST /tasks,body:{"title":"学Java","status":0};GET /tasks看列表。
:octicons-arrow-left-16: 上一章:数据库设计实战 | 下一章:JWT 认证