This commit is contained in:
12600k-rog-d4
2025-12-09 20:33:57 +08:00
parent 144ed3cbdf
commit 8f4d399cfd
1498 changed files with 3778 additions and 3654 deletions

View File

@@ -0,0 +1,448 @@
package services
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/event"
"bbs-go/internal/pkg/search"
"errors"
"math"
"net/http"
"github.com/mlogclub/simple/common/dates"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web/params"
"gorm.io/gorm"
"bbs-go/internal/cache"
"bbs-go/internal/models"
"bbs-go/internal/repositories"
)
var TopicService = newTopicService()
func newTopicService() *topicService {
return &topicService{}
}
type topicService struct{}
func (s *topicService) Get(id int64) *models.Topic {
return repositories.TopicRepository.Get(sqls.DB(), id)
}
func (s *topicService) Take(where ...interface{}) *models.Topic {
return repositories.TopicRepository.Take(sqls.DB(), where...)
}
func (s *topicService) Find(cnd *sqls.Cnd) []models.Topic {
return repositories.TopicRepository.Find(sqls.DB(), cnd)
}
func (s *topicService) FindOne(cnd *sqls.Cnd) *models.Topic {
return repositories.TopicRepository.FindOne(sqls.DB(), cnd)
}
func (s *topicService) FindPageByParams(params *params.QueryParams) (list []models.Topic, paging *sqls.Paging) {
return repositories.TopicRepository.FindPageByParams(sqls.DB(), params)
}
func (s *topicService) FindPageByCnd(cnd *sqls.Cnd) (list []models.Topic, paging *sqls.Paging) {
return repositories.TopicRepository.FindPageByCnd(sqls.DB(), cnd)
}
func (s *topicService) Count(cnd *sqls.Cnd) int64 {
return repositories.TopicRepository.Count(sqls.DB(), cnd)
}
func (s *topicService) Updates(id int64, columns map[string]interface{}) error {
if err := repositories.TopicRepository.Updates(sqls.DB(), id, columns); err != nil {
return err
}
// 添加索引
search.UpdateTopicIndex(s.Get(id))
return nil
}
func (s *topicService) UpdateColumn(id int64, name string, value interface{}) error {
if err := repositories.TopicRepository.UpdateColumn(sqls.DB(), id, name, value); err != nil {
return err
}
// 添加索引
search.UpdateTopicIndex(s.Get(id))
return nil
}
// Delete 删除
func (s *topicService) Delete(topicId, deleteUserId int64, r *http.Request) error {
topic := s.Get(topicId)
if topic == nil {
return nil
}
err := repositories.TopicRepository.UpdateColumn(sqls.DB(), topicId, "status", constants.StatusDeleted)
if err == nil {
// 添加索引
search.DeleteTopicIndex(topicId)
// 删掉标签文章
TopicTagService.DeleteByTopicId(topicId)
// 发送事件
event.Send(event.TopicDeleteEvent{
UserId: topic.UserId,
TopicId: topic.Id,
DeleteUserId: deleteUserId,
})
}
return err
}
// Undelete 取消删除
func (s *topicService) Undelete(id int64) error {
err := repositories.TopicRepository.UpdateColumn(sqls.DB(), id, "status", constants.StatusOk)
if err == nil {
// 删掉标签文章
TopicTagService.UndeleteByTopicId(id)
// 添加索引
search.UpdateTopicIndex(s.Get(id))
}
return err
}
// 更新
func (s *topicService) Edit(topicId, nodeId int64, tags []string, title, content, hideContent string) error {
if len(title) == 0 {
return errors.New("标题不能为空")
}
if strs.RuneLen(title) > 128 {
return errors.New("标题长度不能超过128")
}
node := repositories.TopicNodeRepository.Get(sqls.DB(), nodeId)
if node == nil || node.Status != constants.StatusOk {
return errors.New("节点不存在")
}
err := sqls.DB().Transaction(func(tx *gorm.DB) error {
var (
tagIds []int64
err error
)
if err = repositories.TopicRepository.Updates(sqls.DB(), topicId, map[string]interface{}{
"node_id": nodeId,
"title": title,
"content": content,
"hide_content": hideContent,
}); err != nil {
return err
}
// 创建帖子对应标签
if tagIds, err = repositories.TagRepository.GetOrCreates(tx, tags); err != nil {
return err
}
repositories.TopicTagRepository.DeleteTopicTags(tx, topicId) // 先删掉所有的标签
repositories.TopicTagRepository.AddTopicTags(tx, topicId, tagIds) // 然后重新添加标签
return nil
})
// 添加索引
search.UpdateTopicIndex(s.Get(topicId))
return err
}
// 推荐
func (s *topicService) SetRecommend(topicId int64, recommend bool) error {
topic := s.Get(topicId)
if topic == nil || topic.Status != constants.StatusOk {
return errors.New("帖子不存在")
}
if topic.Recommend == recommend { // 推荐状态没变更
return nil
}
if recommend {
if err := s.Updates(topicId, map[string]interface{}{
"recommend": recommend,
"recommend_time": dates.NowTimestamp(),
}); err != nil {
return err
}
} else {
if err := s.UpdateColumn(topicId, "recommend", recommend); err != nil {
return err
}
}
// 发送事件
event.Send(event.TopicRecommendEvent{
TopicId: topicId,
Recommend: recommend,
})
// 添加索引
search.UpdateTopicIndex(s.Get(topicId))
return nil
}
// GetTopicTags 话题的标签
func (s *topicService) GetTopicTags(topicId int64) []models.Tag {
topicTags := repositories.TopicTagRepository.Find(sqls.DB(), sqls.NewCnd().Where("topic_id = ?", topicId))
var tagIds []int64
for _, topicTag := range topicTags {
tagIds = append(tagIds, topicTag.TagId)
}
return cache.TagCache.GetList(tagIds)
}
// GetTopics 帖子列表(最新、推荐、关注、节点)
func (s *topicService) GetTopics(user *models.User, nodeId, cursor int64) (topics []models.Topic, nextCursor int64, hasMore bool) {
var limit int = 20
if nodeId == constants.NodeIdFollow {
if user != nil {
return s._GetFollowTopics(user.Id, cursor)
}
return
} else {
return s._GetNodeTopics(nodeId, cursor, limit)
}
}
// _GetNodeTopics 帖子列表(最新、推荐、节点)
func (s *topicService) _GetNodeTopics(nodeId, cursor int64, limit int) (topics []models.Topic, nextCursor int64, hasMore bool) {
cnd := sqls.NewCnd()
if nodeId > 0 {
cnd.Eq("node_id", nodeId)
}
if nodeId == constants.NodeIdRecommend {
cnd.Eq("recommend", true)
}
if cursor > 0 {
cnd.Lt("last_comment_time", cursor)
}
cnd.Eq("status", constants.StatusOk).Desc("last_comment_time").Limit(limit)
topics = repositories.TopicRepository.Find(sqls.DB(), cnd)
if len(topics) > 0 {
nextCursor = topics[len(topics)-1].LastCommentTime
hasMore = len(topics) >= limit
} else {
nextCursor = cursor
}
return
}
// _GetFollowTopics 关注帖子列表
func (s *topicService) _GetFollowTopics(userId int64, cursor int64) (topics []models.Topic, nextCursor int64, hasMore bool) {
var limit = 20
cnd := sqls.NewCnd().Eq("user_id", userId)
cnd.Eq("data_type", constants.EntityTopic)
if cursor > 0 {
cnd.Lt("create_time", cursor)
}
cnd.Desc("create_time").Limit(limit)
userFeeds := repositories.UserFeedRepository.Find(sqls.DB(), cnd)
if len(userFeeds) > 0 {
nextCursor = userFeeds[len(userFeeds)-1].CreateTime
hasMore = len(userFeeds) >= limit
} else {
nextCursor = cursor
}
var topicIds []int64
for _, item := range userFeeds {
topicIds = append(topicIds, item.DataId)
}
topics = TopicService.GetTopicByIds(topicIds)
return
}
// 指定标签下话题列表
func (s *topicService) GetTagTopics(tagId, cursor int64) (topics []models.Topic, nextCursor int64, hasMore bool) {
limit := 20
topicTags := repositories.TopicTagRepository.Find(sqls.DB(), sqls.NewCnd().
Eq("tag_id", tagId).
Eq("status", constants.StatusOk).
Desc("last_comment_time").Limit(limit))
if len(topicTags) > 0 {
nextCursor = topicTags[len(topicTags)-1].LastCommentTime
var topicIds []int64
for _, topicTag := range topicTags {
topicIds = append(topicIds, topicTag.TopicId)
}
topicsMap := s.GetTopicInIds(topicIds)
if topicsMap != nil {
for _, topicTag := range topicTags {
if topic, found := topicsMap[topicTag.TopicId]; found {
topics = append(topics, topic)
}
}
}
} else {
nextCursor = cursor
}
hasMore = len(topicTags) >= limit
return
}
func (s *topicService) GetTopicByIds(topicIds []int64) (topics []models.Topic) {
topicsMap := s.GetTopicInIds(topicIds)
for _, topicId := range topicIds {
topic, found := topicsMap[topicId]
if found {
topics = append(topics, topic)
}
}
return
}
// GetTopicInIds 根据编号批量获取主题
func (s *topicService) GetTopicInIds(topicIds []int64) map[int64]models.Topic {
if len(topicIds) == 0 {
return nil
}
var topics []models.Topic
sqls.DB().Where("id in (?)", topicIds).Find(&topics)
topicsMap := make(map[int64]models.Topic, len(topics))
for _, topic := range topics {
topicsMap[topic.Id] = topic
}
return topicsMap
}
// 浏览数+1
func (s *topicService) IncrViewCount(topicId int64) {
sqls.DB().Exec("update t_topic set view_count = view_count + 1 where id = ?", topicId)
}
// 当帖子被评论的时候,更新最后回复时间、回复数量+1
func (s *topicService) onComment(tx *gorm.DB, topicId int64, comment *models.Comment) error {
if err := repositories.TopicRepository.Updates(tx, topicId, map[string]interface{}{
"last_comment_time": comment.CreateTime,
"last_comment_user_id": comment.UserId,
"comment_count": gorm.Expr("comment_count + 1"),
}); err != nil {
return err
}
if err := tx.Exec("update t_topic_tag set last_comment_time = ?, last_comment_user_id = ? where topic_id = ?",
comment.CreateTime, comment.UserId, topicId).Error; err != nil {
return err
}
return nil
}
func (s *topicService) ScanByUser(userId int64, callback func(topics []models.Topic)) {
var cursor int64 = 0
for {
list := repositories.TopicRepository.Find(sqls.DB(), sqls.NewCnd().
Eq("user_id", userId).Gt("id", cursor).Asc("id").Limit(1000))
if len(list) == 0 {
break
}
cursor = list[len(list)-1].Id
callback(list)
}
}
func (s *topicService) Scan(callback func(topics []models.Topic)) {
var cursor int64 = 0
for {
list := repositories.TopicRepository.Find(sqls.DB(), sqls.NewCnd().
Gt("id", cursor).Asc("id").Limit(1000))
if len(list) == 0 {
break
}
cursor = list[len(list)-1].Id
callback(list)
}
}
// 倒序扫描
func (s *topicService) ScanDesc(callback func(topics []models.Topic)) {
var cursor int64 = math.MaxInt64
for {
list := repositories.TopicRepository.Find(sqls.DB(), sqls.NewCnd().
Lt("id", cursor).Desc("id").Limit(1000))
if len(list) == 0 {
break
}
cursor = list[len(list)-1].Id
callback(list)
}
}
// 倒序扫描
func (s *topicService) ScanDescWithDate(dateFrom, dateTo int64, callback func(topics []models.Topic)) {
var cursor int64 = math.MaxInt64
for {
list := repositories.TopicRepository.Find(sqls.DB(), sqls.NewCnd().
Cols("id", "status", "create_time", "update_time").
Lt("id", cursor).Gte("create_time", dateFrom).Lt("create_time", dateTo).Desc("id").Limit(1000))
if len(list) == 0 {
break
}
cursor = list[len(list)-1].Id
callback(list)
}
}
func (s *topicService) GetUserTopics(userId, cursor int64) (topics []models.Topic, nextCursor int64, hasMore bool) {
limit := 20
cnd := sqls.NewCnd()
if userId > 0 {
cnd.Eq("user_id", userId)
}
if cursor > 0 {
cnd.Lt("id", cursor)
}
cnd.Eq("status", constants.StatusOk).Desc("id").Limit(limit)
topics = repositories.TopicRepository.Find(sqls.DB(), cnd)
if len(topics) > 0 {
nextCursor = topics[len(topics)-1].Id
hasMore = len(topics) >= limit
} else {
nextCursor = cursor
}
return
}
func (s *topicService) GetStickyTopics(nodeId int64, limit int) []models.Topic {
if nodeId > 0 {
return s.Find(sqls.NewCnd().Where("node_id = ? and sticky = true and status = ?",
nodeId, constants.StatusOk).Desc("sticky_time").Limit(limit))
} else {
return s.Find(sqls.NewCnd().Where("sticky = true and status = ?",
constants.StatusOk).Desc("sticky_time").Limit(limit))
}
}
func (s *topicService) SetSticky(topicId int64, sticky bool) error {
topic := s.Get(topicId)
if topic == nil || topic.Status != constants.StatusOk {
return errors.New("话题不存在")
}
if topic.Sticky == sticky {
return nil
}
if sticky {
return s.Updates(topicId, map[string]interface{}{
"sticky": true,
"sticky_time": dates.NowTimestamp(),
})
} else {
return s.Updates(topicId, map[string]interface{}{
"sticky": false,
})
}
}