diff --git a/backend/__debug_bin1422413187 b/backend/__debug_bin1422413187 new file mode 100755 index 0000000..551bf80 Binary files /dev/null and b/backend/__debug_bin1422413187 differ diff --git a/backend/config.toml b/backend/config.toml index cf81473..a65ee49 100755 --- a/backend/config.toml +++ b/backend/config.toml @@ -3,7 +3,7 @@ Mode = "development" BaseURI = "https://qvyun.mp.jdwan.com" [Http] -Port = 3000 +Port = 9600 [Swagger] BaseRoute = "doc" @@ -34,4 +34,5 @@ Salt = "LiXi.Y@140202" [Storage] Type = "local" -Path = "/projects/mp-qvyun/backend/fixtures/processed" +# Path = "/projects/mp-qvyun/backend/fixtures/processed" +Path = "/mnt/yangpingliang/publish/processed" diff --git a/backend/database/migrations/20241128075611_init.sql b/backend/database/migrations/20241128075611_init.sql index 21be68a..baa7d6f 100644 --- a/backend/database/migrations/20241128075611_init.sql +++ b/backend/database/migrations/20241128075611_init.sql @@ -72,6 +72,7 @@ CREATE TABLE tenant_id INT8 NOT NULL, title VARCHAR(198) NOT NULL, description VARCHAR(198) NOT NULL default '', + duration INT8 NOT NULL default 0, price INT8 NOT NULL default 0, discount INT8 NOT NULL default 100, publish BOOL NOT NULL default false, diff --git a/backend/database/models/qvyun/public/model/medias.go b/backend/database/models/qvyun/public/model/medias.go index a0dba7c..a3e9bdb 100644 --- a/backend/database/models/qvyun/public/model/medias.go +++ b/backend/database/models/qvyun/public/model/medias.go @@ -18,6 +18,7 @@ type Medias struct { TenantID int64 `json:"tenant_id"` Title string `json:"title"` Description string `json:"description"` + Duration int64 `json:"duration"` Price int64 `json:"price"` Discount int64 `json:"discount"` Publish bool `json:"publish"` diff --git a/backend/database/models/qvyun/public/table/medias.go b/backend/database/models/qvyun/public/table/medias.go index c98b6b2..78ff252 100644 --- a/backend/database/models/qvyun/public/table/medias.go +++ b/backend/database/models/qvyun/public/table/medias.go @@ -22,6 +22,7 @@ type mediasTable struct { TenantID postgres.ColumnInteger Title postgres.ColumnString Description postgres.ColumnString + Duration postgres.ColumnInteger Price postgres.ColumnInteger Discount postgres.ColumnInteger Publish postgres.ColumnBool @@ -73,14 +74,15 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable { TenantIDColumn = postgres.IntegerColumn("tenant_id") TitleColumn = postgres.StringColumn("title") DescriptionColumn = postgres.StringColumn("description") + DurationColumn = postgres.IntegerColumn("duration") PriceColumn = postgres.IntegerColumn("price") DiscountColumn = postgres.IntegerColumn("discount") PublishColumn = postgres.BoolColumn("publish") ResourcesColumn = postgres.StringColumn("resources") CreatedAtColumn = postgres.TimestampColumn("created_at") UpdatedAtColumn = postgres.TimestampColumn("updated_at") - allColumns = postgres.ColumnList{IDColumn, HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn} - mutableColumns = postgres.ColumnList{HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn} + allColumns = postgres.ColumnList{IDColumn, HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, DurationColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn} + mutableColumns = postgres.ColumnList{HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, DurationColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn} ) return mediasTable{ @@ -92,6 +94,7 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable { TenantID: TenantIDColumn, Title: TitleColumn, Description: DescriptionColumn, + Duration: DurationColumn, Price: PriceColumn, Discount: DiscountColumn, Publish: PublishColumn, diff --git a/backend/modules/medias/controller.go b/backend/modules/medias/controller.go index df32094..d7a04df 100644 --- a/backend/modules/medias/controller.go +++ b/backend/modules/medias/controller.go @@ -6,7 +6,7 @@ import ( "backend/providers/jwt" "github.com/gofiber/fiber/v3" - . "github.com/spf13/cast" + log "github.com/sirupsen/logrus" ) // @provider @@ -18,6 +18,7 @@ type Controller struct { func (c *Controller) List(ctx fiber.Ctx) error { filter := ListFilter{} if err := ctx.Bind().Body(&filter); err != nil { + log.WithError(err).Error("parse body failed") return ctx.Status(fiber.StatusBadRequest).JSON(errorx.RequestParseError) } claim := ctx.Locals(consts.CtxKeyClaim).(*jwt.Claims) @@ -32,15 +33,24 @@ func (c *Controller) List(ctx fiber.Ctx) error { // Show func (c *Controller) Show(ctx fiber.Ctx) error { - id := ToInt64(ctx.Params("id")) - tenantId, userId := ToInt64(ctx.Locals("tenantId")), ToInt64(ctx.Locals("userId")) + hash := ctx.Params("hash") + claim := fiber.Locals[*jwt.Claims](ctx, consts.CtxKeyClaim) + log.Debug(claim) - item, err := c.svc.GetByID(ctx.Context(), tenantId, userId, id) + model, err := c.svc.GetMediaByHash(ctx.Context(), claim.TenantID, hash) if err != nil { + log.WithField("action", "medias.Show").WithError(err).Error("GetMediaByHash") return ctx.Status(fiber.StatusInternalServerError).JSON(errorx.InternalError) } - return ctx.JSON(item) + resource := c.svc.ModelToListItem(ctx.Context(), model) + resource.Bought, err = c.svc.HasUserBought(ctx.Context(), claim.TenantID, claim.UserID, model.ID) + if err != nil { + log.WithField("action", "medias.Show").WithError(err).Error("HasUserBought") + return ctx.Status(fiber.StatusInternalServerError).JSON(errorx.InternalError) + } + + return ctx.JSON(resource) } // Audio diff --git a/backend/modules/medias/dto.go b/backend/modules/medias/dto.go index 37f16cc..363fe19 100644 --- a/backend/modules/medias/dto.go +++ b/backend/modules/medias/dto.go @@ -1,17 +1,28 @@ package medias import ( - "backend/database/models/qvyun/public/model" + "time" + "backend/pkg/db" + "backend/pkg/pg" ) type ListFilter struct { db.Pagination - Title *string `json:"title"` - Bought *bool `json:"bought"` + Title *string `json:"title,omitempty"` + Bought *bool `json:"bought,omitempty"` } type ListItem struct { - model.Medias - Bought bool `json:"bought"` + ID int64 `json:"-"` + Hash string `json:"hash"` + Title string `json:"title"` + Description string `json:"description"` + Duration int64 `json:"duration"` + Price int64 `json:"price"` + Discount int64 `json:"discount"` + Resources pg.MediaResources `json:"resources"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Bought bool `json:"bought"` } diff --git a/backend/modules/medias/router.go b/backend/modules/medias/router.go index 125e67a..5537f75 100755 --- a/backend/modules/medias/router.go +++ b/backend/modules/medias/router.go @@ -25,5 +25,6 @@ func (r *Router) Prepare() error { func (r *Router) Register(router fiber.Router) { group := router.Group(r.Name()) - group.Get("", r.controller.List) + group.Post("", r.controller.List) + group.Get(":hash", r.controller.Show) } diff --git a/backend/modules/medias/service.go b/backend/modules/medias/service.go index 4d4a5d0..a591f0f 100644 --- a/backend/modules/medias/service.go +++ b/backend/modules/medias/service.go @@ -7,6 +7,7 @@ import ( "time" "backend/common/media_store" + "backend/database/models/qvyun/public/model" "backend/database/models/qvyun/public/table" "backend/pkg/path" "backend/pkg/pg" @@ -30,9 +31,31 @@ func (svc *Service) Prepare() error { return nil } +// GetMediaByHash +func (svc *Service) GetMediaByHash(ctx context.Context, tenantId int64, hash string) (*model.Medias, error) { + log := svc.log.WithField("method", "GetMediaByHash") + + tbl := table.Medias + stmt := tbl. + SELECT(tbl.AllColumns). + WHERE( + tbl.Hash.EQ(String(hash)).AND( + tbl.TenantID.EQ(Int(tenantId)), + ), + ) + log.Debug(stmt.DebugSql()) + + var m model.Medias + if err := stmt.QueryContext(ctx, svc.db, &m); err != nil { + return nil, errors.Wrapf(err, "get media by hash %s failed", hash) + } + + return &m, nil +} + // GetByID -func (svc *Service) GetByID(ctx context.Context, tenantId, userId, id int64) (*ListItem, error) { - log := svc.log.WithField("method", "GetByID") +func (svc *Service) GetMediaByID(ctx context.Context, tenantId, userId, id int64) (*model.Medias, error) { + log := svc.log.WithField("method", "GetMediaByID") tbl := table.Medias stmt := tbl.SELECT(tbl.AllColumns).WHERE( @@ -40,22 +63,28 @@ func (svc *Service) GetByID(ctx context.Context, tenantId, userId, id int64) (*L tbl.TenantID.EQ(Int(tenantId)), ), ) - log.Debug(stmt.Sql()) + log.Debug(stmt.DebugSql()) - var media ListItem - if err := stmt.QueryContext(ctx, svc.db, &media); err != nil { + var m model.Medias + if err := stmt.QueryContext(ctx, svc.db, &m); err != nil { return nil, errors.Wrap(err, "query media by id") } + return &m, nil +} - // todo: resources - - var err error - media.Bought, err = svc.HasUserBought(ctx, tenantId, userId, media.ID) - if err != nil { - return nil, errors.Wrap(err, "check user bought") +func (svc *Service) ModelToListItem(ctx context.Context, m *model.Medias) *ListItem { + return &ListItem{ + ID: m.ID, + Hash: m.Hash, + Title: m.Title, + Description: m.Description, + Duration: m.Duration, + Price: m.Price, + Discount: m.Discount, + Resources: m.Resources, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, } - - return &media, nil } // List @@ -102,14 +131,24 @@ func (svc *Service) List(ctx context.Context, tenantId, userId int64, filter *Li } log.Debug(stmt.DebugSql()) - var dest []ListItem + var dest []model.Medias if err := stmt.QueryContext(ctx, svc.db, &dest); err != nil { return nil, errors.Wrap(err, "query medias") } - items := lo.Map(dest, func(item ListItem, _ int) ListItem { - if lo.Contains(boughtIDs, item.ID) { - item.Bought = true + items := lo.Map(dest, func(m model.Medias, _ int) ListItem { + item := ListItem{ + ID: m.ID, + Hash: m.Hash, + Title: m.Title, + Description: m.Description, + Duration: m.Duration, + Price: m.Price, + Discount: m.Discount, + Resources: m.Resources, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + Bought: lo.Contains(boughtIDs, m.ID), } return item }) @@ -145,7 +184,7 @@ func (svc *Service) HasUserBought(ctx context.Context, tenantId, userId, mediaId tbl := table.UserMedias stmt := tbl. - SELECT(tbl.MediaID). + SELECT(COUNT(tbl.MediaID).AS("cnt")). WHERE( tbl.TenantID.EQ(Int(tenantId)).AND( tbl.UserID.EQ(Int(userId)).AND( @@ -155,12 +194,14 @@ func (svc *Service) HasUserBought(ctx context.Context, tenantId, userId, mediaId ) log.Debug(stmt.DebugSql()) - var mediaID int64 - if err := stmt.QueryContext(ctx, svc.db, &mediaID); err != nil { + var m struct { + Cnt int64 + } + if err := stmt.QueryContext(ctx, svc.db, &m); err != nil { return false, errors.Wrap(err, "query user bought media") } - return mediaID > 0, nil + return m.Cnt > 0, nil } // Upsert @@ -182,14 +223,15 @@ func (svc *Service) Upsert(ctx context.Context, tenantId int64, item media_store tbl := table.Medias stmt := tbl. - INSERT(tbl.TenantID, tbl.Hash, tbl.Title, tbl.Price, tbl.Resources, tbl.Publish). - VALUES(Int(tenantId), String(item.Hash), String(item.Name), Int(item.Price()), Json(resources.MustValue()), Bool(true)). + INSERT(tbl.TenantID, tbl.Hash, tbl.Title, tbl.Price, tbl.Duration, tbl.Resources, tbl.Publish). + VALUES(Int(tenantId), String(item.Hash), String(item.Name), Int(item.Price()), Int(item.Duration), Json(resources.MustValue()), Bool(true)). ON_CONFLICT(tbl.Hash). DO_UPDATE( SET( tbl.Title.SET(String(item.Name)), tbl.Price.SET(Int(item.Price())), tbl.Resources.SET(Json(resources.MustValue())), + tbl.Duration.SET(Int(item.Duration)), tbl.Publish.SET(Bool(true)), tbl.UpdatedAt.SET(TimestampT(time.Now())), ), diff --git a/backend/pkg/db/pagination.go b/backend/pkg/db/pagination.go index 3a00cd6..01f448c 100644 --- a/backend/pkg/db/pagination.go +++ b/backend/pkg/db/pagination.go @@ -1,7 +1,7 @@ package db type Pagination struct { - Offset string `json:"offset"` + Offset string `json:"offset,omitempty"` OffsetID int64 `json:"-"` Action int `json:"action"` // action: 0 :加载更多 1:刷新 } diff --git a/backend/pkg/pg/media.go b/backend/pkg/pg/media.go index 7d03fd2..ef03db1 100644 --- a/backend/pkg/pg/media.go +++ b/backend/pkg/pg/media.go @@ -18,7 +18,7 @@ type MediaType string type MediaResources []MediaType -func (x MediaResources) Scan(value interface{}) (err error) { +func (x *MediaResources) Scan(value interface{}) (err error) { switch v := value.(type) { case string: return json.Unmarshal([]byte(v), &x) diff --git a/backend/request.http b/backend/request.http new file mode 100644 index 0000000..3af00cf --- /dev/null +++ b/backend/request.http @@ -0,0 +1,15 @@ +@Token=Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcGVuX2lkIjoib01MYTV0eUoydlJIYS1ISTRDTUVrSHp0cTNlVSIsInRlbmFudCI6InlwbCIsInVzZXJfaWQiOjEsInRlbmFudF9pZCI6MSwiZXhwIjoxNzM0MzQ5NDA3LCJuYmYiOjE3MzM3NDQ1OTd9.iQtyxfFDXht4mpCYSllv7opFjYuUHO22WwBl7hAzQYw +@host=http://localhost:9600 + +### Get Medias +POST {{host}}/v1/medias +Content-Type: application/json +Authorization: {{ Token }} + +{ +} + +### Get Media +GET {{host}}/v1/medias/f464a6641a60e2722e4042db8fad2813 +Content-Type: application/json +Authorization: {{ Token }} \ No newline at end of file