package handler import ( "database/sql" "encoding/json" "fmt" "net/http" "time" "gpt-manager-go/internal/models" "gpt-manager-go/internal/repository" "gpt-manager-go/internal/service" ) // InviteHandler 邀请处理器 type InviteHandler struct { accountRepo *repository.ChatGPTAccountRepository invitationRepo *repository.InvitationRepository cardKeyRepo *repository.CardKeyRepository chatgptService *service.ChatGPTService } // NewInviteHandler 创建邀请处理器 func NewInviteHandler( accountRepo *repository.ChatGPTAccountRepository, invitationRepo *repository.InvitationRepository, cardKeyRepo *repository.CardKeyRepository, chatgptService *service.ChatGPTService, ) *InviteHandler { return &InviteHandler{ accountRepo: accountRepo, invitationRepo: invitationRepo, cardKeyRepo: cardKeyRepo, chatgptService: chatgptService, } } // AdminInviteRequest 管理员邀请请求 type AdminInviteRequest struct { Email string `json:"email"` AccountID int `json:"account_id"` // 可选,不传则自动选择 } // CardKeyInviteRequest 卡密邀请请求 type CardKeyInviteRequest struct { CardKey string `json:"card_key"` Email string `json:"email"` } // InviteResponse 邀请响应 type InviteResponse struct { Success bool `json:"success"` Message string `json:"message"` InvitationID int `json:"invitation_id,omitempty"` AccountName string `json:"account_name,omitempty"` } // InviteListResponse 邀请列表响应 type InviteListResponse struct { Success bool `json:"success"` Message string `json:"message"` Data []*models.Invitation `json:"data,omitempty"` } // ListByAccount 获取账号的邀请列表 (GET /api/invite?account_id=xxx) func (h *InviteHandler) ListByAccount(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { respondJSON(w, http.StatusMethodNotAllowed, InviteListResponse{ Success: false, Message: "Method not allowed", }) return } accountIDStr := r.URL.Query().Get("account_id") if accountIDStr == "" { respondJSON(w, http.StatusBadRequest, InviteListResponse{ Success: false, Message: "account_id is required", }) return } var accountID int if _, err := fmt.Sscanf(accountIDStr, "%d", &accountID); err != nil { respondJSON(w, http.StatusBadRequest, InviteListResponse{ Success: false, Message: "Invalid account_id", }) return } invitations, err := h.invitationRepo.FindByAccountID(accountID) if err != nil { respondJSON(w, http.StatusInternalServerError, InviteListResponse{ Success: false, Message: "Failed to fetch invitations", }) return } respondJSON(w, http.StatusOK, InviteListResponse{ Success: true, Message: "OK", Data: invitations, }) } // InviteByAdmin 管理员邀请/移除接口 (POST/DELETE /api/invite) func (h *InviteHandler) InviteByAdmin(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: h.handleInvite(w, r) case http.MethodDelete: h.handleRemoveMember(w, r) default: respondJSON(w, http.StatusMethodNotAllowed, InviteResponse{ Success: false, Message: "Method not allowed", }) } } // handleInvite 处理邀请逻辑 func (h *InviteHandler) handleInvite(w http.ResponseWriter, r *http.Request) { var req AdminInviteRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Invalid request body", }) return } // 验证邮箱 if req.Email == "" { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Email is required", }) return } // 获取账号 var account *models.ChatGPTAccount var err error if req.AccountID > 0 { // 指定账号 account, err = h.accountRepo.FindByID(req.AccountID) if err != nil || account == nil { respondJSON(w, http.StatusNotFound, InviteResponse{ Success: false, Message: "Account not found", }) return } if !account.IsActive || account.AvailableSeats() <= 0 { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Account is not active or has no available seats", }) return } } else { // 自动选择可用账号 account, err = h.findAvailableAccount() if err != nil || account == nil { respondJSON(w, http.StatusServiceUnavailable, InviteResponse{ Success: false, Message: "No available team accounts", }) return } } // 发送邀请 inviteResp, err := h.chatgptService.SendInvite(account.TeamAccountID, account.AuthToken, req.Email) if err != nil { respondJSON(w, http.StatusInternalServerError, InviteResponse{ Success: false, Message: "Failed to send invite: " + err.Error(), }) return } // 创建邀请记录 invitation := &models.Invitation{ AccountID: account.ID, InvitedEmail: req.Email, Status: models.StatusSent, CreatedAt: time.Now(), } if inviteResp.Success { invitation.Status = models.StatusSent } else { invitation.Status = models.StatusFailed invitation.ErrorMessage = sql.NullString{String: inviteResp.Message, Valid: true} } if err := h.invitationRepo.Create(invitation); err != nil { // 记录失败不影响主流程 } if !inviteResp.Success { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: inviteResp.Message, }) return } // 更新账号使用时间 h.accountRepo.UpdateLastUsed(account.ID) respondJSON(w, http.StatusOK, InviteResponse{ Success: true, Message: "Invitation sent successfully", InvitationID: invitation.ID, AccountName: account.Name, }) } // InviteByCardKey 卡密邀请 (POST /api/invite/card) func (h *InviteHandler) InviteByCardKey(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { respondJSON(w, http.StatusMethodNotAllowed, InviteResponse{ Success: false, Message: "Method not allowed", }) return } var req CardKeyInviteRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Invalid request body", }) return } // 验证必填字段 if req.CardKey == "" || req.Email == "" { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Card key and email are required", }) return } // 验证卡密 cardKey, err := h.cardKeyRepo.FindByKey(req.CardKey) if err != nil { respondJSON(w, http.StatusInternalServerError, InviteResponse{ Success: false, Message: "Database error", }) return } if cardKey == nil { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Invalid card key", }) return } // 检查卡密是否可用 if !cardKey.IsUsable() { if cardKey.IsExpired() { respondJSON(w, http.StatusForbidden, InviteResponse{ Success: false, Message: "Card key has expired", }) } else if cardKey.UsedCount >= cardKey.MaxUses { respondJSON(w, http.StatusForbidden, InviteResponse{ Success: false, Message: "Card key usage limit exceeded", }) } else { respondJSON(w, http.StatusForbidden, InviteResponse{ Success: false, Message: "Card key is not active", }) } return } // 查找可用账号 account, err := h.findAvailableAccount() if err != nil || account == nil { respondJSON(w, http.StatusServiceUnavailable, InviteResponse{ Success: false, Message: "No available team accounts", }) return } // 发送邀请 inviteResp, err := h.chatgptService.SendInvite(account.TeamAccountID, account.AuthToken, req.Email) if err != nil { respondJSON(w, http.StatusInternalServerError, InviteResponse{ Success: false, Message: "Failed to send invite: " + err.Error(), }) return } // 创建邀请记录 invitation := &models.Invitation{ CardKeyID: sql.NullInt64{Int64: int64(cardKey.ID), Valid: true}, AccountID: account.ID, InvitedEmail: req.Email, CreatedAt: time.Now(), } if inviteResp.Success { invitation.Status = models.StatusSent } else { invitation.Status = models.StatusFailed invitation.ErrorMessage = sql.NullString{String: inviteResp.Message, Valid: true} } if err := h.invitationRepo.Create(invitation); err != nil { // 记录失败不影响主流程 } if !inviteResp.Success { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: inviteResp.Message, }) return } // 增加卡密使用次数 h.cardKeyRepo.IncrementUsedCount(cardKey.ID) // 更新账号使用时间 h.accountRepo.UpdateLastUsed(account.ID) respondJSON(w, http.StatusOK, InviteResponse{ Success: true, Message: "Invitation sent successfully", }) } // findAvailableAccount 查找可用账号(轮询) func (h *InviteHandler) findAvailableAccount() (*models.ChatGPTAccount, error) { accounts, err := h.accountRepo.FindActiveWithAvailableSeats() if err != nil { return nil, err } if len(accounts) == 0 { return nil, nil } // 返回第一个(已按可用席位数降序、最后使用时间升序排序) return accounts[0], nil } // RemoveMemberRequest 移除成员请求 type RemoveMemberRequest struct { Email string `json:"email"` AccountID int `json:"account_id"` // 必填,指定从哪个账号移除 } // handleRemoveMember 处理移除成员逻辑 func (h *InviteHandler) handleRemoveMember(w http.ResponseWriter, r *http.Request) { var req RemoveMemberRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Invalid request body", }) return } // 验证必填字段 if req.Email == "" { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Email is required", }) return } if req.AccountID <= 0 { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Account ID is required", }) return } // 获取账号 account, err := h.accountRepo.FindByID(req.AccountID) if err != nil || account == nil { respondJSON(w, http.StatusNotFound, InviteResponse{ Success: false, Message: "Account not found", }) return } if !account.IsActive { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: "Account is not active", }) return } // 调用 ChatGPT API 移除成员 removeResp, err := h.chatgptService.RemoveMember(account.TeamAccountID, account.AuthToken, req.Email) if err != nil { respondJSON(w, http.StatusInternalServerError, InviteResponse{ Success: false, Message: "Failed to remove member: " + err.Error(), }) return } if !removeResp.Success { respondJSON(w, http.StatusBadRequest, InviteResponse{ Success: false, Message: removeResp.Message, }) return } // 删除邀请记录 if err := h.invitationRepo.DeleteByEmailAndAccountID(req.Email, req.AccountID); err != nil { // 删除记录失败不影响主流程,只记录日志 } respondJSON(w, http.StatusOK, InviteResponse{ Success: true, Message: "Member removed successfully", AccountName: account.Name, }) }