docs: add React Go platform selection

This commit is contained in:
2026-07-19 19:08:14 +08:00
parent 18b74a9d51
commit 20180085cc
+230
View File
@@ -0,0 +1,230 @@
# React + Go 内容平台技术选型
> 状态:提案
>
> 更新时间:2026-07-19
>
> 适用范围:SuBlog 从 Markdown 静态博客演进为可运营的内容系统。
## 1. 目标与边界
### 目标
- 提供公开的文章阅读、搜索、归档、标签、系列、SEO 和评论体验。
- 提供受权限控制的运营后台,支持文章草稿、发布、定时发布、修订、媒体上传和用户管理。
- 保留当前博客的视觉识别:技术图标环绕头像、主题切换动画、轻量内容阅读体验。
- 将业务规则、鉴权和数据一致性收敛到 Go 服务,前端不持有业务写入逻辑。
### 不在第一阶段解决
- 多租户、计费、复杂工作流和实时协同编辑。
- 微服务、消息队列和 gRPC。
- 直接把 Nuxt/Vue 组件转换为 React 组件。
首版采用模块化单体:一个 Next.js 应用、一个 Go API、一个 PostgreSQL 实例和一个 S3 兼容对象存储。先保证内容链路可靠,再按真实运营需求拆分。
## 2. 结论
采用以下组合:
| 层级 | 选型 | 决策 |
| --- | --- | --- |
| Web 前端 | Next.js App Router + React + TypeScript | 采用 |
| 样式与基础组件 | Tailwind CSS + shadcn/ui | 采用 |
| 视觉动效 | Magic UI | 采用,仅用于体验增强 |
| 后台服务 | Go + Gin | 采用 |
| 数据库 | PostgreSQL | 采用 |
| 媒体存储 | S3 协议对象存储,开发环境使用 MinIO | 采用 |
| 内容编辑器 | 首版 Markdown;富文本需求明确后引入 Tiptap | 延后 |
| API 契约 | OpenAPI 3.1 + TypeScript 客户端生成 | 采用 |
| 服务端状态 | TanStack Query,仅用于后台的客户端交互 | 采用 |
Next.js 的 App Router 默认使用服务端组件,适合将公开文章页在服务端从 Go API 获取数据后输出 HTML,同时把编辑器、筛选和管理表单限制在客户端组件中。[Next.js Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components)
shadcn/ui 的组件代码进入项目而非被黑盒依赖包封装,适合作为长期维护的后台设计系统;Magic UI 则只用于图标云、主题切换等差异化效果,不作为后台基础组件。[shadcn/ui Introduction](https://ui.shadcn.com/docs) [Magic UI Icon Cloud](https://magicui.design/docs/components/icon-cloud)
## 3. 总体架构
```mermaid
flowchart LR
Visitor["读者"] --> Web["Next.js Web\n公开站点与运营后台"]
Operator["运营人员"] --> Web
Web -->|"SSR / HTTPS API"| API["Go API\n鉴权与业务规则"]
API --> DB[("PostgreSQL")]
API --> Storage[("S3 / MinIO\n媒体资源")]
API --> Events["内容发布事件"]
Events --> Revalidate["Next.js 缓存失效端点"]
Revalidate --> Web
```
生产环境使用同一主域名,反向代理按路径分流:
```text
https://example.com/ -> Next.js
https://example.com/admin/ -> Next.js
https://example.com/api/v1/ -> Go API
```
这样浏览器访问 API 不需要 CORS,Go 可使用 `HttpOnly``Secure``SameSite=Lax` 的会话 Cookie。Next.js 仅负责渲染、路由和 API 调用适配;文章发布、权限判断、文件签名和数据写入只能由 Go 服务完成。
## 4. React 前端设计
### 4.1 框架职责
| 区域 | 渲染方式 | 数据方式 | 说明 |
| --- | --- | --- | --- |
| 首页、文章页、归档、标签、系列 | Server Components 优先 | Next 服务端请求 Go API | SEO、首屏和分享信息优先 |
| 搜索、评论、主题切换、头像图标云 | Client Components | 按需调用 API | 仅加载必需的浏览器逻辑 |
| `/admin` | Client Components 为主 | TanStack Query 调用 Go API | 表格、筛选、编辑和乐观更新 |
| 登录页 | Server + Client Components | Go 会话 API | 不在浏览器保存访问令牌 |
TanStack Query 管理后台的远程数据缓存、重新获取和写入后的失效;公开页面不为此引入客户端状态层,直接使用 Next 的服务端数据获取和缓存策略。[TanStack Query Queries](https://tanstack.com/query/latest/docs/framework/react/guides/queries)
### 4.2 UI 与视觉系统
以 Tailwind CSS 的语义变量作为唯一主题来源,先定义 `background``foreground``card``muted``border``primary` 等 token,再由 shadcn/ui 和业务组件共同消费。不要将页面色值散落在组件中。
Magic UI 的定位是“效果层”,使用范围限定为首页品牌区、空状态和少量内容提示;文章正文、后台表格、表单和弹窗保持克制、信息密度优先。
#### 现有视觉资产迁移
| 现有能力 | 当前实现 | React 迁移方案 | 可复用范围 |
| --- | --- | --- | --- |
| 技术图标环绕头像 | `vue-icon-cloud` + CSS | Magic UI `IconCloud` | 图标 slug、图片 URL、尺寸与视觉参数可复用;Vue 组件代码不可复用 |
| 主题切换 | Vue `useColorMode` + View Transitions API | `next-themes` 管理主题状态,Magic UI `AnimatedThemeToggler` 负责动画 | 动画原点、时长、`prefers-reduced-motion` 降级和 CSS token 可复用 |
| 深浅色样式 | CSS 变量 | `globals.css` 的 Tailwind/shadcn token | 色彩语义和视觉值可复用,选择器需重写 |
现有项目并没有直接使用 Magic UI:头像云是 Vue 专用组件,主题切换是自行封装的 View Transitions API。React 端可直接采用 Magic UI 对应组件的实现路径,官方提供了 `IconCloud` 和基于 View Transitions API 的主题切换组件。[Magic UI Theme Toggler](https://magicui.design/docs/components/animated-theme-toggler)
### 4.3 内容编辑策略
首版以 Markdown 为正文规范,延续现有文章资产并降低导入风险:
- 后台编辑器保存 `body_markdown`,预览由与公开页面一致的渲染器生成。
- 文章的标题、摘要、标签、发布时间、封面和状态使用结构化字段保存。
- 每次保存创建修订快照,发布操作产生不可变的发布记录。
当存在非技术作者、复杂嵌入内容或协同编辑的明确需求时,再评估 Tiptap。Tiptap 提供 React 集成和可组合扩展,但其 Markdown 能力仍处于 beta 文档范围,因此不将它作为首版 Markdown 数据规范的前提。[Tiptap Editor](https://tiptap.dev/docs/editor/getting-started/overview)
## 5. Go 后端设计
### 5.1 模块边界
```text
apps/api/
├── cmd/api/ # 程序入口、依赖装配、优雅退出
├── internal/
│ ├── identity/ # 用户、角色、会话、登录限制
│ ├── content/ # 文章、标签、系列、草稿、修订、发布
│ ├── media/ # 上传策略、文件元数据、对象存储
│ ├── site/ # 站点设置、导航、主题等运营配置
│ ├── audit/ # 管理操作审计日志
│ ├── platform/ # 数据库、缓存、对象存储、时钟、ID
│ └── transport/http/ # Gin 路由、鉴权中间件、请求/响应 DTO
├── migrations/ # 仅追加的数据库迁移
└── openapi/ # OpenAPI 源文件与生成配置
```
业务模块拥有自己的服务、仓储接口和测试;HTTP handler 只做参数绑定、鉴权调用和响应映射。禁止 handler 直接拼接 SQL,禁止前端绕过 Go 直接访问数据库或对象存储。
### 5.2 首版数据模型
| 实体 | 关键字段 | 说明 |
| --- | --- | --- |
| `users` | `id``email``status` | 运营用户身份 |
| `roles` / `user_roles` | `role``permission` | RBAC,首版至少管理员、编辑、只读 |
| `sessions` | `token_hash``expires_at``user_id` | 服务端会话,不将访问令牌写入 localStorage |
| `posts` | `slug``title``summary``body_markdown``status``published_at` | 文章当前版本 |
| `post_revisions` | `post_id``content``created_by` | 草稿与发布前可回滚快照 |
| `tags` / `post_tags` | `slug``name` | 标签关系 |
| `media` | `object_key``mime_type``size``created_by` | 文件元数据,文件本体在对象存储 |
| `audit_logs` | `actor_id``action``target``metadata` | 发布、删除、授权等关键操作记录 |
文章状态固定为 `draft``scheduled``published``archived`。状态流转在 `content` 模块校验,数据库事务内同时更新文章、修订和审计日志。
### 5.3 API 与契约
- 使用 `/api/v1/public/*` 提供公开只读接口,例如文章详情、归档、标签、站点配置。
- 使用 `/api/v1/admin/*` 提供后台接口,并按角色授权。
- 以 OpenAPI 3.1 为唯一接口契约,从契约生成 React TypeScript 客户端;Go handler 的集成测试验证响应符合契约。
- 发布、下线、删除、媒体上传等写入接口要求会话、CSRF 防护、审计日志和幂等键。
- 发布成功后,Go 使用签名事件调用 Next.js 缓存失效端点;不得依赖固定时间等待缓存过期。
## 6. 仓库结构
迁移完成后采用 pnpm workspace 与 Go 模块并存的单仓库:
```text
sublog/
├── apps/
│ ├── web/ # Next.js
│ │ ├── app/
│ │ │ ├── (public)/
│ │ │ └── admin/
│ │ ├── components/
│ │ │ ├── ui/ # shadcn/ui 受控源码
│ │ │ ├── effects/ # Magic UI 及品牌动效
│ │ │ └── content/
│ │ ├── lib/api/ # 生成的 API client 与查询封装
│ │ └── styles/
│ └── api/ # Go 服务
├── contracts/openapi/ # API 契约
├── deploy/ # Compose、反向代理、生产部署说明
├── scripts/migrate-content/ # 现有 Markdown 导入工具
└── docs/
```
现有 `content/posts/<slug>/index.md` 仅作为迁移输入和归档备份;迁移完成后,运行时内容的唯一来源是 Go API + PostgreSQL。
## 7. 分阶段迁移
### 阶段 0:技术验证
完成一条最小闭环:Go 提供一篇文章的公开读取接口,Next.js 服务端渲染文章详情,Magic UI 图标云和主题切换在 React 中正确运行。
验收:SEO 元信息可见、首次加载无主题闪烁、禁用动效偏好下主题可正常切换。
### 阶段 1:公开内容迁移
1. 建立 PostgreSQL 迁移、`posts``tags``post_revisions` 和公开 API。
2. 编写一次性导入脚本,读取现有 frontmatter 和 Markdown 正文。
3. 重建首页、文章详情、列表、归档、标签和系列页面。
4. 保留原 URL,缺失页面使用 301 重定向,逐页比对标题、描述、封面和正文。
验收:全部已发布文章可访问;迁移前后的 URL、SEO 字段和图片链接无回归。
### 阶段 2:运营后台
1. 建立登录、会话、RBAC 与审计日志。
2. 实现文章列表、草稿编辑、预览、发布、定时发布和修订回滚。
3. 接入对象存储的预签名上传与媒体选择器。
4. 接入内容发布事件与 Next.js 缓存失效。
验收:编辑只能操作授权资源;发布后公开页与 sitemap 在可预期时间内更新;审计记录可追踪。
### 阶段 3:运营能力完善
- 后台搜索、内容质量检查、死链检测与站点设置。
- 评论审核、内容推荐和访问统计。
- 基于实际作者需求决定是否引入 Tiptap、协同编辑或内容审批流。
## 8. 关键风险与验证
| 风险 | 影响 | MVP 验证 |
| --- | --- | --- |
| Markdown 导入丢失格式 | 历史内容不可逆受损 | 保留原文件,导入后逐篇 hash 与人工抽检 |
| Next 缓存未失效 | 发布后读者仍看到旧内容 | 发布接口后调用签名失效端点,编写端到端测试 |
| 前后端职责混杂 | 规则分散且难以审计 | 代码审查禁止 Next Route Handler 承载业务写入 |
| 富文本过早引入 | 数据格式和编辑器维护成本暴涨 | 首版只验证 Markdown 编辑与预览 |
| 动效影响后台可用性 | 性能与无障碍下降 | Magic UI 仅在品牌区域使用,尊重 `prefers-reduced-motion` |
## 9. 测试与上线标准
- Go:领域服务单元测试、HTTP + PostgreSQL 集成测试、迁移升级测试、权限矩阵测试。
- Web:组件测试覆盖主题与编辑表单;Playwright 覆盖登录、草稿、发布、撤回、文章访问和上传。
- 契约:CI 生成 TypeScript API client,并检测 OpenAPI 变更。
- 发布:健康检查、数据库备份、对象存储生命周期规则、错误监控和审计日志保留策略齐备后才接入真实运营数据。
## 10. 最终决策
React + Go 适合本项目向内容平台演进,但必须以“Next.js 负责体验与渲染、Go 负责全部业务与数据”为边界。优先迁移公开阅读链路,再实现后台闭环;不要在第一阶段同时重写全部页面、接入富文本和实现复杂工作流。