22 KiB
22 KiB
后端架构
**本文引用的文件** - [backend/src/app.js](file://backend/src/app.js) - [backend/package.json](file://backend/package.json) - [backend/src/config/env.js](file://backend/src/config/env.js) - [backend/src/config/database.js](file://backend/src/config/database.js) - [backend/src/config/redis.js](file://backend/src/config/redis.js) - [backend/src/config/logger.js](file://backend/src/config/logger.js) - [backend/src/routes/index.js](file://backend/src/routes/index.js) - [backend/src/middleware/auth.js](file://backend/src/middleware/auth.js) - [backend/src/middleware/bodyLimit.js](file://backend/src/middleware/bodyLimit.js) - [backend/src/utils/jwt.js](file://backend/src/utils/jwt.js) - [backend/src/utils/response.js](file://backend/src/utils/response.js) - [backend/src/routes/auth.js](file://backend/src/routes/auth.js) - [backend/src/services/userBootstrap.js](file://backend/src/services/userBootstrap.js) - [backend/src/models/DashboardUser.js](file://backend/src/models/DashboardUser.js) - [backend/src/models/index.js](file://backend/src/models/index.js) - [backend/src/services/curveClient.js](file://backend/src/services/curveClient.js) - [backend/src/services/eqCacheStorage.js](file://backend/src/services/eqCacheStorage.js) - [backend/src/services/measurementStorage.js](file://backend/src/services/measurementStorage.js)目录
引言
本文件面向后端架构与实现,围绕 Express 应用展开,系统性阐述 MVC 模式落地、中间件体系、路由组织、数据库连接、Redis 缓存、日志系统、认证授权、错误处理、请求限制、API 版本管理、安全防护与性能优化、启动流程、健康检查与监控配置等主题。文档同时提供可视化图示帮助读者快速把握代码结构与交互流程。
项目结构
后端采用模块化分层组织:
- 配置层:环境变量、数据库、Redis、日志
- 中间件层:认证、请求体大小限制
- 路由层:按功能域划分的子路由集合
- 服务层:业务逻辑封装(曲线拉取、EQ 缓存、频响存储等)
- 工具层:JWT、密码、统一响应包装
- 模型层:基于 Sequelize 的数据模型
- 入口:应用启动与监听
graph TB
subgraph "入口"
APP["app.js<br/>启动与中间件注册"]
end
subgraph "配置"
ENV["env.js<br/>环境标识"]
DB["database.js<br/>Sequelize 实例"]
REDIS["redis.js<br/>Redis 客户端"]
LOG["logger.js<br/>Winston 日志"]
end
subgraph "中间件"
AUTHMW["auth.js<br/>认证/鉴权"]
BODYLIMIT["bodyLimit.js<br/>请求体大小限制"]
end
subgraph "路由"
ROUTESIDX["routes/index.js<br/>路由聚合"]
ROUTE_AUTH["routes/auth.js<br/>认证相关"]
end
subgraph "服务"
BOOTSTRAP["userBootstrap.js<br/>超级管理员初始化"]
CURVE["curveClient.js<br/>曲线拉取与校验"]
EQ["eqCacheStorage.js<br/>EQ 缓存读取"]
MEAS["measurementStorage.js<br/>频响文件 S3 存取"]
end
subgraph "模型"
MODELSIDX["models/index.js<br/>模型导出"]
MODEL_USER["DashboardUser.js<br/>用户模型"]
end
subgraph "工具"
JWT["jwt.js<br/>JWT 生成/解析"]
RESP["response.js<br/>统一响应包装"]
end
APP --> ENV
APP --> DB
APP --> REDIS
APP --> LOG
APP --> ROUTESIDX
APP --> AUTHMW
APP --> BODYLIMIT
ROUTESIDX --> ROUTE_AUTH
ROUTE_AUTH --> MODEL_USER
BOOTSTRAP --> MODEL_USER
EQ --> REDIS
MEAS --> DB
CURVE --> LOG
EQ --> LOG
MEAS --> LOG
JWT --> LOG
图表来源
- backend/src/app.js:1-60
- backend/src/config/env.js:1-13
- backend/src/config/database.js:1-24
- backend/src/config/redis.js:1-32
- backend/src/config/logger.js:1-29
- backend/src/routes/index.js:1-13
- backend/src/middleware/auth.js:1-36
- backend/src/middleware/bodyLimit.js:1-12
- backend/src/utils/jwt.js:1-28
- backend/src/utils/response.js:1-25
- backend/src/routes/auth.js:1-112
- backend/src/services/userBootstrap.js:1-28
- backend/src/models/DashboardUser.js:1-58
- backend/src/models/index.js:1-8
- backend/src/services/curveClient.js:1-147
- backend/src/services/eqCacheStorage.js:1-73
- backend/src/services/measurementStorage.js:1-115
章节来源
核心组件
- 应用入口与中间件
- 注册 CORS、JSON 解析、URL 编码解析、请求体大小限制中间件
- 提供根路径与健康检查接口
- 聚合并挂载各业务路由
- 数据库连接
- 基于 Sequelize 连接 MySQL,开发环境开启 SQL 日志输出
- 应用启动时尝试同步数据库表结构
- Redis 缓存
- 单例客户端,支持密码、超时、重试次数配置
- EQ 缓存采用 Redis Hash 结构,提供字段级读取与键列表查询
- 日志系统
- Winston 控制台与文件双通道输出,统一时间戳与格式化
- 认证与授权
- Bearer Token 解析与校验,支持超级管理员强制校验
- 请求限制
- 基于 Content-Length 的请求体大小限制(默认 8MB)
章节来源
- backend/src/app.js:14-59
- backend/src/config/database.js:1-24
- backend/src/config/redis.js:1-32
- backend/src/config/logger.js:1-29
- backend/src/middleware/auth.js:1-36
- backend/src/middleware/bodyLimit.js:1-12
架构总览
下图展示从请求进入至业务处理的关键流程,包括认证、路由分发、模型访问与外部服务调用。
sequenceDiagram
participant C as "客户端"
participant A as "Express 应用(app.js)"
participant M1 as "认证中间件(auth.js)"
participant R as "路由(auth.js)"
participant U as "工具(jwt.js/response.js)"
participant DB as "数据库(Sequelize)"
participant L as "日志(Winston)"
C->>A : "HTTP 请求"
A->>M1 : "执行认证中间件"
M1-->>A : "通过/拒绝"
A->>R : "路由分发"
R->>U : "生成响应/解析令牌"
R->>DB : "查询/更新用户"
DB-->>R : "结果"
R-->>C : "统一响应"
R->>L : "记录日志"
图表来源
- backend/src/app.js:16-37
- backend/src/middleware/auth.js:1-36
- backend/src/routes/auth.js:1-112
- backend/src/utils/jwt.js:1-28
- backend/src/utils/response.js:1-25
- backend/src/config/logger.js:1-29
详细组件分析
MVC 模式实现
- 视图层
- 本项目为 API 服务,无传统视图层;统一以 JSON 响应输出
- 模型层
- 使用 Sequelize 定义数据模型,集中于 models 目录,导出统一索引
- 示例:DashboardUser 模型定义了用户字段、注释与表名
- 控制器层
- Express 路由作为控制器,负责接收请求、调用服务/模型、返回响应
- 示例:认证路由处理登录、获取当前用户、修改密码
classDiagram
class DashboardUser {
+id : number
+username : string
+password_hash : string
+is_super_admin : number
+status : number
+last_login_at : date
+create_at : date
+update_at : date
}
class AuthRoute {
+login()
+getCurrentUser()
+changePassword()
}
AuthRoute --> DashboardUser : "读写"
图表来源
章节来源
- backend/src/models/index.js:1-8
- backend/src/models/DashboardUser.js:1-58
- backend/src/routes/auth.js:1-112
中间件体系
- 认证中间件
- 从 Authorization 头提取 Bearer Token,解码并注入用户上下文
- 支持超级管理员强制校验
- 请求体大小限制
- 基于 Content-Length 校验,超过阈值返回 413
flowchart TD
Start(["进入中间件"]) --> CheckAuth["检查 Authorization 头"]
CheckAuth --> HasToken{"存在 Bearer Token?"}
HasToken --> |否| Reject401["返回 401 未登录"]
HasToken --> |是| Verify["验证 Token"]
Verify --> Valid{"有效?"}
Valid --> |否| Reject401
Valid --> |是| InjectUser["注入用户上下文"]
InjectUser --> Next["放行"]
Reject401 --> End(["结束"])
Next --> End
图表来源
章节来源
路由组织结构
- 路由聚合
- routes/index.js 导出所有业务路由数组,入口统一挂载
- 认证路由
- 登录:校验凭据、更新最近登录时间、签发访问令牌
- 获取当前用户:基于已认证用户 ID 查询
- 修改密码:旧密码校验、新密码长度校验、更新哈希
sequenceDiagram
participant C as "客户端"
participant R as "认证路由(auth.js)"
participant DB as "数据库"
participant JWT as "JWT 工具"
participant RESP as "响应包装"
C->>R : "POST /api/auth/login"
R->>DB : "按用户名查询用户"
DB-->>R : "用户记录"
R->>R : "校验密码/状态"
R->>DB : "更新最近登录时间"
R->>JWT : "签发访问令牌"
R->>RESP : "构造成功响应"
R-->>C : "返回 access_token 与用户信息"
图表来源
章节来源
数据库连接管理
- 连接配置
- 通过环境变量配置主机、端口、用户名、密码、数据库名
- 开发环境启用 SQL 日志,生产关闭
- 启动同步
- 应用启动时尝试同步表结构,失败记录告警但不阻断启动
- 用户模型
- 字段覆盖登录账号、密码哈希、状态、超级管理员标记、时间戳
flowchart TD
Boot(["应用启动"]) --> Sync["sequelize.sync()"]
Sync --> Ok{"同步成功?"}
Ok --> |是| Ready["记录成功日志"]
Ok --> |否| Warn["记录错误日志并继续"]
Ready --> Listen["监听端口"]
Warn --> Listen
图表来源
- backend/src/app.js:42-56
- backend/src/config/database.js:1-24
- backend/src/models/DashboardUser.js:1-58
章节来源
- backend/src/config/database.js:1-24
- backend/src/app.js:42-56
- backend/src/models/DashboardUser.js:1-58
Redis 缓存策略
- 客户端
- 单例懒加载,支持密码、超时、重试次数
- EQ 缓存
- 使用 Redis Hash 存储型号的 EQ 字段
- 提供字段键列表查询与单字段读取
- 读取失败统一抛错并记录日志
sequenceDiagram
participant S as "服务(eqCacheStorage.js)"
participant RC as "Redis 客户端(redis.js)"
participant L as "日志(logger.js)"
S->>RC : "hkeys(key)/hget(key, field)"
alt "键存在"
RC-->>S : "字段列表/字段值"
S-->>S : "解析 JSON 或原始值"
else "键不存在/读取异常"
RC-->>S : "null 或抛错"
S->>L : "记录错误日志"
S-->>S : "抛出统一错误"
end
图表来源
- backend/src/services/eqCacheStorage.js:24-66
- backend/src/config/redis.js:11-29
- backend/src/config/logger.js:1-29
章节来源
日志系统配置
- 输出目标
- 控制台与文件双通道
- 格式化
- 时间戳、级别、消息、附加元数据
- 目录
- 自动创建 logs 目录
章节来源
认证授权机制
- 令牌签发
- 基于 HS256 算法,有效期 12 小时
- 令牌解析
- 校验失败按类型返回不同错误
- 授权
- 超级管理员强制校验,非管理员返回 403
flowchart TD
A["收到请求"] --> B["解析 Authorization 头"]
B --> C{"Bearer Token 存在?"}
C --> |否| E["返回 401 未登录"]
C --> |是| D["jwt.verify 校验"]
D --> F{"有效?"}
F --> |否| G["返回 401 无效凭证/过期"]
F --> |是| H["注入用户上下文"]
H --> I{"是否超级管理员?"}
I --> |否| J["返回 403"]
I --> |是| K["放行"]
图表来源
章节来源
错误处理与统一响应
- 统一响应包装
- 成功/错误/无数据三类包装对象
- 路由层错误捕获
- 认证路由对异常进行日志记录并返回统一错误响应
- 外部服务错误
- S3/曲线服务捕获异常并记录日志,抛出统一错误
章节来源
- backend/src/utils/response.js:1-25
- backend/src/routes/auth.js:60-64
- backend/src/services/measurementStorage.js:76-108
- backend/src/services/curveClient.js:94-138
请求限制策略
- 限制规则
- 基于 Content-Length,超过 8MB 返回 413
- 生效范围
- 全局中间件,适用于所有路由
章节来源
API 版本管理
- 当前实现
- 路由路径采用 /api/{resource} 形态,未显式引入版本前缀
- 建议
- 可在入口处增加 /api/v1 前缀,后续迁移至 v2 时保持向后兼容
章节来源
安全防护
- CORS
- 允许任意来源与凭据
- 认证
- Bearer Token,建议配合 HTTPS 与短令牌有效期
- 密码
- 使用哈希存储,路由层对新密码长度进行约束
- 请求限制
- 8MB 限制,防止恶意大包
章节来源
- backend/src/app.js:17-18
- backend/src/middleware/auth.js:1-36
- backend/src/utils/password.js
- backend/src/middleware/bodyLimit.js:1-12
性能优化方案
- 数据库
- 生产关闭 SQL 日志,减少 IO
- 合理索引与查询条件,避免 N+1
- 缓存
- Redis Hash 结构降低网络往返
- 字段键列表查询避免一次性传输大量数据
- 外部服务
- S3 与曲线接口设置合理超时与错误重试
- 日志
- 控制台与文件双通道,避免过多 info 级日志影响性能
章节来源
- backend/src/config/database.js:15-15
- backend/src/services/eqCacheStorage.js:24-42
- backend/src/services/measurementStorage.js:18-27
- backend/src/services/curveClient.js:94-100
- backend/src/config/logger.js:19-26
启动流程、健康检查与监控
- 启动流程
- 加载环境变量 → 初始化数据库 → 创建超级管理员 → 启动 HTTP 服务器
- 健康检查
- /health 返回健康状态
- 监控建议
- 结合日志与外部 APM 工具,关注慢查询、缓存命中率与外部接口延迟
sequenceDiagram
participant Boot as "启动脚本"
participant App as "app.js"
participant DB as "database.js"
participant Redis as "redis.js"
participant Logger as "logger.js"
Boot->>App : "node src/app.js"
App->>DB : "sequelize.sync()"
App->>App : "ensureBootstrapSuperAdmin()"
App->>Redis : "初始化客户端"
App->>Logger : "记录启动日志"
App->>App : "listen(PORT)"
图表来源
- backend/src/app.js:42-59
- backend/src/config/database.js:1-24
- backend/src/config/redis.js:11-29
- backend/src/config/logger.js:1-29
章节来源
依赖关系分析
- 包管理与运行
- 依赖 express、sequelize、mysql2、jsonwebtoken、winston、ioredis 等
- 启动脚本使用 node 或 nodemon dev
- 内部依赖
- app.js 依赖配置、路由、中间件与引导服务
- 路由依赖模型与工具
- 服务依赖配置与日志
graph LR
P["package.json"] --> E["express"]
P --> S["sequelize"]
P --> M["mysql2"]
P --> J["jsonwebtoken"]
P --> W["winston"]
P --> R["ioredis"]
APP["app.js"] --> CFG["config/*"]
APP --> RT["routes/*"]
APP --> MW["middleware/*"]
APP --> SVC["services/*"]
RT --> MOD["models/*"]
RT --> UT["utils/*"]
SVC --> CFG
SVC --> LOG["logger.js"]
图表来源
章节来源
性能考虑
- 数据库层
- 生产关闭 SQL 日志,避免频繁 I/O
- 合理使用索引与分页
- 缓存层
- Redis Hash 结构适合字段级访问
- 仅读取字段键列表,避免大对象传输
- 外部接口
- 设置超时与错误处理,避免阻塞请求
- 日志
- 控制台与文件双通道,避免 info 级日志过多
故障排查指南
- 认证失败
- 检查 Authorization 头格式与令牌有效性
- 查看日志定位具体错误原因
- 数据库同步失败
- 检查数据库连接参数与权限
- 关注启动阶段的日志告警
- Redis 读取异常
- 检查 Redis 连接参数与网络连通性
- 关注错误日志中的具体异常信息
- S3 读取/上传失败
- 检查凭证或 IAM 角色配置
- 关注 NoSuchKey 等特定异常
章节来源
- backend/src/middleware/auth.js:1-36
- backend/src/app.js:48-51
- backend/src/config/redis.js:24-26
- backend/src/services/measurementStorage.js:102-107
结论
本后端以 Express 为核心,采用清晰的分层与模块化设计,结合 Sequelize、Redis 与 Winston 实现了稳定的数据访问、缓存与日志能力。认证授权、请求限制与统一响应提升了安全性与一致性。建议后续引入 API 版本前缀、完善错误分类与指标上报,持续优化数据库与缓存策略以提升整体性能与可观测性。
附录
- 环境变量
- APP_ENV:development/production
- DATABASE_*:主机、端口、用户名、密码、数据库名
- REDIS_*:主机、端口、密码、DB
- JWT_SECRET:令牌签名密钥
- DASHBOARD_ADMIN_*:超级管理员初始化用户名与密码
- AWS_*:S3 区域、凭证与桶名
- 路由示例
- GET / → 根路径
- GET /health → 健康检查
- POST /api/auth/login → 登录
- GET /api/auth/me → 获取当前用户
- PUT /api/auth/password → 修改密码
章节来源