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 UserWithStatus struct { model.AppUser OnlineStatus string `json:"online_status"` DeviceCount int `json:"device_count"` } func getUserOnlineStatus(user model.AppUser, heartbeatTimeout int) string { if user.Status == "banned" { return "banned" } if user.LastHeartbeatAt == nil { return "offline" } offlineThreshold := time.Duration(heartbeatTimeout) * time.Second if time.Since(*user.LastHeartbeatAt) > offlineThreshold { return "offline" } return "online" } func SetupUserRoutes(r *gin.RouterGroup) { appUsers := r.Group("/app-users") { appUsers.GET("", handleGetUsers) appUsers.POST("", handleCreateUser) appUsers.GET("/:id", handleGetUser) appUsers.PUT("/:id", handleUpdateUser) appUsers.DELETE("/:id", handleDeleteUser) appUsers.GET("/:id/devices", handleGetUserDevices) appUsers.DELETE("/:id/devices/:deviceId", handleUnbindDevice) appUsers.PUT("/:id/expiry", handleUpdateExpiry) appUsers.POST("/batch/status", handleBatchUpdateStatus) appUsers.DELETE("/batch", handleBatchDelete) } } 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 applicationID := c.Query("application_id") if applicationID != "" { var appID uint if _, err := fmt.Sscanf(applicationID, "%d", &appID); err != nil { response.Error(c, 400, "应用ID格式错误") return } var app model.Application if err := database.DB.First(&app, appID).Error; err != nil { response.Error(c, 404, "应用不存在") return } appHeartbeatTimeoutMap = make(map[uint]int) timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout if app.UserID == userID { if err := database.DB.Preload("Application").Where("application_id = ?", appID).Find(&users).Error; err != nil { response.Error(c, 500, "获取用户列表失败") return } } else { var agentApp model.AgentApplication if err := database.DB.Where("application_id = ? AND agent_id = ? AND is_received = ?", appID, userID, true).First(&agentApp).Error; err != nil { response.Error(c, 403, "无权限查看该应用的用户") return } var cardUserIDs []uint if err := database.DB.Model(&model.Card{}). Where("creator_id = ? AND application_id = ? AND app_user_id IS NOT NULL", userID, appID). Pluck("app_user_id", &cardUserIDs).Error; err != nil { response.Error(c, 500, "获取用户列表失败") return } if len(cardUserIDs) > 0 { if err := database.DB.Preload("Application").Where("id IN ?", cardUserIDs).Find(&users).Error; err != nil { response.Error(c, 500, "获取用户列表失败") return } } } } else { 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) response.Error(c, 500, "获取授权列表失败") return } log.Printf("[DEBUG] Found %d agent apps for user %d", len(agentApps), userID) appHeartbeatTimeoutMap = make(map[uint]int) for _, app := range ownApps { timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout var appUsers []model.AppUser if err := database.DB.Preload("Application").Where("application_id = ?", app.ID).Find(&appUsers).Error; err != nil { response.Error(c, 500, "获取用户列表失败") return } users = append(users, appUsers...) } for _, agentApp := range agentApps { var app model.Application if err := database.DB.First(&app, agentApp.ApplicationID).Error; err != nil { log.Printf("[DEBUG] Failed to get application %d: %v", agentApp.ApplicationID, err) continue } log.Printf("[DEBUG] Processing agent app: ApplicationID=%d, AppName=%s", app.ID, app.Name) timeout := app.HeartbeatTimeout if timeout == 0 { timeout = 300 } appHeartbeatTimeoutMap[app.ID] = timeout var cardUserIDs []uint if err := database.DB.Model(&model.Card{}). Where("creator_id = ? AND application_id = ? AND app_user_id IS NOT NULL", userID, app.ID). Pluck("app_user_id", &cardUserIDs).Error; err != nil { log.Printf("[DEBUG] Failed to get card user IDs for app %d: %v", app.ID, err) continue } log.Printf("[DEBUG] Found %d card user IDs for app %d: %v", len(cardUserIDs), app.ID, cardUserIDs) if len(cardUserIDs) > 0 { var appUsers []model.AppUser if err := database.DB.Preload("Application").Where("id IN ?", cardUserIDs).Find(&appUsers).Error; err != nil { log.Printf("[DEBUG] Failed to get users for app %d: %v", app.ID, err) continue } log.Printf("[DEBUG] Found %d users for app %d", len(appUsers), app.ID) users = append(users, appUsers...) } } } totalCount := len(users) onlineCount := 0 offlineCount := 0 bannedCount := 0 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) heartbeatTimeout := 300 if applicationID != "" { heartbeatTimeout = appHeartbeatTimeoutMap[user.ApplicationID] } else if appHeartbeatTimeoutMap != nil { heartbeatTimeout = appHeartbeatTimeoutMap[user.ApplicationID] } onlineStatus := getUserOnlineStatus(user, heartbeatTimeout) switch onlineStatus { case "online": onlineCount++ case "offline": offlineCount++ case "banned": 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), }) } responseData := gin.H{ "users": usersWithStatus, "total": totalCount, "online_count": onlineCount, "offline_count": offlineCount, "banned_count": bannedCount, } log.Printf("[DEBUG] Response data: %+v", responseData) response.Success(c, responseData) } func handleCreateUser(c *gin.Context) { userID := c.GetUint("user_id") var req struct { Username string `json:"username"` Email string `json:"email"` Password string `json:"password"` ApplicationID uint `json:"application_id"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } if req.Username == "" { response.Error(c, 400, "用户名不能为空") return } if req.Password == "" { response.Error(c, 400, "密码不能为空") return } if req.ApplicationID == 0 { response.Error(c, 400, "所属应用不能为空") return } var app model.Application if err := database.DB.First(&app, req.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 = ?", req.ApplicationID, userID, true).First(&agentApp).Error; err != nil { response.Error(c, 403, "无权限在该应用下创建用户") return } } var existingUser model.AppUser if err := database.DB.Where("username = ? AND application_id = ?", req.Username, app.ID).First(&existingUser).Error; err == nil { response.Error(c, 400, "用户已存在") return } user := model.AppUser{ Username: req.Username, Email: req.Email, Password: req.Password, Avatar: "", Status: "active", ApplicationID: app.ID, } if err := database.DB.Create(&user).Error; err != nil { response.Error(c, 500, "创建用户失败") return } service.LogOperation(c, "create", "app_user", &user.ID, fmt.Sprintf("创建用户: %s (应用: %s)", user.Username, app.Name), nil) response.Success(c, user) } func handleGetUser(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var user model.AppUser if err := database.DB.Preload("Application").First(&user, id).Error; err != nil { response.Error(c, 404, "用户不存在") return } var app model.Application if err := database.DB.First(&app, user.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 } var card model.Card if err := database.DB.Where("creator_id = ? AND app_user_id = ?", userID, user.ID).First(&card).Error; err != nil { response.Error(c, 403, "无权限查看该用户") return } } response.Success(c, gin.H{ "user": user, }) } func handleUpdateUser(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var req struct { Username string `json:"username"` Email string `json:"email"` Password string `json:"password"` Status string `json:"status"` ApplicationID uint `json:"application_id"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var user model.AppUser if err := database.DB.First(&user, id).Error; err != nil { response.Error(c, 404, "用户不存在") return } var app model.Application if err := database.DB.First(&app, user.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 } var card model.Card if err := database.DB.Where("creator_id = ? AND app_user_id = ?", userID, user.ID).First(&card).Error; err != nil { response.Error(c, 403, "无权限修改该用户") return } } if req.Username != "" { user.Username = req.Username } if req.Email != "" { user.Email = req.Email } if req.Password != "" { user.Password = req.Password } if req.Status != "" { user.Status = req.Status } if req.ApplicationID != 0 { var newApp model.Application if err := database.DB.First(&newApp, req.ApplicationID).Error; err != nil { response.Error(c, 404, "目标应用不存在") return } if newApp.UserID != userID { var agentApp model.AgentApplication if err := database.DB.Where("application_id = ? AND agent_id = ? AND is_received = ?", req.ApplicationID, userID, true).First(&agentApp).Error; err != nil { response.Error(c, 403, "无权限将用户转移到该应用") return } } user.ApplicationID = req.ApplicationID } if err := database.DB.Save(&user).Error; err != nil { response.Error(c, 500, "更新用户失败") return } service.LogOperation(c, "update", "app_user", &user.ID, fmt.Sprintf("更新用户: %s", user.Username), nil) response.Success(c, user) } func handleDeleteUser(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var user model.AppUser if err := database.DB.First(&user, id).Error; err != nil { response.Error(c, 404, "用户不存在") return } var app model.Application if err := database.DB.First(&app, user.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 } var card model.Card if err := database.DB.Where("creator_id = ? AND app_user_id = ?", userID, user.ID).First(&card).Error; err != nil { response.Error(c, 403, "无权限删除该用户") return } } if err := database.DB.Delete(&user).Error; err != nil { response.Error(c, 500, "删除用户失败") return } service.LogOperation(c, "delete", "app_user", &user.ID, fmt.Sprintf("删除用户: %s (应用: %s)", user.Username, app.Name), nil) response.Success(c, nil) } func handleGetUserDevices(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var user model.AppUser if err := database.DB.First(&user, id).Error; err != nil { response.Error(c, 404, "用户不存在") return } var app model.Application if err := database.DB.First(&app, user.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 } } var devices []model.UserDevice if err := database.DB.Where("user_id = ? AND application_id = ?", user.ID, app.ID).Find(&devices).Error; err != nil { response.Error(c, 500, "获取设备列表失败") return } response.Success(c, devices) } func handleUpdateExpiry(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var req struct { Amount float64 `json:"amount"` Type string `json:"type"` Field string `json:"field"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var appUser model.AppUser if err := database.DB.First(&appUser, id).Error; err != nil { response.Error(c, 404, "用户不存在") return } var app model.Application if err := database.DB.First(&app, appUser.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 } var card model.Card if err := database.DB.Where("creator_id = ? AND app_user_id = ?", userID, appUser.ID).First(&card).Error; err != nil { response.Error(c, 403, "无权限修改该用户") return } } if req.Amount == 0 { response.Error(c, 400, "数值不能为0") return } if appUser.Balance == -1 { response.Error(c, 400, "该用户为永久会员,无需操作") return } now := time.Now() if app.BillingType == "subscription" || req.Field == "days" { if req.Type == "recharge" { var baseTime time.Time if appUser.ExpiryAt != nil && appUser.ExpiryAt.After(now) { baseTime = *appUser.ExpiryAt } else { baseTime = now } duration := time.Duration(req.Amount) * 24 * time.Hour newExpiry := baseTime.Add(duration) appUser.ExpiryAt = &newExpiry } else if req.Type == "deduct" { if appUser.ExpiryAt == nil || appUser.ExpiryAt.Before(now) { response.Error(c, 400, "用户订阅已过期") return } duration := time.Duration(req.Amount) * 24 * time.Hour newExpiry := appUser.ExpiryAt.Add(-duration) if newExpiry.Before(now) { newExpiry = now } appUser.ExpiryAt = &newExpiry } else { response.Error(c, 400, "操作类型错误") return } } else { if req.Type == "recharge" { appUser.Balance += req.Amount } else if req.Type == "deduct" { if appUser.Balance < req.Amount { 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 } response.Success(c, appUser) } func handleBatchUpdateStatus(c *gin.Context) { userID := c.GetUint("user_id") var req struct { UserIDs []uint `json:"user_ids"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var users []model.AppUser if err := database.DB.Where("id IN ?", req.UserIDs).Find(&users).Error; err != nil { response.Error(c, 500, "获取用户列表失败") return } var validUserIDs []uint for _, user := range users { var app model.Application if err := database.DB.First(&app, user.ApplicationID).Error; err != nil { continue } if app.UserID == userID { validUserIDs = append(validUserIDs, user.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 { var card model.Card if err := database.DB.Where("creator_id = ? AND app_user_id = ?", userID, user.ID).First(&card).Error; err == nil { validUserIDs = append(validUserIDs, user.ID) } } } } if len(validUserIDs) > 0 { if err := database.DB.Model(&model.AppUser{}).Where("id IN ?", validUserIDs).Update("status", req.Status).Error; err != nil { response.Error(c, 500, "批量更新状态失败") return } } response.Success(c, nil) } func handleBatchDelete(c *gin.Context) { userID := c.GetUint("user_id") var req struct { UserIDs []uint `json:"user_ids"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } var users []model.AppUser if err := database.DB.Where("id IN ?", req.UserIDs).Find(&users).Error; err != nil { response.Error(c, 500, "获取用户列表失败") return } var validUserIDs []uint for _, user := range users { var app model.Application if err := database.DB.First(&app, user.ApplicationID).Error; err != nil { continue } if app.UserID == userID { validUserIDs = append(validUserIDs, user.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 { var card model.Card if err := database.DB.Where("creator_id = ? AND app_user_id = ?", userID, user.ID).First(&card).Error; err == nil { validUserIDs = append(validUserIDs, user.ID) } } } } if len(validUserIDs) > 0 { if err := database.DB.Where("id IN ?", validUserIDs).Delete(&model.AppUser{}).Error; err != nil { response.Error(c, 500, "批量删除失败") return } } response.Success(c, nil) }