diff --git a/backend/internal/router/admin/announcements.go b/backend/internal/router/admin/announcements.go index 508b7dd..b72b9bb 100644 --- a/backend/internal/router/admin/announcements.go +++ b/backend/internal/router/admin/announcements.go @@ -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 } diff --git a/backend/internal/router/admin/cloud.go b/backend/internal/router/admin/cloud.go index 501bf2b..e21d25a 100644 --- a/backend/internal/router/admin/cloud.go +++ b/backend/internal/router/admin/cloud.go @@ -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") diff --git a/backend/internal/router/admin/devices.go b/backend/internal/router/admin/devices.go index c4e04df..0d3fd3b 100644 --- a/backend/internal/router/admin/devices.go +++ b/backend/internal/router/admin/devices.go @@ -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) { diff --git a/backend/internal/router/admin/users.go b/backend/internal/router/admin/users.go index 9d5b7b1..cdb4770 100644 --- a/backend/internal/router/admin/users.go +++ b/backend/internal/router/admin/users.go @@ -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, diff --git a/backend/internal/router/admin/versions.go b/backend/internal/router/admin/versions.go index 213d164..c1bcf0c 100644 --- a/backend/internal/router/admin/versions.go +++ b/backend/internal/router/admin/versions.go @@ -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 } diff --git a/frontend/src/components/ui/date-picker/DateTimePicker.vue b/frontend/src/components/ui/date-picker/DateTimePicker.vue index c768308..7c17e23 100644 --- a/frontend/src/components/ui/date-picker/DateTimePicker.vue +++ b/frontend/src/components/ui/date-picker/DateTimePicker.vue @@ -57,6 +57,12 @@ function emitValue() { } } +watch(open, (isOpen) => { + if (!isOpen && selectedDate.value) { + emitValue() + } +}) + function resetTime() { selectedDate.value = undefined selectedHour.value = '00' diff --git a/frontend/src/pages/admin/announcements/index.vue b/frontend/src/pages/admin/announcements/index.vue index 2fa917e..e73e7a5 100644 --- a/frontend/src/pages/admin/announcements/index.vue +++ b/frontend/src/pages/admin/announcements/index.vue @@ -1,6 +1,6 @@