fix(用户管理): 修复用户列表页面加载失败问题
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

修复用户列表页面加载失败导致测试超时的问题,确保页面元素正确渲染
This commit is contained in:
iven
2026-04-19 08:46:28 +08:00
parent 0ee9d22634
commit 841766b168
174 changed files with 26366 additions and 675 deletions

View File

@@ -0,0 +1,382 @@
# ERP 平台全面深度审计报告
> 审计日期: 2026-04-18
> 审计方式: 前端功能链路 + API 接口测试 + 代码静态分析 + 安全渗透测试
> 审计范围: 全部 5 个业务模块 + 2 个插件 + 前端 SPA + 安全与代码质量
---
## 一、审计总结
| 维度 | CRITICAL | HIGH | MEDIUM | LOW | 合计 |
|------|----------|------|--------|-----|------|
| 安全 | 2 | 4 | 3 | 2 | 11 |
| 功能 | 1 | 3 | 3 | 1 | 8 |
| 代码质量 | 0 | 1 | 3 | 6 | 10 |
| **合计** | **3** | **8** | **9** | **9** | **29** |
---
## 二、CRITICAL — 必须立即修复
### C-01 Redis 凭据硬编码在配置文件中(泄露到 Git
- **文件**: `crates/erp-server/config/default.toml` (line 11)
- **现象**: `url = "redis://:redis_KBCYJk@129.204.154.246:6379"` 硬编码了远程 Redis 密码和 IP
- **影响**: 凭据已提交到 Git 仓库,任何有代码访问权限的人都能获取 Redis 密码和服务器 IP
- **修复**:
1. 立即轮换 Redis 密码
2.`url` 改回 `__MUST_SET_VIA_ENV__` 占位符
3. 使用环境变量 `ERP__REDIS__URL` 传递
### C-02 存储型 XSS — 用户输入未做 HTML 清理
- **文件**: `crates/erp-auth/src/service/user_service.rs` (创建/更新用户)
- **现象**: 通过 `POST /api/v1/users` 可将 `<script>alert('xss')</script>``<img src=x onerror=alert(1)>` 直接存入数据库的 `display_name``email` 等字段
- **影响**:
- React JSX 自动转义避免了前端直接触发(当前安全)
- 但原始 HTML 已存储在数据库中,在以下场景可触发:
- 邮件模板渲染
- PDF 导出
- OpenAPI 文档中的 schema 示例
- 未来使用非 React 渲染的任何场景
- **验证**:
```
POST /api/v1/users {"display_name":"<img src=x onerror=alert(document.cookie)>"}
→ 201 Created, 原始 HTML 直接入库
```
- **修复**: 后端入库前对所有用户可编辑字段 strip HTML tags 或 escape HTML entities
### C-03 首页工作台统计卡片永久 Loading
- **文件**: `apps/web/src/pages/Home.tsx`
- **现象**: 4 个统计卡片(用户总数、角色数量、流程实例、未读消息)始终显示 loading 动画
- **根因**: `useCountUp` 动画依赖数据加载API 返回格式与前端预期不匹配
- **影响**: 工作台页面无法展示核心统计数据,用户体验极差
- **修复**: 修正统计 API 的数据格式,确保与 `StatisticCard` 组件预期一致
---
## 三、HIGH — 高优先级问题
### H-01 用户名唯一性约束未生效
- **文件**: `crates/erp-auth/src/service/user_service.rs` (创建用户)
- **现象**: 用相同 `username` 创建两次用户均返回 `201 Created`
- **影响**: 可能导致身份混淆、审计日志混乱
- **修复**: 在创建用户前先查询 `username` 是否已存在(同 tenant_id + 未删除),或添加数据库唯一索引
### H-02 消息模板 API 返回空 body
- **文件**: `GET /api/v1/messages/templates`
- **现象**: 返回空 HTTP body非 JSON 格式),前端无法解析
- **影响**: 消息中心"模板"tab 无法展示数据
- **修复**: 修复空列表的序列化处理,确保返回 `{"success":true,"data":{"data":[],"total":0}}`
### H-03 主题 API 返回空 body
- **文件**: `GET /api/v1/config/theme`
- **现象**: 返回空 body 而非 JSON
- **影响**: 主题设置页面无法加载当前配置
- **修复**: 为新租户初始化默认主题配置,或 API 返回默认值
### H-04 JWT Token 体积过大
- **文件**: `crates/erp-auth/src/service/token_service.rs`
- **现象**: Access Token 包含 64 个权限字符串JWT payload 约 2.5KB
- **影响**:
- 每次 HTTP 请求都要携带 2.5KB+ 的 Authorization header
- 影响带宽和性能,尤其在高频 API 调用场景
- 权限变更需要等 Token 过期才生效(最长 15 分钟)
- **修复**:
1. 方案 AJWT 只存角色,权限在服务端 Redis 缓存实时查询
2. 方案 B使用权限位图/bitmask 压缩
3. 方案 C减少 JWT 中的权限列表,改为中间件实时校验
### H-05 字段长度无限制
- **文件**: `crates/erp-auth/src/dto.rs`
- **现象**: `display_name` 可接受 500+ 字符(测试通过 500 个 'A'),无 max length 验证
- **影响**: UI 布局破坏、潜在数据库性能问题
- **修复**: 添加 `#[validate(length(max = 100))]` 等长度约束
---
## 四、MEDIUM — 中优先级问题
### M-01 菜单配置前端硬编码,未使用后端 API
- **文件**: `apps/web/src/components/AppLayout.tsx` 或路由配置
- **现象**: 后端 `GET /api/v1/config/menus` 返回空数组,侧边栏菜单完全前端硬编码
- **影响**: 菜单无法通过管理后台动态配置,插件菜单需要在代码中手动添加
- **修复**: 实现前端从 API 动态加载菜单配置,或在后端初始化默认菜单数据
### M-02 时间戳未本地化显示
- **文件**: 消息中心、审计日志等列表页面
- **现象**: 时间显示为原始 ISO 格式 `2026-04-14T13:10:59.516776Z`,用户不友好
- **影响**: 用户体验差
- **修复**: 使用 dayjs 格式化为本地时间,如 `2026-04-14 21:10:59`
### M-03 前端路由仅做认证守卫,无权限守卫
- **文件**: `apps/web/src/App.tsx`
- **现象**: 路由只检查是否已登录token 存在),不检查用户是否有权限访问特定页面
- **影响**: 无权限用户可以通过直接输入 URL 访问任何页面(虽然 API 层会返回 403
- **修复**: 在路由守卫中增加权限校验,根据 JWT 中的 permissions 控制页面可见性
### M-04 消息响应包含内部 tenant_id
- **文件**: `crates/erp-message/src/handler/message_handler.rs`
- **现象**: `GET /api/v1/messages` 返回每条消息的 `tenant_id` 字段
- **影响**: 泄露内部多租户架构信息
- **修复**: 在 DTO 层排除 `tenant_id` 字段
### M-05 搜索缺少防抖Debounce
- **文件**: `apps/web/src/pages/Users.tsx`, `apps/web/src/components/EntitySelect.tsx`
- **现象**: 用户搜索输入框每次按键都触发 API 请求
- **影响**: 高频请求冲击服务器,用户体验差
- **修复**: 添加 300ms debounce已有 `useDebouncedValue` Hook 但未使用)
### M-06 Organizations 页面 useCallback/useEffect 循环依赖
- **文件**: `apps/web/src/pages/Organizations.tsx`
- **现象**: useCallback 依赖项导致 useEffect 无限循环渲染
- **影响**: 性能问题、可能导致浏览器卡顿
- **修复**: 重构 useCallback 依赖项,消除循环
### M-07 测试数据残留在生产数据库
- **现象**: 数据库中存在以下测试用户和数据:
- `xss_user` — display_name 为 `<script>alert('xss')</script>`
- `test_role_api` — 测试角色
- `audit_test_user` — 审计测试用户
- `testuser01` — 测试用户
- `test_user_api` — API 测试用户
- `Perf test` 消息 — 性能测试消息
- `business_key: PERF-TEST-26311` 的待办任务
- **影响**: 数据污染、潜在安全风险
- **修复**: 清理所有测试数据,确保数据库只包含有意义的业务数据
### M-08 系统设置多个 tab 数据为空
- **现象**: 数据字典、编号规则、系统参数、语言管理等 tab 均无种子数据
- **影响**: 系统看起来像空的,用户需要手动配置所有基础数据
- **修复**: 在 `on_tenant_created` 中初始化默认字典(客户类型、行业、地区等)
### M-09 中文 API 响应编码异常
- **现象**: 部分中文内容在 API JSON 响应中显示为乱码(如 `\u7eef\u8364\u7cba` 而非"系统管理员"
- **影响**: 可能是 curl 的显示问题,也可能是后端序列化配置问题
- **修复**: 确认后端 JSON 序列化使用 `ensure_ascii: false` 等效配置
---
## 五、LOW — 低优先级 / 代码质量
### L-01 死代码 — graph 目录
- **文件**: `apps/web/src/pages/plugins/graph/` (6 个文件)
- **现象**: 完全未使用的代码
- **修复**: 删除或标记为实验性
### L-02 死代码 — 未使用的 Hooks
- **文件**: `apps/web/src/hooks/`
- **现象**: `useDarkMode`, `useDebouncedValue`, `usePaginatedData`, `useApiRequest` 4 个 Hook 未被引用
- **修复**: 清理或接入这些 Hook特别是 useDebouncedValue 在搜索场景很有用)
### L-03 重复代码 — useCountUp
- **现象**: `useCountUp` 在 3 处重复定义
- **修复**: 提取为共享 Hook
### L-04 暗色模式检测逻辑重复
- **现象**: `const isDark = token.colorBgContainer === '#111827'` 在 20+ 组件中重复
- **修复**: 用已有的 `useDarkMode` Hook 替换
### L-05 i18n 已配置但未使用
- **现象**: i18next 已初始化且有 30 个翻译 key但所有页面组件硬编码中文
- **修复**: 逐步替换硬编码中文为 i18n key
### L-06 antd 废弃 API 警告
- **现象**: `Drawer` 的 `width` 属性、`Modal` 的 `destroyOnClose` 已废弃
- **修复**: 升级到 antd 6 的新 API 用法
### L-07 ErrorBoundary 错误信息泄露
- **文件**: `apps/web/src/components/ErrorBoundary.tsx`
- **现象**: 错误边界展示完整的错误堆栈给用户
- **修复**: 生产环境只显示友好错误消息,堆栈信息仅记录到控制台
### L-08 Home 页面使用 dangerouslySetInnerHTML
- **文件**: `apps/web/src/pages/Home.tsx`
- **现象**: 工作台页面使用 `dangerouslySetInnerHTML` 渲染内容
- **影响**: 如果内容包含用户输入,可能导致 XSS
- **修复**: 改用 React 组件渲染
### L-09 插件恢复计数不准确
- **现象**: `Plugin recovered: 0` 但实际 WASM 已加载
- **修复**: 修正 recovery 计数逻辑
---
## 六、安全测试矩阵
| 测试项 | 结果 | 备注 |
|--------|------|------|
| 无 Token 访问受保护端点 | ✅ 401 | 正确拦截 |
| 无效 Token | ✅ 401 | 正确拦截 |
| 篡改 Token payload | ✅ 401 | HMAC 签名校验有效 |
| 错误密码登录 | ✅ 401 | 正确拒绝 |
| 短密码创建用户 | ✅ 400 | 验证 min=6 生效 |
| 空 Token 刷新 | ✅ 401 | 正确拒绝 |
| 旧 Refresh Token 重用 | ✅ 401 | 轮换机制生效 |
| SQL 注入(搜索参数) | ✅ 安全 | SeaORM 参数化查询 |
| SQL 注入UUID 路径) | ✅ 安全 | UUID 解析拒绝非法字符 |
| 存储型 XSS | ❌ 入库 | 后端未清理 HTMLReact 前端安全 |
| 无权限用户访问 API | ✅ 403 | 权限校验正确 |
| 无权限用户提权 | ✅ 403 | 角色分配受权限保护 |
| 限流 | ✅ 生效 | 5 次失败后触发 429 |
| CORS 配置 | ✅ 白名单 | 仅允许 localhost 端口 |
| 凭据泄露 | ❌ Redis 密码硬编码 | 已提交到 Git |
---
## 七、功能链路审计结果
### 7.1 认证链路 ✅ 基本正常
| 环节 | 状态 | 备注 |
|------|------|------|
| 登录 → JWT 签发 | ✅ | access (15min) + refresh (7d) |
| Token 刷新轮换 | ✅ | 旧 Token 使用后立即失效 |
| 密码修改 → Token 吊销 | ✅ | 所有 refresh token 失效 |
| 登出 → Token 吊销 | ✅ | |
| 限流保护 | ✅ | 5 次失败后 429 |
| 审计日志记录 | ✅ | 登录成功/失败均有记录 |
### 7.2 用户管理 ✅ 基本正常,有缺陷
| 环节 | 状态 | 备注 |
|------|------|------|
| CRUD 操作 | ✅ | |
| 角色分配 | ✅ | |
| 用户名唯一性 | ❌ | 重复用户名可创建 |
| 输入验证 | ⚠️ | 密码有验证,其他字段长度/XSS 无验证 |
| 软删除 | ✅ | |
### 7.3 权限管理 ✅ 正常
| 环节 | 状态 | 备注 |
|------|------|------|
| 角色列表 | ✅ | 3 个角色admin/viewer/test |
| 权限分配 | ✅ | 54 个权限可精确分配 |
| 系统角色保护 | ⚠️ | admin 角色权限可被修改 |
| data_scope 配置 | ❌ | 权限对话框中无 data_scope 配置入口 |
### 7.4 工作流引擎 ✅ 正常
| 环节 | 状态 | 备注 |
|------|------|------|
| 流程定义 CRUD | ✅ | 3 个定义draft/published |
| 流程发起 | ✅ | |
| 任务审批 | ✅ | approve/reject |
| 任务委派 | ✅ | |
| 实例监控 | ✅ | running/terminated/suspended |
| 超时检测 | ✅ | 60s 间隔扫描 |
### 7.5 消息中心 ⚠️ 部分异常
| 环节 | 状态 | 备注 |
|------|------|------|
| 消息列表 | ✅ | 10 条消息 |
| 未读计数 | ✅ | bell 图标显示未读数 |
| 标记已读 | ✅ | |
| 全部已读 | ✅ | |
| 消息模板 | ❌ | API 返回空 body |
| 通知设置 | ⚠️ | 未验证 |
| 工作流事件 → 消息 | ✅ | "流程已启动" 消息自动生成 |
### 7.6 系统配置 ⚠️ 数据缺失
| 环节 | 状态 | 备注 |
|------|------|------|
| 数据字典 | ❌ | 空数据,无种子 |
| 菜单配置 | ❌ | 后端空,前端硬编码 |
| 编号规则 | ❌ | 空 |
| 系统参数 | ⚠️ | 未验证 |
| 主题设置 | ❌ | API 返回空 |
| 语言管理 | ⚠️ | 未验证 |
| 修改密码 | ✅ | 功能正常 |
| 审计日志 | ✅ | |
### 7.7 插件系统 ✅ 基本正常
| 环节 | 状态 | 备注 |
|------|------|------|
| CRM 插件运行 | ✅ | 状态:运行中 |
| 客户 CRUD | ✅ | 6 条客户数据 |
| 联系人 | ✅ | |
| 沟通记录 | ✅ | |
| 标签管理 | ✅ | |
| 客户关系 | ✅ | |
| 统计概览 | ⚠️ | |
| 销售漏斗 | ⚠️ | |
---
## 八、修复优先级建议
### 🔴 立即修复(本周内)
| 编号 | 问题 | 预计工作量 |
|------|------|-----------|
| C-01 | Redis 凭据从 config 移除,改用环境变量 | 0.5h |
| C-02 | 后端添加 HTML sanitize 中间件 | 2h |
| C-03 | 修复首页统计卡片数据格式 | 1h |
| H-01 | 添加用户名唯一性校验 | 1h |
### 🟡 本迭代修复2 周内)
| 编号 | 问题 | 预计工作量 |
|------|------|-----------|
| H-02 | 修复消息模板空返回 | 0.5h |
| H-03 | 修复主题 API 空返回 | 0.5h |
| H-04 | JWT 权限压缩或改为服务端查询 | 4h |
| H-05 | 添加字段长度验证 | 1h |
| M-03 | 添加前端路由权限守卫 | 2h |
| M-05 | 搜索添加防抖 | 0.5h |
| M-07 | 清理测试数据 | 1h |
| M-08 | 初始化默认系统配置数据 | 2h |
### 🟢 迭代中逐步修复
| 编号 | 问题 |
|------|------|
| M-01 | 菜单动态加载 |
| M-02 | 时间戳本地化 |
| M-04 | API 响应排除 tenant_id |
| M-06 | Organizations 性能修复 |
| L-01~L-09 | 代码质量清理 |
---
## 九、系统亮点(做得好的地方)
1. **Token 刷新轮换机制** — 旧 Refresh Token 重用被正确拒绝
2. **限流保护** — 登录失败 5 次后触发 429 Too Many Requests
3. **SeaORM 参数化查询** — SQL 注入测试全部被拦截
4. **权限校验完整性** — 无权限用户所有操作返回 403
5. **多租户架构** — JWT 注入 tenant_id中间件自动过滤
6. **审计日志** — 登录/登出/密码修改等关键操作有完整记录
7. **WASM 插件沙箱** — CRM 插件运行稳定6 个实体全部可用
8. **工作流引擎** — BPMN 解析、Token 驱动、任务分配完整实现
9. **错误处理链** — thiserror → AppError → HTTP 响应的统一错误体系
10. **优雅关闭** — CTRL+C 信号处理、模块按拓扑逆序关闭

View File

@@ -0,0 +1,155 @@
# 系统全面审计报告 — 2026-04-18
## 审计环境
| 项目 | 值 |
|---|---|
| PostgreSQL | 18 (原生安装 D:\postgreSQL), 端口 5432 |
| Redis | 未安装/未运行 (限流改为 fail-open 降级) |
| 后端 | Axum 0.8, 端口 3000 |
| 前端 | Vite 8, 端口 5174 |
| 操作系统 | Windows 11 Pro |
## P0 — 严重问题(必须立即修复)
### 1. CRM 插件数据 403 Forbidden — ✅ 已修复
**现象**: 所有 CRM 数据页面(客户、联系人、沟通记录等)返回 403 错误,页面显示"加载数据失败"。
**根因**: CRM 插件在安装时正确注册了 9 条权限到 `permissions` 表(`erp-crm.customer.list` 等),但 **没有自动将这些权限分配给 admin 角色**。导致 JWT 中只有 `plugin.admin``plugin.list`,缺少 `erp-crm.*` 权限。
**修复**: 在 `erp-plugin/src/service.rs` 中新增 `grant_permissions_to_admin()` 函数,在 `install()``enable()` 中自动调用。修复后 CRM 客户列表 API 正常返回数据。
### 2. CRM 插件启动恢复失败 — ✅ 已修复
**现象**: 后端日志 `Failed to recover plugin (initialize): 数据库错误: 关系 "plugin_erp_crm_inventory_item" 不存在`
**根因**: CRM 插件的 `on_init` 回调尝试创建 `inventory_item` 实体的种子数据,但该表不存在。可能是 CRM 插件 WASM 代码中的实体定义与数据库迁移不匹配。
**影响**: 服务器重启后 CRM 插件恢复失败,`Plugins recovered: 0`
**修复**: 通过升级 API 重新上传正确的 CRM WASM 二进制22KB 替换错误的 110KB 测试插件)。修复后插件正常恢复并运行。
### 3. 首页统计数据卡片永久 Loading
**现象**: 工作台首页 4 个统计卡片(用户总数、角色数量、流程实例、未读消息)显示 loading 状态(`busy` 属性),数字不显示。
**根因**: 首页统计卡片使用 `useCountUp` 动画但依赖数据加载,数据加载可能失败或 API 返回格式不匹配。
### 4. 插件 API 路由不支持字符串 ID
**现象**: `/api/v1/plugins/erp-crm/customer` 返回 `UUID parsing failed`
**根因**: 后端路由定义 `Path<(Uuid, String)>`,要求 `plugin_id` 必须是 UUID 格式。但插件的 manifest ID 是字符串(如 `erp-crm`)。
**影响**: 直接用 manifest ID 调用 API 不行,必须先查 UUID。前端已绕过此问题使用 UUID但 API 设计不够友好。
## P1 — 高优先级问题
### 5. XSS: 显示名未转义存储
**现象**: `POST /api/v1/users``display_name` 字段可以存储 `<script>alert(1)</script>`API 返回原样值。
**评估**: React 框架自动转义防止了前端 XSS。但数据库中存储了原始 HTML如果有其他客户端如邮件、导出 PDF 等)不转义渲染,仍存在风险。
**建议**: 后端入库时 strip HTML tags 或 escape。
### 6. 重复用户名检测缺失
**现象**: `POST /api/v1/users``audit_test_user` 创建两次,第二次也返回 `success: true`,没有报重复错误。
**评估**: 第二次创建返回的 `id` 不同但 `username` 相同,说明用户名唯一性约束可能没生效。
### 7. 消息模板 API 返回空
**现象**: `GET /api/v1/messages/templates` 返回空 body非 JSON
**根因**: 可能数据库无模板数据,且空列表情况下序列化异常。
### 8. 主题 API 返回空
**现象**: `GET /api/v1/config/theme` 返回空 body。
### 9. `roles/permissions` 路由冲突 — ✅ 已修复
**现象**: `GET /api/v1/roles/permissions` 返回 UUID 解析错误。
**根因**: 路由 `GET /roles/{id}``permissions` 当成 UUID 解析了。
**修复**: 在 `erp-auth/src/module.rs` 中,在 `/roles/{id}` 之前注册 `/roles/permissions` 精确匹配路由。修复后返回 64 条权限数据。
## P2 — 中优先级问题
### 10. CRM 插件恢复后 Plugin recovered: 0
后端日志显示插件加载成功但 recovery 报 0。on_init 失败导致插件状态变为 error但实际插件 WASM 已加载到内存。
### 11. 创建用户时中文 display_name 解析失败
`POST /api/v1/users``display_name` 含中文字符时,返回 `invalid unicode code point`。可能与 curl 的编码有关而非后端 bug需要进一步验证。
### 12. 菜单数据为空
`GET /api/v1/config/menus` 返回空数组。系统侧边栏菜单是前端硬编码的,后端菜单配置未使用。
### 13. 数据字典为空
`GET /api/v1/config/dictionaries` 返回空。这是正常的(未创建字典数据)。
## P3 — 低优先级 / 代码质量
### 14. 前端死代码
- `src/pages/plugins/graph/` 6 个文件完全未使用
- `src/hooks/` 下 4 个 Hook 未被任何组件引用useDarkMode, useDebouncedValue, usePaginatedData, useApiRequest
- `useCountUp` 在 3 处重复定义
### 15. i18n 已配置但完全未使用
i18next 已初始化,翻译文件有 30 个 key但所有页面组件硬编码中文。
### 16. 暗色模式检测逻辑重复 20+ 次
`const isDark = token.colorBgContainer === '#111827'` 在 20+ 组件中重复,已有 `useDarkMode` Hook 但未使用。
### 17. antd 废弃 API 警告
- `Drawer``width` 属性已废弃
- `Modal``destroyOnClose` 已废弃
- `message` 静态方法无法消费 context
## 安全测试结果
| 测试项 | 结果 |
|---|---|
| 无 token 访问 | 401 Unauthorized |
| 错误 token | 401 Unauthorized |
| 错误密码登录 | 401 Unauthorized |
| 空请求体登录 | 反序列化错误(非 500 |
| 短密码验证 | 400 Bad Request + 详细验证信息 |
| SQL 注入(用户名) | JSON 解析失败(被拦截) |
| XSS(显示名) | 存储了原始 HTML需后端过滤 |
| 权限不足操作 | 403 Forbidden |
## 正常工作的功能
- 登录/登出/Token 刷新
- 用户 CRUD创建/列表/删除)
- 角色 CRUD + 权限查看
- 组织架构三栏管理
- 工作流定义列表/待办任务
- 消息列表/已读/未读计数
- 审计日志记录
- 插件管理(上传/启用/停用)
- 系统设置 Tab 页(字典/语言/菜单/编号/主题/参数/审计/密码)
- OpenAPI 文档端点
## 下一步工作建议
1. **P0-1**: 修复插件权限自动分配给 admin 角色
2. **P0-2**: 修复 CRM 插件 on_init 中 inventory_item 表不存在的问题
3. **P0-3**: 修复首页统计卡片数据加载
4. **P1-5**: 后端 display_name HTML 过滤
5. **P1-6**: 用户名唯一性约束
6. **P1-9**: 修复 roles/permissions 路由冲突
7. 更新所有相关文档wiki/插件系统文档)

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 KiB