docs: complete RuoYi-Vue architecture analysis

- Add frontend architecture analysis (8 issues identified)
- Add backend architecture analysis (8 issues including fastjson vulnerability)
- Add API interaction specification (182 APIs documented, 10 issues)
- Add database structure analysis (49 tables, 10 issues)
- Add architecture overview with system diagram
- Add optimization suggestions (36 recommendations: P0:7, P1:11, P2:18)
- Add README index for all architecture documents
feature/20260628/refactor-frontend-modernization
Lxy 3 weeks ago
parent 27cf8f5964
commit 648b977c1c

@ -0,0 +1,111 @@
# RuoYi-Vue 架构文档
> **项目版本**: 3.8.0 | **分析日期**: 2026-06-28
> **前端**: Vue 2.6.12 + Element UI 2.15.6 | **后端**: Spring Boot 2.5.8 + JDK 1.8
---
## 文档清单
1. [架构概览](architecture-overview.md)
- 系统总体架构(前后端分层图、模块依赖图)
- 技术栈总结(前端 15 项、后端 14 项、数据库)
- 模块划分8 个 Maven 模块职责与依赖关系)
- 关键设计模式MVC、AOP 切面、单例、工厂、策略、模板方法等)
2. [前端架构分析](frontend-architecture.md)
- 组件结构22 个组件组、~40 个视图页面、复用程度分析)
- 路由权限(静态路由 + 动态路由 + 服务端路由,三层权限控制)
- 状态管理Vuex 5 个模块、数据流向、状态持久化)
- API 调用层Axios 封装、拦截器、错误处理、错误码映射)
- 问题清单8 个问题 + 8 条优化建议)
3. [后端架构分析](backend-architecture.md)
- 分层架构Controller → Service → Mapper → Domain四层架构详解
- 安全配置Spring Security 认证流程、JWT Token 机制、权限控制体系)
- AOP 切面LogAspect、DataScopeAspect、DataSourceAspect、RateLimiterAspect
- 模块依赖Maven 多模块依赖图、模块职责、技术栈汇总)
- 问题清单8 个问题及优化建议)
4. [前后端交互规范](api-interaction-spec.md)
- API 接口清单35 个前端模块、19 个 Controller、约 182 个端点)
- 请求/响应格式AjaxResult、TableDataInfo、统一响应结构
- 错误处理(前端响应拦截器、后端全局异常处理、错误码体系)
- 分页排序PageHelper 实现、排序参数、过滤规范、SQL 注入防护)
- 问题清单10 个问题 + 短/中/长期优化建议)
5. [数据库结构分析](database-structure.md)
- 表结构清单(~49 张表:系统管理 21、Quartz 9、股票业务 11、账户记账 6
- 表关系ER 关系图、外键约束分析、多对多关系表)
- 索引分析(主键索引、唯一索引、索引覆盖度评估)
- 查询模式(常用查询、复杂关联查询、统计查询、性能瓶颈识别)
- 问题清单10 个问题 + P0/P1/P2 三级优化建议含 SQL 脚本)
6. [优化建议清单](optimization-suggestions.md)
- 15+ 条优化建议(来自上述文档的汇总提炼)
- 优先级排序P0 紧急 / P1 重要 / P2 一般)
- 实施步骤(短期 1-2 周 / 中期 1-2 月 / 长期持续)
---
## 后续重构计划
基于本架构分析,后续将执行以下重构任务:
### 1. refactor-frontend-modernization — 前端重构
- 升级至 Vue 3 + Vite + Pinia
- 参考 `demo-dashboard-heatmap.html` 风格进行 UI 现代化
- 引入数据可视化组件ECharts 升级)
- 统一权限校验核心模块,消除重复代码
- 引入统一状态持久化层
- 将业务组件从通用组件目录分离
- 抽取 CRUD Schema 驱动高阶组件
### 2. refactor-backend-api — 后端 API 规范化
- 统一 RESTful API 设计规范
- 优化错误处理HTTP 状态码与业务状态码对齐)
- 添加 Swagger/OpenAPI 接口文档
- 替换 fastjson 为 fastjson2 或 Jackson
- 引入统一 DTO/VO 分层
- 补全 `@Log` 操作日志注解
- 清理调试代码(`System.out.println` → `logger`
### 3. refactor-database-optimization — 数据库优化
- 修正主键类型(`double` → `bigint`
- 为 `trade_dates` 等表添加主键
- 为核心查询字段添加索引code、trade_day、user_id 等)
- 优化 `find_in_set` 查询(闭包表或整数数组方案)
- 修复 `statistics` 表字段拼写错误
- 时间查询优化(`date_format` → 范围查询)
- 为业务表添加审计字段与软删除机制
### 4. refactor-coding-standards — 编码规范
- 制定项目编码规范文档
- 集成 Checkstyle / SpotBugs 自动检查
- 集成 ESLint + Prettier 前端代码规范
- 统一命名规范(包名、类名、方法名、变量名)
- 规范注释与 Javadoc 编写
- 建立 Git Commit 信息规范Conventional Commits
---
## 文档导航
```
docs/architecture/
├── README.md ← 当前文件:文档索引与重构计划
├── architecture-overview.md ← 系统总体架构概览
├── frontend-architecture.md ← 前端架构详细分析
├── backend-architecture.md ← 后端架构详细分析
├── api-interaction-spec.md ← 前后端交互规范
├── database-structure.md ← 数据库结构分析
└── optimization-suggestions.md ← 优化建议汇总清单
```
---
*最后更新: 2026-06-28*

@ -0,0 +1,590 @@
# RuoYi-Vue 前后端交互规范
> 文档版本v1.0 | 生成日期2026-06-28 | 适用范围RuoYi-Vue 全项目
---
## 1. API 接口清单
### 1.1 接口总览
项目共包含 **35 个前端 API 模块文件****19 个后端 Controller**,梳理出 **约 180+ 个 API 端点**。
### 1.2 按模块分类
#### 1.2.1 系统管理模块system
| 序号 | 模块 | 前端 API 文件 | 后端 Controller | 接口数 | 基础路径 |
|------|------|--------------|-----------------|--------|----------|
| 1 | 用户管理 | `system/user.js` | `SysUserController` | 13 | `/system/user` |
| 2 | 角色管理 | `system/role.js` | `SysRoleController` | 11 | `/system/role` |
| 3 | 菜单管理 | `system/menu.js` | `SysMenuController` | 7 | `/system/menu` |
| 4 | 部门管理 | `system/dept.js` | `SysDeptController` | 8 | `/system/dept` |
| 5 | 岗位管理 | `system/post.js` | `SysPostController` | 5 | `/system/post` |
| 6 | 字典类型 | `system/dict/type.js` | `SysDictTypeController` | 7 | `/system/dict/type` |
| 7 | 字典数据 | `system/dict/data.js` | `SysDictDataController` | 6 | `/system/dict/data` |
| 8 | 参数配置 | `system/config.js` | `SysConfigController` | 7 | `/system/config` |
| 9 | 公告管理 | `system/notice.js` | `SysNoticeController` | 5 | `/system/notice` |
**系统管理模块小计69 个接口**
#### 1.2.2 监控模块monitor
| 序号 | 模块 | 前端 API 文件 | 后端 Controller | 接口数 | 基础路径 |
|------|------|--------------|-----------------|--------|----------|
| 1 | 在线用户 | `monitor/online.js` | `SysUserOnlineController` | 2 | `/monitor/online` |
| 2 | 登录日志 | `monitor/logininfor.js` | `SysLogininforController` | 3 | `/monitor/logininfor` |
| 3 | 操作日志 | `monitor/operlog.js` | `SysOperlogController` | 3 | `/monitor/operlog` |
| 4 | 服务监控 | `monitor/server.js` | `ServerController` | 1 | `/monitor/server` |
| 5 | 缓存监控 | `monitor/cache.js` | `CacheController` | 1 | `/monitor/cache` |
| 6 | 定时任务 | `monitor/job.js` | `SysJobController` | 7 | `/monitor/job` |
| 7 | 任务日志 | `monitor/jobLog.js` | `SysJobLogController` | 3 | `/monitor/jobLog` |
**监控模块小计20 个接口**
#### 1.2.3 业务模块 — 记账系统booksystem
| 序号 | 模块 | 前端 API 文件 | 后端 Controller | 接口数 | 基础路径 |
|------|------|--------------|-----------------|--------|----------|
| 1 | 交易账户 | `booksystem/account.js` | `AccountController` | 5 | `/booksystem/account` |
| 2 | 交易记账 | `booksystem/book.js` | `AccountBookController` | 5 | `/booksystem/book` |
| 3 | 当日操作 | `booksystem/operations.js` | `OperationsController` | 5 | `/booksystem/operations` |
| 4 | 单笔统计 | `booksystem/statistics.js` | `StatisticsController` | 5 | `/booksystem/statistics` |
| 5 | 持仓统计 | `booksystem/statistictotal.js` | `StatisticsTotalController` | 5 | `/booksystem/statistictotal` |
| 6 | 当日持仓 | `booksystem/statisticremain.js` | `StatisticsRemainController` | 5 | `/booksystem/statisticremain` |
**记账系统模块小计30 个接口**
#### 1.2.4 业务模块 — 股票系统stocksystem
| 序号 | 模块 | 前端 API 文件 | 后端 Controller | 接口数 | 基础路径 |
|------|------|--------------|-----------------|--------|----------|
| 1 | 行情数据 | `stocksystem/stocks.js` | `StocksController` | 6 (+16 扩展) | `/stocksystem/stocks` |
| 2 | 基础数据 | `stocksystem/stockbasic.js` | `StockBasicController` | 5 | `/stocksystem/stockbasic` |
| 3 | 财务数据 | `stocksystem/financial.js` | `StockFinancialController` | 5 | `/stocksystem/financial` |
| 4 | 指数行情 | `stocksystem/stockindex.js` | `StockIndexController` | 5 | `/stocksystem/stockindex` |
| 5 | 涨跌停板 | `stocksystem/stockslimit.js` | `StocksLimitController` | 5 | `/stocksystem/stockslimit` |
| 6 | 动量结果 | `stocksystem/trends.js` | `TrendsController` | 5 (+9 扩展) | `/stocksystem/trends` |
| 7 | 动量个股 | `stocksystem/trendStocks.js` | `StocksInTrendController` | 5 | `/stocksystem/trendStocks` |
| 8 | 创新高低 | `stocksystem/newrecord.js` | `StocksNewRecordController` | 5 (+2 扩展) | `/stocksystem/newrecord` |
| 9 | 股票记账 | `stocksystem/book.js` | `StocksBookController` | 5 | `/stocksystem/book` |
**股票系统模块小计:约 63 个接口**
#### 1.2.5 工具与通用模块
| 序号 | 模块 | 前端 API 文件 | 后端 Controller | 接口数 | 基础路径 |
|------|------|--------------|-----------------|--------|----------|
| 1 | 代码生成 | `tool/gen.js` | `GenController` | 9 | `/tool/gen` |
| 2 | 登录认证 | `login.js` | `SysLoginController` / `CaptchaController` | 6 | `/login`, `/register` 等 |
| 3 | 路由信息 | `menu.js` | `SysLoginController` | 1 | `/getRouters` |
| 4 | 首页聚合 | `index.js` | 多 Controller 组合 | 16 | 分散 |
| 5 | 通用上传 | - | `CommonController` | 2 | `/common/upload`, `/common/download` |
**工具与通用模块小计:约 34 个接口**
### 1.3 分类统计
| 模块大类 | 接口数量 | 占比 |
|----------|----------|------|
| 系统管理 | ~69 | 38% |
| 监控管理 | ~20 | 11% |
| 记账系统 | ~30 | 17% |
| 股票系统 | ~63 | 35% |
| 工具与通用 | ~34 | - |
| **合计** | **~182** | **100%** |
---
## 2. 请求/响应格式
### 2.1 统一响应格式
所有 API 接口统一使用 `AjaxResult` 作为响应载体,其本质是 `HashMap<String, Object>`
#### 2.1.1 成功响应(无数据)
```json
{
"code": 200,
"msg": "操作成功"
}
```
#### 2.1.2 成功响应(带数据)
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"userId": 1,
"userName": "admin"
}
}
```
#### 2.1.3 成功响应(多字段 — AjaxResult.put 扩展)
```json
{
"code": 200,
"msg": "操作成功",
"data": { /* 用户详情 */ },
"roles": [ /* 角色列表 */ ],
"posts": [ /* 岗位列表 */ ],
"postIds": [1, 2],
"roleIds": [1]
}
```
> **注意**:后端使用 `ajax.put(key, value)` 动态追加字段,前端通过 `response.roles` 等方式访问。
#### 2.1.4 分页响应TableDataInfo
```json
{
"total": 100,
"rows": [
{ "userId": 1, "userName": "admin" },
{ "userId": 2, "userName": "test" }
],
"code": 200,
"msg": "查询成功"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `total` | `long` | 总记录数 |
| `rows` | `List<?>` | 当前页数据列表 |
| `code` | `int` | 状态码(固定 200 |
| `msg` | `String` | 消息文本 |
#### 2.1.5 失败响应
```json
{
"code": 500,
"msg": "新增用户'admin'失败,登录账号已存在"
}
```
### 2.2 请求格式规范
#### 2.2.1 查询列表GET
```javascript
// 前端
export function listUser(query) {
return request({ url: '/system/user/list', method: 'get', params: query })
}
// 请求参数通过 URL Query 传递
GET /system/user/list?pageNum=1&pageSize=10&userName=admin&status=0
```
#### 2.2.2 查询详情GET + PathVariable
```javascript
export function getUser(userId) {
return request({ url: '/system/user/' + userId, method: 'get' })
}
// 请求
GET /system/user/1
```
#### 2.2.3 新增POST + JSON Body
```javascript
export function addUser(data) {
return request({ url: '/system/user', method: 'post', data: data })
}
// 请求头: Content-Type: application/json;charset=utf-8
POST /system/user
Body: { "userName": "test", "phonenumber": "13800138000" }
```
#### 2.2.4 修改PUT + JSON Body
```javascript
export function updateUser(data) {
return request({ url: '/system/user', method: 'put', data: data })
}
PUT /system/user
Body: { "userId": 1, "userName": "admin" }
```
#### 2.2.5 删除DELETE + PathVariable
```javascript
export function delUser(userId) {
return request({ url: '/system/user/' + userId, method: 'delete' })
}
DELETE /system/user/1
```
> 后端实际接收 `Long[]` 数组,支持批量删除:`DELETE /system/user/1,2,3`
#### 2.2.6 文件下载POST + Blob
```javascript
export function download(url, params, filename) {
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob'
})
}
```
#### 2.2.7 登录请求(跳过 Token
```javascript
export function login(username, password, code, uuid) {
return request({
url: '/login',
headers: { isToken: false }, // 不携带 Token
method: 'post',
data: { username, password, code, uuid }
})
}
```
### 2.3 登录认证机制
| 环节 | 说明 |
|------|------|
| Token 载体 | JWT Token |
| Token 传递 | `Authorization: Bearer {token}` |
| 请求头配置 | `axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'` |
| Token 跳过 | `headers: { isToken: false }` |
| 超时时间 | 60 秒(验证码接口 20 秒) |
---
## 3. 错误处理机制
### 3.1 前端响应拦截器
位置:`ruoyi-ui/src/utils/request.js`
```javascript
service.interceptors.response.use(res => {
const code = res.data.code || 200; // 默认成功
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
// 弹出确认框 → 重新登录
MessageBox.confirm('登录状态已过期...', '系统提示', {...})
return Promise.reject('无效的会话...')
} else if (code === 500) {
// 全局 Message.error 提示
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code !== 200) {
// 其他错误码 → Notification.error
Notification.error({ title: msg })
return Promise.reject('error')
} else {
return res.data // 正常返回
}
}, error => {
// HTTP 层错误处理
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({ message, type: 'error', duration: 5000 })
return Promise.reject(error)
})
```
### 3.2 后端全局异常处理
位置:`ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java`
| 异常类型 | 处理方式 | 返回状态码 |
|----------|----------|------------|
| `AccessDeniedException` | 权限校验失败 | `403` — "没有权限,请联系管理员授权" |
| `HttpRequestMethodNotSupportedException` | 不支持的请求方法 | `500` — 异常消息 |
| `ServiceException` | 业务异常(带自定义 code | code 或 `500` |
| `BindException` | 参数校验失败 | `500` — 校验错误消息 |
| `MethodArgumentNotValidException` | @Valid 校验失败 | `500` — 字段错误消息 |
| `DemoModeException` | 演示模式限制 | `500` — "演示模式,不允许操作" |
| `RuntimeException` | 未知运行时异常 | `500` — 异常消息 |
| `Exception` | 兜底系统异常 | `500` — 异常消息 |
### 3.3 错误码体系
位置:`ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java`
| 状态码 | 常量名 | 含义 |
|--------|--------|------|
| 200 | `SUCCESS` | 操作成功 |
| 201 | `CREATED` | 对象创建成功 |
| 202 | `ACCEPTED` | 请求已被接受 |
| 204 | `NO_CONTENT` | 操作成功但无返回数据 |
| 301 | `MOVED_PERM` | 资源已永久移除 |
| 303 | `SEE_OTHER` | 重定向 |
| 304 | `NOT_MODIFIED` | 资源未修改 |
| 400 | `BAD_REQUEST` | 参数错误 |
| 401 | `UNAUTHORIZED` | 未授权 / Token 过期 |
| 403 | `FORBIDDEN` | 访问受限 / 权限不足 |
| 404 | `NOT_FOUND` | 资源/服务未找到 |
| 405 | `BAD_METHOD` | 不支持的 HTTP 方法 |
| 409 | `CONFLICT` | 资源冲突/被锁 |
| 415 | `UNSUPPORTED_TYPE` | 不支持的媒体类型 |
| 500 | `ERROR` | 系统内部错误 |
| 501 | `NOT_IMPLEMENTED` | 接口未实现 |
#### 前端错误码映射
位置:`ruoyi-ui/src/utils/errorCode.js`
```javascript
export default {
'401': '认证失败,无法访问系统资源',
'403': '当前操作没有权限',
'404': '访问资源不存在',
'default': '系统未知错误,请反馈给管理员'
}
```
> **注意**:前端仅映射了 3 个业务错误码其余错误码400、409、415 等)直接 fallback 到后端返回的 `msg``default`
### 3.4 错误处理流程图
```
HTTP 层错误 业务层错误
│ │
▼ ▼
Network Error / Timeout res.data.code
│ │
▼ ▼
Message.error code === 401 → MessageBox.confirm → 跳转登录
code === 500 → Message.error
code !== 200 → Notification.error
code === 200 → 正常返回 res.data
```
---
## 4. 分页 / 排序 / 过滤
### 4.1 分页实现
基于 **PageHelper** 实现,采用 ThreadLocal 模式。
#### 4.1.1 后端处理流程
```
前端请求 后端处理
│ │
│ GET /system/user/list │
│ ?pageNum=1&pageSize=10 │
▼ ▼
TableSupport.buildPageRequest()
PageUtils.startPage()
PageHelper.startPage(pageNum, pageSize, orderBy)
执行 MyBatis 查询(自动添加 LIMIT
BaseController.getDataTable(list)
new PageInfo(list).getTotal() → total
return TableDataInfo { total, rows, code, msg }
```
#### 4.1.2 分页参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `pageNum` | `Integer` | 否 | - | 当前页码(从 1 开始) |
| `pageSize` | `Integer` | 否 | - | 每页记录数 |
| `orderByColumn` | `String` | 否 | - | 排序字段名 |
| `isAsc` | `String` | 否 | - | 排序方向:`asc` 或 `desc`,多字段用逗号分隔 |
| `reasonable` | `Boolean` | 否 | - | 分页合理化pageNum<=0 查第一页pageNum>总页数查最后一页) |
#### 4.1.3 后端代码示例
```java
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage(); // 从请求参数读取分页信息
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list); // 封装为 TableDataInfo
}
```
### 4.2 排序规范
#### 4.2.1 排序参数传递
```javascript
// 前端通过分页组件传递
{
pageNum: 1,
pageSize: 10,
orderByColumn: 'createTime',
isAsc: 'desc'
}
```
#### 4.2.2 多字段排序
```
isAsc: createTime desc,updateTime desc
```
#### 4.2.3 SQL 注入防护
```java
// SqlUtil.escapeOrderBySql() 方法对排序字段进行转义
// 只允许字母、数字、下划线、点号、逗号、空格、ASC、DESC
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
PageHelper.orderBy(orderBy);
```
### 4.3 过滤规范
#### 4.3.1 列表查询过滤
采用 **对象参数绑定** 方式:
```java
// 后端
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage();
// user 对象自动绑定 query 参数中的字段
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list);
}
// 前端
GET /system/user/list?userName=admin&phonenumber=138&status=0
```
#### 4.3.2 权限过滤
```java
// 每个接口通过 @PreAuthorize 进行权限校验
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) { ... }
```
#### 4.3.3 数据权限过滤
```java
// 在 Service 层通过 AOP 实现数据权限(部门数据范围)
// 支持:全部数据、本部门数据、本部门及以下、仅本人数据、自定义数据范围
```
### 4.4 导出/导入规范
| 操作 | HTTP 方法 | 路径模式 | 说明 |
|------|-----------|----------|------|
| 导出 Excel | `POST` | `/{module}/export` | 直接写入 HttpServletResponse |
| 导入 Excel | `POST` | `/{module}/importData` | MultipartFile + updateSupport 参数 |
| 下载模板 | `POST` | `/{module}/importTemplate` | 返回空模板文件 |
---
## 5. 存在的问题
### 5.1 问题清单
| # | 问题类型 | 严重程度 | 问题描述 | 涉及范围 | 优化建议 |
|---|----------|----------|----------|----------|----------|
| 1 | **响应格式不统一** | 高 | `AjaxResult`(普通 CRUD`TableDataInfo`(分页列表)返回结构完全不同:前者用 `data` 字段,后者用 `rows` + `total` 字段。前端需要分别处理两种响应结构,增加认知负担。 | 全部列表接口 vs 详情/操作接口 | 统一使用单一响应结构,如 `{ code, msg, data: { list, total } }`,或在 TableDataInfo 中也使用 `data` 字段包裹 |
| 2 | **错误码体系不完整** | 中 | 前端 `errorCode.js` 仅映射了 401/403/404 三个错误码400/409/415 等 HTTP 标准错误码未做前端映射,全部 fallback 到 `default` 或后端 `msg`。后端虽定义了 16 个状态码,但实际使用集中在 200 和 500。 | 全局错误处理 | 完善前端错误码映射表;后端减少 500 的滥用,对参数错误返回 400对业务冲突返回 409 |
| 3 | **排序字段 SQL 注入风险** | 高 | 虽然使用了 `SqlUtil.escapeOrderBySql()` 进行过滤,但正则表达式级别的过滤存在被绕过的可能。排序参数直接从请求参数读取并拼接到 SQL ORDER BY 子句中。 | `TableSupport` + `SqlUtil` | 使用白名单机制,仅允许传入已知的字段名;或使用 MyBatis 动态 SQL 的 `<choose>` 标签做安全排序 |
| 4 | **前后端 API 路径不一致** | 中 | 前端 `index.js` 调用 `/stocksystem/stocks/groupLimitlist`,而后端 `StocksController` 中该方法被注释了权限控制(`@PreAuthorize` 被注释)。另外部分接口前端使用 `/querylist`,后端映射为 `/list`,存在冗余接口。 | 股票系统模块 | 清理未使用的接口;补全权限控制;统一前后端路径命名规范 |
| 5 | **分页参数隐式依赖** | 中 | 分页参数通过 `ServletUtils.getParameter()` 从 HttpServletRequest 中隐式读取,而非通过 `@RequestParam` 显式声明。这使得 API 文档(如 Swagger无法自动生成分页参数说明API 可读性差。 | `TableSupport.getPageDomain()` | 使用 `@RequestParam(defaultValue = "1") Integer pageNum` 显式声明分页参数,或使用 `PageRequest` 对象作为 Controller 方法参数 |
| 6 | **HTTP 状态码与业务状态码混用** | 中 | 后端 HTTP Response 的 Status Code 始终为 200真正的业务状态码在响应体 `code` 字段中。这导致前端无法利用 HTTP 状态码进行路由/拦截逻辑,也与 RESTful 最佳实践不符。 | 全局 | 保持当前模式(业界常见做法)但需在文档中明确说明;或改为 HTTP Status Code 与业务 code 一致 |
| 7 | **GET 请求参数转换方式不标准** | 低 | 前端 GET 请求的 `params` 被手动拼接为 URL 字符串(`tansParams` 函数),而不是使用 axios 原生的 `paramsSerializer`。这可能导致特殊字符编码问题。 | `request.js` 请求拦截器 | 使用 axios 的 `paramsSerializer` 配置,或 `URLSearchParams` 进行参数序列化 |
| 8 | **批量删除路径参数格式不一致** | 低 | 部分 Controller 使用 `@DeleteMapping("/{ids}")` 接收 `Long[]`,但 URL 中数组的传递格式(逗号分隔 vs 多次参数)缺乏统一规范,前端需要手动拼接字符串。 | 所有支持批量删除的接口 | 统一使用 `@DeleteMapping` + `@RequestParam List<Long> ids``@RequestBody` 传递数组 |
| 9 | **大量调试代码未清理** | 低 | `StocksController` 中存在大量 `System.out.println()` 调试输出(超过 50 处以及大段被注释的代码1000+ 行)。这些代码在生产环境中会污染日志,且增加维护成本。 | `stock-system` 模块 | 统一替换为 `logger.info/debug`;清理历史注释代码 |
| 10 | **接口文档缺失** | 中 | 项目虽引入了 Swagger 依赖(`TestController` 中有示例),但所有业务 Controller 均未添加 Swagger 注解(`@Api`、`@ApiOperation`、`@ApiParam` 等),导致无法自动生成 API 文档。 | 全部 Controller | 补充 Swagger/OpenAPI 注解;或接入 SpringDoc / Knife4j |
### 5.2 综合优化建议
#### 短期优化1-2 周)
1. **补充前端错误码映射**:在 `errorCode.js` 中补充 400/409/415 等常见错误码的中文提示
2. **清理调试代码**:将 `StocksController` 中的 `System.out.println` 替换为 `logger`
3. **补全权限注解**:取消 `StocksController` 中被注释的 `@PreAuthorize`
#### 中期优化1-2 月)
4. **接入 API 文档**:为所有 Controller 补充 Swagger 注解,启用 Knife4j 文档页面
5. **规范分页参数**:考虑使用 `@Validated` 显式声明分页参数
6. **统一响应格式**:评估是否需要统一 `AjaxResult``TableDataInfo` 的字段结构
#### 长期优化(持续)
7. **排序安全加固**:实施白名单机制替代正则过滤
8. **RESTful 对齐**:评估是否将业务状态码与 HTTP 状态码对齐
9. **API 版本管理**:当接口变更时引入 `/api/v1/` 等版本前缀
---
## 附录
### A. 关键技术栈
| 层级 | 技术 | 版本 |
|------|------|------|
| 前端框架 | Vue 2 + Element UI | 2.x |
| HTTP 客户端 | Axios | - |
| 后端框架 | Spring Boot | 2.x |
| Web 框架 | Spring MVC | - |
| 权限框架 | Spring Security | - |
| 分页组件 | PageHelper | - |
| ORM | MyBatis | - |
### B. 核心文件路径
| 文件 | 路径 |
|------|------|
| 前端请求封装 | `ruoyi-ui/src/utils/request.js` |
| 前端错误码映射 | `ruoyi-ui/src/utils/errorCode.js` |
| 前端 API 目录 | `ruoyi-ui/src/api/` |
| 后端统一响应 | `ruoyi-common/.../AjaxResult.java` |
| 后端分页响应 | `ruoyi-common/.../TableDataInfo.java` |
| 后端分页工具 | `ruoyi-common/.../PageUtils.java` |
| 后端排序支持 | `ruoyi-common/.../TableSupport.java` |
| 后端全局异常 | `ruoyi-framework/.../GlobalExceptionHandler.java` |
| 后端状态码常量 | `ruoyi-common/.../HttpStatus.java` |
| 后端 BaseController | `ruoyi-common/.../BaseController.java` |
### C. HTTP 方法使用统计
| 方法 | 用途 | 使用频率 |
|------|------|----------|
| `GET` | 查询列表、查询详情、查询树结构、下载 | ~45% |
| `POST` | 新增、登录、注册、导出、导入、分析 | ~30% |
| `PUT` | 修改、状态变更、授权、重置密码 | ~20% |
| `DELETE` | 删除、清空日志、刷新缓存 | ~5% |

@ -0,0 +1,264 @@
# RuoYi-Vue 架构概览
> **项目版本**: 3.8.0 | **分析日期**: 2026-06-28
> **前端**: Vue 2.6.12 + Element UI 2.15.6 | **后端**: Spring Boot 2.5.8 + JDK 1.8
---
## 1. 系统总体架构
### 1.1 系统架构图
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 浏览器客户端 │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ 前端层 (Vue 2 SPA) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │
│ │ │ Views │ │ Layout │ │ Store │ │ Router │ │ Components │ │ │
│ │ │ (40+页) │ │(侧边栏/ │ │ (Vuex 5 │ │(静态+ │ │ (22个组件组)│ │ │
│ │ │ │ │ 标签页) │ │ modules)│ │动态路由) │ │ │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └──────┬──────┘ │ │
│ │ └───────────┴───────────┴───────────┴─────────────┘ │ │
│ │ │ │ │
│ │ ┌────────┴────────┐ │ │
│ │ │ Axios 封装层 │ ← 请求/响应拦截器、错误处理 │ │
│ │ └────────┬────────┘ │ │
│ └───────────────────────────┼───────────────────────────────────────┘ │
└──────────────────────────────┼──────────────────────────────────────────┘
│ HTTP/JSON (Bearer Token)
┌──────────────────────────────────────────────────────────────────────────┐
│ 后端服务层 │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ ruoyi-admin (启动层 & Controller 层 — 28 个 Controller) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │ 系统管理(13) │ │ 监控管理(5) │ │ 业务模块(6) │ │ 工具/公共(4) │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬───────┘ │ │
│ │ └───────────────┴───────────────┴───────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────────────┴──────────────────────────────┐ │ │
│ │ │ ruoyi-framework (框架核心层) │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │ │ │
│ │ │ │ Security │ │ AOP 切面 │ │ Config │ │ Redis/JWT │ │ │ │
│ │ │ │ (认证/ │ │ (4个切面) │ │ (12个配置)│ │ (Token管理)│ │ │ │
│ │ │ │ 授权) │ │ │ │ │ │ │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └───────────┘ │ │ │
│ │ └─────────────────────────┬──────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────┬───────┴───────┬──────────────┐ │ │
│ │ ▼ ▼ ▼ ▼ │ │
│ │ ┌──────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ │
│ │ │ruoyi │ │ book- │ │ stock- │ │ ruoyi- │ │ │
│ │ │system│ │ system │ │ system │ │ quartz │ │ │
│ │ │(12S │ │ (6S/6M) │ │ (9M) │ │ /generator │ │ │
│ │ │ 12M) │ │ │ │ │ │ │ │ │
│ │ └──────┘ └──────────┘ └──────────┘ └────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────────────────┴──────────────────────────────┐ │ │
│ │ │ ruoyi-common (通用工具层) │ │ │
│ │ │ BaseController / AjaxResult / BaseEntity / 注解 / │ │ │
│ │ │ 工具类(20+) / 异常类 / 过滤器 / 常量 / 分页组件 │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ 数据层 │
│ ┌──────────────────────┐ ┌────────────────────────────┐ │
│ │ MySQL 数据库 │ │ Redis 缓存 │ │
│ │ ┌────────────────┐ │ │ ┌──────────────────────┐ │ │
│ │ │ sys_* 系统表 │ │ │ │ login_tokens:{uuid} │ │ │
│ │ │ book_* 账本表 │ │ │ │ 用户会话 / Token │ │ │
│ │ │ stock_* 股票表 │ │ │ ├──────────────────────┤ │ │
│ │ │ quartz_* 任务表 │ │ │ │ 限流计数器 │ │ │
│ │ │ gen_* 生成器表 │ │ │ ├──────────────────────┤ │ │
│ │ └────────────────┘ │ │ │ 配置缓存 / 字典缓存 │ │ │
│ └──────────────────────┘ │ └──────────────────────┘ │ │
│ └────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
```
### 1.2 分层说明
| 层级 | 职责 | 关键技术 |
|------|------|---------|
| **前端展示层** | 页面渲染、用户交互、路由导航、状态管理 | Vue 2 SPA、Element UI、Vuex、Vue Router |
| **API 通信层** | 前后端 HTTP 通信、Token 携带、错误统一处理 | Axios + 拦截器、JWT Bearer Token |
| **Controller 层** | 请求接收、参数校验、权限注解、响应封装 | Spring MVC、@PreAuthorize、@Log |
| **框架核心层** | 安全认证、AOP 切面、数据源路由、全局配置 | Spring Security、JWT、AspectJ、Druid |
| **Service 层** | 业务逻辑、事务管理、数据权限过滤 | @Transactional、@DataScope |
| **Mapper 层** | SQL 映射、动态查询、结果集映射 | MyBatis + XML + PageHelper |
| **Domain 层** | 实体模型、基类继承、VO/DTO 传输 | BaseEntity、TreeEntity |
| **数据存储层** | 持久化存储、会话缓存、限流计数 | MySQL 5.7+、Redis |
---
## 2. 技术栈总结
### 2.1 前端技术栈
| 分类 | 技术 | 版本 | 用途 |
|------|------|------|------|
| 核心框架 | Vue.js | 2.6.12 | 渐进式 MVVM 框架 |
| UI 组件库 | Element UI | 2.15.6 | 桌面端组件库 |
| 路由管理 | Vue Router | 3.4.9 | SPA 路由(含动态路由) |
| 状态管理 | Vuex | 3.6.0 | 集中式状态管理5 个模块) |
| HTTP 客户端 | Axios | 0.24.0 | HTTP 请求封装 |
| 构建工具 | Vue CLI | 4.4.6 | 项目脚手架 |
| CSS 预处理 | Sass | 1.32.13 | 样式预处理器 |
| 图表库 | ECharts | 4.9.0 | 数据可视化 |
| 富文本编辑器 | Quill | 1.3.7 | 富文本编辑 |
| 代码高亮 | highlight.js | 9.18.5 | 代码语法高亮 |
| 拖拽排序 | vuedraggable | 2.24.3 | 列表拖拽 |
| SVG 图标 | svg-sprite-loader | 5.1.1 | SVG 雪碧图 |
| 加密 | jsencrypt | 3.2.1 | RSA 密码加密 |
| 进度条 | NProgress | 0.2.0 | 页面加载进度 |
| Cookie | js-cookie | 3.0.1 | Cookie 读写 |
### 2.2 后端技术栈
| 分类 | 技术 | 版本 | 用途 |
|------|------|------|------|
| 核心框架 | Spring Boot | 2.5.8 | 快速开发框架 |
| JDK | Java | 1.8 | 运行环境 |
| 安全框架 | Spring Security | 内置 | 认证与授权 |
| JWT | jjwt | 0.9.1 | Token 签发与验证 |
| ORM | MyBatis | 2.2.0 (starter) | SQL 映射框架 |
| 分页插件 | PageHelper | 1.4.0 | 物理分页 |
| 连接池 | Druid | 1.2.8 | 数据库连接池 + 监控 |
| 缓存 | Spring Data Redis | 内置 | 会话缓存、限流 |
| API 文档 | Springfox Swagger 3 | 3.0.0 | 接口文档 |
| JSON 解析 | FastJSON | 1.2.79 | JSON 序列化 |
| Excel 处理 | Apache POI | 4.1.2 | Excel 导入导出 |
| 验证码 | Kaptcha | 2.3.2 | 图形验证码 |
| 代码生成 | Velocity | 2.3 | 模板引擎 |
| 系统监控 | Oshi | 5.8.6 | 服务器信息采集 |
| 定时任务 | Quartz | 内置 | 任务调度 |
### 2.3 数据库技术栈
| 技术 | 用途 | 说明 |
|------|------|------|
| MySQL 5.7+ | 主数据库 | 系统表(sys_*)、业务表(book_*/stock_*)、任务表(quartz_*) |
| Redis | 缓存/会话 | Token 存储(login_tokens:{uuid})、限流计数器、配置缓存 |
---
## 3. 模块划分
### 3.1 模块清单
| 模块名 | 类型 | 职责 | 核心内容 |
|--------|------|------|----------|
| **ruoyi-admin** | 启动模块 | Spring Boot 启动入口Controller 层聚合 | 启动类、28 个 Controller、Swagger 配置、打包部署 |
| **ruoyi-framework** | 框架核心 | 安全认证、AOP 切面、数据源、全局配置 | Security、JWT、4 个切面、12 个配置类、异步管理 |
| **ruoyi-system** | 系统管理 | 系统基础业务 | 用户/角色/菜单/部门/字典/岗位/日志 (12 Service + 12 Mapper) |
| **ruoyi-common** | 通用工具 | 基类、工具类、注解、异常、常量 | BaseController、AjaxResult、BaseEntity、20+ 工具类 |
| **ruoyi-generator** | 代码生成 | Velocity 模板驱动的代码生成器 | 表结构查询、模板渲染、2 个 Mapper |
| **ruoyi-quartz** | 定时任务 | 基于 Quartz 的任务调度管理 | 任务 CRUD、执行日志、2 个 Mapper |
| **book-system** | 账本业务 | 账务管理业务模块 | 账户/账本/操作记录/统计 (6 Service + 6 Mapper) |
| **stock-system** | 股票业务 | 股票分析业务模块 | 股票信息/行情/财务/趋势 (9 Mapper) |
### 3.2 模块依赖图
```
┌─────────────┐
│ ruoyi-admin │ ← 启动入口,打包部署
└──────┬──────┘
│ depends on
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌─────────────┐
│ ruoyi- │ │ ruoyi- │ │ ruoyi- │
│ system │ │ quartz │ │ generator │
└──────┬─────┘ └──────┬─────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────┐
│ ruoyi-framework │
│ (Security, AOP, DataSource, Redis, Config)│
└──────────────────────┬──────────────────────┘
│ depends on
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌─────────────┐
│ book- │ │ stock- │ │ ruoyi- │
│ system │ │ system │ │ common │
└────────────┘ └────────────┘ └─────────────┘
```
**依赖规则**
- `ruoyi-admin` → 依赖所有业务模块 + framework
- 业务模块 (`ruoyi-system` / `book-system` / `stock-system` / `quartz` / `generator`) → 依赖 `ruoyi-framework`
- `ruoyi-framework` → 依赖 `ruoyi-common`
- `ruoyi-common` → 无内部依赖(底层基础模块)
---
## 4. 关键设计模式
### 4.1 MVC 模式
```
Controller (ruoyi-admin)
│ 接收请求、参数校验、权限控制
Service (各业务模块)
│ 业务逻辑、事务管理
Mapper (各业务模块)
│ SQL 映射、数据访问
Domain (ruoyi-common / 各模块)
实体模型、基类继承
```
- **Controller**: 统一继承 `BaseController`,获得 `startPage()`、`getDataTable()`、`toAjax()` 等通用方法
- **Service**: 接口 + 实现分离 (`ISysUserService` / `SysUserServiceImpl`)
- **Mapper**: MyBatis XML 映射 + Java 接口,大量使用动态 SQL
- **Domain**: `BaseEntity``TreeEntity` 继承体系
### 4.2 分层架构
**物理分层**Maven 多模块)与 **逻辑分层**Controller → Service → Mapper相结合
```
┌───────────────────────────────────────────┐
│ ruoyi-admin → Controller 层 (表现层) │
├───────────────────────────────────────────┤
│ ruoyi-framework → 框架支撑层 (横切关注点) │
├───────────────────────────────────────────┤
│ ruoyi-system → Service/Mapper (业务层) │
│ book-system → Service/Mapper (业务层) │
│ stock-system → Service/Mapper (业务层) │
├───────────────────────────────────────────┤
│ ruoyi-common → Domain/工具类 (基础层) │
└───────────────────────────────────────────┘
```
### 4.3 AOP 切面
| 切面 | 注解 | 通知类型 | 优先级 | 用途 |
|------|------|---------|--------|------|
| **DataSourceAspect** | `@DataSource` | `@Around` | `@Order(1)` | 多数据源动态切换 |
| **DataScopeAspect** | `@DataScope` | `@Before` | — | 数据权限 SQL 注入 |
| **LogAspect** | `@Log` | `@AfterReturning` / `@AfterThrowing` | — | 操作日志异步记录 |
| **RateLimiterAspect** | `@RateLimiter` | `@Before` | — | Redis 限流控制 |
### 4.4 其他设计模式
| 模式 | 应用场景 | 实现方式 |
|------|---------|---------|
| **单例模式** | Spring Bean 默认作用域 | 配置类 (`SecurityConfig`、`DruidConfig` 等) 通过 `@Configuration` + `@Bean` 实现 |
| **工厂模式** | 异步任务创建 | `AsyncFactory` 创建不同类型的异步任务(操作日志、登录日志) |
| **策略模式** | 数据权限过滤 | `@DataScope` 注解根据不同数据范围类型(全部/本部门/本人等)生成不同的 SQL 过滤策略 |
| **模板方法模式** | BaseController | 基类定义通用流程(分页、响应),子类 Controller 继承复用 |
| **责任链模式** | Spring Security 过滤器链 | `CorsFilter → JwtAuthenticationTokenFilter → AuthenticationFilter` 依次处理 |
| **代理模式** | MyBatis Mapper | MyBatis 通过动态代理生成 Mapper 接口实现 |
| **观察者模式** | Spring 事件机制 | `AsyncManager` 通过线程池异步处理任务,解耦日志写入与主流程 |
---
*文档结束*

@ -0,0 +1,470 @@
# 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`),未来接口升级时无法平滑过渡 |
---
*文档结束*

@ -0,0 +1,455 @@
# RuoYi-Vue 数据库结构分析
> 分析日期2026-06-28
> 数据来源:建表 SQL 文件、Mapper XML 文件、Domain 实体类
---
## 1. 表结构清单
### 1.1 系统管理表sys_* / gen_* / QRTZ_*
系统管理表位于 `ry` 数据库,由 RuoYi 框架提供。
| 表名 | 字段数 | 主键 | 用途 | 备注 |
|------|--------|------|------|------|
| `sys_user` | 18 | user_id (bigint, AI) | 用户信息表 | 含 del_flag 软删除 |
| `sys_dept` | 12 | dept_id (bigint, AI) | 部门表 | 树形结构,含 ancestors |
| `sys_role` | 13 | role_id (bigint, AI) | 角色信息表 | 含 data_scope 数据权限 |
| `sys_menu` | 17 | menu_id (bigint, AI) | 菜单权限表 | 树形结构M/C/F 三种类型 |
| `sys_post` | 9 | post_id (bigint, AI) | 岗位信息表 | - |
| `sys_dict_type` | 9 | dict_id (bigint, AI) | 字典类型表 | **唯一索引**: dict_type |
| `sys_dict_data` | 12 | dict_code (bigint, AI) | 字典数据表 | 关联 dict_type |
| `sys_config` | 9 | config_id (int, AI) | 参数配置表 | - |
| `sys_notice` | 9 | notice_id (int, AI) | 通知公告表 | content 使用 longblob |
| `sys_oper_log` | 15 | oper_id (bigint, AI) | 操作日志记录 | 大表,高频写入 |
| `sys_logininfor` | 8 | info_id (bigint, AI) | 系统访问记录 | 登录日志 |
| `sys_job` | 12 | (job_id, job_name, job_group) 联合主键 | 定时任务调度表 | - |
| `sys_job_log` | 7 | job_log_id (bigint, AI) | 定时任务调度日志 | - |
| `sys_user_role` | 2 | (user_id, role_id) 联合主键 | 用户-角色关联表 | 多对多中间表 |
| `sys_role_menu` | 2 | (role_id, menu_id) 联合主键 | 角色-菜单关联表 | 多对多中间表 |
| `sys_role_dept` | 2 | (role_id, dept_id) 联合主键 | 角色-部门关联表 | 多对多中间表 |
| `sys_user_post` | 2 | (user_id, post_id) 联合主键 | 用户-岗位关联表 | 多对多中间表 |
| `gen_table` | 17 | table_id (bigint, AI) | 代码生成业务表 | - |
| `gen_table_column` | 21 | column_id (bigint, AI) | 代码生成业务表字段 | - |
| `QRTZ_JOB_DETAILS` | 10 | (sched_name, job_name, job_group) | Quartz 任务详情 | 外键关联 |
| `QRTZ_TRIGGERS` | 14 | (sched_name, trigger_name, trigger_group) | Quartz 触发器 | FK → QRTZ_JOB_DETAILS |
| `QRTZ_SIMPLE_TRIGGERS` | 6 | (sched_name, trigger_name, trigger_group) | Quartz 简单触发器 | FK → QRTZ_TRIGGERS |
| `QRTZ_CRON_TRIGGERS` | 5 | (sched_name, trigger_name, trigger_group) | Quartz Cron 触发器 | FK → QRTZ_TRIGGERS |
| `QRTZ_BLOB_TRIGGERS` | 4 | (sched_name, trigger_name, trigger_group) | Quartz Blob 触发器 | FK → QRTZ_TRIGGERS |
| `QRTZ_CALENDARS` | 3 | (sched_name, calendar_name) | Quartz 日历 | - |
| `QRTZ_FIRED_TRIGGERS` | 11 | (sched_name, entry_id) | Quartz 已触发触发器 | - |
| `QRTZ_PAUSED_TRIGGER_GRPS` | 2 | (sched_name, trigger_group) | Quartz 暂停触发器组 | - |
| `QRTZ_SCHEDULER_STATE` | 5 | (sched_name, instance_name) | Quartz 调度器状态 | - |
| `QRTZ_LOCKS` | 2 | (sched_name, lock_name) | Quartz 锁表 | - |
| `QRTZ_SIMPROP_TRIGGERS` | 8 | (sched_name, trigger_name, trigger_group) | Quartz 简单属性触发器 | FK → QRTZ_TRIGGERS |
### 1.2 股票业务表nstocks 数据库)
| 表名 | 字段数 | 主键 | 用途 | 备注 |
|------|--------|------|------|------|
| `stocks` | 15 | id (double, AI) | 全部 A 股每日交易数据 | 核心行情表AUTO_INCREMENT=82694 |
| `stocks_tmp` | 7 | id (double, AI) | 全部 A 股每日交易数据辅助表 | 临时/辅助计算表 |
| `stock_index` | 23 | id (double, AI) | 指数交易行情 | AUTO_INCREMENT=82694 |
| `stock_financial` | 9 | id (double, AI) | A 股财务数据 | AUTO_INCREMENT=137032 |
| `stock_basis` | 15 | id (double, AI) | A 股基础信息 | 大文件(521KB),含上市日期等 |
| `stocks_in_trend` | 5 | id (double, AI) | 动量个股 | 关联 trends 表 |
| `stocks_limit_up` | 3 | id (double, AI) | 每日涨停板 | 仅 code + trade_day |
| `trends` | 9 | id (double, AI) | 动量结果(板块级别) | 含板块排名 |
| `industries` | 5 | id (double, AI) | 东财行业 2 级每日数据 | - |
| `industries_basis` | 3 | id (double, AI) | 东财行业 2 级基础信息 | 行业字典表 |
| `trade_dates` | 3 | **无主键** | 可交易日期日历 | 包含节假日信息 |
### 1.3 账户/记账业务表ry 数据库)
| 表名 | 字段数 | 主键 | 用途 | 备注 |
|------|--------|------|------|------|
| `account` | 9 | id (double, AI) | 账户每日统计 | 净资产、总资产、盈亏 |
| `account_book` | 14 | id (double, AI) | 操作记录表(交易记) | 对应 Operations 实体 |
| `operations` | 16 | id (double, AI) | 操作表(含账户转入转出) | 含 deal_logic 操作逻辑 |
| `statistics` | 10 | id (double, AI) | 交易统计表单笔操作 | 清仓时统计 |
| `statistics_remain` | 10 | id (double, AI) | 统计表当日持仓 | 包含当日清仓 |
| `statistics_total` | 16 | id (double, AI) | 统计表清仓后汇总 | 总盈亏、总手续费等 |
### 1.4 分类统计
| 分类 | 表数量 | 占比 |
|------|--------|------|
| 系统管理表 | 21 | 42% |
| Quartz 定时任务表 | 9 | 18% |
| 股票业务表 | 11 | 22% |
| 账户/记账业务表 | 6 | 12% |
| 代码生成表 | 2 | 4% |
| **合计** | **~49** | **100%** |
---
## 2. 表关系
### 2.1 系统管理模块关系图
```
┌─────────────┐ ┌───────────────┐ ┌─────────────┐
│ sys_dept │◄─────│ sys_user │─────►│ sys_post │
│ (部门树) │ │ (用户信息) │ │ (岗位) │
└──────┬──────┘ └──────┬────────┘ └──────┬──────┘
│ │ │
│ ┌─────┴─────┐ ┌─────┴──────┐
│ │sys_user_ │ │sys_user_ │
│ │ role │ │ post │
│ │(N-M 中间) │ │(N-M 中间) │
│ └─────┬─────┘ └────────────┘
│ │
│ ┌─────┴─────┐ ┌─────────────┐
└─────────────►│ sys_role │◄─────│ sys_role_ │
│ (角色) │ │ dept │
└─────┬─────┘ │(N-M 中间) │
│ └─────────────┘
┌─────┴─────┐
│sys_role_ │ ┌─────────────┐
│ menu │◄─────│ sys_menu │
│(N-M 中间) │ │ (菜单树) │
└───────────┘ └─────────────┘
```
### 2.2 股票业务模块关系图
```
┌──────────────────┐ ┌───────────────┐ ┌──────────────────┐
│ stock_basis │ │ stocks │ │ stock_financial │
│ (股票基础信息) │ │ (每日行情) │ │ (财务数据) │
└────────┬─────────┘ └───────┬───────┘ └──────────────────┘
│ │
│ code(逻辑关联) │ code + trade_day
│ │
▼ ▼
┌──────────────────┐ ┌───────────────┐ ┌──────────────────┐
│ industries_ │ │ stock_index │ │ stocks_in_trend │
│ basis │ │ (指数行情) │ │ (动量个股) │
│ (行业字典) │ └───────────────┘ └────────┬─────────┘
└────────┬─────────┘ │
│ code + trade_day
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ industries │ │ trends │
│ (行业每日数据) │◄──── blemind2 关联 ──────│ (板块动量排名) │
└──────────────────┘ └──────────────────┘
┌──────────────────┐ ┌───────────────┐
│ trade_dates │ │stocks_limit_up│
│ (交易日历) │ │ (涨停板) │
└──────────────────┘ └───────────────┘
```
### 2.3 账户/记账模块关系图
```
┌─────────────┐ ┌───────────────┐ ┌─────────────┐
│ account │ │ operations │◄───►│account_book │
│ (账户统计) │ │ (操作记录) │ │ (交易记录) │
└─────────────┘ └───────┬───────┘ └─────────────┘
user_id │ pre_id
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌───────────┐ ┌─────────────┐
│statistics │ │statistics │ │statistics_ │
│ (单笔统计)│ │ _remain │ │ total │
│ │ │(当日持仓) │ │(清仓汇总) │
└──────────┘ └───────────┘ └─────────────┘
user_id user_id user_id
```
### 2.4 外键约束分析
| 表名 | 外键字段 | 引用表 | 引用字段 | 约束类型 |
|------|----------|--------|----------|----------|
| QRTZ_TRIGGERS | (sched_name, job_name, job_group) | QRTZ_JOB_DETAILS | 主键 | 物理外键 |
| QRTZ_SIMPLE_TRIGGERS | (sched_name, trigger_name, trigger_group) | QRTZ_TRIGGERS | 主键 | 物理外键 |
| QRTZ_CRON_TRIGGERS | (sched_name, trigger_name, trigger_group) | QRTZ_TRIGGERS | 主键 | 物理外键 |
| QRTZ_BLOB_TRIGGERS | (sched_name, trigger_name, trigger_group) | QRTZ_TRIGGERS | 主键 | 物理外键 |
| QRTZ_SIMPROP_TRIGGERS | (sched_name, trigger_name, trigger_group) | QRTZ_TRIGGERS | 主键 | 物理外键 |
**注意**:系统管理表和业务表之间 **没有物理外键约束**,全部采用逻辑关联(通过代码层 JOIN 实现)。
### 2.5 多对多关系表
| 中间表 | 关联实体 1 | 关联实体 2 | 用途 |
|--------|-----------|-----------|------|
| `sys_user_role` | sys_user | sys_role | 用户角色分配 |
| `sys_role_menu` | sys_role | sys_menu | 角色菜单权限 |
| `sys_role_dept` | sys_role | sys_dept | 角色数据权限范围 |
| `sys_user_post` | sys_user | sys_post | 用户岗位分配 |
---
## 3. 索引分析
### 3.1 主键索引清单
| 表名 | 主键类型 | 主键字段 | 自增 |
|------|----------|----------|------|
| sys_user | 单列 | user_id (bigint) | ✅ |
| sys_dept | 单列 | dept_id (bigint) | ✅ |
| sys_role | 单列 | role_id (bigint) | ✅ |
| sys_menu | 单列 | menu_id (bigint) | ✅ |
| sys_post | 单列 | post_id (bigint) | ✅ |
| sys_dict_type | 单列 | dict_id (bigint) | ✅ |
| sys_dict_data | 单列 | dict_code (bigint) | ✅ |
| sys_config | 单列 | config_id (int) | ✅ |
| sys_notice | 单列 | notice_id (int) | ✅ |
| sys_oper_log | 单列 | oper_id (bigint) | ✅ |
| sys_logininfor | 单列 | info_id (bigint) | ✅ |
| sys_job | **联合** | (job_id, job_name, job_group) | job_id 自增 |
| sys_job_log | 单列 | job_log_id (bigint) | ✅ |
| sys_user_role | **联合** | (user_id, role_id) | ❌ |
| sys_role_menu | **联合** | (role_id, menu_id) | ❌ |
| sys_role_dept | **联合** | (role_id, dept_id) | ❌ |
| sys_user_post | **联合** | (user_id, post_id) | ❌ |
| stocks | 单列 | id (double) | ✅ |
| stock_index | 单列 | id (double) | ✅ |
| trade_dates | **无主键** | - | ❌ |
### 3.2 唯一索引
| 表名 | 索引字段 | 说明 |
|------|----------|------|
| sys_dict_type | dict_type | 字典类型编码唯一 |
| sys_user | 无唯一索引 | 用户名/手机/邮箱仅有应用层校验 |
### 3.3 普通索引 / 复合索引
**大部分业务表缺少索引**,仅有主键索引。以下表没有除主键外的任何索引:
- `stocks` — 15 字段,仅有主键
- `stock_index` — 23 字段,仅有主键
- `stock_financial` — 9 字段,仅有主键
- `operations` — 16 字段,仅有主键
- `statistics` — 10 字段,仅有主键
- `statistics_remain` — 10 字段,仅有主键
- `statistics_total` — 16 字段,仅有主键
- `trade_dates` — 无主键、无索引
### 3.4 索引使用情况评估
| 表名 | 索引覆盖度 | 评价 |
|------|-----------|------|
| sys_dict_type | ★★★☆☆ | 有唯一索引,但缺少 status 索引 |
| sys_user | ★★☆☆☆ | 缺少 userName、phone、email 索引 |
| sys_oper_log | ★☆☆☆☆ | 缺少 oper_time、oper_name 索引 |
| stocks | ★☆☆☆☆ | 缺少 code、trade_day 索引 |
| trade_dates | ☆☆☆☆☆ | 无主键无索引 |
---
## 4. 查询模式
### 4.1 常用查询模式
#### 系统管理模块
| 查询场景 | 涉及表 | 查询方式 | 典型条件 |
|----------|--------|----------|----------|
| 用户登录 | sys_user | 等值查询 | user_name = ? |
| 用户列表 | sys_user + sys_dept | LEFT JOIN + 模糊查询 | del_flag='0', LIKE 姓名/手机 |
| 用户详情 | sys_user + sys_dept + sys_user_role + sys_role | 多表 LEFT JOIN | user_id = ? |
| 角色已分配用户 | sys_user + sys_dept + sys_user_role + sys_role | 多表 JOIN + DISTINCT | role_id = ? |
| 角色未分配用户 | sys_user + sys_dept + sys_user_role + sys_role | NOT IN 子查询 | role_id != ? |
| 部门树查询 | sys_dept | 递归/ancestors LIKE | parent_id = ? |
| 菜单树查询 | sys_menu | 递归/parent_id | parent_id = ? |
| 操作日志 | sys_oper_log | 分页 + 时间范围 | oper_time BETWEEN |
| 登录日志 | sys_logininfor | 分页 + 时间范围 | login_time BETWEEN |
| 字典查询 | sys_dict_type + sys_dict_data | 等值关联 | dict_type = ? |
#### 股票业务模块
| 查询场景 | 涉及表 | 查询方式 | 典型条件 |
|----------|--------|----------|----------|
| 股票行情查询 | stocks | 等值 + 范围 | code = ?, trade_day = ? |
| 区间涨跌幅查询 | stocks | 范围查询 | differrange10/20/60 BETWEEN |
| 动量个股查询 | stocks_in_trend + trends | code 关联 | trade_day = ?, type = ? |
| 涨停板查询 | stocks_limit_up | 等值 | trade_day = ? |
| 财务数据查询 | stock_financial | 等值 + 排序 | code = ?, period = ? |
| 指数行情查询 | stock_index | 等值 + 范围 | code = ?, trade_day = ? |
#### 账户记账模块
| 查询场景 | 涉及表 | 查询方式 | 典型条件 |
|----------|--------|----------|----------|
| 账户统计 | account | 等值查询 | trade_day = ?, user_id = ? |
| 操作记录 | operations | 等值 + 模糊 | code = ?, name LIKE, trade_day = ? |
| 当日持仓统计 | statistics_remain | 等值 | code = ?, trade_day = ?, user_id = ? |
| 清仓汇总统计 | statistics_total | 等值 | code = ?, user_id = ? |
| 单笔统计 | statistics | 等值 | code = ?, operations_id = ? |
### 4.2 复杂关联查询
#### 查询 1用户详情4 表 JOIN
```sql
-- SysUserMapper.xml: selectUserVo
SELECT u.*, d.*, r.*
FROM sys_user u
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
LEFT JOIN sys_user_role ur ON u.user_id = ur.user_id
LEFT JOIN sys_role r ON r.role_id = ur.role_id
WHERE u.user_name = ?
```
**分析**4 表 LEFT JOINsys_user_role 和 sys_role 的 JOIN 会产生笛卡尔积膨胀(一个用户多个角色时返回多行),由 MyBatis 的 `<collection>` 标签在应用层聚合。
#### 查询 2部门树递归查询
```sql
-- SysUserMapper.xml: deptId 条件
SELECT t.dept_id FROM sys_dept t
WHERE find_in_set(#{deptId}, ancestors)
```
**分析**:使用 `find_in_set` 函数进行字符串匹配,无法使用索引,全表扫描。
#### 查询 3数据范围过滤
```sql
-- SysUserMapper.xml: dataScope
${params.dataScope} -- 动态 SQL 拼接
```
**分析**:使用 `${}` 直接拼接 SQL存在 SQL 注入风险(虽然框架层有控制)。
### 4.3 统计查询
| 统计场景 | 涉及表 | 说明 |
|----------|--------|------|
| 账户每日盈亏 | account | 按 trade_day 聚合 assets_diff |
| 个股总盈亏 | statistics_total | 按 code + user_id 汇总 total_profit |
| 持仓盈亏 | statistics_remain | 按 trade_day 汇总 total_profit |
| 板块排名 | trends | 按 sort 排序 |
### 4.4 性能瓶颈识别
| 瓶颈 | 位置 | 严重程度 | 说明 |
|------|------|----------|------|
| `find_in_set` 查询 | sys_dept.ancestors | 🔴 高 | 全表扫描,无法利用索引 |
| 日期格式化查询 | sys_user.create_time | 🟡 中 | `date_format(u.create_time,'%y%m%d')` 导致索引失效 |
| LIKE 前缀通配符 | 多处 name/phone | 🟡 中 | `LIKE '%keyword%'` 无法使用索引 |
| 大表无索引 | stocks, stock_index | 🔴 高 | 8 万+ 记录表仅主键索引 |
| NOT IN 子查询 | 未分配用户查询 | 🟡 中 | 可能被 NOT EXISTS 优化 |
| 动态 SQL 拼接 | dataScope | 🟡 中 | 安全风险 + 查询计划不可预测 |
---
## 5. 存在的问题
### 5.1 问题清单
| # | 问题 | 涉及表 | 严重程度 | 影响范围 |
|---|------|--------|----------|----------|
| 1 | **主键使用 double 类型** | 所有业务表stocks, account, operations, statistics 等) | 🔴 严重 | double 是浮点类型,用于主键可能导致精度问题和性能下降 |
| 2 | **trade_dates 表无主键** | trade_dates | 🔴 严重 | 无主键表无法保证数据唯一性,复制和备份可能出错 |
| 3 | **业务表缺少必要索引** | stocks, stock_index, stock_financial, operations 等 | 🔴 严重 | 高频查询字段code, trade_day无索引导致全表扫描 |
| 4 | **statistics 表存在字段名拼写错误** | statistics | 🟡 中等 | 同时存在 `operations_id`(varchar) 和 `operateion_id`(double) 两个含义相近字段 |
| 5 | **无逻辑外键导致数据一致性风险** | sys_user_role, sys_role_menu, statistics 等 | 🟡 中等 | 删除用户/角色时,关联表数据可能残留 |
| 6 | **find_in_set 查询无法利用索引** | sys_dept | 🟡 中等 | 部门树查询性能随数据量线性下降 |
| 7 | **date_format 函数导致索引失效** | sys_user, sys_oper_log 等 | 🟡 中等 | 时间范围查询使用 `date_format()` 包装列 |
| 8 | **缺少审计字段** | 大部分业务表 | 🟢 低 | 业务表缺少 create_by, create_time, update_by, update_time |
| 9 | **缺少软删除机制** | 业务表stocks, operations 等) | 🟢 低 | 数据删除后无法恢复,不符合审计要求 |
| 10 | **大字段 TEXT/BLOB 无分离** | sys_notice, operations.deal_logic, statistics.bz | 🟢 低 | 大文本字段与主表混合存储,影响查询性能 |
### 5.2 优化建议
#### P0必须修复
1. **主键类型修正**:将所有业务表的 `id double` 改为 `id bigint(20) NOT NULL AUTO_INCREMENT`
```sql
ALTER TABLE stocks MODIFY COLUMN id bigint(20) NOT NULL AUTO_INCREMENT;
```
2. **添加核心查询索引**
```sql
-- stocks 表
ALTER TABLE stocks ADD INDEX idx_code_trade (code, trade_day);
ALTER TABLE stocks ADD INDEX idx_trade_day (trade_day);
-- stock_index 表
ALTER TABLE stock_index ADD INDEX idx_code_trade (code, trade_day);
-- operations 表
ALTER TABLE operations ADD INDEX idx_code_trade (code, trade_day);
ALTER TABLE operations ADD INDEX idx_user_id (user_id);
-- statistics_remain 表
ALTER TABLE statistics_remain ADD INDEX idx_code_trade (code, trade_day);
ALTER TABLE statistics_remain ADD INDEX idx_user_id (user_id);
```
3. **trade_dates 表添加主键**
```sql
ALTER TABLE trade_dates ADD PRIMARY KEY (date);
```
#### P1建议修复
4. **修复 statistics 表字段**:合并 `operations_id``operateion_id` 为统一的外键字段
5. **时间查询优化**:将 `date_format(u.create_time,'%y%m%d') >= ?` 改为范围查询:
```sql
AND u.create_time >= #{params.beginTime}
AND u.create_time < DATE_ADD(#{params.endTime}, INTERVAL 1 DAY)
```
6. **部门树查询优化**考虑使用闭包表Closure Table或物化路径的整数数组类型替代 `find_in_set`
#### P2可选改进
7. **添加审计字段**:为所有业务表添加 create_by, create_time, update_by, update_time
8. **添加软删除**:为业务表添加 del_flag 字段
9. **大字段分离**:将 operations.deal_logic、statistics.bz 等 TEXT 字段移至扩展表
---
## 附录:数据库 ER 概览
```
┌─────────────────────────────────────────────────────────────────┐
│ RuoYi-Vue 数据库概览 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ 系统管理 (ry) ──────────────────────────────────────────┐ │
│ │ sys_user ←→ sys_dept (树) │ │
│ │ sys_user ←N:M→ sys_role ←N:M→ sys_menu (树) │ │
│ │ sys_user ←N:M→ sys_post │ │
│ │ sys_role ←N:M→ sys_dept │ │
│ │ sys_dict_type ←1:N→ sys_dict_data │ │
│ │ sys_oper_log / sys_logininfor (日志) │ │
│ │ sys_job → sys_job_log (定时任务) │ │
│ │ gen_table ←1:N→ gen_table_column (代码生成) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 股票业务 (nstocks) ─────────────────────────────────────┐ │
│ │ stock_basis ←(code)→ stocks ←(code)→ stock_financial │ │
│ │ stocks ←(code)→ stocks_in_trend ←(code)→ trends │ │
│ │ stock_index (独立) │ │
│ │ industries_basis ←(name)→ industries │ │
│ │ stocks_limit_up (独立) │ │
│ │ trade_dates (日历) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 账户记账 (ry) ──────────────────────────────────────────┐ │
│ │ account (每日统计) │ │
│ │ operations ←(pre_id)→ operations (自引用) │ │
│ │ operations ←→ statistics (单笔统计) │ │
│ │ operations ←→ statistics_remain (当日持仓) │ │
│ │ operations ←→ statistics_total (清仓汇总) │ │
│ │ account_book (操作快照) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
*文档结束*

@ -0,0 +1,415 @@
# RuoYi-Vue 前端架构分析
> **项目版本**: 3.8.0 | **技术栈**: Vue 2.6.12 + Element UI 2.15.6 + Vuex 3.6 + Vue Router 3.4.9
> **构建工具**: Vue CLI 4.4.6 | **描述**: 摸金分析系统
> **分析日期**: 2026-06-28
---
## 1. 组件结构分析
### 1.1 组件树图ASCII
```
src/
├── components/ # 全局通用组件22 个组件组)
│ ├── Breadcrumb/ # ── 面包屑导航
│ ├── Crontab/ # ── Cron 表达式生成器9 个子组件)
│ │ ├── day.vue / hour.vue / min.vue # 时间维度选择
│ │ ├── month.vue / week.vue / year.vue
│ │ ├── second.vue / result.vue / index.vue
│ ├── DictData/ # ── 字典数据管理器JS 模块)
│ ├── DictTag/ # ── 字典标签渲染
│ ├── Editor/ # ── 富文本编辑器Quill
│ ├── FileUpload/ # ── 文件上传
│ ├── Hamburger/ # ── 汉堡菜单按钮
│ ├── HeaderSearch/ # ── 头部搜索Fuse.js
│ ├── IconSelect/ # ── SVG 图标选择器
│ ├── ImagePreview/ # ── 图片预览
│ ├── ImageUpload/ # ── 图片上传
│ ├── Kdialog/ # ── K 线对话框(业务组件)
│ ├── NegativeDialog/ # ── 涨跌停对话框(业务组件)
│ ├── Pagination/ # ── 分页器
│ ├── PanThumb/ # ── 全景缩略图
│ ├── ParentView/ # ── 父级视图占位
│ ├── RightPanel/ # ── 右侧面板
│ ├── RightToolbar/ # ── 右侧工具栏
│ ├── RuoYi/ # ── 外部链接Doc / Git
│ ├── Screenfull/ # ── 全屏切换
│ ├── SizeSelect/ # ── 组件尺寸选择
│ ├── SvgIcon/ # ── SVG 图标封装
│ ├── ThemePicker/ # ── 主题选择器
│ ├── TopNav/ # ── 顶部导航
│ ├── TrendStocksDialog/ # ── 板块趋势对话框(业务)
│ └── iFrame/ # ── 内嵌 iframe
├── layout/ # 布局框架
│ ├── index.vue # ── 主布局容器
│ ├── components/
│ │ ├── AppMain.vue # ── 主内容区(含 keep-alive
│ │ ├── Navbar.vue # ── 顶部导航栏
│ │ ├── Sidebar/ # ── 侧边栏
│ │ │ ├── index.vue / SidebarItem.vue / Item.vue / Link.vue / Logo.vue
│ │ ├── TagsView/ # ── 标签页视图
│ │ │ ├── index.vue / ScrollPane.vue
│ │ ├── Settings/ # ── 系统设置面板
│ │ └── InnerLink/ # ── 内部链接
│ └── mixin/ResizeHandler.js # ── 响应式 Mixin
├── views/ # 页面视图(按业务模块)
│ ├── dashboard/ # ── 仪表盘5 个图表组件)
│ ├── system/ # ── 系统管理8 个子模块)
│ ├── booksystem/ # ── 账本系统6 个子模块)
│ ├── stocksystem/ # ── 股票系统8 个子模块)
│ ├── monitor/ # ── 系统监控7 个子模块)
│ ├── tool/ # ── 系统工具(代码生成等)
│ ├── error/ # ── 错误页面
│ ├── login.vue / register.vue # ── 认证页面
│ └── index.vue # ── 首页
```
### 1.2 分类统计
| 分类 | 组件/模块数量 | 说明 |
|------|-------------|------|
| **通用 UI 组件** | 10 | Breadcrumb, Pagination, SvgIcon, Hamburger, Screenfull, SizeSelect, ThemePicker, PanThumb, ParentView, iFrame |
| **业务组件** | 4 | Kdialog, NegativeDialog, TrendStocksDialog, RightToolbar |
| **功能组件** | 7 | Crontab(9), DictTag, DictData, Editor, FileUpload, ImageUpload, ImagePreview, IconSelect, HeaderSearch |
| **布局组件** | 8 | Layout, AppMain, Navbar, Sidebar(5), TagsView(2), Settings, InnerLink |
| **视图模块** | 6 大模块 | dashboard, system, booksystem, stocksystem, monitor, tool |
| **视图页面总数** | ~40 个 | 包含子页面和嵌套路由页面 |
### 1.3 组件复用程度分析
**高复用组件**(被全局注册或在多个视图中引用):
- `Pagination` — 几乎所有列表页面
- `DictTag` — 所有涉及字典展示的页面
- `RightToolbar` — 所有 CRUD 列表页
- `Editor` / `FileUpload` / `ImageUpload` — 表单页面
- `Breadcrumb` — 布局层全局使用
- `SvgIcon` — 全局图标渲染基础组件
**中复用组件**(特定业务场景):
- `Crontab` — 仅定时任务模块
- `Kdialog` / `TrendStocksDialog` / `NegativeDialog` — 股票业务模块
- `HeaderSearch` — 仅 Navbar 使用
**低复用组件**(单一职责或一次性使用):
- `PanThumb` — 仅用户个人资料页
- `RightPanel` — 仅布局设置入口
- `RuoYi/Doc` / `RuoYi/Git` — 外部链接
### 1.4 组件职责分析
| 组件 | 职责单一性 | 评价 |
|------|----------|------|
| Crontab | 一般 | 9 个子组件拆分合理,但耦合度较高 |
| Kdialog / NegativeDialog | 低 | 业务逻辑与 UI 混合,体积过大 |
| Dashboard 图表组件 | 好 | 每个图表独立文件,职责清晰 |
| Layout 子组件 | 好 | Sidebar、Navbar、TagsView 分离清晰 |
---
## 2. 路由与权限控制
### 2.1 路由结构图
```
路由系统
├── 静态路由 (constantRoutes) — 始终可访问
│ ├── /redirect/:path — 重定向页
│ ├── /login — 登录页
│ ├── /register — 注册页
│ ├── /404 — 404 错误页
│ ├── /401 — 401 错误页
│ ├── / (首页) — Dashboard
│ └── /user/profile — 个人中心
├── 动态路由 (dynamicRoutes) — 按权限加载
│ ├── /system/user-auth/role/:userId — 分配角色 [system:user:edit]
│ ├── /system/role-auth/user/:roleId — 分配用户 [system:role:edit]
│ ├── /system/dict-data/index/:dictId — 字典数据 [system:dict:list]
│ ├── /monitor/job-log — 调度日志 [monitor:job:list]
│ └── /tool/gen-edit — 修改生成配置 [tool:gen:edit]
└── 服务端路由 — 登录后从后端获取
├── filterAsyncRouter() ── 转换为组件对象
├── filterDynamicRoutes() ── 权限过滤
└── router.addRoutes() ── 动态注册
```
### 2.2 权限加载流程
```
用户登录
├─ 1. 输入用户名/密码/验证码
├─ 2. 调用 POST /login → 获取 token → 存入 Cookie (Admin-Token)
├─ 3. 路由守卫 beforeEach 拦截
│ │
│ ├─ 有 token ? ──NO──→ 跳转 /login
│ │ │
│ │ YES
│ │ │
│ │ ├─ roles.length === 0 ? ──NO──→ next()
│ │ │ │
│ │ │ YES
│ │ │ │
│ │ │ ├─ GetInfo() → 获取用户信息roles + permissions
│ │ │ │
│ │ │ └─ GenerateRoutes() → 请求 /getRouters
│ │ │ │
│ │ │ ├─ filterAsyncRouter() → 字符串转组件
│ │ │ ├─ filterDynamicRoutes() → 权限过滤
│ │ │ └─ router.addRoutes() → 注册路由
│ │ │
│ │ └─ next({ ...to, replace: true }) → 重新导航
│ │
│ └─ 无 token → 白名单 ? ──YES──→ next()
│ │
│ NO
│ │
│ 跳转 /login?redirect=xxx
└─ 4. 页面渲染 → 动态菜单展示
```
### 2.3 权限控制机制
项目采用 **三层权限控制**
| 层级 | 实现方式 | 代码位置 |
|------|---------|---------|
| **路由级** | 路由守卫 + 服务端返回菜单 | `src/permission.js` + `src/store/modules/permission.js` |
| **页面级** | `v-hasPermi` / `v-hasRole` 指令 | `src/directive/permission/` |
| **按钮级** | `$auth.hasPermi()` / `$auth.hasRole()` | `src/plugins/auth.js` |
**权限标识格式**`模块:资源:操作`(如 `system:user:add`
**超级权限**`*:*:*`(拥有所有权限)
**超级角色**`admin`(拥有所有角色权限)
### 2.4 路由懒加载
```javascript
// 开发环境:使用 require 实现
if (process.env.NODE_ENV === 'development') {
return (resolve) => require([`@/views/${view}`], resolve)
}
// 生产环境:使用 import 实现
else {
return () => import(`@/views/${view}`)
}
```
---
## 3. 状态管理分析
### 3.1 Vuex 模块图
```
Vuex Store
├── getters.js # 统一访问入口
│ ├── sidebar, size, device → state.app
│ ├── visitedViews, cachedViews → state.tagsView
│ ├── token, avatar, name, roles → state.user
│ └── permission_routes, topbarRouters, sidebarRouters → state.permission
├── modules/app.js # 应用级状态
│ ├── sidebar { opened, withoutAnimation }
│ ├── device ('desktop' | 'mobile')
│ └── size ('medium' | 'small' | 'mini')
│ └── 持久化Cookies (sidebarStatus, size)
├── modules/user.js # 用户状态
│ ├── token, name, avatar
│ ├── roles: string[]
│ ├── permissions: string[]
│ └── Actions: Login, GetInfo, LogOut, FedLogOut
│ └── 持久化Cookies (Admin-Token)
├── modules/permission.js # 权限/路由状态
│ ├── routes, addRoutes — 完整路由表 / 动态路由
│ ├── defaultRoutes, topbarRouters, sidebarRouters
│ └── Actions: GenerateRoutes
├── modules/tagsView.js # 标签页状态
│ ├── visitedViews: [] — 已访问的标签页
│ ├── cachedViews: [] — 被 keep-alive 缓存的页面
│ └── Actions: addView, delView, delOthersViews, delAllViews,
│ delRightTags, delLeftTags
└── modules/settings.js # 系统设置状态
├── theme, sideTheme, showSettings
├── topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle
└── 持久化localStorage ('layout-setting')
```
### 3.2 数据流向
```
┌──────────────┐
│ Vue 组件 │
└──────┬───────┘
│ dispatch
┌──────────────┐
│ Actions │ ──→ 异步操作API 调用)
└──────┬───────┘
│ commit
┌──────────────┐
│ Mutations │ ──→ 同步修改 state
└──────┬───────┘
┌──────────────┐
│ State │ ──→ 响应式数据
└──────┬───────┘
┌──────┴───────┐
│ Getters │ ──→ 计算属性
└──────┬───────┘
┌──────┴───────┐
│ Vue 组件 │ ←── 重新渲染
└──────────────┘
```
### 3.3 状态持久化
| 状态 | 存储方式 | 键名 | 说明 |
|------|---------|------|------|
| 用户 Token | Cookie | `Admin-Token` | 会话认证 |
| 侧边栏状态 | Cookie | `sidebarStatus` | 展开/折叠 |
| 组件尺寸 | Cookie | `size` | Element UI 默认尺寸 |
| 布局设置 | localStorage | `layout-setting` | JSON 对象(主题、顶栏、标签页等) |
> **注意**Vuex 本身无持久化插件,数据分散在 Cookie 和 localStorage 中手动读写。
---
## 4. API 调用层
### 4.1 Axios 封装架构
```
src/api/ # API 模块目录
├── booksystem/ # ── 账本系统 API6 个文件)
├── stocksystem/ # ── 股票系统 API9 个文件)
├── system/ # ── 系统管理 API7 个文件 + dict 子目录)
├── monitor/ # ── 系统监控 API7 个文件)
├── tool/ # ── 系统工具 API
├── login.js # ── 登录/获取信息/退出
├── menu.js # ── 获取动态路由
└── index.js # ── 通用 API
src/utils/request.js # Axios 实例封装
├── baseURL: process.env.VUE_APP_BASE_API
├── timeout: 60000ms (60秒)
├── Content-Type: application/json;charset=utf-8
└── download() 方法 # ── 文件下载封装
```
### 4.2 拦截器实现
#### 请求拦截器Request Interceptor
```
请求发出
├─ 1. 检查是否需要 tokenconfig.headers.isToken === false 可跳过)
├─ 2. 自动添加 Authorization: Bearer {token}
├─ 3. GET 请求 params 序列化tansParams 处理)
│ └─ 将 ?a=1&b=2 拼接到 URL清空 config.params
└─ 4. 发送请求
```
#### 响应拦截器Response Interceptor
```
收到响应
├─ 解析业务状态码 res.data.code
├─ code === 401 → MessageBox 弹窗 → 确认后 LogOut → 跳首页
├─ code === 500 → Message.error(msg) → Promise.reject
├─ code !== 200 → Notification.error({ title: msg }) → Promise.reject
├─ code === 200 → 返回 res.data
└─ HTTP 错误捕获error
├─ Network Error → "后端接口连接异常"
├─ timeout → "系统接口请求超时"
├─ status code 4xx/5xx → "系统接口 XXX 异常"
└─ Message.error(message, duration: 5s)
```
### 4.3 错误处理机制
| 错误类型 | 处理方式 | 用户体验 |
|---------|---------|---------|
| 401 未授权 | 弹窗确认 → 退出登录 | 强提醒,需用户操作 |
| 500 服务端错误 | Message 提示 | 轻量提示,自动消失 |
| 其他业务错误 | Notification 通知 | 右上角通知 |
| 网络异常 | Message 提示5 秒) | 长时提示 |
| 请求超时 | Message 提示5 秒) | 长时提示 |
| 下载失败 | 解析 blob 中的 JSON 错误信息 | 自动处理登录态失效 |
### 4.4 错误码映射
通过 `src/utils/errorCode.js` 实现业务码到提示文本的映射,支持:
- 预定义错误码映射
- 默认兜底文本
- 服务端返回的自定义 msg
---
## 5. 存在的问题
### 5.1 问题清单
| # | 问题 | 影响 | 严重程度 |
|---|------|------|---------|
| 1 | **Vue 2 + Vue CLI 技术栈老化**Vue 2 已于 2023 年底停止官方维护Vue Router 3、Vuex 3、Vue CLI 4 均为旧版本,存在安全风险且无法使用 Composition API 等现代特性 | 长期维护成本高,无法使用新生态,第三方库兼容性逐步下降 | 🔴 高 |
| 2 | **权限逻辑重复实现**:权限校验在 4 个位置分别实现(`plugins/auth.js`、`utils/permission.js`、`directive/permission/hasPermi.js`、`directive/permission/hasRole.js`),逻辑高度重复但存在细微差异,如超级权限标识散落在各处 | 维护困难,修改权限规则需同时修改多处,易遗漏导致权限漏洞 | 🟡 中 |
| 3 | **状态持久化分散且不统一**Token 存 Cookie、sidebar 状态存 Cookie、布局设置存 localStorage手动读写无统一管理无 vuex-persistedstate 等插件,刷新后部分状态可能不一致 | 状态管理混乱,调试困难,存在不同步风险 | 🟡 中 |
| 4 | **业务组件与通用组件混放**`Kdialog`、`TrendStocksDialog`、`NegativeDialog` 等强业务耦合组件放在 `src/components/` 通用组件目录,且通过全局注册加载,增加了首屏包体积 | 首屏加载变慢,通用组件库被业务代码污染,不利于复用 | 🟡 中 |
| 5 | **视图页面高度模板化**`system/`、`booksystem/`、`stocksystem/` 下的 CRUD 页面结构高度相似(搜索表单 + 表格 + 分页 + 弹窗),但未抽取为可配置的高阶组件或 Schema 驱动表单 | 代码冗余度高,新增 CRUD 页面工作量大,修改通用逻辑需改多个文件 | 🟡 中 |
| 6 | **生产/开发环境使用不同的懒加载方式**:开发环境用 `require()`,生产环境用 `import()`,两份逻辑增加了维护复杂度,且开发环境无法享受代码分割的优化效果 | 开发构建产物大,热更新慢,行为不一致可能导致线上问题 | 🟢 低 |
| 7 | **响应拦截器中 401 处理使用同步 location.href 跳转**`location.href = '/index'` 是硬跳转,绕过了 Vue Router 的导航守卫和状态清理流程 | 可能导致 Vuex 状态未完全清理、标签页数据残留 | 🟢 低 |
| 8 | **缺少统一的请求重试机制**:网络波动或后端短暂不可用时,所有请求直接失败,无自动重试策略 | 用户体验下降,偶发性网络问题导致操作失败 | 🟢 低 |
### 5.2 优化建议
| 优先级 | 建议 | 预期收益 |
|--------|------|---------|
| P0 | 规划升级至 Vue 3 + Vite + Pinia | 获得性能提升、Composition API、更好的类型支持 |
| P1 | 抽取统一的权限校验核心模块,消除重复代码 | 降低维护成本,消除权限逻辑不一致风险 |
| P1 | 引入 vuex-persistedstate 或自定义统一持久化层 | 状态管理规范化,减少手动读写 Cookie/LocalStorage |
| P2 | 将业务组件移至对应业务模块目录,或采用按需异步加载 | 减少首屏包体积,保持通用组件库纯净 |
| P2 | 抽取 CRUD 页面为 Schema 驱动的高阶组件 | 减少 60%+ 的 CRUD 页面代码量 |
| P3 | 统一开发/生产的组件加载方式 | 简化代码,提升开发体验 |
| P3 | 401 处理改用 router.replace() 代替 location.href | 状态清理更完整,避免残留数据 |
| P3 | 在 request.js 中添加可配置的重试机制 | 提升网络不稳定场景下的用户体验 |
---
## 附录:技术栈概览
| 类别 | 技术 | 版本 |
|------|------|------|
| 核心框架 | Vue | 2.6.12 |
| UI 框架 | Element UI | 2.15.6 |
| 路由 | Vue Router | 3.4.9 |
| 状态管理 | Vuex | 3.6.0 |
| HTTP 客户端 | Axios | 0.24.0 |
| 构建工具 | Vue CLI | 4.4.6 |
| CSS 预处理器 | Sass | 1.32.13 |
| 图表库 | ECharts | 4.9.0 |
| 富文本 | Quill | 1.3.7 |
| 代码高亮 | highlight.js | 9.18.5 |
| 拖拽排序 | SortableJS / vuedraggable | 1.10.2 / 2.24.3 |
| SVG 图标 | svg-sprite-loader | 5.1.1 |
| 加密 | jsencrypt | 3.2.1 |
| 进度条 | NProgress | 0.2.0 |
| Cookie | js-cookie | 3.0.1 |

@ -0,0 +1,500 @@
# RuoYi-Vue 优化建议清单
> 文档版本v1.0 | 生成日期2026-06-28
> 数据来源:前端架构分析、后端架构分析、前后端交互规范、数据库结构分析
> 问题总数36 条(前端 8 条、后端 8 条、交互 10 条、数据库 10 条)
---
## P0 - 高优先级(立即实施)
> 影响安全、稳定性、核心功能,建议在 1-2 周内完成。
### 1. fastjson 版本存在已知安全漏洞
- **问题描述**: 项目使用 fastjson 1.2.79,该版本存在多个已知 CVE 漏洞(如 CVE-2022-25845攻击者可利用反序列化漏洞执行远程代码。
- **影响范围**: 全系统所有使用 JSON 序列化/反序列化的接口包括登录、CRUD 操作、文件上传等。
- **推荐方案**: 升级至 fastjson22.x 系列)或替换为 Jackson/Gson。fastjson2 API 兼容性好,迁移成本较低。
- **实施优先级**: P0
- **实施步骤**:
1. 在根 `pom.xml` 中将 fastjson 版本改为 fastjson2 依赖
2. 全局搜索 `com.alibaba.fastjson` 导入,替换为 `com.alibaba.fastjson2`
3. 重点检查 `AjaxResult`、`TokenService`、`LogAspect` 中的 JSON 操作
4. 运行全量回归测试,确保序列化行为一致
5. 安全扫描验证漏洞已修复
### 2. 业务表主键使用 double 类型
- **问题描述**: 所有业务表stocks、account、operations、statistics 等)的主键使用 `double` 类型,浮点数作为主键可能导致精度丢失、索引效率低下、比较运算异常。
- **影响范围**: 股票业务表8 万+ 记录)、账户记账表等所有业务模块的数据查询和关联操作。
- **推荐方案**: 将所有业务表主键改为 `bigint(20) NOT NULL AUTO_INCREMENT`
- **实施优先级**: P0
- **实施步骤**:
1. 备份 `nstocks``ry` 数据库中涉及的业务表
2. 按依赖顺序执行 `ALTER TABLE` 语句修改主键类型(先子表后父表)
3. 更新对应的 Domain 实体类,将 `id` 字段类型从 `Double` 改为 `Long`
4. 更新 Mapper XML 中的主键引用
5. 验证数据完整性,运行全量测试
### 3. trade_dates 表无主键
- **问题描述**: `trade_dates` 表(交易日历)没有任何主键或唯一索引,无法保证数据唯一性,数据库复制和备份可能出错。
- **影响范围**: 所有依赖交易日历的业务查询,包括股票行情、涨跌停板等模块。
- **推荐方案**: 将日期字段设为主键。
- **实施优先级**: P0
- **实施步骤**:
1. 检查 `trade_dates` 表结构,确认日期字段名称
2. 清理可能存在的重复日期记录
3. 执行 `ALTER TABLE trade_dates ADD PRIMARY KEY (date);`
4. 验证查询功能正常
### 4. 核心业务表缺少必要索引
- **问题描述**: `stocks`8 万+ 记录)、`stock_index`、`stock_financial`、`operations` 等大表仅有主键索引高频查询字段code、trade_day、user_id完全无索引导致全表扫描。
- **影响范围**: 所有股票行情查询、操作记录查询、持仓统计查询,数据量增长后性能急剧下降。
- **推荐方案**: 为高频查询字段添加复合索引和单列索引。
- **实施优先级**: P0
- **实施步骤**:
1. 分析慢查询日志,确认高频查询模式
2. 为 `stocks` 表添加 `idx_code_trade(code, trade_day)``idx_trade_day(trade_day)`
3. 为 `stock_index` 表添加 `idx_code_trade(code, trade_day)`
4. 为 `operations` 表添加 `idx_code_trade(code, trade_day)``idx_user_id(user_id)`
5. 为 `statistics_remain` 表添加 `idx_code_trade(code, trade_day)``idx_user_id(user_id)`
6. 使用 `EXPLAIN` 验证索引生效
### 5. 排序字段存在 SQL 注入风险
- **问题描述**: 分页排序参数直接从请求参数读取并拼接到 SQL ORDER BY 子句中,虽使用 `SqlUtil.escapeOrderBySql()` 正则过滤,但正则表达式级别的防护存在被绕过的可能。
- **影响范围**: 所有使用分页排序的列表接口(约 50+ 个),攻击者可能通过精心构造的排序参数执行 SQL 注入。
- **推荐方案**: 使用白名单机制替代正则过滤,仅允许传入已知的合法字段名。
- **实施优先级**: P0
- **实施步骤**:
1. 在 `TableSupport``SqlUtil` 中实现排序字段白名单校验
2. 为每个实体定义允许排序的字段列表
3. 在 `startPage()` 方法中增加白名单校验逻辑
4. 非法排序参数直接拒绝并记录安全日志
5. 使用 SQL 注入扫描工具验证修复效果
### 6. 响应格式不统一
- **问题描述**: `AjaxResult`(普通 CRUD 接口)使用 `data` 字段返回数据,而 `TableDataInfo`(分页列表接口)使用 `rows` + `total` 字段,前端需要分别处理两种完全不同的响应结构。
- **影响范围**: 前端所有 API 调用180+ 个接口涉及两种响应格式处理,增加认知负担和维护成本。
- **推荐方案**: 统一响应结构,推荐在 `TableDataInfo` 中也使用 `data` 字段包裹分页数据,或引入统一的 `ApiResponse<T>` 泛型响应体。
- **实施优先级**: P0
- **实施步骤**:
1. 定义统一响应基类 `ApiResponse<T>`,包含 `code`、`msg`、`data`
2. 分页响应改为 `data: { list: [], total: 0 }` 结构
3. 前端 `request.js` 响应拦截器统一处理单一结构
4. 逐步迁移现有接口(可先从新接口开始)
5. 更新前端所有列表页面的数据提取逻辑
### 7. ${params.dataScope} SQL 拼接注入风险
- **问题描述**: MyBatis XML 中使用 `${params.dataScope}` 直接拼接 SQL 片段,虽然 `DataScopeAspect` 在执行前会清空参数,但若切面执行顺序被改变或绕过,将直接导致 SQL 注入。
- **影响范围**: 所有使用 `@DataScope` 注解的 Service 方法,涉及用户、角色、部门等核心数据查询。
- **推荐方案**: 使用参数化方案替代直接 SQL 拼接,或在切面层进行严格的 SQL 片段校验。
- **实施优先级**: P0
- **实施步骤**:
1. 在 `DataScopeAspect` 中增加 SQL 片段合法性校验(仅允许特定模式)
2. 考虑将数据权限过滤改为 MyBatis 插件方式,在 SQL 解析阶段注入
3. 为关键查询添加安全审计日志
4. 进行安全渗透测试验证
---
## P1 - 中优先级(近期实施)
> 影响开发效率、可维护性,建议在 1-2 个月内完成。
### 8. 使用已废弃的 WebSecurityConfigurerAdapter
- **问题描述**: Spring Security 5.7+ 已废弃 `WebSecurityConfigurerAdapter`,项目当前继承该类进行安全配置,未来升级 Spring Boot 时将无法兼容。
- **影响范围**: `SecurityConfig.java` 安全配置模块,影响所有认证和授权流程。
- **推荐方案**: 改用 `SecurityFilterChain` Bean 配置方式。
- **实施优先级**: P1
- **实施步骤**:
1. 移除 `WebSecurityConfigurerAdapter` 继承
2. 将 `configure(HttpSecurity http)` 方法改为 `@Bean SecurityFilterChain` 方法
3. 将 `configure(WebSecurity web)` 方法改为 `WebSecurityCustomizer` Bean
4. 使用 `Lambda DSL` 风格编写安全规则(`.authorizeHttpRequests()` 替代 `.authorizeRequests()`
5. 验证登录、权限校验、匿名访问等功能正常
### 9. Token 解析异常被静默吞没
- **问题描述**: `TokenService.getLoginUser()` 方法中(第 72-74 行catch 块为空JWT Token 解析异常被完全忽略,导致问题难以排查。
- **影响范围**: 认证模块,所有 Token 验证失败的场景(过期、篡改、格式错误等)均无日志记录。
- **推荐方案**: 至少记录 WARN 级别日志,包含异常信息和请求上下文。
- **实施优先级**: P1
- **实施步骤**:
1. 在 `TokenService` 中注入 Logger
2. 在 catch 块中添加 `log.warn("Token 解析失败: {}", e.getMessage())`
3. 可选:记录请求 IP 和 User-Agent 便于安全审计
4. 验证日志输出正常
### 10. 缺少统一 DTO/VO 分层
- **问题描述**: Domain 实体直接暴露给 Controller 层(如 `SysUser` 同时用于数据库映射和接口响应),密码等敏感字段需额外处理,字段暴露风险高。
- **影响范围**: 全系统所有 Controller 接口,特别是用户管理、角色管理等敏感模块。
- **推荐方案**: 引入独立的 DTO数据传输对象和 VO视图对象使用 MapStruct 或手动转换器进行映射。
- **实施优先级**: P1
- **实施步骤**:
1. 在 `ruoyi-common` 中创建 `dto/``vo/`
2. 为敏感实体SysUser、SysRole 等)创建对应的 DTO/VO
3. Controller 层使用 DTO 接收请求、VO 返回响应
4. 使用 `@JsonIgnore` 或字段排除确保敏感字段不暴露
5. 逐步迁移现有接口
### 11. Vue 2 + Vue CLI 技术栈老化
- **问题描述**: Vue 2 已于 2023 年底停止官方维护Vue Router 3、Vuex 3、Vue CLI 4 均为旧版本,存在安全风险且无法使用 Composition API、`<script setup>` 等现代特性。
- **影响范围**: 前端全量代码40+ 页面、22 个组件组),长期维护成本高,第三方库兼容性逐步下降。
- **推荐方案**: 规划升级至 Vue 3 + Vite + Pinia可采用渐进式迁移策略。
- **实施优先级**: P1
- **实施步骤**:
1. 使用 `@vue/compat` 兼容性构建进行渐进式迁移
2. 先将构建工具从 Vue CLI 迁移至 Vite可独立进行
3. 将 Vuex 3 迁移至 PiniaAPI 相似,迁移成本低)
4. 逐个组件迁移至 Composition API
5. 最终移除 `@vue/compat`,完成纯 Vue 3 升级
### 12. 权限逻辑重复实现
- **问题描述**: 权限校验在 4 个位置分别实现(`plugins/auth.js`、`utils/permission.js`、`directive/permission/hasPermi.js`、`directive/permission/hasRole.js`),逻辑高度重复但存在细微差异,超级权限标识 `*:*:*` 散落在各处。
- **影响范围**: 前端所有权限校验场景,修改权限规则需同时修改多处,易遗漏导致权限漏洞。
- **推荐方案**: 抽取统一的权限校验核心模块,其他模块基于该核心实现。
- **实施优先级**: P1
- **实施步骤**:
1. 创建 `src/utils/permission-core.js` 作为唯一权限判断入口
2. 将超级权限标识、角色标识等常量集中管理
3. `plugins/auth.js`、`directive/permission/` 均调用核心模块
4. 编写单元测试覆盖所有权限判断场景
5. 移除重复代码
### 13. 状态持久化分散且不统一
- **问题描述**: Token 存 Cookie、sidebar 状态存 Cookie、布局设置存 localStorage手动读写无统一管理无持久化插件刷新后部分状态可能不一致。
- **影响范围**: 前端全局状态管理涉及登录态、UI 设置、侧边栏状态等。
- **推荐方案**: 引入 `vuex-persistedstate` 或自定义统一持久化层。
- **实施优先级**: P1
- **实施步骤**:
1. 安装 `vuex-persistedstate``js-cookie`
2. 在 Vuex Store 中配置统一持久化策略
3. 按模块配置不同的存储介质Token → CookieUI → localStorage
4. 移除手动读写 Cookie/LocalStorage 的散点代码
5. 验证刷新后状态恢复正常
### 14. 前后端 API 路径不一致
- **问题描述**: 部分接口前端调用路径与后端映射不一致(如 `/querylist` vs `/list``StocksController` 中存在被注释的权限控制(`@PreAuthorize` 被注释),存在安全漏洞风险。
- **影响范围**: 股票系统模块为主,涉及约 63 个接口。
- **推荐方案**: 清理未使用的接口,补全权限控制,统一前后端路径命名规范。
- **实施优先级**: P1
- **实施步骤**:
1. 逐个比对前端 API 文件与后端 Controller 路径映射
2. 恢复被注释的 `@PreAuthorize` 注解
3. 清理前端未使用的 API 调用
4. 建立前后端路径一致性检查机制(可写脚本自动化)
5. 制定 API 命名规范文档
### 15. 分页参数隐式依赖
- **问题描述**: 分页参数通过 `ServletUtils.getParameter()` 从 HttpServletRequest 中隐式读取,而非通过 `@RequestParam` 显式声明,导致 API 文档Swagger无法自动生成分页参数说明。
- **影响范围**: 所有使用 `BaseController.startPage()` 的分页接口(约 50+ 个)。
- **推荐方案**: 使用 `@RequestParam` 显式声明分页参数,或引入 `PageRequest` 对象作为 Controller 方法参数。
- **实施优先级**: P1
- **实施步骤**:
1. 创建 `PageRequest` 对象(包含 pageNum、pageSize、orderByColumn、isAsc
2. 修改 `BaseController.startPage()` 支持显式参数
3. 为 Controller 分页接口添加 `@RequestParam``PageRequest` 参数
4. 补充 Swagger 注解生成分页参数文档
5. 验证分页功能正常
### 16. 接口文档缺失
- **问题描述**: 项目虽引入 Swagger 依赖,但所有业务 Controller 均未添加 Swagger 注解(`@Api`、`@ApiOperation`、`@ApiParam`),无法自动生成 API 文档,前后端协作效率低。
- **影响范围**: 全部 180+ 个 API 端点,影响前后端协作、测试、接口维护。
- **推荐方案**: 补充 Swagger/OpenAPI 注解,或接入 SpringDoc + Knife4j 增强文档体验。
- **实施优先级**: P1
- **实施步骤**:
1. 升级 Swagger 至 SpringDocOpenAPI 3.0
2. 接入 Knife4j 增强 UI
3. 为每个 Controller 添加 `@Tag`/`@Operation` 注解
4. 为关键参数添加 `@Parameter`/`@Schema` 注解
5. 配置文档访问路径和权限控制
### 17. 无逻辑外键导致数据一致性风险
- **问题描述**: 系统管理表和业务表之间没有物理外键约束,全部采用逻辑关联,删除用户/角色时关联表sys_user_role、sys_role_menu 等)数据可能残留。
- **影响范围**: 用户管理、角色管理、菜单管理等核心模块,长期运行后可能产生脏数据。
- **推荐方案**: 在 Service 层确保级联删除逻辑完整,或引入数据库层面的逻辑外键校验。
- **实施优先级**: P1
- **实施步骤**:
1. 审查所有删除操作(用户、角色、部门、菜单)的级联清理逻辑
2. 补充缺失的关联表清理代码
3. 在事务中确保原子性
4. 添加定期数据一致性校验脚本
5. 可选:在数据库层面添加外键约束(需评估性能影响)
### 18. find_in_set 查询无法利用索引
- **问题描述**: 部门树查询使用 `find_in_set(#{deptId}, ancestors)` 进行字符串匹配,无法使用任何索引,数据量增长后性能线性下降。
- **影响范围**: 所有涉及部门层级查询的场景(用户列表按部门过滤、数据权限范围计算等)。
- **推荐方案**: 使用闭包表Closure Table或物化路径的整数数组类型替代字符串匹配。
- **实施优先级**: P1
- **实施步骤**:
1. 评估闭包表方案:新增 `sys_dept_closure` 表存储祖先后代关系
2. 或使用 MySQL 8.0+ 的 JSON 数组 + JSON 函数替代 `find_in_set`
3. 更新 `DataScopeAspect` 中的 SQL 生成逻辑
4. 在部门增删改时同步维护闭包表/路径
5. 使用 `EXPLAIN` 验证查询使用索引
---
## P2 - 低优先级(长期规划)
> 优化类建议,可在 3-6 个月内逐步实施。
### 19. 业务 Controller 缺少 @Log 注解
- **问题描述**: `book-system` 下的 Controller`AccountController`)部分接口缺少 `@Log` 操作日志注解,审计不完整。
- **影响范围**: 账务系统模块的操作审计,安全合规场景下无法追溯操作记录。
- **推荐方案**: 为所有业务 Controller 的增删改接口补充 `@Log` 注解。
- **实施优先级**: P2
- **实施步骤**:
1. 梳理 `book-system``stock-system` 下所有 Controller
2. 为每个增删改方法添加 `@Log(title = "模块名", businessType = BusinessType.XXX)`
3. 验证操作日志正常写入
4. 建立代码审查规则,新接口必须包含 `@Log`
### 20. Service 层直接注入 Mapper 实现类
- **问题描述**: `SysUserServiceImpl` 中使用 `@Autowired private SysUserMapper userMapper` 直接注入实现类,不符合面向接口编程原则。
- **影响范围**: 全系统所有 Service 实现类,影响代码规范和可测试性。
- **推荐方案**: 改为注入 Mapper 接口类型。
- **实施优先级**: P2
- **实施步骤**:
1. 全局搜索 `@Autowired` 注入 Mapper 的代码
2. 将注入类型从实现类改为接口MyBatis 代理本身实现接口)
3. 使用 `@Resource` 或构造函数注入替代字段注入(可选)
### 21. 缺少接口版本控制
- **问题描述**: 所有 API 路径无版本号(如 `/api/v1/system/user`),未来接口升级时无法平滑过渡,可能导致前端兼容问题。
- **影响范围**: 全系统 API 设计,影响未来接口迭代和客户端兼容。
- **推荐方案**: 引入 URL 路径版本控制(`/api/v1/`)或 Header 版本控制。
- **实施优先级**: P2
- **实施步骤**:
1. 制定 API 版本管理策略(推荐 URL 路径方式)
2. 为所有现有接口添加 `/api/v1/` 前缀
3. 前端 `request.js``baseURL` 中添加版本前缀
4. 建立接口版本废弃机制
5. 文档中注明版本策略
### 22. 生产/开发环境使用不同的懒加载方式
- **问题描述**: 开发环境使用 `require()` 加载组件,生产环境使用 `import()`,两份逻辑增加维护复杂度,且开发环境无法享受代码分割优化。
- **影响范围**: 前端路由懒加载模块,影响开发构建产物大小和热更新速度。
- **推荐方案**: 统一开发/生产环境的组件加载方式,均使用动态 `import()`
- **实施优先级**: P2
- **实施步骤**:
1. 移除 `process.env.NODE_ENV` 条件分支
2. 统一使用 `() => import('@/views/${view}')` 方式
3. 验证开发环境热更新正常
4. 对比构建产物大小变化
### 23. 响应拦截器 401 处理使用 location.href 硬跳转
- **问题描述**: 401 未授权时使用 `location.href = '/index'` 硬跳转,绕过 Vue Router 导航守卫和状态清理流程,可能导致 Vuex 状态未完全清理、标签页数据残留。
- **影响范围**: 用户登录态过期后的退出流程,影响状态清理完整性。
- **推荐方案**: 改用 `router.replace()` 跳转,确保走完整的导航守卫流程。
- **实施优先级**: P2
- **实施步骤**:
1. 在 `request.js` 中导入 router 实例
2. 将 `location.href = '/index'` 改为 `router.replace('/login')`
3. 确保 `LogOut` action 在跳转前被正确调用
4. 验证状态清理完整Token、tagsView、缓存等
### 24. 缺少统一请求重试机制
- **问题描述**: 网络波动或后端短暂不可用时,所有请求直接失败,无自动重试策略,用户体验下降。
- **影响范围**: 所有 API 请求,特别是网络不稳定场景。
- **推荐方案**: 在 `request.js` 中添加可配置的重试机制(如 `axios-retry`)。
- **实施优先级**: P2
- **实施步骤**:
1. 安装 `axios-retry` 或自行实现重试拦截器
2. 配置重试条件(仅网络错误和超时,不重试 4xx/5xx
3. 设置重试次数(建议 2-3 次)和退避策略
4. 为特定请求提供 `maxRetries` 配置覆盖
5. 验证重试行为符合预期
### 25. GET 请求参数转换方式不标准
- **问题描述**: 前端 GET 请求的 `params` 通过 `tansParams` 函数手动拼接为 URL 字符串,而非使用 axios 原生的 `paramsSerializer`,可能导致特殊字符编码问题。
- **影响范围**: 所有 GET 请求,涉及中文、特殊字符的查询参数。
- **推荐方案**: 使用 axios 的 `paramsSerializer` 配置进行参数序列化。
- **实施优先级**: P2
- **实施步骤**:
1. 在 axios 实例配置中添加 `paramsSerializer`
2. 使用 `qs.stringify()``URLSearchParams` 替代手动拼接
3. 移除请求拦截器中的 `tansParams` 逻辑
4. 验证中文、特殊字符参数正常传递
### 26. 批量删除路径参数格式不一致
- **问题描述**: 部分 Controller 使用 `@DeleteMapping("/{ids}")` 接收 `Long[]`URL 中数组传递格式(逗号分隔 vs 多次参数)缺乏统一规范,前端需手动拼接字符串。
- **影响范围**: 所有支持批量删除的接口,前端需要适配不同格式。
- **推荐方案**: 统一使用 `@DeleteMapping` + `@RequestParam List<Long> ids``@RequestBody` 传递数组。
- **实施优先级**: P2
- **实施步骤**:
1. 统一所有批量删除接口为 `DELETE /{module}/{ids}``DELETE /{module}` + RequestBody
2. 前端封装统一的 `delByIds(url, ids)` 方法
3. 更新所有页面的批量删除调用
4. 补充接口文档说明
### 27. 大量调试代码未清理
- **问题描述**: `StocksController` 中存在大量 `System.out.println()` 调试输出(超过 50 处以及大段被注释的代码1000+ 行),污染日志且增加维护成本。
- **影响范围**: `stock-system` 模块,影响生产环境日志质量和代码可读性。
- **推荐方案**: 统一替换为 `logger.info/debug`,清理历史注释代码。
- **实施优先级**: P2
- **实施步骤**:
1. 在 `StocksController` 中创建 Logger 实例
2. 将 `System.out.println()` 替换为 `log.debug()` 或删除
3. 清理大段注释代码1000+ 行)
4. 同样检查 `stock-system` 下其他 Controller
5. 建立代码审查规则,禁止 `System.out.println`
### 28. 缺少审计字段
- **问题描述**: 大部分业务表stocks、operations、statistics 等)缺少 `create_by`、`create_time`、`update_by`、`update_time` 审计字段,无法追溯数据变更历史。
- **影响范围**: 所有业务表的数据审计和问题排查。
- **推荐方案**: 为业务表添加标准审计字段,继承 `BaseEntity`
- **实施优先级**: P2
- **实施步骤**:
1. 为业务表 DDL 添加审计字段
2. 实体类继承 `BaseEntity` 或手动添加审计字段
3. 利用 MyBatis 拦截器或 AOP 自动填充审计字段
4. 历史数据补充默认值
### 29. 缺少软删除机制
- **问题描述**: 业务表stocks、operations 等)数据删除后直接物理删除,无法恢复,不符合审计要求。
- **影响范围**: 所有业务模块的数据管理,特别是财务和统计相关数据。
- **推荐方案**: 为业务表添加 `del_flag` 字段,实现软删除。
- **实施优先级**: P2
- **实施步骤**:
1. 为业务表添加 `del_flag char(1) default '0'` 字段
2. 实体类添加 `delFlag` 属性
3. Mapper XML 中所有查询添加 `del_flag = '0'` 条件
4. 删除操作改为更新 `del_flag = '2'`
5. 管理后台提供回收站/恢复功能(可选)
### 30. 大字段 TEXT/BLOB 无分离
- **问题描述**: `sys_notice.content`longblob、`operations.deal_logic`TEXT、`statistics.bz`TEXT等大文本字段与主表混合存储查询时即使不需要这些字段也会被加载影响性能。
- **影响范围**: 公告管理、操作记录、统计备注等模块的查询性能。
- **推荐方案**: 将大字段移至扩展表,主表仅保留轻量字段。
- **实施优先级**: P2
- **实施步骤**:
1. 为包含大字段的表创建 `_detail` 扩展表
2. 将 TEXT/BLOB 字段迁移至扩展表
3. 主表保留外键关联
4. 仅在需要详情时 JOIN 扩展表
5. 验证查询性能提升
### 31. HTTP 状态码与业务状态码混用
- **问题描述**: 后端 HTTP Response 的 Status Code 始终为 200真正的业务状态码在响应体 `code` 字段中,前端无法利用 HTTP 状态码进行路由/拦截逻辑,也与 RESTful 最佳实践不符。
- **影响范围**: 全局 API 设计,影响前端错误处理逻辑。
- **推荐方案**: 保持当前模式(业界常见做法)但在文档中明确说明;或逐步将 HTTP Status Code 与业务 code 对齐。
- **实施优先级**: P2
- **实施步骤**:
1. 在 API 文档中明确说明当前状态码策略
2. 评估 HTTP 状态码对齐的改造成本
3. 如选择对齐,修改 `GlobalExceptionHandler` 返回对应 HTTP 状态码
4. 前端响应拦截器适配新的状态码策略
### 32. 错误码体系不完整
- **问题描述**: 前端 `errorCode.js` 仅映射了 401/403/404 三个错误码400/409/415 等 HTTP 标准错误码未做前端映射,全部 fallback 到 `default` 或后端 `msg`。后端虽定义了 16 个状态码,但实际使用集中在 200 和 500。
- **影响范围**: 全局错误提示,用户体验不够精准。
- **推荐方案**: 完善前端错误码映射表,后端减少 500 的滥用。
- **实施优先级**: P2
- **实施步骤**:
1. 在 `errorCode.js` 中补充 400/409/415/501 等错误码映射
2. 后端对参数错误返回 400业务冲突返回 409
3. 更新 `GlobalExceptionHandler` 返回更精确的状态码
4. 前端展示更友好的错误提示
### 33. 业务组件与通用组件混放
- **问题描述**: `Kdialog`、`TrendStocksDialog`、`NegativeDialog` 等强业务耦合组件放在 `src/components/` 通用组件目录,通过全局注册加载,增加首屏包体积。
- **影响范围**: 前端首屏加载性能,通用组件库纯净度。
- **推荐方案**: 将业务组件移至对应业务模块目录,或采用按需异步加载。
- **实施优先级**: P2
- **实施步骤**:
1. 将业务组件从 `src/components/` 移至对应业务模块(如 `src/views/stocksystem/components/`
2. 取消全局注册,改为局部注册或异步加载
3. 重新评估首屏包体积变化
4. 制定组件目录规范
### 34. 视图页面高度模板化
- **问题描述**: `system/`、`booksystem/`、`stocksystem/` 下的 CRUD 页面结构高度相似(搜索表单 + 表格 + 分页 + 弹窗),但未抽取为可配置的高阶组件或 Schema 驱动表单。
- **影响范围**: 前端开发效率,新增 CRUD 页面工作量大,修改通用逻辑需改多个文件。
- **推荐方案**: 抽取 CRUD 页面为 Schema 驱动的高阶组件或低代码配置方案。
- **实施优先级**: P2
- **实施步骤**:
1. 分析现有 CRUD 页面的共同模式
2. 设计 Schema 配置格式(字段定义、搜索条件、操作按钮)
3. 实现 `ProTable``ProForm` 高阶组件
4. 在新页面试点使用 Schema 驱动方式
5. 逐步迁移现有页面
### 35. 缺少接口版本控制
- **问题描述**: (与第 21 条重复,此处补充前端视角)前端 API 模块文件中没有版本管理概念,接口变更时需要手动修改 URL。
- **影响范围**: 前端 API 维护,接口升级时容易遗漏。
- **推荐方案**: 前端 API 基础路径集中管理,支持版本切换。
- **实施优先级**: P2
- **实施步骤**:
1. 在 `.env` 文件中定义 `VUE_APP_API_VERSION=v1`
2. `request.js``baseURL` 中动态拼接版本前缀
3. 新接口版本在对应模块文件中并行维护
4. 旧版本接口标记废弃注释
### 36. 日期格式化查询导致索引失效
- **问题描述**: 时间范围查询使用 `date_format(u.create_time, '%y%m%d')` 包装列,导致即使 `create_time` 有索引也无法使用,必须全表扫描。
- **影响范围**: 用户列表、操作日志、登录日志等时间范围查询。
- **推荐方案**: 将函数包裹改为范围查询。
- **实施优先级**: P2
- **实施步骤**:
1. 将 `date_format(u.create_time, '%y%m%d') >= ?` 改为 `u.create_time >= #{beginTime}`
2. 将 `date_format(u.create_time, '%y%m%d') <= ?` 改为 `u.create_time < DATE_ADD(#{endTime}, INTERVAL 1 DAY)`
3. 更新 Mapper XML 中的时间查询条件
4. 前端传入完整的日期时间格式参数
5. 使用 `EXPLAIN` 验证索引生效
---
## 总结
| 优先级 | 数量 | 建议实施周期 | 涵盖领域 |
|--------|------|-------------|----------|
| **P0** | 7 | 1-2 周 | 安全漏洞、主键类型、索引缺失、SQL 注入、响应格式 |
| **P1** | 11 | 1-2 月 | 技术栈升级、安全配置、架构分层、API 规范、文档 |
| **P2** | 18 | 3-6 月 | 代码规范、审计追踪、性能优化、组件重构、开发体验 |
| **合计** | **36** | — | 前端 8、后端 8、交互 10、数据库 10 |
### 实施路线图建议
```
第 1-2 周P0 紧急修复)
├── 升级 fastjson → fastjson2
├── 修复业务表主键 double → bigint
├── trade_dates 添加主键
├── 核心表添加索引
├── 排序字段白名单改造
├── 统一响应格式设计
└── dataScope SQL 拼接加固
第 3-6 周P1 架构优化)
├── Spring Security 配置升级
├── Token 异常日志补充
├── DTO/VO 分层设计
├── Vue 3 迁移评估与 POC
├── 权限核心模块抽取
├── 状态持久化统一
├── API 路径一致性修复
├── 分页参数显式化
├── API 文档接入
├── 级联删除逻辑完善
└── 部门树查询优化
第 7-16 周P2 持续改进)
├── @Log 注解补充
├── Mapper 注入规范化
├── API 版本控制
├── 组件加载方式统一
├── 401 处理优化
├── 请求重试机制
├── 参数序列化标准化
├── 批量删除规范统一
├── 调试代码清理
├── 审计字段添加
├── 软删除机制
├── 大字段分离
├── HTTP 状态码策略
├── 错误码体系完善
├── 组件目录规范
└── CRUD Schema 驱动
```
---
*文档结束*
Loading…
Cancel
Save