diff --git a/accounting/router/api.go b/accounting/router/api.go index fe25ecb67..1d66a17f3 100644 --- a/accounting/router/api.go +++ b/accounting/router/api.go @@ -3,20 +3,22 @@ package router import ( "fmt" + "opencsg.com/csghub-server/builder/instrumentation" + "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" "opencsg.com/csghub-server/accounting/handler" "opencsg.com/csghub-server/api/middleware" + bldmq "opencsg.com/csghub-server/builder/mq" "opencsg.com/csghub-server/common/config" "opencsg.com/csghub-server/mq" ) -func NewAccountRouter(config *config.Config, mqHandler mq.MessageQueue) (*gin.Engine, error) { +func NewAccountRouter(config *config.Config, mqHandler mq.MessageQueue, mqFactory bldmq.MessageQueueFactory) (*gin.Engine, error) { r := gin.New() - r.Use(gin.Recovery()) - r.Use(middleware.Log()) + middleware.SetInfraMiddleware(r, config, instrumentation.Account) needAPIKey := middleware.NeedAPIKey(config) //add router for golang pprof debugGroup := r.Group("/debug", needAPIKey) @@ -34,7 +36,7 @@ func NewAccountRouter(config *config.Config, mqHandler mq.MessageQueue) (*gin.En return nil, fmt.Errorf("error creating multi sync handler, error: %w", err) } createMeteringRoutes(apiGroup, meterHandler) - err = createAdvancedRoutes(apiGroup, config, mqHandler) + err = createAdvancedRoutes(apiGroup, config, mqHandler, mqFactory) if err != nil { return nil, fmt.Errorf("error creating accounting advanced routes, error:%w", err) } diff --git a/accounting/router/api_ce.go b/accounting/router/api_ce.go index 3b900fc4d..d74374375 100644 --- a/accounting/router/api_ce.go +++ b/accounting/router/api_ce.go @@ -4,10 +4,11 @@ package router import ( "github.com/gin-gonic/gin" + bldmq "opencsg.com/csghub-server/builder/mq" "opencsg.com/csghub-server/common/config" "opencsg.com/csghub-server/mq" ) -func createAdvancedRoutes(apiGroup *gin.RouterGroup, config *config.Config, mqHandler mq.MessageQueue) error { +func createAdvancedRoutes(apiGroup *gin.RouterGroup, config *config.Config, mqHandler mq.MessageQueue, mqFactory bldmq.MessageQueueFactory) error { return nil } diff --git a/api/router/api.go b/api/router/api.go index f0c1868f9..a9a3c8865 100644 --- a/api/router/api.go +++ b/api/router/api.go @@ -11,6 +11,8 @@ import ( "github.com/gin-contrib/cors" "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" "github.com/prometheus/client_golang/prometheus/promhttp" swaggerFiles "github.com/swaggo/files" @@ -184,6 +186,7 @@ func NewRouter(config *config.Config, enableSwagger bool) (*gin.Engine, error) { r.Use(middleware.LocalizedErrorMiddleware()) r.Use(middleware.Authenticator(config)) useAdvancedMiddleware(r, config) + createCustomValidator() apiGroup := r.Group("/api/v1") versionHandler := handler.NewVersionHandler() @@ -1330,3 +1333,7 @@ func createWebHookRoutes(apiGroup *gin.RouterGroup, middlewareCollection middlew } return nil } + +func createCustomValidator() { + _, _ = binding.Validator.Engine().(*validator.Validate) +} diff --git a/builder/store/database/account_price.go b/builder/store/database/account_price.go index a3474405f..97acefa93 100644 --- a/builder/store/database/account_price.go +++ b/builder/store/database/account_price.go @@ -134,8 +134,22 @@ func (a *accountPriceStoreImpl) ListBySkuType(ctx context.Context, req types.Acc if err != nil { return nil, 0, fmt.Errorf("failed to counting recorder, error: %w", err) } - _, err = q.Order("sku_type ASC").Order("sku_kind ASC").Order("resource_id ASC"). - Order("sku_unit_type ASC").Order("created_at DESC"). + sortMap := map[string]string{ + "sku_type": "ASC", + "sku_kind": "ASC", + "resource_id": "ASC", + "sku_unit_type": "ASC", + "created_at": "DESC", + } + + if len(req.SortBy) > 0 && len(req.SortOrder) > 0 { + sortMap[req.SortBy] = req.SortOrder + } + _, err = q.Order(fmt.Sprintf("%s %s", "sku_type", sortMap["sku_type"])). + Order(fmt.Sprintf("%s %s", "sku_kind", sortMap["sku_kind"])). + Order(fmt.Sprintf("%s %s", "resource_id", sortMap["resource_id"])). + Order(fmt.Sprintf("%s %s", "sku_unit_type", sortMap["sku_unit_type"])). + Order(fmt.Sprintf("%s %s", "created_at", sortMap["created_at"])). Limit(req.Per).Offset((req.Page-1)*req.Per).Exec(ctx, &result) if err != nil { return nil, 0, fmt.Errorf("select prices by type and kind and resource, error: %w", err) diff --git a/builder/store/database/account_price_test.go b/builder/store/database/account_price_test.go index c910a303c..82d999e7f 100644 --- a/builder/store/database/account_price_test.go +++ b/builder/store/database/account_price_test.go @@ -230,6 +230,52 @@ func TestAccountPriceStore_ListBySkuType(t *testing.T) { } +func TestAccountPriceStore_ListBySkuType_WithSort(t *testing.T) { + db := tests.InitTestDB() + defer db.Close() + ctx := context.TODO() + + store := database.NewAccountPriceStoreWithDB(db) + + prices := []*database.AccountPrice{ + { + SkuType: types.SKUCSGHub, SkuKind: types.SKUPackageAddon, ResourceID: "r1", + SkuUnitType: "u", SkuDesc: "a", + }, + { + SkuType: types.SKUCSGHub, SkuKind: types.SKUPackageAddon, ResourceID: "r3", + SkuUnitType: "u", SkuDesc: "b", + }, + { + SkuType: types.SKUCSGHub, SkuKind: types.SKUPackageAddon, ResourceID: "r2", + SkuUnitType: "u", SkuDesc: "c", + }, + } + + for _, p := range prices { + _, err := store.Create(ctx, *p) + require.Nil(t, err) + } + + data, count, err := store.ListBySkuType(ctx, types.AcctPriceListDBReq{ + SkuType: types.SKUCSGHub, + Page: 1, + Per: 10, + SortBy: "resource_id", + SortOrder: "DESC", + }) + require.Nil(t, err) + require.Equal(t, 3, count) + require.Equal(t, 3, len(data)) + require.Equal(t, "b/c/a", func(prices []database.AccountPrice) string { + names := []string{} + for _, p := range prices { + names = append(names, p.SkuDesc) + } + return strings.Join(names, "/") + }(data)) +} + func TestAccountPriceStore_ListByIds(t *testing.T) { db := tests.InitTestDB() defer db.Close() diff --git a/cmd/csghub-server/cmd/accounting/launch.go b/cmd/csghub-server/cmd/accounting/launch.go index 42fc0f949..23163fefb 100644 --- a/cmd/csghub-server/cmd/accounting/launch.go +++ b/cmd/csghub-server/cmd/accounting/launch.go @@ -1,9 +1,12 @@ package accounting import ( + "context" "fmt" "log/slog" + "opencsg.com/csghub-server/builder/instrumentation" + "github.com/spf13/cobra" "opencsg.com/csghub-server/accounting/consumer" "opencsg.com/csghub-server/accounting/router" @@ -59,7 +62,12 @@ var launchCmd = &cobra.Command{ i18n.InitLocalizersFromEmbedFile() - r, err := router.NewAccountRouter(cfg, mqHandler) + stopOtel, err := instrumentation.SetupOTelSDK(context.Background(), cfg, instrumentation.Account) + if err != nil { + panic(err) + } + + r, err := router.NewAccountRouter(cfg, mqHandler, mqFactory) if err != nil { return fmt.Errorf("failed to init router: %w", err) } @@ -71,7 +79,7 @@ var launchCmd = &cobra.Command{ r, ) server.Run() - + _ = stopOtel(context.Background()) return nil }, } diff --git a/common/types/accounting.go b/common/types/accounting.go index 336f4406e..5462c4012 100644 --- a/common/types/accounting.go +++ b/common/types/accounting.go @@ -369,6 +369,8 @@ type AcctPriceListDBReq struct { SkuType SKUType `json:"sku_type"` SkuKind string `json:"sku_kind"` ResourceID []string `json:"resource_id"` + SortBy string `json:"sort_by"` + SortOrder string `json:"sort_order"` Per int `json:"per"` Page int `json:"page"` } @@ -377,16 +379,18 @@ type AcctPriceListDBReq struct { // // in accounting service and starhub server type AcctPriceListReq struct { - SkuType SKUType `json:"sku_type"` - SkuKind string `json:"sku_kind"` - ResourceID []string `json:"resource_id"` - Filter AcctPriceListFilter `json:"filter"` - Per int `json:"per"` - Page int `json:"page"` + SkuType SKUType `json:"sku_type" form:"sku_type"` + SkuKind string `json:"sku_kind" form:"sku_kind"` + ResourceID []string `json:"resource_id" form:"resource_id"` + Filter AcctPriceListFilter `json:"-"` + SortBy string `json:"sort_by" form:"sort_by" binding:"omitempty,oneof=resource_id"` + SortOrder string `json:"sort_order" form:"sort_order" binding:"omitempty,oneof=ASC DESC asc desc"` + Per int `json:"per" form:"per,default=50" binding:"min=1,max=100"` + Page int `json:"page" form:"page,default=1" binding:"min=1"` } type AcctPriceListFilter struct { - HardwareType string `json:"hardware_type"` + HardwareType string `json:"hardware_type" form:"hardware_type"` } type AcctRechargeReq struct {