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
+241
View File
@@ -0,0 +1,241 @@
package dramaser
import (
"encoding/json"
"errors"
"fmt"
"sort"
"time"
"91porn-server/app/appg"
"91porn-server/app/service/mediaser"
"91porn-server/common/dramatopic"
"91porn-server/common/log"
"91porn-server/models/commod"
"91porn-server/models/v/mediamod"
"91porn-server/models/v/modulesectionmod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
const everyoneLikesCacheTTL = 30 * time.Second
type TopicSummary struct {
TopicID string `json:"topicId"`
Name string `json:"name"`
TopicType string `json:"topicType"`
SystemKey string `json:"systemKey"`
Sort int `json:"sort"`
WorkCount int64 `json:"workCount"`
}
type TopicListResponse struct {
List []TopicSummary `json:"list"`
}
type TopicWorksRequest struct {
TopicID string `form:"topicId" json:"topicId" binding:"required"`
commod.Page
}
type TopicWorksResponse struct {
Topic TopicSummary `json:"topic"`
Total int64 `json:"total"`
HasNext bool `json:"hasNext"`
List []*mediamod.AppMediaBase `json:"list"`
}
func GetTopics(now time.Time) (TopicListResponse, error) {
moduleID, ok, err := dramatopic.ModuleID(now)
if err != nil {
return TopicListResponse{}, err
}
total, err := mediamod.QueryAllCount(activeDramaFilter())
if err != nil {
return TopicListResponse{}, err
}
sections := make([]modulesectionmod.Section, 0)
if ok {
sections, err = modulesectionmod.ListBySubModule(moduleID, 1000)
if err != nil {
return TopicListResponse{}, err
}
}
sectionIDs := make([]primitive.ObjectID, 0, len(sections))
for _, section := range sections {
if section.Status != nil && *section.Status == 1 {
sectionIDs = append(sectionIDs, section.ID)
}
}
counts, err := mediamod.CountActiveDramaBySectionIDs(sectionIDs)
if err != nil {
return TopicListResponse{}, err
}
items := make([]topicSortItem, 0, 3+len(sections))
for _, system := range dramatopic.SystemTopics() {
items = append(items, topicSortItem{summary: TopicSummary{
TopicID: system.ID, Name: system.Name, TopicType: dramatopic.TypeSystem,
SystemKey: system.SystemKey, Sort: system.Sort, WorkCount: total,
}, systemOrder: system.TieOrder})
}
for _, section := range sections {
if section.Status == nil || *section.Status != 1 {
continue
}
items = append(items, topicSortItem{summary: TopicSummary{
TopicID: section.ID.Hex(), Name: section.SectionName, TopicType: dramatopic.TypeCustom,
Sort: valueOrZero(section.Sort), WorkCount: counts[section.ID],
}, updatedAt: section.UpdatedAt})
}
sortTopicItems(items)
response := TopicListResponse{List: make([]TopicSummary, 0, len(items))}
for _, item := range items {
response.List = append(response.List, item.summary)
}
return response, nil
}
type topicSortItem struct {
summary TopicSummary
systemOrder int
updatedAt time.Time
}
func sortTopicItems(items []topicSortItem) {
sort.SliceStable(items, func(i, j int) bool {
if items[i].summary.Sort != items[j].summary.Sort {
return items[i].summary.Sort > items[j].summary.Sort
}
if items[i].systemOrder != items[j].systemOrder {
return items[i].systemOrder > items[j].systemOrder
}
return items[i].updatedAt.After(items[j].updatedAt)
})
}
func GetTopicWorks(uid uint64, req TopicWorksRequest, now time.Time) (TopicWorksResponse, error) {
if system, ok := dramatopic.FindSystem(req.TopicID); ok {
return getSystemTopicWorks(uid, req, system)
}
moduleID, ok, err := dramatopic.ModuleID(now)
if err != nil || !ok {
if err == nil {
err = errors.New("短剧专题模块未配置")
}
return TopicWorksResponse{}, err
}
topicID, err := primitive.ObjectIDFromHex(req.TopicID)
if err != nil {
return TopicWorksResponse{}, errors.New("无效的专题ID")
}
section, err := modulesectionmod.GetBySectionByID(topicID)
if err != nil || section.SubModuleID != moduleID || section.DeletedAt != nil || section.Status == nil || *section.Status != 1 {
if err == nil {
err = errors.New("专题不存在或已停用")
}
return TopicWorksResponse{}, err
}
filter := activeDramaFilter()
filter["sId"] = topicID
total, err := mediamod.QueryAllCount(filter)
if err != nil {
return TopicWorksResponse{}, err
}
list, err := mediamod.QueryAllList(filter, options.Find().
SetSkip(req.Skip64()).SetLimit(req.Limit64()+1).
SetSort(bson.D{{Key: "sectionSort", Value: -1}, {Key: "latestPublishedAt", Value: -1}, {Key: "_id", Value: -1}}))
if err != nil {
return TopicWorksResponse{}, err
}
hasNext := len(list) > int(req.PageSize)
if hasNext {
list = list[:req.PageSize]
}
return TopicWorksResponse{
Topic: TopicSummary{TopicID: req.TopicID, Name: section.SectionName, TopicType: dramatopic.TypeCustom, Sort: valueOrZero(section.Sort), WorkCount: total},
Total: total, HasNext: hasNext, List: fillTopicMedias(list, uid),
}, nil
}
func getSystemTopicWorks(uid uint64, req TopicWorksRequest, topic dramatopic.SystemTopic) (TopicWorksResponse, error) {
filter := activeDramaFilter()
total, err := mediamod.QueryAllCount(filter)
if err != nil {
return TopicWorksResponse{}, err
}
var list []*mediamod.Media
var hasNext bool
if topic.ID == dramatopic.IDEveryone {
list, err = listEveryoneLikes(req.Skip64(), req.Limit64()+1)
if err != nil {
return TopicWorksResponse{}, err
}
hasNext = len(list) > int(req.PageSize)
if hasNext {
list = list[:req.PageSize]
}
} else {
sortBy := bson.D{{Key: "sortCode", Value: -1}, {Key: "latestPublishedAt", Value: -1}, {Key: "_id", Value: -1}}
if topic.ID == dramatopic.IDLatest {
sortBy = bson.D{{Key: "latestPublishedAt", Value: -1}, {Key: "createdAt", Value: -1}, {Key: "_id", Value: -1}}
}
list, err = mediamod.QueryAllList(filter, options.Find().SetSkip(req.Skip64()).SetLimit(req.Limit64()+1).SetSort(sortBy))
if err != nil {
return TopicWorksResponse{}, err
}
hasNext = len(list) > int(req.PageSize)
if hasNext {
list = list[:req.PageSize]
}
}
return TopicWorksResponse{
Topic: TopicSummary{TopicID: topic.ID, Name: topic.Name, TopicType: dramatopic.TypeSystem, SystemKey: topic.SystemKey, Sort: topic.Sort, WorkCount: total},
Total: total, HasNext: hasNext, List: fillTopicMedias(list, uid),
}, nil
}
func fillTopicMedias(list []*mediamod.Media, uid uint64) []*mediamod.AppMediaBase {
filled := mediaser.FillMedias(list, uid, true)
if filled == nil {
return []*mediamod.AppMediaBase{}
}
return filled
}
func listEveryoneLikes(skip, limit int64) ([]*mediamod.Media, error) {
key := fmt.Sprintf("drama:topic:everyone-likes:v1:%d:%d", skip, limit)
if appg.Redis != nil {
cached, err := appg.Redis.Get(key)
if err == nil && cached != nil {
var list []*mediamod.Media
if jsonErr := json.Unmarshal([]byte(*cached), &list); jsonErr == nil {
return list, nil
}
}
}
list, err := mediamod.ListActiveDramaByInteractionScore(skip, limit)
if err != nil {
return nil, err
}
if appg.Redis != nil {
if data, marshalErr := json.Marshal(list); marshalErr == nil {
if cacheErr := appg.Redis.Set(key, data, everyoneLikesCacheTTL); cacheErr != nil {
log.Warn("缓存大家爱看短剧列表失败", log.E(cacheErr))
}
}
}
return list, nil
}
func activeDramaFilter() bson.M {
return bson.M{"mediaType": mediamod.MediaTypeDrama, "status": 1, "isDelete": false}
}
func valueOrZero(value *int) int {
if value == nil {
return 0
}
return *value
}