feat: add list profile

This commit is contained in:
Rogee
2024-12-09 20:33:35 +08:00
parent af8a8e9469
commit 0c9cb498d5
12 changed files with 125 additions and 40 deletions

BIN
backend/__debug_bin1422413187 Executable file

Binary file not shown.

View File

@@ -3,7 +3,7 @@ Mode = "development"
BaseURI = "https://qvyun.mp.jdwan.com" BaseURI = "https://qvyun.mp.jdwan.com"
[Http] [Http]
Port = 3000 Port = 9600
[Swagger] [Swagger]
BaseRoute = "doc" BaseRoute = "doc"
@@ -34,4 +34,5 @@ Salt = "LiXi.Y@140202"
[Storage] [Storage]
Type = "local" Type = "local"
Path = "/projects/mp-qvyun/backend/fixtures/processed" # Path = "/projects/mp-qvyun/backend/fixtures/processed"
Path = "/mnt/yangpingliang/publish/processed"

View File

@@ -72,6 +72,7 @@ CREATE TABLE
tenant_id INT8 NOT NULL, tenant_id INT8 NOT NULL,
title VARCHAR(198) NOT NULL, title VARCHAR(198) NOT NULL,
description VARCHAR(198) NOT NULL default '', description VARCHAR(198) NOT NULL default '',
duration INT8 NOT NULL default 0,
price INT8 NOT NULL default 0, price INT8 NOT NULL default 0,
discount INT8 NOT NULL default 100, discount INT8 NOT NULL default 100,
publish BOOL NOT NULL default false, publish BOOL NOT NULL default false,

View File

@@ -18,6 +18,7 @@ type Medias struct {
TenantID int64 `json:"tenant_id"` TenantID int64 `json:"tenant_id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Duration int64 `json:"duration"`
Price int64 `json:"price"` Price int64 `json:"price"`
Discount int64 `json:"discount"` Discount int64 `json:"discount"`
Publish bool `json:"publish"` Publish bool `json:"publish"`

View File

@@ -22,6 +22,7 @@ type mediasTable struct {
TenantID postgres.ColumnInteger TenantID postgres.ColumnInteger
Title postgres.ColumnString Title postgres.ColumnString
Description postgres.ColumnString Description postgres.ColumnString
Duration postgres.ColumnInteger
Price postgres.ColumnInteger Price postgres.ColumnInteger
Discount postgres.ColumnInteger Discount postgres.ColumnInteger
Publish postgres.ColumnBool Publish postgres.ColumnBool
@@ -73,14 +74,15 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
TenantIDColumn = postgres.IntegerColumn("tenant_id") TenantIDColumn = postgres.IntegerColumn("tenant_id")
TitleColumn = postgres.StringColumn("title") TitleColumn = postgres.StringColumn("title")
DescriptionColumn = postgres.StringColumn("description") DescriptionColumn = postgres.StringColumn("description")
DurationColumn = postgres.IntegerColumn("duration")
PriceColumn = postgres.IntegerColumn("price") PriceColumn = postgres.IntegerColumn("price")
DiscountColumn = postgres.IntegerColumn("discount") DiscountColumn = postgres.IntegerColumn("discount")
PublishColumn = postgres.BoolColumn("publish") PublishColumn = postgres.BoolColumn("publish")
ResourcesColumn = postgres.StringColumn("resources") ResourcesColumn = postgres.StringColumn("resources")
CreatedAtColumn = postgres.TimestampColumn("created_at") CreatedAtColumn = postgres.TimestampColumn("created_at")
UpdatedAtColumn = postgres.TimestampColumn("updated_at") UpdatedAtColumn = postgres.TimestampColumn("updated_at")
allColumns = postgres.ColumnList{IDColumn, 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, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn} mutableColumns = postgres.ColumnList{HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, DurationColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn}
) )
return mediasTable{ return mediasTable{
@@ -92,6 +94,7 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
TenantID: TenantIDColumn, TenantID: TenantIDColumn,
Title: TitleColumn, Title: TitleColumn,
Description: DescriptionColumn, Description: DescriptionColumn,
Duration: DurationColumn,
Price: PriceColumn, Price: PriceColumn,
Discount: DiscountColumn, Discount: DiscountColumn,
Publish: PublishColumn, Publish: PublishColumn,

View File

@@ -6,7 +6,7 @@ import (
"backend/providers/jwt" "backend/providers/jwt"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
. "github.com/spf13/cast" log "github.com/sirupsen/logrus"
) )
// @provider // @provider
@@ -18,6 +18,7 @@ type Controller struct {
func (c *Controller) List(ctx fiber.Ctx) error { func (c *Controller) List(ctx fiber.Ctx) error {
filter := ListFilter{} filter := ListFilter{}
if err := ctx.Bind().Body(&filter); err != nil { if err := ctx.Bind().Body(&filter); err != nil {
log.WithError(err).Error("parse body failed")
return ctx.Status(fiber.StatusBadRequest).JSON(errorx.RequestParseError) return ctx.Status(fiber.StatusBadRequest).JSON(errorx.RequestParseError)
} }
claim := ctx.Locals(consts.CtxKeyClaim).(*jwt.Claims) claim := ctx.Locals(consts.CtxKeyClaim).(*jwt.Claims)
@@ -32,15 +33,24 @@ func (c *Controller) List(ctx fiber.Ctx) error {
// Show // Show
func (c *Controller) Show(ctx fiber.Ctx) error { func (c *Controller) Show(ctx fiber.Ctx) error {
id := ToInt64(ctx.Params("id")) hash := ctx.Params("hash")
tenantId, userId := ToInt64(ctx.Locals("tenantId")), ToInt64(ctx.Locals("userId")) 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 { if err != nil {
log.WithField("action", "medias.Show").WithError(err).Error("GetMediaByHash")
return ctx.Status(fiber.StatusInternalServerError).JSON(errorx.InternalError) 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 // Audio

View File

@@ -1,17 +1,28 @@
package medias package medias
import ( import (
"backend/database/models/qvyun/public/model" "time"
"backend/pkg/db" "backend/pkg/db"
"backend/pkg/pg"
) )
type ListFilter struct { type ListFilter struct {
db.Pagination db.Pagination
Title *string `json:"title"` Title *string `json:"title,omitempty"`
Bought *bool `json:"bought"` Bought *bool `json:"bought,omitempty"`
} }
type ListItem struct { type ListItem struct {
model.Medias ID int64 `json:"-"`
Bought bool `json:"bought"` 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"`
} }

View File

@@ -25,5 +25,6 @@ func (r *Router) Prepare() error {
func (r *Router) Register(router fiber.Router) { func (r *Router) Register(router fiber.Router) {
group := router.Group(r.Name()) group := router.Group(r.Name())
group.Get("", r.controller.List) group.Post("", r.controller.List)
group.Get(":hash", r.controller.Show)
} }

View File

@@ -7,6 +7,7 @@ import (
"time" "time"
"backend/common/media_store" "backend/common/media_store"
"backend/database/models/qvyun/public/model"
"backend/database/models/qvyun/public/table" "backend/database/models/qvyun/public/table"
"backend/pkg/path" "backend/pkg/path"
"backend/pkg/pg" "backend/pkg/pg"
@@ -30,9 +31,31 @@ func (svc *Service) Prepare() error {
return nil 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 // GetByID
func (svc *Service) GetByID(ctx context.Context, tenantId, userId, id int64) (*ListItem, error) { func (svc *Service) GetMediaByID(ctx context.Context, tenantId, userId, id int64) (*model.Medias, error) {
log := svc.log.WithField("method", "GetByID") log := svc.log.WithField("method", "GetMediaByID")
tbl := table.Medias tbl := table.Medias
stmt := tbl.SELECT(tbl.AllColumns).WHERE( 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)), tbl.TenantID.EQ(Int(tenantId)),
), ),
) )
log.Debug(stmt.Sql()) log.Debug(stmt.DebugSql())
var media ListItem var m model.Medias
if err := stmt.QueryContext(ctx, svc.db, &media); err != nil { if err := stmt.QueryContext(ctx, svc.db, &m); err != nil {
return nil, errors.Wrap(err, "query media by id") return nil, errors.Wrap(err, "query media by id")
} }
return &m, nil
}
// todo: resources func (svc *Service) ModelToListItem(ctx context.Context, m *model.Medias) *ListItem {
return &ListItem{
var err error ID: m.ID,
media.Bought, err = svc.HasUserBought(ctx, tenantId, userId, media.ID) Hash: m.Hash,
if err != nil { Title: m.Title,
return nil, errors.Wrap(err, "check user bought") 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 // List
@@ -102,14 +131,24 @@ func (svc *Service) List(ctx context.Context, tenantId, userId int64, filter *Li
} }
log.Debug(stmt.DebugSql()) log.Debug(stmt.DebugSql())
var dest []ListItem var dest []model.Medias
if err := stmt.QueryContext(ctx, svc.db, &dest); err != nil { if err := stmt.QueryContext(ctx, svc.db, &dest); err != nil {
return nil, errors.Wrap(err, "query medias") return nil, errors.Wrap(err, "query medias")
} }
items := lo.Map(dest, func(item ListItem, _ int) ListItem { items := lo.Map(dest, func(m model.Medias, _ int) ListItem {
if lo.Contains(boughtIDs, item.ID) { item := ListItem{
item.Bought = true 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 return item
}) })
@@ -145,7 +184,7 @@ func (svc *Service) HasUserBought(ctx context.Context, tenantId, userId, mediaId
tbl := table.UserMedias tbl := table.UserMedias
stmt := tbl. stmt := tbl.
SELECT(tbl.MediaID). SELECT(COUNT(tbl.MediaID).AS("cnt")).
WHERE( WHERE(
tbl.TenantID.EQ(Int(tenantId)).AND( tbl.TenantID.EQ(Int(tenantId)).AND(
tbl.UserID.EQ(Int(userId)).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()) log.Debug(stmt.DebugSql())
var mediaID int64 var m struct {
if err := stmt.QueryContext(ctx, svc.db, &mediaID); err != nil { Cnt int64
}
if err := stmt.QueryContext(ctx, svc.db, &m); err != nil {
return false, errors.Wrap(err, "query user bought media") return false, errors.Wrap(err, "query user bought media")
} }
return mediaID > 0, nil return m.Cnt > 0, nil
} }
// Upsert // Upsert
@@ -182,14 +223,15 @@ func (svc *Service) Upsert(ctx context.Context, tenantId int64, item media_store
tbl := table.Medias tbl := table.Medias
stmt := tbl. stmt := tbl.
INSERT(tbl.TenantID, tbl.Hash, tbl.Title, tbl.Price, tbl.Resources, tbl.Publish). 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()), Json(resources.MustValue()), Bool(true)). VALUES(Int(tenantId), String(item.Hash), String(item.Name), Int(item.Price()), Int(item.Duration), Json(resources.MustValue()), Bool(true)).
ON_CONFLICT(tbl.Hash). ON_CONFLICT(tbl.Hash).
DO_UPDATE( DO_UPDATE(
SET( SET(
tbl.Title.SET(String(item.Name)), tbl.Title.SET(String(item.Name)),
tbl.Price.SET(Int(item.Price())), tbl.Price.SET(Int(item.Price())),
tbl.Resources.SET(Json(resources.MustValue())), tbl.Resources.SET(Json(resources.MustValue())),
tbl.Duration.SET(Int(item.Duration)),
tbl.Publish.SET(Bool(true)), tbl.Publish.SET(Bool(true)),
tbl.UpdatedAt.SET(TimestampT(time.Now())), tbl.UpdatedAt.SET(TimestampT(time.Now())),
), ),

View File

@@ -1,7 +1,7 @@
package db package db
type Pagination struct { type Pagination struct {
Offset string `json:"offset"` Offset string `json:"offset,omitempty"`
OffsetID int64 `json:"-"` OffsetID int64 `json:"-"`
Action int `json:"action"` // action: 0 :加载更多 1:刷新 Action int `json:"action"` // action: 0 :加载更多 1:刷新
} }

View File

@@ -18,7 +18,7 @@ type MediaType string
type MediaResources []MediaType type MediaResources []MediaType
func (x MediaResources) Scan(value interface{}) (err error) { func (x *MediaResources) Scan(value interface{}) (err error) {
switch v := value.(type) { switch v := value.(type) {
case string: case string:
return json.Unmarshal([]byte(v), &x) return json.Unmarshal([]byte(v), &x)

15
backend/request.http Normal file
View File

@@ -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 }}