package controller import ( "fmt" "net/http" "strconv" "strings" "ezops/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // CloudAccountHandler 云账号处理器 type CloudAccountHandler struct { db *gorm.DB } // NewCloudAccountHandler 创建云账号处理器 func NewCloudAccountHandler(db *gorm.DB) *CloudAccountHandler { return &CloudAccountHandler{db: db} } // ListCloudAccounts 获取云账号列表 // @Summary 获取云账号列表 // @Description 获取所有云账号列表 // @Tags 云账号 // @Produce json // @Security Bearer // @Success 200 {object} model.Response{data=model.ListResponse} // @Router /cloud-accounts [get] func (h *CloudAccountHandler) ListCloudAccounts(c *gin.Context) { var accounts []model.CloudAccount h.db.Find(&accounts) c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "success", Data: model.ListResponse{ List: accounts, Total: int64(len(accounts)), }, }) } // createCloudAccountRequest 创建云账号请求(独立 request struct,避免 CloudAccount.AccessKey/SecretKey 的 json:"-" 导致无法反序列化) type createCloudAccountRequest struct { Name string `json:"name"` Provider string `json:"provider"` AccessKey string `json:"access_key"` SecretKey string `json:"secret_key"` MonthlyCost float64 `json:"monthly_cost"` Status string `json:"status"` } // CreateCloudAccount 创建云账号 // @Summary 创建云账号 // @Description 创建新云账号 // @Tags 云账号 // @Accept json // @Produce json // @Security Bearer // @Param request body createCloudAccountRequest true "创建请求" // @Success 200 {object} model.Response{data=model.CloudAccount} // @Router /cloud-accounts [post] func (h *CloudAccountHandler) CreateCloudAccount(c *gin.Context) { var req createCloudAccountRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } if strings.TrimSpace(req.Name) == "" || strings.TrimSpace(req.Provider) == "" { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: "账号名称和云平台不能为空", }) return } if err := validateCloudAccountCredentials(req.Provider, req.AccessKey, req.SecretKey); err != nil { c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()}) return } status := strings.TrimSpace(req.Status) if status == "" { status = "active" } account := model.CloudAccount{ Name: strings.TrimSpace(req.Name), Provider: strings.TrimSpace(req.Provider), AccessKey: req.AccessKey, SecretKey: req.SecretKey, MonthlyCost: req.MonthlyCost, Status: status, } if err := h.db.Create(&account).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "创建失败", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "创建成功", Data: account, }) } // UpdateCloudAccount 更新云账号 // @Summary 更新云账号 // @Description 更新指定云账号信息 // @Tags 云账号 // @Accept json // @Produce json // @Security Bearer // @Param id path int true "云账号ID" // @Param request body createCloudAccountRequest true "更新请求" // @Success 200 {object} model.Response{data=model.CloudAccount} // @Router /cloud-accounts/{id} [put] func (h *CloudAccountHandler) UpdateCloudAccount(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var req createCloudAccountRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } var account model.CloudAccount if err := h.db.First(&account, id).Error; err != nil { c.JSON(http.StatusNotFound, model.Response{ Code: 404, Message: "云账号不存在", }) return } // 只在提供非空值时更新凭证字段 if strings.TrimSpace(req.Name) != "" { account.Name = strings.TrimSpace(req.Name) } if strings.TrimSpace(req.Provider) != "" { account.Provider = strings.TrimSpace(req.Provider) } if strings.TrimSpace(req.AccessKey) != "" { account.AccessKey = req.AccessKey } if strings.TrimSpace(req.SecretKey) != "" { account.SecretKey = req.SecretKey } if strings.TrimSpace(req.Status) != "" { account.Status = strings.TrimSpace(req.Status) } account.MonthlyCost = req.MonthlyCost if err := validateCloudAccountCredentials(account.Provider, account.AccessKey, account.SecretKey); err != nil { c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()}) return } if err := h.db.Save(&account).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "更新失败", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "更新成功", Data: account, }) } // validateCloudAccountCredentials 校验各云厂商调用 API 所需的凭据格式。 // Logan.Li func validateCloudAccountCredentials(provider, accessKey, secretKey string) error { if strings.TrimSpace(accessKey) == "" || strings.TrimSpace(secretKey) == "" { return fmt.Errorf("AccessKey 和 SecretKey 不能为空") } if provider == "azure" { parts := strings.Split(accessKey, ":") if len(parts) != 3 || strings.TrimSpace(parts[0]) == "" || strings.TrimSpace(parts[1]) == "" || strings.TrimSpace(parts[2]) == "" { return fmt.Errorf("Azure AccessKey 格式应为 租户ID:订阅ID:客户端ID") } } return nil } // DeleteCloudAccount 删除云账号 // @Summary 删除云账号 // @Description 删除指定云账号 // @Tags 云账号 // @Produce json // @Security Bearer // @Param id path int true "云账号ID" // @Success 200 {object} model.Response // @Router /cloud-accounts/{id} [delete] func (h *CloudAccountHandler) DeleteCloudAccount(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) if err := h.db.Delete(&model.CloudAccount{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, model.Response{ Code: 500, Message: "删除失败", }) return } c.JSON(http.StatusOK, model.Response{ Code: 200, Message: "删除成功", }) }