- 卡密列表展示与分页功能 - 单个/批量创建卡密 - 卡密删除与批量删除 - 卡密导出功能 (file-saver) - 启用/禁用状态切换 - 状态判断 (有效/已使用/已失效) - Toast 通知系统 (vue-sonner) - 登录页面错误提示优化 - 后端登录错误消息中文化
323 lines
8.0 KiB
Go
323 lines
8.0 KiB
Go
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
|
|
chatgptService *service.ChatGPTService
|
|
}
|
|
|
|
// NewChatGPTAccountHandler 创建处理器
|
|
func NewChatGPTAccountHandler(repo *repository.ChatGPTAccountRepository, chatgptService *service.ChatGPTService) *ChatGPTAccountHandler {
|
|
return &ChatGPTAccountHandler{
|
|
repo: repo,
|
|
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.repo.Delete(id); err != nil {
|
|
respondJSON(w, http.StatusInternalServerError, AccountResponse{
|
|
Success: false,
|
|
Message: "Failed to delete account",
|
|
})
|
|
return
|
|
}
|
|
|
|
respondJSON(w, http.StatusOK, AccountResponse{
|
|
Success: true,
|
|
Message: "Account deleted successfully",
|
|
})
|
|
}
|