init: web_hgcg_server 初始化提交

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 16:44:55 +08:00
co-authored by Claude Opus 4.7
commit 189556e702
627 changed files with 126228 additions and 0 deletions
+144
View File
@@ -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},
}
}
+333
View File
@@ -0,0 +1,333 @@
package playletmod
import (
"context"
"fmt"
"time"
"videoweb/pub/common/log"
"videoweb/pub/common/srvdef"
"videoweb/pub/mongos"
"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
}
+126
View File
@@ -0,0 +1,126 @@
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
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.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"
}
+163
View File
@@ -0,0 +1,163 @@
package playletmod
import (
"strconv"
"strings"
"time"
"videoweb/pub/pubdef"
)
// 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, title string, updateStatus int, totalEpisode int64) string {
title = strings.TrimSpace(title)
if title == "" {
return ""
}
desc = strings.TrimSpace(desc)
siteName := pubdef.AppName
if updateStatus == UpdateStatusDone {
return desc + siteName + "提供《" + title + "》全集在线观看、剧情介绍及完整选集,并持续更新相关热门短剧。"
}
return desc + siteName + "提供《" + title + "》最新剧集、剧情介绍及在线观看,当前更新至第" + 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"` // 标题
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)
}