You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RuoYi-Vue/docs/architecture/backend-architecture.md

471 lines
22 KiB

# RuoYi-Vue 后端架构分析
> 版本3.8.0 | 分析日期2026-06-28 | 框架Spring Boot 2.5.8
---
## 1. 分层架构分析
### 1.1 整体分层概览
项目采用经典的 **四层架构**Controller → Service → Mapper → Domain各层职责清晰通过 Maven 多模块进行物理隔离。
```
┌─────────────────────────────────────────────────────────────┐
│ ruoyi-admin (启动层) │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ Controller │ │ Config │ │ Application │ │
│ │ (18个) │ │ (Swagger) │ │ RuoYiApplication │ │
│ └──────┬──────┘ └─────────────┘ └──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ruoyi-framework (框架层) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ │
│ │ │ Aspect │ │ Security │ │ Config │ │ Manager │ │ │
│ │ │ (4个切面)│ │ (JWT) │ │ (12个) │ │ (异步) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └─────────┘ │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ruoyi- │ │book- │ │ stock- │ │
│ │system │ │system │ │ system │ │
│ │(15 Mapper│ │(6 Mapper │ │ (9 Mapper │ │
│ │ 12 Service│ │ 6 Service│ │ ... │ │
│ │ 12 Domain)│ │ 6 Domain)│ │ │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ruoyi-common (通用工具层) │ │
│ │ BaseController / AjaxResult / BaseEntity / 注解 / │ │
│ │ 工具类(20+) / 异常类 / 过滤器 / 常量 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 Controller 层分析
**文件位置**`ruoyi-admin/src/main/java/com/ruoyi/web/controller/`
#### Controller 统计
| 分类 | 数量 | 文件列表 |
|------|------|----------|
| 系统管理 | 13 | SysConfigController, SysDeptController, SysDictDataController, SysDictTypeController, SysIndexController, SysLoginController, SysMenuController, SysNoticeController, SysPostController, SysProfileController, SysRegisterController, SysRoleController, SysUserController |
| 监控管理 | 5 | CacheController, ServerController, SysLogininforController, SysOperlogController, SysUserOnlineController |
| 业务模块 | 6 | AccountBookController, AccountController, OperationsController, StatisticsController, StatisticsRemainController, StatisticsTotalController |
| 工具/公共 | 4 | CaptchaController, CommonController, SwaggerController, TestController |
| **合计** | **28** | — |
#### 设计模式
1. **统一继承 BaseController**:所有 Controller 继承 `BaseController`,获得分页、响应、用户上下文等通用能力。
2. **标准 CRUD 接口模式**
```
GET /list → 分页列表查询
GET /{id} → 详情查询
POST / → 新增
PUT / → 修改
DELETE /{ids} → 批量删除
POST /export → Excel 导出
POST /importData → Excel 导入
```
3. **权限注解驱动**:每个接口通过 `@PreAuthorize("@ss.hasPermi('module:entity:action')")` 进行权限控制。
4. **操作日志注解**:通过 `@Log(title = "模块名", businessType = BusinessType.XXX)` 声明式记录操作日志。
### 1.3 Service 层分析
**文件位置**
- 系统模块:`ruoyi-system/src/main/java/com/ruoyi/system/service/`
- 账务模块:`book-system/src/main/java/com/ruoyi/booksystem/service/`
#### 接口与实现分离
采用标准的 **接口 + 实现类** 模式:
```
ISysUserService (接口)
└── SysUserServiceImpl (实现)
```
| 模块 | 接口数 | 实现类数 |
|------|--------|----------|
| ruoyi-system | 12 | 12 |
| book-system | 6 | 6 |
#### 事务管理
使用 `@Transactional` 注解管理事务,主要在以下场景使用:
| 场景 | 方法示例 |
|------|----------|
| 新增用户(含角色/岗位关联) | `insertUser()` |
| 修改用户(含角色/岗位关联) | `updateUser()` |
| 删除用户(含关联清理) | `deleteUserById()`, `deleteUserByIds()` |
| 用户授权角色 | `insertUserAuth()` |
**事务策略特点**
- 仅在涉及多表操作的复合方法上加 `@Transactional`
- 简单 CRUD`updateUserStatus`)不使用事务
- 使用默认的 `REQUIRED` 传播级别和 `RuntimeException` 回滚策略
#### 数据权限注解
Service 层通过 `@DataScope(deptAlias = "d", userAlias = "u")` 注解实现数据权限过滤,在方法执行前由 AOP 切面动态注入 SQL 过滤条件。
### 1.4 Mapper 层分析
**文件位置**
- XML 映射:`{module}/src/main/resources/mapper/{module}/`
- Java 接口:`{module}/src/main/java/com/ruoyi/{module}/mapper/`
#### MyBatis 映射文件统计
| 模块 | XML 文件数 | 主要功能 |
|------|-----------|----------|
| ruoyi-system | 11 | 用户、角色、菜单、部门、字典、岗位、日志、配置、公告 |
| book-system | 6 | 账户、账本、操作、统计 |
| stock-system | 9 | 股票、行情、财务、趋势 |
| ruoyi-quartz | 2 | 定时任务 |
| ruoyi-generator | 2 | 代码生成 |
| **合计** | **30** | — |
#### 动态 SQL 使用
`SysUserMapper.xml` 为例,大量使用 MyBatis 动态 SQL
| 标签 | 用途 | 示例 |
|------|------|------|
| `<if>` | 条件查询 | `<if test="userName != null and userName != ''">` |
| `<foreach>` | 批量操作 | `<foreach collection="array" item="userId">` |
| `<include>` | SQL 片段复用 | `<include refid="selectUserVo"/>` |
| `<sql>` | 定义可复用片段 | `<sql id="selectUserVo">` |
| `<set>` | 动态更新 | `<set><if test="...">field = #{...},</if></set>` |
| `${params.dataScope}` | 数据权限注入 | 由 DataScopeAspect 动态拼接 |
#### ResultMap 关联映射
使用 `<association>``<collection>` 实现一对多关联查询:
```xml
<association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
```
### 1.5 Domain 层分析
#### 基类体系
```
BaseEntity (基础实体)
├── searchValue, createBy, createTime, updateBy, updateTime, remark, params
├── TreeEntity (树形实体)
│ └── parentId, ancestors
└── AjaxResult (统一响应)
└── code, msg, data (继承 HashMap)
```
#### 核心实体类
| 实体 | 位置 | 用途 |
|------|------|------|
| SysUser | ruoyi-common | 用户信息 |
| SysRole | ruoyi-common | 角色信息 |
| SysMenu | ruoyi-common | 菜单/权限 |
| SysDept | ruoyi-common | 部门信息 |
| LoginUser | ruoyi-common | 登录用户上下文 |
| SysConfig/SysNotice/SysOperLog | ruoyi-system | 系统业务实体 |
| Account/AccountBook/Statistics | book-system | 账务业务实体 |
---
## 2. 安全配置
### 2.1 Spring Security 配置
**配置文件**`ruoyi-framework/.../config/SecurityConfig.java`
#### 认证流程
```
HTTP Request
┌──────────────────────┐
│ CorsFilter │ ← 跨域处理
└──────────┬───────────┘
┌──────────────────────┐
│ JwtAuthentication │ ← JWT Token 验证
│ TokenFilter │
└──────────┬───────────┘
┌──────────────────────┐
│ UsernamePassword │ ← Spring Security 内置
│ AuthenticationFilter │
└──────────┬───────────┘
┌──────────────────────┐
│ Controller Handler │
└──────────────────────┘
```
#### 安全配置要点
| 配置项 | 值/策略 |
|--------|---------|
| CSRF | 禁用(无状态 Token 认证) |
| Session | STATELESS无会话 |
| 密码加密 | BCryptPasswordEncoder |
| 匿名路径 | `/login`, `/register`, `/captchaImage` |
| 公共资源 | `/**/*.html`, `/**/*.css`, `/**/*.js`, `/profile/**` |
| Swagger | `/swagger-ui.html`, `/swagger-resources/**` |
| 监控 | `/druid/**` |
| 其他所有请求 | 需要认证 |
#### 方法级安全
```java
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
```
- 启用 `@PreAuthorize``@PostAuthorize` 方法级权限控制
- 启用 `@Secured` 注解支持
### 2.2 JWT Token 机制
**核心类**`ruoyi-framework/.../web/service/TokenService.java`
#### Token 结构
```
JWT Header + Payload Redis 缓存
┌─────────────────────┐ ┌──────────────────────┐
│ { │ │ login_tokens:{uuid} │
│ LOGIN_USER_KEY │──uuid──→ │ { │
│ : uuid │ │ LoginUser Object │
│ } │ │ TTL: 30min │
└─────────────────────┘ └──────────────────────┘
```
#### 关键参数
| 参数 | 配置项 | 默认值 |
|------|--------|--------|
| Token Header | `token.header` | `Authorization` |
| Token Secret | `token.secret` | 自定义密钥 |
| Token 有效期 | `token.expireTime` | 30 分钟 |
| 自动刷新阈值 | 硬编码 | 剩余不足 20 分钟时刷新 |
#### Token 工作流程
1. **登录** → 生成 UUID → 存入 Redis → JWT 携带 UUID → 返回 Token
2. **请求** → 提取 Token → 解析 UUID → Redis 获取用户 → 验证有效期
3. **刷新** → 距过期不足 20 分钟 → 自动续期 Redis TTL
4. **登出** → 删除 Redis 中的用户缓存
### 2.3 权限控制体系
#### 权限模型
```
用户 (SysUser)
└── 角色 (SysRole) [多对多]
└── 菜单/权限 (SysMenu) [多对多]
└── 权限标识: "system:user:list"
```
#### 三种权限控制方式
| 方式 | 注解/实现 | 层级 | 用途 |
|------|-----------|------|------|
| URL 级 | Spring Security `antMatchers` | 网关 | 公开/匿名资源 |
| 方法级 | `@PreAuthorize("@ss.hasPermi('...')")` | Controller | 功能权限 |
| 数据级 | `@DataScope(deptAlias, userAlias)` | Service | 数据范围过滤 |
#### PermissionService 权限判断
```java
@Service("ss")
public class PermissionService {
hasPermi("system:user:list") // 是否拥有某权限
lacksPermi("system:user:list") // 是否不拥有某权限
hasAnyPermi("a,b,c") // 是否拥有任一权限
hasRole("admin") // 是否拥有某角色
lacksRole("admin") // 是否不拥有某角色
hasAnyRoles("admin,common") // 是否拥有任一角色
}
```
权限标识约定:`{模块}:{实体}:{操作}`,如 `system:user:list`、`booksystem:account:add`。
---
## 3. AOP 切面分析
**文件位置**`ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/`
### 3.1 切面总览
```
┌─────────────────────────────────────────────────────┐
│ AOP 切面 (4个) │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ LogAspect │ │ DataScope │ │
│ │ @AfterRet │ │ @Before │ │
│ │ @AfterThrow │ │ 数据权限过滤 │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ DataSource │ │ RateLimiter │ │
│ │ @Around │ │ @Before │ │
│ │ 多数据源切换 │ │ Redis 限流 │ │
│ │ Order(1) │ │ │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
```
### 3.2 日志切面 (LogAspect)
| 属性 | 值 |
|------|-----|
| 触发方式 | `@annotation(Log)` |
| 通知类型 | `@AfterReturning`(成功)、`@AfterThrowing`(异常) |
| 记录内容 | 操作用户、IP、URL、方法名、请求参数、响应结果、业务类型、状态 |
| 写入策略 | **异步写入**AsyncManager + AsyncFactory不阻塞主线程 |
| 参数过滤 | 自动过滤 MultipartFile、HttpServletRequest 等不可序列化对象 |
### 3.3 数据权限切面 (DataScopeAspect)
| 属性 | 值 |
|------|-----|
| 触发方式 | `@annotation(DataScope)` |
| 通知类型 | `@Before` |
| 权限级别 | 5 种:全部(1)、自定义(2)、本部门(3)、本部门及以下(4)、仅本人(5) |
| 实现原理 | 在查询参数 `params.dataScope` 中注入 SQL 片段XML 中通过 `${params.dataScope}` 拼接 |
| 安全机制 | 执行前先清空 `dataScope` 参数防止 SQL 注入 |
**SQL 拼接示例**
```sql
-- 本部门权限
AND (d.dept_id = 100)
-- 本部门及以下
AND (d.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = 100
OR find_in_set(100, ancestors)))
```
### 3.4 数据源切面 (DataSourceAspect)
| 属性 | 值 |
|------|-----|
| 触发方式 | `@annotation(DataSource)``@within(DataSource)` |
| 通知类型 | `@Around` |
| 优先级 | `@Order(1)`(最高优先级) |
| 实现原理 | 通过 `ThreadLocal` 设置当前线程的数据源标识,执行完毕后清理 |
| 底层支持 | `DynamicDataSource`(继承 `AbstractRoutingDataSource` |
### 3.5 限流切面 (RateLimiterAspect)
| 属性 | 值 |
|------|-----|
| 触发方式 | `@annotation(RateLimiter)` |
| 通知类型 | `@Before` |
| 存储介质 | Redis + Lua 脚本(原子操作) |
| 限流维度 | 默认方法级、IPIP + 方法级) |
| 可配置参数 | `key`(键前缀)、`time`(时间窗口/秒)、`count`(最大请求数) |
| 默认值 | 60 秒内最多 100 次请求 |
| 失败响应 | 抛出 `ServiceException("访问过于频繁,请稍候再试")` |
---
## 4. 模块依赖
### 4.1 模块依赖图
```
┌─────────────┐
│ ruoyi-admin │ ← 启动入口,打包部署
└──────┬──────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌──────────────┐
│ruoyi- │ │ruoyi- │ │ ruoyi- │
│system │ │quartz │ │ generator │
└─────┬─────┘ └─────┬─────┘ └──────┬───────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────┐
│ ruoyi-framework │
│ (Security, AOP, DataSource, Config) │
└────────────────────┬─────────────────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌──────────────┐
│book- │ │stock- │ │ ruoyi- │
│system │ │system │ │ common │
└─────┬─────┘ └─────┬─────┘ └──────┬───────┘
│ │ │
└──────────────┼───────────────┘
(所有业务模块依赖)
```
### 4.2 模块职责
| 模块 | 职责 | 核心内容 |
|------|------|----------|
| **ruoyi-admin** | 启动入口 & Controller 层 | SpringBoot 启动类、Controller、Swagger 配置 |
| **ruoyi-framework** | 框架核心 | Security 配置、JWT、AOP 切面、数据源、Redis、MyBatis 配置 |
| **ruoyi-system** | 系统管理业务 | 用户、角色、菜单、部门、字典、岗位、日志的 Service/Mapper/Domain |
| **ruoyi-common** | 通用工具 | 基类、工具类、注解、常量、异常、过滤器、分页组件 |
| **book-system** | 账务业务模块 | 账本、账户、操作记录、统计的 CRUD |
| **stock-system** | 股票业务模块 | 股票信息、行情、财务、趋势分析 |
| **ruoyi-quartz** | 定时任务 | 基于 Quartz 的任务调度 |
| **ruoyi-generator** | 代码生成 | 基于 Velocity 模板的代码生成器 |
### 4.3 技术栈汇总
| 分类 | 技术 | 版本 |
|------|------|------|
| 核心框架 | Spring Boot | 2.5.8 |
| JDK | Java | 1.8 |
| 安全框架 | Spring Security + JWT | 内置 + jjwt 0.9.1 |
| ORM | MyBatis | 2.2.0 (spring-boot-starter) |
| 分页 | PageHelper | 1.4.0 |
| 数据库连接池 | Druid | 1.2.8 |
| 缓存 | Redis (Spring Data Redis) | 内置 |
| API 文档 | Springfox Swagger 3 | 3.0.0 |
| JSON | FastJSON | 1.2.79 |
| Excel | Apache POI | 4.1.2 |
| 验证码 | Kaptcha | 2.3.2 |
| 代码生成 | Velocity | 2.3 |
| 系统监控 | Oshi | 5.8.6 |
---
## 5. 存在的问题
| # | 问题 | 严重等级 | 影响范围 | 优化建议 |
|---|------|----------|----------|----------|
| 1 | **fastjson 版本存在已知漏洞** | 🔴 高 | 全系统 | fastjson 1.2.79 存在多个 CVE 漏洞(如 CVE-2022-25845建议升级至 2.xfastjson2或替换为 Jackson/Gson |
| 2 | **使用已废弃的 WebSecurityConfigurerAdapter** | 🟡 中 | 安全配置 | Spring Security 5.7+ 已废弃 `WebSecurityConfigurerAdapter`,应改用 `SecurityFilterChain` Bean 配置方式 |
| 3 | **Token 解析异常被静默吞没** | 🟡 中 | 认证模块 | `TokenService.getLoginUser()` 中 catch 块为空(第 72-74 行),异常被完全忽略,建议至少记录 WARN 日志 |
| 4 | **SQL 注入风险:${params.dataScope}** | 🟡 中 | 数据权限层 | MyBatis XML 中 `${params.dataScope}` 使用 `${}` 直接拼接,虽有 DataScopeAspect 前置清空防护,但仍存在绕过风险;建议使用参数化方案或严格校验 |
| 5 | **业务 Controller 缺少 @Log 注解** | 🟢 低 | book-system | `book-system` 下的 Controller 中 `AccountController` 等部分接口缺少 `@Log` 操作日志注解,审计不完整 |
| 6 | **缺少统一 DTO/VO 分层** | 🟢 低 | 全系统 | Domain 实体直接暴露给 Controller 层(如 `SysUser` 同时用于数据库映射和接口响应),密码等敏感字段需额外处理;建议引入独立的 DTO/VO 对象 |
| 7 | **Service 层直接注入 Mapper而非通过接口** | 🟢 低 | 全系统 | `SysUserServiceImpl` 中使用 `@Autowired private SysUserMapper userMapper` 直接注入实现类,虽然 MyBatis 通过代理实现,但不符合面向接口编程原则 |
| 8 | **缺少接口版本控制** | 🟢 低 | API 设计 | 所有 API 路径无版本号(如 `/api/v1/system/user`),未来接口升级时无法平滑过渡 |
---
*文档结束*