diff --git a/pkg/compute/models/skus.go b/pkg/compute/models/skus.go index 6a3e1ea687..4bc2231988 100644 --- a/pkg/compute/models/skus.go +++ b/pkg/compute/models/skus.go @@ -793,3 +793,72 @@ func (manager *SServerSkuManager) newFromCloudSku(ctx context.Context, userCred sku.SetModelManager(manager) return manager.TableSpec().Insert(sku) } + +// sku标记为soldout状态。 +func (manager *SServerSkuManager) MarkAsSoldout(id string) error { + if len(id) == 0 { + log.Debugf("MarkAsSoldout sku id should not be emtpy") + return nil + } + + isku, err := manager.FetchById(id) + if err != nil { + return err + } + + sku, ok := isku.(*SServerSku) + if !ok { + return fmt.Errorf("%s is not a sku object", id) + } + + _, err = manager.TableSpec().Update(sku, func() error { + sku.PrepaidStatus = SkuStatusSoldout + sku.PostpaidStatus = SkuStatusSoldout + return nil + }) + + if err != nil { + return err + } + + return nil +} + +// sku标记为soldout状态。 +func (manager *SServerSkuManager) MarkAllAsSoldout(ids []string) error { + var err error + for _, id := range ids { + err = manager.MarkAsSoldout(id) + if err != nil { + return err + } + } + + return nil +} + +// 获取同一个zone下所有Available状态的sku id +func (manager *SServerSkuManager) FetchAllAvailableSkuIdByZoneId(zoneId string) ([]string, error) { + q := manager.Query() + if len(zoneId) == 0 { + return nil, fmt.Errorf("FetchAllAvailableSkuIdByZoneId zone id should not be emtpy") + } + + skus := make([]SServerSku, 0) + q = q.Equals("zone_id", zoneId) + q = q.Filter(sqlchemy.OR( + sqlchemy.Equals(q.Field("prepaid_status"), SkuStatusAvailable), + sqlchemy.Equals(q.Field("postpaid_status"), SkuStatusAvailable))) + + err := q.All(&skus) + if err != nil { + return nil, err + } + + ids := make([]string, len(skus)) + for i := range skus { + ids[i] = skus[i].GetId() + } + + return ids, nil +} diff --git a/pkg/compute/skus/skus.go b/pkg/compute/skus/skus.go index fbe3e7ea6a..c32fdfd389 100644 --- a/pkg/compute/skus/skus.go +++ b/pkg/compute/skus/skus.go @@ -8,6 +8,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/utils" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -174,6 +175,14 @@ func (self *SkusZone) doUpdate(odata *models.SServerSku, sku jsonutils.JSONObjec func (self *SkusZone) SyncToLocalDB() error { log.Debugf("SkusZone %s start sync.", self.ExternalZoneId) + // 更新已经soldout的sku + localIds, err := models.ServerSkuManager.FetchAllAvailableSkuIdByZoneId(self.ZoneId) + if err != nil { + return err + } + + // 本次已被更新的sku id + updatedIds := make([]string, 0) for _, sku := range self.skus { name, _ := sku.GetString("name") @@ -199,10 +208,20 @@ func (self *SkusZone) SyncToLocalDB() error { if err := self.doUpdate(odata, sku); err != nil { return err } + + updatedIds = append(updatedIds, odata.Id) } } - defer log.Debugf("SkusZone %s sync to local db.total %d,created %d,updated %d", self.ExternalZoneId, self.total, self.created, self.updated) + // 处理已经下架的sku: 将本次未更新且处于available状态的sku置为soldout状态 + abandonIds := diff(localIds, updatedIds) + log.Debugf("SyncToLocalDB abandon sku %s", abandonIds) + err = models.ServerSkuManager.MarkAllAsSoldout(abandonIds) + if err != nil { + return err + } + + defer log.Debugf("SkusZone %s sync to local db.total %d,created %d,updated %d. abandoned %d", self.ExternalZoneId, self.total, self.created, self.updated, len(abandonIds)) return nil } @@ -358,3 +377,15 @@ func SyncSkusByRegion(region *models.SCloudregion) error { return nil } + +// 找出origins中存在,但是compares中不存在的element +func diff(origins, compares []string) []string { + ret := make([]string, 0) + for _, o := range origins { + if !utils.IsInStringArray(o, compares) && len(o) > 0 { + ret = append(ret, o) + } + } + + return ret +} diff --git a/pkg/compute/skus/skus_test.go b/pkg/compute/skus/skus_test.go new file mode 100644 index 0000000000..0de26c3d66 --- /dev/null +++ b/pkg/compute/skus/skus_test.go @@ -0,0 +1,34 @@ +package skus + +import ( + "reflect" + "testing" +) + +func Test_diff(t *testing.T) { + type args struct { + origins []string + compares []string + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "Test array diff", + args: args{ + origins: []string{"1", "2", "3"}, + compares: []string{"2", "3", "5"}, + }, + want: []string{"1"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := diff(tt.args.origins, tt.args.compares); !reflect.DeepEqual(got, tt.want) { + t.Errorf("diff() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/mcclient/modules/managers.go b/pkg/mcclient/modules/managers.go index 5fa631f235..b618c03b91 100644 --- a/pkg/mcclient/modules/managers.go +++ b/pkg/mcclient/modules/managers.go @@ -1,5 +1,10 @@ package modules +/* +添加新manager注意事项: +1. version字段 -- 在endpoint中注册的url如果携带版本。例如http://x.x.x.x/api/v1,那么必须标注对应version字段。否者可能导致yunionapi报资源not found的错误。 +*/ + func NewResourceManager(serviceType string, keyword, keywordPlural string, columns, adminColumns []string) ResourceManager { return ResourceManager{