package medias import ( "context" "database/sql" "path/filepath" "time" "backend/common/media_store" "backend/database/models/qvyun/public/model" "backend/database/models/qvyun/public/table" "backend/pkg/path" "backend/pkg/pg" "backend/providers/storage" . "github.com/go-jet/jet/v2/postgres" "github.com/pkg/errors" "github.com/samber/lo" "github.com/sirupsen/logrus" ) // @provider:except type Service struct { db *sql.DB storageConfig *storage.Config log *logrus.Entry `inject:"false"` } func (svc *Service) Prepare() error { svc.log = logrus.WithField("module", "medias.service") 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) 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( tbl.ID.EQ(Int(id)).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.Wrap(err, "query media by id") } return &m, nil } 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, } } // List func (svc *Service) List(ctx context.Context, tenantId, userId int64, filter *ListFilter) ([]ListItem, error) { log := svc.log.WithField("method", "List") boughtIDs, err := svc.GetUserBoughtMedias(ctx, tenantId, userId) if err != nil { return nil, errors.Wrap(err, "get user bought medias") } tbl := table.Medias stmt := tbl. SELECT(tbl.AllColumns). WHERE(tbl.TenantID.EQ(Int(tenantId))). ORDER_BY(tbl.ID.DESC()) if filter.Title != nil && *filter.Title != "" { stmt = stmt.WHERE(tbl.Title.LIKE(String("%" + *filter.Title + "%"))) } if filter.Bought != nil && *filter.Bought { if len(boughtIDs) > 0 { stmt = stmt. WHERE(tbl.ID.IN(lo.Map(boughtIDs, func(item int64, _ int) Expression { return Int(item) })...)) } } else { stmt = stmt.WHERE(tbl.Publish.EQ(Bool(true))) } if filter.OffsetID > 0 { if filter.Action == 0 { stmt = stmt.WHERE(tbl.ID.GT(Int(filter.OffsetID))) } if filter.Action == 1 { stmt = stmt.WHERE(tbl.ID.LT(Int(filter.OffsetID))) stmt = stmt.LIMIT(10) } } else { stmt = stmt.LIMIT(10) } log.Debug(stmt.DebugSql()) 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(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 }) return items, nil } // GetUserBoughtMedias func (svc *Service) GetUserBoughtMedias(ctx context.Context, tenant int64, userID int64) ([]int64, error) { log := svc.log.WithField("method", "GetUserBoughtMedias") tbl := table.UserMedias stmt := tbl. SELECT(tbl.MediaID). WHERE( tbl.TenantID.EQ(Int(tenant)).AND( tbl.UserID.EQ(Int(userID)), ), ) log.Debug(stmt.Sql()) var mediaIDs []int64 if err := stmt.QueryContext(ctx, svc.db, &mediaIDs); err != nil { return nil, errors.Wrap(err, "query user bought medias") } return mediaIDs, nil } // HasUserBought func (svc *Service) HasUserBought(ctx context.Context, tenantId, userId, mediaId int64) (bool, error) { log := svc.log.WithField("method", "HasUserBought") tbl := table.UserMedias stmt := tbl. SELECT(COUNT(tbl.MediaID).AS("cnt")). WHERE( tbl.TenantID.EQ(Int(tenantId)).AND( tbl.UserID.EQ(Int(userId)).AND( tbl.MediaID.EQ(Int(mediaId)), ), ), ) log.Debug(stmt.DebugSql()) 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 m.Cnt > 0, nil } // Upsert func (svc *Service) Upsert(ctx context.Context, tenantId int64, item media_store.VideoInfo) error { log := svc.log.WithField("method", "Upsert") resources := pg.MediaResources{} if path.DirExists(filepath.Join(svc.storageConfig.Path, item.Hash, pg.MediaTypeVideo.String())) { resources = append(resources, pg.MediaTypeVideo) } if path.DirExists(filepath.Join(svc.storageConfig.Path, item.Hash, pg.MediaTypeAudio.String())) { resources = append(resources, pg.MediaTypeAudio) } if path.DirExists(filepath.Join(svc.storageConfig.Path, item.Hash, pg.MediaTypePdf.String())) { resources = append(resources, pg.MediaTypePdf) } tbl := table.Medias stmt := tbl. 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())), ), ) log.Debug(stmt.DebugSql()) if _, err := stmt.ExecContext(ctx, svc.db); err != nil { return errors.Wrapf(err, "upsert media: %s %s", item.Hash, item.Name) } return nil }