You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
23 KiB
23 KiB
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 成功响应(无数据)
{
"code": 200,
"msg": "操作成功"
}
2.1.2 成功响应(带数据)
{
"code": 200,
"msg": "操作成功",
"data": {
"userId": 1,
"userName": "admin"
}
}
2.1.3 成功响应(多字段 — AjaxResult.put 扩展)
{
"code": 200,
"msg": "操作成功",
"data": { /* 用户详情 */ },
"roles": [ /* 角色列表 */ ],
"posts": [ /* 岗位列表 */ ],
"postIds": [1, 2],
"roleIds": [1]
}
注意:后端使用
ajax.put(key, value)动态追加字段,前端通过response.roles等方式访问。
2.1.4 分页响应(TableDataInfo)
{
"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 失败响应
{
"code": 500,
"msg": "新增用户'admin'失败,登录账号已存在"
}
2.2 请求格式规范
2.2.1 查询列表(GET)
// 前端
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)
export function getUser(userId) {
return request({ url: '/system/user/' + userId, method: 'get' })
}
// 请求
GET /system/user/1
2.2.3 新增(POST + JSON Body)
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)
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)
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)
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)
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
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
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 后端代码示例
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage(); // 从请求参数读取分页信息
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list); // 封装为 TableDataInfo
}
4.2 排序规范
4.2.1 排序参数传递
// 前端通过分页组件传递
{
pageNum: 1,
pageSize: 10,
orderByColumn: 'createTime',
isAsc: 'desc'
}
4.2.2 多字段排序
isAsc: createTime desc,updateTime desc
4.2.3 SQL 注入防护
// SqlUtil.escapeOrderBySql() 方法对排序字段进行转义
// 只允许字母、数字、下划线、点号、逗号、空格、ASC、DESC
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
PageHelper.orderBy(orderBy);
4.3 过滤规范
4.3.1 列表查询过滤
采用 对象参数绑定 方式:
// 后端
@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 权限过滤
// 每个接口通过 @PreAuthorize 进行权限校验
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) { ... }
4.3.3 数据权限过滤
// 在 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 周)
- 补充前端错误码映射:在
errorCode.js中补充 400/409/415 等常见错误码的中文提示 - 清理调试代码:将
StocksController中的System.out.println替换为logger - 补全权限注解:取消
StocksController中被注释的@PreAuthorize
中期优化(1-2 月)
- 接入 API 文档:为所有 Controller 补充 Swagger 注解,启用 Knife4j 文档页面
- 规范分页参数:考虑使用
@Validated显式声明分页参数 - 统一响应格式:评估是否需要统一
AjaxResult和TableDataInfo的字段结构
长期优化(持续)
- 排序安全加固:实施白名单机制替代正则过滤
- RESTful 对齐:评估是否将业务状态码与 HTTP 状态码对齐
- 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% |