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

19 KiB
Raw Permalink Blame History

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

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

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

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

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

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/:id2s 间隔),返回最新 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 = plusplan = 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

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

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           # 全局任务状态 + 轮询逻辑

路由结构:

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 配置

# 管理员密码 (必填)
ADMIN_PASSWORD=your_secure_password

# JWT 密钥 (可选,默认随机生成)
JWT_SECRET=random_secret_key

# 服务端口 (默认 8080)
PORT=8080

# 数据库路径 (默认 ./gptplus.db)
DB_PATH=./gptplus.db

八、构建 & 部署

开发模式

# 前端
cd web/frontend && npm run dev

# 后端 (代理前端)
go run ./cmd/gptplus/ --dev

生产构建

# 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: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/* 全部 核心业务逻辑不动