diff --git a/backend/app/http/orders/controller_order.go b/backend/app/http/orders/controller_order.go index 489cc53..58e83f6 100644 --- a/backend/app/http/orders/controller_order.go +++ b/backend/app/http/orders/controller_order.go @@ -61,7 +61,7 @@ func (c *OrderController) List(ctx fiber.Ctx, claim *jwt.Claims, pagination *req // Create order // @Router /api/v1/orders [post] // @Bind claim local -// @Bind hash +// @Bind hash path // @Bind tenantSlug cookie key(tenant) func (c *OrderController) Create(ctx fiber.Ctx, claim *jwt.Claims, tenantSlug, hash string) (*UserOrder, error) { user, err := c.userSvc.GetUserByID(ctx.Context(), claim.UserID) diff --git a/backend/app/http/orders/routes.gen.go b/backend/app/http/orders/routes.gen.go index 30a6367..316760a 100644 --- a/backend/app/http/orders/routes.gen.go +++ b/backend/app/http/orders/routes.gen.go @@ -53,4 +53,9 @@ func (r *Routes) Register(router fiber.Router) { PathParam[string]("orderID"), )) + router.Post("/v1/orders/pay/notify/:channel", DataFunc1( + r.payController.Notify, + PathParam[string]("channel"), + )) + } diff --git a/backend/app/http/posts/provider.gen.go b/backend/app/http/posts/provider.gen.go index 34161aa..5300158 100755 --- a/backend/app/http/posts/provider.gen.go +++ b/backend/app/http/posts/provider.gen.go @@ -4,21 +4,27 @@ import ( "database/sql" "backend/app/http/tenants" + "backend/app/http/users" "git.ipao.vip/rogeecn/atom" "git.ipao.vip/rogeecn/atom/container" "git.ipao.vip/rogeecn/atom/contracts" "git.ipao.vip/rogeecn/atom/utils/opt" + "github.com/speps/go-hashids/v2" ) func Provide(opts ...opt.Option) error { if err := container.Container.Provide(func( + hashIds *hashids.HashID, svc *Service, tenantSvc *tenants.Service, + userSvc *users.Service, ) (*Controller, error) { obj := &Controller{ + hashIds: hashIds, svc: svc, tenantSvc: tenantSvc, + userSvc: userSvc, } if err := obj.Prepare(); err != nil { return nil, err @@ -44,9 +50,11 @@ func Provide(opts ...opt.Option) error { } if err := container.Container.Provide(func( db *sql.DB, + hashIds *hashids.HashID, ) (*Service, error) { obj := &Service{ - db: db, + db: db, + hashIds: hashIds, } if err := obj.Prepare(); err != nil { return nil, err diff --git a/backend/app/http/posts/routes.gen.go b/backend/app/http/posts/routes.gen.go index 94512b6..68d53b7 100644 --- a/backend/app/http/posts/routes.gen.go +++ b/backend/app/http/posts/routes.gen.go @@ -53,4 +53,11 @@ func (r *Routes) Register(router fiber.Router) { PathParam[string]("hash"), )) + router.Post("/api/v1/posts", Func3( + r.controller.Create, + Local[*jwt.Claims]("claim"), + CookieParam("tenant"), + Body[PostBody]("body"), + )) + } diff --git a/backend/app/http/storages/controller.go b/backend/app/http/storages/controller.go new file mode 100644 index 0000000..044e420 --- /dev/null +++ b/backend/app/http/storages/controller.go @@ -0,0 +1,69 @@ +package storages + +import ( + "backend/database/models/qvyun_v2/public/model" + + "github.com/gofiber/fiber/v3" + "github.com/samber/lo" + log "github.com/sirupsen/logrus" +) + +// @provider +type Controller struct { + svc *Service + log *log.Entry `inject:"false"` +} + +func (c *Controller) Prepare() error { + c.log = log.WithField("module", "storages.Controller") + return nil +} + +// @Router /api/v1/storages [get] +func (ctl *Controller) List(ctx fiber.Ctx) ([]Storage, error) { + storages, err := ctl.svc.GetStorages(ctx.Context()) + if err != nil { + return nil, err + } + + return lo.Map(storages, func(item model.Storages, _ int) Storage { + return Storage{Storages: item} + }), nil +} + +// @Router /api/v1/storages/:id [get] +// @Bind id path +func (ctl *Controller) Show(ctx fiber.Ctx, id int64) (*Storage, error) { + storage, err := ctl.svc.GetStorageByID(ctx.Context(), id) + if err != nil { + return nil, err + } + + return &Storage{Storages: *storage}, nil +} + +// Delete +// @Router /api/v1/storages/:id [delete] +// @Bind id path +func (ctl *Controller) Delete(ctx fiber.Ctx, id int64) error { + return ctl.svc.DeleteStorageByID(ctx.Context(), id) +} + +// Create +// @Router /api/v1/storages [post] +// @Bind req body +func (ctl *Controller) Create(ctx fiber.Ctx, req *CreateStorageReq) error { + m := &model.Storages{ + Name: req.Name, + Type: req.Type, + Config: req.Config, + } + return ctl.svc.Create(ctx.Context(), m) +} + +// SetDefault +// @Router /api/v1/storages/:id/default [put] +// @Bind id path +func (ctl *Controller) SetDefault(ctx fiber.Ctx, id int64) error { + return ctl.svc.SetDefault(ctx.Context(), id) +} diff --git a/backend/app/http/storages/dto.go b/backend/app/http/storages/dto.go new file mode 100644 index 0000000..b6b7400 --- /dev/null +++ b/backend/app/http/storages/dto.go @@ -0,0 +1,16 @@ +package storages + +import ( + "backend/database/fields" + "backend/database/models/qvyun_v2/public/model" +) + +type Storage struct { + model.Storages +} + +type CreateStorageReq struct { + Name string `json:"name"` + Config string `json:"config"` + Type fields.StorageType `json:"type"` +} diff --git a/backend/app/http/storages/provider.gen.go b/backend/app/http/storages/provider.gen.go new file mode 100755 index 0000000..59ae18c --- /dev/null +++ b/backend/app/http/storages/provider.gen.go @@ -0,0 +1,56 @@ +package storages + +import ( + "database/sql" + + "git.ipao.vip/rogeecn/atom" + "git.ipao.vip/rogeecn/atom/container" + "git.ipao.vip/rogeecn/atom/contracts" + "git.ipao.vip/rogeecn/atom/utils/opt" +) + +func Provide(opts ...opt.Option) error { + if err := container.Container.Provide(func( + svc *Service, + ) (*Controller, error) { + obj := &Controller{ + svc: svc, + } + if err := obj.Prepare(); err != nil { + return nil, err + } + + return obj, nil + }); err != nil { + return err + } + if err := container.Container.Provide(func( + controller *Controller, + ) (contracts.HttpRoute, error) { + obj := &Routes{ + controller: controller, + } + if err := obj.Prepare(); err != nil { + return nil, err + } + + return obj, nil + }, atom.GroupRoutes); err != nil { + return err + } + if err := container.Container.Provide(func( + db *sql.DB, + ) (*Service, error) { + obj := &Service{ + db: db, + } + if err := obj.Prepare(); err != nil { + return nil, err + } + + return obj, nil + }); err != nil { + return err + } + return nil +} diff --git a/backend/app/http/storages/routes.gen.go b/backend/app/http/storages/routes.gen.go new file mode 100644 index 0000000..24e371c --- /dev/null +++ b/backend/app/http/storages/routes.gen.go @@ -0,0 +1,55 @@ +// Code generated by the atomctl ; DO NOT EDIT. + +package storages + +import ( + . "backend/pkg/f" + + _ "git.ipao.vip/rogeecn/atom" + _ "git.ipao.vip/rogeecn/atom/contracts" + "github.com/gofiber/fiber/v3" + log "github.com/sirupsen/logrus" +) + +// @provider contracts.HttpRoute atom.GroupRoutes +type Routes struct { + log *log.Entry `inject:"false"` + controller *Controller +} + +func (r *Routes) Prepare() error { + r.log = log.WithField("module", "routes.storages") + return nil +} + +func (r *Routes) Name() string { + return "storages" +} + +func (r *Routes) Register(router fiber.Router) { + // 注册路由组: Controller + router.Get("/api/v1/storages", DataFunc0( + r.controller.List, + )) + + router.Get("/api/v1/storages/:id", DataFunc1( + r.controller.Show, + PathParam[int64]("id"), + )) + + router.Delete("/api/v1/storages/:id", Func1( + r.controller.Delete, + PathParam[int64]("id"), + )) + + router.Post("/api/v1/storages", Func1( + r.controller.Create, + Body[CreateStorageReq]("req"), + )) + + router.Put("/api/v1/storages/:id/default", Func1( + r.controller.SetDefault, + PathParam[int64]("id"), + )) + +} diff --git a/backend/app/http/storages/service.go b/backend/app/http/storages/service.go new file mode 100644 index 0000000..af8192f --- /dev/null +++ b/backend/app/http/storages/service.go @@ -0,0 +1,124 @@ +package storages + +import ( + "context" + "database/sql" + + "backend/database/models/qvyun_v2/public/model" + "backend/database/models/qvyun_v2/public/table" + "backend/providers/otel" + + . "github.com/go-jet/jet/v2/postgres" + log "github.com/sirupsen/logrus" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +// @provider:except +type Service struct { + db *sql.DB + log *log.Entry `inject:"false"` +} + +func (svc *Service) Prepare() error { + svc.log = log.WithField("module", "storages.service") + _ = Int(1) + return nil +} + +// GetStorages +func (svc *Service) GetStorages(ctx context.Context) ([]model.Storages, error) { + _, span := otel.Start(ctx, "storages.service.GetStorages") + defer span.End() + + tbl := table.Storages + stmt := tbl.SELECT(tbl.AllColumns) + span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql())) + + var storages []model.Storages + if err := stmt.QueryContext(ctx, svc.db, &storages); err != nil { + return nil, err + } + return storages, nil +} + +// GetStorageByID +func (svc *Service) GetStorageByID(ctx context.Context, id int64) (*model.Storages, error) { + _, span := otel.Start(ctx, "storages.service.GetStorageByID") + defer span.End() + + tbl := table.Storages + stmt := tbl.SELECT(tbl.AllColumns).WHERE(tbl.ID.EQ(Int64(id))) + span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql())) + + var storage model.Storages + if err := stmt.QueryContext(ctx, svc.db, &storage); err != nil { + return nil, err + } + return &storage, nil +} + +// DeleteStorageByID +func (svc *Service) DeleteStorageByID(ctx context.Context, id int64) error { + _, span := otel.Start(ctx, "storages.service.DeleteStorageByID") + defer span.End() + + tbl := table.Storages + stmt := tbl.DELETE().WHERE(tbl.ID.EQ(Int64(id))) + span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql())) + + _, err := stmt.ExecContext(ctx, svc.db) + return err +} + +// Create +func (svc *Service) Create(ctx context.Context, req *model.Storages) error { + _, span := otel.Start(ctx, "storages.service.Create") + defer span.End() + + tbl := table.Storages + stmt := tbl.INSERT(tbl.Name, tbl.Type, tbl.Config).VALUES(req.Name, req.Type, req.Config) + span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql())) + + _, err := stmt.ExecContext(ctx, svc.db) + return err +} + +// SetDefault +func (svc *Service) SetDefault(ctx context.Context, id int64) error { + _, span := otel.Start(ctx, "storages.service.SetDefault") + defer span.End() + + // Start transaction + tx, err := svc.db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + + tbl := table.Storages + + // First, set all storages' is_default to false + resetStmt := tbl. + UPDATE(tbl.IsDefault). + SET(tbl.IsDefault.SET(Bool(false))) + + span.SetAttributes(semconv.DBStatementKey.String(resetStmt.DebugSql())) + if _, err := resetStmt.ExecContext(ctx, tx); err != nil { + return err + } + + // Then, set the specified storage's is_default to true + setDefaultStmt := tbl. + UPDATE(tbl.IsDefault). + SET(tbl.IsDefault.SET(Bool(true))). + WHERE(tbl.ID.EQ(Int64(id))) + + span.SetAttributes(semconv.DBStatementKey.String(setDefaultStmt.DebugSql())) + + if _, err := setDefaultStmt.ExecContext(ctx, tx); err != nil { + return err + } + + // Commit transaction + return tx.Commit() +} diff --git a/backend/app/http/storages/service_test.go b/backend/app/http/storages/service_test.go new file mode 100644 index 0000000..f9e2eb6 --- /dev/null +++ b/backend/app/http/storages/service_test.go @@ -0,0 +1,37 @@ +package storages + +import ( + "testing" + + "backend/app/service/testx" + + . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/suite" + "go.uber.org/dig" +) + +type ServiceInjectParams struct { + dig.In + Svc *Service +} + +type ServiceTestSuite struct { + suite.Suite + ServiceInjectParams +} + +func Test_DiscoverMedias(t *testing.T) { + providers := testx.Default().With( + Provide, + ) + + testx.Serve(providers, t, func(params ServiceInjectParams) { + suite.Run(t, &ServiceTestSuite{ServiceInjectParams: params}) + }) +} + +func (s *ServiceTestSuite) Test_Service() { + Convey("Test Service", s.T(), func() { + So(s.Svc, ShouldNotBeNil) + }) +} diff --git a/backend/database/fields/storage.go b/backend/database/fields/storage.go index c33946c..36580e7 100644 --- a/backend/database/fields/storage.go +++ b/backend/database/fields/storage.go @@ -3,3 +3,16 @@ package fields // swagger:enum UserStatus // ENUM( Local ,AliOSS, S3, MinIO) type StorageType int16 + +type StorageConfig struct { + Path *string `json:"path"` + S3 *StorageS3Config `json:"s3"` +} + +type StorageS3Config struct { + Endpoint string `json:"endpoint"` + AccessKeyID string `json:"access_key_id"` + AccessKeySecret string `json:"access_key_secret"` + BucketName string `json:"bucket"` + Path string `json:"path"` +} diff --git a/backend/pkg/f/func_data.go b/backend/pkg/f/func_data.go index ceb515f..940039e 100644 --- a/backend/pkg/f/func_data.go +++ b/backend/pkg/f/func_data.go @@ -4,7 +4,7 @@ import ( "github.com/gofiber/fiber/v3" ) -func DataFunc[T any]( +func DataFunc0[T any]( f func(fiber.Ctx) (T, error), ) fiber.Handler { return func(ctx fiber.Ctx) error {