perf: 性能优化与错误修复

- 修复 N+1 查询: users/devices/agents 批量 GROUP BY 替代循环查询
- 添加分页: cards/finance/agents/devices API
- Redis 初始化根据安装配置 redis.enabled 决定是否连接
- 修复 DefaultVal 解析错误: 使用 sql.NullString 处理 NULL 值
- Dashboard 优化: 替换 ECharts 世界地图为 Chart.js 环形饼图
- 适配前端 cards 页面新 API 响应格式
- 添加数据库索引优化查询性能
- 实现可配置的数据清理定时任务
This commit is contained in:
2026-05-06 19:19:02 +08:00
parent 98986b0eb7
commit 086c5c6573
27 changed files with 843 additions and 467 deletions
+43 -10
View File
@@ -11,6 +11,7 @@ import (
"verification-platform-backend/pkg/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type UserWithStatus struct {
@@ -192,6 +193,28 @@ func handleGetUsers(c *gin.Context) {
offlineCount := 0
bannedCount := 0
userIDs := make([]uint, len(users))
for i, u := range users {
userIDs[i] = u.ID
}
deviceCountMap := make(map[uint]int)
if len(userIDs) > 0 {
type DeviceCountResult struct {
UserID uint
Count int
}
var deviceCounts []DeviceCountResult
database.DB.Model(&model.UserDevice{}).
Select("user_id, COUNT(*) as count").
Where("user_id IN ?", userIDs).
Group("user_id").
Find(&deviceCounts)
for _, dc := range deviceCounts {
deviceCountMap[dc.UserID] = dc.Count
}
}
usersWithStatus := make([]UserWithStatus, 0, len(users))
for _, user := range users {
log.Printf("[DEBUG] User ID=%d, Username=%s, LastLoginAt=%v, LastHeartbeatAt=%v", user.ID, user.Username, user.LastLoginAt, user.LastHeartbeatAt)
@@ -214,15 +237,12 @@ func handleGetUsers(c *gin.Context) {
bannedCount++
}
var deviceCount int64
database.DB.Model(&model.UserDevice{}).Where("user_id = ?", user.ID).Count(&deviceCount)
log.Printf("[DEBUG] 用户 %s (ID=%d) 余额: %f", user.Username, user.ID, user.Balance)
usersWithStatus = append(usersWithStatus, UserWithStatus{
AppUser: user,
OnlineStatus: onlineStatus,
DeviceCount: int(deviceCount),
DeviceCount: deviceCountMap[user.ID],
})
}
@@ -705,22 +725,35 @@ func handleUpdateExpiry(c *gin.Context) {
}
} else {
if req.Type == "recharge" {
appUser.Balance += req.Amount
if err := database.DB.Model(&model.AppUser{}).Where("id = ?", appUser.ID).
Update("balance", gorm.Expr("balance + ?", req.Amount)).Error; err != nil {
response.Error(c, 500, "充值失败")
return
}
} else if req.Type == "deduct" {
if appUser.Balance < req.Amount {
result := database.DB.Model(&model.AppUser{}).Where("id = ? AND balance >= ?", appUser.ID, req.Amount).
Update("balance", gorm.Expr("balance - ?", req.Amount))
if result.Error != nil {
response.Error(c, 500, "扣费失败")
return
}
if result.RowsAffected == 0 {
response.Error(c, 400, "余额不足")
return
}
appUser.Balance -= req.Amount
} else {
response.Error(c, 400, "操作类型错误")
return
}
}
if err := database.DB.Save(&appUser).Error; err != nil {
response.Error(c, 500, "更新失败")
return
if app.BillingType == "balance" {
database.DB.Where("id = ?", appUser.ID).First(&appUser)
} else {
if err := database.DB.Save(&appUser).Error; err != nil {
response.Error(c, 500, "更新失败")
return
}
}
response.Success(c, appUser)