feat: 初始化 ChatGPT Team 管理后端项目

- 添加用户认证模块 (JWT + 密码管理)
- 添加 ChatGPT 账户管理功能
- 添加卡密管理功能 (创建、批量生成、查询)
- 添加邀请功能
- 配置数据库迁移和路由系统
This commit is contained in:
sar
2026-01-13 14:42:56 +08:00
commit 42c423bd32
29 changed files with 2969 additions and 0 deletions

View File

@@ -0,0 +1,229 @@
package service
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// ChatGPTService ChatGPT API 服务
type ChatGPTService struct {
client *http.Client
baseURL string
}
// NewChatGPTService 创建 ChatGPT 服务
func NewChatGPTService() *ChatGPTService {
return &ChatGPTService{
client: &http.Client{
Timeout: 30 * time.Second,
},
baseURL: "https://chatgpt.com/backend-api",
}
}
// SubscriptionInfo 订阅信息
type SubscriptionInfo struct {
SeatsInUse int `json:"seats_in_use"`
SeatsEntitled int `json:"seats_entitled"`
ActiveStart time.Time `json:"active_start"`
ActiveUntil time.Time `json:"active_until"`
IsValid bool `json:"is_valid"`
}
// subscriptionResponse ChatGPT API 响应结构
type subscriptionResponse struct {
SeatsInUse int `json:"seats_in_use"`
SeatsEntitled int `json:"seats_entitled"`
ActiveStart string `json:"active_start"`
ActiveUntil string `json:"active_until"`
}
// GetSubscription 获取订阅信息
func (s *ChatGPTService) GetSubscription(teamAccountID, authToken string) (*SubscriptionInfo, error) {
url := fmt.Sprintf("%s/subscriptions?account_id=%s", s.baseURL, teamAccountID)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
// 设置请求头
req.Header.Set("Authorization", authToken)
req.Header.Set("chatgpt-account-id", teamAccountID)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
resp, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return &SubscriptionInfo{IsValid: false}, nil
}
var subResp subscriptionResponse
if err := json.Unmarshal(body, &subResp); err != nil {
return &SubscriptionInfo{IsValid: false}, nil
}
// 解析时间
info := &SubscriptionInfo{
SeatsInUse: subResp.SeatsInUse,
SeatsEntitled: subResp.SeatsEntitled,
IsValid: true,
}
if subResp.ActiveStart != "" {
if t, err := time.Parse(time.RFC3339, subResp.ActiveStart); err == nil {
info.ActiveStart = t
}
}
if subResp.ActiveUntil != "" {
if t, err := time.Parse(time.RFC3339, subResp.ActiveUntil); err == nil {
info.ActiveUntil = t
}
}
return info, nil
}
// InviteRequest 邀请请求结构
type InviteRequest struct {
EmailAddresses []string `json:"email_addresses"`
Role string `json:"role"`
ResendEmails bool `json:"resend_emails"`
}
// InviteResponse 邀请响应
type InviteResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
RawBody string `json:"-"`
}
// SendInvite 发送邀请到 ChatGPT Team
func (s *ChatGPTService) SendInvite(teamAccountID, authToken, email string) (*InviteResponse, error) {
url := fmt.Sprintf("%s/accounts/%s/invites", s.baseURL, teamAccountID)
// 构建请求体
reqBody := InviteRequest{
EmailAddresses: []string{email},
Role: "standard-user",
ResendEmails: true,
}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
// 设置请求头
req.Header.Set("Authorization", authToken)
req.Header.Set("chatgpt-account-id", teamAccountID)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
resp, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
// 检查响应状态码
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return &InviteResponse{
Success: true,
Message: "Invitation sent successfully",
RawBody: string(body),
}, nil
}
// 解析错误响应
return &InviteResponse{
Success: false,
Message: fmt.Sprintf("API returned status %d: %s", resp.StatusCode, string(body)),
RawBody: string(body),
}, nil
}
// RemoveMemberResponse 移除成员响应
type RemoveMemberResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
RawBody string `json:"-"`
}
// RemoveMember 从 ChatGPT Team 移除成员
func (s *ChatGPTService) RemoveMember(teamAccountID, authToken, email string) (*RemoveMemberResponse, error) {
url := fmt.Sprintf("%s/accounts/%s/invites", s.baseURL, teamAccountID)
// 构建请求体 - 只需要邮箱
reqBody := map[string]string{
"email_address": email,
}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(bodyBytes))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
// 设置请求头
req.Header.Set("Authorization", authToken)
req.Header.Set("chatgpt-account-id", teamAccountID)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
resp, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
// 检查响应状态码
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return &RemoveMemberResponse{
Success: true,
Message: "Member removed successfully",
RawBody: string(body),
}, nil
}
// 解析错误响应
return &RemoveMemberResponse{
Success: false,
Message: fmt.Sprintf("API returned status %d: %s", resp.StatusCode, string(body)),
RawBody: string(body),
}, nil
}