package handler import ( "database/sql" "encoding/json" "net/http" "strconv" "time" "gpt-manager-go/internal/models" "gpt-manager-go/internal/repository" "gpt-manager-go/internal/service" ) // ChatGPTAccountHandler ChatGPT 账号处理器 type ChatGPTAccountHandler struct { repo *repository.ChatGPTAccountRepository invitationRepo *repository.InvitationRepository chatgptService *service.ChatGPTService } // NewChatGPTAccountHandler 创建处理器 func NewChatGPTAccountHandler(repo *repository.ChatGPTAccountRepository, invitationRepo *repository.InvitationRepository, chatgptService *service.ChatGPTService) *ChatGPTAccountHandler { return &ChatGPTAccountHandler{ repo: repo, invitationRepo: invitationRepo, chatgptService: chatgptService, } } // CreateAccountRequest 创建账号请求 type CreateAccountRequest struct { Name string `json:"name"` TeamAccountID string `json:"team_account_id"` AuthToken string `json:"auth_token"` } // AccountResponse 账号响应 type AccountResponse struct { Success bool `json:"success"` Message string `json:"message"` Data *models.ChatGPTAccount `json:"data,omitempty"` } // ListResponse 列表响应 type ListResponse struct { Success bool `json:"success"` Message string `json:"message"` Data []*models.ChatGPTAccount `json:"data,omitempty"` Total int `json:"total,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"page_size,omitempty"` } // Create 创建账号 func (h *ChatGPTAccountHandler) Create(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { respondJSON(w, http.StatusMethodNotAllowed, AccountResponse{ Success: false, Message: "Method not allowed", }) return } var req CreateAccountRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJSON(w, http.StatusBadRequest, AccountResponse{ Success: false, Message: "Invalid request body", }) return } // 验证必填字段 if req.TeamAccountID == "" || req.AuthToken == "" { respondJSON(w, http.StatusBadRequest, AccountResponse{ Success: false, Message: "team_account_id and auth_token are required", }) return } // 检查是否已存在 existing, err := h.repo.FindByTeamAccountID(req.TeamAccountID) if err != nil { respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Database error: " + err.Error(), }) return } if existing != nil { respondJSON(w, http.StatusConflict, AccountResponse{ Success: false, Message: "Team account already exists", }) return } // 调用 ChatGPT API 获取订阅信息 subInfo, err := h.chatgptService.GetSubscription(req.TeamAccountID, req.AuthToken) if err != nil { respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Failed to verify team account: " + err.Error(), }) return } // 设置账号名称 name := req.Name if name == "" { name = "Team-" + req.TeamAccountID[:8] } // 创建账号 account := &models.ChatGPTAccount{ Name: name, AuthToken: req.AuthToken, TeamAccountID: req.TeamAccountID, SeatsInUse: subInfo.SeatsInUse, SeatsEntitled: subInfo.SeatsEntitled, IsActive: subInfo.IsValid, } // 设置时间 if subInfo.IsValid { account.ActiveStart = sql.NullTime{Time: subInfo.ActiveStart, Valid: !subInfo.ActiveStart.IsZero()} account.ActiveUntil = sql.NullTime{Time: subInfo.ActiveUntil, Valid: !subInfo.ActiveUntil.IsZero()} account.LastCheck = sql.NullTime{Time: time.Now(), Valid: true} } if err := h.repo.Create(account); err != nil { respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Failed to create account: " + err.Error(), }) return } // 隐藏敏感信息 account.AuthToken = "" respondJSON(w, http.StatusCreated, AccountResponse{ Success: true, Message: "Account created successfully", Data: account, }) } // List 获取账号列表 func (h *ChatGPTAccountHandler) List(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { respondJSON(w, http.StatusMethodNotAllowed, ListResponse{ Success: false, Message: "Method not allowed", }) return } // 获取分页参数 pageStr := r.URL.Query().Get("page") pageSizeStr := r.URL.Query().Get("page_size") page, _ := strconv.Atoi(pageStr) pageSize, _ := strconv.Atoi(pageSizeStr) // 默认值 if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 } if pageSize > 100 { pageSize = 100 } // 获取总数 total, err := h.repo.Count() if err != nil { respondJSON(w, http.StatusInternalServerError, ListResponse{ Success: false, Message: "Failed to count accounts", }) return } // 获取分页数据 accounts, err := h.repo.FindAllPaginated(page, pageSize) if err != nil { respondJSON(w, http.StatusInternalServerError, ListResponse{ Success: false, Message: "Failed to fetch accounts", }) return } // 隐藏敏感信息 for _, a := range accounts { a.AuthToken = "" } respondJSON(w, http.StatusOK, ListResponse{ Success: true, Message: "OK", Data: accounts, Total: total, Page: page, PageSize: pageSize, }) } // Refresh 刷新账号信息 func (h *ChatGPTAccountHandler) Refresh(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { respondJSON(w, http.StatusMethodNotAllowed, AccountResponse{ Success: false, Message: "Method not allowed", }) return } // 获取账号 ID idStr := r.URL.Query().Get("id") id, err := strconv.Atoi(idStr) if err != nil { respondJSON(w, http.StatusBadRequest, AccountResponse{ Success: false, Message: "Invalid account ID", }) return } account, err := h.repo.FindByID(id) if err != nil || account == nil { respondJSON(w, http.StatusNotFound, AccountResponse{ Success: false, Message: "Account not found", }) return } // 调用 ChatGPT API 获取订阅信息 subInfo, err := h.chatgptService.GetSubscription(account.TeamAccountID, account.AuthToken) if err != nil { // API 调用失败,增加失败计数并设为不活跃 account.ConsecutiveFailures++ account.IsActive = false account.LastCheck = sql.NullTime{Time: time.Now(), Valid: true} h.repo.Update(account) respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Failed to refresh: " + err.Error(), }) return } // 更新账号信息 if subInfo.IsValid { account.SeatsInUse = subInfo.SeatsInUse account.SeatsEntitled = subInfo.SeatsEntitled account.ActiveStart = sql.NullTime{Time: subInfo.ActiveStart, Valid: !subInfo.ActiveStart.IsZero()} account.ActiveUntil = sql.NullTime{Time: subInfo.ActiveUntil, Valid: !subInfo.ActiveUntil.IsZero()} account.IsActive = true account.ConsecutiveFailures = 0 } else { account.IsActive = false account.ConsecutiveFailures++ } account.LastCheck = sql.NullTime{Time: time.Now(), Valid: true} if err := h.repo.Update(account); err != nil { respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Failed to update account", }) return } // 隐藏敏感信息 account.AuthToken = "" respondJSON(w, http.StatusOK, AccountResponse{ Success: true, Message: "Account refreshed successfully", Data: account, }) } // Delete 删除账号 func (h *ChatGPTAccountHandler) Delete(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { respondJSON(w, http.StatusMethodNotAllowed, AccountResponse{ Success: false, Message: "Method not allowed", }) return } idStr := r.URL.Query().Get("id") id, err := strconv.Atoi(idStr) if err != nil { respondJSON(w, http.StatusBadRequest, AccountResponse{ Success: false, Message: "Invalid account ID", }) return } // 先删除相关的邀请记录,避免外键约束失败 if err := h.invitationRepo.DeleteByAccountID(id); err != nil { respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Failed to delete related invitations: " + err.Error(), }) return } if err := h.repo.Delete(id); err != nil { respondJSON(w, http.StatusInternalServerError, AccountResponse{ Success: false, Message: "Failed to delete account: " + err.Error(), }) return } respondJSON(w, http.StatusOK, AccountResponse{ Success: true, Message: "Account deleted successfully", }) } // BatchDeleteRequest 批量删除请求 type BatchDeleteAccountRequest struct { IDs []int `json:"ids"` } // BatchDeleteResponse 批量操作响应 type BatchOperationResponse struct { Success bool `json:"success"` Message string `json:"message"` SuccessCount int `json:"success_count"` FailedCount int `json:"failed_count"` } // BatchDelete 批量删除账号 func (h *ChatGPTAccountHandler) BatchDelete(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { respondJSON(w, http.StatusMethodNotAllowed, BatchOperationResponse{ Success: false, Message: "Method not allowed", }) return } var req BatchDeleteAccountRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJSON(w, http.StatusBadRequest, BatchOperationResponse{ Success: false, Message: "Invalid request body", }) return } if len(req.IDs) == 0 { respondJSON(w, http.StatusBadRequest, BatchOperationResponse{ Success: false, Message: "No accounts selected", }) return } successCount := 0 failedCount := 0 for _, id := range req.IDs { // 先删除相关的邀请记录 if err := h.invitationRepo.DeleteByAccountID(id); err != nil { failedCount++ continue } // 再删除账号 if err := h.repo.Delete(id); err != nil { failedCount++ continue } successCount++ } respondJSON(w, http.StatusOK, BatchOperationResponse{ Success: failedCount == 0, Message: "Batch delete completed", SuccessCount: successCount, FailedCount: failedCount, }) } // BatchRefreshRequest 批量刷新请求 type BatchRefreshRequest struct { IDs []int `json:"ids"` } // BatchRefresh 批量刷新账号 func (h *ChatGPTAccountHandler) BatchRefresh(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { respondJSON(w, http.StatusMethodNotAllowed, BatchOperationResponse{ Success: false, Message: "Method not allowed", }) return } var req BatchRefreshRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJSON(w, http.StatusBadRequest, BatchOperationResponse{ Success: false, Message: "Invalid request body", }) return } if len(req.IDs) == 0 { respondJSON(w, http.StatusBadRequest, BatchOperationResponse{ Success: false, Message: "No accounts selected", }) return } successCount := 0 failedCount := 0 for _, id := range req.IDs { account, err := h.repo.FindByID(id) if err != nil || account == nil { failedCount++ continue } // 调用 ChatGPT API 获取订阅信息 subInfo, err := h.chatgptService.GetSubscription(account.TeamAccountID, account.AuthToken) if err != nil { account.ConsecutiveFailures++ account.IsActive = false account.LastCheck = sql.NullTime{Time: time.Now(), Valid: true} h.repo.Update(account) failedCount++ continue } // 更新账号信息 if subInfo.IsValid { account.SeatsInUse = subInfo.SeatsInUse account.SeatsEntitled = subInfo.SeatsEntitled account.ActiveStart = sql.NullTime{Time: subInfo.ActiveStart, Valid: !subInfo.ActiveStart.IsZero()} account.ActiveUntil = sql.NullTime{Time: subInfo.ActiveUntil, Valid: !subInfo.ActiveUntil.IsZero()} account.IsActive = true account.ConsecutiveFailures = 0 } else { account.IsActive = false account.ConsecutiveFailures++ } account.LastCheck = sql.NullTime{Time: time.Now(), Valid: true} if err := h.repo.Update(account); err != nil { failedCount++ continue } successCount++ } respondJSON(w, http.StatusOK, BatchOperationResponse{ Success: failedCount == 0, Message: "Batch refresh completed", SuccessCount: successCount, FailedCount: failedCount, }) }