外观
架构说明
明确定位
每个产品都应该有明确的定位,需要知道自己的目标客户是哪些人群,定位到了目标客户,才能够有针对性的开发。
峰云CMS的定位是面向中小微企业的企业级建站程序。所以我们在架构设计上进行了针对性地调整。
- 我们不会过度强调性能而引入额外的复杂度。比如我们未使用分布式系统架构和消息队列;比如数据库方面我们仅使用了
MySql/MariaDB,未使用Redis、MongoDB等其他类型的数据库。 - 我们不使用特别小众的技术栈,这会导致一定的招人成本和上手速度。
- 我们会尽量让程序容易部署和发布。
- 我们会保持技术栈始终是一个“较新”的状态。一来更容易维护,二来可以使用一些新特性。比如现在Vue3已经出来很久了,我们就不会再使用Vue2了。比如
Node最新LTS版本已经原生支持TypeScript了,这意味着服务端代码可以免去频繁的TypeScript转JavaScript的编译过程了! - 作为企业级程序,我们会使用
TypeScript来提高代码质量。
技术栈
- Node.js v22.18.0:该版本原生支持
TypeScript。免去服务端代码的编译环节,大大提高了开发和部署时的效率。 - Vue3:渐进式前端框架,使用
TypeScript进行开发。 - ElementPlus:前端 UI 组件库,用于快速搭建 PC 端界面。
- TypeScript:JavaScript 超集,支持类型声明,提高代码质量和可维护性。
- Express:作为最经典和广泛使用的
Node.js服务端框架,相比其他Node.js框架,学习和使用成本低很多。 - MySql/MariaDB:关系型数据库,用于存储数据。session数据也会持久化到其中。
- Knex.js:SQL构建器,用于生成数据库查询语句和定义数据库表结构,也作为数据库迁移工具。
- PM2:进程管理工具,用于生产环境部署。
- Zod:用于数据验证和类型推断。
- RabbitMQ:消息队列(用于异步任务处理)。
- EJS:服务端模板引擎,用于 SSR 渲染。
- Vite:前端构建工具,提供快速的开发体验。
- VitePress:静态站点生成器,可用于生成文档。
- Nuxt 4:基于 Vue 3 的全栈框架,支持 SSR、SSG 和 SPA 模式,用于构建需要 SEO 优化的独立前端项目(位于
apps-home目录)。 - 七牛云 OSS:对象存储服务。
- 蓝兔支付:第三方支付服务(微信支付)。
- 微信公众号:集成微信公众号功能。
代码目录结构说明
text
aimian/
├── apps/ # 部署到主站域名下的前端子应用目录
│ ├── _base/ # 基础共享组件和工具
│ ├── account/ # 用户账户管理应用
│ ├── admin/ # 管理后台应用
│ ├── bugs/ # Bug 追踪系统应用
│ ├── ppt/ # AI PPT应用
│ ├── design/ # 设计工具应用
│ ├── todos/ # Hi备忘录应用
│ ├── album/ # 开心相册应用
│ └── work # 非常工作台应用
├── apps-home/ # 部署到独立域名下的前端应用目录,一般作为各个产品的落地页
│ ├── blog/ # 站长个人博客应用
│ ├── bugs/ # Bug 追踪系统落地页
│ ├── ppt/ # AI PPT应用落地页
│ ├── design/ # 设计工具应用落地页
│ ├── todos/ # Hi备忘录应用落地页
│ ├── album/ # 开心相册应用落地页
│ └── work # 非常工作台应用落地页
├── assets/ # 供服务端程序使用的静态资源,如邮件模板、提示词模板等
├── build/ # 构建、部署用的脚本
├── consumers/ # RabbitMQ 消息队列消费者脚本,独立进程启动
├── database/ # 用于存放一些将其他数据导入到主站数据库的脚本
├── docs/ # 项目文档
├── hosts/ # 同步和部署存放在不同服务器主机上的 nginx 配置文件,RabbitMQ 和 Docker脚本等
├── https/ # 存放 .http 文件,可用于接口测试
├── localhost/ # 在本地开发机上运行的一些程序,不需要部署到正式服务器上
├── logs/ # 程序运行时产生的日志文件,不需要纳入版本控制,也不需要lint
├── migrations/ # 数据库迁移文件
├── node_modules/ # 项目依赖的第三方 npm 包
├── patches/ # 用于临时修复第三方包里的代码,供 `patch-package` 包使用
├── public/ # 静态资源文件
├── schedules/ # 定时任务脚本
├── typings/ # TypeScript 类型定义(前后端项目共享)
├── views/ # EJS 模板文件(SSR 渲染)
├── src/ # 服务端源代码
│ ├── bin/ # 服务启动入口
│ ├── controllers/ # 控制器层(处理 HTTP 请求)
│ ├── middlewares/ # 中间件
│ ├── models/ # 数据模型层(数据库表定义)
│ ├── routes/ # 路由定义
│ ├── services/ # 业务逻辑层+数据访问层
│ └── scripts/ # 工具脚本
└── package.json # 项目配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
目录详细说明
apps目录
该目录用于存放前端子应用代码。
- apps/_base/:子应用使用的基础代码,封装了公共方法、基础组件、通用业务逻辑
src/components/:共享组件(如 RechargeDialog、SiteTopBar 等)src/scripts/:工具函数(请求封装、常量定义等)src/stores/:状态管理(用户状态等)src/styles/:全局样式
- apps/account/:用户账户管理应用(注册、登录、余额管理等)
- apps/admin/:管理后台应用(仪表盘、用户余额管理、交易记录查询等)
- apps/docs/:文档站点(VitePress)
当你要开发其他前端子应用时,请在apps目录下创建对应的目录。apps目录下除_base目录外,每个目录都是一个相对独立的前端项目。之所以说是“相对独立”,是因为它们的依赖都是统一声明在项目根目录下的package.json中的,并不需要每个子应用里重复安装。
建议
- 现有的子应用都是使用
Vue3+TypeScript+ElementPlus技术栈开发的,新的子应用也推荐使用这一套技术栈(并不强制)。 - 子应用一般不需要支持SEO,所以不建议使用next之类的全栈框架来进行纯前端输出,无故提高了项目的复杂度和编译时间。
apps-home目录
该目录用于存放前端独立项目的代码。
有时候我们希望创建独立的前端项目,或者是为子应用创建一个落地页,这些项目通常需要支持SEO,并且一般使用独立域名下的根路径url来访问,比如https://www.example.com/。
这些项目的代码通常会存放在apps-home目录下,每个子目录都是一个相对独立的前端项目。
Nuxt 4 模板项目
apps-home/_base 目录下提供了一个 Nuxt 4 框架的模板项目,后续在 apps-home 下新建的 Nuxt 项目应参考该模板项目的写法进行开发。
模板项目结构:
apps-home/_base/
├── app/ # 应用源代码目录
│ ├── app.vue # 根组件
│ └── pages/ # 页面目录(基于文件系统的路由)
│ ├── index.vue # 首页
│ └── blog/
│ └── [slug].vue # 动态路由页面
├── build/
│ └── deploy.mts # 部署脚本
├── nuxt.config.ts # Nuxt 配置文件
└── tsconfig.json # TypeScript 配置文件1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
核心特性:
配置文件(
nuxt.config.ts):- 使用
generateNuxtConfig函数生成配置(位于build/nuxt.config.mts) - 支持 CDN 配置(
cdnURL) - 自动配置构建目录和输出目录
- 支持开发服务器端口配置
- 使用
TypeScript 配置(
tsconfig.json):- 包含项目根目录的类型定义(
../../typings/*.d.ts) - 包含共享脚本(
../../src/scripts/*.mts) - 配置路径别名:
@/*:指向./app/*@base/*:指向../_base/app/*(用于引用模板项目代码)
- 包含项目根目录的类型定义(
页面路由:
- 基于文件系统的路由(
app/pages/目录) - 支持动态路由(如
[slug].vue) - 使用
<NuxtPage />组件渲染页面 - 使用
<NuxtLink>组件进行路由导航
- 基于文件系统的路由(
部署脚本(
build/deploy.mts):- 使用
deployNuxtApp函数(位于build/deploy-nuxt.mts) - 将
.html文件部署到服务器 - 将其他静态资源部署到 CDN
- 支持环境变量配置(
FOLDER_NAME、domain等)
- 使用
创建新的 Nuxt 项目:
- 复制
apps-home/_base目录到apps-home/your-project-name - 修改
nuxt.config.ts中的cdnURL配置 - 修改
build/deploy.mts中的domain配置 - 在
app/pages/目录下创建页面文件 - 根据需要修改
tsconfig.json中的路径别名
注意事项:
- Nuxt 4 项目编译后的静态文件会输出到
node_modules/.nuxt-{FOLDER_NAME}/.output/public目录 - 部署时,
.html文件会上传到服务器,其他静态资源会上传到 CDN - 开发时需要通过环境变量
FOLDER_NAME和PORT来指定项目名称和端口号 - 项目依赖统一在根目录的
package.json中管理,不需要在每个项目中单独安装
assets目录
该目录用于存放供服务端使用的模板文件(但不包括用于SSR输出HTML文件内容的视图模板文件)。
比如:
- 发送邮件时使用的邮件html模板(文件名示例:
email-template.ejs)。 - AI项目使用的系统提示词模板(文件名示例:
ai-prompt-template.md)。 - Excel模板文件。
注意
- 这些模板文件不会直接被前端项目使用,而是会被服务端代码读取(通常,程序只读取这些文件的内容,不应修改其内容)。
- 用于支持接口SSR渲染HTML的视图模板文件不存放于此,而是单独存放于
views目录下。
consumers/ 目录
消息队列消费者,用于处理异步任务。
build目录
存放编译、构建相关的脚本文件。
database目录
用于存放将外部数据导入本项目数据库用的脚本文件。比如:
- 从其他项目迁移数据到本项目数据库。
- 爬虫爬取数据并写入到本项目数据库。
hosts目录
nginx相关的配置文件存在于此。用于将nginx相关配置和SSL证书等于服务器上的版本进行同步。
建议
通常一个子目录对应一台服务器。
localhost目录
本地开发时启动的服务代码存放于此。例如:
- 反cloudflare的代理服务(用于爬取数据时绕过cloudflare的反爬机制)。
logs目录
用于存放服务端程序运行产生的日志文件。
提示
若该目录不存在,程序运行时会自动创建该目录。不需要将该目录提交到代码仓库。
migrations目录
用于存放knex的数据库迁移文件。
命名规范:
- 格式:
YYYYMMDDHHMMSS_description.mts - 示例:
20251105141026_create_balance_tables.mts
node_modules目录
通过 npm install 安装的依赖包会存放在此目录下。
提示
安装依赖时会自动创建该目录。不需要将该目录提交到代码仓库。
patches目录
用于存放供 patch-package 工具打补丁的文件。
patch-package是干嘛的?
有时候我们安装的第三方依赖包的代码有问题,或者是我们需要对依赖包的代码进行一些修改,这时候就可以使用 patch-package 工具来打补丁。
public目录
用于存放静态资源文件。部分编译产物也会生成到这个目录下。
主要子目录:
public/css/:存放页面级样式文件.scss源文件(如article.scss、about.scss)- 编译后的
.min.css文件(如article.min.css、about.min.css)
public/js/:存放页面级JS文件.js源文件(如article.mjs、about.mjs)- 编译后的
.min.js文件(如article.min.js、about.min.js)
public/static/:存放其他静态资源(图片、字体、第三方库等)
提示
对于生成到该目录下的编译产物(如 .min.css、.min.js),请不要将其提交到代码仓库。
schedules目录
用于存放定时任务脚本。比如:
- 定期优化数据库表格。
- 数据统计。
- 定期备份数据库数据。
src目录
服务端源代码。通常来说,这个目录下有你最重要的业务代码。
- bin目录:服务启动脚本(入口)。
- controllers目录:用于存放控制器代码。负责处理HTTP请求和响应。
- models目录:用于存放模型代码。负责定义数据库表结构。
- routes目录:用于存放路由代码。负责定义 URL 路由,参数校验,中间件处理。
- services目录:用于存放服务代码。负责实现业务逻辑和数据库操作。
- scripts目录:用于存放工具代码。封装通用功能。
- middlewares: 用于存放中间件代码。
src/controllers/
控制器层,处理 HTTP 请求和响应,负责参数验证、调用服务层、返回响应。
命名规范:
- 文件名对应业务模块,如
balance.mts、user.mts - 导出函数以小写字母
c(表示Controller)开头,如cGetBalance、cCreateUser - 参数验证 Schema 以
s(表示Schema)开头,如sGetBalance、sCreateUser
示例结构:
typescript
// src/controllers/balance.mts
export const sGetBalance = zod.object({});
export const cGetBalance: ExpressRequestHandler = async (req, res, next) => {
// 处理逻辑
};1
2
3
4
5
2
3
4
5
src/models/
数据模型层,使用 Knex.js 定义数据库表结构。
主要职责:
- 定义数据库表结构
命名规范:
- 文件名采用 PascalCase,如
BalanceAccount.mts - 导出函数以
createTable开头,如createTableBalanceAccountIfNotExist
src/routes/
路由定义层,将 HTTP 请求映射到对应的控制器。同时会进行参数校验和其他中间件处理
命名规范:
- 文件名对应业务模块
- 路由路径通常与业务模块对应,如
/balance/balance、/balance/transactions
src/services/
业务逻辑层,包含核心业务逻辑实现。
主要职责:
- 实现具体的业务逻辑
- 调用模型层进行数据库操作
- 调用第三方服务(支付、短信、邮件等)
- 处理复杂的数据转换和计算
命名规范:
- 函数名采用驼峰命名,如
getUserBalance、createRechargeOrder
src/scripts/
工具脚本目录,提供各种通用工具函数。
主要工具:
ConstantUtils.mts:常量定义(环境变量、配置等)DatabaseUtils.mts:数据库工具LogUtils.mts:日志工具EmailUtils.mts:邮件工具SmsUtils.mts:短信工具EncryptUtils.mts:加密工具- 等等...
src/middlewares/
中间件目录,包含各种 Express 中间件。
主要中间件:
AuthMiddleware.mts:身份验证中间件UserMiddleware.mts:用户信息中间件BodyValidateMiddleware.mts:请求体验证中间件RateLimitMiddleware.mts:限流中间件SessionMiddleware.mts:会话中间件ErrorMiddleware.mts:错误处理中间件- 等等...
typings目录
用于存放TypeScript类型定义文件。前后端项目均可复用这里的类型声明,减少重复定义,避免前后端类型不一致。
views目录
服务端SSR渲染HTML文本时使用的视图模板文件存放于此。
目录结构:
views/:页面级 EJS 模板文件(如index.ejs、article.ejs、about.ejs等)views/_parts/:可复用的公共组件(如head.ejs、styles.ejs、navbar.ejs、footer.ejs、scripts.ejs等)
页面级样式和JS代码分离规范:
- 页面特有的、无法复用的样式必须提取到
public/css/目录下,创建与页面同名的.scss文件(如article.scss、about.scss) - 页面特有的、无法复用的JS代码必须提取到
public/js/目录下,创建与页面同名的.js文件(如article.mjs、about.mjs) - 在 EJS 文件中,通过
head.ejs组件的additionalStyles参数引用外部样式文件,通过scripts.ejs组件的additionalScripts参数引用外部JS文件 - 禁止在页面级 EJS 文件中使用
<style>标签定义样式(公共组件的样式除外) - 禁止在页面级 EJS 文件中使用
<script>标签定义JS代码(公共组件的脚本除外) - 内联样式(
style属性)应尽量转换为CSS类,除非是动态生成的样式 - 内联事件处理器(如
onclick、onerror)应提取到对应的JS文件中,使用事件监听器实现
静态资源缓存控制:
- 为了在发版时强制刷新浏览器缓存,同时保持发版之间的缓存命中率,项目实现了基于构建时间戳的缓存控制机制
- 在应用启动时(
src/app.mts),会生成一个BUILD_TIMESTAMP并设置到app.locals,所有 EJS 模板都可以访问该变量 - 非第三方静态资源(非
static/开头的文件)会自动添加时间戳查询参数(如?v=1234567890) - 第三方库文件(
static/开头的文件)不添加时间戳,正常使用浏览器缓存 - 工作原理:
- 每次应用启动(发版时)会生成新的时间戳,强制刷新缓存
- 在两次发版之间,时间戳保持不变,浏览器可以正常缓存静态资源
- 在模板中使用:
<%= typeof BUILD_TIMESTAMP !== 'undefined' ? BUILD_TIMESTAMP : Date.now() %>
相关目录:
public/css/:存放页面级样式文件(.scss源文件和编译后的.min.css文件)public/js/:存放页面级JS文件(.js源文件和编译后的.min.js文件)
根目录
package.json
package.json 比较重要,所以单独拎出来讲。
json
{
// 项目名称
"name": "aimian",
// 软件版本号
"version": "0.0.1",
// 是否为私有仓库
"private": true,
// 项目采用ES Module模块化方案
"type": "module",
// 入口文件
"main": "src/bin/www.mts",
// 模块导入路径别名
"imports": {},
// 脚本命令
"scripts": {},
// 生产依赖
"dependencies": {},
// 开发依赖
"devDependencies": {}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
其他文件
- .editorconfig:通用编辑器配置文件。
- .env:环境变量配置文件。
- .env.int:集成环境变量配置文件。
- .env.pre:预发布环境变量配置文件。
- .env.production:生产环境变量配置文件。
- .gitignore:Git忽略文件。
- .npmrc:npm配置文件。
- .nvmrc:Node版本管理配置文件。
- .prettierignore:Prettier忽略文件。
- .prettierrc:Prettier配置文件。
- .eslint.config.lib.mts:ESLint库配置文件。
- .eslint.config.server.mts:ESLint服务端配置文件。
- .eslint.config.vue.mts:ESLintVue配置文件。
- knexfile.mts:Knex配置文件。
- package-lock.json:npm依赖锁文件。
- README.md:项目README文件。
- tsconfig.json:TypeScript配置文件。
架构模式
1. MVC 架构
项目采用经典的 MVC(Model-View-Controller)架构:
- Model:
src/models/- 数据模型和数据库操作 - View:
views/(服务端模板)+apps/(前端应用) - Controller:
src/controllers/- 处理 HTTP 请求和响应
2. 分层架构
服务端采用分层架构:
Controller 层(控制器)
↓
Service 层(业务逻辑和数据访问)
↓
Model 层(数据模型)
↓
Database(数据库)1
2
3
4
5
6
7
2
3
4
5
6
7
3. 模块导入路径别名
项目使用路径别名简化导入:
typescript
// 在 package.json 中定义
"imports": {
"#controllers/*": "./src/controllers/*.mts",
"#services/*": "./src/services/*.mts",
"#models/*": "./src/models/*.mts",
// ... 等等
}
// 使用示例
import { getUserBalance } from "#services/balance";
import { cGetBalance } from "#controllers/balance";1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
4. 类型定义共享
前后端共享 typings/ 目录下的类型定义,确保类型一致性。
5. 环境配置
所有配置通过环境变量传入,在 src/scripts/ConstantUtils.mts 中统一管理。
主要配置:
- 数据库配置
- 支付配置
- 邮件配置
- 短信配置
- 前端应用端口配置
- 等等...
6. 错误处理
- 使用统一的错误处理中间件
ErrorMiddleware - 使用
TReturn<T>类型表示可能返回错误的函数 - 格式:
[Error | null, T | undefined]
7. 响应格式
使用统一的响应格式:
typescript
// 成功响应
res.success(data);
// 分页响应
res.successPage({ list, total, pageNo, pageSize });
// 失败响应
res.fail(message, statusCode);1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
相关文档
快速参考
- 项目开发规范 - 核心原则、技术栈概览、快速开发指南