package developer import ( "crypto/md5" "encoding/hex" "encoding/json" "fmt" "io" "os" "path/filepath" "strconv" "strings" "time" "verification-platform-backend/internal/database" "verification-platform-backend/internal/middleware" "verification-platform-backend/internal/model" "verification-platform-backend/internal/service" "verification-platform-backend/pkg/response" "github.com/gin-gonic/gin" ) func SetupCloudRoutes(r *gin.RouterGroup) { cloudConstants := r.Group("/cloud-constants") { cloudConstants.GET("", handleGetCloudConstants) cloudConstants.GET("/:id", handleGetCloudConstant) cloudConstants.GET("/:id/download", handleDownloadCloudConstant) cloudConstants.POST("", handleCreateCloudConstant) cloudConstants.POST("/upload", handleUploadCloudConstant) cloudConstants.PUT("/:id", handleUpdateCloudConstant) cloudConstants.DELETE("/:id", handleDeleteCloudConstant) } cloudVariables := r.Group("/cloud-variables") { cloudVariables.GET("", handleGetCloudVariables) cloudVariables.GET("/:id", handleGetCloudVariable) cloudVariables.GET("/:id/download", handleDownloadCloudVariable) cloudVariables.POST("", handleCreateCloudVariable) cloudVariables.POST("/upload", handleUploadCloudVariable) cloudVariables.PUT("/:id", handleUpdateCloudVariable) cloudVariables.DELETE("/:id", handleDeleteCloudVariable) cloudVariables.GET("/:id/records", handleGetCloudVariableRecords) cloudVariables.DELETE("/:id/records", handleDeleteCloudVariableRecords) } } func handleGetCloudConstants(c *gin.Context) { userID := c.GetUint("user_id") appID := c.Query("app_id") var constants []model.CloudConstant query := database.DB.Where("user_id = ?", userID) if appID != "" { query = query.Where("app_id = ?", appID) } if err := query.Find(&constants).Error; err != nil { response.Error(c, 500, "获取云端常量失败") return } response.Success(c, gin.H{ "constants": constants, "total": len(constants), }) } func handleGetCloudConstant(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var constant model.CloudConstant if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&constant).Error; err != nil { response.Error(c, 404, "云端常量不存在") return } response.Success(c, constant) } func handleCreateCloudConstant(c *gin.Context) { userID := c.GetUint("user_id") var req struct { AppID uint `json:"app_id"` Key string `json:"key"` Value string `json:"value"` VarType string `json:"var_type"` Description string `json:"description"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } if req.Key == "" { response.Error(c, 400, "变量名不能为空") return } if req.Status != "active" && req.Status != "inactive" { req.Status = "active" } if req.VarType == "" { req.VarType = "string" } constant := model.CloudConstant{ UserID: userID, AppID: &req.AppID, Key: req.Key, Value: req.Value, VarType: req.VarType, Description: req.Description, Status: req.Status, } if err := database.DB.Create(&constant).Error; err != nil { response.Error(c, 500, "创建云端常量失败") return } service.LogOperation(c, "create", "cloud_constant", &constant.ID, fmt.Sprintf("创建云端常量: %s", constant.Key), nil) response.Success(c, constant) } func handleUpdateCloudConstant(c *gin.Context) { userID := c.GetUint("user_id") var req struct { Key string `json:"key"` Value string `json:"value"` VarType string `json:"var_type"` Description string `json:"description"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } id := c.Param("id") var constant model.CloudConstant if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&constant).Error; err != nil { response.Error(c, 404, "云端常量不存在") return } constant.Key = req.Key constant.Value = req.Value constant.VarType = req.VarType constant.Description = req.Description if req.Status == "active" || req.Status == "inactive" { constant.Status = req.Status } if err := database.DB.Save(&constant).Error; err != nil { response.Error(c, 500, "更新云端常量失败") return } service.LogOperation(c, "update", "cloud_constant", &constant.ID, fmt.Sprintf("更新云端常量: %s", constant.Key), nil) response.Success(c, constant) } func handleDeleteCloudConstant(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var constant model.CloudConstant if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&constant).Error; err != nil { response.Error(c, 404, "云端常量不存在") return } if constant.VarType == "binary" && constant.FilePath != "" { if err := middleware.UpdateStorageUsed(userID, constant.FileSize, "delete"); err != nil { fmt.Printf("更新存储使用量失败: %v\n", err) } filePath := strings.TrimPrefix(constant.FilePath, "/") if _, err := os.Stat(filePath); err == nil { os.Remove(filePath) } } if err := database.DB.Where("id = ? AND user_id = ?", id, userID).Delete(&model.CloudConstant{}).Error; err != nil { response.Error(c, 500, "删除云端常量失败") return } service.LogOperation(c, "delete", "cloud_constant", &constant.ID, fmt.Sprintf("删除云端常量: %s", constant.Key), nil) response.Success(c, nil) } func handleUploadCloudConstant(c *gin.Context) { userID := c.GetUint("user_id") file, header, err := c.Request.FormFile("file") if err != nil { response.Error(c, 400, "请选择要上传的文件") return } defer file.Close() appIDStr := c.PostForm("app_id") key := c.PostForm("key") description := c.PostForm("description") status := c.PostForm("status") if appIDStr == "" { response.Error(c, 400, "请选择应用") return } if key == "" { response.Error(c, 400, "请输入变量名") return } var appID uint fmt.Sscanf(appIDStr, "%d", &appID) if status != "active" && status != "inactive" { status = "active" } var user model.User if err := database.DB.First(&user, userID).Error; err != nil { response.Error(c, 500, "获取用户信息失败") return } if user.CurrentPackageID != nil { var permission model.PackagePermission if err := database.DB.Where("package_id = ?", user.CurrentPackageID).First(&permission).Error; err == nil { maxStorageBytes := int64(permission.MaxStorage) * 1024 * 1024 if user.StorageUsed+header.Size > maxStorageBytes { usedMB := float64(user.StorageUsed) / 1024 / 1024 maxMB := float64(permission.MaxStorage) response.Error(c, 403, fmt.Sprintf("存储空间不足,已使用 %.2f MB / %.2f MB", usedMB, maxMB)) return } } } uploadDir := "uploads/cloud-files" if err := os.MkdirAll(uploadDir, 0755); err != nil { response.Error(c, 500, "创建上传目录失败") return } ext := filepath.Ext(header.Filename) filename := fmt.Sprintf("%d_%d%s", userID, time.Now().UnixNano(), ext) filePath := filepath.Join(uploadDir, filename) dst, err := os.Create(filePath) if err != nil { response.Error(c, 500, "创建文件失败") return } defer dst.Close() hash := md5.New() multiWriter := io.MultiWriter(dst, hash) if _, err := io.Copy(multiWriter, file); err != nil { response.Error(c, 500, "保存文件失败") return } fileMD5 := hex.EncodeToString(hash.Sum(nil)) fileURL := "/uploads/cloud-files/" + filename mimeType := header.Header.Get("Content-Type") if mimeType == "" { mimeType = "application/octet-stream" } constant := model.CloudConstant{ UserID: userID, AppID: &appID, Key: key, Value: fileURL, VarType: "binary", FilePath: fileURL, FileSize: header.Size, MimeType: mimeType, OriginalName: header.Filename, FileMD5: fileMD5, Description: description, Status: status, } if err := database.DB.Create(&constant).Error; err != nil { os.Remove(filePath) response.Error(c, 500, "创建云端常量失败") return } if err := middleware.UpdateStorageUsed(userID, header.Size, "upload"); err != nil { fmt.Printf("更新存储使用量失败: %v\n", err) } usage := model.StorageUsage{ UserID: userID, ApplicationID: &appID, ResourceType: "cloud_constant", ResourceID: constant.ID, FileName: header.Filename, FileSize: header.Size, Action: "upload", CreatedAt: time.Now(), } database.DB.Create(&usage) service.LogOperation(c, "create", "cloud_constant", &constant.ID, fmt.Sprintf("上传文件常量: %s", constant.Key), nil) response.Success(c, constant) } func handleDownloadCloudConstant(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var constant model.CloudConstant if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&constant).Error; err != nil { response.Error(c, 404, "云端常量不存在") return } if constant.VarType != "binary" || constant.FilePath == "" { response.Error(c, 400, "该常量不是文件类型") return } filePath := constant.FilePath if strings.HasPrefix(filePath, "/") { filePath = filePath[1:] } if _, err := os.Stat(filePath); os.IsNotExist(err) { response.Error(c, 404, "文件不存在") return } c.Header("Content-Description", "File Transfer") c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", constant.OriginalName)) c.Header("Content-Transfer-Encoding", "binary") c.Header("Expires", "0") c.Header("Cache-Control", "must-revalidate") c.Header("Pragma", "public") c.FileAttachment(filePath, constant.OriginalName) } func handleGetCloudVariables(c *gin.Context) { userID := c.GetUint("user_id") appID := c.Query("app_id") var variables []model.CloudVariable query := database.DB.Where("user_id = ?", userID) if appID != "" { query = query.Where("app_id = ?", appID) } if err := query.Find(&variables).Error; err != nil { response.Error(c, 500, "获取云端变量失败") return } response.Success(c, gin.H{ "variables": variables, "total": len(variables), }) } func handleGetCloudVariable(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var variable model.CloudVariable if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&variable).Error; err != nil { response.Error(c, 404, "云端变量不存在") return } response.Success(c, variable) } func handleCreateCloudVariable(c *gin.Context) { userID := c.GetUint("user_id") var req struct { AppID uint `json:"app_id"` Key string `json:"key"` DefaultValue string `json:"default_value"` VarType string `json:"var_type"` MaxRecords int `json:"max_records"` Scope string `json:"scope"` WritePermission string `json:"write_permission"` Description string `json:"description"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } if req.Key == "" { response.Error(c, 400, "变量名不能为空") return } if req.Scope != "app" && req.Scope != "user" { req.Scope = "app" } if req.WritePermission != "admin" && req.WritePermission != "user" && req.WritePermission != "app_user" { req.WritePermission = "admin" } if req.Status != "active" && req.Status != "inactive" { req.Status = "active" } if req.VarType == "" { req.VarType = "string" } variable := model.CloudVariable{ UserID: userID, AppID: &req.AppID, Key: req.Key, DefaultValue: req.DefaultValue, VarType: req.VarType, MaxRecords: req.MaxRecords, Scope: req.Scope, WritePermission: req.WritePermission, Description: req.Description, Status: req.Status, } if err := database.DB.Create(&variable).Error; err != nil { response.Error(c, 500, "创建云端变量失败") return } response.Success(c, variable) } func handleUpdateCloudVariable(c *gin.Context) { userID := c.GetUint("user_id") var req struct { Key string `json:"key"` DefaultValue string `json:"default_value"` VarType string `json:"var_type"` MaxRecords int `json:"max_records"` WritePermission string `json:"write_permission"` Description string `json:"description"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } id := c.Param("id") var variable model.CloudVariable if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&variable).Error; err != nil { response.Error(c, 404, "云端变量不存在") return } variable.Key = req.Key variable.DefaultValue = req.DefaultValue variable.VarType = req.VarType variable.MaxRecords = req.MaxRecords if req.WritePermission == "admin" || req.WritePermission == "user" || req.WritePermission == "app_user" { variable.WritePermission = req.WritePermission } variable.Description = req.Description if req.Status == "active" || req.Status == "inactive" { variable.Status = req.Status } if err := database.DB.Save(&variable).Error; err != nil { response.Error(c, 500, "更新云端变量失败") return } response.Success(c, variable) } func handleDeleteCloudVariable(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var variable model.CloudVariable if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&variable).Error; err != nil { response.Error(c, 404, "云端变量不存在") return } if variable.VarType == "binary" && variable.FilePath != "" { if err := middleware.UpdateStorageUsed(userID, variable.FileSize, "delete"); err != nil { fmt.Printf("更新存储使用量失败: %v\n", err) } filePath := strings.TrimPrefix(variable.FilePath, "/") if _, err := os.Stat(filePath); err == nil { os.Remove(filePath) } } if err := database.DB.Delete(&variable).Error; err != nil { response.Error(c, 500, "删除云端变量失败") return } response.Success(c, nil) } func handleUploadCloudVariable(c *gin.Context) { userID := c.GetUint("user_id") file, header, err := c.Request.FormFile("file") if err != nil { response.Error(c, 400, "请选择要上传的文件") return } defer file.Close() appIDStr := c.PostForm("app_id") key := c.PostForm("key") description := c.PostForm("description") status := c.PostForm("status") scope := c.PostForm("scope") writePermission := c.PostForm("write_permission") if appIDStr == "" { response.Error(c, 400, "请选择应用") return } if key == "" { response.Error(c, 400, "请输入变量名") return } var appID uint fmt.Sscanf(appIDStr, "%d", &appID) if status != "active" && status != "inactive" { status = "active" } if scope != "app" && scope != "user" { scope = "app" } if writePermission != "admin" && writePermission != "user" { writePermission = "admin" } var user model.User if err := database.DB.First(&user, userID).Error; err != nil { response.Error(c, 500, "获取用户信息失败") return } if user.CurrentPackageID != nil { var permission model.PackagePermission if err := database.DB.Where("package_id = ?", user.CurrentPackageID).First(&permission).Error; err == nil { maxStorageBytes := int64(permission.MaxStorage) * 1024 * 1024 if user.StorageUsed+header.Size > maxStorageBytes { usedMB := float64(user.StorageUsed) / 1024 / 1024 maxMB := float64(permission.MaxStorage) response.Error(c, 403, fmt.Sprintf("存储空间不足,已使用 %.2f MB / %.2f MB", usedMB, maxMB)) return } } } uploadDir := "uploads/cloud-files" if err := os.MkdirAll(uploadDir, 0755); err != nil { response.Error(c, 500, "创建上传目录失败") return } ext := filepath.Ext(header.Filename) filename := fmt.Sprintf("%d_%d%s", userID, time.Now().UnixNano(), ext) filePath := filepath.Join(uploadDir, filename) dst, err := os.Create(filePath) if err != nil { response.Error(c, 500, "创建文件失败") return } defer dst.Close() hash := md5.New() multiWriter := io.MultiWriter(dst, hash) if _, err := io.Copy(multiWriter, file); err != nil { response.Error(c, 500, "保存文件失败") return } fileMD5 := hex.EncodeToString(hash.Sum(nil)) fileURL := "/uploads/cloud-files/" + filename mimeType := header.Header.Get("Content-Type") if mimeType == "" { mimeType = "application/octet-stream" } variable := model.CloudVariable{ UserID: userID, AppID: &appID, Key: key, DefaultValue: fileURL, VarType: "binary", FilePath: fileURL, FileSize: header.Size, MimeType: mimeType, OriginalName: header.Filename, FileMD5: fileMD5, Scope: scope, WritePermission: writePermission, Description: description, Status: status, } if err := database.DB.Create(&variable).Error; err != nil { os.Remove(filePath) response.Error(c, 500, "创建云端变量失败") return } if err := middleware.UpdateStorageUsed(userID, header.Size, "upload"); err != nil { fmt.Printf("更新存储使用量失败: %v\n", err) } usage := model.StorageUsage{ UserID: userID, ApplicationID: &appID, ResourceType: "cloud_variable", ResourceID: variable.ID, FileName: header.Filename, FileSize: header.Size, Action: "upload", CreatedAt: time.Now(), } database.DB.Create(&usage) service.LogOperation(c, "create", "cloud_variable", &variable.ID, fmt.Sprintf("上传文件变量: %s", variable.Key), nil) response.Success(c, variable) } func handleDownloadCloudVariable(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var variable model.CloudVariable if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&variable).Error; err != nil { response.Error(c, 404, "云端变量不存在") return } if variable.VarType != "binary" || variable.FilePath == "" { response.Error(c, 400, "该变量不是文件类型") return } filePath := variable.FilePath if strings.HasPrefix(filePath, "/") { filePath = filePath[1:] } if _, err := os.Stat(filePath); os.IsNotExist(err) { response.Error(c, 404, "文件不存在") return } c.Header("Content-Description", "File Transfer") c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", variable.OriginalName)) c.Header("Content-Transfer-Encoding", "binary") c.Header("Expires", "0") c.Header("Cache-Control", "must-revalidate") c.Header("Pragma", "public") c.FileAttachment(filePath, variable.OriginalName) } func handleGetCloudVariableRecords(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var variable model.CloudVariable if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&variable).Error; err != nil { response.Error(c, 404, "云端变量不存在") return } if variable.VarType != "stream" { response.Error(c, 400, "该变量不是记录类型") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } userIDFilter := c.Query("user_id") startDate := c.Query("start_date") endDate := c.Query("end_date") var total int64 query := database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID) if userIDFilter != "" { query = query.Where("app_user_id = ?", userIDFilter) } if startDate != "" { query = query.Where("created_at >= ?", startDate+" 00:00:00") } if endDate != "" { query = query.Where("created_at <= ?", endDate+" 23:59:59") } query.Count(&total) var records []model.CloudVariableRecord offset := (page - 1) * pageSize if err := query.Order("created_at DESC").Limit(pageSize).Offset(offset).Find(&records).Error; err != nil { response.Error(c, 500, "获取记录失败") return } result := make([]gin.H, len(records)) for i, r := range records { var data map[string]interface{} json.Unmarshal([]byte(r.Data), &data) record := gin.H{ "id": r.ID, "data": data, "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), }) } func handleDeleteCloudVariableRecords(c *gin.Context) { userID := c.GetUint("user_id") id := c.Param("id") var variable model.CloudVariable if err := database.DB.Where("id = ? AND user_id = ?", id, userID).First(&variable).Error; err != nil { response.Error(c, 404, "云端变量不存在") return } if variable.VarType != "stream" { response.Error(c, 400, "该变量不是记录类型") return } var req struct { IDs []uint `json:"ids"` Before string `json:"before"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") return } if len(req.IDs) > 0 { if err := database.DB.Where("cloud_variable_id = ? AND id IN ?", variable.ID, req.IDs).Delete(&model.CloudVariableRecord{}).Error; err != nil { response.Error(c, 500, "删除记录失败") return } } else if req.Before != "" { if err := database.DB.Where("cloud_variable_id = ? AND created_at < ?", variable.ID, req.Before).Delete(&model.CloudVariableRecord{}).Error; err != nil { response.Error(c, 500, "删除记录失败") return } } else { response.Error(c, 400, "请指定要删除的记录") return } response.Success(c, nil) }