Files
common-server-public/bbs-go/server/internal/services/article_service.go
2025-03-03 17:43:50 +08:00

288 lines
8.3 KiB
Go

package services
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/bbsurls"
"bbs-go/internal/pkg/seo"
"errors"
"math"
"strings"
"bbs-go/internal/cache"
"bbs-go/internal/repositories"
"github.com/mlogclub/simple/common/dates"
"github.com/mlogclub/simple/common/jsons"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web/params"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
"gorm.io/gorm"
"bbs-go/internal/models"
)
var ArticleService = newArticleService()
func newArticleService() *articleService {
return &articleService{}
}
type articleService struct {
}
func (s *articleService) Get(id int64) *models.Article {
return repositories.ArticleRepository.Get(sqls.DB(), id)
}
func (s *articleService) Take(where ...interface{}) *models.Article {
return repositories.ArticleRepository.Take(sqls.DB(), where...)
}
func (s *articleService) Find(cnd *sqls.Cnd) []models.Article {
return repositories.ArticleRepository.Find(sqls.DB(), cnd)
}
func (s *articleService) FindOne(cnd *sqls.Cnd) *models.Article {
return repositories.ArticleRepository.FindOne(sqls.DB(), cnd)
}
func (s *articleService) FindPageByParams(params *params.QueryParams) (list []models.Article, paging *sqls.Paging) {
return repositories.ArticleRepository.FindPageByParams(sqls.DB(), params)
}
func (s *articleService) FindPageByCnd(cnd *sqls.Cnd) (list []models.Article, paging *sqls.Paging) {
return repositories.ArticleRepository.FindPageByCnd(sqls.DB(), cnd)
}
func (s *articleService) Update(t *models.Article) error {
err := repositories.ArticleRepository.Update(sqls.DB(), t)
return err
}
func (s *articleService) Updates(id int64, columns map[string]interface{}) error {
err := repositories.ArticleRepository.Updates(sqls.DB(), id, columns)
return err
}
func (s *articleService) UpdateColumn(id int64, name string, value interface{}) error {
err := repositories.ArticleRepository.UpdateColumn(sqls.DB(), id, name, value)
return err
}
func (s *articleService) Delete(id int64) error {
err := repositories.ArticleRepository.UpdateColumn(sqls.DB(), id, "status", constants.StatusDeleted)
if err == nil {
ArticleTagService.DeleteByArticleId(id)
}
return err
}
// 根据文章编号批量获取文章
func (s *articleService) GetArticleInIds(articleIds []int64) []models.Article {
if len(articleIds) == 0 {
return nil
}
var articles []models.Article
sqls.DB().Where("id in (?)", articleIds).Order("id desc").Find(&articles)
return articles
}
// 获取文章对应的标签
func (s *articleService) GetArticleTags(articleId int64) []models.Tag {
articleTags := repositories.ArticleTagRepository.Find(sqls.DB(), sqls.NewCnd().Where("article_id = ?", articleId))
var tagIds []int64
for _, articleTag := range articleTags {
tagIds = append(tagIds, articleTag.TagId)
}
return cache.TagCache.GetList(tagIds)
}
// 文章列表
func (s *articleService) GetArticles(cursor int64) (articles []models.Article, nextCursor int64, hasMore bool) {
limit := 20
cnd := sqls.NewCnd().Eq("status", constants.StatusOk).Desc("id").Limit(limit)
if cursor > 0 {
cnd.Lt("id", cursor)
}
articles = repositories.ArticleRepository.Find(sqls.DB(), cnd)
if len(articles) > 0 {
nextCursor = articles[len(articles)-1].Id
hasMore = len(articles) >= limit
} else {
nextCursor = cursor
}
return
}
// 标签文章列表
func (s *articleService) GetTagArticles(tagId int64, cursor int64) (articles []models.Article, nextCursor int64, hasMore bool) {
limit := 20
cnd := sqls.NewCnd().Eq("tag_id", tagId).Eq("status", constants.StatusOk).Desc("id").Limit(limit)
if cursor > 0 {
cnd.Lt("id", cursor)
}
nextCursor = cursor
articleTags := repositories.ArticleTagRepository.Find(sqls.DB(), cnd)
if len(articleTags) > 0 {
var articleIds []int64
for _, articleTag := range articleTags {
articleIds = append(articleIds, articleTag.ArticleId)
nextCursor = articleTag.Id
}
articles = s.GetArticleInIds(articleIds)
}
hasMore = len(articleTags) >= limit
return
}
// 发布文章
func (s *articleService) Publish(userId int64, form models.CreateArticleForm) (article *models.Article, err error) {
form.Title = strings.TrimSpace(form.Title)
form.Summary = strings.TrimSpace(form.Summary)
form.Content = strings.TrimSpace(form.Content)
if strs.IsBlank(form.Title) {
return nil, errors.New("标题不能为空")
}
if strs.IsBlank(form.Content) {
return nil, errors.New("内容不能为空")
}
// 获取后台配置 否是开启发表文章审核
status := constants.StatusOk
if SysConfigService.IsArticlePending() {
status = constants.StatusReview
}
article = &models.Article{
UserId: userId,
Title: form.Title,
Summary: form.Summary,
Content: form.Content,
ContentType: form.ContentType,
Status: status,
SourceUrl: form.SourceUrl,
CreateTime: dates.NowTimestamp(),
UpdateTime: dates.NowTimestamp(),
}
if form.Cover != nil {
article.Cover = jsons.ToJsonStr(form.Cover)
}
err = sqls.DB().Transaction(func(tx *gorm.DB) error {
var (
tagIds []int64
err error
)
if tagIds, err = repositories.TagRepository.GetOrCreates(tx, form.Tags); err != nil {
return err
}
if err = repositories.ArticleRepository.Create(tx, article); err != nil {
return err
}
repositories.ArticleTagRepository.AddArticleTags(tx, article.Id, tagIds)
return nil
})
if err == nil {
seo.Push(bbsurls.ArticleUrl(article.Id))
}
return
}
// 修改文章
func (s *articleService) Edit(articleId int64, tags []string, title, content string, cover *models.ImageDTO) error {
if len(title) == 0 {
return errors.New("请输入标题")
}
if len(content) == 0 {
return errors.New("请填写文章内容")
}
err := sqls.DB().Transaction(func(tx *gorm.DB) error {
updates := map[string]interface{}{
"title": title,
"content": content,
}
if cover != nil {
updates["cover"] = jsons.ToJsonStr(cover)
} else {
updates["cover"] = ""
}
err := repositories.ArticleRepository.Updates(sqls.DB(), articleId, updates)
if err != nil {
return err
}
tagIds, _ := repositories.TagRepository.GetOrCreates(tx, tags) // 创建文章对应标签
repositories.ArticleTagRepository.DeleteArticleTags(tx, articleId) // 先删掉所有的标签
repositories.ArticleTagRepository.AddArticleTags(tx, articleId, tagIds) // 然后重新添加标签
return nil
})
cache.ArticleTagCache.Invalidate(articleId)
return err
}
func (s *articleService) PutTags(articleId int64, tags []string) {
tagIds, _ := repositories.TagRepository.GetOrCreates(sqls.DB(), tags) // 创建文章对应标签
repositories.ArticleTagRepository.DeleteArticleTags(sqls.DB(), articleId) // 先删掉所有的标签
repositories.ArticleTagRepository.AddArticleTags(sqls.DB(), articleId, tagIds) // 然后重新添加标签
cache.ArticleTagCache.Invalidate(articleId)
}
// 倒序扫描
func (s *articleService) ScanDesc(callback func(articles []models.Article)) {
var cursor int64 = math.MaxInt64
for {
logrus.Info("scan articles desc, cursor:" + cast.ToString(cursor))
list := repositories.ArticleRepository.Find(sqls.DB(), sqls.NewCnd().
Cols("id", "status", "user_id", "content_type", "create_time", "update_time").
Lt("id", cursor).Desc("id").Limit(1000))
if len(list) == 0 {
break
}
cursor = list[len(list)-1].Id
callback(list)
}
}
func (s *articleService) ScanByUser(userId int64, callback func(articles []models.Article)) {
var cursor int64 = 0
for {
list := repositories.ArticleRepository.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)
}
}
// 浏览数+1
func (s *articleService) IncrViewCount(articleId int64) {
sqls.DB().Exec("update t_article set view_count = view_count + 1 where id = ?", articleId)
}
func (s *articleService) GetUserArticles(userId, cursor int64) (articles []models.Article, 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)
articles = repositories.ArticleRepository.Find(sqls.DB(), cnd)
if len(articles) > 0 {
nextCursor = articles[len(articles)-1].Id
hasMore = len(articles) >= limit
} else {
nextCursor = cursor
}
return
}