Files
GPT_Management/backend/internal/handler/card_key_handler.go
sar 8d60704eda feat: 实现前端卡密管理界面
- 卡密列表展示与分页功能

- 单个/批量创建卡密

- 卡密删除与批量删除

- 卡密导出功能 (file-saver)

- 启用/禁用状态切换

- 状态判断 (有效/已使用/已失效)

- Toast 通知系统 (vue-sonner)

- 登录页面错误提示优化

- 后端登录错误消息中文化
2026-01-13 21:34:56 +08:00

368 lines
8.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"net/http"
"strconv"
"strings"
"time"
"gpt-manager-go/internal/middleware"
"gpt-manager-go/internal/models"
"gpt-manager-go/internal/repository"
)
// CardKeyHandler 卡密处理器
type CardKeyHandler struct {
repo *repository.CardKeyRepository
}
// NewCardKeyHandler 创建处理器
func NewCardKeyHandler(repo *repository.CardKeyRepository) *CardKeyHandler {
return &CardKeyHandler{repo: repo}
}
// CreateCardKeyRequest 创建卡密请求
type CreateCardKeyRequest struct {
ValidityDays int `json:"validity_days"` // 有效期天数默认30
MaxUses int `json:"max_uses"` // 最大使用次数默认1
}
// BatchCreateCardKeyRequest 批量创建卡密请求
type BatchCreateCardKeyRequest struct {
Count int `json:"count"` // 创建数量
ValidityDays int `json:"validity_days"` // 有效期天数默认30
MaxUses int `json:"max_uses"` // 最大使用次数默认1
}
// CardKeyResponse 卡密响应
type CardKeyResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Data *models.CardKey `json:"data,omitempty"`
Keys []*models.CardKey `json:"keys,omitempty"`
Total int `json:"total,omitempty"`
Page int `json:"page,omitempty"`
PageSize int `json:"page_size,omitempty"`
}
// Handle 处理卡密接口 (GET: 列表, POST: 创建)
func (h *CardKeyHandler) Handle(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
h.list(w, r)
case http.MethodPost:
h.create(w, r)
default:
respondJSON(w, http.StatusMethodNotAllowed, CardKeyResponse{
Success: false,
Message: "Method not allowed",
})
}
}
// create 创建单个卡密
func (h *CardKeyHandler) create(w http.ResponseWriter, r *http.Request) {
var req CreateCardKeyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
// 允许空请求体,使用默认值
req = CreateCardKeyRequest{}
}
// 设置默认值
if req.ValidityDays <= 0 {
req.ValidityDays = 30
}
if req.MaxUses <= 0 {
req.MaxUses = 1
}
// 获取当前用户
claims := middleware.GetUserFromContext(r.Context())
// 生成卡密
cardKey := &models.CardKey{
Key: generateCardKey(),
MaxUses: req.MaxUses,
UsedCount: 0,
ValidityType: "custom",
ExpiresAt: time.Now().AddDate(0, 0, req.ValidityDays),
IsActive: true,
CreatedByID: claims.UserID,
CreatedAt: time.Now(),
}
if err := h.repo.Create(cardKey); err != nil {
respondJSON(w, http.StatusInternalServerError, CardKeyResponse{
Success: false,
Message: "Failed to create card key: " + err.Error(),
})
return
}
respondJSON(w, http.StatusCreated, CardKeyResponse{
Success: true,
Message: "Card key created successfully",
Data: cardKey,
})
}
// BatchCreate 批量创建卡密 (POST /api/cardkeys/batch)
func (h *CardKeyHandler) BatchCreate(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
respondJSON(w, http.StatusMethodNotAllowed, CardKeyResponse{
Success: false,
Message: "Method not allowed",
})
return
}
var req BatchCreateCardKeyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondJSON(w, http.StatusBadRequest, CardKeyResponse{
Success: false,
Message: "Invalid request body",
})
return
}
// 验证和设置默认值
if req.Count <= 0 {
respondJSON(w, http.StatusBadRequest, CardKeyResponse{
Success: false,
Message: "Count must be greater than 0",
})
return
}
if req.Count > 100 {
respondJSON(w, http.StatusBadRequest, CardKeyResponse{
Success: false,
Message: "Count cannot exceed 100",
})
return
}
if req.ValidityDays <= 0 {
req.ValidityDays = 30
}
if req.MaxUses <= 0 {
req.MaxUses = 1
}
// 获取当前用户
claims := middleware.GetUserFromContext(r.Context())
// 批量创建
var createdKeys []*models.CardKey
expiresAt := time.Now().AddDate(0, 0, req.ValidityDays)
for i := 0; i < req.Count; i++ {
cardKey := &models.CardKey{
Key: generateCardKey(),
MaxUses: req.MaxUses,
UsedCount: 0,
ValidityType: "custom",
ExpiresAt: expiresAt,
IsActive: true,
CreatedByID: claims.UserID,
CreatedAt: time.Now(),
}
if err := h.repo.Create(cardKey); err != nil {
// 如果创建失败,返回已创建的卡密
respondJSON(w, http.StatusInternalServerError, CardKeyResponse{
Success: false,
Message: "Failed to create some card keys",
Keys: createdKeys,
})
return
}
createdKeys = append(createdKeys, cardKey)
}
respondJSON(w, http.StatusCreated, CardKeyResponse{
Success: true,
Message: "Card keys created successfully",
Keys: createdKeys,
})
}
// list 获取卡密列表
func (h *CardKeyHandler) list(w http.ResponseWriter, r *http.Request) {
// 获取分页参数
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, CardKeyResponse{
Success: false,
Message: "Failed to count card keys",
})
return
}
// 获取分页数据
cardKeys, err := h.repo.FindAllPaginated(page, pageSize)
if err != nil {
respondJSON(w, http.StatusInternalServerError, CardKeyResponse{
Success: false,
Message: "Failed to fetch card keys",
})
return
}
respondJSON(w, http.StatusOK, CardKeyResponse{
Success: true,
Message: "OK",
Keys: cardKeys,
Total: total,
Page: page,
PageSize: pageSize,
})
}
// generateCardKey 生成卡密格式: XXXX-XXXX-XXXX-XXXX
func generateCardKey() string {
bytes := make([]byte, 8)
rand.Read(bytes)
hex := strings.ToUpper(hex.EncodeToString(bytes))
return hex[0:4] + "-" + hex[4:8] + "-" + hex[8:12] + "-" + hex[12:16]
}
// Delete 删除单个卡密 (DELETE /api/cardkeys/delete?id=xxx)
func (h *CardKeyHandler) Delete(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodDelete {
respondJSON(w, http.StatusMethodNotAllowed, CardKeyResponse{
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, CardKeyResponse{
Success: false,
Message: "Invalid card key ID",
})
return
}
if err := h.repo.Delete(id); err != nil {
respondJSON(w, http.StatusInternalServerError, CardKeyResponse{
Success: false,
Message: "Failed to delete card key",
})
return
}
respondJSON(w, http.StatusOK, CardKeyResponse{
Success: true,
Message: "Card key deleted successfully",
})
}
// BatchDeleteRequest 批量删除请求
type BatchDeleteRequest struct {
IDs []int `json:"ids"`
}
// BatchDelete 批量删除卡密 (DELETE /api/cardkeys/batch)
func (h *CardKeyHandler) BatchDelete(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodDelete {
respondJSON(w, http.StatusMethodNotAllowed, CardKeyResponse{
Success: false,
Message: "Method not allowed",
})
return
}
var req BatchDeleteRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondJSON(w, http.StatusBadRequest, CardKeyResponse{
Success: false,
Message: "Invalid request body",
})
return
}
if len(req.IDs) == 0 {
respondJSON(w, http.StatusBadRequest, CardKeyResponse{
Success: false,
Message: "No IDs provided",
})
return
}
if err := h.repo.BatchDelete(req.IDs); err != nil {
respondJSON(w, http.StatusInternalServerError, CardKeyResponse{
Success: false,
Message: "Failed to delete card keys",
})
return
}
respondJSON(w, http.StatusOK, CardKeyResponse{
Success: true,
Message: "Card keys deleted successfully",
})
}
// ToggleActiveRequest 切换激活状态请求
type ToggleActiveRequest struct {
ID int `json:"id"`
IsActive bool `json:"is_active"`
}
// ToggleActive 切换卡密激活状态 (POST /api/cardkeys/toggle)
func (h *CardKeyHandler) ToggleActive(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
respondJSON(w, http.StatusMethodNotAllowed, CardKeyResponse{
Success: false,
Message: "Method not allowed",
})
return
}
var req ToggleActiveRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondJSON(w, http.StatusBadRequest, CardKeyResponse{
Success: false,
Message: "Invalid request body",
})
return
}
if err := h.repo.ToggleActive(req.ID, req.IsActive); err != nil {
respondJSON(w, http.StatusInternalServerError, CardKeyResponse{
Success: false,
Message: "Failed to toggle card key status",
})
return
}
respondJSON(w, http.StatusOK, CardKeyResponse{
Success: true,
Message: "Card key status updated successfully",
})
}