Files
yangzhaohan b7cc3c4265 feat(web): 初始化前端基础架构和样式
- 新增.gitignore,忽略日志、node_modules、dist及编辑器配置文件
- 创建基础React应用及组件App,集成React、Vite和样式
- 设计响应式CSS变量和全局样式,支持暗黑模式
- 新增图标SVG符号定义,用于社交及文档链接展示
- 配置TypeScript项目(tsconfig)和ESLint规则保障代码质量
- 配置Vite开发服务器,支持代理接口与模块别名
- 增加前端API客户端封装与健康检查接口定义
- 增加类型定义文件,映射后端数据模型接口
- 创建index.html作为前端入口页面
- 新增package.json定义依赖和运行脚本,包含React及Ant Design等库
2026-07-21 21:53:34 +08:00

465 lines
20 KiB
Markdown
Raw Permalink 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.
# Janus - AI 中转站 完整开发计划
## Context
构建一个 AI API 中转/网关服务(Janus),使用 Go + Gin + PostgreSQLGORM),前端采用前后端分离架构(Vue 3)。
用户背景:Java 转 Go,项目兼顾学习目的。计划按阶段递增复杂度,每个阶段引入新的 Go 语言特性:
- 早期:struct、interface、error handling、package 组织
- 中期:goroutine、channel、sync 包(并发安全)
- 后期:middleware 模式、context、observability
## 架构总览
```
┌─────────────────────────────────────────────────┐
│ Clients │
│ (OpenAI SDK / Anthropic SDK / curl) │
└─────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Janus Gateway │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Gin │ │ Proxy │ │ Management │ │
│ │ Router │ │ Engine │ │ API (CRUD) │ │
│ └────┬─────┘ └────┬─────┘ └───────┬───────┘ │
│ │ │ │ │
│ ┌────┴─────────────┴───────────────┴───────┐ │
│ │ Service Layer │ │
│ │ ┌──────────┐ ┌────────┐ ┌────────────┐ │ │
│ │ │ Balancer │ │ Health │ │ Rate Limiter│ │ │
│ │ │ Service │ │Checker │ │ Service │ │ │
│ │ └──────────┘ └────────┘ └────────────┘ │ │
│ └────────────────┬──────────────────────────┘ │
│ │ │
│ ┌────────────────┴──────────────────────────┐ │
│ │ Repository Layer (GORM) │ │
│ └────────────────┬──────────────────────────┘ │
│ │ │
│ ┌────────────────┴──────────────────────────┐ │
│ │ PostgreSQL │ │
│ └────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Vue 3 Dashboard (SPA) │ │
│ └────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Upstream AI Providers │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ OpenAI │ │Anthropic │ │ Compatible │ │
│ │ API │ │ API │ │ (DS, QWen) │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────┘
```
## 项目目录结构
```
janus/
├── cmd/
│ └── server/
│ └── main.go # 应用入口,依赖组装
├── internal/
│ ├── config/
│ │ └── config.go # YAML 配置加载(viper
│ ├── model/
│ │ ├── provider.go # Provider 模型
│ │ ├── api_key.go # ApiKey 模型
│ │ ├── model.go # AIModel 模型
│ │ └── request_log.go # RequestLog 模型
│ ├── repository/
│ │ ├── provider_repo.go # Provider CRUD
│ │ ├── api_key_repo.go # ApiKey CRUD
│ │ ├── model_repo.go # AIModel CRUD
│ │ └── request_log_repo.go # Log 查询
│ ├── service/
│ │ ├── provider_svc.go # Provider 业务逻辑
│ │ ├── api_key_svc.go # ApiKey 管理
│ │ ├── balancer_svc.go # 负载均衡核心
│ │ ├── proxy_svc.go # 请求代理引擎
│ │ ├── health_svc.go # 健康检查 & 故障转移
│ │ └── rate_limit_svc.go # 速率限制
│ ├── handler/
│ │ ├── proxy_handler.go # /v1/* 代理端点
│ │ ├── provider_handler.go # Provider 管理 API
│ │ ├── api_key_handler.go # ApiKey 管理 API
│ │ ├── model_handler.go # Model 管理 API
│ │ ├── stats_handler.go # 统计 API
│ │ └── health_handler.go # 健康检查
│ ├── middleware/
│ │ ├── auth.go # API Key 认证
│ │ ├── ratelimit.go # 速率限制中间件
│ │ ├── logging.go # 请求日志中间件
│ │ ├── cors.go # CORS
│ │ └── recovery.go # Panic 恢复
│ ├── proxy/
│ │ ├── openai.go # OpenAI 协议适配器
│ │ ├── anthropic.go # Anthropic 协议适配器
│ │ └── adapter.go # 适配器接口定义
│ ├── balancer/
│ │ ├── strategy.go # 策略接口
│ │ ├── round_robin.go # 轮询实现
│ │ └── weighted.go # 加权实现
│ ├── health/
│ │ ├── checker.go # 健康检查 Worker
│ │ └── circuit_breaker.go # 熔断器
│ └── index/
│ └── index.go # 路由注册
├── pkg/
│ ├── response/
│ │ └── response.go # 统一响应格式
│ ├── errors/
│ │ └── errors.go # 自定义错误类型
│ └── utils/
│ └── utils.go # 工具函数
├── web/ # Vue 3 前端项目
│ ├── src/
│ │ ├── views/
│ │ ├── components/
│ │ ├── api/
│ │ └── index/
│ ├── package.json
│ └── vite.config.ts
├── migrations/ # 数据库迁移 SQL
│ ├── 001_init.up.sql
│ └── 001_init.down.sql
├── config.yaml # 默认配置
└── go.mod
```
## 数据库设计
```sql
-- providers: AI 服务提供商
CREATE TABLE providers (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE, -- openai / anthropic / deepseek
base_url VARCHAR(255) NOT NULL, -- https://api.openai.com
protocol VARCHAR(20) NOT NULL DEFAULT 'openai', -- openai / anthropic
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active / disabled
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- api_keys: API 密钥池
CREATE TABLE api_keys (
id BIGSERIAL PRIMARY KEY,
provider_id BIGINT NOT NULL REFERENCES providers(id),
key_value TEXT NOT NULL, -- 加密存储
name VARCHAR(100), -- 别名/备注
weight INT NOT NULL DEFAULT 1, -- 负载权重
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/disabled/depleted
fail_count INT NOT NULL DEFAULT 0, -- 连续失败计数
last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- models: 可用模型列表
CREATE TABLE models (
id BIGSERIAL PRIMARY KEY,
provider_id BIGINT NOT NULL REFERENCES providers(id),
name VARCHAR(100) NOT NULL, -- gpt-4o / claude-sonnet-4-5
input_price DECIMAL(10,8) DEFAULT 0, -- 每 1K token 价格
output_price DECIMAL(10,8) DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(provider_id, name)
);
-- request_logs: 请求日志
CREATE TABLE request_logs (
id BIGSERIAL PRIMARY KEY,
api_key_id BIGINT,
model_id BIGINT,
provider_id BIGINT,
request_method VARCHAR(10),
request_path VARCHAR(500),
request_body TEXT,
response_body TEXT,
status_code INT,
latency_ms INT,
tokens_in INT DEFAULT 0,
tokens_out INT DEFAULT 0,
cost DECIMAL(10,6) DEFAULT 0,
error_message TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 索引
CREATE INDEX idx_api_keys_provider_id ON api_keys(provider_id);
CREATE INDEX idx_api_keys_status ON api_keys(status);
CREATE INDEX idx_models_provider_id ON models(provider_id);
CREATE INDEX idx_request_logs_created_at ON request_logs(created_at DESC);
CREATE INDEX idx_request_logs_provider_id ON request_logs(provider_id);
```
## API 设计
### 代理端点(OpenAI 兼容 & Anthropic 兼容)
```
POST /v1/chat/completions # OpenAI 聊天补全
POST /v1/embeddings # OpenAI 嵌入
POST /v1/messages # Anthropic Messages API
GET /v1/models # 列出可用模型(聚合所有 provider)
```
### 管理 APIDashboard 用)
```
# Providers
GET /api/admin/providers
POST /api/admin/providers
GET /api/admin/providers/:id
PUT /api/admin/providers/:id
DELETE /api/admin/providers/:id
# API Keys
GET /api/admin/api-keys?provider_id=
POST /api/admin/api-keys
PUT /api/admin/api-keys/:id
DELETE /api/admin/api-keys/:id
POST /api/admin/api-keys/:id/toggle # 启用/禁用
# Models
GET /api/admin/models?provider_id=
POST /api/admin/models
PUT /api/admin/models/:id
DELETE /api/admin/models/:id
# Stats
GET /api/admin/stats/overview # 总览统计
GET /api/admin/stats/requests # 请求明细
```
## 负载均衡 & 故障转移设计
### 负载策略接口
```go
type Strategy interface {
Next(keys []*model.ApiKey) *model.ApiKey
Name() string
}
```
### 策略实现
1. **RoundRobin** - 无状态轮询,适合均匀分布
2. **WeightedRoundRobin** - 按 weight 字段加权分配
### 健康检查
- 每个 ApiKey 维护 `fail_count` 连续失败计数
- 失败超过阈值(默认 3 次)自动标记为 `disabled`
- 后台 goroutine 定期重试 disabled 的 key30s 间隔)
- 正常请求也作为隐式健康检查
### 故障转移流程
1. Balancer 选取一个 ApiKey
2. 代理请求到上游
3. 如果失败(5xx / 超时 / 网络错误):
- 递增 `fail_count`
- 达到阈值标记 `disabled`
- 立即从剩余 key 中选择重试(最多 3 次)
4. 如果成功:重置 `fail_count` 为 0
### 熔断器(单例 per ApiKey
```
状态机: CLOSED → (连续失败) → OPEN → (冷却时间) → HALF_OPEN → (成功) → CLOSED
→ (失败) → OPEN
```
## 分阶段实施计划
### Phase 1: 项目骨架 & 基础设施(第 1-2 天)
> 📋 **详细计划**: [phase-1-infrastructure.md](phase-1-infrastructure.md)
**学习目标**: Go 项目组织、package 管理、Gin 基础
**核心任务**:
- 初始化 `go.mod`,安装 gin / gorm / viper / pgx 依赖
- 实现配置加载(Viper + YAML + 环境变量)
- 实现 GORM 数据库连接和连接池配置
- 实现 `/health` 端点,组装 Gin 启动流程
- 添加 `.env` 支持和 `.gitignore`
**交付物**: 可运行的服务,响应 `/health`,连接数据库
### Phase 2: 数据模型 & Repository 层(第 2-3 天)
**学习目标**: Go struct、GORM 关系映射、interface 定义
具体任务:
- [ ] 定义 `internal/model/provider.go`struct + GORM 标签)
- [ ] 定义 `internal/model/api_key.go`
- [ ] 定义 `internal/model/model.go`AIModel
- [ ] 定义 `internal/model/request_log.go`
- [ ] 编写 SQL 迁移脚本 `migrations/001_init.up.sql`
- [ ] 实现 `internal/repository/provider_repo.go`CRUD
- [ ] 实现 `internal/repository/api_key_repo.go`
- [ ] 实现 `internal/repository/model_repo.go`
- [ ] 编写 Repository 层的单元测试
**交付物**: 完整的数据访问层,可通过测试验证
### Phase 3: Provider & API Key 管理 API(第 3-4 天)
**学习目标**: Gin handler、请求验证、JSON 序列化、统一错误处理
具体任务:
- [ ] 实现 `pkg/response/response.go`:统一 JSON 响应格式
- [ ] 实现 `pkg/errors/errors.go`:业务错误码定义
- [ ] 实现 `internal/service/provider_svc.go`
- [ ] 实现 `internal/service/api_key_svc.go`
- [ ] 实现 `internal/handler/provider_handler.go`CRUD API
- [ ] 实现 `internal/handler/api_key_handler.go`CRUD API
- [ ] 实现 `internal/handler/model_handler.go`:模型管理 API
- [ ] 添加请求验证(binding tags + validator
- [ ] Postman/curl 可测试的完整管理 API
**交付物**: 可通过 REST API 管理 Provider、ApiKey、Model
### Phase 4: 代理引擎(第 4-6 天)
**学习目标**: HTTP 反向代理、streaming、context、Go http.Client
具体任务:
- [ ] 实现 `internal/proxy/adapter.go`:协议适配器接口
```go
type Adapter interface {
Forward(ctx context.Context, key *model.ApiKey, body []byte) (*ProxyResult, error)
Protocol() string
}
```
- [ ] 实现 `internal/proxy/openai.go`OpenAI 请求转发
- 支持 `/v1/chat/completions`(含 streaming
- 支持 `/v1/embeddings`
- [ ] 实现 `internal/proxy/anthropic.go`Anthropic 请求转发
- 支持 `/v1/messages`(含 streaming
- [ ] 实现 `internal/service/proxy_svc.go`:编排代理流程
- [ ] 实现 `internal/handler/proxy_handler.go`
- 解析请求路径确定目标 protocol
- 从 header 获取 Janus API Key → 查找 upstream key 池
- 调用代理服务
- [ ] 实现 `internal/middleware/auth.go`Janus 自身的 API Key 认证
- [ ] 添加请求日志记录(写入 request_logs
- [ ] 处理 streaming 响应(SSE
**交付物**: 可以用 curl 通过 Janus 调用 OpenAI/Anthropic
### Phase 5: 负载均衡(第 6-7 天)
**学习目标**: Go interface 多态、sync.Mutex、原子操作
具体任务:
- [ ] 实现 `internal/balancer/strategy.go`Strategy 接口
- [ ] 实现 `internal/balancer/round_robin.go`
- 线程安全的轮询计数器(sync.Mutex 或 atomic
- 跳过 disabled key
- [ ] 实现 `internal/balancer/weighted.go`
- 加权轮询算法
- 平滑加权轮询(Nginx 算法)
- [ ] 实现 `internal/service/balancer_svc.go`
- 集成策略选择(可通过 API 或配置切换)
- 从 DB 加载可用 key 列表(带缓存)
- [ ] 修改 ProxyService 集成 BalancerService
- [ ] 编写负载均衡的单元测试(验证分布均匀性)
**交付物**: 请求自动在多个 ApiKey 间负载均衡
### Phase 6: 健康检查 & 故障转移(第 7-9 天)
**学习目标**: Goroutine、Channel、context 取消、并发模式
具体任务:
- [ ] 实现 `internal/health/circuit_breaker.go`
- 三态状态机(Closed/Open/HalfOpen
- 可配置阈值和冷却时间
- [ ] 实现 `internal/health/checker.go`
- 后台 goroutine 定期检查 disabled key
- 向每个 provider 发送轻量健康检查请求
- 通过 channel 接收检查结果
- [ ] 实现 `internal/service/health_svc.go`
- 管理所有 key 的健康状态
- 优雅关闭(context 取消 → 停止所有 goroutine
- [ ] 修改 ProxyService 添加故障转移逻辑:
- 请求失败 → 标记 key → 从剩余池中重试
- 最大重试次数 3
- 熔断器介入
- [ ] 添加健康状态 APIGET /api/admin/keys/:id/health
**交付物**: 单个 key 故障时自动切换,故障 key 自动恢复
### Phase 7: 速率限制 & 可观测性(第 9-10 天)
**学习目标**: Middleware 模式、token bucket 算法、结构化日志
具体任务:
- [ ] 实现 `internal/service/rate_limit_svc.go`
- Token bucket 算法(golang.org/x/time/rate
- 支持按 Janus API Key 限流
- 支持按上游模型限流
- [ ] 实现 `internal/middleware/ratelimit.go`
- 从 Gin context 提取 key
- 返回 429 Too Many Requests
- 添加 Retry-After header
- [ ] 实现 `internal/middleware/logging.go`
- 结构化日志(使用 slog 或 zerolog
- 记录 latency、status、path、tokens
- [ ] 实现 `internal/middleware/recovery.go`Panic 恢复
- [ ] 实现 `internal/handler/stats_handler.go`
- 总请求数、成功率、平均延迟
- 按 Provider/Model 分组统计
- 费用汇总
- [ ] 添加 `internal/middleware/cors.go`
**交付物**: 完整的中间件链,可观测的统计数据
### Phase 8: Dashboard 前端 & 容器化(第 10-14 天)
**学习目标**: 前后端集成、Docker、项目收尾
具体任务:
- [ ] 初始化 Vue 3 + Vite 项目(`web/`
- [ ] 安装 UI 组件库(Element Plus 或 Naive UI
- [ ] 实现 Providers 管理页面
- [ ] 实现 API Keys 管理页面
- [ ] 实现 Models 管理页面
- [ ] 实现统计仪表盘页面(图表)
- [ ] 前端构建产物由 Gin 静态文件服务托管
- [ ] 编写 Dockerfile(多阶段构建)
- [ ] 编写 docker-compose.ymlJanus + PostgreSQL
- [ ] 编写 README.md(中英文)
**交付物**: 完整的可部署项目
## Go 学习路径映射
| Phase | Go 特性 | Java 对比 |
|-------|---------|-----------|
| 1 | package, import, func, defer | package ≈ package, defer ≈ finally |
| 2 | struct, interface, 指针 | struct ≈ class(无继承), interface 隐式满足 |
| 3 | error handling, type assertion | error 返回值 ≠ exception |
| 4 | context, http.Client, io.Reader | context ≈ 请求上下文传递 |
| 5 | sync.Mutex, atomic, slice | 无内置锁语法,需显式使用 |
| 6 | goroutine, channel, select | ≈ ThreadPool + BlockingQueue |
| 7 | middleware 闭包, functional options | ≈ Filter chain / Builder pattern |
| 8 | 前后端集成, Docker | 通用技能 |
## 关键技术决策
1. **配置管理**: Viper(支持 YAML + 环境变量覆盖)
2. **ORM**: GORM v2AutoMigrate 用于开发,生产用迁移脚本)
3. **API Key 加密**: 使用 AES-256-GCM 加密存储 key_value
4. **Streaming**: 使用 `http.Flusher` + SSE 协议
5. **日志**: slogGo 1.21+ 内置结构化日志)
6. **前端**: Vue 3 + Vite + Element Plus(轻量、国内文档丰富)
7. **容器化**: 多阶段 Docker 构建,docker-compose 一键部署
## 验证方式
每个 Phase 完成后的验证方法:
1. `go build ./...` 编译通过
2. `go vet ./...` 静态分析通过
3. `go test ./...` 单元测试通过
4. curl / Postman 手动测试关键 API
5. Phase 4+ 可用 OpenAI SDK 指向 Janus 实际调用
6. Phase 6 手动停用一个 key 验证故障转移
7. Phase 8 `docker-compose up` 一键启动