整理内容: - 删除 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 ./... 通过
14 KiB
14 KiB
前后端一致性 + 架构性能专项审查报告
审查日期: 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 |
| CONSISTENCY-20 | 角色状态 | 前端发送数字 `0 |
| CONSISTENCY-21 | 权限状态 | 前端发送数字 `0 |
| CONSISTENCY-22 | 设备状态 | 前端发送数字 `0 |
| 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 正确对齐的 API(72 个端点)
✅ 所有 72 个端点的 URL 路径和 HTTP 方法都正确匹配。问题完全在于请求/响应载荷格式,不在于路由。
2.4 修复建议(按优先级)
P0:修复响应协议(阻塞所有功能)
方案 A(推荐):添加 Gin 响应包装中间件
// 拦截所有 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 + SELECT(2 次查询),即使只需要 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 用户前必须完成的变更:
- 迁移到 PostgreSQL
- 合并认证中间件查询为 1-2 次缓存查找
- 添加 Redis 作为共享缓存层
- 流式导出替代内存加载
- 添加自动化日志清理 cron
四、综合建议
P0:立即修复(阻塞生产部署)
-
修复响应格式协议不匹配(CONSISTENCY-01)
- 添加 Gin 响应包装中间件
- 或重写前端 client.ts 接受裸响应
-
修复关键字段错位(CONSISTENCY-12, 13, 14, 15)
- 设备下线:body → header
- 修改密码:current_password → old_password
- TOTP 状态:enabled → totp_enabled
- Capabilities:重写后端响应字段
-
修复认证中间件 N+1 查询(PERF-01, 02)
- 合并为单次 JOIN 查询
- 将 user.Status 纳入缓存条目
-
修复导出无界查询(PERF-03)
- 添加 LIMIT(如 100K 上限)
- 或实现游标分页流式导出
P1:当前迭代解决
-
标准化响应 Key(CONSISTENCY-02 到 11)
- 所有列表端点统一
{items, total, page, page_size}
- 所有列表端点统一
-
标准化状态类型(CONSISTENCY-19 到 22)
- 后端改为接受数字值
-
修复分页参数(CONSISTENCY-23)
- 后端接受
page/page_size,内部转换
- 后端接受
-
修复 Dashboard stats 查询(PERF-05)
- 合并为单次 GROUP BY 查询
-
修复 L1Cache 并发(PERF-15)
- Get 使用 RLock
-
修复 goroutine 泄漏(PERF-14)
- 添加 context.WithTimeout
P2:下一轮优化
- 迁移到 PostgreSQL(PERF-28)
- 添加 Redis 共享缓存(PERF-29, 30)
- 前端代码分割(PERF-21)
- ProfileSecurityPage 拆分(PERF-22)
- WebhooksPage 服务端过滤(PERF-23)
- 添加响应压缩(PERF-17)
- 实现 Stub 端点(CONSISTENCY-30, 31, 32)
五、审查方法说明
本次审查采用多智能体并行模式:
- 一致性审查智能体: 交叉比对每个前端服务调用与后端 handler,检查 URL、方法、请求体、响应格式、错误处理、数据模型
- 性能审查智能体: 审查数据库查询、内存使用、并发模式、HTTP 配置、前端 bundle、运行时渲染、网络请求、架构扩展性
审查覆盖:
- 前端: 13 个服务文件 + HTTP 客户端 + 类型定义
- 后端: 所有 handler + repository + service + middleware + cache + 配置
- 架构: 数据库选择、缓存策略、水平扩展能力