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"
[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"

View File

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

View File

@@ -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"`

View File

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

View File

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

View File

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

View File

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

View File

@@ -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())),
),

View File

@@ -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:刷新
}

View File

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

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