Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+309
View File
@@ -0,0 +1,309 @@
package modulesectionmod
import (
"91porn-server/common/log"
"fmt"
"time"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/stderr"
"91porn-server/models"
"91porn-server/models/commod"
"91porn-server/models/v/moduleconfmod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
const table = models.Section
var mdb *db.MongoDB
func initIndex() {
coll := coll(nil)
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "sectionName", Value: 1}, {Key: "subModuleID", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{
{Key: "subModuleID", Value: 1}, {Key: "status", Value: 1}, {Key: "sort", Value: -1}, {Key: "createdAt", Value: -1},
},
},
}
if _, err := coll.CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
func InsertOne(section Section) error {
section.CreatedAt = time.Now()
section.UpdatedAt = section.CreatedAt
ok, err := checkLimit(section.SubModuleID)
if err != nil {
return err
}
if !ok {
return stderr.ModuleConfLimitExceed
}
_, err = coll(nil).InsertOne(section)
return err
}
func UpdateOne(set EditSelector) error {
subModule, err := moduleconfmod.GetByID(*set.SubModuleID)
if err != nil {
return err
}
total, err := CountBySubModuleID(*set.SubModuleID)
if err != nil {
return err
}
if (set.Status != nil && *set.Status == 1) && total >= int64(subModule.SectionLimit) {
return stderr.ModuleConfLimitExceed
}
cond := bson.M{"updatedAt": time.Now()}
if set.Status != nil {
cond["status"] = set.Status
}
if set.Tags != nil && len(*set.Tags) > 0 {
cond["tags"] = set.Tags
} else {
cond["tags"] = []string{}
}
if set.TagIds != nil && len(set.TagIds) > 0 {
cond["tagIds"] = set.TagIds
} else {
cond["tagIds"] = []primitive.ObjectID{}
}
if set.SectionName != nil {
cond["sectionName"] = set.SectionName
}
if set.SubModuleID != nil {
cond["subModuleID"] = set.SubModuleID
}
if set.ShowType != nil {
cond["showType"] = set.ShowType
}
if set.Sort != nil {
cond["sort"] = set.Sort
}
if set.SectionCover != nil {
cond["sectionCover"] = set.SectionCover
}
if set.Hot != nil {
cond["hot"] = set.Hot
}
_, err = coll(nil).UpdateOne(bson.M{"_id": set.ID}, bson.M{"$set": cond})
return err
}
// DeleteOne 删除一条数据,仅作删除标记-deletedAt
func DeleteOne(id primitive.ObjectID) error {
now := time.Now()
status := uint8(0)
set := EditSelector{ID: id, DeletedAt: &now, Status: &status}
return UpdateOne(set)
}
func DeleteById(id primitive.ObjectID) (err error) {
_, err = coll(nil).DeleteOne(bson.M{"_id": id})
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DeleteById", table, "DeleteOne", err),
log.Any("id", id),
)
return
}
return
}
// GetBySectionID 根据专题ID获取专题详情
func GetBySectionByID(sectionID primitive.ObjectID) (section Section, err error) {
err = coll(nil).FindOne(&section, bson.M{"_id": sectionID})
return
}
// GetBySubModuleID 通过亚模块id获取专题列表
func GetBySubModuleID(subModuleID primitive.ObjectID, page commod.Page) (section []Section, hasNext bool, err error) {
opts := options.FindOptions{}
opts.SetSkip(int64(page.Skip())).SetLimit(int64(page.Limit()) + 1).SetSort(bson.D{{"sort", 1}, {"_id", -1}})
if err = coll(nil).Find(&section, bson.M{"subModuleID": subModuleID, "status": 1}, &opts); err != nil {
return
}
if uint64(len(section)) > page.Limit() {
hasNext = true
section = section[:page.Limit()]
}
return
}
func GetAllBySubModuleID(subModuleID primitive.ObjectID) (section []Section, err error) {
section = make([]Section, 0)
op := options.Find().SetSort(bson.D{{"sort", 1}})
err = coll(nil).Find(&section, bson.M{"subModuleID": subModuleID, "status": 1}, op)
return
}
func HomePageGetBySubModuleID(subModuleID primitive.ObjectID) (section []Section, err error) {
section = make([]Section, 0)
op := options.Find().SetSort(bson.D{{"sort", 1}}).SetLimit(50)
err = coll(nil).Find(&section, bson.M{"subModuleID": subModuleID, "status": 1}, op)
return
}
func CountBySubModuleID(subModuleID primitive.ObjectID) (int64, error) {
return coll(nil).Count(bson.M{"subModuleID": subModuleID, "status": 1})
}
func AllSections() (list []Section, err error) {
err = coll(nil).Find(&list, bson.M{"status": 1})
return
}
// AllSectionsModule 获取所有激活的专题,以及对应的模块信息
func AllSectionsModule() (list []SectionModule, err error) {
pipeline := []bson.M{
{"$match": bson.M{"status": 1}},
{"$lookup": bson.M{
"from": models.ModuleConf,
"localField": "subModuleID",
"foreignField": "_id",
"as": "sectionMoudle",
}},
}
err = coll(nil).Aggregate(&list, pipeline)
return
}
func GetByIDs(ids []primitive.ObjectID) (list []SectionModule, err error) {
pipeline := []bson.M{
{"$match": bson.M{"_id": bson.M{"$in": ids}}},
{"$lookup": bson.M{
"from": models.ModuleConf,
"localField": "subModuleID",
"foreignField": "_id",
"as": "sectionMoudle",
}},
}
err = coll(nil).Aggregate(&list, pipeline)
return
}
// TODO: 并发不安全
func checkLimit(subModuleID primitive.ObjectID) (ok bool, err error) {
subModule, err := moduleconfmod.GetByID(subModuleID)
if err != nil {
return
}
total, err := CountBySubModuleID(subModuleID)
if err != nil {
return
}
return subModule.SectionLimit == 0 || int(total) < subModule.SectionLimit, nil
}
// ListBySubModule returns topic metadata under one module. The hard limit keeps
// the merged system/custom topic response bounded even if historical data is malformed.
func ListBySubModule(subModuleID primitive.ObjectID, limit int64) ([]Section, error) {
if limit <= 0 || limit > 1000 {
limit = 1000
}
var list []Section
err := coll(nil).Find(&list, bson.M{
"subModuleID": subModuleID,
"deletedAt": nil,
}, options.Find().SetSort(bson.D{{Key: "sort", Value: -1}, {Key: "updatedAt", Value: -1}}).SetLimit(limit))
return list, err
}
// UpdateTopicFields updates a section only when it belongs to the expected module.
func UpdateTopicFields(t *db.MongoTool, id, subModuleID primitive.ObjectID, fields bson.M) (int64, error) {
fields["updatedAt"] = time.Now()
result, err := coll(t).UpdateOne(bson.M{
"_id": id, "subModuleID": subModuleID, "deletedAt": nil,
}, bson.M{"$set": fields})
if err != nil {
return 0, err
}
return result.MatchedCount, nil
}
func DeleteTopic(t *db.MongoTool, id, subModuleID primitive.ObjectID) (int64, error) {
result, err := coll(t).DeleteOne(bson.M{"_id": id, "subModuleID": subModuleID})
if err != nil {
return 0, err
}
return result.DeletedCount, nil
}
// Search 根据条件检索section
func Search(q QuerySelector, p commod.Page) (list []Section, hasNext bool, total int64, err error) {
skip := int64(p.Skip())
limit := int64(p.Limit() + 1)
opts := options.FindOptions{
Skip: &skip,
Limit: &limit,
Sort: append(p.GetSort(), bson.E{Key: "createdAt", Value: -1}),
}
filter, err := common.ToBsonM(q)
if err != nil {
return
}
if q.SectionName != nil {
filter["sectionName"] = primitive.Regex{
Pattern: *q.SectionName,
}
}
if err = coll(nil).Find(&list, filter, &opts); err != nil {
return
}
if total, err = coll(nil).Count(filter); err != nil {
return
}
if uint64(len(list)) > p.Limit() {
hasNext = true
list = list[:p.Limit()]
}
return
}
// GetBySectionByIDs 根据专题IDS获取专题详情
func GetBySectionByIDs(sectionIds []primitive.ObjectID) (section []Section, err error) {
err = coll(nil).Find(&section, bson.M{"_id": bson.M{"$in": sectionIds}})
return
}
// GetBySectionBySids 根据专题IDS获取专题详情
func GetBySectionBySids(sIds []primitive.ObjectID) (section []Section, err error) {
err = coll(nil).Find(&section, bson.M{"subModuleID": bson.M{"$in": sIds}, "status": 1})
return
}
// GetBySectionID 根据专题ID获取专题详情
func GetBySectionID(sectionID primitive.ObjectID) (section Section, err error) {
err = coll(nil).FindOne(&section, bson.M{"_id": sectionID})
return
}
// QueryAllList 分页查询文档
func QueryAllList(filter primitive.M, opts ...*options.FindOptions) (out []*Section, err error) {
if err = coll(nil).Find(&out, filter, opts...); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "QueryAllList", table, "Find", err),
log.Any("filter", filter),
log.Any("opts", opts),
)
return nil, err
}
return
}
+144
View File
@@ -0,0 +1,144 @@
package modulesectionmod
import (
"time"
"91porn-server/common/db"
"91porn-server/models/v/moduleconfmod"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const (
OneLargeAndFourSmall = 101 // 101 一大四小(横-长视频)
FourGrid = 102 // 102 四宫格(横-长视频/ACG)
SixGrid = 103 // 103 六宫格(横-长视频)
Horizontal15Scroll = 104 // 104 横屏1.5滑动
Horizontal25Scroll = 105 // 105 横屏2.5滑动
HorizontalList = 106 // 106 横屏列表展示
HorizontalSingle = 107 // 107 横屏大图(单个列表展示)
VerticalFourGrid = 201 // 四宫格(视频/ACG)
VerticalSixGrid = 202 // 六宫格(视频/ACG)
VerticalNineGrid = 203 // 九宫格(视频/ACG)
Vertical15Scroll = 204 // 竖屏1.5滑动(ACG)
Vertical25Scroll = 205 // 竖屏2.5滑动(ACG)
GuessYouLike = 301 // 猜你喜欢
)
var ShowTypeHashVale = map[int]int{
OneLargeAndFourSmall: 5, // 101 一大四小(横-长视频)
FourGrid: 4, // 102 四宫格(横-长视频)
SixGrid: 6, // 103 六宫格(横-长视频)
Horizontal15Scroll: 12, // 104 横屏1.5滑动
Horizontal25Scroll: 12, // 105 横屏2.5滑动
HorizontalList: 3, // 106 横屏列表展示
HorizontalSingle: 3, // 107 横屏大图(单个列表展示)
VerticalFourGrid: 4, // 2 四宫格(竖-短视频)
VerticalSixGrid: 6, // 4 六宫格(竖-短视频)
VerticalNineGrid: 9, // 5 九宫格(竖-长视频)
Vertical15Scroll: 12, // 7 竖屏1.5滑动
Vertical25Scroll: 12, // 7 竖屏2.5滑动
}
var ACGShowTypeHashVale = map[int]int{
VerticalFourGrid: 4, // 2 四宫格(竖-短视频)
VerticalSixGrid: 6, // 4 六宫格(竖-短视频)
VerticalNineGrid: 9, // 5 九宫格(竖-长视频)
Vertical15Scroll: 12, // 7 竖屏1.5滑动
Vertical25Scroll: 12, // 7 竖屏2.5滑动
}
// Section 专题配置
type Section struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` // ID
SectionName string `json:"sectionName" bson:"sectionName" binding:"required"` // 亚模块下的专题名称
SectionTitle string `json:"sectionTitle" bson:"sectionTitle"` // 专题的标题
SectionCover *string `json:"sectionCover" bson:"sectionCover"` // 亚模块封面
SubModuleID primitive.ObjectID `json:"subModuleID" bson:"subModuleID" binding:"required"` // 亚模块id
OriginalUserID *uint64 `json:"originalUserID" bson:"originalUserID,omitempty"` // 原创博主用户ID,原创模块需要使用
Status *uint8 `json:"status" bson:"status" binding:"required"` // 状态,0-关闭,1-展示
Sort *int `json:"sort" bson:"sort" binding:"required"` // 排序
Hot bool `json:"hot" bson:"hot"` // 是否显示hot标识
CreatedAt time.Time `json:"createdAt" bson:"createdAt,omitempty"` // 创建时间
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` // 刷新时间
DeletedAt *time.Time `json:"deletedAt" bson:"deletedAt,omitempty"` // 删除时间,不能去除omitempty
ShowType int `json:"showType" bson:"showType,omitempty"` // 展示样式
Tags *[]string `json:"tags" bson:"tags,omitempty"` // 所属标签
TagIds *[]primitive.ObjectID `json:"tagIds" bson:"tagIds,omitempty"` // 所属标签Id
}
func Init() {
mdb = db.Init(table)
initIndex()
}
type EditSelector struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty" binding:"required"`
SectionName *string `json:"sectionName,omitempty" bson:"sectionName,omitempty"`
SectionCover *string `json:"sectionCover" bson:"sectionCover,omitempty"`
SubModuleID *primitive.ObjectID `json:"subModuleID,omitempty" bson:"subModuleID,omitempty" binding:"required"`
Status *uint8 `json:"status,omitempty" bson:"status,omitempty"`
Sort *int `json:"sort,omitempty" bson:"sort,omitempty"`
Hot *bool `json:"hot" bson:"hot"` // 是否显示hot标识
OriginalUserID *uint64 `json:"originalUserID,omitempty" bson:"originalUserID,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty" bson:"updatedAt"`
DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
ShowType *int `json:"showType,omitempty" bson:"showType,omitempty"`
Tags *[]string `json:"tags" bson:"tags,omitempty"` // 标签
TagIds []primitive.ObjectID `json:"tagIds" bson:"tagIds,omitempty"` // 所属标签Id
}
type QuerySelector struct {
ID *primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty" form:"id,omitempty" swaggertype:"string"`
SectionName *string `json:"sectionName,omitempty" bson:"sectionName,omitempty" form:"sectionName,omitempty"`
SubModuleID *primitive.ObjectID `json:"subModuleID,omitempty" bson:"subModuleID,omitempty" form:"subModuleID,omitempty" swaggertype:"string"`
Status *uint8 `json:"status,omitempty" bson:"status,omitempty" form:"status,omitempty"`
}
type ListResp struct {
Total int64 `json:"total"` // 总数
HasNext bool `json:"hasNext"` // 是否还有下一页
List []SectionDetail `json:"list"` // 列表
}
type SectionDetail struct {
Section
ModuleInfo
OriginalBloggerInfo
}
type ModuleInfo struct {
ModuleName string `json:"moduleName"` // 模块名称
SubModuleName string `json:"subModuleName"` // 子模块名称
SectionLimit int `json:"sectionLimit"` // 专题数量限制
}
type OriginalBloggerInfo struct {
Name string `json:"name"` // 博主姓名
Portrait string `json:"portrait"` // 头像
OfficialCert bool `json:"officialCert"` // 是否官方认证
IsMadou bool `json:"isMadou"` // 是否工作室
}
type SectionModule struct {
Section `bson:",inline"`
SectionModule []moduleconfmod.ModuleConf `bson:"sectionMoudle"`
}
type ModuleSection struct {
moduleconfmod.ModuleConf // 模块,子模块信息
Sections []Section `json:"sections"` // 子模块下的所有专题
}
// AllSectionConf 专题配置
type AllSectionConf struct {
ID primitive.ObjectID `json:"id" bson:"id"` // 文档ID
ModuleName string `json:"moduleName" bson:"moduleName" ` // 版块名称
AllSection []SectionConf `json:"allSection" bson:"allSection"` // 所有专题
}
type SectionConf struct {
ID primitive.ObjectID `json:"id" bson:"id"` // 文档ID
SectionName string `json:"sectionName" bson:"sectionName" ` // 专题名称
}