- 添加用户认证模块 (JWT + 密码管理) - 添加 ChatGPT 账户管理功能 - 添加卡密管理功能 (创建、批量生成、查询) - 添加邀请功能 - 配置数据库迁移和路由系统
230 lines
6.1 KiB
Go
230 lines
6.1 KiB
Go
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
|
|
}
|