20251209
This commit is contained in:
287
archive/bbs-go/server/internal/services/article_service.go
Normal file
287
archive/bbs-go/server/internal/services/article_service.go
Normal file
@@ -0,0 +1,287 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user