| @@ -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"` | |||
| @@ -1446,4 +1446,24 @@ 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,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 { | |||
| Id string `json:"id"` | |||
| } | |||
| @@ -179,9 +179,14 @@ service pcm { | |||
| @handler getClusterByIdHandler | |||
| get /core/getClusterById (getClusterByIdReq) returns (getClusterByIdResp) | |||
| @doc "获取指定集群资源规格列表" | |||
| @handler getClusterResourceSpecHandler | |||
| get /core/ai/resourceSpec/list (ResourceSpecReq) returns (PageResult) | |||
| //集群资源规格----- 开始 | |||
| @doc "与Api接口对比集群资源规格" | |||
| @handler compareResourceSpecHandler | |||
| get /core/ai/resourceSpec/compare (ResourceSpecReq) returns (PageResult) | |||
| @doc "同步指定资源规格" | |||
| @handler syncResourceSpecHandler | |||
| put /core/ai/resourceSpec/sync (SyncResourceReq) returns (CommonResp) | |||
| @doc "获取指定资源规格详情" | |||
| @handler detailResourceSpecHandler | |||
| @@ -193,11 +198,12 @@ service pcm { | |||
| @doc "编辑资源规格" | |||
| @handler editResourceSpecHandler | |||
| put /core/ai/resourceSpec/edit (ResourceSpec) returns (CommonResp) | |||
| put /core/ai/resourceSpec/edit (EditResourceReq) returns (CommonResp) | |||
| @doc "删除资源规格" | |||
| @handler deleteResourceSpecHandler | |||
| delete /core/ai/resourceSpec/delete/:id (DeletePathId) returns (CommonResp) | |||
| //集群资源规格----- 结束 | |||
| } | |||
| //hpc二级接口 | |||
| @@ -9,7 +9,7 @@ import ( | |||
| "net/http" | |||
| ) | |||
| func GetClusterResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | |||
| func CompareResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | |||
| return func(w http.ResponseWriter, r *http.Request) { | |||
| var req types.ResourceSpecReq | |||
| if err := httpx.Parse(r, &req); err != nil { | |||
| @@ -17,8 +17,8 @@ func GetClusterResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc | |||
| return | |||
| } | |||
| l := core.NewGetClusterResourceSpecLogic(r.Context(), svcCtx) | |||
| resp, err := l.GetClusterResourceSpec(&req) | |||
| l := core.NewCompareResourceSpecLogic(r.Context(), svcCtx) | |||
| resp, err := l.CompareResourceSpec(&req) | |||
| result.HttpResult(r, w, resp, err) | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,24 @@ | |||
| package core | |||
| import ( | |||
| "github.com/zeromicro/go-zero/rest/httpx" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/logic/core" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/repository/result" | |||
| "net/http" | |||
| ) | |||
| func SyncResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | |||
| return func(w http.ResponseWriter, r *http.Request) { | |||
| var req types.SyncResourceReq | |||
| if err := httpx.Parse(r, &req); err != nil { | |||
| result.ParamErrorResult(r, w, err) | |||
| return | |||
| } | |||
| l := core.NewSyncResourceSpecLogic(r.Context(), svcCtx) | |||
| resp, err := l.SyncResourceSpec(&req) | |||
| result.HttpResult(r, w, resp, err) | |||
| } | |||
| } | |||
| @@ -370,6 +370,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { | |||
| Path: "/core/ai/resourceSpec/cancelAlarm", | |||
| Handler: core.CancelResourceSpecAlarmHandler(serverCtx), | |||
| }, | |||
| { | |||
| // 与Api接口对比集群资源规格 | |||
| Method: http.MethodGet, | |||
| Path: "/core/ai/resourceSpec/compare", | |||
| Handler: core.CompareResourceSpecHandler(serverCtx), | |||
| }, | |||
| { | |||
| // 删除资源规格 | |||
| Method: http.MethodDelete, | |||
| @@ -389,10 +395,10 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { | |||
| Handler: core.EditResourceSpecHandler(serverCtx), | |||
| }, | |||
| { | |||
| // 获取指定集群资源规格列表 | |||
| Method: http.MethodGet, | |||
| Path: "/core/ai/resourceSpec/list", | |||
| Handler: core.GetClusterResourceSpecHandler(serverCtx), | |||
| // 同步指定资源规格 | |||
| Method: http.MethodPut, | |||
| Path: "/core/ai/resourceSpec/sync", | |||
| Handler: core.SyncResourceSpecHandler(serverCtx), | |||
| }, | |||
| { | |||
| // 获取节点资产 | |||
| @@ -2,31 +2,31 @@ package core | |||
| import ( | |||
| "context" | |||
| "errors" | |||
| "fmt" | |||
| "github.com/mitchellh/mapstructure" | |||
| "github.com/rs/zerolog/log" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/logic/schedule" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils" | |||
| "gorm.io/gorm" | |||
| "time" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||
| "github.com/zeromicro/go-zero/core/logx" | |||
| "gorm.io/gorm" | |||
| ) | |||
| type GetClusterResourceSpecLogic struct { | |||
| type CompareResourceSpecLogic struct { | |||
| logx.Logger | |||
| ctx context.Context | |||
| svcCtx *svc.ServiceContext | |||
| } | |||
| const ( | |||
| ChangeTypeCreated = 0 | |||
| ChangeTypeModified = 1 | |||
| ChangeTypeDeleted = 2 | |||
| ChangeTypeNormal = 0 // 资源规格正常 | |||
| ChangeTypeModified = 1 // 资源规格变更 | |||
| ChangeTypeDeleted = 2 // 资源被删除 | |||
| ) | |||
| type APIResponse struct { | |||
| @@ -54,22 +54,23 @@ type Metric struct { | |||
| Value float64 `json:"value"` | |||
| } | |||
| func NewGetClusterResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetClusterResourceSpecLogic { | |||
| return &GetClusterResourceSpecLogic{ | |||
| func NewCompareResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CompareResourceSpecLogic { | |||
| return &CompareResourceSpecLogic{ | |||
| Logger: logx.WithContext(ctx), | |||
| ctx: ctx, | |||
| svcCtx: svcCtx, | |||
| } | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) GetClusterResourceSpec(req *types.ResourceSpecReq) (*types.PageResult, error) { | |||
| func (l *CompareResourceSpecLogic) CompareResourceSpec(req *types.ResourceSpecReq) (resp *types.PageResult, err error) { | |||
| log.Debug().Msgf("开始比较资源规格,ClusterId: %s, PageNum: %d, PageSize: %d", req.ClusterId, req.PageNum, req.PageSize) | |||
| if req.ClusterId == "" { | |||
| return nil, errors.New("ClusterId is required") | |||
| return resp, nil | |||
| } | |||
| // 获取集群资源数据 | |||
| startTime := time.Now() | |||
| apiResources, err := l.fetchClusterResources(req.ClusterId) | |||
| apiResources, err := l.FetchClusterResources(req.ClusterId) | |||
| log.Debug().Msgf("调用获取ai训练资源接口耗时: %v", time.Since(startTime)) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("failed to fetch cluster resources: %w", err) | |||
| @@ -84,24 +85,31 @@ func (l *GetClusterResourceSpecLogic) GetClusterResourceSpec(req *types.Resource | |||
| return l.queryDatabaseResults(req) | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) fetchClusterResources(ClusterId string) ([]APIResponse, error) { | |||
| func (l *CompareResourceSpecLogic) FetchClusterResources(clusterId string) ([]APIResponse, error) { | |||
| queryLogic := schedule.NewQueryResourcesLogic(l.ctx, l.svcCtx) | |||
| resources, err := queryLogic.QueryResources(&types.QueryResourcesReq{ | |||
| ClusterIDs: []string{ClusterId}, | |||
| }) | |||
| resources, err := queryLogic.QueryResources(&types.QueryResourcesReq{}) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("query resources failed: %w", err) | |||
| } | |||
| var apiResponses []APIResponse | |||
| if err := l.decodeAPIResponse(resources.Data, &apiResponses); err != nil { | |||
| if err := decodeAPIResponse(resources.Data, &apiResponses); err != nil { | |||
| return nil, fmt.Errorf("decode response failed: %w", err) | |||
| } | |||
| return apiResponses, nil | |||
| // 过滤出指定集群的资源 | |||
| var filteredResponses []APIResponse | |||
| for _, response := range apiResponses { | |||
| if response.ClusterId == clusterId && response.Resources != nil { | |||
| filteredResponses = append(filteredResponses, response) | |||
| } | |||
| } | |||
| if len(filteredResponses) == 0 { | |||
| return nil, fmt.Errorf("no resources found for cluster ID: %s", clusterId) | |||
| } | |||
| return filteredResponses, nil | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) decodeAPIResponse(input interface{}, output *[]APIResponse) error { | |||
| func decodeAPIResponse(input interface{}, output *[]APIResponse) error { | |||
| config := &mapstructure.DecoderConfig{ | |||
| Result: output, | |||
| TagName: "json", | |||
| @@ -124,7 +132,7 @@ func (l *GetClusterResourceSpecLogic) decodeAPIResponse(input interface{}, outpu | |||
| return nil | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) syncResourcesToDB(apiResponses []APIResponse) error { | |||
| func (l *CompareResourceSpecLogic) syncResourcesToDB(apiResponses []APIResponse) error { | |||
| for _, response := range apiResponses { | |||
| // 转换API响应到数据库模型 | |||
| dbSpecs, apiSpecs, err := l.processAPIResponse(response) | |||
| @@ -133,14 +141,14 @@ func (l *GetClusterResourceSpecLogic) syncResourcesToDB(apiResponses []APIRespon | |||
| } | |||
| // 处理资源变更 | |||
| if err := l.handleResourceChanges(utils.StringToInt64(response.ClusterId), dbSpecs, apiSpecs); err != nil { | |||
| if err := l.handleResourceChanges(dbSpecs, apiSpecs); err != nil { | |||
| return fmt.Errorf("failed to handle resource changes: %w", err) | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) processAPIResponse(response APIResponse) ([]models.TResourceSpec, []models.TResourceSpec, error) { | |||
| func (l *CompareResourceSpecLogic) processAPIResponse(response APIResponse) ([]models.TResourceSpec, []models.TResourceSpec, error) { | |||
| ClusterId := utils.StringToInt64(response.ClusterId) | |||
| var dbSpecs []models.TResourceSpec | |||
| if err := l.svcCtx.DbEngin.Model(models.TResourceSpec{}).Preload("BaseResourceSpecs"). | |||
| @@ -151,6 +159,10 @@ func (l *GetClusterResourceSpecLogic) processAPIResponse(response APIResponse) ( | |||
| var apiSpecs []models.TResourceSpec | |||
| for _, res := range response.Resources { | |||
| // 检查资源类型和名称是否存在 | |||
| if res.Resource.Name == "" || res.Resource.Type == "" { | |||
| continue | |||
| } | |||
| spec := l.convertToResourceSpec(ClusterId, res) | |||
| apiSpecs = append(apiSpecs, spec) | |||
| } | |||
| @@ -158,11 +170,10 @@ func (l *GetClusterResourceSpecLogic) processAPIResponse(response APIResponse) ( | |||
| return dbSpecs, apiSpecs, nil | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) handleResourceChanges(ClusterId int64, dbSpecs, apiSpecs []models.TResourceSpec) error { | |||
| // 创建资源映射 | |||
| func (l *CompareResourceSpecLogic) handleResourceChanges(dbSpecs, apiSpecs []models.TResourceSpec) error { | |||
| dbMap := make(map[string]models.TResourceSpec) | |||
| for _, spec := range dbSpecs { | |||
| key := resourceKey(spec.Type, spec.Name) | |||
| key := spec.SourceKey | |||
| dbMap[key] = spec | |||
| } | |||
| @@ -172,21 +183,93 @@ func (l *GetClusterResourceSpecLogic) handleResourceChanges(ClusterId int64, dbS | |||
| apiMap[key] = spec | |||
| } | |||
| // 处理新增或更新的资源 | |||
| var createSpecs []*models.TResourceSpec | |||
| var modifiedIDs []int64 | |||
| var normalIDs []int64 | |||
| // 第一阶段:收集需要处理的操作 | |||
| for key, apiSpec := range apiMap { | |||
| dbSpec, exists := dbMap[key] | |||
| if !exists { | |||
| if err := l.createNewResource(&apiSpec); err != nil { | |||
| return err | |||
| } | |||
| // 创建资源副本避免指针重复 | |||
| newSpec := apiSpec | |||
| // 初始化时间字段 | |||
| newSpec.CreateTime = time.Now() | |||
| newSpec.UpdateTime = time.Now() | |||
| createSpecs = append(createSpecs, &newSpec) | |||
| continue | |||
| } | |||
| // 检查资源规格变更 | |||
| if l.isSpecChanged(dbSpec, apiSpec) { | |||
| if err := l.updateResource(&dbSpec, apiSpec); err != nil { | |||
| return err | |||
| modifiedIDs = append(modifiedIDs, dbSpec.Id) | |||
| } else { | |||
| normalIDs = append(normalIDs, dbSpec.Id) | |||
| } | |||
| } | |||
| // 第二阶段:批量处理数据库操作 | |||
| // 批量创建新资源及关联资源 | |||
| if len(createSpecs) > 0 { | |||
| tx := l.svcCtx.DbEngin.Begin() | |||
| if tx.Error != nil { | |||
| return fmt.Errorf("failed to start transaction: %w", tx.Error) | |||
| } | |||
| // 批量插入主资源 | |||
| if err := tx.CreateInBatches(createSpecs, 100).Error; err != nil { | |||
| tx.Rollback() | |||
| return fmt.Errorf("failed to batch create resources: %w", err) | |||
| } | |||
| // 准备关联资源数据 | |||
| var baseResources []models.TBaseResourceSpec | |||
| for _, spec := range createSpecs { | |||
| for i := range spec.BaseResourceSpecs { | |||
| br := &spec.BaseResourceSpecs[i] | |||
| br.ResourceSpecId = spec.Id | |||
| br.CreateTime = time.Now() | |||
| br.UpdateTime = time.Now() | |||
| baseResources = append(baseResources, *br) | |||
| } | |||
| } | |||
| // 批量插入关联资源 | |||
| if len(baseResources) > 0 { | |||
| if err := tx.CreateInBatches(baseResources, 100).Error; err != nil { | |||
| tx.Rollback() | |||
| return fmt.Errorf("failed to batch create base resources: %w", err) | |||
| } | |||
| } | |||
| if err := tx.Commit().Error; err != nil { | |||
| return fmt.Errorf("transaction commit failed: %w", err) | |||
| } | |||
| } | |||
| // 批量更新变更资源 | |||
| now := time.Now() | |||
| if len(modifiedIDs) > 0 { | |||
| if err := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | |||
| Where("id IN ?", modifiedIDs). | |||
| Updates(map[string]interface{}{ | |||
| "change_type": ChangeTypeModified, | |||
| "update_time": now, | |||
| }).Error; err != nil { | |||
| return fmt.Errorf("batch update modified failed: %w", err) | |||
| } | |||
| } | |||
| // 批量更新正常资源 | |||
| if len(normalIDs) > 0 { | |||
| if err := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | |||
| Where("id IN ?", normalIDs). | |||
| Updates(map[string]interface{}{ | |||
| "change_type": ChangeTypeNormal, | |||
| "update_time": now, | |||
| }).Error; err != nil { | |||
| return fmt.Errorf("batch update normal failed: %w", err) | |||
| } | |||
| } | |||
| // 处理删除的资源 | |||
| @@ -205,101 +288,40 @@ func resourceKey(resType, name string) string { | |||
| return fmt.Sprintf("%s::%s", resType, name) | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) createNewResource(spec *models.TResourceSpec) error { | |||
| func (l *CompareResourceSpecLogic) createNewResource(spec *models.TResourceSpec) error { | |||
| return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | |||
| if err := tx.Create(spec).Error; err != nil { | |||
| return fmt.Errorf("failed to create resource: %w", err) | |||
| } | |||
| for i := range spec.BaseResourceSpecs { | |||
| spec.BaseResourceSpecs[i].ResourceSpecId = spec.Id | |||
| spec.BaseResourceSpecs[i].Id = 0 | |||
| } | |||
| if len(spec.BaseResourceSpecs) > 0 { | |||
| if err := tx.CreateInBatches(spec.BaseResourceSpecs, 100).Error; err != nil { | |||
| return fmt.Errorf("failed to create base resources: %w", err) | |||
| } | |||
| } | |||
| return nil | |||
| }) | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) updateResource(existing *models.TResourceSpec, newSpec models.TResourceSpec) error { | |||
| // 标识资源规格变更 | |||
| func (l *CompareResourceSpecLogic) updateResource(existing *models.TResourceSpec, newSpec models.TResourceSpec, changeType int) error { | |||
| return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | |||
| updates := map[string]interface{}{ | |||
| "total_count": newSpec.TotalCount, | |||
| "available_count": newSpec.AvailableCount, | |||
| "change_type": ChangeTypeModified, | |||
| "update_time": time.Now(), | |||
| "change_type": changeType, | |||
| "update_time": time.Now(), | |||
| } | |||
| if err := tx.Model(existing).Updates(updates).Error; err != nil { | |||
| return fmt.Errorf("failed to update resource: %w", err) | |||
| } | |||
| return l.syncBaseResources(tx, existing.Id, newSpec.BaseResourceSpecs) | |||
| return nil | |||
| }) | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) 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 *GetClusterResourceSpecLogic) markResourceDeleted(id int64) error { | |||
| func (l *CompareResourceSpecLogic) markResourceDeleted(id int64) error { | |||
| return l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | |||
| Where("id = ?", id). | |||
| Update("change_type", ChangeTypeDeleted). | |||
| Error | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) isSpecChanged(old, new models.TResourceSpec) bool { | |||
| func (l *CompareResourceSpecLogic) isSpecChanged(old, new models.TResourceSpec) bool { | |||
| if old.TotalCount != new.TotalCount || | |||
| old.AvailableCount != new.AvailableCount || | |||
| old.Region != new.Region { | |||
| @@ -326,8 +348,9 @@ func (l *GetClusterResourceSpecLogic) isSpecChanged(old, new models.TResourceSpe | |||
| return len(oldBaseMap) > 0 | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) convertToResourceSpec(ClusterId int64, res Resource) models.TResourceSpec { | |||
| func (l *CompareResourceSpecLogic) convertToResourceSpec(ClusterId int64, res Resource) models.TResourceSpec { | |||
| spec := models.TResourceSpec{ | |||
| SourceKey: resourceKey(res.Resource.Type, res.Resource.Name), | |||
| Type: res.Resource.Type, | |||
| Name: res.Resource.Name, | |||
| TotalCount: int64(res.Resource.Total.Value), | |||
| @@ -335,7 +358,7 @@ func (l *GetClusterResourceSpecLogic) convertToResourceSpec(ClusterId int64, res | |||
| ClusterId: ClusterId, | |||
| CreateTime: time.Now(), | |||
| UpdateTime: time.Now(), | |||
| ChangeType: ChangeTypeCreated, | |||
| ChangeType: ChangeTypeNormal, | |||
| } | |||
| for _, br := range res.BaseResources { | |||
| @@ -354,7 +377,7 @@ func (l *GetClusterResourceSpecLogic) convertToResourceSpec(ClusterId int64, res | |||
| return spec | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) queryDatabaseResults(req *types.ResourceSpecReq) (*types.PageResult, error) { | |||
| func (l *CompareResourceSpecLogic) queryDatabaseResults(req *types.ResourceSpecReq) (*types.PageResult, error) { | |||
| result := &types.PageResult{ | |||
| PageNum: req.PageNum, | |||
| PageSize: req.PageSize, | |||
| @@ -376,7 +399,7 @@ func (l *GetClusterResourceSpecLogic) queryDatabaseResults(req *types.ResourceSp | |||
| return result, nil | |||
| } | |||
| func (l *GetClusterResourceSpecLogic) buildBaseQuery(req *types.ResourceSpecReq) *gorm.DB { | |||
| func (l *CompareResourceSpecLogic) buildBaseQuery(req *types.ResourceSpecReq) *gorm.DB { | |||
| query := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | |||
| Where("cluster_id = ?", utils.StringToInt64(req.ClusterId)). | |||
| Where("deleted_at IS NULL") | |||
| @@ -2,7 +2,10 @@ package core | |||
| import ( | |||
| "context" | |||
| "github.com/pkg/errors" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||
| "gorm.io/gorm" | |||
| "time" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||
| @@ -25,11 +28,49 @@ func NewDeleteResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) | |||
| } | |||
| func (l *DeleteResourceSpecLogic) DeleteResourceSpec(req *types.DeletePathId) (resp *types.CommonResp, err error) { | |||
| db := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}).Table("t_resource_spec") | |||
| err = db.Delete(&models.TResourceSpec{}, req.Id).Error | |||
| if err != nil { | |||
| return nil, err | |||
| // 初始化事务 | |||
| tx := l.svcCtx.DbEngin.Begin() | |||
| defer func() { | |||
| if r := recover(); r != nil { | |||
| tx.Rollback() | |||
| panic(r) | |||
| } | |||
| }() | |||
| // 1. 检查主资源规格是否存在 | |||
| var mainSpec models.TResourceSpec | |||
| if err := tx.Where("id = ? AND deleted_at IS NULL", req.Id). | |||
| First(&mainSpec). | |||
| Error; err != nil { | |||
| tx.Rollback() | |||
| if errors.Is(err, gorm.ErrRecordNotFound) { | |||
| return nil, errors.Errorf("资源规格不存在 (ID: %s)", req.Id) | |||
| } | |||
| return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %s)", req.Id) | |||
| } | |||
| // 2. 删除主资源规格(软删除) | |||
| if err := tx.Model(&models.TResourceSpec{}). | |||
| Where("id = ?", req.Id). | |||
| Update("deleted_at", time.Now()). | |||
| Error; err != nil { | |||
| tx.Rollback() | |||
| return nil, errors.Wrapf(err, "删除主资源规格失败 (ID: %s)", req.Id) | |||
| } | |||
| // 3. 删除关联的基础资源规格(软删除) | |||
| if err := tx.Model(&models.TBaseResourceSpec{}). | |||
| Where("resource_spec_id = ?", req.Id). | |||
| Update("deleted_at", time.Now()). | |||
| Error; err != nil { | |||
| tx.Rollback() | |||
| return nil, errors.Wrapf(err, "删除基础资源规格失败 (ID: %s)", req.Id) | |||
| } | |||
| // 提交事务 | |||
| if err := tx.Commit().Error; err != nil { | |||
| return nil, errors.Wrap(err, "提交事务失败") | |||
| } | |||
| return | |||
| return resp, nil | |||
| } | |||
| @@ -3,12 +3,12 @@ package core | |||
| import ( | |||
| "context" | |||
| "github.com/pkg/errors" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||
| "github.com/zeromicro/go-zero/core/logx" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||
| "github.com/zeromicro/go-zero/core/logx" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils" | |||
| "gorm.io/gorm" | |||
| ) | |||
| type EditResourceSpecLogic struct { | |||
| @@ -24,31 +24,140 @@ func NewEditResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) * | |||
| svcCtx: svcCtx, | |||
| } | |||
| } | |||
| 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() | |||
| } | |||
| }() | |||
| func (l *EditResourceSpecLogic) EditResourceSpec(req *types.ResourceSpec) (resp *types.CommonResp, err error) { | |||
| //检查是否存在 | |||
| var count int64 | |||
| db := l.svcCtx.DbEngin.Model(&types.ResourceSpec{}).Table("t_resource_spec") | |||
| err = db.Where("id = ? and deleted_at is null", req.Id).Count(&count).Error | |||
| if err != nil { | |||
| // 1. 验证资源规格存在 | |||
| var existing models.TResourceSpec | |||
| if err = tx.Model(&models.TResourceSpec{}). | |||
| Where("id = ? AND deleted_at IS NULL", req.Id). | |||
| First(&existing). | |||
| Error; err != nil { | |||
| if errors.Is(err, gorm.ErrRecordNotFound) { | |||
| return nil, errors.Errorf("资源规格不存在 (ID: %d)", req.Id) | |||
| } | |||
| return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %d)", req.Id) | |||
| } | |||
| // 2. 参数校验 | |||
| if err = validateRequestParams(req); err != nil { | |||
| return nil, err | |||
| } | |||
| if count == 0 { | |||
| return nil, errors.New("资源规格不存在") | |||
| // 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 | |||
| } | |||
| resp = &types.CommonResp{} | |||
| db = l.svcCtx.DbEngin.Model(&types.ResourceSpec{}).Table("t_resource_spec") | |||
| err = db.Where("id = ?", req.Id).Save(req).Error | |||
| if err != nil { | |||
| // 5. 更新子资源规格 | |||
| if err = updateSubResources(tx, req); err != nil { | |||
| return nil, err | |||
| } | |||
| baseDb := l.svcCtx.DbEngin.Model(models.TBaseResourceSpec{}).Table("t_base_resource_spec") | |||
| for _, spec := range req.BaseResourceSpecs { | |||
| err = baseDb.Where("id = ?", spec.Id).Updates(&spec).Error | |||
| if 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": {}, | |||
| "monthly": {}, | |||
| "perUse": {}, | |||
| } | |||
| if _, ok := validCostTypes[req.CostType]; !ok { | |||
| return errors.Errorf("资源规格计费类型不合法 (ID: %d)", req.Id) | |||
| } | |||
| return 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 | |||
| } | |||
| @@ -0,0 +1,135 @@ | |||
| package core | |||
| import ( | |||
| "context" | |||
| "fmt" | |||
| "github.com/pkg/errors" | |||
| "github.com/rs/zerolog/log" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||
| "gorm.io/gorm" | |||
| "strconv" | |||
| "time" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||
| "github.com/zeromicro/go-zero/core/logx" | |||
| ) | |||
| type SyncResourceSpecLogic struct { | |||
| logx.Logger | |||
| ctx context.Context | |||
| svcCtx *svc.ServiceContext | |||
| } | |||
| func NewSyncResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SyncResourceSpecLogic { | |||
| return &SyncResourceSpecLogic{ | |||
| Logger: logx.WithContext(ctx), | |||
| ctx: ctx, | |||
| svcCtx: svcCtx, | |||
| } | |||
| } | |||
| func (l *SyncResourceSpecLogic) SyncResourceSpec(req *types.SyncResourceReq) (resp *types.CommonResp, err error) { | |||
| var mainSpec models.TResourceSpec | |||
| if err := l.svcCtx.DbEngin.Where("id = ? AND deleted_at IS NULL", req.Id). | |||
| First(&mainSpec). | |||
| Error; err != nil { | |||
| if errors.Is(err, gorm.ErrRecordNotFound) { | |||
| return nil, errors.Errorf("资源规格不存在 (ID: %s)", req.Id) | |||
| } | |||
| return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %s)", req.Id) | |||
| } | |||
| // 获取集群资源数据 | |||
| startTime := time.Now() | |||
| compareLogic := NewCompareResourceSpecLogic(l.ctx, l.svcCtx) | |||
| apiResources, err := compareLogic.FetchClusterResources(strconv.FormatInt(mainSpec.ClusterId, 10)) | |||
| log.Debug().Msgf("调用获取ai训练资源接口耗时: %v", time.Since(startTime)) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("failed to fetch cluster resources: %w", err) | |||
| } | |||
| for _, response := range apiResources { | |||
| // 转换API响应到数据库模型 | |||
| _, apiSpecs, err := compareLogic.processAPIResponse(response) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| // 同步资源到数据库 | |||
| for _, spec := range apiSpecs { | |||
| if spec.SourceKey == mainSpec.SourceKey { | |||
| err := l.updateResource(&mainSpec, spec) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return nil, nil | |||
| } | |||
| func (l *SyncResourceSpecLogic) updateResource(existing *models.TResourceSpec, newSpec models.TResourceSpec) error { | |||
| return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | |||
| updates := map[string]interface{}{ | |||
| "type": newSpec.Type, | |||
| "total_count": newSpec.TotalCount, | |||
| "available_count": newSpec.AvailableCount, | |||
| "change_type": ChangeTypeNormal, | |||
| "update_time": time.Now(), | |||
| } | |||
| if err := tx.Model(existing).Updates(updates).Error; err != nil { | |||
| return fmt.Errorf("failed to update resource: %w", err) | |||
| } | |||
| return l.syncBaseResources(tx, existing.Id, newSpec.BaseResourceSpecs) | |||
| }) | |||
| } | |||
| func (l *SyncResourceSpecLogic) 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 | |||
| } | |||
| @@ -52,6 +52,7 @@ func (l *CommitHpcTaskLogic) CommitHpcTask(req *types.CommitHpcTaskReq) (resp *t | |||
| // 构建主任务结构体 | |||
| userId, _ := strconv.ParseInt(req.Parameters["UserId"], 10, 64) | |||
| taskModel := models.Task{ | |||
| Id: utils.GenSnowflakeID(), | |||
| Name: req.Name, | |||
| Description: req.Description, | |||
| CommitTime: time.Now(), | |||
| @@ -76,6 +77,7 @@ func (l *CommitHpcTaskLogic) CommitHpcTask(req *types.CommitHpcTaskReq) (resp *t | |||
| cardCount, _ := strconv.ParseInt(req.Parameters["cardCount"], 10, 64) | |||
| timelimit, _ := strconv.ParseInt(req.Parameters["timeLimit"], 10, 64) | |||
| hpcInfo := models.TaskHpc{ | |||
| Id: utils.GenSnowflakeID(), | |||
| TaskId: taskModel.Id, | |||
| AdapterId: clusterInfo.AdapterId, | |||
| AdapterName: adapterInfo.Name, | |||
| @@ -51,7 +51,7 @@ func StatusReport(url string, report interface{}) error { | |||
| Post(url) | |||
| bodyStr, _ := jsoniter.MarshalToString(report) | |||
| log.Debug().Msgf("任务状态上报到中间件请求参数:[%v], 返回值: [%v]", bodyStr, string(rp.Body())) | |||
| log.Debug().Msgf("上报任务状态到中间件,请求url:%s, 请求参数:%s, 响应结果:%s", url, bodyStr, string(rp.Body())) | |||
| if err != nil { | |||
| logx.Errorf("############ Report Status Message Error %s", err.Error()) | |||
| @@ -41,8 +41,16 @@ func UpdateHpcTaskStatus(svc *svc.ServiceContext) { | |||
| svc.Scheduler.HpcService.TaskSyncLock.Lock() | |||
| defer svc.Scheduler.HpcService.TaskSyncLock.Unlock() | |||
| taskList := make([]*models.TaskHpc, 0) | |||
| //TODO 暂时忽略任务状态 | |||
| sqlStr := `select * from task_hpc where job_id!='' order by created_time desc limit 10` | |||
| sqlStr := `SELECT * | |||
| FROM task_hpc | |||
| WHERE | |||
| job_id != '' | |||
| AND ( | |||
| status NOT IN ('Failed', 'Completed', 'Cancelled') | |||
| OR start_time < created_time | |||
| ) | |||
| ORDER BY created_time DESC | |||
| LIMIT 10` | |||
| db := svc.DbEngin.Raw(sqlStr).Scan(&taskList) | |||
| if db.Error != nil { | |||
| logx.Errorf(db.Error.Error()) | |||
| @@ -2125,6 +2125,20 @@ type Driver_info struct { | |||
| Ipmi_username string `json:"ipmi_username" copier:"ipmi_username"` | |||
| } | |||
| type EditResourceReq struct { | |||
| 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 { | |||
| AllowedAccessIps []string `json:"allowedAccessIps" copier:"AllowedAccessIps"` | |||
| DevService string `json:"devService" copier:"DevService"` | |||
| @@ -4591,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"` | |||
| @@ -5498,6 +5512,10 @@ type SyncClusterAlertReq struct { | |||
| AlertRecordsMap map[string]interface{} `json:"alertRecordsMap"` | |||
| } | |||
| type SyncResourceReq struct { | |||
| Id string `json:"id"` | |||
| } | |||
| type Tags struct { | |||
| } | |||
| @@ -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"` // 用户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:"-"` // 删除时间 | |||
| } | |||
| ) | |||
| @@ -38,21 +38,22 @@ 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"` // 用户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 | |||
| SourceKey string `db:"source_key" json:"source_key"` // 数据源标识(type-name) | |||
| 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"` | |||
| } | |||