优化分页和筛选功能:实现服务端分页和筛选,修复总数不匹配问题

- 优化设备管理、版本管理、用户管理、在线实例、公告管理页面的分页功能
- 后端添加筛选参数支持(application_id, status, type, search等)
- 前端移除冗余的前端筛选逻辑,改为服务端筛选
- 修复分页总数与筛选数据不匹配的问题
- 优化云端变量记录页面的时间筛选和搜索功能
- 修复DateTimePicker组件关闭后不自动更新的问题
This commit is contained in:
2026-05-08 13:45:21 +08:00
parent 4c060c494d
commit d0ef52ab4f
12 changed files with 542 additions and 155 deletions
+36 -3
View File
@@ -1,4 +1,4 @@
package admin
package admin
import (
"fmt"
@@ -28,6 +28,10 @@ func handleGetAllAnnouncements(c *gin.Context) {
page := c.DefaultQuery("page", "1")
pageSize := c.DefaultQuery("page_size", "20")
applicationIDFilter := c.Query("application_id")
typeFilter := c.Query("type")
statusFilter := c.Query("status")
searchFilter := c.Query("search")
var total int64
@@ -59,7 +63,36 @@ func handleGetAllAnnouncements(c *gin.Context) {
log.Printf("[DEBUG] appNameMap: %v\n", appNameMap)
database.DB.Model(&model.Announcement{}).Where("application_id IN ?", appIDs).Count(&total)
countQuery := database.DB.Model(&model.Announcement{}).Where("application_id IN ?", appIDs)
if applicationIDFilter != "" {
countQuery = countQuery.Where("application_id = ?", applicationIDFilter)
}
if typeFilter != "" {
countQuery = countQuery.Where("type = ?", typeFilter)
}
if statusFilter != "" {
countQuery = countQuery.Where("status = ?", statusFilter)
}
if searchFilter != "" {
searchLower := strings.ToLower(searchFilter)
countQuery = countQuery.Where("LOWER(title) LIKE ? OR LOWER(content) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
}
countQuery.Count(&total)
query := database.DB.Where("application_id IN ?", appIDs)
if applicationIDFilter != "" {
query = query.Where("application_id = ?", applicationIDFilter)
}
if typeFilter != "" {
query = query.Where("type = ?", typeFilter)
}
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
if searchFilter != "" {
searchLower := strings.ToLower(searchFilter)
query = query.Where("LOWER(title) LIKE ? OR LOWER(content) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
}
var announcements []model.Announcement
offset := 0
@@ -72,7 +105,7 @@ func handleGetAllAnnouncements(c *gin.Context) {
limit = pageSizeInt
}
if err := database.DB.Where("application_id IN ?", appIDs).Order("is_top DESC, created_at DESC").Limit(limit).Offset(offset).Find(&announcements).Error; err != nil {
if err := query.Order("is_top DESC, created_at DESC").Limit(limit).Offset(offset).Find(&announcements).Error; err != nil {
response.Error(c, 500, "获取公告列表失败")
return
}
+127
View File
@@ -722,6 +722,7 @@ func handleGetCloudVariableRecords(c *gin.Context) {
userIDFilter := c.Query("user_id")
startDate := c.Query("start_date")
endDate := c.Query("end_date")
searchQuery := c.Query("search")
var total int64
query := database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID)
@@ -734,6 +735,102 @@ func handleGetCloudVariableRecords(c *gin.Context) {
if endDate != "" {
query = query.Where("created_at <= ?", endDate+" 23:59:59")
}
if searchQuery != "" {
var allRecords []model.CloudVariableRecord
if err := query.Order("created_at DESC").Find(&allRecords).Error; err != nil {
response.Error(c, 500, "获取记录失败")
return
}
var filteredRecords []model.CloudVariableRecord
searchLower := strings.ToLower(searchQuery)
for _, record := range allRecords {
if strings.Contains(strings.ToLower(fmt.Sprintf("%d", record.ID)), searchLower) {
filteredRecords = append(filteredRecords, record)
continue
}
if record.AppUserID != nil {
var appUser model.AppUser
if err := database.DB.Select("username").First(&appUser, *record.AppUserID).Error; err == nil {
if strings.Contains(strings.ToLower(appUser.Username), searchLower) {
filteredRecords = append(filteredRecords, record)
continue
}
}
}
if strings.Contains(strings.ToLower(record.CreatedAt), searchLower) {
filteredRecords = append(filteredRecords, record)
continue
}
if record.Data != "" {
if strings.Contains(strings.ToLower(record.Data), searchLower) {
filteredRecords = append(filteredRecords, record)
continue
}
var parsedData interface{}
if err := json.Unmarshal([]byte(record.Data), &parsedData); err == nil {
if searchData(parsedData, searchLower) {
filteredRecords = append(filteredRecords, record)
continue
}
}
}
}
total = int64(len(filteredRecords))
start := (page - 1) * pageSize
end := start + pageSize
if start > len(filteredRecords) {
filteredRecords = []model.CloudVariableRecord{}
} else {
if end > len(filteredRecords) {
end = len(filteredRecords)
}
filteredRecords = filteredRecords[start:end]
}
result := make([]gin.H, len(filteredRecords))
for i, r := range filteredRecords {
var parsedData interface{}
if r.Data != "" {
json.Unmarshal([]byte(r.Data), &parsedData)
}
if parsedData == nil && r.Data != "" {
parsedData = r.Data
}
record := gin.H{
"id": r.ID,
"data": parsedData,
"created_at": r.CreatedAt,
}
if r.AppUserID != nil {
record["user_id"] = r.AppUserID
var appUser model.AppUser
if err := database.DB.Select("id, username").First(&appUser, *r.AppUserID).Error; err == nil {
record["user"] = gin.H{
"id": appUser.ID,
"username": appUser.Username,
}
}
}
result[i] = record
}
response.Success(c, gin.H{
"records": result,
"total": total,
"page": page,
"page_size": pageSize,
"total_pages": (total + int64(pageSize) - 1) / int64(pageSize),
})
return
}
query.Count(&total)
var records []model.CloudVariableRecord
@@ -779,6 +876,36 @@ func handleGetCloudVariableRecords(c *gin.Context) {
})
}
func searchData(data interface{}, searchLower string) bool {
switch v := data.(type) {
case map[string]interface{}:
for _, value := range v {
if searchData(value, searchLower) {
return true
}
}
case []interface{}:
for _, item := range v {
if searchData(item, searchLower) {
return true
}
}
case string:
if strings.Contains(strings.ToLower(v), searchLower) {
return true
}
case float64, int, int64, float32:
if strings.Contains(strings.ToLower(fmt.Sprintf("%v", v)), searchLower) {
return true
}
case bool:
if strings.Contains(strings.ToLower(fmt.Sprintf("%v", v)), searchLower) {
return true
}
}
return false
}
func handleDeleteCloudVariableRecords(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
+45 -3
View File
@@ -42,6 +42,8 @@ func handleGetDevices(c *gin.Context) {
userIDFilter := c.Query("user_id")
deviceIDFilter := c.Query("device_id")
applicationIDFilter := c.Query("application_id")
statusFilter := c.Query("status")
var devices []model.UserDevice
var appHeartbeatTimeoutMap map[uint]int
@@ -95,6 +97,12 @@ func handleGetDevices(c *gin.Context) {
if deviceIDFilter != "" {
countQuery = countQuery.Where("device_id = ?", deviceIDFilter)
}
if applicationIDFilter != "" {
countQuery = countQuery.Where("application_id = ?", applicationIDFilter)
}
if statusFilter != "" {
countQuery = countQuery.Where("status = ?", statusFilter)
}
countQuery.Count(&total)
query := database.DB.Preload("User").Preload("Application").Where("application_id IN ?", appIDs)
@@ -107,6 +115,14 @@ func handleGetDevices(c *gin.Context) {
query = query.Where("device_id = ?", deviceIDFilter)
}
if applicationIDFilter != "" {
query = query.Where("application_id = ?", applicationIDFilter)
}
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
offset := (page - 1) * pageSize
@@ -339,6 +355,11 @@ func handleGetSessions(c *gin.Context) {
userID := c.GetUint("user_id")
deviceIDFilter := c.Query("device_id")
appIDFilter := c.Query("app_id")
usernameFilter := c.Query("username")
searchFilter := c.Query("search")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
var ownApps []model.Application
if err := database.DB.Where("user_id = ?", userID).Find(&ownApps).Error; err != nil {
@@ -377,7 +398,7 @@ func handleGetSessions(c *gin.Context) {
}
if len(appIDs) == 0 {
response.Success(c, gin.H{"sessions": []SessionWithDetails{}})
response.Success(c, gin.H{"sessions": []SessionWithDetails{}, "total": 0, "page": page, "page_size": pageSize})
return
}
@@ -391,8 +412,13 @@ func handleGetSessions(c *gin.Context) {
query = query.Where("application_id = ?", appIDFilter)
}
var total int64
query.Count(&total)
offset := (page - 1) * pageSize
var sessions []model.DeviceSession
if err := query.Find(&sessions).Error; err != nil {
if err := query.Offset(offset).Limit(pageSize).Find(&sessions).Error; err != nil {
response.Error(c, 500, "获取会话列表失败")
return
}
@@ -418,6 +444,22 @@ func handleGetSessions(c *gin.Context) {
timeoutThreshold := time.Now().Add(-time.Duration(heartbeatTimeout) * time.Second)
isOnline := session.LastHeartbeat != nil && session.LastHeartbeat.After(timeoutThreshold)
if usernameFilter != "" && user.Username != usernameFilter {
continue
}
if searchFilter != "" {
searchLower := strings.ToLower(searchFilter)
instanceIDMatch := strings.Contains(strings.ToLower(session.InstanceID), searchLower)
deviceIDMatch := strings.Contains(strings.ToLower(device.DeviceID), searchLower)
usernameMatch := strings.Contains(strings.ToLower(user.Username), searchLower)
deviceNameMatch := strings.Contains(strings.ToLower(device.DeviceName), searchLower)
if !instanceIDMatch && !deviceIDMatch && !usernameMatch && !deviceNameMatch {
continue
}
}
sessionsWithDetails = append(sessionsWithDetails, SessionWithDetails{
DeviceSession: session,
DeviceID: device.DeviceID,
@@ -428,7 +470,7 @@ func handleGetSessions(c *gin.Context) {
})
}
response.Success(c, gin.H{"sessions": sessionsWithDetails})
response.Success(c, gin.H{"sessions": sessionsWithDetails, "total": total, "page": page, "page_size": pageSize})
}
func handleDeleteSession(c *gin.Context) {
+41 -7
View File
@@ -3,6 +3,7 @@ package admin
import (
"fmt"
"log"
"strconv"
"time"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
@@ -57,10 +58,14 @@ func handleGetUsers(c *gin.Context) {
userID := c.GetUint("user_id")
log.Printf("[DEBUG] handleGetUsers called, userID: %d", userID)
var users []model.AppUser
var appHeartbeatTimeoutMap map[uint]int
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
applicationID := c.Query("application_id")
statusFilter := c.Query("status")
var users []model.AppUser
var appHeartbeatTimeoutMap map[uint]int
if applicationID != "" {
var appID uint
if _, err := fmt.Sscanf(applicationID, "%d", &appID); err != nil {
@@ -81,8 +86,13 @@ func handleGetUsers(c *gin.Context) {
}
appHeartbeatTimeoutMap[app.ID] = timeout
query := database.DB.Preload("Application").Where("application_id = ?", appID)
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
if app.UserID == userID {
if err := database.DB.Preload("Application").Where("application_id = ?", appID).Find(&users).Error; err != nil {
if err := query.Find(&users).Error; err != nil {
response.Error(c, 500, "获取用户列表失败")
return
}
@@ -102,7 +112,11 @@ func handleGetUsers(c *gin.Context) {
}
if len(cardUserIDs) > 0 {
if err := database.DB.Preload("Application").Where("id IN ?", cardUserIDs).Find(&users).Error; err != nil {
query = database.DB.Preload("Application").Where("id IN ?", cardUserIDs)
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
if err := query.Find(&users).Error; err != nil {
response.Error(c, 500, "获取用户列表失败")
return
}
@@ -137,7 +151,11 @@ func handleGetUsers(c *gin.Context) {
}
if len(ownAppIDs) > 0 {
if err := database.DB.Preload("Application").Where("application_id IN ?", ownAppIDs).Find(&users).Error; err != nil {
query := database.DB.Preload("Application").Where("application_id IN ?", ownAppIDs)
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
if err := query.Find(&users).Error; err != nil {
response.Error(c, 500, "获取用户列表失败")
return
}
@@ -179,8 +197,12 @@ func handleGetUsers(c *gin.Context) {
}
if len(allCardUserIDs) > 0 {
query := database.DB.Preload("Application").Where("id IN ?", allCardUserIDs)
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
var appUsers []model.AppUser
if err := database.DB.Preload("Application").Where("id IN ?", allCardUserIDs).Find(&appUsers).Error; err != nil {
if err := query.Find(&appUsers).Error; err != nil {
log.Printf("[DEBUG] Failed to get users: %v", err)
} else {
users = append(users, appUsers...)
@@ -246,9 +268,21 @@ func handleGetUsers(c *gin.Context) {
})
}
offset := (page - 1) * pageSize
end := offset + pageSize
if offset > len(usersWithStatus) {
offset = len(usersWithStatus)
}
if end > len(usersWithStatus) {
end = len(usersWithStatus)
}
paginatedUsers := usersWithStatus[offset:end]
responseData := gin.H{
"users": usersWithStatus,
"users": paginatedUsers,
"total": totalCount,
"page": page,
"page_size": pageSize,
"online_count": onlineCount,
"offline_count": offlineCount,
"banned_count": bannedCount,
+36 -3
View File
@@ -1,4 +1,4 @@
package admin
package admin
import (
"archive/zip"
@@ -40,6 +40,10 @@ func handleGetAllVersions(c *gin.Context) {
page := c.DefaultQuery("page", "1")
pageSize := c.DefaultQuery("page_size", "20")
applicationIDFilter := c.Query("application_id")
updateStrategyFilter := c.Query("update_strategy")
updateMethodFilter := c.Query("update_method")
searchFilter := c.Query("search")
var total int64
@@ -71,7 +75,36 @@ func handleGetAllVersions(c *gin.Context) {
log.Printf("[DEBUG] appNameMap: %v\n", appNameMap)
database.DB.Model(&model.Version{}).Where("application_id IN ?", appIDs).Count(&total)
countQuery := database.DB.Model(&model.Version{}).Where("application_id IN ?", appIDs)
if applicationIDFilter != "" {
countQuery = countQuery.Where("application_id = ?", applicationIDFilter)
}
if updateStrategyFilter != "" {
countQuery = countQuery.Where("update_strategy = ?", updateStrategyFilter)
}
if updateMethodFilter != "" {
countQuery = countQuery.Where("update_method = ?", updateMethodFilter)
}
if searchFilter != "" {
searchLower := strings.ToLower(searchFilter)
countQuery = countQuery.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
}
countQuery.Count(&total)
query := database.DB.Where("application_id IN ?", appIDs)
if applicationIDFilter != "" {
query = query.Where("application_id = ?", applicationIDFilter)
}
if updateStrategyFilter != "" {
query = query.Where("update_strategy = ?", updateStrategyFilter)
}
if updateMethodFilter != "" {
query = query.Where("update_method = ?", updateMethodFilter)
}
if searchFilter != "" {
searchLower := strings.ToLower(searchFilter)
query = query.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
}
var versions []model.Version
offset := 0
@@ -84,7 +117,7 @@ func handleGetAllVersions(c *gin.Context) {
limit = pageSizeInt
}
if err := database.DB.Where("application_id IN ?", appIDs).Order("created_at DESC").Limit(limit).Offset(offset).Find(&versions).Error; err != nil {
if err := query.Order("created_at DESC").Limit(limit).Offset(offset).Find(&versions).Error; err != nil {
response.Error(c, 500, "获取版本列表失败")
return
}