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

591 lines
23 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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