Files
user-system/docs/code-review/CONSISTENCY_PERFORMANCE_REVIEW_2026-04-02.md
long-agent 5b6bd93179 refactor: 整理项目根目录结构
整理内容:
- 删除 60+ 临时测试输出文件 (*.txt)
- 移动二进制文件到 bin/ 目录
- 移动 Shell 脚本到 scripts/ 目录
  - scripts/dev/: check_gitea.sh, check_sub2api.sh, run_tests.sh
  - scripts/deploy/: deploy_*.sh, simple_deploy.sh
  - scripts/ops/: fix_nginx.sh, fix_ssl.sh, install_docker.sh
  - scripts/test/: test_*.sh, test_*.bat
- 移动批处理文件到 scripts/
- 移动 Python 脚本到 tools/
- 清理临时日志文件

保留根目录必要文件:
- go.mod, go.sum, go.work
- Makefile, docker-compose.yml
- .env.example, .gitignore
- README.md, AGENTS.md, DEPLOY_GUIDE.md

验证: go build ./... && go test ./... 通过
2026-04-07 18:10:36 +08:00

289 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前后端一致性 + 架构性能专项审查报告
**审查日期**: 2026-04-02
**审查范围**: 前后端一致性 + 架构性能执行
**审查方法**: 多智能体并行审查(一致性审查、性能审查)
---
## 一、执行摘要
### 综合评分
| 维度 | 得分 | 说明 |
|------|------|------|
| 前后端一致性 | 2.0/10 | 存在根本性协议层不匹配72 个端点路由正确但请求/响应格式全面错位 |
| 架构性能执行 | 5.5/10 | 架构基础合理,但 SQLite、N+1 查询、无界导出等严重制约扩展性 |
| **综合评分** | **3.8/10** | 这是当前项目最薄弱的两个环节,必须优先修复 |
### 问题统计
| 严重级别 | 一致性 | 性能 | 总计 |
|----------|--------|------|------|
| 🔴 严重 | 18 | 7 | 25 |
| 🟡 重要 | 14 | 17 | 31 |
| 💭 轻微 | 8 | 8 | 16 |
| **总计** | **40** | **32** | **72** |
---
## 二、前后端一致性审查
### 2.1 根本性问题:响应格式协议不匹配
**这是整个项目最严重的一致性问题。**
- **前端期望**: 所有 API 响应格式为 `{code: number, data: T, message: string}`
- **后端实际**: 直接返回裸 JSON`{users: [...], total: 100}``{error: "..."}`
- **影响**: 前端 `client.ts` 检查 `result.code !== 0` 时,`result.code``undefined`,导致**每个 API 调用都会抛出错误**。
- **结论**: 如果这不是在隔离测试环境中运行(测试环境可能走了不同的代码路径),整个应用将无法正常工作。
### 2.2 一致性问题分类
#### 🔴 严重问题18 个)
| ID | 类别 | 前端 | 后端 | 问题 |
|----|------|------|------|------|
| CONSISTENCY-01 | 全局 | `client.ts:240-245` | ALL handlers | 响应格式不匹配:前端期望 `{code, data, message}`,后端返回裸 JSON |
| CONSISTENCY-02 | 用户列表 | `users.ts:23-24` | `user_handler.go:76-81` | 响应 key: `items` vs `users`,分页: `page/page_size` vs `offset/limit` |
| CONSISTENCY-03 | 角色列表 | `roles.ts:11-15` | `role_handler.go:52-55` | 响应 key: `items` vs `roles`,缺 `page/page_size` |
| CONSISTENCY-04 | 角色权限 | `roles.ts:38-40` | `role_handler.go:160` | 前端期望数组,后端返回 `{permissions: [...]}` |
| CONSISTENCY-05 | 权限列表 | `permissions.ts:22-23` | `permission_handler.go:52-55` | 前端期望数组,后端返回 `{permissions, total}` |
| CONSISTENCY-06 | 权限树 | `permissions.ts:14-15` | `permission_handler.go:153` | 前端期望数组,后端返回 `{permissions: tree}` |
| CONSISTENCY-07 | 设备列表 | `devices.ts:10-14` | `device_handler.go:63-68` | 响应 key: `items` vs `devices` |
| CONSISTENCY-08 | 管理员设备 | `devices.ts:18-22` | `device_handler.go:198-203` | 响应 key: `items` vs `devices` |
| CONSISTENCY-09 | Webhook 列表 | `webhooks.ts:35-47` | `webhook_handler.go:26` | 响应 key: `data` vs `webhooks` |
| CONSISTENCY-10 | 登录日志 | `login-logs.ts:12-22` | `log_handler.go:43-48` | 响应 key: `list` vs `logs``size` vs `page_size` |
| CONSISTENCY-11 | 操作日志 | `operation-logs.ts:12-22` | `log_handler.go:52` | 响应 key: `list` vs `logs` |
| CONSISTENCY-12 | 下线设备 | `devices.ts:58-59` | `device_handler.go:308-314` | 前端发送 body `current_device_id`,后端读 header `X-Device-ID` |
| CONSISTENCY-13 | 修改密码 | `profile.ts:52-53` | `user_handler.go:160-162` | 前端发送 `current_password`,后端期望 `old_password` |
| CONSISTENCY-14 | TOTP 状态 | `auth.ts:129-130` | `totp_handler.go:38` | 前端期望 `totp_enabled`,后端返回 `enabled` |
| CONSISTENCY-15 | Capabilities | `auth.ts:34-36` | `auth_handler.go:136-141` | 字段完全错位:前端期望 `password/email_activation/...`,后端返回 `register/login/...` |
| CONSISTENCY-16 | Bootstrap | `types/auth.ts:80-84` | `auth_handler.go:243-247` | 前端 `email` 可选,后端必填;前端发 `nickname`,后端不收 |
| CONSISTENCY-17 | 注册 | `types/auth.ts:71-78` | `auth_handler.go:22-28` | 前端发 `phone_code`,后端不收 |
| CONSISTENCY-18 | 重置密码 | `types/auth.ts:114-118` | `password_reset_handler.go:65-68` | 前端发 `confirm_password`,后端不收 |
#### 🟡 重要问题14 个)
| ID | 类别 | 问题 |
|----|------|------|
| CONSISTENCY-19 | 用户状态 | 前端发送数字 `0|1|2|3`,后端期望字符串 `"active"/"inactive"/...` |
| CONSISTENCY-20 | 角色状态 | 前端发送数字 `0|1`,后端期望字符串 `"enabled"/"disabled"` |
| CONSISTENCY-21 | 权限状态 | 前端发送数字 `0|1`,后端期望字符串 `"enabled"/"disabled"` |
| CONSISTENCY-22 | 设备状态 | 前端发送数字 `0|1`,后端期望字符串 `"active"/"inactive"` |
| CONSISTENCY-23 | 分页参数 | 前端发送 `page/page_size`,后端读取 `offset/limit` |
| CONSISTENCY-24 | 用户更新 | 前端发 7 个字段,后端只收 2 个email, nickname |
| CONSISTENCY-25 | 登录方式 | 前端只支持 username后端支持 account/email/phone |
| CONSISTENCY-26 | CSRF Token | 响应未包装,`result.code` 为 undefined |
| CONSISTENCY-27 | Token 重试 | 401 重试所有方法(含 POST/PUT/DELETE可能导致重复操作 |
| CONSISTENCY-28 | OAuth 授权 | 后端返回格式不匹配 |
| CONSISTENCY-29 | OAuth 交换 | 后端返回格式不匹配 |
| CONSISTENCY-30 | 用户角色 | 后端返回空 stub |
| CONSISTENCY-31 | 分配角色 | 后端返回 stub 但状态码 200 |
| CONSISTENCY-32 | 统计接口 | 后端返回 stub |
#### 💭 轻微问题8 个)
| ID | 类别 | 问题 |
|----|------|------|
| CONSISTENCY-33 | OAuth | 前端发送 `return_to` 参数,后端不读取 |
| CONSISTENCY-34 | 短信验证码 | 前端期望 void后端返回对象 |
| CONSISTENCY-35 | 头像上传 | 前端期望对象,后端返回 stub |
| CONSISTENCY-36 | 导出字段 | 前端发送逗号分隔字符串 |
| CONSISTENCY-37 | 日志导出格式 | 格式参数传递方式需确认 |
| CONSISTENCY-38 | TOTP 验证 | 前端期望 void后端返回 `{verified: true}` |
| CONSISTENCY-39 | 社交账号 | 前端期望数组,后端返回包装对象 |
| CONSISTENCY-40 | 设备指纹 | 前端无持久化设备标识 |
### 2.3 正确对齐的 API72 个端点)
✅ 所有 72 个端点的 URL 路径和 HTTP 方法都正确匹配。问题完全在于请求/响应载荷格式,不在于路由。
### 2.4 修复建议(按优先级)
#### P0修复响应协议阻塞所有功能
**方案 A推荐添加 Gin 响应包装中间件**
```go
// 拦截所有 c.JSON() 调用,自动包装为 {code: 0, data: <original>, message: ""}
func ResponseWrapper() gin.HandlerFunc {
return func(c *gin.Context) {
// 包装成功响应
// 错误响应包装为 {code: <http_status>, data: null, message: <error>}
}
}
```
**方案 B重写前端 client.ts**
移除 `result.code !== 0` 检查,直接返回 `response.json()`
#### P1标准化响应 Key
- 所有列表端点统一返回 `{items, total, page, page_size}`
- 所有直接数组端点(权限树、角色权限)直接返回数组
#### P2修复关键字段错位
| 端点 | 修复 |
|------|------|
| `POST /devices/me/logout-others` | 前端改为发送 `X-Device-ID` header |
| `PUT /users/:id/password` | 前端改为发送 `old_password` |
| `GET /auth/2fa/status` | 后端改为返回 `totp_enabled` |
| `GET /auth/capabilities` | 后端重写响应字段名 |
| `GET /auth/csrf-token` | 包装响应或前端直接读取 |
#### P3标准化状态类型
- 所有状态端点统一接受数字值0, 1, 2, 3
- 后端 switch 语句改为处理 `int` 而非 `string`
#### P4修复分页
- `GET /users`: 接受 `page/page_size` 参数,内部转换为 `offset/limit`
- 所有分页端点统一返回 `page``page_size`
---
## 三、架构性能执行审查
### 3.1 性能问题分类
#### 🔴 严重问题7 个)
| ID | 类别 | 文件 | 问题 | 影响 |
|----|------|------|------|------|
| PERF-01 | 数据库 | `middleware/auth.go:131-197` | 认证中间件 N+1 查询:每个请求 7-8 次 DB 查询 | 1000 并发用户 = 7000-8000 DB 查询/秒 |
| PERF-02 | 数据库 | `middleware/auth.go:210-221` | isUserActive 每次请求都执行 SELECT * | 缓存命中也无法避免 |
| PERF-03 | 数据库 | `login_log.go:118-139` | 导出/无分页查询加载全表到内存 | 百万级日志表 OOM |
| PERF-14 | 并发 | `auth.go:482-487` | 无界 goroutine + context.Background() | DB 降级时 goroutine 泄漏 → 连接池耗尽 |
| PERF-28 | 架构 | `V1__init.sql` | SQLite 作为生产数据库 | 写入串行化,吞吐量上限 50-100 writes/sec |
| PERF-29 | 架构 | `middleware/auth.go:38-47` | L1 缓存每进程独立,无法水平扩展 | 多实例部署权限变更 30 分钟传播延迟 |
| C03 | 前端 | `client.ts:210-221` | Token 刷新重试非幂等请求 | 可能导致重复创建用户等操作 |
#### 🟡 重要问题17 个)
| ID | 类别 | 问题 |
|----|------|------|
| PERF-04 | 数据库 | List() 总是 COUNT + SELECT2 次查询),即使只需要 count |
| PERF-05 | 数据库 | Dashboard stats 8+ 次顺序查询 |
| PERF-06 | 数据库 | GetAncestorIDs 顺序单行查询(最多 5 层) |
| PERF-07 | 数据库 | BatchSet 事务内 N 次顺序查询 |
| PERF-08 | 数据库 | LIKE '%keyword%' 4 列无全文索引 |
| PERF-09 | 数据库 | GetActiveDevices/GetTrustedDevices 无分页限制 |
| PERF-11 | 内存 | L1Cache updateAccessOrder 使用 O(n) 切片操作 |
| PERF-12 | 内存 | BatchDelete 未预分配切片容量 |
| PERF-15 | 并发 | L1Cache Get 使用写锁Lock而非读锁RLock |
| PERF-17 | HTTP | 无响应压缩中间件 |
| PERF-18 | HTTP | 大多数路由无请求体大小限制 |
| PERF-19 | HTTP | 操作日志中间件为每个写请求分配 4KB 缓冲 |
| PERF-21 | Bundle | 无代码分割配置antd + react 打包在一起) |
| PERF-22 | Runtime | ProfileSecurityPage 946 行 mega-component |
| PERF-23 | Runtime | WebhooksPage 客户端过滤 + 分页 |
| PERF-26 | Network | ProfileSecurityPage 挂载时 6 个并行 API 调用,无请求去重 |
| PERF-30 | 架构 | 无会话管理扩展性(多实例无法强制登出) |
#### 💭 轻微问题8 个)
| ID | 类别 | 问题 |
|----|------|------|
| PERF-10 | 数据库 | UpdateLastLogin 使用 map[string]interface{} |
| PERF-13 | 内存 | generateUniqueUsername 最多 1001 次顺序 DB 查询 |
| PERF-16 | 并发 | 祖先 ID 收集未并行化 |
| PERF-20 | HTTP | 全局 30s 超时对所有请求统一应用 |
| PERF-24 | Runtime | UsersPage columns 未 useMemo |
| PERF-25 | Runtime | PermissionsPage buildTreeData 每次渲染递归 |
| PERF-27 | Network | 401 重试未检查 body 是否可流式传输 |
| PERF-31 | 架构 | Webhook 事件通过无重试 goroutine 发布 |
### 3.2 前 5 大性能瓶颈
| 排名 | 问题 | 影响 |
|------|------|------|
| 1 | **SQLite 作为生产数据库** | 写入串行化,登录风暴时级联超时 |
| 2 | **认证中间件 N+1 查询** | 每个请求 7-8 次 DB 查询,冷启动时查询风暴 |
| 3 | **无界导出查询** | 导出端点加载全表到内存,百万级数据 OOM |
| 4 | **Dashboard stats 顺序查询** | 8 次顺序查询,冷加载 200-500ms |
| 5 | **泄漏的无界 goroutine** | DB 降级时 goroutine 堆积 → 连接池耗尽 → 全面宕机 |
### 3.3 架构扩展性评估
| 用户规模 | 状态 | 说明 |
|----------|------|------|
| 100 用户 | ✅ 就绪 | SQLite 可处理轻量并发 |
| 1,000 用户 | ⚠️ 有风险 | 登录突发(>50/sec会导致 SQLite 写入争用 |
| 10,000 用户 | ❌ 不可用 | SQLite 写入串行化成为硬瓶颈,认证中间件查询量不可持续 |
**10,000 用户前必须完成的变更**:
1. 迁移到 PostgreSQL
2. 合并认证中间件查询为 1-2 次缓存查找
3. 添加 Redis 作为共享缓存层
4. 流式导出替代内存加载
5. 添加自动化日志清理 cron
---
## 四、综合建议
### P0立即修复阻塞生产部署
1. **修复响应格式协议不匹配**CONSISTENCY-01
- 添加 Gin 响应包装中间件
- 或重写前端 client.ts 接受裸响应
2. **修复关键字段错位**CONSISTENCY-12, 13, 14, 15
- 设备下线body → header
- 修改密码current_password → old_password
- TOTP 状态enabled → totp_enabled
- Capabilities重写后端响应字段
3. **修复认证中间件 N+1 查询**PERF-01, 02
- 合并为单次 JOIN 查询
- 将 user.Status 纳入缓存条目
4. **修复导出无界查询**PERF-03
- 添加 LIMIT如 100K 上限)
- 或实现游标分页流式导出
### P1当前迭代解决
5. **标准化响应 Key**CONSISTENCY-02 到 11
- 所有列表端点统一 `{items, total, page, page_size}`
6. **标准化状态类型**CONSISTENCY-19 到 22
- 后端改为接受数字值
7. **修复分页参数**CONSISTENCY-23
- 后端接受 `page/page_size`,内部转换
8. **修复 Dashboard stats 查询**PERF-05
- 合并为单次 GROUP BY 查询
9. **修复 L1Cache 并发**PERF-15
- Get 使用 RLock
10. **修复 goroutine 泄漏**PERF-14
- 添加 context.WithTimeout
### P2下一轮优化
11. **迁移到 PostgreSQL**PERF-28
12. **添加 Redis 共享缓存**PERF-29, 30
13. **前端代码分割**PERF-21
14. **ProfileSecurityPage 拆分**PERF-22
15. **WebhooksPage 服务端过滤**PERF-23
16. **添加响应压缩**PERF-17
17. **实现 Stub 端点**CONSISTENCY-30, 31, 32
---
## 五、审查方法说明
本次审查采用多智能体并行模式:
- **一致性审查智能体**: 交叉比对每个前端服务调用与后端 handler检查 URL、方法、请求体、响应格式、错误处理、数据模型
- **性能审查智能体**: 审查数据库查询、内存使用、并发模式、HTTP 配置、前端 bundle、运行时渲染、网络请求、架构扩展性
审查覆盖:
- 前端: 13 个服务文件 + HTTP 客户端 + 类型定义
- 后端: 所有 handler + repository + service + middleware + cache + 配置
- 架构: 数据库选择、缓存策略、水平扩展能力