Signed-off-by: jagger <cossjie@foxmail.com>
This commit is contained in:
jagger 2025-05-26 18:33:12 +08:00
parent a357b6f631
commit 55e25b2241
4 changed files with 126 additions and 205 deletions

View File

@ -1394,7 +1394,7 @@ type ResourceCostRecord {
}
type ResourceSpecReq {
ClusterId string `form:"clusterId"`
ClusterId string `form:"clusterId,optional"`
Type string `form:"type,optional"`
Name string `form:"name,optional"`
Status string `form:"status,optional"`
@ -1449,10 +1449,19 @@ type BaseResourceSpec {
}
type EditResourceReq {
Id int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
Id int64 `json:"id,string" gorm:"column:id;primaryKey;autoIncrement"`
status string `json:"status" gorm:"column:status"`
CostPerUnit string `json:"costPerUnit" gorm:"column:cost_per_unit"`
CostType string `json:"costType" gorm:"column:cost_type"` //计费类型hourly, daily, monthly,perUse
Type string `json:"type,optional" gorm:"column:type"`
// 基础资源规格
StorageValue string `json:"storageValue,optional"`
StorageUnit string `json:"storageUnit,optional"`
CpuValue string `json:"cpuValue,optional"`
CpuUnit string `json:"cpuUnit,optional"`
MemoryValue string `json:"memoryValue,optional"`
MemoryUnit string `json:"memoryUnit,optional"`
}
type SyncResourceReq {

View File

@ -242,59 +242,10 @@ func (l *CompareResourceSpecLogic) updateResource(existing *models.TResourceSpec
return fmt.Errorf("failed to update resource: %w", err)
}
return l.syncBaseResources(tx, existing.Id, newSpec.BaseResourceSpecs)
return nil
})
}
func (l *CompareResourceSpecLogic) syncBaseResources(tx *gorm.DB, specID int64, newResources []models.TBaseResourceSpec) error {
// 处理基础资源更新
var existingResources []models.TBaseResourceSpec
if err := tx.Where("resource_spec_id = ?", specID).Find(&existingResources).Error; err != nil {
return fmt.Errorf("failed to query base resources: %w", err)
}
existingMap := make(map[string]models.TBaseResourceSpec)
for _, r := range existingResources {
key := resourceKey(r.Type, r.Name)
existingMap[key] = r
}
// 处理更新和新增
for i, newRes := range newResources {
newRes.ResourceSpecId = specID
key := resourceKey(newRes.Type, newRes.Name)
if existing, exists := existingMap[key]; exists {
newRes.Id = existing.Id
newRes.CreateTime = existing.CreateTime
if err := tx.Save(&newRes).Error; err != nil {
return fmt.Errorf("failed to update base resource: %w", err)
}
} else {
if err := tx.Create(&newRes).Error; err != nil {
return fmt.Errorf("failed to create base resource: %w", err)
}
}
newResources[i] = newRes
}
// 处理删除
currentIDs := make(map[int64]struct{})
for _, r := range newResources {
currentIDs[r.Id] = struct{}{}
}
for _, existing := range existingResources {
if _, exists := currentIDs[existing.Id]; !exists {
if err := tx.Delete(&existing).Error; err != nil {
return fmt.Errorf("failed to delete base resource: %w", err)
}
}
}
return nil
}
func (l *CompareResourceSpecLogic) markResourceDeleted(id int64) error {
return l.svcCtx.DbEngin.Model(&models.TResourceSpec{}).
Where("id = ?", id).

View File

@ -36,8 +36,9 @@ func (l *EditResourceSpecLogic) EditResourceSpec(req *types.EditResourceReq) (re
}
}()
// 1. 验证资源规格存在
var existing models.TResourceSpec
if err := tx.Model(&models.TResourceSpec{}).
if err = tx.Model(&models.TResourceSpec{}).
Where("id = ? AND deleted_at IS NULL", req.Id).
First(&existing).
Error; err != nil {
@ -47,10 +48,42 @@ func (l *EditResourceSpecLogic) EditResourceSpec(req *types.EditResourceReq) (re
return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %d)", req.Id)
}
if req.Status != "0" && req.Status != "1" {
return nil, errors.Errorf("资源规格状态不合法 (ID: %d)", req.Id)
// 2. 参数校验
if err = validateRequestParams(req); err != nil {
return nil, err
}
// 3. 转换参数
statusInt := utils.StringToInt64(req.Status)
costPerUnit := utils.StringToFloat64(req.CostPerUnit)
// 4. 更新主资源规格
if err = updateMainResourceSpec(tx, req.Id, statusInt, req.CostType, costPerUnit); err != nil {
return nil, err
}
// 5. 更新子资源规格
if err = updateSubResources(tx, req); err != nil {
return nil, err
}
// 提交事务
if err = tx.Commit().Error; err != nil {
return nil, errors.Wrap(err, "提交事务失败")
}
// 返回成功响应
return resp, nil
}
// validateRequestParams 验证请求参数合法性
func validateRequestParams(req *types.EditResourceReq) error {
// 状态校验
if req.Status != "0" && req.Status != "1" {
return errors.Errorf("资源规格状态不合法 (ID: %d)", req.Id)
}
// 计费类型校验
validCostTypes := map[string]struct{}{
"hourly": {},
"daily": {},
@ -58,152 +91,73 @@ func (l *EditResourceSpecLogic) EditResourceSpec(req *types.EditResourceReq) (re
"perUse": {},
}
if _, ok := validCostTypes[req.CostType]; !ok {
return nil, errors.Errorf("资源规格计费类型不合法 (ID: %d)", req.Id)
return errors.Errorf("资源规格计费类型不合法 (ID: %d)", req.Id)
}
statusInt := utils.StringToInt64(req.Status)
costPerUnit := utils.StringToFloat64(req.CostPerUnit)
// 4. 更新资源规格
updateData := map[string]interface{}{
"status": statusInt,
"cost_type": req.CostType,
"cost_per_unit": costPerUnit,
}
if err := tx.Model(&models.TResourceSpec{}).
Where("id = ?", req.Id).
Updates(updateData).
Error; err != nil {
return nil, errors.Wrapf(err, "更新资源规格失败 (ID: %d)", req.Id)
}
// 提交事务
if err := tx.Commit().Error; err != nil {
return nil, errors.Wrap(err, "提交事务失败")
}
return resp, nil
return nil
}
//
//func (l *EditResourceSpecLogic) EditResourceSpec(req *types.EditResourceReq) (resp *types.CommonResp, err error) {
// startTime := time.Now()
// resources, err := ConvertResourceSpec(req)
// l.Infof("转换主资源规格耗时:%v", time.Since(startTime))
// if err != nil {
// return nil, errors.Wrap(err, "资源规格转换失败")
// }
//
// tx := l.svcCtx.DbEngin.Begin()
// defer func() {
// if r := recover(); r != nil {
// tx.Rollback()
// panic(r)
// }
// }()
//
// // 检查主资源存在性
// var existing models.TResourceSpec
// if err := tx.Model(&models.TResourceSpec{}).
// Where("id = ? AND deleted_at IS NULL", resources.Id).
// First(&existing).
// Error; err != nil {
// tx.Rollback()
// return nil, errors.Wrapf(err, "资源规格不存在 (ID: %d)", resources.Id)
// }
//
// // 更新主资源
// if err := tx.Model(&models.TResourceSpec{}).
// Where("id = ?", resources.Id).
// Select("*").
// Updates(&resources).
// Error; err != nil {
// tx.Rollback()
// return nil, errors.Wrap(err, "更新主资源规格失败")
// }
//
// // 更新子资源
// for _, spec := range resources.BaseResourceSpecs {
// spec.ResourceSpecId = resources.Id // 确保关联关系正确
// result := tx.Model(&models.TBaseResourceSpec{}).
// Where("id = ? AND resource_spec_id = ?", spec.Id, resources.Id).
// Updates(&spec)
// if result.Error != nil {
// tx.Rollback()
// return nil, errors.Wrapf(result.Error, "更新子资源失败 (ID: %d)", spec.Id)
// }
// }
//
// if err := tx.Commit().Error; err != nil {
// return nil, errors.Wrap(err, "事务提交失败")
// }
//
// return resp, nil
//}
//
//// 类型转换相关函数保持不变,但建议添加更多错误处理
//func decodeHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
// // 增强类型转换错误处理
// switch {
// case f.Kind() == reflect.String && t.Kind() == reflect.Int64:
// v, err := strconv.ParseInt(data.(string), 10, 64)
// if err != nil {
// return nil, fmt.Errorf("类型转换失败: %v -> %v (%w)", f, t, err)
// }
// return v, nil
// case f.Kind() == reflect.String && t == reflect.TypeOf(time.Time{}):
// v, err := time.Parse(time.RFC3339, data.(string))
// if err != nil {
// return nil, fmt.Errorf("时间格式解析失败: %w", err)
// }
// return v, nil
// case f.Kind() == reflect.Int32 && t.Kind() == reflect.Int64:
// return int64(data.(int32)), nil
// }
// return data, nil
//}
//
//func decodeWithHook(input, output interface{}) error {
// decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
// DecodeHook: mapstructure.ComposeDecodeHookFunc(
// decodeHook,
// mapstructure.StringToTimeHookFunc(time.RFC3339),
// ),
// Result: output,
// TagName: "json",
// })
// if err != nil {
// return fmt.Errorf("创建解码器失败: %w", err)
// }
// if err := decoder.Decode(input); err != nil {
// return fmt.Errorf("数据解码失败: %w", err)
// }
// return nil
//}
//
//func convertBaseSpecs(specs []types.BaseResourceSpec) ([]models.TBaseResourceSpec, error) {
// tSpecs := make([]models.TBaseResourceSpec, 0, len(specs))
// for i, spec := range specs {
// var tSpec models.TBaseResourceSpec
// if err := decodeWithHook(spec, &tSpec); err != nil {
// return nil, fmt.Errorf("基础资源规格转换失败 (索引 %d): %w", i, err)
// }
// tSpecs = append(tSpecs, tSpec)
// }
// return tSpecs, nil
//}
//
//func ConvertResourceSpec(spec *types.ResourceSpec) (models.TResourceSpec, error) {
// var tSpec models.TResourceSpec
// if err := decodeWithHook(spec, &tSpec); err != nil {
// return models.TResourceSpec{}, fmt.Errorf("主资源规格转换失败: %w", err)
// }
//
// baseSpecs, err := convertBaseSpecs(spec.BaseResourceSpecs)
// if err != nil {
// return models.TResourceSpec{}, fmt.Errorf("基础资源规格转换失败: %w", err)
// }
// tSpec.BaseResourceSpecs = baseSpecs
//
// return tSpec, nil
//}
// updateMainResourceSpec 更新主资源规格
func updateMainResourceSpec(tx *gorm.DB, id int64, status int64, costType string, costPerUnit float64) error {
return tx.Model(&models.TResourceSpec{}).
Where("id = ?", id).
Updates(map[string]interface{}{
"status": status,
"cost_type": costType,
"cost_per_unit": costPerUnit,
}).
Error
}
// updateSubResources 更新子资源规格
func updateSubResources(tx *gorm.DB, req *types.EditResourceReq) error {
// 定义更新操作集合
updateOperations := []struct {
Value string
Unit string
SpecType string
SpecName string
}{
{req.CpuValue, req.CpuUnit, "CPU", ""},
{req.MemoryValue, req.MemoryUnit, "MEMORY", "RAM"},
{req.StorageValue, req.StorageUnit, "STORAGE", ""},
}
// 批量执行更新操作
for _, op := range updateOperations {
if op.Value == "" && op.Unit == "" {
continue
}
if err := updateBaseResourceSpec(tx, req.Id, op.SpecType, op.SpecName, op.Value, op.Unit); err != nil {
return errors.Wrapf(err, "更新%s规格失败 (ID: %d)", op.SpecType, req.Id)
}
}
return nil
}
// updateBaseResourceSpec 通用基础资源规格更新函数
func updateBaseResourceSpec(tx *gorm.DB, specID int64, specType string, specName string, value, unit string) error {
updates := make(map[string]interface{})
if value != "" {
updates["total_value"] = value
}
if unit != "" {
updates["total_unit"] = unit
}
if len(updates) == 0 {
return nil
}
query := tx.Model(&models.TBaseResourceSpec{}).
Where("resource_spec_id = ? AND type = ?", specID, specType)
if specName != "" {
query = query.Where("name = ?", specName)
}
if err := query.Updates(updates).Error; err != nil {
return errors.Wrapf(err, "更新%s规格失败", specType)
}
return nil
}

View File

@ -2126,10 +2126,17 @@ type Driver_info struct {
}
type EditResourceReq struct {
Id int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
Status string `json:"status" gorm:"column:status"`
CostPerUnit string `json:"costPerUnit" gorm:"column:cost_per_unit"`
CostType string `json:"costType" gorm:"column:cost_type"` //计费类型hourly, daily, monthly,perUse
Id int64 `json:"id,string" gorm:"column:id;primaryKey;autoIncrement"`
Status string `json:"status" gorm:"column:status"`
CostPerUnit string `json:"costPerUnit" gorm:"column:cost_per_unit"`
CostType string `json:"costType" gorm:"column:cost_type"` //计费类型hourly, daily, monthly,perUse
Type string `json:"type,optional" gorm:"column:type"`
StorageValue string `json:"storageValue,optional"`
StorageUnit string `json:"storageUnit,optional"`
CpuValue string `json:"cpuValue,optional"`
CpuUnit string `json:"cpuUnit,optional"`
MemoryValue string `json:"memoryValue,optional"`
MemoryUnit string `json:"memoryUnit,optional"`
}
type EndpointsReq struct {
@ -4598,7 +4605,7 @@ type ResourceSpec struct {
}
type ResourceSpecReq struct {
ClusterId string `form:"clusterId"`
ClusterId string `form:"clusterId,optional"`
Type string `form:"type,optional"`
Name string `form:"name,optional"`
Status string `form:"status,optional"`