package developer import ( "fmt" "log" "time" "verification-platform-backend/internal/database" "verification-platform-backend/internal/model" "verification-platform-backend/internal/service" "verification-platform-backend/pkg/response" "github.com/gin-gonic/gin" ) type DeviceWithDetails struct { model.UserDevice OnlineSessions int `json:"online_sessions"` } func SetupDeviceRoutes(r *gin.RouterGroup) { devices := r.Group("/devices") { devices.GET("", handleGetDevices) devices.PUT("/:id/status", handleUpdateDeviceStatus) devices.DELETE("/:id", handleUnbindDevice) devices.DELETE("/batch", handleBatchUnbindDevices) devices.POST("/batch/status", handleBatchUpdateDeviceStatus) devices.POST("/:id/force-offline", handleForceOfflineDevice) } sessions := r.Group("/sessions") { sessions.GET("", handleGetSessions) sessions.DELETE("/:id", handleDeleteSession) } } func handleGetDevices(c *gin.Context) { userID := c.GetUint("user_id") log.Printf("[DEBUG] handleGetDevices called, userID: %d", userID) userIDFilter := c.Query("user_id") deviceIDFilter := c.Query("device_id") var devices []model.UserDevice var appHeartbeatTimeoutMap map[uint]int var ownApps []model.Application if err := database.DB.Where("user_id = ?", userID).Find(&ownApps).Error; err != nil { response.Error(c, 500, "获取应用列表失败") return } var agentApps []model.AgentApplication if err := database.DB.Where("agent_id = ? AND is_received = ?", userID, true).Find(&agentApps).Error; err != nil { log.Printf("[DEBUG] Failed to get agent apps: %v", err) } appHeartbeatTimeoutMap = make(map[uint]int) var appIDs []uint for _, app := range ownApps { timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout appIDs = append(appIDs, app.ID) } for _, agentApp := range agentApps { var app model.Application if err := database.DB.First(&app, agentApp.ApplicationID).Error; err != nil { continue } timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout appIDs = append(appIDs, app.ID) } if len(appIDs) == 0 { response.Success(c, gin.H{"devices": []DeviceWithDetails{}}) return } query := database.DB.Preload("User").Preload("Application").Where("application_id IN ?", appIDs) if userIDFilter != "" { query = query.Where("user_id = ?", userIDFilter) } if deviceIDFilter != "" { query = query.Where("device_id = ?", deviceIDFilter) } if err := query.Find(&devices).Error; err != nil { response.Error(c, 500, "获取设备列表失败") return } devicesWithDetails := make([]DeviceWithDetails, 0, len(devices)) for _, device := range devices { heartbeatTimeout := appHeartbeatTimeoutMap[device.ApplicationID] timeoutThreshold := time.Now().Add(-time.Duration(heartbeatTimeout) * time.Second) var onlineSessionCount int64 database.DB.Model(&model.DeviceSession{}). Where("device_id = ? AND last_heartbeat > ?", device.ID, timeoutThreshold). Count(&onlineSessionCount) devicesWithDetails = append(devicesWithDetails, DeviceWithDetails{ UserDevice: device, OnlineSessions: int(onlineSessionCount), }) } response.Success(c, gin.H{"devices": devicesWithDetails}) } func handleUpdateDeviceStatus(c *gin.Context) { userID := c.GetUint("user_id") deviceID := c.Param("id") var req struct { Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var device model.UserDevice if err := database.DB.First(&device, deviceID).Error; err != nil { response.Error(c, 404, "设备不存在") return } var app model.Application if err := database.DB.First(&app, device.ApplicationID).Error; err != nil { response.Error(c, 404, "应用不存在") return } if app.UserID != userID { var agentApp model.AgentApplication if err := database.DB.Where("application_id = ? AND agent_id = ? AND is_received = ?", app.ID, userID, true).First(&agentApp).Error; err != nil { response.Error(c, 403, "无权限修改该设备") return } } device.Status = req.Status if err := database.DB.Save(&device).Error; err != nil { response.Error(c, 500, "更新设备状态失败") return } response.Success(c, device) } func handleUnbindDevice(c *gin.Context) { userID := c.GetUint("user_id") deviceID := c.Param("id") var device model.UserDevice if err := database.DB.First(&device, deviceID).Error; err != nil { response.Error(c, 404, "设备不存在") return } var app model.Application if err := database.DB.First(&app, device.ApplicationID).Error; err != nil { response.Error(c, 404, "应用不存在") return } if app.UserID != userID { var agentApp model.AgentApplication if err := database.DB.Where("application_id = ? AND agent_id = ? AND is_received = ?", app.ID, userID, true).First(&agentApp).Error; err != nil { response.Error(c, 403, "无权限解绑该设备") return } } database.DB.Where("device_id = ?", device.ID).Delete(&model.DeviceSession{}) if err := database.DB.Delete(&device).Error; err != nil { response.Error(c, 500, "解绑设备失败") return } service.LogOperation(c, "unbind", "device", &device.ID, fmt.Sprintf("解绑设备: %s (应用: %s)", device.DeviceID, app.Name), nil) response.Success(c, nil) } func handleBatchUnbindDevices(c *gin.Context) { userID := c.GetUint("user_id") var req struct { DeviceIDs []uint `json:"device_ids"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var devices []model.UserDevice if err := database.DB.Where("id IN ?", req.DeviceIDs).Find(&devices).Error; err != nil { response.Error(c, 500, "获取设备列表失败") return } var validDeviceIDs []uint for _, device := range devices { var app model.Application if err := database.DB.First(&app, device.ApplicationID).Error; err != nil { continue } if app.UserID == userID { validDeviceIDs = append(validDeviceIDs, device.ID) } else { var agentApp model.AgentApplication if err := database.DB.Where("application_id = ? AND agent_id = ? AND is_received = ?", app.ID, userID, true).First(&agentApp).Error; err == nil { validDeviceIDs = append(validDeviceIDs, device.ID) } } } if len(validDeviceIDs) > 0 { database.DB.Where("device_id IN ?", validDeviceIDs).Delete(&model.DeviceSession{}) if err := database.DB.Where("id IN ?", validDeviceIDs).Delete(&model.UserDevice{}).Error; err != nil { response.Error(c, 500, "批量解绑失败") return } } service.LogOperation(c, "batch_unbind", "device", nil, fmt.Sprintf("批量解绑设备: %d个", len(validDeviceIDs)), nil) response.Success(c, nil) } func handleBatchUpdateDeviceStatus(c *gin.Context) { userID := c.GetUint("user_id") var req struct { DeviceIDs []uint `json:"device_ids"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var devices []model.UserDevice if err := database.DB.Where("id IN ?", req.DeviceIDs).Find(&devices).Error; err != nil { response.Error(c, 500, "获取设备列表失败") return } var validDeviceIDs []uint for _, device := range devices { var app model.Application if err := database.DB.First(&app, device.ApplicationID).Error; err != nil { continue } if app.UserID == userID { validDeviceIDs = append(validDeviceIDs, device.ID) } else { var agentApp model.AgentApplication if err := database.DB.Where("application_id = ? AND agent_id = ? AND is_received = ?", app.ID, userID, true).First(&agentApp).Error; err == nil { validDeviceIDs = append(validDeviceIDs, device.ID) } } } if len(validDeviceIDs) > 0 { if err := database.DB.Model(&model.UserDevice{}).Where("id IN ?", validDeviceIDs).Update("status", req.Status).Error; err != nil { response.Error(c, 500, "批量更新状态失败") return } } response.Success(c, nil) } type SessionWithDetails struct { model.DeviceSession DeviceID string `json:"device_identifier"` DeviceName string `json:"device_name"` Username string `json:"username"` AppName string `json:"app_name"` IsOnline bool `json:"is_online"` } func handleGetSessions(c *gin.Context) { userID := c.GetUint("user_id") deviceIDFilter := c.Query("device_id") appIDFilter := c.Query("app_id") var ownApps []model.Application if err := database.DB.Where("user_id = ?", userID).Find(&ownApps).Error; err != nil { response.Error(c, 500, "获取应用列表失败") return } var agentApps []model.AgentApplication if err := database.DB.Where("agent_id = ? AND is_received = ?", userID, true).Find(&agentApps).Error; err != nil { log.Printf("[DEBUG] Failed to get agent apps: %v", err) } appHeartbeatTimeoutMap := make(map[uint]int) var appIDs []uint for _, app := range ownApps { timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout appIDs = append(appIDs, app.ID) } for _, agentApp := range agentApps { var app model.Application if err := database.DB.First(&app, agentApp.ApplicationID).Error; err != nil { continue } timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout appIDs = append(appIDs, app.ID) } if len(appIDs) == 0 { response.Success(c, gin.H{"sessions": []SessionWithDetails{}}) return } query := database.DB.Model(&model.DeviceSession{}).Where("application_id IN ?", appIDs) if deviceIDFilter != "" { query = query.Where("device_id = ?", deviceIDFilter) } if appIDFilter != "" { query = query.Where("application_id = ?", appIDFilter) } var sessions []model.DeviceSession if err := query.Find(&sessions).Error; err != nil { response.Error(c, 500, "获取会话列表失败") return } sessionsWithDetails := make([]SessionWithDetails, 0, len(sessions)) for _, session := range sessions { var device model.UserDevice if err := database.DB.First(&device, session.DeviceID).Error; err != nil { continue } var user model.AppUser if err := database.DB.First(&user, session.UserID).Error; err != nil { continue } var app model.Application if err := database.DB.First(&app, session.ApplicationID).Error; err != nil { continue } heartbeatTimeout := appHeartbeatTimeoutMap[session.ApplicationID] timeoutThreshold := time.Now().Add(-time.Duration(heartbeatTimeout) * time.Second) isOnline := session.LastHeartbeat != nil && session.LastHeartbeat.After(timeoutThreshold) sessionsWithDetails = append(sessionsWithDetails, SessionWithDetails{ DeviceSession: session, DeviceID: device.DeviceID, DeviceName: device.DeviceName, Username: user.Username, AppName: app.Name, IsOnline: isOnline, }) } response.Success(c, gin.H{"sessions": sessionsWithDetails}) } func handleDeleteSession(c *gin.Context) { userID := c.GetUint("user_id") sessionID := c.Param("id") if sessionID == "" { response.Error(c, 400, "会话ID不能为空") return } var session model.DeviceSession if err := database.DB.First(&session, sessionID).Error; err != nil { response.Error(c, 404, "会话不存在") return } var ownApps []model.Application database.DB.Where("user_id = ?", userID).Find(&ownApps) ownAppIDs := make([]uint, len(ownApps)) for i, app := range ownApps { ownAppIDs[i] = app.ID } var agentApps []model.AgentApplication database.DB.Where("agent_id = ? AND is_received = ?", userID, true).Find(&agentApps) agentAppIDs := make([]uint, 0) for _, agentApp := range agentApps { agentAppIDs = append(agentAppIDs, agentApp.ApplicationID) } validAppIDs := append(ownAppIDs, agentAppIDs...) isValid := false for _, appID := range validAppIDs { if session.ApplicationID == appID { isValid = true break } } if !isValid { response.Error(c, 403, "无权操作此会话") return } if err := database.DB.Delete(&session).Error; err != nil { response.Error(c, 500, "删除会话失败") return } response.Success(c, nil) } func handleForceOfflineDevice(c *gin.Context) { userID := c.GetUint("user_id") deviceID := c.Param("id") if deviceID == "" { response.Error(c, 400, "设备ID不能为空") return } var device model.UserDevice if err := database.DB.First(&device, deviceID).Error; err != nil { response.Error(c, 404, "设备不存在") return } var ownApps []model.Application database.DB.Where("user_id = ?", userID).Find(&ownApps) ownAppIDs := make([]uint, len(ownApps)) for i, app := range ownApps { ownAppIDs[i] = app.ID } var agentApps []model.AgentApplication database.DB.Where("agent_id = ? AND is_received = ?", userID, true).Find(&agentApps) agentAppIDs := make([]uint, 0) for _, agentApp := range agentApps { agentAppIDs = append(agentAppIDs, agentApp.ApplicationID) } validAppIDs := append(ownAppIDs, agentAppIDs...) isValid := false for _, appID := range validAppIDs { if device.ApplicationID == appID { isValid = true break } } if !isValid { response.Error(c, 403, "无权操作此设备") return } var sessions []model.DeviceSession if err := database.DB.Where("device_id = ?", device.ID).Find(&sessions).Error; err != nil { response.Error(c, 500, "获取会话列表失败") return } if len(sessions) == 0 { response.Success(c, gin.H{"count": 0}) return } if err := database.DB.Where("device_id = ?", device.ID).Delete(&model.DeviceSession{}).Error; err != nil { response.Error(c, 500, "强制离线失败") return } response.Success(c, gin.H{"count": len(sessions)}) }