- 卡密列表展示与分页功能 - 单个/批量创建卡密 - 卡密删除与批量删除 - 卡密导出功能 (file-saver) - 启用/禁用状态切换 - 状态判断 (有效/已使用/已失效) - Toast 通知系统 (vue-sonner) - 登录页面错误提示优化 - 后端登录错误消息中文化
368 lines
8.9 KiB
Go
368 lines
8.9 KiB
Go
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",
|
||
})
|
||
}
|