从零打造一个跨端 TodoList API:iTodo 项目技术揭秘

这是一篇关于我个人项目 iTodo 的技术复盘。它是一个用 Spring Boot 4 + PostgreSQL + Redis 构建的跨端待办事项 HTTP API,同时也是我第一次系统性地用 Claude(AI 结对开发) 从规格文档一路写到腾讯云生产部署的完整实践。

这篇文章不只讲”我做了什么”,更想讲清楚三件事:架构怎么搭的、工程规范怎么定的、以及 怎么和 AI 协作把它落地的。如果你也在做后端项目、或者好奇 AI 辅助开发的真实流程,希望能给你一些可复用的思路。


一、这个项目是什么

iTodo 是一个面向 小程序端、网页端、App 端 的待办事项后端服务,只提供 HTTP API,不含前端界面。功能上对标成熟的任务管理产品:清单管理、任务的完成/重要/”我的一天”、子任务步骤、标签、提醒、多端增量同步、全文搜索。

它刻意做成了一个”麻雀虽小五脏俱全”的工程样板:


二、技术栈与选型思路

层次 选型 为什么
语言 Java 21 LTS (Temurin) 用上 Record、模式匹配、虚拟线程红利,长期支持
框架 Spring Boot 4.0.6 + Spring MVC 生态最成熟,团队/AI 都熟
安全 Spring Security 7 + JWT (jjwt) 无状态认证,天然适配多端
数据库 PostgreSQL 17 + HikariCP 强一致、TIMESTAMPTZ/INET/UUID 原生支持好
ORM MyBatis-Plus 3.5.16 简单 CRUD 零 SQL,复杂查询回落到 XML
缓存/限流 Redis 8.4 + Redisson 分布式限流、令牌桶
迁移 Flyway 数据库变更版本化,杜绝手改生产库
文档 springdoc-openapi 代码即文档,自动生成 Swagger UI
工具 Lombok + MapStruct + Hutool 减样板、DTO 映射、常用工具
观测 Actuator + Micrometer 健康检查与指标

选型的一条隐藏主线是:每一层都选”约束性强、约定优于配置”的方案。因为项目从一开始就打算和 AI 协作,工具越”有脾气”(有明确的正确用法),AI 生成的代码就越不容易跑偏。


三、整体架构

3.1 按功能分包(package-by-feature)

项目没有采用传统的 controller / service / mapper 三大目录横切,而是 按业务模块垂直切分:

1
2
3
4
5
6
7
8
9
10
11
12
com.example.itodo
├── auth        // 认证:登录/注册/刷新/登出、微信小程序
├── user        // 用户资料
├── todo        // 清单、任务、步骤、智能视图(核心域)
├── tag         // 标签
├── reminder    // 提醒
├── sync        // 多端同步(bootstrap + changes)
├── search      // 搜索
├── security    // JWT、过滤器、SecurityConfig
├── infra       // 基础设施,如限流 ratelimit
├── config      // OpenApi / Jackson / Redisson / WebMvc / Actuator
└── common      // 统一响应、错误、分页、TraceId 过滤器

每个功能包内自带 entity / mapper / dto / Service / Controller。这样做的好处是:改一个功能,相关代码都在一个包里,对人和 AI 都更友好——给 AI 的上下文可以精确到”只看 todo 包”。

3.2 分层与数据流

一次请求的完整链路是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Client
  │  Authorization: Bearer <JWT>
  ▼
JwtAuthenticationFilter ──► 解析 JWT,注入 CurrentUser
  ▼
Controller (@RestController, /api/v1/**)
  │  @Valid 校验 DTO;@AuthenticationPrincipal 拿当前用户
  ▼
Service  ──► 业务逻辑 + 权限校验(一切查询按 ownerId 收敛)
  │          写操作同时写入 sync_changes
  ▼
Mapper (MyBatis-Plus)  ──► PostgreSQL
  ▲
ApiResponseAdvice ──► 出参统一包装 + 注入 traceId

两个贯穿始终的横切设计值得单独说:

统一响应结构。 所有接口出参都被包装成 ApiResponse<T>:

1
2
3
4
5
6
public record ApiResponse<T>(
    boolean success,
    T data,
    ApiError error,
    String traceId
) { ... }

Controller 里只需 return ApiResponse.ok(data),而 traceId 由 ApiResponseAdvice(一个 ResponseBodyAdvice)在写出响应前自动从 MDC 补齐。前端拿到的永远是同一个信封结构,成功看 data,失败看 error,排障看 traceId。

全链路 TraceId。 TraceIdFilter 在请求入口生成/透传 traceId 并放入 MDC,日志和响应体都带上它。线上出问题时,用户报一个 traceId 就能串起整条日志。


四、几个核心设计亮点

4.1 认证:多身份 + Refresh Token 轮换

一个用户可以绑定多个”身份来源”(邮箱、手机号、微信),核心表结构是 users + user_identities 分离:

安全上的几条硬规矩(写进了 CLAUDE.md):短时效 access token + 轮换 refresh token、只存 refresh token 哈希、绝不打印密码/token、不使用带凭据的通配符 CORS。

一个真实的坑:微信 jscode2session 返回的 Content-Type 是 text/plain(但 body 其实是 JSON),RestClient.body(Xxx.class) 会直接抛 UnknownContentTypeException。解决办法是先读成 String 再用 ObjectMapper 手动解析。这类”文档没写、只有踩了才知道”的细节,正是需要沉淀进项目记忆的东西。

4.2 多端同步:版本号驱动的增量拉取

同步是这个项目最有意思的部分。策略很克制但很实用:

这个设计的精髓是 用一张 append-only 的变更日志表,把”多端一致性”这个复杂问题降维成”拉取比我新的记录”。代码层面所有写操作都通过一个统一的 SyncChangeService 落变更,新增资源类型只要扩展 SyncResourceType 枚举即可。

4.3 限流:Redisson 令牌桶保护认证接口

登录/注册/刷新这类接口是暴力破解的重灾区,用 Redisson 的分布式限流在 infra.ratelimit 里统一挡在前面,参数通过 RateLimitProperties 外部化配置。

4.4 软删除与乐观锁

所有业务表都有 deleted_at(软删除)和 created_at / updated_at。核心的 todos 表还有一个 version BIGINT,既服务于同步的版本推进,也为未来的乐观锁留了口子。


五、工程规范:这才是项目的”骨架”

代码谁都会写,但一个项目能不能持续演进、能不能让 AI 稳定产出正确代码,靠的是规范。iTodo 把规范拆成了几套明确的约定。

5.1 CLAUDE.md:给 AI 的”项目宪法”

CLAUDE.md 是整个 AI 协作的核心。它不是文档,而是 每次对话都会被加载的强约束,用最精炼的语言告诉 AI”在这个项目里什么必须做、什么绝不能做”。它包含几个板块:

写好 CLAUDE.md 的心得:规则要短、要绝对、要可验证。像”用户数据查询必须 scope by authenticated user”这种一句话规则,胜过一整页解释,因为 AI 每次生成 Controller/Service 时都会自动带上这条约束。

5.2 接口规范

5.3 数据库规范

从 V1__init_schema.sql 能提炼出一套非常一致的建表约定:

生产环境的一次教训:早期生产库曾绕过 Flyway 手工建表,导致 flyway_schema_history 记录了 V1/V2”已应用”,但实际有表缺失,微信登录直接报 relation "user_identities" does not exist。修复方式是写一个幂等的 CREATE TABLE IF NOT EXISTS 脚本补齐、且不动 Flyway 历史。这件事把”永远走 Flyway”这条规矩从纸面变成了肌肉记忆。

5.4 文档规范:OpenSpec 先行

项目在 docs/openspec/ 下维护了一整套 中文规格文档,在写代码之前就把系统说清楚:

这套文档还有一条硬约束:因为仓库是公开的,所有文档、示例配置里不得出现任何真实密钥,命名也用中性表达、不体现任何第三方品牌。

5.5 安全与部署规范

docs/deployment/ 下有完整的生产落地方案:itodo.service(systemd)、nginx.conf.example(HTTPS 反代 + 安全响应头)、security-checklist.md(安全清单)、restore-test.md(备份恢复演练)。核心原则:只开必要端口、数据库/Redis 不出公网、生产关闭 Swagger、密钥全走 OS 环境变量。


六、如何用 Claude 开发这个项目

这可能是最多人好奇的部分。我的做法不是”让 AI 帮我写个函数”,而是把 AI 当成一个 需要明确规格和护栏的结对工程师。整个流程是这样的:

步骤 1:先写规格,再写代码(Spec-Driven)

在动第一行代码之前,先和 AI 一起把 docs/openspec/ 写出来——目标、范围、模块、接口、分阶段计划。规格是 AI 的地图。有了它,后面每次让 AI 写某个模块,都能引用”按 feature-modules.md 里 任务 部分的接口清单实现”,而不是让它自由发挥。

步骤 2:用 CLAUDE.md 立护栏

把不可违背的规则沉淀进 CLAUDE.md(见第五章)。它会被自动加载,等于给 AI 装了一层”永远在线”的约束。这一步是把项目质量从”靠每次提示词”变成”靠制度保证”的关键。

步骤 3:按阶段推进,小步验证

严格按 development-plan.md 的 6 个阶段走:基础设施 → 认证 → 清单任务 → 标签提醒同步 → 部署加固 → 增强。每个阶段都有验收标准,完成一个阶段就用 mvn clean package 验证一次,再进下一个。绝不一次让 AI 生成一大坨。

步骤 4:把”验证”做成闭环

这是踩坑最多、也最重要的一环。几条血泪经验:

步骤 5:沉淀项目记忆

把上面这些坑、这些约定,持续写进项目的长期记忆里(AI 协作时的 memory 文件)。这样 同一个错误不会犯第二次——下次 AI 再碰 UUID、碰 TodoStatus、碰 Flyway,都会自动想起历史教训。这一步让 AI 协作真正产生了”复利”。

一句话总结这套方法论

规格给方向,CLAUDE.md 立规矩,分阶段控节奏,clean 验证兜底,记忆防复发。

AI 写代码的速度不是瓶颈,”让它每次都写对”才是。而”写对”靠的不是更聪明的提示词,是更好的工程约束。


七、你可以从这个项目学到什么

即使不用 AI,这个项目本身也是一份不错的后端工程范本。我认为最值得借鉴的几点:

  1. 统一响应 + 全链路 TraceId:让前后端协作和线上排障都变简单,成本极低,收益极高。
  2. package-by-feature 分包:改动内聚,给人和 AI 的上下文都更清晰。
  3. DTO 三分法(Request / QueryRequest / Response)+ MapStruct:实体永不出边界,编译期映射零反射。
  4. append-only 变更日志做多端同步:把复杂的一致性问题降维成”拉取增量”。
  5. 数据库层兜底一切:UUID 主键、TIMESTAMPTZ、软删除、部分索引、CHECK 约束、ON DELETE CASCADE,把正确性尽量下沉到数据库。
  6. Flyway 迁移铁律:永不手改生产库,一次教训终身受用。
  7. Spec-Driven + CLAUDE.md 的 AI 协作范式:规格先行、护栏立规、闭环验证、记忆防坑。

八、写在最后

iTodo 对我而言不只是”又做了一个 TodoList”。它验证了一个假设:当工程规范足够清晰、验证闭环足够严密时,AI 结对开发能稳定产出生产级代码。 AI 没有替代思考,反而逼着我把每一条约定都显式化——路径规范、DTO 规范、建表规范、安全规范,全都得写下来、说清楚。而这些被显式化的规范,最终既服务了 AI,也让项目本身变得更健壮。

如果你也想试试 AI 辅助开发,我的建议是:别急着让它写代码,先花时间和它一起把规格和规矩定下来。 磨刀不误砍柴工。


项目技术栈:Java 21 · Spring Boot 4 · PostgreSQL 17 · Redis 8.4 · MyBatis-Plus · Flyway · JWT。全程采用 Spec-Driven + CLAUDE.md 护栏的 AI 结对开发模式。