我在 4 周内独自构建了全栈 F1 Fantasy 平台 — 使用 AI Agents
Source: Dev.to
介绍
本文最初发表于 binaryroute.com。
在2025年12月下旬的短假期间,我打开笔记本电脑,心中只有一个简单的想法——打造我作为粉丝一直想要的 F1 幻想联盟平台。
四周后,Formula1.Plus 正式上线:
- 70 + 个数据库表
- 30 + 个 API 模块
- 赛道预测、实时排行榜、私人联盟、遥测仪表盘、新闻聚合、社区功能以及完整的管理后台
全部由 一名开发者 独立构建并发布。
本文将技术性地回顾我是如何实现这一切的——先搭建的框架、选用的技术栈、使其成为可能的 AI 工作流,以及我以后会做哪些不同的选择。
独自全栈开发的问题
如果你曾经尝试独自构建一个全栈产品,你一定体会过其中的痛苦。你会同时成为:
- Architect
- Front‑end developer
- Back‑end developer
- DBA
- DevOps engineer
- QA tester
一次性全部兼顾。
每一次上下文切换都会消耗你的时间。你花在“管道”上的时间多于产品本身。项目进展停滞、范围缩小,或者在进行到一半时就精疲力竭。
这样一个项目——包括预测引擎、评分系统、排行榜、联赛、带语义搜索的新闻聚合、遥测仪表盘、后台任务处理、Passkey 认证——原本需要 6 个月的艰苦开发。很可能在第 3 个月就被放弃。
有两件事改变了这个算式:
- 我构建的一个用于消除样板代码的框架。
- 充当配对程序员的 AI 代理。
ProjectX – 打破模板的框架
在写下任何一行 F1 代码之前,我已经构建了 ProjectX —— 一个带有 CLI 的主观化全栈 TypeScript 框架。
Idea: 只需定义一次数据库模式,运行一个命令,即可生成所有代码。
projectx crud --models drivers,races,predictions
这条命令会生成:
- 使用 Hono 的 API 路由,包含通过 Zod 进行的输入校验
- 包含业务逻辑和授权钩子的 Service 层
- 使用 Drizzle ORM 查询的 Repository 层
- 为前端提供带有正确缓存键的 TanStack Query 钩子
- 为 Service 层生成的单元测试脚手架
全部 类型安全,全部通过 依赖注入 连接。无需手动编写 API 类型定义 —— 前端可以通过 Hono 的 typed client 从后端直接导入路由类型。
Architecture Diagram (simplified)
HTTP Layer (Hono Routes)
↓
Middleware (Auth, Rate Limiting, Logging, Caching)
↓
DI Container
↓
Service Layer (Business Logic + Authorization)
↓
Repository Layer (Drizzle ORM)
↓
PostgreSQL
每个功能都遵循此模式。每个功能都有自己的文件夹。后期使用的 AI 代理能够立即在该结构中导航,因为它在整个项目中保持了一致性。
仓库结构
f1plus/
├── apps/
│ ├── api/ # Hono backend
│ └── web/ # TanStack Start frontend
├── packages/
│ ├── db/ # Drizzle schemas + migrations
│ ├── db-sync/ # F1 data synchronization
│ ├── types/ # Shared TypeScript types
│ ├── ui/ # shadcn/ui component library
│ ├── emails/ # React Email templates
│ ├── env/ # Zod‑based env validation
│ └── tsconfig/ # Shared TS configs
七个共享包、两个应用、一个 pnpm 工作区。所有内容共享类型,毫不偏离。
这个结构是唯一最重要的决定。原因并非它新颖——而是它让我和 AI 代理从第一天起就拥有了可预测的代码库。
Source: …
Stack 选择与理由
TanStack Start
- SSR‑ready(服务器端渲染就绪)
- 通过 TanStack Router 实现基于文件的路由
- 内置 TanStack Query 用于数据获取
路由实现了完整的类型安全——路由参数、搜索参数、loader 都都有类型。再配合后端 Hono 的类型化客户端,我可以实现 从数据库到组件的端到端类型安全,且无需手动编写任何类型。
// 前端 Hook —— 由 ProjectX CLI 生成
export function useDriverStandings(seasonId: string) {
return useQuery({
queryKey: ['driver-standings', seasonId],
queryFn: () => api.standings.drivers.$get({ query: { seasonId } }),
})
}
查询函数的类型来源于 Hono 路由定义。只要更改 API 响应结构,TypeScript 就会立刻在前端报错。
Tailwind v4 + CSS 自定义属性
我定义了一套设计令牌:
--f1-bg-card;
--f1-bg-secondary;
--f1-border;
--f1-text;
--f1-text-muted;
--f1-red;
这些令牌在浅色模式和暗色模式下会解析为不同的值。所有组件都使用这些令牌而不是硬编码颜色,从而实现 零组件级别覆盖 的完整 UI 主题化。
shadcn/ui
提供可访问、未样式化的基础组件,你可以自行定制。没有依赖锁定。我对每个组件都做了定制,以匹配 F1 的美学风格。
Hono
一个 15 KB 的 Web 框架,可运行在 Node、Cloudflare Workers、Deno、Bun 等环境。选择它的三大理由:
- 类型化路由 ——
hono/client零运行时开销的类型推断。 - 中间件组合 —— 限流、鉴权、日志、请求体大小限制、CORS 等都可以组合使用。
- 性能 —— 速度快,性能可观。
// 限流层级
const rateLimits = {
global: { max: 200, window: '1m' },
mutations:{ max: 30, window: '1m' },
expensive:{ max: 20, window: '1m' },
}
Drizzle ORM
在原始 SQL 与重量级 ORM 之间的最佳平衡。类型安全的查询、零运行时开销,且模式定义就是普通的 TypeScript。
export const drivers = pgTable('drivers', {
id: text('id').primaryKey(),
name: text('name').notNull(),
abbreviation:varchar('abbreviation', { length: 3 }),
nationality: text('nationality'),
dateOfBirth: date('date_of_birth'),
// …
})
pgvector + HuggingFace Transformers
用于 语义搜索 新闻文章——使用 Transformers 为文章生成向量嵌入,并通过相似度进行查询。这让用户能够按意义而非仅关键词搜索 F1 新闻。
后台任务工作者
| Worker | 负责的任务 |
|---|---|
| Scoring | 计算比赛得分并更新排行榜 |
| News Sync | 抓取并处理 F1 新闻文章 |
| 通过 React Email + Resend 发送事务性邮件 | |
| F1DB Sync | 自动同步来自 F1DB(GitHub 上社区维护的开源 F1 数据集)的历史数据 |
| Task | 通用异步任务 |
AI 配对编程工作流
- 提示生成 – 我用自然语言描述了所需的功能。
- 代码框架 – AI 生成了骨架(CLI 命令、文件布局)。
- 迭代细化 – 我请求具体细节(验证、认证钩子、测试)。
- 审查与合并 – 我检查了差异,运行测试并完成合并。
由于 ProjectX 强制执行了一致且可预测的结构,AI 能够可靠地定位正确的文件,导入正确的类型,并遵循现有约定。
我会做的不同之处
| 区域 | 原始做法 | 修订后做法 |
|---|---|---|
| Testing | 生成了单元测试脚手架,但后续大多数测试都是手动编写的。 | 从第 0 天起在服务层采用 property‑based testing(fast‑check)。 |
| CI/CD | 简单的 GitHub Actions 工作流。 | 为每个 PR 添加 preview deployments,使用 Vercel/Cloudflare Pages 及早捕获 UI 回归。 |
| Observability | 基础的控制台日志。 | 集成 OpenTelemetry + Loki/Grafana,实现 API 调用和后台任务的分布式追踪。 |
| Feature Flags | 硬编码的开关。 | 使用 LaunchDarkly‑风格的标记库,实现新评分算法的渐进式发布。 |
| Documentation | README + 行内注释。 | 使用 typedoc + swagger-ui 自动从 Hono 路由类型生成 API docs。 |
结束语
独自构建一个生产级、全栈的幻想联盟平台是件艰巨的任务,但只要有:
- 一个 有主见、自动生成代码的框架(ProjectX),它可以消除样板代码,
- AI 代理 充当配对程序员,
- 一个 精挑细选、类型安全的技术栈(TanStack Start、Hono、Drizzle、shadcn/ui),
工作量就能从数月压缩到数周。
如果你正准备单枪匹马进行全栈开发,首先要 标准化你的架构 并 利用 AI 完成重复的脚手架搭建。其余的工作将会顺畅得多。
祝编码愉快!
通用异步任务
Bull Board 提供用于监控所有队列的管理员仪表盘。
BetterAuth 处理 OAuth(Google、Discord、X)以及 Passkey/WebAuthn 支持。
Passkey 是认证的未来——无需密码,抗钓鱼,支持生物识别。
只用了一个下午就完成了设置。
让四周开发成为可能的关键环节
在整个构建过程中,我使用了 Claude Code、OpenAI Codex 和 Gemini——不是作为自动补全,而是作为能够在上下文中保持完整代码库的协作者。
- Claude Code 是核心开发者——直接在仓库中编写、重构和调试代码。
- Claude、Codex 和 Gemini 充当架构师:每个非平凡功能都会经过详尽的设计过程,我会在确定最终实现方案前收集多个模型的观点。不同模型会捕捉到不同的边缘情况,交叉覆盖提升了信心。
功能脚手架
我会描述一个功能(例如 “添加一个预测系统,让用户为每场比赛挑选车手,并使用每周锁定机制获取额外积分”),然后代理会生成:
- 数据模型
- 服务层
- 路由
- 验证规则
- 前端 Hook
第一次生成并不完美,但大约达到 80 %。我会审查、调整并迭代。
并行代码审查
当我需要将 50+ 个组件文件 中硬编码的 bg-white/10 不透明度模式迁移到用于浅色模式的 CSS 自定义属性时,我同时启动了三个 AI 代理——每个代理处理一批文件,并遵循相同的映射指南。原本需要整整一天的繁琐查找‑替换工作,几分钟内就完成,且结果保持一致。
调试
示例: 赛道‑电路 SVG 组件出现模糊效果,原因是 8 层堆叠的 CSS drop-shadow 滤镜 产生了指数级叠加。代理定位到根本原因(每个滤镜都作用于前面所有滤镜的累计结果),删除了轮廓系统,并调整了描边宽度——这本可能让我花费一小时的修复工作。
一致性
随着代码库增长到 50+ 个组件,AI 仍保持模式一致:
- 相同的命名约定
- 相同的文件结构
- 相同的验证方式
这正是单独开发者往往会开始偷工减料的地方。
关键洞见: AI 代理的表现取决于你提供的模式。
ProjectX 的强制性架构为 AI 提供了护栏:
- 服务位于
features/<name>/<name>.service.ts - 验证使用 Zod 模式,放在
features/<name>/validations.ts - 路由保持轻量——将业务委托给服务层
- 前端 Hook 遵循 TanStack Query 约定
如果没有这种一致性,你只会更快地生成“意大利面”代码。该框架不仅为我服务,也为 AI 提供了指导。
Source: …
部署故事
TanStack Start 通过 Vite 插件构建为 Cloudflare Workers。前端在边缘运行,全球分布,几乎没有冷启动。
- 成本: 在此规模下几乎免费;Cloudflare 的免费层非常慷慨。
Railway 运行 Hono API、PostgreSQL 和 Redis。Docker 多阶段构建保持镜像精简。健康检查、自动重启和部署预览都是内置的。
railway.toml
[deploy]
healthcheckPath = "/health/ready"
healthcheckTimeout = 30
restartPolicyType = "on_failure"
restartPolicyMaxRetries = 5
两个工作流
- CI – 对每个 PR 进行 lint、类型检查、构建、测试
- 部署 – 手动触发,先运行迁移,然后将 API 部署到 Railway,前端部署到 Cloudflare
Push to main → CI passes → Trigger deploy →
Run migrations → Deploy API (Railway) → Deploy Web (Cloudflare)
整个生产基础设施 —— API 服务器、PostgreSQL、Redis、边缘部署的前端、CI/CD —— 费用约为 $20 / 月(使用无服务器数据库和 Redis 选项约为 $10 / 月)。无需 Kubernetes。无需 Terraform。无需 DevOps 工程师。
4 周内的发布内容
预测与计分
- 赛道预测,包含车手和车队选择
- 本周锁定(Lock‑of‑the‑week)机制,用于大胆预测(额外积分)
- 最后名次选择、车队前 3 名、额外选择
- 通过后台工作者实现的自动计分引擎
排行榜与联赛
- 实时排行榜,包含全时段和单赛季排名
- 使用邀请码的私有联赛
- 联赛专属排行榜和积分榜
遥测与数据
- 车手 DNA 细分及表现分析
- 自动同步自 F1DB(社区维护的开源数据集)的历史数据
- 赛道概况,包含往期成绩和统计数据
- 基于 Recharts 的可视化
社区
- Grand Stand — 投票、讨论、社区互动
- 使用语义搜索 (
pgvector) 的新闻聚合 - 活动动态流和社交功能
管理后台
- 赛事管理与预测配置
- 通过 Bull Board 进行队列监控
- 审计日志、联系人管理、投票模板
认证与基础设施
- OAuth(Google、Discord、X)+ Passkey/WebAuthn
- 限流(分层:全局、变更、耗时查询)
- 使用 OpenTelemetry 的分布式追踪
- 使用 Pino 的结构化日志
F1 赛季即将到来,我正在在第 1 轮之前获取早期用户。
ProjectX – 开源
ProjectX——使这一切成为可能的框架——即将开源。它是一个单一 monorepo 的 CLI,能够开箱即用地脚手架 Web、移动端和浏览器扩展,并提供完整的架构。
功能包括:
- 插件系统