mirror of https://github.com/go-sonic/sonic.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
9.5 KiB
Go
309 lines
9.5 KiB
Go
package impl
|
|
|
|
import (
|
|
"context"
|
|
"database/sql/driver"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/go-sonic/sonic/cache"
|
|
"github.com/go-sonic/sonic/consts"
|
|
"github.com/go-sonic/sonic/dal"
|
|
"github.com/go-sonic/sonic/event"
|
|
"github.com/go-sonic/sonic/model/entity"
|
|
"github.com/go-sonic/sonic/model/param"
|
|
"github.com/go-sonic/sonic/model/property"
|
|
"github.com/go-sonic/sonic/service"
|
|
"github.com/go-sonic/sonic/util"
|
|
"github.com/go-sonic/sonic/util/xerr"
|
|
)
|
|
|
|
type postServiceImpl struct {
|
|
service.BasePostService
|
|
CategoryService service.CategoryService
|
|
OptionService service.OptionService
|
|
Event event.Bus
|
|
Cache cache.Cache
|
|
}
|
|
|
|
func NewPostService(basePostService service.BasePostService,
|
|
categoryService service.CategoryService,
|
|
optionService service.OptionService,
|
|
event event.Bus,
|
|
cache cache.Cache,
|
|
) service.PostService {
|
|
return &postServiceImpl{
|
|
BasePostService: basePostService,
|
|
CategoryService: categoryService,
|
|
OptionService: optionService,
|
|
Event: event,
|
|
Cache: cache,
|
|
}
|
|
}
|
|
|
|
func (p postServiceImpl) Page(ctx context.Context, postQuery param.PostQuery) ([]*entity.Post, int64, error) {
|
|
if postQuery.PageNum < 0 || postQuery.PageSize <= 0 {
|
|
return nil, 0, xerr.BadParam.New("").WithStatus(xerr.StatusBadRequest).WithMsg("Paging parameter error")
|
|
}
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
postCategoryDAL := dal.GetQueryByCtx(ctx).PostCategory
|
|
postDo := postDAL.WithContext(ctx).Where(postDAL.Type.Eq(consts.PostTypePost))
|
|
err := BuildSort(postQuery.Sort, &postDAL, &postDo)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
if postQuery.Keyword != nil {
|
|
postDo.Where(postDAL.Title.Like("%" + *postQuery.Keyword + "%")).Or(postDAL.OriginalContent.Like("%" + *postQuery.Keyword + "%"))
|
|
}
|
|
|
|
if postQuery.WithPassword != nil && !*postQuery.WithPassword {
|
|
postDo.Where(postDAL.Password.Neq(""))
|
|
}
|
|
if len(postQuery.Statuses) > 0 {
|
|
statuesValue := make([]driver.Valuer, len(postQuery.Statuses))
|
|
for i, status := range postQuery.Statuses {
|
|
statuesValue[i] = driver.Valuer(status)
|
|
}
|
|
postDo = postDo.Where(postDAL.Status.In(statuesValue...))
|
|
}
|
|
if postQuery.CategoryID != nil {
|
|
postDo.Join(&entity.PostCategory{}, postDAL.ID.EqCol(postCategoryDAL.PostID)).Where(postCategoryDAL.CategoryID.Eq(*postQuery.CategoryID))
|
|
}
|
|
|
|
posts, totalCount, err := postDo.FindByPage(postQuery.PageNum*postQuery.PageSize, postQuery.PageSize)
|
|
if err != nil {
|
|
return nil, 0, WrapDBErr(err)
|
|
}
|
|
return posts, totalCount, nil
|
|
}
|
|
|
|
func (p postServiceImpl) IncreaseLike(ctx context.Context, postID int32) error {
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
info, err := postDAL.WithContext(ctx).Where(postDAL.ID.Eq(postID)).UpdateSimple(postDAL.Likes.Add(1))
|
|
if err != nil {
|
|
return WrapDBErr(err)
|
|
}
|
|
if info.RowsAffected != 1 {
|
|
return xerr.NoType.New("increase post like failed postID=%v", postID).WithStatus(xerr.StatusBadRequest).WithMsg("failed to like post")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p postServiceImpl) Create(ctx context.Context, postParam *param.Post) (*entity.Post, error) {
|
|
post, err := p.ConvertParam(ctx, postParam)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
needEncrypt, err := p.CategoryService.IsCategoriesEncrypt(ctx, postParam.CategoryIDs...)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
if post.Status != consts.PostStatusDraft && (post.Password != "" || needEncrypt) {
|
|
post.Status = consts.PostStatusIntimate
|
|
}
|
|
|
|
post, err = p.CreateOrUpdate(ctx, post, postParam.CategoryIDs, postParam.TagIDs, postParam.MetaParam)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Todo delete authorization
|
|
p.Event.Publish(ctx, &event.LogEvent{
|
|
LogKey: strconv.Itoa(int(post.ID)),
|
|
LogType: consts.LogTypePostPublished,
|
|
Content: post.Title,
|
|
IpAddress: util.GetClientIP(ctx),
|
|
})
|
|
return post, nil
|
|
}
|
|
|
|
func (p postServiceImpl) Update(ctx context.Context, postID int32, postParam *param.Post) (*entity.Post, error) {
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
post, err := postDAL.WithContext(ctx).Where(postDAL.ID.Eq(postID)).First()
|
|
if err != nil {
|
|
return nil, WrapDBErr(err)
|
|
}
|
|
postToUpdate, err := p.ConvertParam(ctx, postParam)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if postToUpdate.CreateTime == (time.Time{}) {
|
|
postToUpdate.CreateTime = post.CreateTime
|
|
}
|
|
postToUpdate.ID = post.ID
|
|
post, err = p.CreateOrUpdate(ctx, postToUpdate, postParam.CategoryIDs, postParam.TagIDs, postParam.MetaParam)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// TODO should use transcation
|
|
p.Event.Publish(ctx, &event.PostUpdateEvent{
|
|
PostID: post.ID,
|
|
})
|
|
p.Event.Publish(ctx, &event.LogEvent{
|
|
LogKey: strconv.Itoa(int(post.ID)),
|
|
LogType: consts.LogTypePostEdited,
|
|
Content: post.Title,
|
|
IpAddress: util.GetClientIP(ctx),
|
|
})
|
|
return post, nil
|
|
}
|
|
|
|
func (p postServiceImpl) ConvertParam(ctx context.Context, postParam *param.Post) (*entity.Post, error) {
|
|
post := &entity.Post{
|
|
Type: consts.PostTypePost,
|
|
DisallowComment: postParam.DisallowComment,
|
|
OriginalContent: postParam.OriginalContent,
|
|
Password: postParam.Password,
|
|
MetaDescription: postParam.MetaDescription,
|
|
MetaKeywords: postParam.MetaKeywords,
|
|
Template: postParam.Template,
|
|
Thumbnail: postParam.Thumbnail,
|
|
Title: postParam.Title,
|
|
TopPriority: postParam.TopPriority,
|
|
Status: postParam.Status,
|
|
EditTime: util.TimePtr(time.Now()),
|
|
Summary: postParam.Summary,
|
|
FormatContent: postParam.Content,
|
|
}
|
|
if postParam.EditorType != nil {
|
|
post.EditorType = *postParam.EditorType
|
|
} else {
|
|
post.EditorType = consts.EditorTypeMarkdown
|
|
}
|
|
|
|
post.WordCount = util.HtmlFormatWordCount(post.FormatContent)
|
|
if postParam.Slug == "" {
|
|
post.Slug = util.Slug(postParam.Title)
|
|
} else {
|
|
post.Slug = util.Slug(postParam.Slug)
|
|
}
|
|
if postParam.CreateTime != nil {
|
|
time.UnixMilli(*postParam.CreateTime)
|
|
}
|
|
return post, nil
|
|
}
|
|
|
|
func (p postServiceImpl) CountByStatus(ctx context.Context, status consts.PostStatus) (int64, error) {
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
count, err := postDAL.WithContext(ctx).Where(postDAL.Type.Eq(consts.PostTypePost), postDAL.Status.Eq(status)).Count()
|
|
if err != nil {
|
|
return 0, WrapDBErr(err)
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
func (p postServiceImpl) Preview(ctx context.Context, postID int32) (string, error) {
|
|
post, err := p.GetByPostID(ctx, postID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
token := util.GenUUIDWithOutDash()
|
|
p.Cache.Set(token, token, time.Minute*10)
|
|
|
|
previewURL := strings.Builder{}
|
|
|
|
isEnabledAbsolutePath, err := p.OptionService.IsEnabledAbsolutePath(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if isEnabledAbsolutePath {
|
|
blogBaseURL, err := p.OptionService.GetBlogBaseURL(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
previewURL.WriteString(blogBaseURL)
|
|
}
|
|
post.Slug = url.QueryEscape(post.Slug)
|
|
fullPath, err := p.BuildFullPath(ctx, post)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
previewURL.WriteString("/")
|
|
previewURL.WriteString(fullPath)
|
|
previewURL.WriteString("?token=")
|
|
previewURL.WriteString(token)
|
|
return previewURL.String(), nil
|
|
}
|
|
|
|
func (p postServiceImpl) CountVisit(ctx context.Context) (int64, error) {
|
|
var count float64
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
err := postDAL.WithContext(ctx).Select(postDAL.Visits.Sum().IfNull(0)).Where(postDAL.Type.Eq(consts.PostTypePost), postDAL.Status.Eq(consts.PostStatusPublished)).Scan(&count)
|
|
if err != nil {
|
|
return 0, WrapDBErr(err)
|
|
}
|
|
return int64(count), nil
|
|
}
|
|
|
|
func (p postServiceImpl) CountLike(ctx context.Context) (int64, error) {
|
|
var count float64
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
err := postDAL.WithContext(ctx).Select(postDAL.Likes.Sum().IfNull(0)).Where(postDAL.Type.Eq(consts.PostTypePost), postDAL.Status.Eq(consts.PostStatusPublished)).Scan(&count)
|
|
if err != nil {
|
|
return 0, WrapDBErr(err)
|
|
}
|
|
return int64(count), nil
|
|
}
|
|
|
|
func (p postServiceImpl) GetPrevPosts(ctx context.Context, post *entity.Post, size int) ([]*entity.Post, error) {
|
|
postSort := p.OptionService.GetOrByDefault(ctx, property.IndexSort)
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
postDO := postDAL.WithContext(ctx).Where(postDAL.Status.Eq(consts.PostStatusPublished))
|
|
|
|
if postSort == "createTime" {
|
|
postDO = postDO.Where(postDAL.CreateTime.Gt(post.CreateTime)).Order(postDAL.CreateTime)
|
|
} else if postSort == "editTime" {
|
|
var editTime time.Time
|
|
if post.EditTime == nil {
|
|
editTime = post.CreateTime
|
|
} else {
|
|
editTime = *post.EditTime
|
|
}
|
|
postDO = postDO.Where(postDAL.EditTime.Gt(editTime)).Order(postDAL.EditTime)
|
|
} else if postSort == "visits" {
|
|
postDO = postDO.Where(postDAL.Visits.Gt(post.Visits)).Order(postDAL.EditTime)
|
|
} else {
|
|
return nil, nil
|
|
}
|
|
|
|
posts, err := postDO.Find()
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, nil
|
|
}
|
|
return nil, WrapDBErr(err)
|
|
}
|
|
return posts, nil
|
|
}
|
|
func (p postServiceImpl) GetNextPosts(ctx context.Context, post *entity.Post, size int) ([]*entity.Post, error) {
|
|
postSort := p.OptionService.GetOrByDefault(ctx, property.IndexSort)
|
|
postDAL := dal.GetQueryByCtx(ctx).Post
|
|
postDO := postDAL.WithContext(ctx).Where(postDAL.Status.Eq(consts.PostStatusPublished))
|
|
|
|
if postSort == "createTime" {
|
|
postDO = postDO.Where(postDAL.CreateTime.Lt(post.CreateTime)).Order(postDAL.CreateTime.Desc())
|
|
} else if postSort == "editTime" {
|
|
var editTime time.Time
|
|
if post.EditTime == nil {
|
|
editTime = post.CreateTime
|
|
} else {
|
|
editTime = *post.EditTime
|
|
}
|
|
postDO = postDO.Where(postDAL.EditTime.Lt(editTime)).Order(postDAL.EditTime.Desc())
|
|
} else if postSort == "visits" {
|
|
postDO = postDO.Where(postDAL.Visits.Lt(post.Visits)).Order(postDAL.EditTime.Desc())
|
|
} else {
|
|
return nil, nil
|
|
}
|
|
|
|
posts, err := postDO.Find()
|
|
if err != nil {
|
|
return nil, WrapDBErr(err)
|
|
}
|
|
return posts, nil
|
|
}
|