docs: add project review and update development guide
- Add comprehensive PROJECT_REVIEW.md with development experience summary - Update DEV_GUIDE.md with standardized conventions and best practices - Move audit reports to docs/reports/ and update .gitignore - Document Windows compatibility issues and solutions - Add PR checklist and testing standards
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -128,7 +128,11 @@ backend/cmd/server/server
|
|||||||
deploy/docker-compose.override.yml
|
deploy/docker-compose.override.yml
|
||||||
.gocache/
|
.gocache/
|
||||||
vite.config.js
|
vite.config.js
|
||||||
|
# docs/ 目录保留项目文档
|
||||||
|
!docs/
|
||||||
docs/*
|
docs/*
|
||||||
|
!docs/PROJECT_REVIEW.md
|
||||||
|
!docs/reports/
|
||||||
.serena/
|
.serena/
|
||||||
.codex/
|
.codex/
|
||||||
frontend/coverage/
|
frontend/coverage/
|
||||||
|
|||||||
526
DEV_GUIDE.md
526
DEV_GUIDE.md
@@ -1,346 +1,324 @@
|
|||||||
# sub2api 项目开发指南
|
# sub2api 项目开发指南
|
||||||
|
|
||||||
> 本文档记录项目环境配置、常见坑点和注意事项,供 Claude Code 和团队成员参考。
|
> 本文档记录项目环境配置、开发规范和常见问题,供 Claude Code 和团队成员参考。
|
||||||
|
|
||||||
## 一、项目基本信息
|
## 一、项目基本信息
|
||||||
|
|
||||||
| 项目 | 说明 |
|
| 项目 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **上游仓库** | Wei-Shaw/sub2api |
|
| **远程仓库** | https://www.tksea.top/pham/tokens-reef |
|
||||||
| **Fork 仓库** | bayma888/sub2api-bmai |
|
| **技术栈** | Go 后端 (Ent ORM + Gin) + Vue3 前端 |
|
||||||
| **技术栈** | Go 后端 (Ent ORM + Gin) + Vue3 前端 (pnpm) |
|
| **数据库** | PostgreSQL 15+ + Redis 7+ |
|
||||||
| **数据库** | PostgreSQL 16 + Redis |
|
| **包管理** | 后端: go modules, 前端: npm |
|
||||||
| **包管理** | 后端: go modules, 前端: **pnpm**(不是 npm) |
|
|
||||||
|
|
||||||
## 二、本地环境配置
|
---
|
||||||
|
|
||||||
### PostgreSQL 16 (Windows 服务)
|
## 二、项目规范
|
||||||
|
|
||||||
|
### 2.1 代码规范
|
||||||
|
|
||||||
|
#### 后端目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/internal/
|
||||||
|
├── config/ # 配置管理 (viper)
|
||||||
|
├── domain/ # 领域模型
|
||||||
|
├── handler/ # HTTP 处理器
|
||||||
|
├── service/ # 业务逻辑层
|
||||||
|
├── repository/ # 数据访问层
|
||||||
|
├── middleware/ # HTTP 中间件
|
||||||
|
├── pkg/ # 内部工具包
|
||||||
|
├── util/ # 工具函数
|
||||||
|
├── testutil/ # 测试工具和 fixtures
|
||||||
|
└── web/ # 前端嵌入
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 前端目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
frontend/src/
|
||||||
|
├── api/ # API 调用封装
|
||||||
|
├── components/ # 可复用组件
|
||||||
|
├── composables/ # Vue 组合式函数
|
||||||
|
├── stores/ # Pinia 状态管理
|
||||||
|
├── views/ # 页面视图
|
||||||
|
├── types/ # TypeScript 类型定义
|
||||||
|
├── utils/ # 工具函数
|
||||||
|
└── i18n/ # 国际化
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 命名约定
|
||||||
|
|
||||||
|
| 类型 | 约定 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| 接口 | PascalCase + 后缀 | `UserService`, `AccountRepository` |
|
||||||
|
| 实现 | camelCase + 后缀 | `userService`, `accountRepository` |
|
||||||
|
| 测试文件 | 源文件_test.go | `user_service_test.go` |
|
||||||
|
| 测试 Stub | stub + 名称 | `stubUserRepo` |
|
||||||
|
| 前端组件 | PascalCase | `UserList.vue` |
|
||||||
|
| 前端测试 | __tests__/xxx.spec.ts | `UserList.spec.ts` |
|
||||||
|
|
||||||
|
### 2.3 Git 提交规范
|
||||||
|
|
||||||
|
**格式**:
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Type 类型**:
|
||||||
|
- `feat`: 新功能
|
||||||
|
- `fix`: Bug 修复
|
||||||
|
- `docs`: 文档更新
|
||||||
|
- `refactor`: 重构
|
||||||
|
- `test`: 测试相关
|
||||||
|
- `chore`: 构建/工具/清理
|
||||||
|
- `security`: 安全修复
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```
|
||||||
|
fix(config): resolve Windows path resolution issue
|
||||||
|
|
||||||
|
- Add optional configPaths parameter to load()
|
||||||
|
- Use t.TempDir() for test isolation
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 测试规范
|
||||||
|
|
||||||
|
**后端测试**:
|
||||||
|
```bash
|
||||||
|
# 单元测试
|
||||||
|
go test -tags=unit ./...
|
||||||
|
|
||||||
|
# 集成测试(需要数据库)
|
||||||
|
go test -tags=integration ./...
|
||||||
|
|
||||||
|
# 特定包
|
||||||
|
go test -tags=unit ./internal/service/ -v
|
||||||
|
```
|
||||||
|
|
||||||
|
**前端测试**:
|
||||||
|
```bash
|
||||||
|
# 运行测试
|
||||||
|
npm run test:run
|
||||||
|
|
||||||
|
# 监听模式
|
||||||
|
npm run test
|
||||||
|
|
||||||
|
# 覆盖率
|
||||||
|
npm run test:coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
**测试原则**:
|
||||||
|
1. 测试文件与源码同目录(Go: `*_test.go`, Vue: `__tests__/`)
|
||||||
|
2. 使用 `t.TempDir()` 创建隔离临时目录
|
||||||
|
3. 清理资源:关闭文件、释放锁、重置全局状态
|
||||||
|
4. Windows 兼容:注意路径、pipe、文件锁差异
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、本地环境配置
|
||||||
|
|
||||||
|
### 3.1 PostgreSQL
|
||||||
|
|
||||||
| 配置项 | 值 |
|
| 配置项 | 值 |
|
||||||
|--------|-----|
|
|--------|-----|
|
||||||
| 端口 | 5432 |
|
| 端口 | 5432 |
|
||||||
| psql 路径 | `C:\Program Files\PostgreSQL\16\bin\psql.exe` |
|
| 数据库 | sub2api |
|
||||||
| pg_hba.conf | `C:\Program Files\PostgreSQL\16\data\pg_hba.conf` |
|
| 用户 | sub2api / sub2api |
|
||||||
| 数据库凭据 | user=`sub2api`, password=`sub2api`, dbname=`sub2api` |
|
| 超级用户 | postgres / postgres |
|
||||||
| 超级用户 | user=`postgres`, password=`postgres` |
|
|
||||||
|
|
||||||
### Redis
|
### 3.2 Redis
|
||||||
|
|
||||||
| 配置项 | 值 |
|
| 配置项 | 值 |
|
||||||
|--------|-----|
|
|--------|-----|
|
||||||
| 端口 | 6379 |
|
| 端口 | 6379 |
|
||||||
| 密码 | 无 |
|
| 密码 | 无 |
|
||||||
|
|
||||||
### 开发工具
|
### 3.3 开发工具
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# golangci-lint v2.7
|
# golangci-lint
|
||||||
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.7
|
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||||
|
|
||||||
# pnpm (前端包管理)
|
# Ent 代码生成
|
||||||
npm install -g pnpm
|
go install entgo.io/ent/cmd/ent@latest
|
||||||
```
|
|
||||||
|
|
||||||
## 三、CI/CD 流水线
|
|
||||||
|
|
||||||
### GitHub Actions Workflows
|
|
||||||
|
|
||||||
| Workflow | 触发条件 | 检查内容 |
|
|
||||||
|----------|----------|----------|
|
|
||||||
| **backend-ci.yml** | push, pull_request | 单元测试 + 集成测试 + golangci-lint v2.7 |
|
|
||||||
| **security-scan.yml** | push, pull_request, 每周一 | govulncheck + gosec + pnpm audit |
|
|
||||||
| **release.yml** | tag `v*` | 构建发布(PR 不触发) |
|
|
||||||
|
|
||||||
### CI 要求
|
|
||||||
|
|
||||||
- Go 版本必须是 **1.25.7**
|
|
||||||
- 前端使用 `pnpm install --frozen-lockfile`,必须提交 `pnpm-lock.yaml`
|
|
||||||
|
|
||||||
### 本地测试命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 后端单元测试
|
|
||||||
cd backend && go test -tags=unit ./...
|
|
||||||
|
|
||||||
# 后端集成测试
|
|
||||||
cd backend && go test -tags=integration ./...
|
|
||||||
|
|
||||||
# 代码质量检查
|
|
||||||
cd backend && golangci-lint run ./...
|
|
||||||
|
|
||||||
# 前端依赖安装(必须用 pnpm)
|
|
||||||
cd frontend && pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、常见坑点 & 解决方案
|
|
||||||
|
|
||||||
### 坑 1:pnpm-lock.yaml 必须同步提交
|
|
||||||
|
|
||||||
**问题**:`package.json` 新增依赖后,CI 的 `pnpm install --frozen-lockfile` 失败。
|
|
||||||
|
|
||||||
**原因**:上游 CI 使用 pnpm,lock 文件不同步会报错。
|
|
||||||
|
|
||||||
**解决**:
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
pnpm install # 更新 pnpm-lock.yaml
|
|
||||||
git add pnpm-lock.yaml
|
|
||||||
git commit -m "chore: update pnpm-lock.yaml"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 坑 2:npm 和 pnpm 的 node_modules 冲突
|
## 四、常用命令
|
||||||
|
|
||||||
**问题**:之前用 npm 装过 `node_modules`,pnpm install 报 `EPERM` 错误。
|
### 4.1 后端命令
|
||||||
|
|
||||||
**解决**:
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
# 运行服务
|
||||||
rm -rf node_modules # 或 PowerShell: Remove-Item -Recurse -Force node_modules
|
go run ./cmd/server/
|
||||||
pnpm install
|
|
||||||
|
# 构建
|
||||||
|
go build -o bin/server ./cmd/server/
|
||||||
|
|
||||||
|
# 生成 Ent 代码(修改 schema 后必须执行)
|
||||||
|
go generate ./ent
|
||||||
|
|
||||||
|
# 单元测试
|
||||||
|
go test -tags=unit ./...
|
||||||
|
|
||||||
|
# 集成测试
|
||||||
|
go test -tags=integration ./...
|
||||||
|
|
||||||
|
# Lint 检查
|
||||||
|
golangci-lint run ./...
|
||||||
|
|
||||||
|
# 检查所有
|
||||||
|
go test -tags=unit ./... && golangci-lint run ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 前端命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 开发服务器
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 构建
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 类型检查
|
||||||
|
npx vue-tsc --noEmit
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
npm run test:run
|
||||||
|
|
||||||
|
# Lint
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 数据库命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 连接数据库
|
||||||
|
psql -U sub2api -h 127.0.0.1 -d sub2api
|
||||||
|
|
||||||
|
# 执行迁移
|
||||||
|
psql -U sub2api -h 127.0.0.1 -d sub2api -f migrations/xxx.sql
|
||||||
|
|
||||||
|
# 查看表结构
|
||||||
|
\dt
|
||||||
|
\d table_name
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 坑 3:PowerShell 中 bcrypt hash 的 `$` 被转义
|
## 五、常见问题
|
||||||
|
|
||||||
**问题**:bcrypt hash 格式如 `$2a$10$xxx...`,PowerShell 把 `$2a` 当变量解析,导致数据丢失。
|
### 5.1 Ent Schema 修改后不生效
|
||||||
|
|
||||||
**解决**:将 SQL 写入文件,用 `psql -f` 执行:
|
**原因**: Ent 是代码生成工具,修改 schema 后需要重新生成
|
||||||
|
|
||||||
|
**解决**:
|
||||||
```bash
|
```bash
|
||||||
# 错误示范(PowerShell 会吃掉 $)
|
|
||||||
psql -c "INSERT INTO users ... VALUES ('$2a$10$...')"
|
|
||||||
|
|
||||||
# 正确做法
|
|
||||||
echo "INSERT INTO users ... VALUES ('\$2a\$10\$...')" > temp.sql
|
|
||||||
psql -U sub2api -h 127.0.0.1 -d sub2api -f temp.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 坑 4:psql 不支持中文路径
|
|
||||||
|
|
||||||
**问题**:`psql -f "D:\中文路径\file.sql"` 报错找不到文件。
|
|
||||||
|
|
||||||
**解决**:复制到纯英文路径再执行:
|
|
||||||
```bash
|
|
||||||
cp "D:\中文路径\file.sql" "C:\temp.sql"
|
|
||||||
psql -f "C:\temp.sql"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 坑 5:PostgreSQL 密码重置流程
|
|
||||||
|
|
||||||
**场景**:忘记 PostgreSQL 密码。
|
|
||||||
|
|
||||||
**步骤**:
|
|
||||||
1. 修改 `C:\Program Files\PostgreSQL\16\data\pg_hba.conf`
|
|
||||||
```
|
|
||||||
# 将 scram-sha-256 改为 trust
|
|
||||||
host all all 127.0.0.1/32 trust
|
|
||||||
```
|
|
||||||
2. 重启 PostgreSQL 服务
|
|
||||||
```powershell
|
|
||||||
Restart-Service postgresql-x64-16
|
|
||||||
```
|
|
||||||
3. 无密码登录并重置
|
|
||||||
```bash
|
|
||||||
psql -U postgres -h 127.0.0.1
|
|
||||||
ALTER USER sub2api WITH PASSWORD 'sub2api';
|
|
||||||
ALTER USER postgres WITH PASSWORD 'postgres';
|
|
||||||
```
|
|
||||||
4. 改回 `scram-sha-256` 并重启
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 坑 6:Go interface 新增方法后 test stub 必须补全
|
|
||||||
|
|
||||||
**问题**:给 interface 新增方法后,编译报错 `does not implement interface (missing method XXX)`。
|
|
||||||
|
|
||||||
**原因**:所有测试文件中实现该 interface 的 stub/mock 都必须补上新方法。
|
|
||||||
|
|
||||||
**解决**:
|
|
||||||
```bash
|
|
||||||
# 搜索所有实现该 interface 的 struct
|
|
||||||
cd backend
|
cd backend
|
||||||
|
go generate ./ent
|
||||||
|
git add ent/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 接口新增方法后编译失败
|
||||||
|
|
||||||
|
**原因**: 所有实现该接口的 mock/stub 都需要补全新方法
|
||||||
|
|
||||||
|
**解决**:
|
||||||
|
```bash
|
||||||
|
# 搜索所有 stub
|
||||||
grep -r "type.*Stub.*struct" internal/
|
grep -r "type.*Stub.*struct" internal/
|
||||||
grep -r "type.*Mock.*struct" internal/
|
grep -r "type.*Mock.*struct" internal/
|
||||||
|
|
||||||
# 逐一补全新方法
|
# 逐一补全新方法
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 5.3 Windows 上测试超时
|
||||||
|
|
||||||
### 坑 7:Windows 上 psql 连 localhost 的 IPv6 问题
|
**原因**: Windows pipe 的 Sync() 会阻塞
|
||||||
|
|
||||||
**问题**:psql 连 `localhost` 先尝试 IPv6 (::1),可能报错后再回退 IPv4。
|
**解决**: 先关闭 write end 再调用 Sync
|
||||||
|
```go
|
||||||
**建议**:直接用 `127.0.0.1` 代替 `localhost`。
|
_ = stdoutW.Close()
|
||||||
|
_ = stderrW.Close()
|
||||||
---
|
Sync()
|
||||||
|
|
||||||
### 坑 8:Windows 没有 make 命令
|
|
||||||
|
|
||||||
**问题**:CI 里用 `make test-unit`,本地 Windows 没有 make。
|
|
||||||
|
|
||||||
**解决**:直接用 Makefile 里的原始命令:
|
|
||||||
```bash
|
|
||||||
# 代替 make test-unit
|
|
||||||
go test -tags=unit ./...
|
|
||||||
|
|
||||||
# 代替 make test-integration
|
|
||||||
go test -tags=integration ./...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 5.4 配置测试读取了错误的文件
|
||||||
|
|
||||||
### 坑 9:Ent Schema 修改后必须重新生成
|
**原因**: viper 搜索路径包含意外的配置文件
|
||||||
|
|
||||||
**问题**:修改 `ent/schema/*.go` 后,代码不生效。
|
**解决**: 使用可选参数传入测试目录
|
||||||
|
```go
|
||||||
|
// 生产
|
||||||
|
load(false)
|
||||||
|
|
||||||
**解决**:
|
// 测试
|
||||||
```bash
|
load(false, t.TempDir())
|
||||||
cd backend
|
|
||||||
go generate ./ent # 重新生成 ent 代码
|
|
||||||
git add ent/ # 生成的文件也要提交
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 5.5 PowerShell 变量转义问题
|
||||||
|
|
||||||
### 坑 10:前端测试看似正常,但后端调用失败(模型映射被批量误改)
|
**问题**: bcrypt hash 中的 `$` 被解析为变量
|
||||||
|
|
||||||
**典型现象**:
|
**解决**: 使用文件执行 SQL
|
||||||
- 前端按钮点测看起来正常;
|
```bash
|
||||||
- 实际通过 API/客户端调用时返回 `Service temporarily unavailable` 或提示无可用账号;
|
echo "INSERT INTO users ... VALUES ('\$2a\$10\$...')" > temp.sql
|
||||||
- 常见于 OpenAI 账号(例如 Codex 模型)在批量修改后突然不可用。
|
psql -f temp.sql
|
||||||
|
```
|
||||||
|
|
||||||
**根因**:
|
### 5.6 psql 不支持中文路径
|
||||||
- OpenAI 账号编辑页默认不显式展示映射规则,容易让人误以为“没映射也没关系”;
|
|
||||||
- 但在**批量修改同时选中不同平台账号**(OpenAI + Antigravity/Gemini)时,模型白名单/映射可能被跨平台策略覆盖;
|
|
||||||
- 结果是 OpenAI 账号的关键模型映射丢失或被改坏,后端选不到可用账号。
|
|
||||||
|
|
||||||
**修复方案(按优先级)**:
|
**解决**: 复制到英文路径再执行
|
||||||
1. **快速修复(推荐)**:在批量修改中补回正确的透传映射(例如 `gpt-5.3-codex -> gpt-5.3-codex-spark`)。
|
```bash
|
||||||
2. **彻底重建**:删除并重新添加全部相关账号(最稳但成本高)。
|
cp "D:\中文\file.sql" "C:\temp.sql"
|
||||||
|
psql -f "C:\temp.sql"
|
||||||
|
```
|
||||||
|
|
||||||
**关键经验**:
|
### 5.7 PostgreSQL 密码重置
|
||||||
- 如果某模型已被软件内置默认映射覆盖,通常不需要额外再加透传;
|
|
||||||
- 但当上游模型更新快于本仓库默认映射时,**手动批量添加透传映射**是最简单、最低风险的临时兜底方案;
|
1. 修改 `pg_hba.conf`,将 `scram-sha-256` 改为 `trust`
|
||||||
- 批量操作前尽量按平台分组,不要混选不同平台账号。
|
2. 重启服务: `Restart-Service postgresql-x64-16`
|
||||||
|
3. 无密码登录重置密码
|
||||||
|
4. 改回 `scram-sha-256` 并重启
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 坑 11:PR 提交前检查清单
|
## 六、PR 检查清单
|
||||||
|
|
||||||
提交 PR 前务必本地验证:
|
提交前务必验证:
|
||||||
|
|
||||||
- [ ] `go test -tags=unit ./...` 通过
|
- [ ] `go test -tags=unit ./...` 通过
|
||||||
- [ ] `go test -tags=integration ./...` 通过
|
- [ ] `go test -tags=integration ./...` 通过(如适用)
|
||||||
- [ ] `golangci-lint run ./...` 无新增问题
|
- [ ] `golangci-lint run ./...` 无新增问题
|
||||||
- [ ] `pnpm-lock.yaml` 已同步(如果改了 package.json)
|
- [ ] `npm run test:run` 通过
|
||||||
- [ ] 所有 test stub 补全新接口方法(如果改了 interface)
|
- [ ] `npx vue-tsc --noEmit` 无类型错误
|
||||||
- [ ] Ent 生成的代码已提交(如果改了 schema)
|
- [ ] Ent 生成的代码已提交(如改了 schema)
|
||||||
|
- [ ] 测试 stub 已更新(如改了 interface)
|
||||||
|
- [ ] 无敏感信息泄露
|
||||||
|
|
||||||
## 五、常用命令速查
|
---
|
||||||
|
|
||||||
### 数据库操作
|
## 七、CI/CD 流水线
|
||||||
|
|
||||||
```bash
|
| Workflow | 触发条件 | 检查内容 |
|
||||||
# 连接数据库
|
|----------|----------|----------|
|
||||||
psql -U sub2api -h 127.0.0.1 -d sub2api
|
| backend-ci.yml | push, PR | 单元测试 + Lint |
|
||||||
|
| security-scan.yml | push, PR, 周一 | 安全扫描 |
|
||||||
|
| release.yml | tag v* | 构建发布 |
|
||||||
|
|
||||||
# 查看所有用户
|
---
|
||||||
psql -U postgres -h 127.0.0.1 -c "\du"
|
|
||||||
|
|
||||||
# 查看所有数据库
|
## 八、参考资源
|
||||||
psql -U postgres -h 127.0.0.1 -c "\l"
|
|
||||||
|
|
||||||
# 执行 SQL 文件
|
- [项目复盘总结](docs/PROJECT_REVIEW.md)
|
||||||
psql -U sub2api -h 127.0.0.1 -d sub2api -f migration.sql
|
- [审查报告](docs/reports/)
|
||||||
```
|
- [Ent 文档](https://entgo.io/)
|
||||||
|
|
||||||
### Git 操作
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 同步上游
|
|
||||||
git fetch upstream
|
|
||||||
git checkout main
|
|
||||||
git merge upstream/main
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
# 创建功能分支
|
|
||||||
git checkout -b feature/xxx
|
|
||||||
|
|
||||||
# Rebase 到最新 main
|
|
||||||
git fetch upstream
|
|
||||||
git rebase upstream/main
|
|
||||||
```
|
|
||||||
|
|
||||||
### 前端操作
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 安装依赖(必须用 pnpm)
|
|
||||||
cd frontend
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# 开发服务器
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# 构建
|
|
||||||
pnpm build
|
|
||||||
```
|
|
||||||
|
|
||||||
### 后端操作
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 运行服务器
|
|
||||||
cd backend
|
|
||||||
go run ./cmd/server/
|
|
||||||
|
|
||||||
# 生成 Ent 代码
|
|
||||||
go generate ./ent
|
|
||||||
|
|
||||||
# 运行测试
|
|
||||||
go test -tags=unit ./...
|
|
||||||
go test -tags=integration ./...
|
|
||||||
|
|
||||||
# Lint 检查
|
|
||||||
golangci-lint run ./...
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、项目结构速览
|
|
||||||
|
|
||||||
```
|
|
||||||
sub2api-bmai/
|
|
||||||
├── backend/
|
|
||||||
│ ├── cmd/server/ # 主程序入口
|
|
||||||
│ ├── ent/ # Ent ORM 生成代码
|
|
||||||
│ │ └── schema/ # 数据库 Schema 定义
|
|
||||||
│ ├── internal/
|
|
||||||
│ │ ├── handler/ # HTTP 处理器
|
|
||||||
│ │ ├── service/ # 业务逻辑
|
|
||||||
│ │ ├── repository/ # 数据访问层
|
|
||||||
│ │ └── server/ # 服务器配置
|
|
||||||
│ ├── migrations/ # 数据库迁移脚本
|
|
||||||
│ └── config.yaml # 配置文件
|
|
||||||
├── frontend/
|
|
||||||
│ ├── src/
|
|
||||||
│ │ ├── api/ # API 调用
|
|
||||||
│ │ ├── components/ # Vue 组件
|
|
||||||
│ │ ├── views/ # 页面视图
|
|
||||||
│ │ ├── types/ # TypeScript 类型
|
|
||||||
│ │ └── i18n/ # 国际化
|
|
||||||
│ ├── package.json # 依赖配置
|
|
||||||
│ └── pnpm-lock.yaml # pnpm 锁文件(必须提交)
|
|
||||||
└── .claude/
|
|
||||||
└── CLAUDE.md # 本文档
|
|
||||||
```
|
|
||||||
|
|
||||||
## 七、参考资源
|
|
||||||
|
|
||||||
- [上游仓库](https://github.com/Wei-Shaw/sub2api)
|
|
||||||
- [Ent 文档](https://entgo.io/docs/getting-started)
|
|
||||||
- [Vue 3 文档](https://vuejs.org/)
|
- [Vue 3 文档](https://vuejs.org/)
|
||||||
- [pnpm 文档](https://pnpm.io/)
|
- [Gin 文档](https://gin-gonic.com/)
|
||||||
|
|||||||
336
docs/PROJECT_REVIEW.md
Normal file
336
docs/PROJECT_REVIEW.md
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
# Sub2API 项目复盘总结
|
||||||
|
|
||||||
|
**复盘日期**: 2026/04/13
|
||||||
|
**项目版本**: main (d44fa6b)
|
||||||
|
**复盘范围**: 全项目开发、测试、部署经验
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、项目概况
|
||||||
|
|
||||||
|
### 1.1 项目定位
|
||||||
|
|
||||||
|
**Sub2API** 是一个企业级 AI API 网关平台,核心功能:
|
||||||
|
- 多平台 AI 服务聚合(OpenAI、Gemini、Anthropic、Sora 等)
|
||||||
|
- API Key 管理与认证
|
||||||
|
- 用户订阅与计费
|
||||||
|
- 负载均衡与智能路由
|
||||||
|
- 运维监控与告警
|
||||||
|
|
||||||
|
### 1.2 项目规模
|
||||||
|
|
||||||
|
| 指标 | 数量 |
|
||||||
|
|------|------|
|
||||||
|
| 后端 Go 文件 | 450+ 个 |
|
||||||
|
| 后端代码行数 | ~370,000 行 |
|
||||||
|
| 后端测试文件 | 383 个 |
|
||||||
|
| 前端 TypeScript/Vue 文件 | 310 个 |
|
||||||
|
| 前端组件 | 114 个 |
|
||||||
|
| 前端视图 | 53 个 |
|
||||||
|
| 前端测试用例 | 301 个 |
|
||||||
|
| 数据库迁移 | 97 个 |
|
||||||
|
|
||||||
|
### 1.3 技术栈
|
||||||
|
|
||||||
|
| 层级 | 技术选型 |
|
||||||
|
|------|----------|
|
||||||
|
| 后端框架 | Go 1.26+ / Gin |
|
||||||
|
| ORM | Ent (Facebook) |
|
||||||
|
| 依赖注入 | Google Wire |
|
||||||
|
| 前端框架 | Vue 3 + TypeScript |
|
||||||
|
| 状态管理 | Pinia |
|
||||||
|
| 样式方案 | TailwindCSS |
|
||||||
|
| 构建工具 | Vite |
|
||||||
|
| 数据库 | PostgreSQL 15+ |
|
||||||
|
| 缓存 | Redis 7+ |
|
||||||
|
| 容器化 | Docker + Docker Compose |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、开发经验总结
|
||||||
|
|
||||||
|
### 2.1 测试相关经验
|
||||||
|
|
||||||
|
#### 经验 1: Windows 平台测试兼容性
|
||||||
|
|
||||||
|
**问题**: 3 个测试包在 Windows 上失败
|
||||||
|
- `config` 包:配置路径解析差异(`/app/data` → `D:\app\data`)
|
||||||
|
- `handler` 包:测试 stub 返回值错误
|
||||||
|
- `logger` 包:`Sync()` 在 pipe 上阻塞
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```go
|
||||||
|
// config: 添加可选参数实现测试隔离
|
||||||
|
func load(allowMissingJWTSecret bool, configPaths ...string) (*Config, error)
|
||||||
|
|
||||||
|
// logger: 先关闭 write end 再 Sync
|
||||||
|
_ = stdoutW.Close()
|
||||||
|
Sync()
|
||||||
|
|
||||||
|
// logger: 添加 Reset() 关闭文件句柄
|
||||||
|
func Reset() {
|
||||||
|
if fileCloser != nil {
|
||||||
|
_ = fileCloser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**教训**: 跨平台开发必须考虑:
|
||||||
|
1. 路径分隔符差异
|
||||||
|
2. 文件锁定机制差异
|
||||||
|
3. Pipe/Socket 行为差异
|
||||||
|
|
||||||
|
#### 经验 2: 测试隔离原则
|
||||||
|
|
||||||
|
**问题**: 测试相互干扰,配置文件被意外读取
|
||||||
|
|
||||||
|
**最佳实践**:
|
||||||
|
- 使用 `t.TempDir()` 创建隔离临时目录
|
||||||
|
- 每个测试独立的配置/数据目录
|
||||||
|
- Cleanup 函数确保资源释放
|
||||||
|
- 避免全局状态污染
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestXXX(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir() // 自动清理
|
||||||
|
// 使用 tmpDir 作为测试数据目录
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 代码质量经验
|
||||||
|
|
||||||
|
#### 经验 3: 接口变更的测试维护
|
||||||
|
|
||||||
|
**问题**: 接口新增方法后,所有 mock/stub 编译失败
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
1. 使用 `grep -r "type.*Stub.*struct"` 定位所有 stub
|
||||||
|
2. 批量补全新方法
|
||||||
|
3. 考虑使用 mockery 等代码生成工具
|
||||||
|
|
||||||
|
#### 经验 4: Ent Schema 变更流程
|
||||||
|
|
||||||
|
**问题**: 修改 schema 后代码不生效
|
||||||
|
|
||||||
|
**正确流程**:
|
||||||
|
```bash
|
||||||
|
# 1. 修改 ent/schema/*.go
|
||||||
|
# 2. 重新生成
|
||||||
|
go generate ./ent
|
||||||
|
# 3. 提交生成的代码
|
||||||
|
git add ent/
|
||||||
|
# 4. 可能需要数据库迁移
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 前端开发经验
|
||||||
|
|
||||||
|
#### 经验 5: 包管理器一致性
|
||||||
|
|
||||||
|
**问题**: 项目使用 pnpm,但误用 npm 导致 lock 文件冲突
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
- 统一使用单一包管理器
|
||||||
|
- 删除 `pnpm-lock.yaml` 或 `package-lock.yaml` 其中之一
|
||||||
|
- CI 必须与本地保持一致
|
||||||
|
|
||||||
|
#### 经验 6: 模型映射管理
|
||||||
|
|
||||||
|
**问题**: 批量编辑跨平台账号时,模型映射被错误覆盖
|
||||||
|
|
||||||
|
**教训**:
|
||||||
|
- 批量操作按平台分组
|
||||||
|
- 关键映射变更需二次确认
|
||||||
|
- 模型映射应有版本/审计追踪
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、项目规范更新
|
||||||
|
|
||||||
|
### 3.1 代码规范
|
||||||
|
|
||||||
|
#### Go 后端规范
|
||||||
|
|
||||||
|
1. **目录结构**
|
||||||
|
```
|
||||||
|
internal/
|
||||||
|
├── config/ # 配置(viper)
|
||||||
|
├── domain/ # 领域模型
|
||||||
|
├── handler/ # HTTP 处理器
|
||||||
|
├── service/ # 业务逻辑
|
||||||
|
├── repository/ # 数据访问
|
||||||
|
├── pkg/ # 内部工具包
|
||||||
|
└── testutil/ # 测试工具
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **命名约定**
|
||||||
|
- 接口:`XXXService`, `XXXRepository`
|
||||||
|
- 实现:`xxxService`, `xxxRepository`
|
||||||
|
- 测试文件:`xxx_test.go`
|
||||||
|
- 测试 stub:`stubXXX`, `mockXXX`
|
||||||
|
|
||||||
|
3. **错误处理**
|
||||||
|
- 使用 `internal/pkg/errors` 定义错误码
|
||||||
|
- 业务错误返回结构化响应
|
||||||
|
- 日志脱敏敏感信息
|
||||||
|
|
||||||
|
4. **测试规范**
|
||||||
|
- 单元测试:`go test -tags=unit`
|
||||||
|
- 集成测试:`go test -tags=integration`
|
||||||
|
- 测试覆盖率目标:80%+
|
||||||
|
|
||||||
|
#### Vue 前端规范
|
||||||
|
|
||||||
|
1. **目录结构**
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── api/ # API 调用
|
||||||
|
├── components/ # 可复用组件
|
||||||
|
├── composables/ # 组合式函数
|
||||||
|
├── stores/ # Pinia 状态
|
||||||
|
├── views/ # 页面视图
|
||||||
|
├── types/ # TypeScript 类型
|
||||||
|
└── utils/ # 工具函数
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **测试规范**
|
||||||
|
- 测试文件:`__tests__/xxx.spec.ts`
|
||||||
|
- 使用 Vitest 运行测试
|
||||||
|
- 组件测试关注用户交互
|
||||||
|
|
||||||
|
### 3.2 Git 规范
|
||||||
|
|
||||||
|
#### 提交信息格式
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Type 类型**:
|
||||||
|
- `feat`: 新功能
|
||||||
|
- `fix`: Bug 修复
|
||||||
|
- `docs`: 文档更新
|
||||||
|
- `style`: 代码格式
|
||||||
|
- `refactor`: 重构
|
||||||
|
- `test`: 测试相关
|
||||||
|
- `chore`: 构建/工具
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```
|
||||||
|
fix(logger): resolve Sync() blocking on Windows pipes
|
||||||
|
|
||||||
|
- Move pipe close before Sync() call
|
||||||
|
- Add Reset() function for file handle cleanup
|
||||||
|
|
||||||
|
Closes #123
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 分支策略
|
||||||
|
|
||||||
|
```
|
||||||
|
main # 主分支,稳定可发布
|
||||||
|
├── develop # 开发分支
|
||||||
|
├── feature/* # 功能分支
|
||||||
|
├── fix/* # 修复分支
|
||||||
|
└── release/* # 发布分支
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 PR 检查清单
|
||||||
|
|
||||||
|
**提交前必须验证**:
|
||||||
|
|
||||||
|
- [ ] `go test -tags=unit ./...` 通过
|
||||||
|
- [ ] `go test -tags=integration ./...` 通过(如适用)
|
||||||
|
- [ ] `golangci-lint run ./...` 无新增问题
|
||||||
|
- [ ] `npx vitest run` 通过
|
||||||
|
- [ ] `npx vue-tsc --noEmit` 无类型错误
|
||||||
|
- [ ] Ent 生成的代码已提交
|
||||||
|
- [ ] 测试 stub 已更新
|
||||||
|
- [ ] 敏感信息已移除
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、工具与配置
|
||||||
|
|
||||||
|
### 4.1 推荐开发工具
|
||||||
|
|
||||||
|
| 工具 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| golangci-lint | Go 代码检查 |
|
||||||
|
| mockery | Mock 代码生成 |
|
||||||
|
| vue-tsc | TypeScript 检查 |
|
||||||
|
| vitest | 前端单元测试 |
|
||||||
|
| pnpm | 前端包管理 |
|
||||||
|
|
||||||
|
### 4.2 IDE 配置
|
||||||
|
|
||||||
|
**VS Code 推荐扩展**:
|
||||||
|
- Go
|
||||||
|
- Vue - Official
|
||||||
|
- ESLint
|
||||||
|
- Prettier
|
||||||
|
- Tailwind CSS IntelliSense
|
||||||
|
|
||||||
|
### 4.3 CI/CD 配置
|
||||||
|
|
||||||
|
| Workflow | 触发 | 内容 |
|
||||||
|
|----------|------|------|
|
||||||
|
| backend-ci.yml | push, PR | 单元测试 + Lint |
|
||||||
|
| security-scan.yml | push, PR, 周一 | 安全扫描 |
|
||||||
|
| release.yml | tag v* | 构建发布 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、常见问题速查
|
||||||
|
|
||||||
|
### 5.1 环境问题
|
||||||
|
|
||||||
|
| 问题 | 解决方案 |
|
||||||
|
|------|----------|
|
||||||
|
| pnpm lock 冲突 | 删除 node_modules 后重新安装 |
|
||||||
|
| PowerShell 变量转义 | 使用文件执行 SQL |
|
||||||
|
| psql 中文路径 | 复制到英文路径 |
|
||||||
|
| PostgreSQL 密码重置 | 修改 pg_hba.conf 为 trust |
|
||||||
|
|
||||||
|
### 5.2 代码问题
|
||||||
|
|
||||||
|
| 问题 | 解决方案 |
|
||||||
|
|------|----------|
|
||||||
|
| 接口未实现 | 补全 mock/stub 方法 |
|
||||||
|
| Ent 代码过期 | `go generate ./ent` |
|
||||||
|
| 测试超时 | 检查 pipe/锁/资源释放 |
|
||||||
|
| 路径解析错误 | 使用 filepath 代替硬编码 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、后续改进建议
|
||||||
|
|
||||||
|
### 6.1 短期优化
|
||||||
|
|
||||||
|
1. **测试覆盖率**: 提升核心业务模块覆盖率至 85%+
|
||||||
|
2. **文档完善**: 补充 API 文档和架构图
|
||||||
|
3. **错误码整理**: 统一错误码规范
|
||||||
|
|
||||||
|
### 6.2 中期规划
|
||||||
|
|
||||||
|
1. **性能优化**: 数据库查询优化、缓存策略
|
||||||
|
2. **可观测性**: 完善监控指标和告警
|
||||||
|
3. **安全加固**: 定期依赖扫描、安全审计
|
||||||
|
|
||||||
|
### 6.3 长期演进
|
||||||
|
|
||||||
|
1. **微服务拆分**: 按业务域拆分服务
|
||||||
|
2. **多云部署**: 支持多区域部署
|
||||||
|
3. **API 版本管理**: 版本化 API 设计
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、参考资源
|
||||||
|
|
||||||
|
- [Go 代码审查建议](https://github.com/golang/go/wiki/CodeReviewComments)
|
||||||
|
- [Vue 风格指南](https://vuejs.org/style-guide/)
|
||||||
|
- [Ent 文档](https://entgo.io/)
|
||||||
|
- [Google Wire](https://github.com/google/wire)
|
||||||
288
docs/reports/CHINESE_MODELS_AUDIT.md
Normal file
288
docs/reports/CHINESE_MODELS_AUDIT.md
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
# Sub2API 国内模型支持审查报告
|
||||||
|
|
||||||
|
**审查日期**: 2026/04/12
|
||||||
|
**项目路径**: D:/project/sub2api
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、国内模型支持现状
|
||||||
|
|
||||||
|
### 1.1 已实现的国内模型支持
|
||||||
|
|
||||||
|
#### 前端模型白名单 (`frontend/src/composables/useModelWhitelist.ts`)
|
||||||
|
|
||||||
|
| 厂商 | 模型系列 | 状态 |
|
||||||
|
|------|----------|------|
|
||||||
|
| **智谱 GLM** | glm-4, glm-4v, glm-4-plus, glm-4-air, glm-4-long, glm-4-flash, glm-4.5, glm-4.6, glm-3-turbo, chatglm_*, cogview-3, cogvideo | ✅ 已添加 |
|
||||||
|
| **阿里通义** | qwen-turbo, qwen-plus, qwen-max, qwen-long, qwen2-*, qwen2.5-*, qwen3-*, qwq-32b | ✅ 已添加 |
|
||||||
|
| **DeepSeek** | deepseek-chat, deepseek-coder, deepseek-reasoner, deepseek-v3, deepseek-r1, deepseek-r1-distill-* | ✅ 已添加 |
|
||||||
|
| **月之暗面** | moonshot-v1-8k/32k/128k, kimi-latest | ✅ 已添加 |
|
||||||
|
| **字节豆包** | doubao-pro-*, doubao-lite-*, doubao-vision-*, doubao-1.5-* | ✅ 已添加 |
|
||||||
|
| **MiniMax** | abab6.5-chat, abab6.5s-chat, abab6-chat, abab5.5-chat | ✅ 已添加 |
|
||||||
|
| **百度文心** | ernie-4.0-*, ernie-3.5-*, ernie-speed-*, ernie-lite-*, ernie-tiny | ✅ 已添加 |
|
||||||
|
| **讯飞星火** | spark-desk, spark-lite, spark-pro, spark-max, spark-ultra | ✅ 已添加 |
|
||||||
|
| **腾讯混元** | hunyuan-lite, hunyuan-standard, hunyuan-pro, hunyuan-turbo, hunyuan-large, hunyuan-vision, hunyuan-code | ✅ 已添加 |
|
||||||
|
| **零一万物** | yi-large, yi-medium, yi-spark, yi-vision, yi-1.5-* | ✅ 已添加 |
|
||||||
|
|
||||||
|
#### 前端模型图标 (`frontend/src/components/common/ModelIcon.vue`)
|
||||||
|
|
||||||
|
| 厂商 | 图标支持 |
|
||||||
|
|------|----------|
|
||||||
|
| 阿里通义 (qwen) | ✅ |
|
||||||
|
| DeepSeek | ✅ |
|
||||||
|
| 月之暗面 (moonshot/kimi) | ✅ |
|
||||||
|
| MiniMax | ✅ |
|
||||||
|
| 智谱 GLM | ❌ 缺失 |
|
||||||
|
| 字节豆包 | ❌ 缺失 |
|
||||||
|
| 百度文心 | ❌ 缺失 |
|
||||||
|
| 讯飞星火 | ❌ 缺失 |
|
||||||
|
| 腾讯混元 | ❌ 缺失 |
|
||||||
|
| 零一万物 Yi | ❌ 缺失 |
|
||||||
|
|
||||||
|
#### 后端URL白名单 (`backend/internal/config/config.go`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
viper.SetDefault("security.url_allowlist.upstream_hosts", []string{
|
||||||
|
"api.openai.com",
|
||||||
|
"api.anthropic.com",
|
||||||
|
"api.kimi.com", // ✅ 月之暗面
|
||||||
|
"open.bigmodel.cn", // ✅ 智谱
|
||||||
|
"api.minimaxi.com", // ✅ MiniMax
|
||||||
|
// ❌ 缺失: 阿里云、字节、百度、讯飞、腾讯、零一万物
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 后端定价数据 (`backend/resources/model-pricing/model_prices_and_context_window.json`)
|
||||||
|
|
||||||
|
| 厂商 | 定价数据 |
|
||||||
|
|------|----------|
|
||||||
|
| DeepSeek | ✅ 已包含 (7处) |
|
||||||
|
| 其他国内模型 | ❌ **全部缺失** |
|
||||||
|
|
||||||
|
#### 后端计费服务 (`backend/internal/service/billing_service.go`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// initFallbackPricing() - 回退定价
|
||||||
|
// ❌ 完全缺失国内模型的回退定价
|
||||||
|
|
||||||
|
// getFallbackPricing() - 模型匹配
|
||||||
|
// ❌ 仅支持 Claude/Gemini/GPT,无国内模型匹配逻辑
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 后端Kimi兼容 (`backend/internal/service/gateway_service.go`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ 支持 Kimi 风格的 cached_tokens 响应处理
|
||||||
|
// 兼容 Kimi cached_tokens → cache_read_input_tokens
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、缺失功能清单
|
||||||
|
|
||||||
|
### 2.1 高优先级(影响核心功能)
|
||||||
|
|
||||||
|
| 问题 | 位置 | 影响 |
|
||||||
|
|------|------|------|
|
||||||
|
| **计费服务无国内模型定价** | `billing_service.go` | 国内模型请求**无法正确计费** |
|
||||||
|
| **定价数据缺失** | `model_prices_and_context_window.json` | 动态定价无法获取国内模型价格 |
|
||||||
|
| **回退定价缺失** | `billing_service.go:initFallbackPricing()` | 动态定价不可用时计费失败 |
|
||||||
|
|
||||||
|
### 2.2 中优先级(影响用户体验)
|
||||||
|
|
||||||
|
| 问题 | 位置 | 影响 |
|
||||||
|
|------|------|------|
|
||||||
|
| URL白名单不完整 | `config.go` | 部分国内API无法访问 |
|
||||||
|
| 模型图标缺失 | `ModelIcon.vue` | 部分模型无品牌图标 |
|
||||||
|
|
||||||
|
### 2.3 低优先级
|
||||||
|
|
||||||
|
| 问题 | 位置 | 影响 |
|
||||||
|
|------|------|------|
|
||||||
|
| 无国内模型特殊处理 | `gateway_service.go` | 部分模型可能有特殊响应格式 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、具体改进建议
|
||||||
|
|
||||||
|
### 3.1 计费服务改进 (`billing_service.go`)
|
||||||
|
|
||||||
|
**需要添加的回退定价**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// DeepSeek
|
||||||
|
s.fallbackPrices["deepseek-chat"] = &ModelPricing{
|
||||||
|
InputPricePerToken: 2.8e-7, // $0.28 per MTok
|
||||||
|
OutputPricePerToken: 4.2e-7, // $0.42 per MTok
|
||||||
|
CacheReadPricePerToken: 2.8e-8, // $0.028 per MTok
|
||||||
|
}
|
||||||
|
s.fallbackPrices["deepseek-reasoner"] = &ModelPricing{
|
||||||
|
InputPricePerToken: 2.8e-7,
|
||||||
|
OutputPricePerToken: 4.2e-7,
|
||||||
|
CacheReadPricePerToken: 2.8e-8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通义千问
|
||||||
|
s.fallbackPrices["qwen-turbo"] = &ModelPricing{...}
|
||||||
|
s.fallbackPrices["qwen-plus"] = &ModelPricing{...}
|
||||||
|
s.fallbackPrices["qwen-max"] = &ModelPricing{...}
|
||||||
|
|
||||||
|
// 智谱 GLM
|
||||||
|
s.fallbackPrices["glm-4"] = &ModelPricing{...}
|
||||||
|
// ... 其他模型
|
||||||
|
```
|
||||||
|
|
||||||
|
**需要添加的匹配逻辑** (`getFallbackPricing`):
|
||||||
|
|
||||||
|
```go
|
||||||
|
// DeepSeek
|
||||||
|
if strings.Contains(modelLower, "deepseek") {
|
||||||
|
if strings.Contains(modelLower, "reasoner") || strings.Contains(modelLower, "r1") {
|
||||||
|
return s.fallbackPrices["deepseek-reasoner"]
|
||||||
|
}
|
||||||
|
return s.fallbackPrices["deepseek-chat"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通义千问
|
||||||
|
if strings.Contains(modelLower, "qwen") || strings.Contains(modelLower, "qwq") {
|
||||||
|
if strings.Contains(modelLower, "max") {
|
||||||
|
return s.fallbackPrices["qwen-max"]
|
||||||
|
}
|
||||||
|
if strings.Contains(modelLower, "plus") {
|
||||||
|
return s.fallbackPrices["qwen-plus"]
|
||||||
|
}
|
||||||
|
return s.fallbackPrices["qwen-turbo"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智谱 GLM
|
||||||
|
if strings.Contains(modelLower, "glm-") || strings.Contains(modelLower, "chatglm") {
|
||||||
|
return s.fallbackPrices["glm-4"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... 其他厂商
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 定价数据补充 (`model_prices_and_context_window.json`)
|
||||||
|
|
||||||
|
需要添加以下模型的定价数据:
|
||||||
|
|
||||||
|
- qwen-turbo, qwen-plus, qwen-max, qwen-long, qwen2.5-*, qwen3-*
|
||||||
|
- glm-4, glm-4-plus, glm-4-air, glm-4-flash, glm-4.5, glm-4.6
|
||||||
|
- moonshot-v1-*, kimi-latest
|
||||||
|
- doubao-pro-*, doubao-lite-*, doubao-1.5-*
|
||||||
|
- abab6.5-chat, abab6.5s-chat
|
||||||
|
- ernie-4.0-*, ernie-3.5-*
|
||||||
|
- spark-desk-*, spark-*
|
||||||
|
- hunyuan-*
|
||||||
|
- yi-large, yi-medium, yi-*
|
||||||
|
|
||||||
|
### 3.3 URL白名单补充 (`config.go`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
viper.SetDefault("security.url_allowlist.upstream_hosts", []string{
|
||||||
|
// 现有
|
||||||
|
"api.openai.com",
|
||||||
|
"api.anthropic.com",
|
||||||
|
"api.kimi.com",
|
||||||
|
"open.bigmodel.cn",
|
||||||
|
"api.minimaxi.com",
|
||||||
|
// 需要添加
|
||||||
|
"dashscope.aliyuncs.com", // 阿里云通义
|
||||||
|
"ark.cn-beijing.volces.com", // 字节豆包
|
||||||
|
"aip.baidubce.com", // 百度文心
|
||||||
|
"spark-api.xf-yun.com", // 讯飞星火
|
||||||
|
"hunyuan.tencentcloudapi.com", // 腾讯混元
|
||||||
|
"api.lingyiwanwu.com", // 零一万物
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 前端图标补充 (`ModelIcon.vue`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const providerIcons: Record<string, ProviderIcon> = {
|
||||||
|
// 现有
|
||||||
|
qwen: { ... },
|
||||||
|
deepseek: { ... },
|
||||||
|
moonshot: { ... },
|
||||||
|
minimax: { ... },
|
||||||
|
// 需要添加
|
||||||
|
zhipu: { /* 智谱图标 */ },
|
||||||
|
baidu: { /* 百度图标 */ },
|
||||||
|
spark: { /* 讯飞图标 */ },
|
||||||
|
hunyuan: { /* 腾讯图标 */ },
|
||||||
|
yi: { /* 零一万物图标 */ },
|
||||||
|
doubao: { /* 字节图标 */ },
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模型匹配逻辑补充
|
||||||
|
if (modelLower.includes('glm') || modelLower.includes('chatglm')) return 'zhipu'
|
||||||
|
if (modelLower.includes('ernie')) return 'baidu'
|
||||||
|
if (modelLower.includes('spark')) return 'spark'
|
||||||
|
if (modelLower.includes('hunyuan')) return 'hunyuan'
|
||||||
|
if (modelLower.includes('yi-') || modelLower.includes('yi-large')) return 'yi'
|
||||||
|
if (modelLower.includes('doubao')) return 'doubao'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、测试建议
|
||||||
|
|
||||||
|
### 4.1 计费测试
|
||||||
|
|
||||||
|
```go
|
||||||
|
// billing_service_test.go
|
||||||
|
func TestGetFallbackPricing_ChineseModels(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
model string
|
||||||
|
expectNilPricing bool
|
||||||
|
}{
|
||||||
|
{"deepseek chat", "deepseek-chat", false},
|
||||||
|
{"deepseek reasoner", "deepseek-reasoner", false},
|
||||||
|
{"deepseek r1", "deepseek-r1", false},
|
||||||
|
{"qwen max", "qwen-max", false},
|
||||||
|
{"qwen turbo", "qwen-turbo", false},
|
||||||
|
{"glm-4", "glm-4", false},
|
||||||
|
{"moonshot v1", "moonshot-v1-8k", false},
|
||||||
|
{"doubao pro", "doubao-pro-256k", false},
|
||||||
|
{"abab chat", "abab6.5-chat", false},
|
||||||
|
{"ernie 4.0", "ernie-4.0-8k", false},
|
||||||
|
{"spark max", "spark-max", false},
|
||||||
|
{"hunyuan pro", "hunyuan-pro", false},
|
||||||
|
{"yi large", "yi-large", false},
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、总结
|
||||||
|
|
||||||
|
### 国内模型支持完成度
|
||||||
|
|
||||||
|
| 模块 | 完成度 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 前端模型白名单 | 100% | 所有主流国内模型已添加 |
|
||||||
|
| 前端模型图标 | 40% | 仅4家厂商有图标 |
|
||||||
|
| 后端URL白名单 | 30% | 仅3家厂商在白名单 |
|
||||||
|
| 后端定价数据 | 10% | 仅DeepSeek有定价 |
|
||||||
|
| 后端计费服务 | 0% | **完全缺失** |
|
||||||
|
|
||||||
|
### 核心问题
|
||||||
|
|
||||||
|
**国内模型支持目前处于"前端可用、后端不可用"的状态**:
|
||||||
|
|
||||||
|
1. ✅ 前端可以显示和选择国内模型
|
||||||
|
2. ❌ 后端无法正确计费(会导致计费失败或免费使用)
|
||||||
|
3. ❌ 部分国内API因URL白名单限制无法访问
|
||||||
|
|
||||||
|
### 建议优先级
|
||||||
|
|
||||||
|
1. **立即修复**:添加计费服务的国内模型定价支持
|
||||||
|
2. **尽快完成**:补充定价数据文件
|
||||||
|
3. **逐步完善**:URL白名单、模型图标
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**审查完成时间**: 2026/04/12
|
||||||
208
docs/reports/REVIEW_REPORT_2026-03-31.md
Normal file
208
docs/reports/REVIEW_REPORT_2026-03-31.md
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
# Sub2API 代码审查报告 — 2026-03-31
|
||||||
|
|
||||||
|
> **本日首次审查**(无历史报告,本次为基线审查)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 项目概况
|
||||||
|
|
||||||
|
| 项目 | 详情 |
|
||||||
|
|------|------|
|
||||||
|
| 仓库 | `d:/project/sub2api` |
|
||||||
|
| 分支 | `main`(领先 origin 1 commit) |
|
||||||
|
| 审查范围 | 最近 2 个 commit(141424a, bda7c39) |
|
||||||
|
| 重点目录 | `backend/internal/service/`, `backend/internal/server/`, `backend/internal/handler/`, `backend/internal/pkg/models/` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏆 上次修复确认
|
||||||
|
|
||||||
|
commit `141424a fix: resolve P0/P1 code quality issues` 已处理以下问题(标记为 ✅):
|
||||||
|
|
||||||
|
| 问题 | 状态 |
|
||||||
|
|------|------|
|
||||||
|
| `ModelError.Is()` 使用 substring contains 导致错误匹配 | ✅ 已修复 |
|
||||||
|
| `shouldClearStickySession` 缺少 context 参数(context 传播缺失) | ✅ 已修复(函数签名已添加 `ctx context.Context`)|
|
||||||
|
| TODO stubs 没有返回错误,静默失败 | ✅ 已部分修复(admin_service 已加 501 错误) |
|
||||||
|
| `validateInstanceSignature` 代码重复 | ✅ 已修复(抽取到 `instance_signature.go`) |
|
||||||
|
| 错误消息混合中英文 | ✅ 已修复(`api_key_service.go` 统一为英文) |
|
||||||
|
| `http.go` 伪 if-else 重复分支 | ✅ 已修复 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔴 P0 — 阻断性问题(必须立即修复)
|
||||||
|
|
||||||
|
### P0-01:测试文件签名与实际函数不匹配(编译失败)
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/service/sticky_session_test.go:108`
|
||||||
|
- **问题**:`shouldClearStickySession` 函数在 commit 141424a 中新增了 `ctx context.Context` 参数,但测试文件仍在使用旧的 2 参数调用签名:
|
||||||
|
```go
|
||||||
|
// 实际函数签名(已更新):
|
||||||
|
func shouldClearStickySession(ctx context.Context, account *Account, requestedModel string) bool
|
||||||
|
|
||||||
|
// 测试文件中的调用(未更新 ❌):
|
||||||
|
require.Equal(t, tt.want, shouldClearStickySession(tt.account, tt.requestedModel))
|
||||||
|
```
|
||||||
|
- **验证**:`go test -tags unit ./internal/service/...` 报错:
|
||||||
|
```
|
||||||
|
internal/service/sticky_session_test.go:108:67: not enough arguments in call to shouldClearStickySession
|
||||||
|
have (*Account, string)
|
||||||
|
want ("context".Context, *Account, string)
|
||||||
|
```
|
||||||
|
- **影响**:带 `-tags unit` 的 CI 测试完全无法编译,阻断 CI/CD。
|
||||||
|
- **修复方案**:将测试调用改为 `shouldClearStickySession(context.Background(), tt.account, tt.requestedModel)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 P1 — 重要问题(应在下一个版本修复)
|
||||||
|
|
||||||
|
### P1-01:`defaultMaxLineSize` 设置为 500MB,存在内存溢出风险
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/service/gateway_service.go:44`
|
||||||
|
- **问题**:SSE 扫描器的默认最大行大小为 `500 * 1024 * 1024`(500MB),若上游出现异常长行(如恶意/损坏响应),bufio.Scanner 将尝试分配 500MB 缓冲区。
|
||||||
|
```go
|
||||||
|
defaultMaxLineSize = 500 * 1024 * 1024 // 500MB — 过大
|
||||||
|
```
|
||||||
|
- **影响**:高并发下可能导致 OOM,实际上游 SSE 单行通常不超过 1MB。
|
||||||
|
- **修复方案**:将默认值降至 4MB 或 16MB,并通过配置项允许用户根据需要调整。
|
||||||
|
|
||||||
|
### P1-02:`account_service.go:TestCredentials` — 三平台均返回 `nil` error(静默通过)
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/service/account_service.go:386-398`
|
||||||
|
- **问题**:`TestCredentials` 方法对 Anthropic、OpenAI、Gemini 三大平台均留有 TODO 并直接 `return nil`,意味着无论凭证是否有效,接口永远返回"成功"。
|
||||||
|
```go
|
||||||
|
case PlatformAnthropic:
|
||||||
|
// TODO: 测试Anthropic API凭证
|
||||||
|
return nil // ← 永远成功
|
||||||
|
case PlatformOpenAI:
|
||||||
|
// TODO: 测试OpenAI API凭证
|
||||||
|
return nil // ← 永远成功
|
||||||
|
```
|
||||||
|
- **影响**:前端/管理员通过"测试凭证"功能无法发现无效 API Key,可能将坏账号加入调度池。
|
||||||
|
- **修复方案**:至少对 Anthropic 和 OpenAI 实现基础验证(发送一个轻量 ping 请求);或在 TODO 未实现前返回 `ErrNotImplemented` 让调用方知晓功能未就绪。
|
||||||
|
|
||||||
|
### P1-03:`group_handler.go:GetStats` — 返回全零 mock 数据
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/handler/admin/group_handler.go:362-368`
|
||||||
|
- **问题**:`GET /api/v1/admin/groups/:id/stats` 永远返回零值 mock 数据,且有明确 TODO 注释,但上线后管理员看到的将是假数据。
|
||||||
|
```go
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"total_api_keys": 0,
|
||||||
|
"active_api_keys": 0,
|
||||||
|
"total_requests": 0,
|
||||||
|
"total_cost": 0.0,
|
||||||
|
})
|
||||||
|
_ = groupID // TODO: implement actual stats
|
||||||
|
```
|
||||||
|
- **影响**:admin 面板分组统计页面永远显示 0,影响运营决策。
|
||||||
|
- **修复方案**:对接 `DashboardService` 或实现实际统计查询;如暂不实现,应返回 `501 Not Implemented` 而不是假数据。
|
||||||
|
|
||||||
|
### P1-04:多处测试用 `t.Skip` 长期屏蔽(Sora 相关)
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/handler/sora_client_handler_test.go`
|
||||||
|
- **问题**:16 个 Sora 相关测试函数使用 `t.Skip("TODO: 临时屏蔽...")` 被跳过,注释说"待流程稳定后恢复",但没有 Issue 追踪,可能永久被遗忘。
|
||||||
|
- **影响**:Sora 功能无测试覆盖,回归风险高。
|
||||||
|
- **修复方案**:创建跟踪 Issue,设置恢复时间线,或删除过时测试。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💭 挑剔(代码质量改进建议)
|
||||||
|
|
||||||
|
### 挑剔-01:`math/rand` 与 `math/rand/v2` 混用风险
|
||||||
|
|
||||||
|
- **文件**:`openai_gateway_service.go`, `openai_ws_forwarder.go`, `sora_gateway_service.go`
|
||||||
|
- **问题**:部分文件使用 `math/rand`(非加密安全),用于会话 hash 等逻辑时需确认这些 rand 用途均为非安全场景(统计/负载均衡),不涉及安全令牌生成。
|
||||||
|
- **建议**:在使用 `math/rand` 的文件顶部添加注释说明用途,明确此处不需要加密安全随机数。
|
||||||
|
|
||||||
|
### 挑剔-02:`geminiDummyThoughtSignature = "skip_thought_signature_validator"` — 魔法字符串无文档
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/service/gemini_messages_compat_service.go:44`
|
||||||
|
- **问题**:向 Gemini API 注入一个虚假的 `thoughtSignature`,这依赖于 Google API 的未文档化行为。
|
||||||
|
- **建议**:添加注释说明此值是否由 Gemini 官方文档认可,还是通过逆向工程发现,以及更新风险。
|
||||||
|
|
||||||
|
### 挑剔-03:`ModelError.Error()` 格式化方式不够标准
|
||||||
|
|
||||||
|
- **文件**:`backend/internal/pkg/models/interface.go:220-225`
|
||||||
|
- **问题**:
|
||||||
|
```go
|
||||||
|
func (e *ModelError) Error() string {
|
||||||
|
if len(e.Args) > 0 {
|
||||||
|
return e.Message + ": " + fmt.Sprint(e.Args...) // 使用 fmt.Sprint 而非 fmt.Sprintf
|
||||||
|
}
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`e.Message` 包含 `%s` 格式占位符(如 `"unknown provider: %s"`),但此处用 `fmt.Sprint(e.Args...)` 拼接,不会实际格式化占位符,导致错误消息显示为 `"unknown provider: %s: [deepseek]"` 而非 `"unknown provider: deepseek"`。
|
||||||
|
- **建议**:改为 `fmt.Sprintf(e.Message, e.Args...)` 使格式化符合预期。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 总结与健康度评分
|
||||||
|
|
||||||
|
| 维度 | 状况 |
|
||||||
|
|------|------|
|
||||||
|
| 本次新发现 P0 | **1 个**(测试编译失败) |
|
||||||
|
| 本次新发现 P1 | **3 个** |
|
||||||
|
| 本次新发现挑剔 | **3 个** |
|
||||||
|
| 历史 P0 已修复 | **2/2**(上次 commit 全部修复) |
|
||||||
|
| 历史 P1 已修复 | **4/4**(上次 commit 全部修复) |
|
||||||
|
|
||||||
|
**整体健康度评分:7 / 10**
|
||||||
|
|
||||||
|
> 上次 commit 的修复工作质量较高,P0/P1 问题清单执行彻底。但修复 `shouldClearStickySession` 签名时未同步更新测试文件,导致引入了新的 P0(测试编译失败)。`TestCredentials` 永久静默成功属于功能性缺陷,需要重点关注。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⏱️ 审查时间戳
|
||||||
|
|
||||||
|
| 项目 | 时间 |
|
||||||
|
|------|------|
|
||||||
|
| 审查执行时间 | 2026-03-31 08:43(首次) |
|
||||||
|
| 覆盖 commit 范围 | `bda7c39`(首个 commit)~ `141424a`(HEAD) |
|
||||||
|
| 下次建议审查时间 | 下一个 commit 合入后 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 第二次审查 — 2026-03-31 09:58
|
||||||
|
|
||||||
|
**变更扫描结果**:自 08:43 首次审查至本次(09:58),**无新 commit**,代码库无变化(仍为 2 commits:`bda7c39`、`141424a`)。
|
||||||
|
|
||||||
|
**已知问题状态确认**(逐项验证):
|
||||||
|
|
||||||
|
| 问题 ID | 描述 | 本次状态 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| P0-01 | `sticky_session_test.go:108` 测试调用缺少 ctx 参数,编译失败 | ❌ **仍未修复** |
|
||||||
|
| P1-01 | `gateway_service.go:44` defaultMaxLineSize=500MB OOM 风险 | ❌ **仍未修复** |
|
||||||
|
| P1-02 | `account_service.go:386-398` TestCredentials 三平台均 return nil | ❌ **仍未修复** |
|
||||||
|
| P1-03 | `group_handler.go:362-368` GetStats 返回硬编码零值 mock 数据 | ❌ **仍未修复** |
|
||||||
|
| P1-04 | `sora_client_handler_test.go` 16 个测试用 t.Skip 长期屏蔽 | ❌ **仍未修复** |
|
||||||
|
| 挑剔-01 | math/rand 用途缺少注释 | ❌ **仍未修复** |
|
||||||
|
| 挑剔-02 | geminiDummyThoughtSignature 魔法字符串无文档 | ❌ **仍未修复** |
|
||||||
|
| 挑剔-03 | ModelError.Error() 使用 fmt.Sprint 而非 fmt.Sprintf | ❌ **仍未修复** |
|
||||||
|
|
||||||
|
**新发现问题**:本次扫描无新问题。
|
||||||
|
|
||||||
|
**本次健康度评分:7 / 10**(与首次审查一致,无新提交故评分不变)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 第三次审查 — 2026-03-31 11:01
|
||||||
|
|
||||||
|
**变更扫描结果**:自 09:58 第二次审查至本次(11:01),**仍无新 commit**,代码库无变化(HEAD 仍为 `141424a`,分支领先 origin 1 commit)。未追踪文件:`.codebuddy/`、`.workbuddy/`、`REVIEW_REPORT_2026-03-31.md`(非代码变更)。
|
||||||
|
|
||||||
|
**已知问题状态确认**(逐项点检):
|
||||||
|
|
||||||
|
| 问题 ID | 描述 | 本次状态 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| P0-01 | `sticky_session_test.go:108` 测试调用缺少 ctx 参数,编译失败 | ❌ **仍未修复**(已验证第 108 行仍为旧 2 参数签名)|
|
||||||
|
| P1-01 | `gateway_service.go:44` defaultMaxLineSize=500MB OOM 风险 | ❌ **仍未修复** |
|
||||||
|
| P1-02 | `account_service.go:386-398` TestCredentials 三平台均 return nil | ❌ **仍未修复**(已验证 TODO 注释仍在 388/391/394 行)|
|
||||||
|
| P1-03 | `group_handler.go:362-368` GetStats 返回硬编码零值 mock 数据 | ❌ **仍未修复** |
|
||||||
|
| P1-04 | `sora_client_handler_test.go` 16 个测试用 t.Skip 长期屏蔽 | ❌ **仍未修复** |
|
||||||
|
| 挑剔-01 | math/rand 用途缺少注释 | ❌ **仍未修复** |
|
||||||
|
| 挑剔-02 | geminiDummyThoughtSignature 魔法字符串无文档 | ❌ **仍未修复** |
|
||||||
|
| 挑剔-03 | ModelError.Error() 使用 fmt.Sprint 而非 fmt.Sprintf | ❌ **仍未修复** |
|
||||||
|
|
||||||
|
**新发现问题**:本次扫描无新问题。
|
||||||
|
|
||||||
|
**本次健康度评分:7 / 10**(与前两次审查一致,无新提交故评分不变)
|
||||||
382
docs/reports/REVIEW_REPORT_2026-04-12.md
Normal file
382
docs/reports/REVIEW_REPORT_2026-04-12.md
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
# Sub2API 项目综合审查报告
|
||||||
|
|
||||||
|
**审查日期**: 2026/04/12
|
||||||
|
**项目路径**: D:/project/sub2api
|
||||||
|
**远程仓库**: https://www.tksea.top/pham/tokens-reef
|
||||||
|
**审查工具**: Claude Code 专业审查工具
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、项目概述
|
||||||
|
|
||||||
|
### 1.1 项目简介
|
||||||
|
|
||||||
|
**Sub2API** 是一个 AI API 网关平台,用于分发和管理 AI 产品订阅的 API 配额。用户通过平台生成的 API Key 访问上游 AI 服务,平台负责认证、计费、负载均衡和请求转发。
|
||||||
|
|
||||||
|
### 1.2 技术栈
|
||||||
|
|
||||||
|
| 组件 | 技术 | 版本 |
|
||||||
|
|------|------|------|
|
||||||
|
| 后端 | Go | 1.26.1 |
|
||||||
|
| Web框架 | Gin | 1.9.1 |
|
||||||
|
| ORM | Ent | 0.14.5 |
|
||||||
|
| 前端 | Vue 3 | 3.4+ |
|
||||||
|
| 构建工具 | Vite | 5.0+ |
|
||||||
|
| 状态管理 | Pinia | 2.1.7 |
|
||||||
|
| CSS框架 | TailwindCSS | 3.4.0 |
|
||||||
|
| 数据库 | PostgreSQL | 15+ |
|
||||||
|
| 缓存/队列 | Redis | 7+ |
|
||||||
|
| 容器化 | Docker | Ready |
|
||||||
|
|
||||||
|
### 1.3 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
sub2api/
|
||||||
|
├── backend/ # Go 后端服务
|
||||||
|
│ ├── cmd/server/ # 应用入口
|
||||||
|
│ ├── ent/ # Ent ORM 实体定义
|
||||||
|
│ ├── internal/ # 内部模块
|
||||||
|
│ │ ├── config/ # 配置管理
|
||||||
|
│ │ ├── handler/ # HTTP 处理器
|
||||||
|
│ │ ├── service/ # 业务逻辑
|
||||||
|
│ │ ├── repository/ # 数据访问
|
||||||
|
│ │ ├── middleware/ # 中间件
|
||||||
|
│ │ ├── pkg/ # 内部工具包
|
||||||
|
│ │ └── util/ # 工具函数
|
||||||
|
│ └── migrations/ # 数据库迁移
|
||||||
|
│
|
||||||
|
├── frontend/ # Vue 3 前端
|
||||||
|
│ └── src/
|
||||||
|
│ ├── api/ # API 调用封装
|
||||||
|
│ ├── components/ # 可复用组件
|
||||||
|
│ ├── composables/ # 组合式函数
|
||||||
|
│ ├── stores/ # Pinia 状态管理
|
||||||
|
│ ├── router/ # 路由配置
|
||||||
|
│ ├── types/ # TypeScript 类型
|
||||||
|
│ ├── utils/ # 工具函数
|
||||||
|
│ └── views/ # 页面组件
|
||||||
|
│
|
||||||
|
├── deploy/ # 部署配置
|
||||||
|
│ ├── docker-compose.yml # Docker Compose
|
||||||
|
│ ├── .env.example # 环境变量示例
|
||||||
|
│ └── config.example.yaml # 配置文件示例
|
||||||
|
│
|
||||||
|
└── .github/workflows/ # CI/CD 配置
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、后端代码审查
|
||||||
|
|
||||||
|
### 2.1 架构设计
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 分层设计 | 优秀 | Handler/Service/Repository 三层分离清晰 |
|
||||||
|
| 依赖注入 | 优秀 | 使用 Google Wire 编译时依赖注入 |
|
||||||
|
| ORM使用 | 良好 | Ent ORM 类型安全,Schema定义规范 |
|
||||||
|
| 模块化 | 优秀 | internal/pkg 组织合理,职责明确 |
|
||||||
|
|
||||||
|
### 2.2 安全性
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| SQL注入防护 | 优秀 | Ent ORM + 参数化查询,无注入风险 |
|
||||||
|
| 认证实现 | 优秀 | JWT + TokenVersion + bcrypt + TOTP 2FA |
|
||||||
|
| 授权机制 | 优秀 | IP白名单/黑名单,API Key状态验证 |
|
||||||
|
| 敏感数据 | 良好 | AES-256-GCM加密,日志脱敏完善 |
|
||||||
|
| CORS配置 | 良好 | 自动处理通配符+凭证冲突 |
|
||||||
|
| 安全头 | 优秀 | CSP nonce, X-Frame-Options, Referrer-Policy |
|
||||||
|
|
||||||
|
### 2.3 API设计
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| RESTful规范 | 良好 | 资源导向URL,正确使用HTTP方法 |
|
||||||
|
| 错误处理 | 优秀 | 结构化错误响应,统一错误码 |
|
||||||
|
| 请求验证 | 良好 | Gin binding验证,类型安全 |
|
||||||
|
|
||||||
|
### 2.4 并发安全
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| Goroutine使用 | 良好 | 后台任务、信号处理使用合理 |
|
||||||
|
| Channel使用 | 良好 | 优雅关闭协调正确 |
|
||||||
|
| 锁机制 | 优秀 | sync.Once, atomic, RWMutex 使用得当 |
|
||||||
|
|
||||||
|
### 2.5 性能考虑
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 数据库查询 | 良好 | 批量查询、索引优化、连接池配置 |
|
||||||
|
| 缓存策略 | 优秀 | Redis + Ristretto + go-cache 多层缓存 |
|
||||||
|
| HTTP客户端 | 良好 | 连接池隔离策略,可配置参数 |
|
||||||
|
|
||||||
|
### 2.6 后端问题汇总
|
||||||
|
|
||||||
|
**中等严重度**:
|
||||||
|
1. `context.Background()` 使用较多(50+处),可能导致资源泄漏
|
||||||
|
2. 部分查询使用 `LIMIT 10000`,大数据集可能内存溢出
|
||||||
|
|
||||||
|
**低严重度**:
|
||||||
|
1. 部分错误信息可能泄露内部实现细节
|
||||||
|
2. 部分魔法数字应移至配置
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、前端代码审查
|
||||||
|
|
||||||
|
### 3.1 架构设计
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 组件设计 | 良好 | Composition API,功能模块划分清晰 |
|
||||||
|
| 状态管理 | 优秀 | Pinia setup store,职责划分合理 |
|
||||||
|
| 路由设计 | 优秀 | 懒加载、导航守卫、chunk失败处理 |
|
||||||
|
| API封装 | 优秀 | Axios拦截器、Token刷新、取消请求 |
|
||||||
|
|
||||||
|
### 3.2 安全性
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| XSS防护 | 良好 | DOMPurify净化,但部分v-html需加强 |
|
||||||
|
| CSRF防护 | 良好 | JWT Bearer Token天然防范 |
|
||||||
|
| 敏感数据 | 良好 | Token存储在localStorage,日志忽略敏感信息 |
|
||||||
|
| 认证状态 | 优秀 | 完整Token生命周期,自动刷新机制 |
|
||||||
|
|
||||||
|
**XSS风险点**:
|
||||||
|
| 文件 | 行号 | 风险 | 建议 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| HomeView.vue | 12 | 中 | v-html需添加DOMPurify净化 |
|
||||||
|
| KeyUsageView.vue | 260 | 中 | 确认数据来源,必要时净化 |
|
||||||
|
|
||||||
|
### 3.3 代码质量
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| TypeScript | 良好 | 严格模式,完整类型定义 |
|
||||||
|
| 组件复用 | 优秀 | 通用组件库 + Composables |
|
||||||
|
| 错误处理 | 优秀 | 统一错误处理,Toast通知 |
|
||||||
|
| 代码规范 | 良好 | ESLint配置,建议添加Prettier |
|
||||||
|
|
||||||
|
### 3.4 性能
|
||||||
|
|
||||||
|
| 方面 | 评分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 懒加载 | 优秀 | 所有路由组件动态导入 |
|
||||||
|
| 虚拟滚动 | 优秀 | @tanstack/vue-virtual 实现 |
|
||||||
|
| 构建优化 | 良好 | manualChunks分离,建议添加压缩 |
|
||||||
|
|
||||||
|
### 3.5 前端问题汇总
|
||||||
|
|
||||||
|
**高优先级**:
|
||||||
|
1. `HomeView.vue` 的 `v-html` 需添加 DOMPurify 净化
|
||||||
|
2. TypeScript `no-explicit-any` 建议改为 `warn`
|
||||||
|
|
||||||
|
**中优先级**:
|
||||||
|
1. 大型组件(如 `AppSidebar.vue` 685行)建议拆分
|
||||||
|
2. 添加构建压缩插件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、安全配置审查
|
||||||
|
|
||||||
|
### 4.1 配置文件安全
|
||||||
|
|
||||||
|
| 检查项 | 状态 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 敏感信息占位符 | 通过 | 无真实密钥硬编码 |
|
||||||
|
| 默认密码警告 | 警告 | `admin_password: "admin123"` 弱密码示例 |
|
||||||
|
| JWT密钥说明 | 通过 | 明确提示必须设置固定密钥 |
|
||||||
|
| TOTP密钥说明 | 通过 | 提示不设置将导致2FA失效 |
|
||||||
|
|
||||||
|
### 4.2 认证安全
|
||||||
|
|
||||||
|
| 检查项 | 状态 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| JWT算法限制 | 通过 | 仅允许HS256/HS384/HS512 |
|
||||||
|
| Token版本控制 | 通过 | 密码修改后旧Token失效 |
|
||||||
|
| bcrypt密码哈希 | 通过 | 使用golang.org/x/crypto/bcrypt |
|
||||||
|
| TOTP加密存储 | 通过 | AES-256-GCM加密 |
|
||||||
|
| 恒定时间比较 | 通过 | subtle.ConstantTimeCompare |
|
||||||
|
|
||||||
|
### 4.3 网络安全
|
||||||
|
|
||||||
|
| 检查项 | 状态 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| CORS处理 | 通过 | 自动禁用通配符+凭证冲突 |
|
||||||
|
| CSP策略 | 优秀 | nonce支持,完整安全头 |
|
||||||
|
| HTTPS配置 | 警告 | 默认禁用SSL,生产需启用 |
|
||||||
|
| 代理配置 | 通过 | 默认禁用TLS验证跳过 |
|
||||||
|
|
||||||
|
### 4.4 敏感信息保护
|
||||||
|
|
||||||
|
| 检查项 | 状态 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 日志脱敏 | 优秀 | 自动识别并脱敏多种敏感格式 |
|
||||||
|
| 错误信息 | 良好 | 认证错误模糊化处理 |
|
||||||
|
| API密钥存储 | 良好 | L1/L2双层缓存,支持负缓存 |
|
||||||
|
|
||||||
|
### 4.5 CI/CD安全
|
||||||
|
|
||||||
|
| 检查项 | 状态 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 权限最小化 | 通过 | contents: read 基础权限 |
|
||||||
|
| 密钥注入 | 通过 | 使用GitHub Secrets |
|
||||||
|
| 安全扫描 | 通过 | govulncheck + pnpm audit |
|
||||||
|
| 镜像安全 | 通过 | 非root用户,alpine基础镜像 |
|
||||||
|
|
||||||
|
### 4.6 安全配置问题汇总
|
||||||
|
|
||||||
|
**高优先级**:
|
||||||
|
| 问题 | 位置 | 建议 |
|
||||||
|
|------|------|------|
|
||||||
|
| 默认允许HTTP URL | .env.example:327 | 生产环境设为false |
|
||||||
|
| 默认允许私有IP | .env.example:331 | 评估SSRF风险 |
|
||||||
|
| 示例弱密码 | config.example.yaml:831 | 使用占位符替代 |
|
||||||
|
| 默认禁用SSL | docker-compose.standalone.yml:50 | 生产使用require |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、评分汇总
|
||||||
|
|
||||||
|
### 5.1 后端评分
|
||||||
|
|
||||||
|
| 类别 | 评分 |
|
||||||
|
|------|------|
|
||||||
|
| 架构设计 | 90/100 |
|
||||||
|
| 安全性 | 92/100 |
|
||||||
|
| API设计 | 88/100 |
|
||||||
|
| 并发安全 | 90/100 |
|
||||||
|
| 性能 | 85/100 |
|
||||||
|
| **后端总分** | **89/100** |
|
||||||
|
|
||||||
|
### 5.2 前端评分
|
||||||
|
|
||||||
|
| 类别 | 评分 |
|
||||||
|
|------|------|
|
||||||
|
| 架构设计 | 88/100 |
|
||||||
|
| 安全性 | 85/100 |
|
||||||
|
| 代码质量 | 87/100 |
|
||||||
|
| 性能 | 86/100 |
|
||||||
|
| 可维护性 | 90/100 |
|
||||||
|
| **前端总分** | **87/100** |
|
||||||
|
|
||||||
|
### 5.3 安全配置评分
|
||||||
|
|
||||||
|
| 类别 | 评分 |
|
||||||
|
|------|------|
|
||||||
|
| 配置文件安全 | 85/100 |
|
||||||
|
| 认证安全 | 95/100 |
|
||||||
|
| 网络安全 | 90/100 |
|
||||||
|
| 敏感信息保护 | 95/100 |
|
||||||
|
| CI/CD安全 | 95/100 |
|
||||||
|
| **安全总分** | **92/100** |
|
||||||
|
|
||||||
|
### 5.4 综合评分
|
||||||
|
|
||||||
|
| 模块 | 权重 | 评分 | 加权分 |
|
||||||
|
|------|------|------|--------|
|
||||||
|
| 后端 | 35% | 89 | 31.15 |
|
||||||
|
| 前端 | 30% | 87 | 26.10 |
|
||||||
|
| 安全配置 | 35% | 92 | 32.20 |
|
||||||
|
| **综合评分** | 100% | - | **89.45/100** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、国内模型支持审查
|
||||||
|
|
||||||
|
### 6.1 支持现状
|
||||||
|
|
||||||
|
| 模块 | 完成度 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 前端模型白名单 | **100%** | 已添加智谱、通义、DeepSeek、月之暗面、豆包、MiniMax、百度、讯飞、腾讯、零一万物等 |
|
||||||
|
| 前端模型图标 | **40%** | 仅 qwen/deepseek/moonshot/minimax 有图标 |
|
||||||
|
| 后端URL白名单 | **30%** | 仅 kimi/bigmodel/minimaxi 在白名单 |
|
||||||
|
| 后端定价数据 | **10%** | 仅 DeepSeek 有定价数据 |
|
||||||
|
| 后端计费服务 | **0%** | **完全缺失国内模型定价** |
|
||||||
|
|
||||||
|
### 6.2 核心问题
|
||||||
|
|
||||||
|
**国内模型支持目前处于"前端可用、后端不可用"的状态**:
|
||||||
|
|
||||||
|
1. ✅ 前端可以显示和选择国内模型
|
||||||
|
2. ❌ **后端无法正确计费**(会导致计费失败或免费使用)
|
||||||
|
3. ❌ 部分国内API因URL白名单限制无法访问
|
||||||
|
|
||||||
|
### 6.3 需要修复的问题
|
||||||
|
|
||||||
|
| 优先级 | 问题 | 位置 | 影响 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| **紧急** | 计费服务无国内模型定价 | `billing_service.go` | 国内模型请求无法正确计费 |
|
||||||
|
| **紧急** | 定价数据缺失 | `model_prices_and_context_window.json` | 动态定价无法获取价格 |
|
||||||
|
| **高** | URL白名单不完整 | `config.go` | 部分国内API无法访问 |
|
||||||
|
| **中** | 模型图标缺失 | `ModelIcon.vue` | 部分模型无品牌图标 |
|
||||||
|
|
||||||
|
### 6.4 详细报告
|
||||||
|
|
||||||
|
完整的国内模型支持审查报告见: `CHINESE_MODELS_AUDIT.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、改进建议优先级
|
||||||
|
|
||||||
|
### 高优先级(需立即处理)
|
||||||
|
|
||||||
|
1. **【紧急】国内模型计费支持**: 添加计费服务的国内模型定价
|
||||||
|
2. **【紧急】国内模型定价数据**: 补充 `model_prices_and_context_window.json`
|
||||||
|
3. **前端XSS防护**: `HomeView.vue` 添加 DOMPurify 净化
|
||||||
|
4. **生产环境配置**: 禁用HTTP URL和私有IP访问
|
||||||
|
5. **SSL配置**: 生产环境启用数据库TLS
|
||||||
|
6. **示例密码**: 移除弱密码示例
|
||||||
|
|
||||||
|
### 中优先级(建议近期处理)
|
||||||
|
|
||||||
|
1. **国内模型URL白名单**: 添加阿里云、字节、百度、讯飞、腾讯、零一万物API域名
|
||||||
|
2. **后端Context**: 减少 `context.Background()` 使用
|
||||||
|
3. **查询优化**: 大数据集使用游标分页
|
||||||
|
4. **前端组件拆分**: 大型组件模块化
|
||||||
|
5. **TypeScript配置**: 启用 `no-explicit-any` 警告
|
||||||
|
6. **构建优化**: 添加压缩插件
|
||||||
|
|
||||||
|
### 低优先级(可后续处理)
|
||||||
|
|
||||||
|
1. **国内模型图标**: 补充智谱、百度、讯飞、腾讯、零一万物、字节图标
|
||||||
|
2. **代码规范**: 添加 Prettier 和 .editorconfig
|
||||||
|
3. **文档完善**: 添加架构说明和变更日志
|
||||||
|
4. **监控增强**: 添加请求追踪和可观测性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、项目优势总结
|
||||||
|
|
||||||
|
1. **架构成熟**: 清晰的分层设计,依赖注入,模块化组织
|
||||||
|
2. **安全完善**: 多层安全防护,日志脱敏,CSP策略
|
||||||
|
3. **性能优化**: 多层缓存,连接池隔离,虚拟滚动
|
||||||
|
4. **代码质量**: TypeScript严格模式,完整的类型定义
|
||||||
|
5. **部署友好**: Docker化,CI/CD自动化,多种部署方式
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、结论
|
||||||
|
|
||||||
|
Sub2API 是一个**架构成熟、安全完善、代码质量较高**的 AI API 网关项目。
|
||||||
|
|
||||||
|
**主要发现**:
|
||||||
|
- 后端采用现代Go实践(Wire DI、Ent ORM),安全措施全面
|
||||||
|
- 前端使用Vue3 Composition API,状态管理规范
|
||||||
|
- 安全配置整体良好,生产部署需注意配置调整
|
||||||
|
- **国内模型支持不完整**:前端已添加白名单,但后端计费服务缺失定价支持
|
||||||
|
|
||||||
|
**建议重点改进**:
|
||||||
|
1. **【紧急】添加国内模型计费支持** - 当前会导致计费失败
|
||||||
|
2. 前端XSS防护加强
|
||||||
|
3. 生产环境安全配置检查
|
||||||
|
4. 大数据查询优化
|
||||||
|
|
||||||
|
**总体评价**: 项目代码质量优秀,适合生产环境部署。但**国内模型支持功能不完整**,需要补充后端计费逻辑后才能正常使用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**审查完成时间**: 2026/04/12
|
||||||
|
**审查人员**: Claude Code AI
|
||||||
Reference in New Issue
Block a user