626 lines
20 KiB
Go
626 lines
20 KiB
Go
package handlers
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"license-manager/internal/api/middleware"
|
|
"license-manager/internal/config"
|
|
"license-manager/internal/models"
|
|
"license-manager/internal/service"
|
|
"license-manager/pkg/i18n"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type LicenseHandler struct {
|
|
licenseService service.LicenseService
|
|
}
|
|
|
|
// NewLicenseHandler 创建许可证处理器
|
|
func NewLicenseHandler(licenseService service.LicenseService) *LicenseHandler {
|
|
return &LicenseHandler{
|
|
licenseService: licenseService,
|
|
}
|
|
}
|
|
|
|
// GetLicenseList 查询许可证列表
|
|
// @Summary 查询许可证列表
|
|
// @Description 分页查询许可证列表,支持筛选
|
|
// @Tags 许可证管理
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param page query int false "页码,默认1" minimum(1)
|
|
// @Param page_size query int false "每页条数,默认20,最大100" minimum(1) maximum(100)
|
|
// @Param authorization_code_id query string false "授权码ID筛选"
|
|
// @Param customer_id query string false "客户ID筛选"
|
|
// @Param status query string false "状态筛选" Enums(active, inactive, revoked)
|
|
// @Param is_online query bool false "在线状态筛选"
|
|
// @Param sort query string false "排序字段,默认created_at" Enums(created_at, updated_at, activated_at, last_heartbeat)
|
|
// @Param order query string false "排序方向,默认desc" Enums(asc, desc)
|
|
// @Success 200 {object} models.APIResponse{data=models.LicenseListResponse} "查询成功"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 401 {object} models.ErrorResponse "未认证"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/licenses [get]
|
|
func (h *LicenseHandler) GetLicenseList(c *gin.Context) {
|
|
var req models.LicenseListRequest
|
|
if err := c.ShouldBindQuery(&req); err != nil {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message + ": " + err.Error(),
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
data, err := h.licenseService.GetLicenseList(ctx, &req)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusOK, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// GetLicense 获取许可证详情
|
|
// @Summary 获取许可证详情
|
|
// @Description 根据许可证ID获取许可证详细信息
|
|
// @Tags 许可证管理
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "许可证ID"
|
|
// @Success 200 {object} models.APIResponse{data=models.LicenseDetailResponse} "查询成功"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 401 {object} models.ErrorResponse "未认证"
|
|
// @Failure 404 {object} models.ErrorResponse "许可证不存在"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/licenses/{id} [get]
|
|
func (h *LicenseHandler) GetLicense(c *gin.Context) {
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
data, err := h.licenseService.GetLicense(ctx, id)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusOK, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// CreateLicense 手动添加许可证
|
|
// @Summary 手动添加许可证
|
|
// @Description 为指定授权码手动创建许可证
|
|
// @Tags 许可证管理
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param request body models.LicenseCreateRequest true "许可证创建信息"
|
|
// @Success 201 {object} models.APIResponse{data=models.License} "创建成功"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 401 {object} models.ErrorResponse "未认证"
|
|
// @Failure 404 {object} models.ErrorResponse "授权码不存在"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/licenses [post]
|
|
func (h *LicenseHandler) CreateLicense(c *gin.Context) {
|
|
var req models.LicenseCreateRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message + ": " + err.Error(),
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
data, err := h.licenseService.CreateLicense(ctx, &req)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusCreated, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// RevokeLicense 撤销许可证
|
|
// @Summary 撤销许可证
|
|
// @Description 撤销指定的许可证,撤销后无法再使用
|
|
// @Tags 许可证管理
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "许可证ID"
|
|
// @Param request body models.LicenseRevokeRequest true "撤销原因"
|
|
// @Success 200 {object} models.APIResponse{data=models.License} "撤销成功"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 401 {object} models.ErrorResponse "未认证"
|
|
// @Failure 404 {object} models.ErrorResponse "许可证不存在"
|
|
// @Failure 409 {object} models.ErrorResponse "许可证已被撤销"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/licenses/{id}/revoke [put]
|
|
func (h *LicenseHandler) RevokeLicense(c *gin.Context) {
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
var req models.LicenseRevokeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message + ": " + err.Error(),
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
data, err := h.licenseService.RevokeLicense(ctx, id, &req)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusOK, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// DownloadLicenseFile 下载许可证文件
|
|
// @Summary 下载许可证文件
|
|
// @Description 下载加密的许可证文件,用于客户端软件激活
|
|
// @Tags 许可证管理
|
|
// @Accept json
|
|
// @Produce application/zip
|
|
// @Security BearerAuth
|
|
// @Param id path string true "许可证ID"
|
|
// @Success 200 {file} binary "包含许可证文件和RSA公钥的压缩包"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 401 {object} models.ErrorResponse "未认证"
|
|
// @Failure 404 {object} models.ErrorResponse "许可证不存在"
|
|
// @Failure 409 {object} models.ErrorResponse "许可证已被撤销"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/licenses/{id}/download [get]
|
|
func (h *LicenseHandler) DownloadLicenseFile(c *gin.Context) {
|
|
lang := middleware.GetLanguage(c)
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
fileData, fileName, licenseKey, err := h.licenseService.GenerateLicenseFile(ctx, id)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
cfg := config.GetConfig()
|
|
if cfg == nil || cfg.License.RSA.PublicKeyPath == "" {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
publicKeyPath := filepath.Clean(cfg.License.RSA.PublicKeyPath)
|
|
publicKeyData, err := os.ReadFile(publicKeyPath)
|
|
if err != nil {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
zipWriter := zip.NewWriter(&buffer)
|
|
|
|
const zipLicenseFileName = "license.lic"
|
|
licenseFileWriter, err := zipWriter.Create(zipLicenseFileName)
|
|
if err != nil {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
if _, err := licenseFileWriter.Write(fileData); err != nil {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
publicKeyWriter, err := zipWriter.Create("rsa_public_key.pem")
|
|
if err != nil {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
if _, err := publicKeyWriter.Write(publicKeyData); err != nil {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
if err := zipWriter.Close(); err != nil {
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
packageFileName := fmt.Sprintf("license_package_%s.zip", strings.TrimSpace(licenseKey))
|
|
if strings.TrimSpace(licenseKey) == "" {
|
|
baseName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
|
|
packageFileName = fmt.Sprintf("%s_package.zip", baseName)
|
|
}
|
|
|
|
// 设置响应头,指示这是一个文件下载
|
|
c.Header("Content-Description", "File Transfer")
|
|
c.Header("Content-Type", "application/zip")
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", packageFileName))
|
|
c.Header("Content-Transfer-Encoding", "binary")
|
|
c.Header("Cache-Control", "must-revalidate")
|
|
c.Header("Pragma", "public")
|
|
|
|
// 返回文件内容
|
|
c.Data(http.StatusOK, "application/zip", buffer.Bytes())
|
|
}
|
|
|
|
// ActivateLicense 激活许可证
|
|
// @Summary 激活许可证
|
|
// @Description 客户端使用授权码激活软件,获取许可证文件
|
|
// @Tags 许可证激活
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body models.ActivateRequest true "激活请求"
|
|
// @Success 200 {object} models.APIResponse{data=models.ActivateResponse} "激活成功"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 404 {object} models.ErrorResponse "授权码不存在"
|
|
// @Failure 409 {object} models.ErrorResponse "授权码已锁定或已过期"
|
|
// @Failure 429 {object} models.ErrorResponse "激活数量已达上限"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/activate [post]
|
|
func (h *LicenseHandler) ActivateLicense(c *gin.Context) {
|
|
var req models.ActivateRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message + ": " + err.Error(),
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
// 获取客户端IP
|
|
clientIP := c.ClientIP()
|
|
|
|
data, err := h.licenseService.ActivateLicense(ctx, &req, clientIP)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusOK, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// Heartbeat 心跳检测
|
|
// @Summary 心跳检测
|
|
// @Description 客户端定期发送心跳,更新在线状态和使用数据
|
|
// @Tags 许可证激活
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body models.HeartbeatRequest true "心跳请求"
|
|
// @Success 200 {object} models.APIResponse{data=models.HeartbeatResponse} "心跳成功"
|
|
// @Failure 400 {object} models.ErrorResponse "请求参数无效"
|
|
// @Failure 404 {object} models.ErrorResponse "许可证不存在"
|
|
// @Failure 409 {object} models.ErrorResponse "许可证已被撤销"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/heartbeat [post]
|
|
func (h *LicenseHandler) Heartbeat(c *gin.Context) {
|
|
var req models.HeartbeatRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900001", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message + ": " + err.Error(),
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
// 获取客户端IP
|
|
clientIP := c.ClientIP()
|
|
|
|
data, err := h.licenseService.Heartbeat(ctx, &req, clientIP)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusOK, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// GetStatsOverview 获取授权概览统计
|
|
// @Summary 获取授权概览统计
|
|
// @Description 获取授权码、许可证的总体统计信息
|
|
// @Tags 统计分析
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Success 200 {object} models.APIResponse{data=models.StatsOverviewResponse} "查询成功"
|
|
// @Failure 401 {object} models.ErrorResponse "未认证"
|
|
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
|
// @Router /api/v1/stats/overview [get]
|
|
func (h *LicenseHandler) GetStatsOverview(c *gin.Context) {
|
|
// 设置语言到Context中
|
|
ctx := middleware.WithLanguage(c.Request.Context(), c)
|
|
|
|
data, err := h.licenseService.GetStatsOverview(ctx)
|
|
if err != nil {
|
|
// 错误已经在Service层完全包装好了,直接使用
|
|
var i18nErr *i18n.I18nError
|
|
if errors.As(err, &i18nErr) {
|
|
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
|
Code: i18nErr.Code,
|
|
Message: i18nErr.Message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
} else {
|
|
// 兜底:理论上不应该到这里,因为Service层应该返回I18nError
|
|
lang := middleware.GetLanguage(c)
|
|
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang)
|
|
c.JSON(status, models.ErrorResponse{
|
|
Code: errCode,
|
|
Message: message,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
lang := middleware.GetLanguage(c)
|
|
successMessage := i18n.GetErrorMessage("000000", lang)
|
|
c.JSON(http.StatusOK, models.APIResponse{
|
|
Code: "000000",
|
|
Message: successMessage,
|
|
Data: data,
|
|
})
|
|
}
|