feat: 从黄果 NET hgdjnet-server 9bfbafe 导入 HKDJ 官网后端
来源:hgdjnet-server 9bfbafe10d417deea959bd53117bc88a10e60a83 项目身份 HKDJ / JHA-216,独立官网仓库,Jenkins 守卫键 hkdjnet 测试目录 /server/hkdj_net,App/Web API 30331/30332 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
package playletmod
|
||||
|
||||
import "go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
const categoryRankField = "_categoryRank"
|
||||
|
||||
// PlayletCategoryBind 短剧绑定的分类及该分类下的排序
|
||||
type PlayletCategoryBind struct {
|
||||
Id int64 `json:"id" bson:"id"` // 分类ID
|
||||
Rank int64 `json:"rank" bson:"rank"` // 该分类下的排序,越大越靠前
|
||||
}
|
||||
|
||||
// NormalizeCategoryBinds 去重并丢弃无效分类ID,保留首次出现的排序
|
||||
func NormalizeCategoryBinds(binds []PlayletCategoryBind) []PlayletCategoryBind {
|
||||
result := make([]PlayletCategoryBind, 0, len(binds))
|
||||
seen := make(map[int64]struct{}, len(binds))
|
||||
for _, bind := range binds {
|
||||
if bind.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[bind.Id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[bind.Id] = struct{}{}
|
||||
result = append(result, PlayletCategoryBind{Id: bind.Id, Rank: bind.Rank})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CategoryIdsFromBinds 从分类绑定中提取分类ID
|
||||
func CategoryIdsFromBinds(binds []PlayletCategoryBind) []int64 {
|
||||
result := make([]int64, 0, len(binds))
|
||||
for _, bind := range binds {
|
||||
if bind.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
result = append(result, bind.Id)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CategoryBindsFromIds 仅有分类ID时补默认排序0
|
||||
func CategoryBindsFromIds(ids []int64) []PlayletCategoryBind {
|
||||
result := make([]PlayletCategoryBind, 0, len(ids))
|
||||
seen := make(map[int64]struct{}, len(ids))
|
||||
for _, id := range ids {
|
||||
if id <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
result = append(result, PlayletCategoryBind{Id: id})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MergeCategoryBindsByIds 按新分类ID集合保留旧排序,新增分类rank为0
|
||||
func MergeCategoryBindsByIds(exist []PlayletCategoryBind, ids []int64) []PlayletCategoryBind {
|
||||
rankByID := make(map[int64]int64, len(exist))
|
||||
for _, bind := range exist {
|
||||
if bind.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
rankByID[bind.Id] = bind.Rank
|
||||
}
|
||||
result := make([]PlayletCategoryBind, 0, len(ids))
|
||||
seen := make(map[int64]struct{}, len(ids))
|
||||
for _, id := range ids {
|
||||
if id <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
result = append(result, PlayletCategoryBind{Id: id, Rank: rankByID[id]})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CategoryRankOf 取指定分类下的排序,未绑定返回0
|
||||
func CategoryRankOf(binds []PlayletCategoryBind, categoryId int64) int64 {
|
||||
if categoryId <= 0 {
|
||||
return 0
|
||||
}
|
||||
for _, bind := range binds {
|
||||
if bind.Id == categoryId {
|
||||
return bind.Rank
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// EffectiveCategoryBinds 历史数据只有categoryIds时补默认绑定,便于编辑回显
|
||||
func EffectiveCategoryBinds(item *Playlet) []PlayletCategoryBind {
|
||||
if item == nil {
|
||||
return make([]PlayletCategoryBind, 0)
|
||||
}
|
||||
if len(item.CategoryBinds) > 0 {
|
||||
return item.CategoryBinds
|
||||
}
|
||||
return CategoryBindsFromIds(item.CategoryIds)
|
||||
}
|
||||
|
||||
// CategoryRankAddFields 聚合取出当前分类的排序字段,供分类列表排序
|
||||
func CategoryRankAddFields(categoryId int64) bson.M {
|
||||
return bson.M{
|
||||
categoryRankField: bson.M{
|
||||
"$let": bson.M{
|
||||
"vars": bson.M{
|
||||
"hit": bson.M{
|
||||
"$first": bson.M{
|
||||
"$filter": bson.M{
|
||||
"input": bson.M{"$ifNull": bson.A{"$categoryBinds", bson.A{}}},
|
||||
"as": "b",
|
||||
"cond": bson.M{"$eq": bson.A{"$$b.id", categoryId}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"in": bson.M{"$ifNull": bson.A{"$$hit.rank", 0}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CategoryRankSort 分类短剧排序:当前分类rank降序,再按上架时间和ID
|
||||
func CategoryRankSort() bson.D {
|
||||
return bson.D{
|
||||
{Key: categoryRankField, Value: -1},
|
||||
{Key: "onlineAt", Value: -1},
|
||||
{Key: "_id", Value: -1},
|
||||
}
|
||||
}
|
||||
|
||||
// CategoryRankAdminSort 后台按分类查询时的排序:当前分类rank降序
|
||||
func CategoryRankAdminSort() bson.D {
|
||||
return bson.D{
|
||||
{Key: categoryRankField, Value: -1},
|
||||
{Key: "_id", Value: -1},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
package playletmod
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hkdj-web-server/pub/common/log"
|
||||
"hkdj-web-server/pub/common/srvdef"
|
||||
"hkdj-web-server/pub/mongos"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
var coll *mongos.Collection
|
||||
|
||||
// Init 初始化 playlet 集合和常用查询索引
|
||||
func Init(ctx context.Context) {
|
||||
coll = mongos.Shop.Coll(TN.Playlet)
|
||||
initIndex(ctx)
|
||||
}
|
||||
|
||||
func initIndex(ctx context.Context) {
|
||||
indexes := []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.ThemeIds, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.ThemeIds, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.CountCollect, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.ThemeIds, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.UpdatedAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.TagIds, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.UpdatedAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.CategoryIds, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.UpdatedAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.IsFeatured, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.ShowOnBanner, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.TopRank, Value: -1},
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.IsRecommend, Value: 1},
|
||||
{Key: FN.Playlet.RecommendRank, Value: -1},
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.CountCollect, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.CountBrowse, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.HeatValue, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.OnlineAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.UpdatedAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.Slug, Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.MediaCenterId, Value: 1},
|
||||
},
|
||||
Options: options.Index().
|
||||
SetUnique(true).
|
||||
SetPartialFilterExpression(bson.M{
|
||||
FN.Playlet.MediaCenterId: bson.M{"$gt": 0},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.OperateType, Value: 1},
|
||||
{Key: FN.Playlet.Status, Value: 1},
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: FN.Playlet.ContentType, Value: 1},
|
||||
{Key: FN.Playlet.CreatedAt, Value: -1},
|
||||
{Key: FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
Options: options.Index().
|
||||
SetPartialFilterExpression(bson.M{
|
||||
FN.Playlet.MediaCenterId: bson.M{"$gt": 0},
|
||||
}),
|
||||
},
|
||||
}
|
||||
if _, err := coll.CreateIndex(ctx, indexes); err != nil {
|
||||
panic(fmt.Sprintf("(playletmod:initIndex) Create Index failed! err:%+v:", err))
|
||||
}
|
||||
}
|
||||
|
||||
// InsertOnePlaylet 插入一条短剧数据
|
||||
func InsertOnePlaylet(ctx context.Context, doc *Playlet) *srvdef.SrvErr {
|
||||
if doc.Id == 0 {
|
||||
doc.Id = mongos.Lid.Playlet.Get()
|
||||
}
|
||||
now := time.Now()
|
||||
if doc.CreatedAt.IsZero() {
|
||||
doc.CreatedAt = now
|
||||
}
|
||||
doc.UpdatedAt = now
|
||||
if _, err := coll.InsertOne(ctx, doc); err != nil {
|
||||
log.Warn("playletmod:InsertOnePlaylet failed", log.Any("doc", doc), log.E(err))
|
||||
return srvdef.New(srvdef.ErrDbInsertError, "", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelOnePlaylet 删除一条短剧数据
|
||||
func DelOnePlaylet(ctx context.Context, filter bson.M) (int64, *srvdef.SrvErr) {
|
||||
count, err := coll.DeleteOne(ctx, filter)
|
||||
if err != nil {
|
||||
log.Warn("playletmod:DelOnePlaylet failed", log.E(err), log.Any("filter", filter))
|
||||
return 0, srvdef.New(srvdef.ErrDbDeleteError, "", "")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// DelManyPlaylet 按条件批量删除短剧数据
|
||||
func DelManyPlaylet(ctx context.Context, filter bson.M) (int64, *srvdef.SrvErr) {
|
||||
count, err := coll.DeleteMany(ctx, filter)
|
||||
if err != nil {
|
||||
log.Warn("playletmod:DelManyPlaylet failed", log.E(err), log.Any("filter", filter))
|
||||
return 0, srvdef.New(srvdef.ErrDbDeleteError, "", "")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// FindOnePlaylet 查询一条短剧数据
|
||||
func FindOnePlaylet(ctx context.Context, filter bson.M, opts ...*options.FindOneOptions) (*Playlet, *srvdef.SrvErr) {
|
||||
var doc Playlet
|
||||
if err := coll.FindOneEx(ctx, &doc, filter, opts...); err != nil {
|
||||
if err != mongo.ErrNoDocuments {
|
||||
log.Warn("playletmod:FindOnePlaylet failed", log.E(err), log.Any("filter", filter))
|
||||
return nil, srvdef.New(srvdef.ErrDbQueryError, "", "")
|
||||
}
|
||||
return nil, srvdef.New(srvdef.ErrNoData, "", "")
|
||||
}
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
// FindManyPlaylet 按条件查询多条短剧数据
|
||||
func FindManyPlaylet(ctx context.Context, filter bson.M, opts ...*options.FindOptions) (PlayletList, *srvdef.SrvErr) {
|
||||
data := make(PlayletList, 0)
|
||||
if err := coll.Find(ctx, &data, filter, opts...); err != nil {
|
||||
log.Warn("playletmod:FindManyPlaylet failed", log.E(err), log.Any("filter", filter))
|
||||
return nil, srvdef.New(srvdef.ErrDbQueryError, "", "")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// FindManyPlayletByCategoryRank 按指定分类的排序分页查询短剧
|
||||
func FindManyPlayletByCategoryRank(ctx context.Context, filter bson.M, categoryId int64, skip, limit int64, sort bson.D, projection bson.D) (PlayletList, *srvdef.SrvErr) {
|
||||
if skip < 0 {
|
||||
skip = 0
|
||||
}
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
if sort == nil {
|
||||
sort = CategoryRankSort()
|
||||
}
|
||||
pipeline := []bson.M{
|
||||
{"$match": filter},
|
||||
{"$addFields": CategoryRankAddFields(categoryId)},
|
||||
{"$sort": sort},
|
||||
{"$skip": skip},
|
||||
{"$limit": limit},
|
||||
}
|
||||
pipeline = append(pipeline, bson.M{"$unset": categoryRankField})
|
||||
if len(projection) > 0 {
|
||||
proj := bson.M{}
|
||||
for _, field := range projection {
|
||||
proj[field.Key] = field.Value
|
||||
}
|
||||
pipeline = append(pipeline, bson.M{"$project": proj})
|
||||
}
|
||||
list := make(PlayletList, 0)
|
||||
if err := AggregatePlaylet(ctx, &list, pipeline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// CountPlaylet 统计满足条件的短剧数量
|
||||
func CountPlaylet(ctx context.Context, filter bson.M, opts ...*options.CountOptions) (int64, *srvdef.SrvErr) {
|
||||
count, err := coll.Count(ctx, filter, opts...)
|
||||
if err != nil {
|
||||
log.Warn("playletmod:CountPlaylet failed", log.E(err), log.Any("filter", filter))
|
||||
return 0, srvdef.New(srvdef.ErrDbQueryError, "", "")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// UpdateOnePlaylet 更新一条短剧数据
|
||||
func UpdateOnePlaylet(ctx context.Context, filter bson.M, update bson.M) *srvdef.SrvErr {
|
||||
result, err := coll.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
log.Warn("playletmod:UpdateOnePlaylet failed", log.E(err), log.Any("filter", filter))
|
||||
return srvdef.New(srvdef.ErrDbUpdateError, "", "")
|
||||
}
|
||||
if result.MatchedCount != 1 {
|
||||
return srvdef.New(srvdef.ErrNoData, "", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindOneAndUpdatePlaylet 更新一条短剧数据并返回更新后的数据
|
||||
func FindOneAndUpdatePlaylet(ctx context.Context, filter bson.M, update bson.M, opts ...*options.FindOneAndUpdateOptions) (*Playlet, *srvdef.SrvErr) {
|
||||
var doc Playlet
|
||||
if err := coll.FindOneAndUpdate(ctx, &doc, filter, update, opts...); err != nil {
|
||||
if err != mongo.ErrNoDocuments {
|
||||
log.Warn("更新并查询短剧数据失败", log.E(err), log.Any("filter", filter))
|
||||
return nil, srvdef.New(srvdef.ErrDbUpdateError, "", "")
|
||||
}
|
||||
return nil, srvdef.New(srvdef.ErrNoData, "", "")
|
||||
}
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
// UpdateManyPlaylet 批量更新短剧数据
|
||||
func UpdateManyPlaylet(ctx context.Context, filter bson.M, update bson.M) *srvdef.SrvErr {
|
||||
result, err := coll.UpdateMany(ctx, filter, update)
|
||||
if err != nil {
|
||||
log.Warn("批量更新短剧数据失败", log.E(err), log.Any("filter", filter))
|
||||
return srvdef.New(srvdef.ErrDbUpdateError, "", "")
|
||||
}
|
||||
if result.MatchedCount == 0 {
|
||||
return srvdef.New(srvdef.ErrNoData, "", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BulkWritePlaylet 批量写入短剧更新操作。
|
||||
func BulkWritePlaylet(ctx context.Context, docs []mongo.WriteModel, opts ...*options.BulkWriteOptions) *srvdef.SrvErr {
|
||||
if len(docs) == 0 {
|
||||
return nil
|
||||
}
|
||||
result, err := coll.BulkWrite(ctx, docs, opts...)
|
||||
if err != nil {
|
||||
log.Warn("批量写入短剧数据失败", log.E(err))
|
||||
return srvdef.New(srvdef.ErrDbUpdateError, "", "")
|
||||
}
|
||||
if result.MatchedCount == 0 {
|
||||
return srvdef.New(srvdef.ErrNoData, "", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AggregatePlaylet 聚合查询短剧
|
||||
func AggregatePlaylet(ctx context.Context, bind interface{}, pipeline []bson.M) *srvdef.SrvErr {
|
||||
if err := coll.Aggregate(ctx, bind, pipeline); err != nil {
|
||||
log.Warn("playletmod:AggregatePlaylet failed", log.E(err), log.Any("pipeline", pipeline))
|
||||
return srvdef.New(srvdef.ErrDbQueryError, "", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package playletmod
|
||||
|
||||
// TagPlaylet 保存 playlet 集合字段名
|
||||
type TagPlaylet struct {
|
||||
Id string
|
||||
MediaCenterId string
|
||||
MediaSource string
|
||||
MediaCreatedAt string
|
||||
MediaUpdatedAt string
|
||||
MediaSyncTime string
|
||||
Status string
|
||||
IsFeatured string
|
||||
IsOriginal string
|
||||
MediaBusiness string
|
||||
IsRecommend string
|
||||
ShowOnBanner string
|
||||
Banner string
|
||||
ReviewAt string
|
||||
OnlineAt string
|
||||
ThemeIds string
|
||||
CategoryIds string
|
||||
CategoryBinds string
|
||||
Title string
|
||||
PlayName string
|
||||
Description string
|
||||
Slug string
|
||||
Desc string
|
||||
Anchor string
|
||||
Cover string
|
||||
HorizontalCover string
|
||||
CompressedCover string
|
||||
CompressedHorizontalCover string
|
||||
Preview string
|
||||
TagIds string
|
||||
ContentType string
|
||||
OperateType string
|
||||
PayType string
|
||||
SingleCoins string
|
||||
TotalEpisode string
|
||||
UpdateStatus string
|
||||
TopRank string
|
||||
RecommendRank string
|
||||
CountComment string
|
||||
CountBuy string
|
||||
CountBrowse string
|
||||
HeatValue string
|
||||
CountCollect string
|
||||
CountLike string
|
||||
VirtualCountLike string
|
||||
CountDisLike string
|
||||
SeoInfo struct {
|
||||
Description string
|
||||
Keyworld string
|
||||
Title string
|
||||
}
|
||||
IndexNow string
|
||||
OperatorId string
|
||||
OperatorName string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}
|
||||
|
||||
var FN struct {
|
||||
Playlet TagPlaylet
|
||||
}
|
||||
|
||||
var TN struct {
|
||||
Playlet string
|
||||
}
|
||||
|
||||
func init() {
|
||||
FN.Playlet.Id = "_id"
|
||||
FN.Playlet.MediaCenterId = "mediaCenterId"
|
||||
FN.Playlet.MediaSource = "mediaSource"
|
||||
FN.Playlet.MediaCreatedAt = "mediaCreatedAt"
|
||||
FN.Playlet.MediaUpdatedAt = "mediaUpdatedAt"
|
||||
FN.Playlet.MediaSyncTime = "mediaSyncTime"
|
||||
FN.Playlet.Status = "status"
|
||||
FN.Playlet.IsFeatured = "isFeatured"
|
||||
FN.Playlet.IsOriginal = "isOriginal"
|
||||
FN.Playlet.MediaBusiness = "mediaBusiness"
|
||||
FN.Playlet.IsRecommend = "isRecommend"
|
||||
FN.Playlet.ShowOnBanner = "showOnBanner"
|
||||
FN.Playlet.Banner = "banner"
|
||||
FN.Playlet.ReviewAt = "reviewAt"
|
||||
FN.Playlet.OnlineAt = "onlineAt"
|
||||
FN.Playlet.ThemeIds = "themeIds"
|
||||
FN.Playlet.CategoryIds = "categoryIds"
|
||||
FN.Playlet.CategoryBinds = "categoryBinds"
|
||||
FN.Playlet.Title = "title"
|
||||
FN.Playlet.PlayName = "playName"
|
||||
FN.Playlet.Description = "description"
|
||||
FN.Playlet.Slug = "slug"
|
||||
FN.Playlet.Desc = "desc"
|
||||
FN.Playlet.Anchor = "anchor"
|
||||
FN.Playlet.Cover = "cover"
|
||||
FN.Playlet.HorizontalCover = "horizontalCover"
|
||||
FN.Playlet.CompressedCover = "compressedCover"
|
||||
FN.Playlet.CompressedHorizontalCover = "compressedHorizontalCover"
|
||||
FN.Playlet.Preview = "preview"
|
||||
FN.Playlet.TagIds = "tagIds"
|
||||
FN.Playlet.ContentType = "contentType"
|
||||
FN.Playlet.OperateType = "operateType"
|
||||
FN.Playlet.PayType = "payType"
|
||||
FN.Playlet.SingleCoins = "singleCoins"
|
||||
FN.Playlet.TotalEpisode = "totalEpisode"
|
||||
FN.Playlet.UpdateStatus = "updateStatus"
|
||||
FN.Playlet.TopRank = "topRank"
|
||||
FN.Playlet.RecommendRank = "recommendRank"
|
||||
FN.Playlet.CountComment = "countComment"
|
||||
FN.Playlet.CountBuy = "countBuy"
|
||||
FN.Playlet.CountBrowse = "countBrowse"
|
||||
FN.Playlet.HeatValue = "heatValue"
|
||||
FN.Playlet.CountCollect = "countCollect"
|
||||
FN.Playlet.CountLike = "countLike"
|
||||
FN.Playlet.VirtualCountLike = "virtualCountLike"
|
||||
FN.Playlet.CountDisLike = "countDisLike"
|
||||
FN.Playlet.SeoInfo.Description = "seoInfo.webDescription"
|
||||
FN.Playlet.SeoInfo.Keyworld = "seoInfo.webKeyworld"
|
||||
FN.Playlet.SeoInfo.Title = "seoInfo.webTitle"
|
||||
FN.Playlet.IndexNow = "indexNow"
|
||||
FN.Playlet.OperatorId = "operatorId"
|
||||
FN.Playlet.OperatorName = "operatorName"
|
||||
FN.Playlet.CreatedAt = "createdAt"
|
||||
FN.Playlet.UpdatedAt = "updatedAt"
|
||||
|
||||
TN.Playlet = "playlet"
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package playletmod
|
||||
|
||||
import (
|
||||
"hkdj-web-server/pub/pubdef"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ContentType 内容类型(与媒资 media_type 对应)
|
||||
type ContentType int32
|
||||
|
||||
const (
|
||||
ContentTypeTheater ContentType = 0 // 短剧(对应 media_type=5)
|
||||
ContentTypeAlbum ContentType = 1 // 普通短剧/影集(对应 media_type=6)
|
||||
ContentTypeComic ContentType = 2 // 漫画短剧(对应 media_type=4)
|
||||
ContentTypeAnime ContentType = 3 // 动漫(对应 media_type=1)
|
||||
ContentTypeManga ContentType = 4 // 漫画(对应 media_type=2)
|
||||
)
|
||||
|
||||
// Status 上架状态
|
||||
type Status int32
|
||||
|
||||
const (
|
||||
StatusDown Status = 0 // 下架
|
||||
StatusUp Status = 1 // 上架
|
||||
)
|
||||
|
||||
// PayType 支付方式(剧级默认,连载上架后新增剧集沿用)
|
||||
type PayType int32
|
||||
|
||||
const (
|
||||
PayTypeFree PayType = 0 // 免费
|
||||
PayTypeCoin PayType = 1 // 金币
|
||||
PayTypeVip PayType = 2 // VIP
|
||||
)
|
||||
|
||||
// OperateType 运营类型
|
||||
type OperateType int32
|
||||
|
||||
const (
|
||||
OperateTypePlaylet OperateType = 1 // 短剧
|
||||
OperateTypeComic OperateType = 2 // 漫剧
|
||||
)
|
||||
|
||||
// NormalizeOperateType 历史数据未写入时按短剧处理
|
||||
func NormalizeOperateType(t OperateType) OperateType {
|
||||
if t == OperateTypeComic {
|
||||
return OperateTypeComic
|
||||
}
|
||||
return OperateTypePlaylet
|
||||
}
|
||||
|
||||
// IsValidOperateType 校验运营类型
|
||||
func IsValidOperateType(t OperateType) bool {
|
||||
return t == OperateTypePlaylet || t == OperateTypeComic
|
||||
}
|
||||
|
||||
const (
|
||||
UpdateStatusSerial = 1 // 连载中
|
||||
UpdateStatusDone = 2 // 已完结
|
||||
)
|
||||
|
||||
const (
|
||||
MediaSourceDefault = "" // 普通(嘉华)媒资
|
||||
MediaSourceHG = "hg" // 黄果媒资
|
||||
)
|
||||
|
||||
// BuildPlayNameDescription 按剧名和连载状态生成 SEO Description。
|
||||
func BuildPlayNameDescription(desc, playName string, updateStatus int, totalEpisode int64) string {
|
||||
playName = strings.TrimSpace(playName)
|
||||
if playName == "" {
|
||||
return ""
|
||||
}
|
||||
desc = strings.TrimSpace(desc)
|
||||
siteName := pubdef.AppName
|
||||
if updateStatus == UpdateStatusDone {
|
||||
return desc + siteName + "提供《" + playName + "》全集在线观看、剧情介绍及完整选集,并持续更新相关热门短剧。"
|
||||
}
|
||||
return desc + siteName + "提供《" + playName + "》最新剧集、剧情介绍及在线观看,当前更新至第" + strconv.FormatInt(totalEpisode, 10) + "集,后续内容持续更新。"
|
||||
}
|
||||
|
||||
// Playlet @table: playlet
|
||||
//
|
||||
// Playlet 连载短剧集合模型
|
||||
type Playlet struct {
|
||||
Id int64 `json:"id" bson:"_id,omitempty"`
|
||||
MediaCenterId uint `json:"mediaCenterId" bson:"mediaCenterId"` // 媒资中心资源ID
|
||||
MediaSource string `json:"mediaSource" bson:"mediaSource"` // 媒资来源,空值为普通媒资
|
||||
MediaCreatedAt time.Time `json:"mediaCreatedAt" bson:"mediaCreatedAt"` // 媒资中心创建时间
|
||||
MediaUpdatedAt time.Time `json:"mediaUpdatedAt" bson:"mediaUpdatedAt"` // 媒资中心更新时间
|
||||
MediaSyncTime int64 `json:"mediaSyncTime" bson:"mediaSyncTime"` // 媒资中心增量同步时间点
|
||||
Status Status `json:"status" bson:"status"` // 状态: 0下架 1上架
|
||||
IsFeatured bool `json:"isFeatured" bson:"isFeatured"` // 是否爆剧(true-是,false-否)
|
||||
IsOriginal bool `json:"isOriginal" bson:"isOriginal"` // 是否原创:后台新增为true,媒资入库为false
|
||||
MediaBusiness string `json:"mediaBusiness" bson:"mediaBusiness"` // 媒资业务来源,按媒资标题前缀记录,如 CGDJ;空表示未标记
|
||||
IsRecommend bool `json:"isRecommend" bson:"isRecommend"` // 是否原创(推荐) true-推荐,false-不推荐
|
||||
ShowOnBanner bool `json:"showOnBanner" bson:"showOnBanner"` // 是否显示在首页banner
|
||||
Banner string `json:"banner" bson:"banner"` // banner图
|
||||
ReviewAt time.Time `json:"reviewAt" bson:"reviewAt"` // 审核时间
|
||||
OnlineAt time.Time `json:"onlineAt" bson:"onlineAt"` // 上架时间
|
||||
ThemeIds []int64 `json:"themeIds" bson:"themeIds"` // 题材ID集合
|
||||
CategoryIds []int64 `json:"categoryIds" bson:"categoryIds"` // 分类ID集合
|
||||
CategoryBinds []PlayletCategoryBind `json:"categoryBinds" bson:"categoryBinds"` // 分类绑定及排序
|
||||
Title string `json:"title" bson:"title"` // 标题
|
||||
PlayName string `json:"playName" bson:"playName"` // 剧名,后台维护
|
||||
Description string `json:"description" bson:"description"` // SEO Description,按剧名规则自动生成
|
||||
Slug string `json:"slug" bson:"slug"` // slug
|
||||
Desc string `json:"desc" bson:"desc"` // 简介
|
||||
Anchor string `json:"anchor" bson:"anchor"` // 作者/演员
|
||||
Cover string `json:"cover" bson:"cover"` // 封面原图
|
||||
HorizontalCover string `json:"horizontalCover" bson:"horizontalCover"` // 横版封面
|
||||
CompressedCover string `json:"compressedCover" bson:"compressedCover"` // 压缩封面,压缩成功才有值
|
||||
CompressedHorizontalCover string `json:"compressedHorizontalCover" bson:"compressedHorizontalCover"` // 压缩横版封面,压缩成功才有值
|
||||
Preview string `json:"preview" bson:"preview"` // 预告地址
|
||||
TagIds []int64 `json:"tagIds" bson:"tagIds"` // 标签ID
|
||||
ContentType ContentType `json:"contentType" bson:"contentType"` // 内容类型: 0短剧 1普通短剧/影集 2漫画短剧 3动漫 4漫画
|
||||
OperateType OperateType `json:"operateType" bson:"operateType"` // 运营类型: 1短剧 2漫剧
|
||||
PayType PayType `json:"payType" bson:"payType"` // 支付方式 0-免费 1-金币 2-VIP
|
||||
SingleCoins int64 `json:"singleCoins" bson:"singleCoins"` // 单集金币(支付方式为1-金币时候生效)
|
||||
TotalEpisode int64 `json:"totalEpisode" bson:"totalEpisode"` // 总集数
|
||||
UpdateStatus int `json:"updateStatus" bson:"updateStatus"` // 更新状态 1-连载中 2-已完结
|
||||
TopRank int64 `json:"topRank" bson:"topRank"` // 置顶排名
|
||||
RecommendRank int64 `json:"recommendRank" bson:"recommendRank"` // 推荐排序
|
||||
CountComment int64 `json:"countComment" bson:"countComment"` // 评论数
|
||||
CountBuy int64 `json:"countBuy" bson:"countBuy"` // 购买数
|
||||
CountBrowse int64 `json:"countBrowse" bson:"countBrowse"` // 浏览数
|
||||
HeatValue int64 `json:"heatValue" bson:"heatValue"` // 热度值
|
||||
CountCollect int64 `json:"countCollect" bson:"countCollect"` // 收藏数
|
||||
CountLike int64 `json:"countLike" bson:"countLike"` // 喜欢数
|
||||
VirtualCountLike int64 `json:"virtualCountLike" bson:"virtualCountLike"` // 虚拟喜欢数,后台配置
|
||||
CountDisLike int64 `json:"countDisLike" bson:"countDisLike"` // 不喜欢数
|
||||
SeoInfo pubdef.SeoValue `json:"seoInfo" bson:"seoInfo"` // Seo相关信息
|
||||
IndexNow int64 `json:"indexNow" bson:"indexNow"` // 搜索引擎推送标记 0:待提交 1:已入队/已提交
|
||||
OperatorId int64 `json:"operatorId" bson:"operatorId"` // 最后操作人ID(后台账号)
|
||||
OperatorName string `json:"operatorName" bson:"operatorName"` // 最后操作人名称
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` // 修改时间
|
||||
}
|
||||
|
||||
// PlayletList 短剧列表
|
||||
type PlayletList []*Playlet
|
||||
|
||||
// DisplayCover App 展示用封面:压缩图有值则优先,否则回退原图
|
||||
func (p *Playlet) DisplayCover() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if cover := strings.TrimSpace(p.CompressedCover); cover != "" {
|
||||
return cover
|
||||
}
|
||||
return strings.TrimSpace(p.Cover)
|
||||
}
|
||||
|
||||
// DisplayHorizontalCover App 展示用横版封面:压缩图有值则优先,否则回退原图
|
||||
func (p *Playlet) DisplayHorizontalCover() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if cover := strings.TrimSpace(p.CompressedHorizontalCover); cover != "" {
|
||||
return cover
|
||||
}
|
||||
return strings.TrimSpace(p.HorizontalCover)
|
||||
}
|
||||
Reference in New Issue
Block a user