Files
gpt-plus-gpt/docs/web-panel-plan.md
2026-03-15 20:48:19 +08:00

539 lines
19 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.
# GPT-Plus Web 管理面板方案
## 一、技术选型
| 层 | 选型 | 理由 |
|---|------|------|
| Backend | Go + Gin | 与现有 Go 代码无缝集成Gin 轻量高性能 |
| Frontend | Vue 3 + Vite + Element Plus | 中后台组件库成熟,中文友好 |
| Database | SQLite + GORM | 零部署依赖,单文件数据库,适合单机场景 |
| Auth | JWT + bcrypt | .env 设置管理员密码JWT 签发 token |
| 部署 | 单二进制 + embed 前端 | `go:embed` 嵌入前端 dist一个二进制即运行 |
## 二、目录结构
```
gpt-plus/
├── cmd/
│ └── gptplus/
│ └── main.go # 入口:启动 web server
├── config/
│ └── config.go # 保留Web 化后从 DB 读取
├── internal/
│ ├── db/
│ │ ├── db.go # GORM 初始化 + 自动迁移
│ │ ├── models.go # 数据模型
│ │ └── query.go # 查询/聚合方法
│ ├── handler/
│ │ ├── auth.go # POST /api/login
│ │ ├── config.go # GET/PUT /api/config
│ │ ├── task.go # 任务 CRUD + 控制
│ │ ├── account.go # 账号列表/搜索/导出/检查
│ │ └── middleware.go # JWT 鉴权中间件
│ ├── task/
│ │ ├── manager.go # 任务管理器(创建/运行/停止)
│ │ ├── runner.go # 单任务执行器(调用现有 runOnce
│ │ └── types.go # 任务状态/事件类型
│ └── service/
│ ├── account_svc.go # 账号业务逻辑
│ └── export_svc.go # 导出(单文件/ZIP 打包)
├── pkg/ # 现有核心逻辑不动
│ ├── auth/
│ ├── chatgpt/
│ ├── stripe/
│ ├── httpclient/
│ ├── provider/
│ │ ├── card/
│ │ ├── email/
│ │ └── proxy/
│ ├── captcha/
│ └── storage/ # 保留文件导出DB 为主存储
├── web/
│ └── frontend/ # Vue 3 SPA
│ ├── src/
│ │ ├── views/
│ │ │ ├── Login.vue
│ │ │ ├── Dashboard.vue # 概览(统计卡片)
│ │ │ ├── Config.vue # 配置管理
│ │ │ ├── Tasks.vue # 任务列表
│ │ │ ├── TaskDetail.vue # 任务详情 + 实时进度
│ │ │ ├── Accounts.vue # 账号面板
│ │ │ └── AccountDetail.vue # 母号详情(含小号列表)
│ │ ├── api/ # axios 封装
│ │ ├── router/
│ │ ├── stores/ # Pinia 状态管理
│ │ └── components/
│ ├── package.json
│ └── vite.config.ts
├── .env # ADMIN_PASSWORD=xxx, JWT_SECRET=xxx
├── gptplus.db # SQLite 数据库文件 (运行时生成)
└── go.mod
```
## 三、数据模型
### 3.1 系统配置表 `configs`
```go
type SystemConfig struct {
ID uint `gorm:"primaryKey"`
Key string `gorm:"uniqueIndex;size:100"` // e.g. "proxy.url", "email.provider"
Value string `gorm:"type:text"`
Group string `gorm:"size:50;index"` // "proxy", "email", "card", "stripe", etc.
Label string `gorm:"size:100"` // 中文显示名
Type string `gorm:"size:20"` // "string", "int", "bool", "password", "textarea"
UpdatedAt time.Time
}
```
配置组 (Group):
- `proxy` — 代理模式(直连/固定代理/B2Proxy动态代理、B2Proxy API 地址、代理区域(国家代码,如 US/JP/KR、协议(socks5/http)、会话时长等
- `email` — 邮箱网关域名、API Key
- `card` — 卡片默认绑定上限(max_binds)、默认地址信息、开卡 API 密钥
- `stripe` — build_hash, tag_version, fingerprint_dir
- `captcha` — 验证码提供商、API Key
- `account` — 密码长度、locale
- `team` — 开关、workspace 前缀、座位数、优惠券、邀请数
> 邮箱不再作为配置项,去掉 MailGateway全量使用 Outlook。邮箱通过 mailboxes 表管理。
### 3.2 邮箱表 `mailboxes`
```go
type Mailbox struct {
ID uint `gorm:"primaryKey"`
Email string `gorm:"size:200;uniqueIndex"`
Password string `gorm:"size:200"`
ClientID string `gorm:"size:200"` // Outlook OAuth client_id
RefreshToken string `gorm:"type:text"` // Outlook OAuth refresh_token
// 使用状态
Status string `gorm:"size:20;index;default:available"`
// available / in_use / used / used_member / failed / disabled
UsedByAccountID *uint `gorm:"index"`
UsedForRole string `gorm:"size:20"` // "owner" / "member"
UsedAt *time.Time
TaskID string `gorm:"size:36;index"`
CreatedAt time.Time `gorm:"index"`
UpdatedAt time.Time
}
```
**邮箱生命周期**:
```
available → in_use → used (主号) / used_member (小号) / failed (可回收)
→ disabled (管理员禁用)
failed → available (回收重用)
```
**适配**: 新增 `DBEmailProvider`,不再用内存索引,完全 DB 状态驱动。
**API**: 导入/回收/禁用/统计,邮箱面板可视化管理。
### 3.3 注册任务表 `tasks`
```go
type Task struct {
ID string `gorm:"primaryKey;size:36"` // UUID
Type string `gorm:"size:20;index"` // "plus", "team", "both"
TotalCount int // 本轮要注册多少个
DoneCount int // 已完成(成功+失败)
SuccessCount int // 成功数
FailCount int // 失败数
Status string `gorm:"size:20;index"` // pending/running/stopping/stopped/completed
Config string `gorm:"type:text"` // 快照:任务创建时的配置 JSON
CreatedAt time.Time `gorm:"index"`
StartedAt *time.Time
StoppedAt *time.Time
}
```
状态流转:
```
pending → running → completed
→ stopping → stopped (graceful stop)
```
### 3.3 任务日志表 `task_logs`
```go
type TaskLog struct {
ID uint `gorm:"primaryKey"`
TaskID string `gorm:"size:36;index"`
Index int // 第几个账号 (1-based)
Email string `gorm:"size:200"`
Status string `gorm:"size:20"` // success/failed/skipped
Plan string `gorm:"size:20"` // plus/team/free
Error string `gorm:"type:text"`
Duration int // 耗时(秒)
CreatedAt time.Time
}
```
### 3.4 卡密表 `card_codes` + 卡片表 `cards`
两层管理:卡密(兑换码)→ 卡片(已开卡)。同一时间只有一张卡 active。
**CardCode**: unused → redeeming → redeemed(关联card) / failed
**Card**: available → active(全局唯一) → exhausted / rejected / disabled
自动切换active失效 → 找available → 无则兑换卡密 → 无则报错
API: `/api/cards` + `/api/card-codes` (CRUD + 激活 + 兑换 + 统计)
UI: 两个Tab卡片+卡密),顶部高亮当前激活卡
详见 Obsidian 完整文档。
### 3.5 账号表 `accounts`
```go
type Account struct {
ID uint `gorm:"primaryKey"`
TaskID string `gorm:"size:36;index"` // 来源任务
Email string `gorm:"size:200;uniqueIndex"`
Password string `gorm:"size:100"`
Plan string `gorm:"size:20;index"` // "plus", "team_owner", "team_member"
ParentID *uint `gorm:"index"` // team_member → 指向 team_owner
Parent *Account `gorm:"foreignKey:ParentID"`
SubAccounts []Account `gorm:"foreignKey:ParentID"` // team_owner 的小号列表
// Auth tokens
AccessToken string `gorm:"type:text"`
RefreshToken string `gorm:"type:text"`
IDToken string `gorm:"type:text"`
AccountID string `gorm:"size:100"`
DeviceID string `gorm:"size:100"`
UserID string `gorm:"size:100"`
// Team specific
TeamWorkspaceID string `gorm:"size:100"`
WorkspaceToken string `gorm:"type:text"` // team workspace-scoped token
// Status
Status string `gorm:"size:20;index;default:active"` // active/banned/unknown
StatusCheckedAt *time.Time
Note string `gorm:"type:text"` // 用户备注
CreatedAt time.Time `gorm:"index"`
UpdatedAt time.Time
}
```
## 四、API 设计
### 4.1 认证
```
POST /api/login { password: "xxx" } → { token: "jwt..." }
```
所有 `/api/*` 路由需 `Authorization: Bearer <jwt>` 头(中间件校验)。
### 4.2 配置管理
```
GET /api/config → { groups: { proxy: [...], email: [...], ... } }
PUT /api/config { items: [ {key, value}, ... ] }
GET /api/config/test-connection → 测试代理/邮箱/卡片连通性
```
### 4.3 任务管理
```
POST /api/tasks { type: "plus", count: 10 } → { id: "uuid", status: "pending" }
GET /api/tasks → [ { id, type, total, done, success, fail, status, created_at } ]
GET /api/tasks/:id → { ...task, logs: [...] }
POST /api/tasks/:id/start → 启动任务
POST /api/tasks/:id/stop → 优雅停止(完成当前轮后停止)
DELETE /api/tasks/:id → 删除已完成/已停止的任务
```
**实时进度**: 前端轮询 `GET /api/tasks/:id`2s 间隔),返回最新 done/success/fail 计数 + 最新日志。
### 4.4 账号管理
```
GET /api/accounts ?plan=plus|team_owner&search=email&page=1&size=20
GET /api/accounts/:id → 账号详情(含小号列表)
POST /api/accounts/check { ids: [1,2,3] } → 批量检查状态
PUT /api/accounts/:id/note { note: "xxx" } → 更新备注
POST /api/accounts/export { ids: [1,2,3], note: "导出备注" } → 文件下载
```
**账号列表规则**:
- 默认只显示 `plan = plus``plan = team_owner`
- `team_member` 不在列表中显示,归属于其 `parent` (team_owner)
- 搜索可以搜到 team_member显示其所属母号
**导出规则**:
- 导出 Plus 账号 → 单个 `{email}.auth.json`
- 导出 Team 母号 → 母号 `.auth.json` + 所有小号 `.auth.json`
- 导出文件数 > 1 → 自动打包为 `.zip`
- 导出文件名: `export_{备注}_{timestamp}.zip``{email}.auth.json`
### 4.5 Dashboard
```
GET /api/dashboard → { total_accounts, plus_count, team_count,
active_tasks, recent_tasks: [...],
today_registrations, success_rate }
```
## 五、任务执行引擎
### 5.1 TaskManager
```go
type TaskManager struct {
mu sync.Mutex
running map[string]*TaskRunner // taskID → runner
db *gorm.DB
}
func (m *TaskManager) Start(taskID string) error
func (m *TaskManager) Stop(taskID string) error // 设置 stopping flag
func (m *TaskManager) GetStatus(taskID string) *TaskStatus
```
### 5.2 TaskRunner
```go
type TaskRunner struct {
task *db.Task
config *config.Config // 从任务快照还原
cancel context.CancelFunc
stopping atomic.Bool // graceful stop signal
}
func (r *TaskRunner) Run(ctx context.Context) {
for i := 0; i < r.task.TotalCount; i++ {
if r.stopping.Load() {
break // 当前轮完成后停止
}
result := runOnce(ctx, i, ...) // 调用现有核心逻辑
r.saveResult(result) // 写入 DB
r.updateProgress() // 更新任务计数
}
}
```
### 5.3 停止逻辑
用户点击"停止" → `POST /api/tasks/:id/stop`:
1. 设置 `task.Status = "stopping"` (DB)
2. 设置 `runner.stopping = true` (内存)
3. 当前正在执行的 `runOnce()` 会跑完
4. 下一轮循环检测到 `stopping` → break
5. 更新 `task.Status = "stopped"` (DB)
## 六、前端页面
### 6.0 全局布局架构
```
┌─────────────────────────────────────────────────────┐
│ Navbar [Logo GPT-Plus] [配置] [任务] [账号] [■ 任务进度浮窗] [退出] │
├─────────────────────────────────────────────────────┤
│ │
│ <router-view /> │
│ (当前页面内容) │
│ │
└─────────────────────────────────────────────────────┘
```
**核心设计: 全局任务进度浮窗 (TaskProgressWidget)**
右上角常驻一个**任务进度迷你组件**,行为规则:
- 有 running/stopping 状态的任务时**始终显示**,跨路由不消失
- 登录页**不显示**layout 不包含 navbar
- 无活跃任务时**隐藏**或显示为灰色空状态
- 点击浮窗可**展开**查看详情或**跳转**到任务详情页
**浮窗 UI**:
```
┌──────────────────────────┐
│ ● 任务进行中 3/10 │ ← 收起态:一行摘要 + 进度条
│ ████████░░░░ 30% │
│ [展开 ▼] │
├──────────────────────────┤ ← 展开态:显示详细信息
│ 类型: Plus │
│ 成功: 2 失败: 1 │
│ 当前: user_xxx@outlook.. │
│ [查看详情] [停止任务] │
└──────────────────────────┘
```
**实现方式**:
- `Pinia` 全局 store: `useTaskStore()` — 管理活跃任务状态
- store 内置定时轮询3s 间隔),**独立于路由组件生命周期**
- 登录成功后启动轮询,退出登录时停止
- 组件: `TaskProgressWidget.vue` 挂在 `AppLayout.vue` 的 navbar 中
```
src/
├── layouts/
│ ├── AppLayout.vue # 已登录布局navbar + sidebar + router-view
│ └── BlankLayout.vue # 登录页布局:无 navbar
├── components/
│ └── TaskProgressWidget.vue # 全局任务进度浮窗
├── stores/
│ └── taskStore.ts # 全局任务状态 + 轮询逻辑
```
**路由结构**:
```typescript
const routes = [
{
path: '/login',
component: BlankLayout, // 无 navbar无浮窗
children: [{ path: '', component: Login }]
},
{
path: '/',
component: AppLayout, // 有 navbar + TaskProgressWidget
meta: { requiresAuth: true },
children: [
{ path: '', component: Dashboard },
{ path: 'config', component: Config },
{ path: 'tasks', component: Tasks },
{ path: 'tasks/:id', component: TaskDetail },
{ path: 'accounts', component: Accounts },
{ path: 'accounts/:id', component: AccountDetail },
]
}
]
```
### 6.1 登录页 (BlankLayout)
- 单密码输入框 + 登录按钮
- JWT 存 localStorage
- 登录成功 → 跳转 Dashboard + 启动 taskStore 轮询
### 6.2 Dashboard (首页)
- 统计卡片: 总账号数、Plus 数、Team 数、今日注册数
- 活跃任务列表(简要)
- 最近注册记录
### 6.3 配置页
- 分组表单 (Tabs: 代理 | 邮箱 | 卡片 | Stripe | 验证码 | 账号 | Team)
- 每组内用 Element Plus 表单组件
- 密码类型用 `el-input type="password"`
- 底部"保存"按钮 + 连通性测试按钮
### 6.4 任务页
- 顶部: "新建任务" 按钮 → 弹窗 (选类型 + 输入数量)
- 任务列表表格: ID | 类型 | 进度 (3/10) | 成功/失败 | 状态 | 操作
- 状态标签: pending=灰, running=蓝(动画), stopping=黄, stopped=橙, completed=绿
- 操作: 启动 | 停止 | 查看详情 | 删除
- 点击任务 → 任务详情页
### 6.5 任务详情页
- 进度条 + 计数器 (成功/失败/总数)
- 实时日志表格: 序号 | 邮箱 | 状态 | 类型 | 耗时 | 错误信息
- 2 秒轮询刷新(与全局 store 复用数据)
### 6.6 账号面板
- 筛选栏: 类型下拉(全部/Plus/Team母号) | 搜索框(邮箱) | 状态筛选(全部/正常/封禁)
- 表格: 勾选框 | 邮箱 | 类型 | 状态 | 小号数 | 备注 | 创建时间 | 操作
- 操作: 查看详情 | 编辑备注 | 检查状态 | 导出
- 批量操作栏: 勾选后 → 批量导出 | 批量检查状态
- 导出弹窗: 输入备注 → 确认导出 → 浏览器下载
### 6.7 账号详情页 (Team 母号)
- 母号信息卡片
- 小号列表表格 (最多 4 个): 邮箱 | 状态 | Token 概要
## 七、.env 配置
```env
# 管理员密码 (必填)
ADMIN_PASSWORD=your_secure_password
# JWT 密钥 (可选,默认随机生成)
JWT_SECRET=random_secret_key
# 服务端口 (默认 8080)
PORT=8080
# 数据库路径 (默认 ./gptplus.db)
DB_PATH=./gptplus.db
```
## 八、构建 & 部署
### 开发模式
```bash
# 前端
cd web/frontend && npm run dev
# 后端 (代理前端)
go run ./cmd/gptplus/ --dev
```
### 生产构建
```bash
# 1. 构建前端
cd web/frontend && npm run build
# 2. 构建后端 (embed 前端 dist)
CGO_ENABLED=1 go build -o gptplus ./cmd/gptplus/
# 注: SQLite 需要 CGO
# 3. 部署
scp gptplus server:/root/gptplus/
scp .env server:/root/gptplus/
ssh server 'cd /root/gptplus && ./gptplus'
```
### 单二进制方案
```go
//go:embed web/frontend/dist
var frontendFS embed.FS
// Gin 中挂载
router.StaticFS("/", http.FS(frontendFS))
```
## 九、迁移计划
### Phase 1: 基础框架 (Day 1-2)
- [ ] 初始化 SQLite + GORM + 数据模型
- [ ] Gin 路由 + JWT 中间件
- [ ] 配置 CRUD API
- [ ] Vue 3 项目初始化 + 登录页 + 配置页
### Phase 2: 任务引擎 (Day 2-3)
- [ ] TaskManager + TaskRunner
- [ ] 重构 runOnce() 解耦:接受 config 参数而非全局状态
- [ ] 任务 API + 前端任务页
### Phase 3: 账号面板 (Day 3-4)
- [ ] Account 存储从文件 → DB
- [ ] 搜索/筛选/分页 API
- [ ] 导出JSON/ZIP
- [ ] 状态检查 (调用 ChatGPT session API)
- [ ] 前端账号面板
### Phase 4: 打磨 (Day 4-5)
- [ ] Dashboard 统计
- [ ] embed 前端 + 单二进制构建
- [ ] 错误处理 + 日志
- [ ] 部署到服务器
## 十、改动范围
| 动作 | 路径 | 说明 |
|------|------|------|
| 新增 | `internal/db/` | GORM 模型 + 查询 |
| 新增 | `internal/handler/` | Gin HTTP handlers |
| 新增 | `internal/task/` | 任务管理引擎 |
| 新增 | `internal/service/` | 导出/检查业务逻辑 |
| 新增 | `web/frontend/` | Vue 3 SPA |
| 重写 | `cmd/gptplus/main.go` | CLI → Web Server |
| 修改 | `config/config.go` | 支持从 DB 加载 |
| 修改 | `pkg/storage/json.go` | 同时写 DB + 文件 |
| 新增 | `.env` | 管理员密码 + JWT 密钥 |
| 保留 | `pkg/*` 全部 | 核心业务逻辑不动 |