Browse Source

#2624

add resource scene api
tags/v1.22.8.2^2
chenyifan01 3 years ago
parent
commit
c86f9e5909
9 changed files with 451 additions and 24 deletions
  1. +2
    -0
      models/models.go
  2. +12
    -2
      models/resource_queue.go
  3. +292
    -5
      models/resource_scene.go
  4. +42
    -11
      models/resource_specification.go
  5. +58
    -2
      routers/admin/resources.go
  6. +3
    -0
      routers/routes/routes.go
  7. +2
    -2
      services/cloudbrain/resource/resource_queue.go
  8. +35
    -0
      services/cloudbrain/resource/resource_scene.go
  9. +5
    -2
      services/cloudbrain/resource/resource_specification.go

+ 2
- 0
models/models.go View File

@@ -147,6 +147,8 @@ func init() {
new(AiModelConvert), new(AiModelConvert),
new(ResourceQueue), new(ResourceQueue),
new(ResourceSpecification), new(ResourceSpecification),
new(ResourceScene),
new(ResourceSceneSpec),
) )


tablesStatistic = append(tablesStatistic, tablesStatistic = append(tablesStatistic,


+ 12
- 2
models/resource_queue.go View File

@@ -82,6 +82,10 @@ type ResourceQueueCodesRes struct {
AiCenterCode string AiCenterCode string
} }


type GetQueueCodesOptions struct {
Cluster string
}

func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes { func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes {
resList := make([]*ResourceQueueRes, len(list)) resList := make([]*ResourceQueueRes, len(list))
for i, v := range list { for i, v := range list {
@@ -143,9 +147,15 @@ func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueu
return n, r, nil return n, r, nil
} }


func GetResourceQueueCodes() ([]*ResourceQueueCodesRes, error) {
func GetResourceQueueCodes(opts GetQueueCodesOptions) ([]*ResourceQueueCodesRes, error) {
cond := builder.NewCond()
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"cluster": opts.Cluster})
}
cond = cond.And(builder.NotNull{"queue_code"})
cond = cond.And(builder.Neq{"queue_code": ""})
r := make([]*ResourceQueueCodesRes, 0) r := make([]*ResourceQueueCodesRes, 0)
err := x.Table("resource_queue").Where("queue_code is not null AND queue_code != '' ").Find(&r)
err := x.Table("resource_queue").Where(cond).OrderBy("id desc").Find(&r)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 292
- 5
models/resource_scene.go View File

@@ -1,6 +1,15 @@
package models package models


import "code.gitea.io/gitea/modules/timeutil"
import (
"code.gitea.io/gitea/modules/timeutil"
"errors"
"xorm.io/builder"
)

const (
Exclusive = iota + 1
NotExclusive
)


type ResourceScene struct { type ResourceScene struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
@@ -9,9 +18,9 @@ type ResourceScene struct {
IsExclusive bool IsExclusive bool
ExclusiveOrg string ExclusiveOrg string
QueueId int64 QueueId int64
CreatedTime timeutil.TimeStamp `xorm:"INDEX created"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64 CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"INDEX updated"`
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64 UpdatedBy int64
DeleteTime timeutil.TimeStamp `xorm:"deleted"` DeleteTime timeutil.TimeStamp `xorm:"deleted"`
DeletedBy int64 DeletedBy int64
@@ -19,7 +28,285 @@ type ResourceScene struct {


type ResourceSceneSpec struct { type ResourceSceneSpec struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
SceneId int64 `xorm:"INDEX"`
SpecId int64 `xorm:"INDEX"`
SceneId int64 `xorm:"unique(idx_scene_spec)"`
SpecId int64 `xorm:"unique(idx_scene_spec)"`
CreatedTime timeutil.TimeStamp `xorm:"created"` CreatedTime timeutil.TimeStamp `xorm:"created"`
} }

type ResourceSceneReq struct {
ID int64
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
QueueId int64
CreatorId int64
SpecIds []int64
}

type SearchResourceSceneOptions struct {
ListOptions
JobType string
IsExclusive int
AiCenterCode string
QueueId int64
}

type ResourceSceneListRes struct {
TotalSize int64
List []ResourceSceneRes
}

func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes {
return &ResourceSceneListRes{
TotalSize: totalSize,
List: list,
}
}

type ResourceSceneRes struct {
ID int64
SceneName string
JobType JobType
IsExclusive bool
ExclusiveOrg string
Cluster string
AiCenterCode string
QueueCode string
Specs []ResourceSpecWithSceneId
}

func (ResourceSceneRes) TableName() string {
return "resource_scene"
}

type ResourceSpecWithSceneId struct {
ID int64
SourceSpecId string
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
UpdatedTime timeutil.TimeStamp
SceneId int64
}

func (ResourceSpecWithSceneId) TableName() string {
return "resource_specification"
}

func InsertResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
defer sess.Close()

//check
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds).And(builder.Eq{"queue_id": r.QueueId}).And(builder.Eq{"status": SpecOnShelf})
if err := sess.Where(cond).Find(&specs); err != nil {
return err
}
if len(specs) < len(r.SpecIds) {
return errors.New("specIds not correct")
}

rs := ResourceScene{
SceneName: r.SceneName,
JobType: r.JobType,
IsExclusive: r.IsExclusive,
ExclusiveOrg: r.ExclusiveOrg,
QueueId: r.QueueId,
CreatedBy: r.CreatorId,
UpdatedBy: r.CreatorId,
}
_, err := sess.InsertOne(&rs)
if err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
rss := make([]ResourceSceneSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = ResourceSceneSpec{
SceneId: rs.ID,
SpecId: v,
}
}

_, err = sess.Insert(&rss)
if err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func UpdateResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

// find old scene
old := ResourceScene{}
if has, _ := sess.ID(r.ID).Get(&old); !has {
return errors.New("ResourceScene not exist")
}
//check specification
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds).And(builder.Eq{"queue_id": old.QueueId}).And(builder.Eq{"status": SpecOnShelf})
if err := sess.Where(cond).Find(&specs); err != nil {
return err
}
if len(specs) < len(r.SpecIds) {
return errors.New("specIds not correct")
}

//update scene
rs := ResourceScene{
SceneName: r.SceneName,
IsExclusive: r.IsExclusive,
ExclusiveOrg: r.ExclusiveOrg,
}
if _, err = sess.ID(r.ID).UseBool("is_exclusive").Update(&rs); err != nil {
return err
}

//delete scene spec relation
if _, err = sess.Where("scene_id = ? ", r.ID).Delete(&ResourceSceneSpec{}); err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
//build new scene spec relation
rss := make([]ResourceSceneSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = ResourceSceneSpec{
SceneId: r.ID,
SpecId: v,
}
}
if _, err = sess.Insert(&rss); err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func DeleteResourceScene(sceneId int64) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

if _, err = sess.ID(sceneId).Delete(&ResourceScene{}); err != nil {
return err
}
if _, err = sess.Where("scene_id = ? ", sceneId).Delete(&ResourceSceneSpec{}); err != nil {
return err
}
return sess.Commit()
}

func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceSceneRes, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}
if opts.JobType != "" {
cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType})
}
if opts.IsExclusive == Exclusive {
cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 1})
} else if opts.IsExclusive == NotExclusive {
cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 0})
}
if opts.AiCenterCode != "" {
cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode})
}
if opts.QueueId > 0 {
cond = cond.And(builder.Eq{"resource_scene.queue_id": opts.QueueId})
}
cond = cond.And(builder.NewCond().Or(builder.Eq{"resource_scene.delete_time": 0}).Or(builder.IsNull{"resource_scene.delete_time"}))
count, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.id = resource_scene.queue_id").Count(&ResourceSceneRes{})
if err != nil {
return 0, nil, err
}
cols := []string{"resource_scene.id", "resource_scene.scene_name", "resource_scene.job_type", "resource_scene.is_exclusive",
"resource_scene.exclusive_org", "resource_queue.cluster", "resource_queue.ai_center_code",
"resource_queue.queue_code"}
r := make([]ResourceSceneRes, 0)
if err = x.Where(cond).Cols(cols...).
Join("INNER", "resource_queue", "resource_queue.id = resource_scene.queue_id").
Desc("resource_scene.id").
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&r); err != nil {
return 0, nil, err
}

if len(r) == 0 {
return 0, r, err
}
//find related specs
sceneIds := make([]int64, 0, len(r))
for _, v := range r {
sceneIds = append(sceneIds, v.ID)
}

specs := make([]ResourceSpecWithSceneId, 0)

if err := x.Cols("resource_specification.id",
"resource_specification.source_spec_id",
"resource_specification.acc_cards_num",
"resource_specification.cpu_cores",
"resource_specification.mem_gi_b",
"resource_specification.gpu_mem_gi_b",
"resource_specification.share_mem_gi_b",
"resource_specification.unit_price",
"resource_specification.status",
"resource_specification.updated_time",
"resource_scene_spec.scene_id",
).In("resource_scene_spec.scene_id", sceneIds).
Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id").
OrderBy("resource_specification.acc_cards_num").
Find(&specs); err != nil {
return 0, nil, err
}

specsMap := make(map[int64][]ResourceSpecWithSceneId, 0)
for _, v := range specs {
if _, ok := specsMap[v.SceneId]; !ok {
specsMap[v.SceneId] = []ResourceSpecWithSceneId{v}
} else {
specsMap[v.SceneId] = append(specsMap[v.SceneId], v)
}
}

for i, v := range r {
s := specsMap[v.ID]
if s == nil {
s = make([]ResourceSpecWithSceneId, 0)
}
r[i].Specs = s
}

return count, r, nil
}

+ 42
- 11
models/resource_specification.go View File

@@ -46,6 +46,7 @@ func (r ResourceSpecification) ConvertToRes() *ResourceSpecificationRes {


type ResourceSpecificationReq struct { type ResourceSpecificationReq struct {
QueueId int64 `binding:"Required"` QueueId int64 `binding:"Required"`
SourceSpecId string
AccCardsNum int AccCardsNum int
CpuCores int CpuCores int
MemGiB float32 MemGiB float32
@@ -60,6 +61,7 @@ type ResourceSpecificationReq struct {
func (r ResourceSpecificationReq) ToDTO() ResourceSpecification { func (r ResourceSpecificationReq) ToDTO() ResourceSpecification {
return ResourceSpecification{ return ResourceSpecification{
QueueId: r.QueueId, QueueId: r.QueueId,
SourceSpecId: r.SourceSpecId,
AccCardsNum: r.AccCardsNum, AccCardsNum: r.AccCardsNum,
CpuCores: r.CpuCores, CpuCores: r.CpuCores,
MemGiB: r.MemGiB, MemGiB: r.MemGiB,
@@ -77,6 +79,12 @@ type SearchResourceSpecificationOptions struct {
ListOptions ListOptions
QueueId int64 QueueId int64
Status int Status int
Cluster string
}

type SearchResourceBriefSpecificationOptions struct {
QueueId int64
Cluster string
} }


type ResourceSpecAndQueueListRes struct { type ResourceSpecAndQueueListRes struct {
@@ -108,6 +116,10 @@ type ResourceSpecificationRes struct {
UpdatedTime timeutil.TimeStamp UpdatedTime timeutil.TimeStamp
} }


func (ResourceSpecificationRes) TableName() string {
return "resource_specification"
}

type ResourceSpecAndQueueRes struct { type ResourceSpecAndQueueRes struct {
Spec *ResourceSpecificationRes Spec *ResourceSpecificationRes
Queue *ResourceQueueRes Queue *ResourceQueueRes
@@ -133,8 +145,8 @@ func InsertResourceSpecification(r ResourceSpecification) (int64, error) {
return x.Insert(&r) return x.Insert(&r)
} }


func UpdateResourceSpecificationById(queueId int64, queue ResourceSpecification) (int64, error) {
return x.ID(queueId).Update(&queue)
func UpdateResourceSpecificationById(queueId int64, spec ResourceSpecification) (int64, error) {
return x.ID(queueId).Update(&spec)
} }


func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64, []ResourceSpecAndQueue, error) { func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64, []ResourceSpecAndQueue, error) {
@@ -148,14 +160,18 @@ func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64
if opts.Status > 0 { if opts.Status > 0 {
cond = cond.And(builder.Eq{"resource_specification.status": opts.Status}) cond = cond.And(builder.Eq{"resource_specification.status": opts.Status})
} }
n, err := x.Where(cond).Count(&ResourceSpecAndQueue{})
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster})
}
n, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
Count(&ResourceSpecAndQueue{})
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }


r := make([]ResourceSpecAndQueue, 0) r := make([]ResourceSpecAndQueue, 0)
err = x.Where(cond). err = x.Where(cond).
Join("LEFT", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
Desc("resource_specification.id"). Desc("resource_specification.id").
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&r) Find(&r)
@@ -165,17 +181,32 @@ func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64
return n, r, nil return n, r, nil
} }


func ResourceSpecOnShelf(id int64) (int64, error) {
param := ResourceSpecification{
Status: SpecOnShelf,
}
return x.Where("id = ? and status in (?,?)", id, SpecOffShelf, SpecNotVerified).Update(&param)
func ResourceSpecOnShelf(id int64, spec ResourceSpecification) (int64, error) {
spec.Status = SpecOnShelf
return x.Where("id = ? and status in (?,?)", id, SpecOffShelf, SpecNotVerified).Update(&spec)
} }


func ResourceSpecOffShelf(id int64) (int64, error) { func ResourceSpecOffShelf(id int64) (int64, error) {
//todo delete scene
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()
//delete scene spec relation
if _, err = sess.Where("spec_id = ?", id).Delete(&ResourceSceneSpec{}); err != nil {
return 0, err
}

param := ResourceSpecification{ param := ResourceSpecification{
Status: SpecOffShelf, Status: SpecOffShelf,
} }
return x.Where("id = ? and status = ?", id, SpecOnShelf).Update(&param)
n, err := sess.Where("id = ? and status = ?", id, SpecOnShelf).Update(&param)
if err != nil {
return 0, err
}
sess.Commit()
return n, err
} }

+ 58
- 2
routers/admin/resources.go View File

@@ -59,7 +59,8 @@ func GetResourceQueueList(ctx *context.Context) {
} }


func GetResourceQueueCodes(ctx *context.Context) { func GetResourceQueueCodes(ctx *context.Context) {
list, err := resource.GetResourceQueueCodes()
cluster := ctx.Query("cluster")
list, err := resource.GetResourceQueueCodes(models.GetQueueCodesOptions{Cluster: cluster})
if err != nil { if err != nil {
log.Error("GetResourceQueueCodes error.%v", err) log.Error("GetResourceQueueCodes error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error())) ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
@@ -96,10 +97,12 @@ func GetResourceSpecificationList(ctx *context.Context) {
page := ctx.QueryInt("page") page := ctx.QueryInt("page")
queue := ctx.QueryInt64("queue") queue := ctx.QueryInt64("queue")
status := ctx.QueryInt("status") status := ctx.QueryInt("status")
cluster := ctx.Query("cluster")
list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{ list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 20}, ListOptions: models.ListOptions{Page: page, PageSize: 20},
QueueId: queue, QueueId: queue,
Status: status, Status: status,
Cluster: cluster,
}) })
if err != nil { if err != nil {
log.Error("GetResourceSpecificationList error.%v", err) log.Error("GetResourceSpecificationList error.%v", err)
@@ -135,7 +138,7 @@ func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecif
//only UnitPrice and permitted to change //only UnitPrice and permitted to change
err = resource.UpdateResourceSpecification(id, req) err = resource.UpdateResourceSpecification(id, req)
case "on-shelf": case "on-shelf":
err = resource.ResourceSpecOnShelf(id)
err = resource.ResourceSpecOnShelf(id, req)
case "off-shelf": case "off-shelf":
err = resource.ResourceSpecOffShelf(id) err = resource.ResourceSpecOffShelf(id)
} }
@@ -147,3 +150,56 @@ func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecif
} }
ctx.JSON(http.StatusOK, response.Success()) ctx.JSON(http.StatusOK, response.Success())
} }

func GetResourceSceneList(ctx *context.Context) {
page := ctx.QueryInt("page")
jobType := ctx.Query("jobType")
aiCenterCode := ctx.Query("center")
queueId := ctx.QueryInt64("queue")
isExclusive := ctx.QueryInt("IsExclusive")
list, err := resource.GetResourceSceneList(models.SearchResourceSceneOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 20},
JobType: jobType,
IsExclusive: isExclusive,
AiCenterCode: aiCenterCode,
QueueId: queueId,
})
if err != nil {
log.Error("GetResourceSceneList error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(list))
}

func AddResourceScene(ctx *context.Context, req models.ResourceSceneReq) {
req.CreatorId = ctx.User.ID
err := resource.AddResourceScene(req)
if err != nil {
log.Error("AddResourceScene error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func UpdateResourceScene(ctx *context.Context, req models.ResourceSceneReq) {
id := ctx.ParamsInt64(":id")
action := ctx.Query("action")

req.ID = id
var err error
switch action {
case "edit":
err = resource.UpdateResourceScene(req)
case "delete":
err = resource.DeleteResourceScene(id)
}

if err != nil {
log.Error("UpdateResourceScene error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

+ 3
- 0
routers/routes/routes.go View File

@@ -622,6 +622,9 @@ func RegisterRoutes(m *macaron.Macaron) {
}) })
m.Group("/scene", func() { m.Group("/scene", func() {
m.Get("", admin.GetScenePage) m.Get("", admin.GetScenePage)
m.Get("/list", admin.GetResourceSceneList)
m.Post("/add", binding.Bind(models.ResourceSceneReq{}), admin.AddResourceScene)
m.Post("/update/:id", binding.BindIgnErr(models.ResourceSceneReq{}), admin.UpdateResourceScene)
}) })
}) })
}, adminReq) }, adminReq)


+ 2
- 2
services/cloudbrain/resource/resource_queue.go View File

@@ -30,8 +30,8 @@ func GetResourceQueueList(opts models.SearchResourceQueueOptions) (*models.Resou
return models.NewResourceQueueListRes(n, r), nil return models.NewResourceQueueListRes(n, r), nil
} }


func GetResourceQueueCodes() ([]*models.ResourceQueueCodesRes, error) {
r, err := models.GetResourceQueueCodes()
func GetResourceQueueCodes(opts models.GetQueueCodesOptions) ([]*models.ResourceQueueCodesRes, error) {
r, err := models.GetResourceQueueCodes(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 35
- 0
services/cloudbrain/resource/resource_scene.go View File

@@ -0,0 +1,35 @@
package resource

import (
"code.gitea.io/gitea/models"
)

func AddResourceScene(req models.ResourceSceneReq) error {
if err := models.InsertResourceScene(req); err != nil {
return err
}
return nil
}

func UpdateResourceScene(req models.ResourceSceneReq) error {
if err := models.UpdateResourceScene(req); err != nil {
return err
}
return nil
}

func DeleteResourceScene(id int64) error {
if err := models.DeleteResourceScene(id); err != nil {
return err
}
return nil
}

func GetResourceSceneList(opts models.SearchResourceSceneOptions) (*models.ResourceSceneListRes, error) {
n, r, err := models.SearchResourceScene(opts)
if err != nil {
return nil, err
}

return models.NewResourceSceneListRes(n, r), nil
}

+ 5
- 2
services/cloudbrain/resource/resource_specification.go View File

@@ -23,6 +23,7 @@ func UpdateResourceSpecification(specId int64, req models.ResourceSpecificationR
return nil return nil
} }


//GetResourceSpecificationList returns specification and queue
func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions) (*models.ResourceSpecAndQueueListRes, error) { func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions) (*models.ResourceSpecAndQueueListRes, error) {
n, r, err := models.SearchResourceSpecification(opts) n, r, err := models.SearchResourceSpecification(opts)
if err != nil { if err != nil {
@@ -32,8 +33,10 @@ func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions
return models.NewResourceSpecAndQueueListRes(n, r), nil return models.NewResourceSpecAndQueueListRes(n, r), nil
} }


func ResourceSpecOnShelf(id int64) error {
_, err := models.ResourceSpecOnShelf(id)
func ResourceSpecOnShelf(id int64, req models.ResourceSpecificationReq) error {
_, err := models.ResourceSpecOnShelf(id, models.ResourceSpecification{
UnitPrice: req.UnitPrice,
})
if err != nil { if err != nil {
return err return err
} }


Loading…
Cancel
Save