Files
GPT_Management/internal/handler/invite_handler.go
sar 42c423bd32 feat: 初始化 ChatGPT Team 管理后端项目
- 添加用户认证模块 (JWT + 密码管理)
- 添加 ChatGPT 账户管理功能
- 添加卡密管理功能 (创建、批量生成、查询)
- 添加邀请功能
- 配置数据库迁移和路由系统
2026-01-13 14:42:56 +08:00

394 lines
9.7 KiB
Go

package handler
import (
"database/sql"
"encoding/json"
"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"`
}
// 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,
})
}