|
|
# 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% |
|