价格

架构总览

相关源文件

本页面内容基于以下源文件生成:

本项目是一个基于 TypeScript 的全栈 Web 应用,采用 Monorepo 架构组织代码。系统核心由 Fastify 后端服务与 Vite 驱动的多页前端应用构成,通过 TRPC 实现端到端类型安全的通信。数据持久化层使用 MongoDB 作为主存储,Redis 作为缓存与会话管理,整体架构设计注重开发效率与类型安全。

系统架构总览

系统采用经典的三层架构模式,前端层包含两个独立部署的 SPA 应用(Admin 管理后台与 PC 用户端),通过 TRPC 协议与后端 Fastify 服务通信。后端服务负责业务逻辑处理、数据持久化与定时任务调度。

正在加载图表渲染器...

架构要点说明

  1. 多页应用隔离:Admin 与 PC 端作为独立 SPA 应用,拥有各自的入口文件与路由系统,通过 Vite 多页构建配置实现代码分离(vite.config.ts:31-38

  2. 统一后端服务:Fastify 服务监听 5550 端口,集成 CORS、JWT、Redis、MongoDB 等中间件,提供 REST 与 RPC 双协议支持(back/init.ts:12-32

  3. 类型安全通信:TRPC 框架提供端到端类型推导,前端通过共享 AppRouter 类型定义获得完整的 IDE 智能提示与编译时类型检查(back/router.ts:19-21

  4. 数据库版本控制:内置迁移脚本系统,通过版本号标记管理数据库 Schema 变更,支持索引创建与数据结构演进(back/migrate.ts:4-31

  5. 定时任务调度:基于 node-cron 实现后台任务调度,开发环境下每 10 秒执行健康检查,生产环境可配置业务定时任务(back/procedures/index.ts:4-11

后端服务架构

后端服务基于 Fastify 框架构建,采用插件化架构组织功能模块。核心初始化流程在 back/init.ts 中完成,包括框架实例创建、中间件注册与外部服务连接。

框架初始化与插件配置

Fastify 实例创建时配置了路由参数长度限制(5000 字符),以支持复杂的查询参数场景。根据运行环境动态注册 CORS 中间件,开发环境允许所有来源的跨域请求(back/init.ts:19-23)。

typescript
1// 核心插件注册顺序
21. @fastify/cors    - 跨域资源共享(仅开发环境)
32. @fastify/jwt     - JWT 认证(密钥:自定义秘钥)
43. @fastify/redis   - Redis 连接(URL 来自环境变量)
54. @fastify/mongodb - MongoDB 连接(强制关闭模式)

JWT 插件使用硬编码密钥(生产环境应改为环境变量注入),Redis 与 MongoDB 连接均通过环境变量配置,支持容器化部署时的配置注入(back/init.ts:24-32)。

TRPC 路由定义与 OpenAPI 集成

TRPC 路由采用集中式注册模式,所有业务过程(Procedures)从 back/rpc/ 目录导入后聚合为单一路由对象。路由定义时通过 .meta() 方法附加 OpenAPI 元数据,包括接口摘要、描述与标签分类(back/router.ts:7-17)。

typescript
1// 路由元数据自动转换逻辑
2for (const key of Object.keys(routes)) {
3  const route = routes[key]
4  if (route.meta?.openapi) {
5    api.method = route._def.type === "query" ? "GET" : "POST"
6    api.path = `/${key}`
7  }
8}

系统自动根据 TRPC 过程类型(query/mutation)映射 HTTP 方法,query 对应 GET 请求,mutation 对应 POST 请求。这种设计允许同一套代码同时支持 TRPC 调用与标准 REST API 访问(back/router.ts:10-16)。

服务启动与数据库迁移流程

后端服务启动流程采用顺序初始化策略:首先监听端口,然后执行数据库迁移,最后启动定时任务调度器。任何步骤失败都会导致进程退出(back/index.ts:96-104)。

typescript
1async function main() {
2  const addr = await app.listen({ port: 5550, host: "0.0.0.0" })
3  console.info(`服务器运行在内网: ${addr}`)
4  const v = await migrate()
5  console.info("数据库迁移完成,当前版本:v." + v)
6  await runProcedures()
7  console.info("定时任务启动完成")
8}

进程级别注册了 unhandledRejection 事件监听器,捕获所有未处理的 Promise 拒绝,防止静默失败(back/index.ts:106-108)。这种防御性编程策略在生产环境中至关重要,确保异常可追溯。

前端多页应用架构

前端采用 Vite 驱动的多页应用(MPA)架构,Admin 与 PC 端作为独立应用构建与部署。这种架构避免了单页应用随业务增长导致的包体积膨胀问题,同时允许不同端使用独立的路由与状态管理策略。

Vite 多页应用配置

Vite 配置文件定义了两个构建入口:admin.htmlpc.html,分别对应管理后台与用户端应用。Rollup 构建选项确保输出文件按入口名称命名(vite.config.ts:31-38)。

开发服务器配置了自定义中间件,实现路由重写逻辑:访问 /admin/* 路径时重写为 /admin.html,访问 /pc/* 路径时重写为 /pc.html。这种 SPA fallback 策略确保前端路由正常工作(vite.config.ts:14-27)。

typescript
1// 开发服务器路由重写中间件
2configureServer(server) {
3  server.middlewares.use((req, _res, next) => {
4    if (req.url) {
5      if (url.startsWith("/admin")) req.url = "/admin.html"
6      else if (url.startsWith("/pc")) req.url = "/pc.html"
7    }
8    next()
9  })
10}

构建配置还集成了 React Compiler(Babel 插件)与 Tailwind CSS Vite 插件,实现组件级自动优化与原子化 CSS(vite.config.ts:7-13)。

Admin 与 PC 端独立入口

两个前端应用拥有平行的入口结构,均使用 React 18 的 createRoot API 挂载组件。入口文件配置了 Ant Design 的中文语言包与全局主题,通过 ConfigProvider 组件注入(src/admin/index.tsx:16-28)。

Admin 端入口注册了全局 TRPC 错误监听器,通过 subscribeRpcError 函数订阅 unhandledrejection 事件,过滤出 TRPC 错误并使用 Ant Design 的 message.error 显示(src/admin/index.tsx:13-15)。

typescript
1// 全局错误处理订阅
2subscribeRpcError((e) => {
3  message.error(e)
4})

PC 端入口采用相同的错误处理策略,两个应用共享 src/shared/rpc.ts 中的客户端配置与错误处理逻辑(src/pc/index.tsx:13-15)。

React 组件挂载与路由配置

Admin 端使用 react-router-domRouterProvider 组件挂载路由配置,路由定义独立于入口文件,便于后续扩展菜单生成功能(src/admin/router.tsx:1-18)。

typescript
1// 路由配置示例
2export const routes = [
3  {
4    path: "/",
5    element: <Dashboard />,
6    icon: <HomeOutlined />,
7    label: "看板",
8  },
9]

路由配置对象扩展了 RouteObject 类型,添加 iconlabel 字段,用于自动生成侧边栏菜单。路由实例创建时指定 basename: "/admin",确保与开发服务器的路由重写规则匹配(src/admin/router.tsx:16-18)。

前后端通信机制

系统采用 TRPC 框架实现前后端通信,提供端到端类型安全。TRPC 允许前端直接调用后端函数(语义上),底层自动处理 HTTP 请求序列化与响应解析。

TRPC Client 配置

前端 RPC 客户端在 src/shared/rpc.ts 中创建,使用 @trpc/clientcreateTRPCClient 函数。客户端配置了 HTTP 批处理链接,根据环境变量动态选择后端地址(src/shared/rpc.ts:12-18)。

typescript
1export default createTRPCClient<AppRouter>({
2  links: [
3    httpBatchLink({
4      url: import.meta.env.PROD ? "/api/rpc" : "http://localhost:5550/rpc",
5    }),
6  ],
7})

开发环境下客户端直连后端 5550 端口,生产环境下使用相对路径 /api/rpc,依赖反向代理转发请求。这种配置避免了开发环境的 CORS 问题,同时保持生产部署的灵活性(src/shared/rpc.ts:14-16)。

类型安全的 RPC 调用

前端组件通过导入共享的 RPC 客户端实例调用后端接口。Admin 端 Main 组件在 useEffect 钩子中调用 rpc.ping.mutate() 方法,该方法对应后端的 ping mutation 过程(src/admin/Main.tsx:6-11)。

typescript
1useEffect(() => {
2  rpc.ping.mutate().then((res) => {
3    console.log(res) // res 类型自动推导为 "pong"
4  })
5}, [])

TypeScript 编译器会验证调用参数与返回值类型,任何类型不匹配都会在编译时报错。这种类型安全机制消除了前后端接口契约不一致的风险(src/admin/Main.tsx:8-10)。

统一错误处理

RPC 客户端导出了 subscribeRpcError 函数,用于订阅全局 TRPC 错误。该函数监听浏览器的 unhandledrejection 事件,通过错误对象的 name 属性过滤出 TRPCClientErrorsrc/shared/rpc.ts:4-10)。

typescript
1export function subscribeRpcError(cb: (err: string) => void) {
2  window.addEventListener("unhandledrejection", (e) => {
3    if (e.reason?.name === "TRPCClientError") {
4      cb(e.reason.message)
5    }
6  })
7}

这种设计允许应用在入口处统一处理所有未捕获的 RPC 错误,避免在每个组件中重复编写错误处理逻辑。错误消息通过回调函数传递,调用方可选择使用 Ant Design 的 message.error 或其他通知机制展示(src/shared/rpc.ts:5-9)。

数据模型与存储

数据持久化层使用 MongoDB 作为主存储,采用 Zod 库定义 Schema 并提供运行时类型校验。数据模型定义与集合访问函数集中在 back/models/base.ts 中管理。

MongoDB 集合定义

系统通过 Fastify MongoDB 插件获取数据库实例,使用工厂函数模式创建集合访问器。每个集合访问器函数返回指定类型的 MongoDB Collection 对象(back/models/base.ts:32-44)。

typescript
1export const pingHisCols = () => 
2  app.mongo.db!.collection<PingHis>("pingHis")
3
4export const dbVersionCols = () => 
5  app.mongo.db!.collection<DbVersion>("dbVersions")

这种设计避免了在模块加载时访问数据库实例(此时 Fastify 尚未完成初始化),同时提供类型安全的集合操作。泛型参数确保 findinsertOne 等方法的参数与返回值类型正确(back/models/base.ts:32-33)。

Zod Schema 定义

数据模型使用 Zod 库定义,同时提供运行时校验与 TypeScript 类型推导。pingHis Schema 定义了三个字段:时间戳、IP 地址与请求次数(back/models/base.ts:36-40)。

typescript
1export const pingHis = z.object({
2  time: z.number().describe("时间戳"),
3  ip: z.string().describe("请求IP地址"),
4  count: z.number().describe("请求次数"),
5})
6
7export type PingHis = z.infer<typeof pingHis>

z.infer 工具类型从 Schema 自动推导 TypeScript 类型,确保类型定义与 Schema 同步。describe 方法附加的字段描述会自动同步到 OpenAPI 文档(back/models/base.ts:42)。

系统还定义了通用的高阶 Schema 工厂函数,如 paginatedResult 用于生成分页响应结构(back/models/base.ts:17-22)。

数据库迁移脚本

迁移系统通过版本号标记管理数据库结构演进。migrate 函数首先查询当前版本,然后按顺序执行各版本的迁移逻辑(back/migrate.ts:4-31)。

typescript
1export default async function migrate() {
2  const version = await dbVersionCols().findOne({ tag: "base" })
3  let v = version ? version.version : 0
4  
5  if (!version) {
6    // 初始化版本记录
7    await dbVersionCols().updateOne(
8      { tag: "base" },
9      { $set: { version: 0 } },
10      { upsert: true }
11    )
12  }
13  
14  if (v === 0) {
15    await pingHisCols().createIndex({ time: -1 })
16    await dbVersionCols().updateOne({ tag: "base" }, { $set: { version: 1 } })
17    v++
18  }
19  // 后续版本迁移...
20}

每个版本迁移块包含索引创建、集合重命名、数据转换等操作。迁移完成后更新版本号,确保下次启动时跳过已执行的迁移。这种幂等设计支持安全回滚与重新部署(back/migrate.ts:25-31)。

环境变量类型定义在 back/index.d.ts 中声明,包含 MongoDB 连接 URI、数据库名称、Redis URI 等配置项(back/index.d.ts:3-13)。

定时任务与后台服务

后台任务调度基于 node-cron 库实现,支持标准的 cron 表达式配置。任务定义与调度器启动逻辑集中在 back/procedures/ 目录。

Cron 定时任务调度

runProcedures 函数负责启动所有定时任务。开发环境下注册了一个每 10 秒执行一次的健康检查任务,生产环境可根据业务需求配置实际任务(back/procedures/index.ts:4-11)。

typescript
1export default function runProcedures() {
2  if (process.env.NODE_ENV === "development") {
3    cron.schedule("*/10 * * * * *", testService, {
4      timezone: "Asia/Shanghai",
5    })
6  }
7}

Cron 表达式 */10 * * * * * 表示每分钟的第 0、10、20、30、40、50 秒执行。时区配置为 Asia/Shanghai,确保任务在正确的本地时间执行(back/procedures/index.ts:7-9)。

后台服务逻辑

示例任务 testService 查询数据库版本并打印日志,用于验证服务运行状态。实际业务中可替换为数据清理、报表生成、消息推送等任务(back/procedures/test.ts:3-8)。

typescript
1export default async function testService() {
2  const v = await dbVersionCols().findOne({ tag: "base" })
3  console.log(`v.${v?.version || 0}版本服务器运行正常`)
4}

任务函数使用 async/await 语法处理异步操作,未捕获的异常会被进程级别的 unhandledRejection 监听器捕获。生产环境建议添加任务重试与告警机制(back/procedures/test.ts:4-7)。

核心数据流与调用链

以下时序图展示了前端组件调用后端 TRPC 过程的完整数据流,包括请求发起、服务端处理与响应返回。

正在加载图表渲染器...

数据流要点说明

  1. 请求发起:React 组件通过 useEffect 钩子调用 RPC 客户端的 mutate() 方法,触发异步请求(src/admin/Main.tsx:8

  2. 请求序列化:TRPC 客户端将函数调用转换为 HTTP POST 请求,请求体包含过程名称与参数的 JSON 序列化(src/shared/rpc.ts:14-16

  3. 服务端处理:Fastify 接收请求后,TRPC 中间件根据路径 /rpc/ping 路由到对应的 procedure 处理器(back/router.ts:19

  4. 数据库操作ping 过程执行 MongoDB 的 updateOne 操作,使用 upsert 模式确保文档存在(back/rpc/base.ts:21-34

  5. 响应返回:服务端返回字面量 "pong",TRPC 自动校验输出类型匹配 Zod Schema 定义(back/rpc/base.ts:35

核心设计决策与取舍

决策点选择方案理由已知限制
前端架构Vite MPA避免单包体积膨胀,支持独立部署共享代码需手动提取
通信协议TRPC端到端类型安全,无需 Schema 定义文件与非 TS 客户端集成困难
数据库MongoDB灵活 Schema,适合快速迭代缺少强一致性事务
缓存层Redis高性能会话存储,支持发布订阅增加运维复杂度
样式方案Tailwind CSS原子化 CSS,开发效率高类名较长,HTML 体积增加
定时任务node-cron简单易用,无需额外依赖不支持分布式锁
构建工具Vite快速 HMR,原生 ESM 支持部分老旧插件不兼容
ORM 策略原生 MongoDB Driver性能最优,无抽象层开销需手动处理关联查询

关键架构权衡

  1. TRPC vs GraphQL:选择 TRPC 放弃了 GraphQL 的灵活查询能力,但获得了更简单的开发模型与更小的运行时开销。对于内部管理系统,TRPC 的类型安全优势更为重要。

  2. MPA vs SPA:多页应用架构牺牲了页面间状态共享的便利性,换取了更好的构建隔离与独立部署能力。Admin 与 PC 端业务差异较大时,这种架构更为合适。

  3. Zod 运行时校验:在 TRPC 过程中使用 Zod 进行输入输出校验,虽然增加了少量运行时开销,但提供了强类型保障与自动生成的 API 文档。

  4. 硬编码 JWT 密钥:当前实现使用硬编码密钥(back/init.ts:24),生产环境必须改为环境变量注入,否则存在安全风险。

  5. 开发环境 CORS 全开:开发环境配置 origin: "*"back/init.ts:21),便于本地调试,但生产环境必须配置严格的白名单。

技术选型详表

技术版本要求用途选型理由替代方案
Fastify^4.x后端框架高性能,插件生态丰富Express, Koa, NestJS
TRPC^10.xRPC 框架端到端类型安全GraphQL, tRPC, gRPC
React^18.x前端框架组件化,生态成熟Vue, Svelte, Angular
Vite^5.x构建工具快速 HMR,原生 ESMWebpack, Rollup, esbuild
MongoDB^6.x主数据库灵活 Schema,水平扩展PostgreSQL, MySQL
Redis^7.x缓存/会话高性能,支持多种数据结构Memcached, etcd
Zod^3.xSchema 校验TypeScript 优先,类型推导Joi, Yup, io-ts
Ant Design^5.xUI 组件库企业级组件,中文支持好Material UI, Chakra UI
node-cron^3.x定时任务标准 cron 语法,轻量Agenda, Bull, node-schedule
dayjs^1.x日期处理轻量,API 兼容 Momentdate-fns, luxon
React Router^6.x前端路由声明式,支持懒加载TanStack Router, Wouter

模块依赖关系

正在加载图表渲染器...

依赖关系说明

  1. 前端共享层shared/rpc.ts 被 Admin 与 PC 端共同依赖,封装了 TRPC 客户端配置与错误处理逻辑(src/shared/rpc.ts:12-18

  2. 后端核心层back/init.ts 提供框架实例与 TRPC 构建器,被路由层与过程层依赖(back/init.ts:34-36

  3. 数据访问层back/models/base.ts 被迁移脚本、RPC 过程、定时任务共同依赖,提供统一的集合访问接口(back/models/base.ts:32-44

  4. 启动流程back/index.ts 作为入口文件,协调初始化、迁移、路由注册与任务启动的顺序(back/index.ts:96-104

关键配置与启动流程

环境变量配置

系统依赖以下环境变量运行,定义在 back/index.d.ts 中:

变量名类型必需说明
NODE_ENVstring运行环境:development/production
MONGO_URIstringMongoDB 连接字符串
MONGO_NAMEstring数据库名称
REDIS_URIstringRedis 连接字符串
DEFAULT_USER_IDstring默认用户标识

环境变量类型声明确保 TypeScript 编译器能正确推导 process.env 属性类型(back/index.d.ts:3-9)。

启动流程详解

正在加载图表渲染器...

启动阶段说明

  1. 端口监听:Fastify 服务绑定 0.0.0.0:5550,允许容器内外访问(back/index.ts:97

  2. 数据库迁移:查询版本记录,按需执行增量迁移脚本(back/migrate.ts:4-31

  3. 任务调度:根据环境注册定时任务,开发环境启动健康检查(back/procedures/index.ts:4-11

  4. 异常捕获:进程级别注册 unhandledRejection 监听器,确保异常可追溯(back/index.ts:106-108

开发与生产配置差异

配置项开发环境生产环境
后端地址localhost:5550/api/rpc(代理)
CORSorigin: "*"需配置白名单
定时任务每 10 秒健康检查按需配置业务任务
日志级别logger: false建议开启
JWT 密钥硬编码必须使用环境变量