This commit is contained in:
JiXieShi
2024-11-16 23:59:15 +08:00
parent f722153536
commit 87859c7bb8
42 changed files with 2018 additions and 485 deletions

View File

@@ -1,45 +1,33 @@
package api
import (
"licserver/internal/model"
"licserver/internal/service"
"net/http"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type DashboardHandler struct {
db *gorm.DB
dashboardService *service.DashboardService
}
func NewDashboardHandler(db *gorm.DB) *DashboardHandler {
return &DashboardHandler{db: db}
func NewDashboardHandler(dashboardService *service.DashboardService) *DashboardHandler {
return &DashboardHandler{dashboardService: dashboardService}
}
func (h *DashboardHandler) GetStats(c *gin.Context) {
var stats struct {
TotalDevices int64 `json:"total_devices"`
TotalLicenses int64 `json:"total_licenses"`
TodayNew int64 `json:"today_new"`
OnlineDevices int64 `json:"online_devices"`
// GetDashboardStats 获取仪表盘统计数据
func (h *DashboardHandler) GetDashboardStats(c *gin.Context) {
stats, err := h.dashboardService.GetDashboardStats()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": -1,
"error": err.Error(),
})
return
}
// 获取设备总数
h.db.Model(&model.Device{}).Count(&stats.TotalDevices)
// 获取授权码总数
h.db.Model(&model.LicenseCode{}).Count(&stats.TotalLicenses)
// 获取今日新增设备数
today := time.Now().Format("2006-01-02")
h.db.Model(&model.Device{}).Where("DATE(created_at) = ?", today).Count(&stats.TodayNew)
// 获取在线设备数最近30分钟内有活动的设备
thirtyMinutesAgo := time.Now().Add(-30 * time.Minute)
h.db.Model(&model.Device{}).
Where("last_active_at > ?", thirtyMinutesAgo).
Count(&stats.OnlineDevices)
c.JSON(http.StatusOK, stats)
c.JSON(http.StatusOK, gin.H{
"code": 0,
"data": stats,
})
}

View File

@@ -3,22 +3,25 @@ package api
import (
"fmt"
"net/http"
"time"
"strconv"
"licserver/internal/model"
"licserver/internal/service"
"licserver/internal/utils"
"github.com/gin-gonic/gin"
)
type DeviceHandler struct {
deviceService *service.DeviceService
config *utils.Config
}
func NewDeviceHandler(deviceService *service.DeviceService) *DeviceHandler {
func NewDeviceHandler(deviceService *service.DeviceService, config *utils.Config) *DeviceHandler {
return &DeviceHandler{deviceService: deviceService}
return &DeviceHandler{deviceService: deviceService, config: config}
}
@@ -50,7 +53,7 @@ func (h *DeviceHandler) GetDevices(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
params := &service.DeviceQueryParams{
@@ -89,19 +92,37 @@ func (h *DeviceHandler) GetDevices(c *gin.Context) {
}
func (h *DeviceHandler) UpdateStartCount(c *gin.Context) {
uid := c.Param("uid")
if err := h.deviceService.UpdateStartCount(uid); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
// 更新启动次数
err := h.deviceService.UpdateStartCount(uid)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": -1,
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{"message": "启动次数更新成功"})
// 获取更新后的设备信息
device, err := h.deviceService.GetDevice(uid)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": -1,
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "启动次数更新成功",
"data": gin.H{
"start_count": device.StartCount,
"status": device.Status,
"last_active_at": device.LastActiveAt,
},
})
}
func (h *DeviceHandler) UpdateDevice(c *gin.Context) {
@@ -207,19 +228,50 @@ func (h *DeviceHandler) RegisterDevice(c *gin.Context) {
}
func (h *DeviceHandler) ValidateDevice(c *gin.Context) {
uid := c.Param("uid")
if err := h.deviceService.ValidateDevice(uid); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
device, err := h.deviceService.GetDevice(uid)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": -1,
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{"message": "设备验证通过"})
// 验证设备状态
if err := h.deviceService.ValidateDevice(uid); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": -1,
"error": err.Error(),
})
return
}
// 准备加密响应数据
response := utils.DeviceValidateResponse{
Status: device.Status,
LicenseType: device.LicenseType,
ExpireTime: device.ExpireTime.Format(time.RFC3339),
StartCount: device.StartCount,
MaxUses: device.MaxUses,
Timestamp: time.Now().Unix(),
}
// 加密响应
encrypted, err := utils.EncryptResponse(response, []byte(h.config.Security.EncryptKey))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": -1,
"error": "加密响应失败: " + err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"data": encrypted,
})
}
func (h *DeviceHandler) BindLicense(c *gin.Context) {
@@ -360,14 +412,17 @@ func (h *DeviceHandler) UpdateDeviceModel(c *gin.Context) {
return
}
var model model.DeviceModel
if err := c.ShouldBindJSON(&model); err != nil {
var input model.DeviceModel
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.deviceService.UpdateDeviceModel(uint(id), &model); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
if err := h.deviceService.UpdateDeviceModel(uint(id), &input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": -1,
"error": err.Error(),
})
return
}
@@ -455,17 +510,3 @@ func (h *DeviceHandler) GetDeviceLogs(c *gin.Context) {
"data": logs,
})
}
// GetDashboardStats 获取仪表盘统计数据
func (h *DeviceHandler) GetDashboardStats(c *gin.Context) {
stats, err := h.deviceService.GetDashboardStats()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"data": stats,
})
}

View File

@@ -16,6 +16,7 @@ func SetupRouter(
siteHandler *SiteHandler,
tokenHandler *TokenHandler,
licenseHandler *LicenseHandler,
dashboardHandler *DashboardHandler,
) *gin.Engine {
r := gin.Default()
@@ -63,6 +64,8 @@ func SetupRouter(
api.POST("/captcha/reset-password", userHandler.SendResetPasswordCaptcha)
api.POST("/validate-token", tokenHandler.ValidateToken)
api.POST("/devices/register", deviceHandler.RegisterDevice)
api.POST("/devices/:uid/start", deviceHandler.UpdateStartCount)
api.GET("/devices/:uid/validate", deviceHandler.ValidateDevice)
// 需要认证的API
authorized := api.Group("")
@@ -76,7 +79,6 @@ func SetupRouter(
authorized.POST("/devices/models/batch", middleware.AdminRequired(), deviceHandler.BatchDeleteDeviceModels)
// 设备管理
authorized.POST("/devices/register", deviceHandler.RegisterDevice)
authorized.GET("/devices/registered", deviceHandler.GetRegisteredDevices)
authorized.POST("/devices/:uid/license", middleware.AdminRequired(), deviceHandler.BindLicense)
authorized.DELETE("/devices/:uid/license", middleware.AdminRequired(), deviceHandler.UnbindLicense)
@@ -113,7 +115,7 @@ func SetupRouter(
authorized.POST("/licenses/use", licenseHandler.UseLicense)
// 仪表盘统计
authorized.GET("/dashboard/stats", deviceHandler.GetDashboardStats)
authorized.GET("/dashboard/stats", dashboardHandler.GetDashboardStats)
}
}

View File

@@ -21,44 +21,45 @@ func NewUserHandler(userService *service.UserService) *UserHandler {
func (h *UserHandler) Login(c *gin.Context) {
var input struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
Captcha string `json:"captcha" binding:"required"`
CaptchaId string `json:"captchaId" binding:"required"`
}
var input service.LoginInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 验证验证码
if !h.userService.GetCaptchaService().VerifyImageCaptcha(input.CaptchaId, input.Captcha) {
if !h.userService.VerifyCaptcha(input.CaptchaId, input.Captcha) {
c.JSON(http.StatusBadRequest, gin.H{"error": "验证码错误"})
return
}
token, err := h.userService.Login(input.Username, input.Password)
// 验证用户名密码
user, err := h.userService.ValidateUser(input.Username, input.Password)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 设置 cookie
// c.SetCookie("token", token, 86400, "/", "", false, true) // 24小时过期httpOnly=true
// 生成 JWT token
token, err := h.userService.GenerateToken(user)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "生成token失败"})
return
}
c.JSON(http.StatusOK, gin.H{"token": token})
// 返回token和用户信息
c.JSON(http.StatusOK, gin.H{
"code": 0,
"data": gin.H{
"token": token,
"user": gin.H{
"id": user.ID,
"username": user.Username,
"email": user.Email,
"role": user.Role,
},
},
})
}
@@ -146,7 +147,7 @@ func (h *UserHandler) ResetPassword(c *gin.Context) {
}
c.JSON(http.StatusOK, gin.H{"message": "重置密邮件已发送"})
c.JSON(http.StatusOK, gin.H{"message": "重置密邮件已发送"})
}