技术栈
了解组成 Offer快帮 的核心技术与选型理由。
了解组成 Offer快帮 简历工作台的核心技术与选型理由。本文按职责划分技术层次,帮助你在动手前建立完整心智模型:哪些库承担页面与渲染,哪些库负责数据与 AI,哪些库负责把 Next.js 应用真正带到 Cloudflare 边缘。
技术栈选型围绕四个目标展开:让真实经历更容易整理、让 AI 修改可解释且可回退、让运营成本随边缘运行时下降、让代码仓库本身可被 AI 编程工具准确理解与改写。下面按层次说明每一类技术的角色。
应用骨架
Next.js 16
Next.js 是项目唯一的页面与服务端框架,使用 App Router 组织路由、布局与渲染边界。
- App Router + Server Actions:营销页、应用工作区与 API 入口都通过
src/app/**暴露;表单提交、数据变更与权限校验优先复用 Server Action 与 Route Handler,避免再叠一层后端服务。 - Turbopack 默认开启:通过
pnpm dev启动的开发服务器使用 Turbopack;Next.js 16 起 Turbopack 已稳定为默认打包器,提供 2–5× 的生产构建提速和最高约 10× 的 Fast Refresh 加速。 - 缓存与渲染:
revalidateTag、updateTag与可选的 Cache Components 让 ISR、按标签失效和读后即写语义保持一致;本项目主要在简历预览与范本页面使用增量渲染。
本机
next dev与 OpenNext 提供的opennextjs-cloudflare preview不是同一种环境。涉及 D1、R2、KV 或 Durable Objects 的逻辑要在preview/deploy后再验证一遍。
React 19
React 19 提供 Server Components、Actions、Suspense 与新 Hooks,是 AI 流式返回与简历富文本编辑的共同底座。
- Server Components:营销页与简历预览默认在服务端渲染,把交互范围控制在编辑器、对话面板与即时反馈区域。
- Actions +
useActionState:登录、注册、范本维护、简历保存等表单直接走 Action,乐观更新与加载态由 React 自身接管。 - Refs 作为 prop 与新 Context API:让简历编辑器、TipTap 工具栏与命令面板组合更简洁,减少
forwardRef样板代码。
TypeScript
TypeScript 7 在整个仓库严格开启;tsconfig.json 通过 next typegen 生成的类型涵盖 Next.js 与 Cloudflare 绑定,CI 中执行 pnpm typecheck 作为类型门禁。
- 数据库 Schema、AI 契约与 API 路由共享一套类型;新增字段必须同步编辑、预览、持久化与历史版本四个链路。
- 不通过新增
any、@ts-ignore或关闭strict来掩盖类型问题,必要例外需在评审中说明范围。
样式与界面
Tailwind CSS 4
Tailwind CSS 4 提供工具优先的样式层。项目使用 tw-animate-css、@tailwindcss/postcss 与 CSS-first 配置,主题令牌放在 globals.css 中以 @theme 直接管理。
- v4 由 Rust 编写的 Oxide 引擎驱动,全量构建显著加快,HMR 接近即时。
- 设计令牌统一从
globals.css读取,便于在亮暗模式下维护同一套语义令牌,与DESIGN.md中的语义主题规则保持一致。 - 不使用任意值组合动态类名;动态类必须完整写在源代码中以保证 Tailwind 静态扫描能识别。
Radix UI 与 Lucide
Radix UI 提供可访问的底层原语(Dialog、Popover、Select、Tabs、Tooltip、Switch、Slider 等),全部封装在 src/components/ui 中。
- 所有交互控件遵循
docs/agent-rules/ui.md的约定:必须显示指针光标,破坏性操作走ConfirmPopover,仅图标控件同时具备aria-label与可见提示。 - Lucide 提供图标;图标与产品品牌、版式规则保持一致,不随意替换。
shadcn/ui 风格的本地组件
项目以 src/components/ui 为基础组件目录,组件代码留在仓库里、可以任意扩展与修改;不存在外部主题黑盒。components.json 仅作为本地添加新组件的脚手架入口,不接管样式或动画实现。
Motion(Framer Motion)
Motion(framer-motion 的继承者)承担简历模板切换、对话框展开、Toast 入场与统计数字变化的动效,强调克制——所有阴影与位移都遵循 DESIGN.md 的“对象专属阴影”规则,不为装饰而动。
TipTap
TipTap 是简历富文本编辑器的引擎,基于 ProseMirror,并通过 @tiptap/react 与 @tiptap/starter-kit 接入。
- 富文本支持加粗、斜体、列表、标题、链接、行内代码、任务列表与代码块,覆盖工作 / 项目 / 教育 / 技能等简历区块的表达需求。
- 简历内容以结构化 JSON 持久化;导出 PDF、PNG、Markdown 与 JSON 时分别走对应渲染器,避免把内部 ProseMirror 结构直接暴露给用户。
- 编辑器在 SSR 下需要
immediatelyRender: false,避免水合阶段的运行时错误。
AI Elements
src/components/ai-elements 是项目内的 AI 专用 UI 组件层,沿用 src/components/ui 的 Tailwind v4 + Radix 约定,源码完全留在仓库,可自由扩展与主题化;它不是封闭的 UI 框架,而是为 Vercel AI SDK 7 流式输出准备的可组合原语。
message/response:承担 Markdown、CJK、代码块、数学与图表渲染,与 Streamdown 协同负责流式内容的可读性。conversation:处理流式消息与自动贴底,配合use-stick-to-bottom让长对话保持最新内容可见。reasoning/tool/sources:分别承接推理过程、工具调用状态与引用来源,把模型的中间状态显式呈现给用户。actions/suggestion/prompt-input/model-selector:承载重新生成、快捷建议、附件输入与模型切换等交互。code-block:提供复制、下载与语言识别等代码块增强能力。
业务功能从 src/features/chat、src/features/ai-resume、src/features/career 与 src/features/ai-image 组合使用这些原语;新增 Agent 工具时通常只需补一个工具状态组件并接入对应消息数据,不需要重写聊天工作区。
命令面板与画布交互
cmdk 提供快速键命令面板原语,封装在 src/components/ui/command.tsx 中,承担简历区块跳转、AI 命令、范本搜索等高频交互;react-zoom-pan-pinch 提供头像与简历导出 PNG 的双指/滚轮缩放和拖动,封装在 src/components/ui/image-preview-dialog.tsx 中。两者都遵循 src/components/ui 的 Tailwind v4 + Radix 风格。
样式与 class 工具集
class-variance-authority(cva)负责声明按钮、徽章、Sidebar 等组件的尺寸、颜色、状态变体;clsx 与 tailwind-merge 在 src/lib/utils.ts 中组合成 cn(),合并动态类时正确处理 Tailwind 冲突。三者一起保证样式变体可读、动态拼接不出错。
状态、数据获取与表单
TanStack Query
TanStack Query 承担登录后工作区的服务端状态缓存,例如简历列表、范本目录、AI 任务进度与积分余额。
- 默认的查询策略只缓存请求级与页面级数据;账户身份与 Cookie 会话仍走 Better Auth,不重复建模。
- 缓存键全部使用强类型常量,避免散落的字符串拼接。
Zustand
Zustand 用于编辑器本地状态,例如当前选中的简历区块、布局配置、未保存草稿与命令面板状态。
- 不在 Zustand 中保存从数据库拉取的列表数据,避免与 TanStack Query 形成两份真相源。
React Hook Form + Zod
React Hook Form 与 Zod 4 是表单的事实标准。
- 校验 schema 在
validation.ts中声明,运行时验证、数据库 schema 推导(通过drizzle-zod)和 Server Action 入参校验复用同一份定义。 - 服务端校验是最终权威:客户端通过不等于信任,载荷进入业务前必须再次通过 Zod 校验。
业务边界与服务端动作
next-safe-action
Next Safe Action 把输入校验、错误响应与上下文注入统一封装。src/lib/safe-action.ts 暴露的工厂注入数据库访问、当前会话与统一错误类型。
- 破坏性操作(删除简历、移除范本、清空历史)使用
ConfirmPopover二次确认后,再走 Safe Action。 - 任何写入数据库或调用第三方服务的代码必须位于 Safe Action 或
src/features/<feature>/api/service.ts,不直接在页面或 Route Handler 中拼接业务规则。
AI 与内容生成
Vercel AI SDK 7
ai(ai@7)是 AI 调用的统一入口,由 @ai-sdk/react、@ai-sdk/google、@ai-sdk/anthropic 与 @ai-sdk/openai-compatible 共同支撑。
- 模型注册表位于
src/ai/core/models,可在运行时按需切换 Gemini、Claude 与兼容 OpenAI 的服务,避免把供应商协议泄漏到业务层。 - 结构化生成走
Output.object、Output.array,生成的简历字段、面试题与改写建议必须经过 Zod 校验后才能进入业务。 - 流式返回通过
streamText、useChat与项目内的 AI Elements 组件渲染,长对话使用use-stick-to-bottom让用户始终看到最新内容。
Streamdown
Streamdown 是为 AI 流式输出优化的 Markdown 渲染器,由 @streamdown/code、@streamdown/cjk、@streamdown/math、@streamdown/mermaid 等插件提供代码高亮、中文标点修正、数学公式与流程图能力。
- 简历中的代码片段、面试问答、职位描述引用等场景共用一套渲染管线,避免不同页面重复实现。
- 必须在
globals.css中添加@source "../node_modules/streamdown/dist/*.js";与各插件对应的@source行,Tailwind v4 才能识别 Streamdown 使用的工具类。
Gemini 与其他模型
当前实际调用的是 Google Gemini(通过 @ai-sdk/google)。Claude、OpenAI-compatible provider 已接入模型注册表,可在配置 GOOGLE_GENERATIVE_AI_API_KEY 之外通过替换环境变量切换。
- AI 只负责“改写真实经历”的表达层,不参与简历事实层;最终落库的内容由用户在编辑器中确认。
- 调用积分、暂计费与并发限流由
src/ai与src/integrations/credits共同处理,避免模型调用绕过用量控制。
数据与持久化
Drizzle ORM
Drizzle ORM 定义 src/integrations/database/schema.ts 中的所有表与关系,并生成可编译为 SQL 的类型安全查询。
- Schema 与迁移都和代码同仓库,迁移文件位于
drizzle/,可通过pnpm db:generate与pnpm db:migrate:dev/pnpm db:migrate:prod推进;不要在线上手动改表。 - 查询按已认证用户标识限定范围,不信任请求体里的所有权字段。
drizzle-zod把 Drizzle 表结构推导为 Zod schema,复用到 Server Action 与 API 校验中(例如src/integrations/payment/types.ts用createSelectSchema直接得到支付记录的运行时校验器)。
Cloudflare D1
D1 是基于 SQLite 的边缘关系数据库,绑定名 NEXT_TAG_CACHE_D1,用于简历、用户、积分、AI 设置与范本等主存储。
- 在 Worker 中通过 OpenNext 的
getCloudflareContext()拿到当前请求的 binding,本地开发依赖wrangler d1 migrations apply --local。 - D1 是 SQLite 方言,没有连接池概念,每次请求得到新 binding;要避免在热路径上做长事务。
LibSQL
@libsql/client 在脚本与本地工具中作为 D1 的 SQLite 兼容客户端出现,仅用于 Drizzle Kit、备份脚本与本地集成测试。
认证与会话
Better Auth
Better Auth 负责邮箱密码登录、Google 登录、邮箱验证与密码重置等账户能力。
- 通过 Drizzle 适配器(
drizzleAdapter(db, { provider: "sqlite" }))接入 D1,没有使用 Better Auth 的 D1 直连适配器,所有表都在 Drizzle Schema 中显式定义。 - 服务端在任何敏感操作前重新读取
getSession(),不依赖客户端传入的角色或权限字段。 - Cookie 配置与跨域策略集中在
src/features/auth中,登录页、注册页与受保护路由都通过统一的中间逻辑处理跳转与回跳。
支付与商业能力
Creem(默认支付)
Creem 是默认的 Merchant of Record,托管全球税务与合规,提供订阅、一次性付费、积分包与 Webhook。
- 单点集成即可在 190+ 国家合规收单;不需要自行处理 VAT、Sales Tax 与发票格式。
- Webhook 入口在
src/integrations/payment,用于把订单状态同步到本地积分与权限模型。 - 价格 ID、回调密钥和 Webhook 密钥通过
CREEM_*系列环境变量注入,不要写入仓库。
Stripe(可选)
项目同时保留 Stripe 适配,便于需要按 Stripe 收款或在 Creem 覆盖范围之外的地区扩展;@stripe/stripe-js 用于浏览器端,Webhook 与订阅管理走对应的 src/integrations/payment 模块。
邮件与通知
Resend
Resend 提供事务邮件 API;DKIM、SPF、DMARC 与送达统计由其控制台统一管理。
- 发件域名、收件 Audience 与 API Key 集中在
.dev.vars与 Cloudflare Secrets 中,避免散落在多处。 - Webhook 用于订阅确认、密码重置与简历分享通知,与前端
use-intl钩子协作只发当前语言版本的邮件。
React Email
React Email 6 以 React 组件方式编写邮件模板,输出与主流收件箱(Gmail、Outlook、Apple Mail)兼容的 HTML。
- 模板放在
src/integrations/mail/templates,通过pnpm exec react-email dev预览;邮件中的链接与按钮统一使用品牌色#ffd621与深墨色文字,避免在浅黄背景上写白色或低对比度文案。 next.config.ts用 stub 模块替换prettier/plugins/html与prettier/standalone,避免 React Email 的render在 Cloudflare Worker 中拖入 Prettier。
Sonner
Sonner 是项目内唯一的 Toast 组件,承载保存成功、导出完成、AI 改写被接受等即时反馈。
- 不同语义(成功 / 失败 / 加载中 / Promise)共用同一套 API,避免页面引入多个 Toast 库。
对象存储
Cloudflare R2 + S3 兼容接口
头像、简历导出缓存等静态对象存储在 Cloudflare R2 中,通过 aws4fetch 与 S3 兼容协议访问,不依赖 Worker 的 R2 binding。
- 桶名与区域写在
wrangler.jsonc的vars中;访问密钥通过wrangler secret put注入,不要硬编码。 - 上传链路在
src/integrations/storage,校验文件类型、大小与目录后生成预签名 URL,前端直传减少 Worker 流量。
文档与内容管理
Fumadocs 16
Fumadocs 16 与 fumadocs-mdx、fumadocs-ui 共同提供文档、博客与法律页的内容系统。
- 内容源位于
src/content/project、src/content/blog、src/content/legal,本地新增 MDX 后由source.config.ts自动收录。 - 文章内可通过 MDX 组件复用
src/components/ui中的基础控件,避免另写样式。
Shiki
Shiki 在文档站与简历编辑器中提供语法高亮,使用与主题一致的 github-light / github-dark 配色,保证亮暗模式下都有正确对比度。
MDX
MDX 让文档可以直接嵌入 React 组件。@types/mdx、mdast-util-toc、remark 与 unist-util-visit 提供前置依赖与目录提取能力。
国际化
next-intl 4
next-intl 4 提供路由、消息加载与翻译键的自动补全;use-intl 是它的客户端补充包。
- 当前默认且唯一公开的运行时语言是简体中文,URL 不使用
/zh前缀,访问/zh/*或/en/*会被规范化到无前缀中文路径。 - 所有用户可见文案集中放在
messages/*.json,新增文案必须同步修改messages/zh.json并避免散落到 JSX 中。
数据可视化与辅助组件
- Recharts 3:用于积分趋势、订阅概况与后台统计的图表;图表配色与设计令牌一致,不引入与品牌色冲突的第二主色。
- Number Flow:用于积分余额、价格、简历完成度等数字的过渡动画,比单纯
setText更有可读性。 use-stick-to-bottom:AI 流式对话、简历改写预览与长列表自动滚动到底端。qrcode:用于简历公开分享链接与登录二维码。html-to-image:把简历预览区域导出为 PNG。pdfjs-dist:解析用户在导入阶段上传的 PDF 简历与职位描述。sanitize-html:清洗粘贴到简历与简历中的富文本,阻止script、危险属性与外部资源。@tanstack/react-table:后台表格(用户管理、积分流水、范本目录等)使用 headless 表格核心,按列排序、筛选、分页由src/components/admin/*维护,配合 TanStack Query 拉取数据。html-react-parser:把外部简历与职位描述的 HTML 字符串转成 React 节点,配合sanitize-html在简历预览与 AI 对话中回放富文本。fflate:压缩/解压库,用于简历附件(.docx、模板包等)的 zip 解析与生成(src/features/career/files/resume-file.ts)。
通用工具与基础设施
uuidv4/v5:长 ID 生成;支付流水、积分交易、AI 示例消息 ID 等场景要求稳定的全局唯一键(src/integrations/payment、src/integrations/credits、src/components/ai/chat)。nanoid:短 ID 生成;AI Elements 的prompt-input用它给附件与消息片段打本地键,避免上传到服务端。ufo:URL 工具集;src/middleware.ts用withQuery给跳转链接附加查询参数,src/integrations/payment/providers/creem.ts用joinURL拼接 Creem 回调 URL。deepmerge:在src/i18n/中深度合并基础消息模块与语言叠加模块,使部分缺失的翻译自动回退到默认文案而不报错。cookie:解析 Cookie 头,Better Auth 与src/i18n/lib/request-locale.ts的语言探测都依赖它。
部署与基础设施
OpenNext for Cloudflare
@opennextjs/cloudflare 把 Next.js 构建产物适配到 Cloudflare Worker 运行时,保留 App Router、Server Components 与 ISR 等行为。
pnpm opennext:build生成.open-next/worker.js,pnpm preview在本地用真实workerd预览,pnpm deploy部署到生产。- D1、R2、KV 与 Durable Objects 都通过
getCloudflareContext()在 Worker 内获取;本地next dev拿不到真实绑定,需要在preview与deploy后验证依赖绑定的逻辑。
Cloudflare Workers / KV / Durable Objects
- Workers:应用运行时的宿主;通过
nodejs_compat与nodejs_compat_populate_process_env开启 Node.js API 兼容。 - KV(
NEXT_INC_CACHE_KV):增量缓存与会话相关键值存储。 - Durable Objects(
NEXT_CACHE_DO_QUEUE):用于 OpenNext 的队列与一致性协调。
Wrangler
Wrangler 是 Cloudflare 的部署与本地工具。仓库预置的脚本包括:
pnpm dev:本地 Turbopack 开发。pnpm opennext:build:构建 Cloudflare Worker 产物。pnpm preview:本地用workerd预览。pnpm deploy:部署到生产。pnpm db:migrate:dev/pnpm db:migrate:prod:本地与远程 D1 迁移。pnpm cf-typegen:刷新cloudflare-env.d.ts中的 Cloudflare 绑定类型。
质量与工具链
Biome 2.5
Biome 2.5 同时承担格式化与 lint,速度优于 ESLint + Prettier 组合。规则以 biome.json 为准,pnpm lint 与 pnpm lint:staged 在本地与 CI 中执行。
- 不在 Biome 之外再叠加 Prettier,避免两套格式化器产生冲突。
- 修改行为时同步更新注释与文档;自明的 UI 包装与局部函数不强制注释。
Drizzle Kit
Drizzle Kit 用于生成迁移与应用迁移;pnpm db:studio 提供本地 Schema 浏览器。
Node 测试与内置能力
scripts/check-storage-boundaries.mjs 与 src/test/storage/*.test.mjs、src/test/scripts/*.test.mjs 在 pnpm check:storage 中执行,使用 Node 内置 --test 框架;新增存储边界与脚本需要在测试中体现。
隐私与合规
vanilla-cookieconsent
vanilla-cookieconsent 提供 GDPR 合规的 Cookie 同意层。Google Analytics、Umami、Plausible 等分析供应商的配置集中在 src/config/analytics.ts,同意前的脚本加载逻辑由同意组件统一控制。
选型原则小结
- 真实优先:所有技术都为“让真实经历更容易整理”这件事服务;不为追新而引入新库。
- 边界清楚:AI、积分、支付、邮件与存储各自独立于业务边界,第三方升级不会污染业务层。
- 边缘优先:数据、AI 之外的渲染、缓存与会话状态尽量贴近用户,部署在 Cloudflare 全局网络。
- 类型贯穿:Schema、API 契约、AI 输出与表单校验复用同一套 Zod 校验,编译期捕获问题。
- 可被 AI 改写:组件代码留在仓库里、Tailwind 主题用语义令牌、API 边界清晰,让 AI 编程工具能够理解、改写并继续在现有结构上工作。
想深入了解每一层的目录组织与运行机制,看项目结构;了解 API 拆分与服务边界,看API 分层;了解数据模型与迁移流程,看数据库。