基于 Spring Boot 的 Todo REST API,提供用户注册、登录认证、JWT 鉴权、RBAC 角色权限控制,以及 Todo 的创建、查询、修改、切换完成状态、删除、筛选等能力。
- RESTful Todo API
- 用户注册和登录
- JWT Token 鉴权
- USER / ADMIN 角色权限控制
- 管理员用户、Todo 和统计接口
- 统一处理 401 未登录和 403 无权限响应
- Todo 数据按用户隔离
- Todo 软删除和恢复
- completedAt / deletedAt 生命周期字段
- Todo 操作日志
- Todo 附件上传、列表、下载和删除
- Todo 操作日志事件驱动异步写入
- Todo 过期扫描定时任务
- Todo 过期扫描任务状态查询
- Service 层事务管理
- Todo 详情和操作日志本地缓存
- 支持 Redis profile 作为外部缓存
- Redis 分布式锁、固定窗口限流和请求幂等
- Kafka / RabbitMQ 消息队列适配和 Todo 操作日志消息化
- Actuator 健康检查、应用信息和运行指标
- Actuator liveness / readiness 探针
- dev / prod 多环境配置示例
- 使用
@ConfigurationProperties绑定业务配置 - 本地文件存储配置和附件元数据管理
- 统一 API 响应结构
- 业务错误码
- 请求 / 响应 DTO 分层
- 分页、排序、筛选列表查询
- 全局异常处理
- Bean Validation 参数校验
- Spring Data JPA 数据持久化
- Flyway 数据库迁移
- 默认使用 H2 本地数据库
- 支持 PostgreSQL profile
- Java 21 多阶段 Dockerfile
- Docker Compose 管理 Java 应用、PostgreSQL 和附件数据卷
- Swagger / OpenAPI 接口文档
- 带 requestId 的请求日志记录
- 按功能拆分的 MockMvc 接口测试
- JUnit 5 / Mockito Service 层单元测试
- 查询索引、聚合统计和 EXPLAIN 性能测试
- 数据库 CHECK 约束和 Todo 乐观锁
- 并发更新冲突返回 HTTP 409
- 数据库关系、迁移和索引设计文档
- GitHub Actions CI 配置
- Java 21
- Maven
- Spring Boot 3.3.2
- Spring Web
- Spring Validation
- Spring Security
- Spring Data JPA
- Spring Cache
- Spring Data Redis
- Spring Kafka
- Spring AMQP / RabbitMQ
- Spring Event / Async
- Spring Scheduling
- Spring Boot Actuator
- Springdoc OpenAPI
- Flyway
- H2 Database
- PostgreSQL Driver
- Docker / Docker Compose(可选)
- GitHub Actions(可选)
- JUnit 5 / MockMvc
HTTP 请求
-> Security Filter
-> Controller
-> Service
-> Event Publisher
-> Event Listener
-> Scheduled Job
-> Repository
-> Database
Actuator
-> Health / Info / Metrics
Entity
-> Mapper
-> Response DTO
主要包结构:
com.zading.todoapi
├── config 工程配置,例如 OpenAPI 配置
│ └── properties 业务配置绑定对象
├── controller HTTP 接口入口
├── dto 请求 / 响应对象
├── event 应用内部事件、事件发布器和事件监听器
├── exception 自定义异常和全局异常处理
├── job 定时任务和后台批处理入口
├── logging 请求日志过滤器
├── mapper Entity 到 DTO 的转换
├── model JPA Entity
├── repository Spring Data JPA Repository
├── redis Redis / 内存版锁、限流和幂等实现
├── messaging 消息模型、发布器、消费者和幂等处理器
├── security JWT 和 Spring Security 配置
└── service 业务逻辑
java-todo-api/
├── .dockerignore
├── .env.example
├── .github/
│ └── workflows/
│ └── ci.yml
├── docker-compose.yml
├── Dockerfile
├── pom.xml
├── README.md
├── docs/
│ ├── week-01-learning.md
│ ├── week-02-learning.md
│ ├── week-03-learning.md
│ ├── week-04-learning.md
│ ├── week-05-learning.md
│ ├── week-06-learning.md
│ ├── week-07-learning.md
│ ├── week-08-learning.md
│ ├── week-09-learning.md
│ ├── week-10-learning.md
│ ├── week-11-learning.md
│ ├── week-12-learning.md
│ ├── week-13-learning.md
│ ├── week-14-learning.md
│ ├── week-15-learning.md
│ ├── week-16-learning.md
│ ├── week-17-learning.md
│ ├── week-18-learning.md
│ ├── week-19-learning.md
│ ├── week-20-learning.md
│ ├── week-21-learning.md
│ ├── week-22-learning.md
│ ├── week-23-learning.md
│ ├── week-24-learning.md
│ ├── week-25-learning.md
│ └── week-26-learning.md
└── src/
├── main/
│ ├── java/com/zading/todoapi/
│ │ ├── TodoApiApplication.java
│ │ ├── config/
│ │ ├── controller/
│ │ ├── dto/
│ │ ├── event/
│ │ ├── exception/
│ │ ├── job/
│ │ ├── logging/
│ │ ├── mapper/
│ │ ├── model/
│ │ ├── repository/
│ │ ├── redis/
│ │ ├── security/
│ │ └── service/
│ └── resources/
│ ├── application-dev.properties
│ ├── application.properties
│ ├── application-postgres.properties
│ ├── application-prod.properties
│ ├── application-kafka.properties
│ ├── application-rabbitmq.properties
│ ├── application-redis.properties
│ └── db/migration/
│ ├── V1__create_todos_table.sql
│ ├── V2__add_priority_and_due_date_to_todos.sql
│ ├── V3__create_users_and_link_todos.sql
│ ├── V4__add_todo_lifecycle_fields.sql
│ ├── V5__create_todo_action_logs_table.sql
│ ├── V6__create_todo_attachments_table.sql
│ ├── V7__add_user_role.sql
│ ├── V8__add_todo_query_indexes.sql
│ └── V9__add_data_constraints_and_todo_version.sql
└── test/
├── java/com/zading/todoapi/
│ ├── ApplicationSmokeTests.java
│ ├── ActuatorTests.java
│ ├── AuthApiTests.java
│ ├── OpenApiTests.java
│ ├── RbacApiTests.java
│ ├── TodoQueryOptimizationTests.java
│ ├── TodoDatabaseDesignTests.java
│ ├── RedisProtectionTests.java
│ ├── TodoAttachmentApiTests.java
│ ├── TodoApiTests.java
│ ├── service/
│ │ ├── AdminServiceTest.java
│ │ ├── AuthServiceTest.java
│ │ ├── TodoAttachmentServiceTest.java
│ │ └── TodoServiceTest.java
│ └── support/
└── resources/
├── mockito-extensions/
└── application-test.properties
- JDK 21
- Maven 3.9+
PostgreSQL 是可选依赖。默认 profile 使用 H2,因此本地没有安装 PostgreSQL 也可以直接运行。
Docker 也是可选工具。当前仓库提供了 docker-compose.yml,但不要求本机已经安装 Docker。
默认配置文件:
src/main/resources/application.properties
默认数据源:
spring.datasource.url=jdbc:h2:file:./data/todo-db-v2;MODE=PostgreSQL
spring.datasource.username=sa
spring.datasource.password=数据库文件会生成在:
data/
data/ 目录已加入 Git 忽略规则。
PostgreSQL 配置文件:
src/main/resources/application-postgres.properties
默认连接信息:
spring.datasource.url=${DB_URL:jdbc:postgresql://localhost:5432/java_todo_api}
spring.datasource.username=${DB_USERNAME:postgres}
spring.datasource.password=${DB_PASSWORD:}创建数据库:
CREATE DATABASE java_todo_api;使用 PostgreSQL profile 启动:
DB_USERNAME=postgres DB_PASSWORD=your_password mvn spring-boot:run -Dspring-boot.run.profiles=postgres如果本机以后安装了 Docker,可以使用仓库内置的 Compose 配置启动 PostgreSQL:
cp .env.example .env
docker compose up -d postgres然后启动 PostgreSQL profile:
DB_USERNAME=postgres DB_PASSWORD=postgres mvn spring-boot:run -Dspring-boot.run.profiles=postgres第 24 周提供了多阶段 Dockerfile:构建阶段使用 Maven + JDK 21,运行阶段只使用 JRE 21 和最终 JAR。应用容器默认使用 prod profile,并通过 Compose 内部服务名 postgres 连接数据库。
先生成环境变量文件,并修改 JWT_SECRET:
cp .env.example .env检查 Compose 配置:
docker compose config构建并启动 Java 应用和 PostgreSQL:
docker compose up -d --build查看服务状态和应用日志:
docker compose ps
docker compose logs -f app应用启动后可以访问:
http://localhost:8080/swagger-ui.html
http://localhost:8080/actuator/health
停止容器但保留数据库和附件数据:
docker compose down如果明确需要删除数据卷,再执行:
docker compose down -v注意:应用容器连接数据库时使用 postgres:5432,不能使用 localhost:5432。在容器内部,localhost 指向应用容器自己;postgres 才是 Compose 网络中的数据库服务名。附件保存到 app_uploads volume,容器删除后仍然可以保留。
开发环境配置文件:
src/main/resources/application-dev.properties
dev profile 面向本地开发:
保留 H2 Console
开启 SQL 日志
开启请求日志
业务包日志级别为 DEBUG
启动方式:
mvn spring-boot:run -Dspring-boot.run.profiles=dev生产环境配置文件:
src/main/resources/application-prod.properties
prod profile 面向真实部署:
关闭 H2 Console
关闭 SQL 输出
敏感配置从环境变量读取
保留 Actuator 基础健康检查和探针
生产环境需要提供环境变量:
DB_URL=jdbc:postgresql://localhost:5432/java_todo_api
DB_USERNAME=postgres
DB_PASSWORD=your_password
JWT_SECRET=your-strong-secret
JWT_EXPIRATION_MINUTES=120启动方式:
DB_URL=jdbc:postgresql://localhost:5432/java_todo_api \
DB_USERNAME=postgres \
DB_PASSWORD=your_password \
JWT_SECRET=your-strong-secret \
java -jar target/java-todo-api-1.0.0.jar --spring.profiles.active=prod测试配置文件:
src/test/resources/application-test.properties
测试环境使用 H2 内存数据库。
Redis 配置文件:
src/main/resources/application-redis.properties
默认启动不连接 Redis,而是使用单 JVM 内存实现。启用 Redis profile 后,缓存、分布式锁、限流和幂等 Key 才会使用真实 Redis:
mvn spring-boot:run -Dspring-boot.run.profiles=redis第 26 周的业务参数位于 app.redis.*:
app.redis.lock-lease=5m
app.redis.idempotency-ttl=10m
app.redis.rate-limit-window=1m
app.redis.login-limit=5
app.redis.todo-create-limit=30没有安装 Redis 时,不要启用 redis profile;默认配置已经可以运行、测试和学习接口流程。
默认配置文件提供了学习环境可用的 JWT 配置:
app.jwt.secret=${JWT_SECRET:java-todo-api-learning-secret-change-me-at-least-32-chars}
app.jwt.expiration-minutes=${JWT_EXPIRATION_MINUTES:120}生产环境不要使用默认密钥,应该通过环境变量设置:
JWT_SECRET=your-strong-secret
JWT_EXPIRATION_MINUTES=120项目使用 @ConfigurationProperties 把业务配置绑定成 Java 对象:
JwtProperties app.jwt.*
RequestLoggingProperties app.request-logging.*
TodoOverdueJobProperties app.todo.overdue-job.*
FileStorageProperties app.file-storage.*
RedisProtectionProperties app.redis.*
这样业务代码不需要分散读取字符串配置 key,配置结构也更容易校验和维护。
Todo 附件使用本地文件系统存储真实文件,数据库只保存附件元信息。
默认配置:
app.file-storage.root-location=uploads
app.file-storage.max-file-size-bytes=5242880说明:
root-location 附件根目录
max-file-size-bytes 单个附件最大字节数,默认 5MB
uploads/ 已加入 .gitignore,不会提交到 Git 仓库。
测试环境使用:
app.file-storage.root-location=target/test-uploads接口文档地址:
http://localhost:8080/swagger-ui.html
OpenAPI JSON 地址:
http://localhost:8080/v3/api-docs
项目提供 Spring Boot Actuator 基础可观测性端点:
GET /actuator/health 应用健康状态
GET /actuator/health/liveness 应用进程是否存活
GET /actuator/health/readiness 应用是否准备好接收请求
GET /actuator/info 应用基础信息
GET /actuator/metrics JVM、HTTP、进程等运行指标列表
默认只开放 health、info、metrics 三类端点:
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=never
management.endpoint.health.probes.enabled=true
management.info.env.enabled=true默认环境不强制检查 Redis:
management.health.redis.enabled=false这是为了保证本机没有安装 Redis 时,应用仍然可以正常启动,/actuator/health 也不会因为 Redis 未连接而返回 DOWN。
启用 redis profile 时,会重新打开 Redis 健康检查:
management.health.redis.enabled=true默认开启请求日志:
app.request-logging.enabled=true
app.request-logging.request-id-header=X-Request-Id日志会记录:
requestId=abc123 HTTP GET /api/todos -> 200 (12 ms)
如果请求头中带了 X-Request-Id,系统会复用它;如果没有携带,系统会自动生成一个 UUID,并通过响应头返回。
默认使用 Spring Boot Simple Cache:
spring.cache.type=simple这是本地内存缓存,不需要安装 Redis。当前缓存内容:
todoDetail Todo 详情缓存
todoLogs Todo 操作日志缓存
写操作会清理相关缓存,避免返回旧数据:
update / toggle / delete / restore
-> 清理 todoDetail
-> 清理 todoLogs
如果本地或服务器已经有 Redis,可以启用 redis profile,把缓存底层从本地内存切换成 Redis:
mvn spring-boot:run -Dspring-boot.run.profiles=redisRedis 连接配置文件:
src/main/resources/application-redis.properties
默认连接信息:
spring.cache.type=redis
spring.data.redis.host=${REDIS_HOST:localhost}
spring.data.redis.port=${REDIS_PORT:6379}
spring.data.redis.password=${REDIS_PASSWORD:}
spring.data.redis.timeout=2s当前 Redis 缓存 TTL:
todoDetail 10 分钟
todoLogs 5 分钟
默认缓存 10 分钟
如果没有安装 Redis,不要启用 redis profile,直接使用默认启动方式即可。
项目使用 Spring Event 解耦 Todo 主业务和操作日志写入:
TodoService
-> 发布 TodoActionLogEvent
-> 事务提交后 TodoActionLogEventListener 接收事件
-> 使用 todoTaskExecutor 异步写入 TodoActionLog
异步线程池配置文件:
src/main/java/com/zading/todoapi/config/AsyncConfig.java
当前线程池参数:
核心线程数:2
最大线程数:4
队列容量:100
线程名前缀:todo-async-
项目使用 Spring Scheduling 定时扫描过期 Todo:
每天 09:00
-> 扫描 dueDate 早于今天、未完成、未删除的 Todo
-> 如果还没有 OVERDUE 日志,则发布 TodoActionLogEvent
-> 异步写入 Todo 操作日志
定时任务入口:
src/main/java/com/zading/todoapi/job/TodoOverdueJob.java
默认配置:
app.todo.overdue-job.enabled=true
app.todo.overdue-job.cron=0 0 9 * * *
app.todo.overdue-job.zone=Asia/Shanghai
app.todo.overdue-job.page-size=50测试环境会关闭定时任务:
app.todo.overdue-job.enabled=false启用 Redis profile 后,过期扫描会先获取 lock:todo-overdue-job。如果其他应用实例已经持有这把锁,本实例会跳过本轮扫描。
任务最近一次执行状态可以通过内部接口查询:
GET /api/internal/jobs/todo-overdue
Authorization: Bearer <token>响应数据字段:
| 字段 | 说明 |
|---|---|
jobName |
任务名称 |
lastRunDate |
最近一次扫描对应的业务日期 |
lastRunAt |
最近一次实际执行时间 |
lastSuccess |
最近一次是否执行成功 |
lastProcessedCount |
最近一次处理的 Todo 数量 |
lastDurationMs |
最近一次耗时,单位毫秒 |
lastErrorMessage |
最近一次失败原因,成功时为 null |
Flyway 迁移脚本目录:
src/main/resources/db/migration/
当前迁移脚本:
V1__create_todos_table.sql
V2__add_priority_and_due_date_to_todos.sql
V3__create_users_and_link_todos.sql
V4__add_todo_lifecycle_fields.sql
V5__create_todo_action_logs_table.sql
V6__create_todo_attachments_table.sql
V7__add_user_role.sql
V8__add_todo_query_indexes.sql
V9__add_data_constraints_and_todo_version.sql
JPA 不负责自动修改表结构:
spring.jpa.hibernate.ddl-auto=validate数据库表结构由 Flyway 管理,JPA 只校验 Entity 模型和数据库表结构是否匹配。
第 25 周的数据库设计说明位于:
docs/database-design.md
文档记录了表关系、数据库约束、索引、事务边界和 Todo 乐观锁设计。
cd /Users/zading/Documents/Java/java-todo-api
mvn spring-boot:run健康检查:
curl http://localhost:8080/hello预期响应:
Hello Spring Boot
接口文档:
http://localhost:8080/swagger-ui.html
mvn test测试结构:
src/test/java/com/zading/todoapi/
├── ActuatorTests.java Actuator 健康检查 / 信息 / 指标测试
├── ApplicationSmokeTests.java 应用冒烟测试
├── AuthApiTests.java 注册 / 登录接口测试
├── OpenApiTests.java OpenAPI 文档测试
├── RbacApiTests.java 角色和管理员接口测试
├── TodoQueryOptimizationTests.java 索引、聚合查询和 EXPLAIN 测试
├── TodoDatabaseDesignTests.java 数据库迁移、约束和乐观锁测试
├── RedisProtectionTests.java 内存锁、限流和幂等测试
├── TodoAttachmentApiTests.java Todo 附件上传 / 下载接口测试
├── TodoApiTests.java Todo 业务接口测试
├── service/
│ ├── AdminServiceTest.java AdminService 单元测试
│ ├── AuthServiceTest.java AuthService 单元测试
│ ├── TodoAttachmentServiceTest.java
│ └── TodoServiceTest.java TodoService 单元测试
└── support/
├── AbstractApiTest.java 测试公共配置和数据清理
├── AuthTestClient.java 认证接口测试辅助类
└── TodoTestClient.java Todo 接口测试辅助类
测试覆盖:
- 健康检查接口
- 用户注册
- 用户登录
- 新注册用户默认 USER 角色
- 重复用户名注册失败
- 错误密码登录失败
- 未登录访问 Todo 返回 401
- 未登录访问管理员接口返回 401
- 普通用户访问管理员接口返回 403
- 管理员查询用户、Todo 和统计数据
- 创建 Todo
- 查询 Todo 列表
- 修改 Todo
- 切换完成状态
- 删除 Todo
- 恢复软删除 Todo
- 查询 Todo 操作日志
- 上传 Todo 附件
- 查询 Todo 附件列表
- 下载 Todo 附件
- 删除 Todo 附件
- 空附件上传失败
- Todo 附件用户隔离
- Todo 操作日志事件驱动异步写入
- Todo 过期扫描定时任务
- Todo 过期扫描任务状态查询
- Todo 详情缓存和缓存失效
- Todo 操作日志缓存和缓存失效
- Redis profile 配置和 TTL 设置
- 按完成状态筛选
- 按标题关键词搜索
- 分页和排序
- priority / dueDate 字段
- completedAt / deletedAt 生命周期字段
- 软删除后默认查询不可见
- Todo 操作日志写入和用户隔离
- Todo 数据按用户隔离
- OpenAPI 文档可访问
- Actuator health / info / metrics 可访问
- Actuator liveness / readiness 可访问
- 请求日志 requestId 响应头
- 参数校验错误响应
- 统一成功 / 错误响应结构
- 业务错误码
- 资源不存在错误响应
- Service 层正常流程、异常和用户隔离单元测试
- JWT 角色传递和密码校验单元测试
- 管理员统计和软删除查询单元测试
- 附件大小、路径安全和文件生命周期单元测试
- Todo 查询索引、聚合统计和执行计划测试
- Redis 分布式锁、限流和幂等流程测试
Service 单元测试可以单独运行:
mvn -q -Dtest=TodoServiceTest,AuthServiceTest,AdminServiceTest,TodoAttachmentServiceTest test查询优化测试可以单独运行:
mvn -q -Dtest=TodoQueryOptimizationTests test仓库提供 GitHub Actions 配置:
.github/workflows/ci.yml
当前 CI 会在 main 分支的 push 和 pull request 上执行:
mvn test
mvn package -DskipTests
CI 配置不会在本地自动执行;只有代码推送到 GitHub 并启用 Actions 后才会运行。
mvn package运行打包后的应用:
java -jar target/java-todo-api-1.0.0.jar使用 PostgreSQL profile 运行打包后的应用:
DB_USERNAME=postgres DB_PASSWORD=your_password \
java -jar target/java-todo-api-1.0.0.jar --spring.profiles.active=postgres使用 dev profile 运行:
java -jar target/java-todo-api-1.0.0.jar --spring.profiles.active=dev使用 prod profile 运行:
DB_URL=jdbc:postgresql://localhost:5432/java_todo_api \
DB_USERNAME=postgres \
DB_PASSWORD=your_password \
JWT_SECRET=your-strong-secret \
java -jar target/java-todo-api-1.0.0.jar --spring.profiles.active=prodGET /hello响应:
Hello Spring Boot
POST /api/auth/register
Content-Type: application/json请求体:
{
"username": "zading",
"password": "123456"
}响应:
{
"success": true,
"code": "CREATED",
"message": "创建成功",
"data": {
"id": 1,
"username": "zading",
"role": "USER",
"createdAt": "2026-08-12T10:00:00.123456"
},
"path": null
}POST /api/auth/login
Content-Type: application/json请求体:
{
"username": "zading",
"password": "123456"
}响应:
{
"success": true,
"code": "OK",
"message": "成功",
"data": {
"token": "xxxxx.yyyyy.zzzzz",
"tokenType": "Bearer"
},
"path": null
}管理员接口统一位于 /api/admin/**,只有 ADMIN 角色可以访问。
GET /api/admin/users?page=0&size=10&sort=id,asc
Authorization: Bearer <admin-token>GET /api/admin/todos?page=0&size=10&sort=id,asc
Authorization: Bearer <admin-token>默认不返回软删除 Todo;管理员可以通过 includeDeleted=true 查看:
GET /api/admin/todos?includeDeleted=true
Authorization: Bearer <admin-token>GET /api/admin/statistics
Authorization: Bearer <admin-token>普通注册用户默认是 USER,公开注册接口不会接收 role 字段。学习环境可以先注册用户,再执行:
UPDATE users SET role = 'ADMIN' WHERE username = 'zading';修改角色后需要重新登录,获得包含新角色的 JWT。
未登录请求返回 401 UNAUTHORIZED,普通用户请求管理员接口返回 403 FORBIDDEN。
除 /hello 健康检查接口外,业务 API 统一使用下面的响应结构:
{
"success": true,
"code": "OK",
"message": "成功",
"data": {},
"path": null
}字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
success |
boolean | 请求是否成功 |
code |
string | 业务状态码,例如 OK、TODO_NOT_FOUND |
message |
string | 给前端展示或调试使用的消息 |
data |
object/null | 真正的业务数据 |
path |
string/null | 出错时的请求路径 |
前端接入时,业务数据统一从 data 字段中读取。
除 /hello、/api/auth/register、/api/auth/login、/actuator/health/**、/actuator/info、/actuator/metrics/**、/h2-console/**、/v3/api-docs/** 和 /swagger-ui/** 外,其他接口都需要登录。/api/admin/** 还需要 ADMIN 角色。
访问 Todo API 时需要携带:
Authorization: Bearer <token>GET /actuator/health
GET /actuator/health/liveness
GET /actuator/health/readiness
GET /actuator/info
GET /actuator/metrics
GET /actuator/metrics/jvm.memory.usedActuator 端点用于观察应用运行状态,不返回具体业务数据。
GET /api/internal/jobs/todo-overdue
Authorization: Bearer <token>响应:
{
"success": true,
"code": "OK",
"message": "成功",
"data": {
"jobName": "todo-overdue",
"lastRunDate": "2026-08-21",
"lastRunAt": "2026-08-21T09:00:00.123456",
"lastSuccess": true,
"lastProcessedCount": 3,
"lastDurationMs": 18,
"lastErrorMessage": null
},
"path": null
}POST /api/todos/{todoId}/attachments
Authorization: Bearer <token>
Content-Type: multipart/form-data表单字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file |
file | 是 | 要上传的附件文件 |
响应:
{
"success": true,
"code": "CREATED",
"message": "创建成功",
"data": {
"id": 1,
"todoId": 10,
"originalFilename": "note.txt",
"contentType": "text/plain",
"fileSize": 16,
"createdAt": "2026-08-21T14:30:00.123456"
},
"path": null
}GET /api/todos/{todoId}/attachments
Authorization: Bearer <token>响应:
{
"success": true,
"code": "OK",
"message": "成功",
"data": [
{
"id": 1,
"todoId": 10,
"originalFilename": "note.txt",
"contentType": "text/plain",
"fileSize": 16,
"createdAt": "2026-08-21T14:30:00.123456"
}
],
"path": null
}GET /api/todos/{todoId}/attachments/{attachmentId}/download
Authorization: Bearer <token>下载接口直接返回文件流,并设置:
Content-Type: <文件类型>
Content-Disposition: attachmentDELETE /api/todos/{todoId}/attachments/{attachmentId}
Authorization: Bearer <token>响应:
{
"success": true,
"code": "OK",
"message": "删除附件成功",
"data": null,
"path": null
}GET /api/todos
Authorization: Bearer <token>Query 参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
completed |
boolean | 否 | 按完成状态筛选 |
keyword |
string | 否 | 按标题关键词搜索 |
page |
number | 否 | 页码,从 0 开始,默认 0 |
size |
number | 否 | 每页数量,默认 10,最大 100 |
sort |
string | 否 | 排序规则,格式为 字段,方向,默认 id,asc |
示例:
GET /api/todos
GET /api/todos?page=0&size=10
GET /api/todos?page=0&size=10&completed=true
GET /api/todos?page=0&size=10&keyword=java
GET /api/todos?page=0&size=10&completed=false&keyword=java&sort=createdAt,desc响应:
{
"success": true,
"code": "OK",
"message": "成功",
"data": {
"items": [
{
"id": 1,
"title": "实现 Todo API",
"completed": false,
"deleted": false,
"priority": "HIGH",
"dueDate": "2026-09-20",
"completedAt": null,
"deletedAt": null,
"createdAt": "2026-08-12T10:00:00.123456",
"updatedAt": "2026-08-12T10:00:00.123456"
}
],
"page": 0,
"size": 10,
"totalElements": 1,
"totalPages": 1,
"first": true,
"last": true
},
"path": null
}GET /api/todos/{id}
Authorization: Bearer <token>示例:
curl http://localhost:8080/api/todos/1 \
-H "Authorization: Bearer <token>"GET /api/todos/{id}/logs
Authorization: Bearer <token>示例:
curl http://localhost:8080/api/todos/1/logs \
-H "Authorization: Bearer <token>"响应:
{
"success": true,
"code": "OK",
"message": "成功",
"data": [
{
"id": 1,
"action": "CREATED",
"description": "创建 Todo",
"createdAt": "2026-08-17T10:00:00.123456"
},
{
"id": 2,
"action": "COMPLETED",
"description": "完成 Todo",
"createdAt": "2026-08-17T10:10:00.123456"
}
],
"path": null
}当前记录的操作类型:
CREATED 创建 Todo
UPDATED 修改 Todo
COMPLETED 完成 Todo
UNCOMPLETED 取消完成 Todo
DELETED 删除 Todo
RESTORED 恢复 Todo
POST /api/todos
Content-Type: application/json
Authorization: Bearer <token>请求体:
{
"title": "实现 Todo API",
"priority": "HIGH",
"dueDate": "2026-09-20"
}示例:
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"title":"实现 Todo API","priority":"HIGH","dueDate":"2026-09-20"}'成功状态码:
201 Created
PATCH /api/todos/{id}
Content-Type: application/json
Authorization: Bearer <token>请求体:
{
"title": "更新 API 文档",
"completed": true,
"priority": "LOW",
"dueDate": "2026-09-25"
}两个字段都可以单独传。
示例:
curl -X PATCH http://localhost:8080/api/todos/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"title":"更新 API 文档","completed":true,"priority":"LOW","dueDate":"2026-09-25"}'PATCH /api/todos/{id}/toggle
Authorization: Bearer <token>示例:
curl -X PATCH http://localhost:8080/api/todos/1/toggle \
-H "Authorization: Bearer <token>"完成状态规则:
未完成 -> 完成:completed = true,completedAt 写入当前时间
完成 -> 未完成:completed = false,completedAt 清空为 null
DELETE /api/todos/{id}
Authorization: Bearer <token>示例:
curl -X DELETE http://localhost:8080/api/todos/1 \
-H "Authorization: Bearer <token>"成功状态码:
200 OK
响应:
{
"success": true,
"code": "OK",
"message": "删除成功",
"data": null,
"path": null
}删除行为说明:
当前删除是软删除:数据库记录不会物理删除,而是设置 deleted = true,并写入 deletedAt。
普通查询默认只返回 deleted = false 的 Todo。
PATCH /api/todos/{id}/restore
Authorization: Bearer <token>示例:
curl -X PATCH http://localhost:8080/api/todos/1/restore \
-H "Authorization: Bearer <token>"响应:
{
"success": true,
"code": "OK",
"message": "恢复成功",
"data": {
"id": 1,
"title": "实现 Todo API",
"completed": false,
"deleted": false,
"priority": "HIGH",
"dueDate": "2026-09-20",
"completedAt": null,
"deletedAt": null,
"createdAt": "2026-08-12T10:00:00.123456",
"updatedAt": "2026-08-12T10:05:00.123456"
},
"path": null
}参数校验错误示例:
{
"success": false,
"code": "VALIDATION_FAILED",
"message": "title: 任务标题不能为空",
"data": null,
"path": "/api/todos"
}资源不存在示例:
{
"success": false,
"code": "TODO_NOT_FOUND",
"message": "Todo 不存在,id = 999",
"data": null,
"path": "/api/todos/999"
}未登录示例:
{
"success": false,
"code": "UNAUTHORIZED",
"message": "请先登录",
"data": null,
"path": "/api/todos"
}学习文档位于 docs/:
- 第 1 周:Java 基础和开发环境
- 第 2 周:面向对象、异常、集合和 Maven
- 第 3 周:控制台 Todo 项目和分层重构
- 第 4 周:Spring Boot Todo API
- 第 5 周:JPA 和数据库持久化
- 第 6 周:Profile、Flyway 和 DTO 分层
- 第 7 周:分页、排序、参数校验和字段扩展
- 第 8 周:用户注册、登录认证、JWT 和 Todo 数据隔离
- 第 9 周:Docker Compose、PostgreSQL、OpenAPI 和请求日志
- 第 10 周:测试体系重构、集成测试思维和 CI 准备
- 第 11 周:软删除、恢复接口和 Todo 生命周期
- 第 12 周:统一响应结构、错误码和参数校验
- 第 13 周:事务、Todo 操作日志和数据一致性
- 第 14 周:缓存、接口性能和查询优化
- 第 15 周:Redis 缓存入门和外部缓存配置
- 第 16 周:异步事件、线程池和操作日志解耦
- 第 17 周:定时任务、批处理和过期 Todo 扫描
- 第 18 周:Actuator 可观测性和后台任务状态查询
- 第 19 周:生产化配置、启动方式和日志排查
- 第 20 周:文件上传、下载和 Todo 附件管理
- 第 21 周:RBAC 角色权限控制与管理端接口
- 第 22 周:单元测试、Mock 和 Service 层测试
- 第 23 周:查询优化、索引和慢 SQL 思维
- 第 24 周:Docker 基础和项目容器化
- 第 25 周:PostgreSQL 深入和数据库设计
- 第 26 周:Redis 深入——分布式锁、限流和幂等
- 第 27 周:Kafka / RabbitMQ 消息队列
- 第 28 周:综合项目复盘和工程化重构