391 lines
10 KiB
Go
391 lines
10 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"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"
|
|
)
|
|
|
|
func SetupDeviceRoutes(r *gin.RouterGroup) {
|
|
r.GET("/devices", handleAppGetDevices)
|
|
r.POST("/unbind-device", handleAppUnbindDevice)
|
|
r.GET("/device-count", handleAppGetDeviceCount)
|
|
r.GET("/instances", handleAppGetInstances)
|
|
r.POST("/instances/:instance_id/offline", handleAppForceOfflineInstance)
|
|
}
|
|
|
|
func SetupDevicePublicRoutes(r *gin.RouterGroup) {
|
|
r.POST("/unbind-device-with-auth", handleAppUnbindDeviceWithAuth)
|
|
r.POST("/change-password", handleAppChangePassword)
|
|
}
|
|
|
|
func handleAppUnbindDeviceWithAuth(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
DeviceID string `json:"device_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
if req.Username == "" || req.Password == "" || req.DeviceID == "" {
|
|
response.Error(c, 400, "用户名、密码和设备ID不能为空")
|
|
return
|
|
}
|
|
|
|
var user model.AppUser
|
|
if err := database.DB.Where("username = ? AND application_id = ?", req.Username, app.ID).First(&user).Error; err != nil {
|
|
response.Error(c, 404, "用户不存在")
|
|
return
|
|
}
|
|
|
|
if user.Password != req.Password {
|
|
response.Error(c, 401, "密码错误")
|
|
return
|
|
}
|
|
|
|
var device model.UserDevice
|
|
if err := database.DB.Where("user_id = ? AND application_id = ? AND device_id = ?", user.ID, app.ID, req.DeviceID).First(&device).Error; err != nil {
|
|
response.Error(c, 404, "设备不存在")
|
|
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
|
|
}
|
|
|
|
if user.DeviceID == req.DeviceID {
|
|
now := time.Now()
|
|
database.DB.Model(&user).Updates(map[string]interface{}{
|
|
"device_id": "",
|
|
"last_heartbeat_at": &now,
|
|
})
|
|
}
|
|
|
|
service.LogVerification(c, &app.ID, &user.ID, "unbind_device", fmt.Sprintf("用户解绑设备(认证): %s", req.DeviceID), req.DeviceID, nil)
|
|
|
|
response.Success(c, gin.H{
|
|
"message": "解绑成功",
|
|
})
|
|
}
|
|
|
|
func handleAppGetDevices(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
return
|
|
}
|
|
|
|
var devices []model.UserDevice
|
|
if err := database.DB.Where("user_id = ? AND application_id = ?", userID, app.ID).Find(&devices).Error; err != nil {
|
|
response.Error(c, 500, "获取设备列表失败")
|
|
return
|
|
}
|
|
|
|
result := make([]gin.H, 0)
|
|
for _, device := range devices {
|
|
var onlineSessionCount int64
|
|
tenMinutesAgo := time.Now().Add(-10 * time.Minute)
|
|
database.DB.Model(&model.DeviceSession{}).
|
|
Where("device_id = ? AND last_heartbeat > ?", device.ID, tenMinutesAgo).
|
|
Count(&onlineSessionCount)
|
|
|
|
result = append(result, gin.H{
|
|
"id": device.ID,
|
|
"device_id": device.DeviceID,
|
|
"device_name": device.DeviceName,
|
|
"device_type": device.DeviceType,
|
|
"status": device.Status,
|
|
"online_sessions": onlineSessionCount,
|
|
"created_at": device.CreatedAt,
|
|
})
|
|
}
|
|
|
|
response.Success(c, result)
|
|
}
|
|
|
|
func handleAppGetDeviceCount(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
UserID uint `json:"user_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
var count int64
|
|
if err := database.DB.Model(&model.UserDevice{}).Where("user_id = ? AND application_id = ?", req.UserID, app.ID).Count(&count).Error; err != nil {
|
|
response.Error(c, 500, "获取设备数量失败")
|
|
return
|
|
}
|
|
|
|
response.Success(c, gin.H{
|
|
"count": count,
|
|
"max_devices": app.MaxDevices,
|
|
"remaining": app.MaxDevices - int(count),
|
|
})
|
|
}
|
|
|
|
func handleAppUnbindDevice(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
UserID uint `json:"user_id"`
|
|
DeviceID string `json:"device_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
var device model.UserDevice
|
|
if err := database.DB.Where("user_id = ? AND application_id = ? AND device_id = ?", req.UserID, app.ID, req.DeviceID).First(&device).Error; err != nil {
|
|
response.Error(c, 404, "设备不存在")
|
|
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
|
|
}
|
|
|
|
var user model.AppUser
|
|
if err := database.DB.First(&user, req.UserID).Error; err == nil {
|
|
if user.DeviceID == req.DeviceID {
|
|
now := time.Now()
|
|
database.DB.Model(&user).Updates(map[string]interface{}{
|
|
"device_id": "",
|
|
"last_heartbeat_at": &now,
|
|
})
|
|
}
|
|
}
|
|
|
|
service.LogVerification(c, &app.ID, &user.ID, "unbind_device", fmt.Sprintf("用户解绑设备: %s", req.DeviceID), req.DeviceID, nil)
|
|
|
|
response.Success(c, gin.H{
|
|
"message": "解绑成功",
|
|
})
|
|
}
|
|
|
|
func handleAppGetInstances(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
UserID uint `json:"user_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
var devices []model.UserDevice
|
|
if err := database.DB.Where("user_id = ? AND application_id = ?", req.UserID, app.ID).Find(&devices).Error; err != nil {
|
|
response.Error(c, 500, "获取设备列表失败")
|
|
return
|
|
}
|
|
|
|
deviceIDs := make([]uint, len(devices))
|
|
for i, d := range devices {
|
|
deviceIDs[i] = d.ID
|
|
}
|
|
|
|
heartbeatTimeout := app.HeartbeatTimeout
|
|
if heartbeatTimeout <= 0 {
|
|
heartbeatTimeout = 10
|
|
}
|
|
timeoutThreshold := time.Now().Add(-time.Duration(heartbeatTimeout) * time.Minute)
|
|
|
|
var sessions []model.DeviceSession
|
|
if len(deviceIDs) > 0 {
|
|
if err := database.DB.Where("device_id IN ?", deviceIDs).Order("last_heartbeat DESC").Find(&sessions).Error; err != nil {
|
|
response.Error(c, 500, "获取实例列表失败")
|
|
return
|
|
}
|
|
}
|
|
|
|
result := make([]gin.H, 0)
|
|
for _, session := range sessions {
|
|
isOnline := session.LastHeartbeat != nil && session.LastHeartbeat.After(timeoutThreshold)
|
|
|
|
var device model.UserDevice
|
|
for _, d := range devices {
|
|
if d.ID == session.DeviceID {
|
|
device = d
|
|
break
|
|
}
|
|
}
|
|
|
|
result = append(result, gin.H{
|
|
"id": session.ID,
|
|
"instance_id": session.InstanceID,
|
|
"device_id": device.DeviceID,
|
|
"device_name": device.DeviceName,
|
|
"is_online": isOnline,
|
|
"last_heartbeat": session.LastHeartbeat,
|
|
"created_at": session.CreatedAt,
|
|
})
|
|
}
|
|
|
|
response.Success(c, result)
|
|
}
|
|
|
|
func handleAppForceOfflineInstance(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
instanceID := c.Param("instance_id")
|
|
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
UserID uint `json:"user_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
var devices []model.UserDevice
|
|
if err := database.DB.Where("user_id = ? AND application_id = ?", req.UserID, app.ID).Find(&devices).Error; err != nil {
|
|
response.Error(c, 500, "获取设备列表失败")
|
|
return
|
|
}
|
|
|
|
deviceIDs := make([]uint, len(devices))
|
|
for i, d := range devices {
|
|
deviceIDs[i] = d.ID
|
|
}
|
|
|
|
if len(deviceIDs) == 0 {
|
|
response.Error(c, 404, "实例不存在")
|
|
return
|
|
}
|
|
|
|
result := database.DB.Where("instance_id = ? AND device_id IN ?", instanceID, deviceIDs).Delete(&model.DeviceSession{})
|
|
if result.RowsAffected == 0 {
|
|
response.Error(c, 404, "实例不存在")
|
|
return
|
|
}
|
|
|
|
service.LogVerification(c, &app.ID, &req.UserID, "force_offline", fmt.Sprintf("强制离线实例: %s", instanceID), instanceID, nil)
|
|
|
|
response.Success(c, gin.H{
|
|
"message": "已强制离线",
|
|
})
|
|
}
|
|
|
|
func handleAppChangePassword(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Username string `json:"username" binding:"required"`
|
|
OldPassword string `json:"old_password" binding:"required"`
|
|
NewPassword string `json:"new_password" binding:"required,min=6"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
var user model.AppUser
|
|
if err := database.DB.Where("username = ? AND application_id = ?", req.Username, app.ID).First(&user).Error; err != nil {
|
|
response.Error(c, 404, "用户不存在")
|
|
return
|
|
}
|
|
|
|
if user.Password != req.OldPassword {
|
|
response.Error(c, 400, "原密码错误")
|
|
return
|
|
}
|
|
|
|
user.Password = req.NewPassword
|
|
if err := database.DB.Save(&user).Error; err != nil {
|
|
response.Error(c, 500, "密码修改失败")
|
|
return
|
|
}
|
|
|
|
response.Success(c, gin.H{
|
|
"message": "密码修改成功",
|
|
})
|
|
}
|