update resourceSpec

Signed-off-by: jagger <cossjie@foxmail.com>
This commit is contained in:
jagger 2025-05-22 10:39:20 +08:00
parent 82aabde7cf
commit c3b12e876e
7 changed files with 207 additions and 135 deletions

View File

@ -1446,4 +1446,11 @@ type BaseResourceSpec {
UserId string `json:"userId" gorm:"column:user_id"`
CreateTime string `json:"createTime" gorm:"column:create_time"`
UpdateTime string `json:"updateTime" gorm:"column:update_time"`
}
type EditResourceReq {
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
}

View File

@ -193,7 +193,7 @@ service pcm {
@doc "编辑资源规格"
@handler editResourceSpecHandler
put /core/ai/resourceSpec/edit (ResourceSpec) returns (CommonResp)
put /core/ai/resourceSpec/edit (EditResourceReq) returns (CommonResp)
@doc "删除资源规格"
@handler deleteResourceSpecHandler

View File

@ -11,7 +11,7 @@ import (
func EditResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ResourceSpec
var req types.EditResourceReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return

View File

@ -2,16 +2,13 @@ package core
import (
"context"
"fmt"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc"
"gitlink.org.cn/JointCloud/pcm-coordinator/internal/types"
"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models"
"reflect"
"strconv"
"time"
"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils"
"gorm.io/gorm"
)
type EditResourceSpecLogic struct {
@ -27,125 +24,186 @@ func NewEditResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
svcCtx: svcCtx,
}
}
func (l *EditResourceSpecLogic) EditResourceSpec(req *types.ResourceSpec) (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, "资源规格转换失败")
}
func (l *EditResourceSpecLogic) EditResourceSpec(req *types.EditResourceReq) (resp *types.CommonResp, err error) {
// 初始化事务
tx := l.svcCtx.DbEngin.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
panic(r)
} else if err != nil {
tx.Rollback()
}
}()
// 检查主资源存在性
var existing models.TResourceSpec
if err := tx.Model(&models.TResourceSpec{}).
Where("id = ? AND deleted_at IS NULL", resources.Id).
Where("id = ? AND deleted_at IS NULL", req.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 errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.Errorf("资源规格不存在 (ID: %d)", req.Id)
}
return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %d)", req.Id)
}
if req.Status != "0" && req.Status != "1" {
return nil, errors.Errorf("资源规格状态不合法 (ID: %d)", req.Id)
}
validCostTypes := map[string]struct{}{
"hourly": {},
"daily": {},
"monthly": {},
"perUse": {},
}
if _, ok := validCostTypes[req.CostType]; !ok {
return nil, 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 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
}
//
//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
//}

View File

@ -2125,6 +2125,13 @@ type Driver_info struct {
Ipmi_username string `json:"ipmi_username" copier:"ipmi_username"`
}
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
}
type EndpointsReq struct {
AllowedAccessIps []string `json:"allowedAccessIps" copier:"AllowedAccessIps"`
DevService string `json:"devService" copier:"DevService"`

View File

@ -38,18 +38,18 @@ type (
}
TBaseResourceSpec struct {
Id int64 `db:"id" json:"id,omitempty"` // 主键id
ResourceSpecId int64 `db:"resource_spec_id" json:"resourceSpecId,omitempty"` // 关联资源规格ID
Type string `db:"type" json:"type,omitempty"` // 类型名称
Name string `db:"name" json:"name,omitempty"` // 名称(如显存类型)
TotalValue float64 `db:"total_value" json:"totalValue,omitempty"` // 总量值
TotalUnit string `db:"total_unit" json:"totalUnit,omitempty"` // 总量值单位GB/core等
AvailableValue float64 `db:"available_value" json:"availableValue,omitempty"` // 可用值
AvailableUnit string `db:"available_unit" json:"availableUnit,omitempty"` // 可用值单位GB/core等
UserId int64 `db:"user_id" json:"userId,omitempty"` // 用户ID
CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间
UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间
DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间
Id int64 `db:"id" json:"id"` // 主键id
ResourceSpecId int64 `db:"resource_spec_id" json:"resourceSpecId"` // 关联资源规格ID
Type string `db:"type" json:"type"` // 类型名称
Name string `db:"name" json:"name"` // 名称(如显存类型)
TotalValue float64 `db:"total_value" json:"totalValue"` // 总量值
TotalUnit string `db:"total_unit" json:"totalUnit"` // 总量值单位GB/core等
AvailableValue float64 `db:"available_value" json:"availableValue"` // 可用值
AvailableUnit string `db:"available_unit" json:"availableUnit"` // 可用值单位GB/core等
UserId int64 `db:"user_id" json:"userId"` // 用户ID
CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间
UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间
DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间
}
)

View File

@ -38,21 +38,21 @@ type (
}
TResourceSpec struct {
Id int64 `db:"id" json:"id,omitempty"` // 主键id
Type string `db:"type" json:"type,omitempty"` // 类型名称
Name string `db:"name" json:"name,omitempty"` // 规格名称
TotalCount int64 `db:"total_count" json:"totalCount,omitempty"` // 资源总量
AvailableCount int64 `db:"available_count" json:"availableCount,omitempty"` // 可用数量
ChangeType int64 `db:"change_type" json:"changeType,omitempty"` // 变更类型0: 正常1变更2删除
Status int64 `db:"status" json:"status,omitempty"` // 状态0未上架1已上架
Region string `db:"region" json:"region,omitempty"` // 所属区域(可扩展多区域)
ClusterId int64 `db:"cluster_id" json:"clusterId,string,omitempty"` // 集群ID
CostPerUnit float64 `db:"cost_per_unit" json:"costPerUnit,omitempty"` // 单位时间积分消耗
CostType string `db:"cost_type" json:"costType,omitempty"` // 计费类型hourly, daily, monthly,perUse
UserId int64 `db:"user_id" json:"userId,omitempty"` // 用户ID
CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间
UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间
DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间
Id int64 `db:"id" json:"id"` // 主键id
Type string `db:"type" json:"type"` // 类型名称
Name string `db:"name" json:"name"` // 规格名称
TotalCount int64 `db:"total_count" json:"totalCount"` // 资源总量
AvailableCount int64 `db:"available_count" json:"availableCount"` // 可用数量
ChangeType int64 `db:"change_type" json:"changeType"` // 变更类型0: 正常1变更2删除
Status int64 `db:"status" json:"status"` // 状态0未上架1已上架
Region string `db:"region" json:"region"` // 所属区域(可扩展多区域)
ClusterId int64 `db:"cluster_id" json:"clusterId,string"` // 集群ID
CostPerUnit float64 `db:"cost_per_unit" json:"costPerUnit"` // 单位时间积分消耗
CostType string `db:"cost_type" json:"costType"` // 计费类型hourly, daily, monthly,perUse
UserId int64 `db:"user_id" json:"userId"` // 用户ID
CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间
UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间
DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间
BaseResourceSpecs []TBaseResourceSpec `gorm:"foreignKey:ResourceSpecId" json:"baseResourceSpecs,omitempty"`
}