diff --git a/README.md b/README.md index a83f316..fbe3b99 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,10 @@ - 博客页面至多显示数量 - Feed中显示最近数量 - 讨论 - - 其他评论设置-启用评论嵌套,最多嵌套层数 - - 在每个页面顶部显示 `新旧`评论 + - 其他评论设置 + - `启用|禁止`评论嵌套,最多嵌套层数 + - 分页显示评论,每页显示评论条数,默认显示`最前/后`页 + - 在每个页面顶部显示 `新旧`评论 #### 主题支持程度 diff --git a/app/pkg/cache/cache.go b/app/pkg/cache/cache.go index 3a0cf7c..1a15642 100644 --- a/app/pkg/cache/cache.go +++ b/app/pkg/cache/cache.go @@ -47,7 +47,7 @@ func InitActionsCommonCache() { return config.GetConfig().CacheTime.RecentPostCacheTime }) - cachemanager.NewVarMemoryCache(dao.RecentComments, c.CacheTime.RecentCommentsCacheTime, "recentComments", func() time.Duration { + cachemanager.NewVarMemoryCache(RecentComment, c.CacheTime.RecentCommentsCacheTime, "recentComments", func() time.Duration { return config.GetConfig().CacheTime.RecentCommentsCacheTime }) @@ -58,6 +58,9 @@ func InitActionsCommonCache() { cachemanager.NewMemoryMapCache(nil, PostTopComments, 30*time.Second, "PostCommentsIds", func() time.Duration { return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime }) + cachemanager.NewMemoryMapCache(nil, dao.PostTopCommentNum, 30*time.Second, "postTopCommentsNum", func() time.Duration { + return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime + }) cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, time.Hour, "postCommentData", func() time.Duration { return config.GetConfig().CacheTime.CommentsCacheTime @@ -131,7 +134,7 @@ func CategoriesTags(ctx context.Context, t ...string) []models.TermsMy { if len(t) > 0 { tt = t[0] } - r, err := cachemanager.Get[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second) + r, err := cachemanager.GetBy[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second) logs.IfError(err, "get category fail") return r } @@ -140,7 +143,7 @@ func AllCategoryTagsNames(ctx context.Context, t ...string) map[string]struct{} if len(t) > 0 { tt = t[0] } - r, err := cachemanager.Get[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second) + r, err := cachemanager.GetBy[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second) if err != nil { logs.Error(err, "get category fail") return nil diff --git a/app/pkg/cache/comments.go b/app/pkg/cache/comments.go index 03c9a62..be866d0 100644 --- a/app/pkg/cache/comments.go +++ b/app/pkg/cache/comments.go @@ -2,13 +2,15 @@ package cache import ( "context" + "fmt" "github.com/fthvgb1/wp-go/app/pkg/dao" "github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/models" + "github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache/cachemanager" - "github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper/number" + str "github.com/fthvgb1/wp-go/helper/strings" "time" ) @@ -23,7 +25,7 @@ func RecentComments(ctx context.Context, n int) (r []models.Comments) { } func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) { - ids, err := cachemanager.Get[[]uint64]("PostCommentsIds", ctx, Id, time.Second) + ids, err := cachemanager.GetBy[[]uint64]("PostCommentsIds", ctx, Id, time.Second) if err != nil { return nil, err } @@ -31,11 +33,11 @@ func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) { } func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) { - return cachemanager.Get[models.Comments]("postCommentData", ctx, id, time.Second) + return cachemanager.GetBy[models.Comments]("postCommentData", ctx, id, time.Second) } func GetCommentDataByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) { - return cachemanager.GetMultiple[models.Comments]("postCommentData", ctx, ids, time.Second) + return cachemanager.GetBatchBy[models.Comments]("postCommentData", ctx, ids, time.Second) } func NewCommentCache() *cache.MapCache[string, string] { @@ -43,17 +45,67 @@ func NewCommentCache() *cache.MapCache[string, string] { return r } -func PostTopComments(ctx context.Context, _ string, a ...any) (helper.PaginationData[uint64], error) { +func PostTopComments(ctx context.Context, _ string, a ...any) ([]uint64, error) { postId := a[0].(uint64) page := a[1].(int) limit := a[2].(int) total := a[3].(int) - v, total, err := dao.PostCommentsIds(ctx, postId, page, limit, total) + v, _, err := dao.PostCommentsIds(ctx, postId, page, limit, total) if err != nil { - return helper.PaginationData[uint64]{}, err + return nil, err } - return helper.PaginationData[uint64]{ - Data: v, - TotalRaw: total, - }, nil + return v, nil +} + +func RecentComment(ctx context.Context, a ...any) (r []models.Comments, err error) { + r, err = dao.RecentComments(ctx, a...) + if err != nil { + return r, err + } + for i, comment := range r { + r[i].CommentAuthorUrl, err = GetCommentUrl(ctx, comment.CommentId, comment.CommentPostId) + if err != nil { + return nil, err + } + } + return +} + +func GetCommentUrl(ctx context.Context, commentId, postId uint64) (string, error) { + if wpconfig.GetOption("page_comments") != "1" { + return fmt.Sprintf("/p/%d#comment-%d", postId, commentId), nil + } + commentsPerPage := str.ToInteger(wpconfig.GetOption("comments_per_page"), 5) + topCommentId, err := AncestorCommentId(ctx, commentId) + if err != nil { + return "", err + } + totalNum, err := cachemanager.GetBy[int]("postTopCommentsNum", ctx, postId, time.Second) + if err != nil { + return "", err + } + if totalNum <= commentsPerPage { + return fmt.Sprintf("/p/%d#comment-%d", postId, commentId), nil + } + num, err := dao.PreviousCommentNum(ctx, topCommentId, postId) + if err != nil { + return "", err + } + order := wpconfig.GetOption("comment_order") + page := number.DivideCeil(num+1, commentsPerPage) + if order == "desc" { + page = number.DivideCeil(totalNum-num, commentsPerPage) + } + return fmt.Sprintf("/p/%d/comment-page-%d/#comment-%d", postId, page, commentId), nil +} + +func AncestorCommentId(ctx context.Context, commentId uint64) (uint64, error) { + comment, err := cachemanager.GetBy[models.Comments]("postCommentData", ctx, commentId, time.Second) + if err != nil { + return 0, err + } + if comment.CommentParent == 0 { + return comment.CommentId, nil + } + return AncestorCommentId(ctx, comment.CommentParent) } diff --git a/app/pkg/cache/feed.go b/app/pkg/cache/feed.go index f272856..f44d22d 100644 --- a/app/pkg/cache/feed.go +++ b/app/pkg/cache/feed.go @@ -111,7 +111,7 @@ func postFeed(c context.Context, id string, _ ...any) (x string, err error) { if err != nil { return } - comments, err := cachemanager.GetMultiple[models.Comments]("postCommentData", c, ids, time.Second) + comments, err := cachemanager.GetBatchBy[models.Comments]("postCommentData", c, ids, time.Second) if err != nil { return } @@ -182,7 +182,7 @@ func commentsFeed(c context.Context, _ ...any) (r []string, err error) { } return rss2.Item{ Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle), - Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId), + Link: t.CommentAuthorUrl, Creator: t.CommentAuthor, Description: desc, PubDate: t.CommentDateGmt.Format(timeFormat), diff --git a/app/pkg/cache/postmeta.go b/app/pkg/cache/postmeta.go index 618b9fb..1556911 100644 --- a/app/pkg/cache/postmeta.go +++ b/app/pkg/cache/postmeta.go @@ -7,8 +7,8 @@ import ( ) func GetPostMetaByPostIds(ctx context.Context, ids []uint64) ([]map[string]any, error) { - return cachemanager.GetMultiple[map[string]any]("postMetaData", ctx, ids, time.Second) + return cachemanager.GetBatchBy[map[string]any]("postMetaData", ctx, ids, time.Second) } func GetPostMetaByPostId(ctx context.Context, id uint64) (map[string]any, error) { - return cachemanager.Get[map[string]any]("postMetaData", ctx, id, time.Second) + return cachemanager.GetBy[map[string]any]("postMetaData", ctx, id, time.Second) } diff --git a/app/pkg/cache/posts.go b/app/pkg/cache/posts.go index 9424654..023fcba 100644 --- a/app/pkg/cache/posts.go +++ b/app/pkg/cache/posts.go @@ -15,15 +15,15 @@ import ( ) func GetPostById(ctx context.Context, id uint64) (models.Posts, error) { - return cachemanager.Get[models.Posts]("postData", ctx, id, time.Second) + return cachemanager.GetBy[models.Posts]("postData", ctx, id, time.Second) } func GetPostsByIds(ctx context.Context, ids []uint64) ([]models.Posts, error) { - return cachemanager.GetMultiple[models.Posts]("postData", ctx, ids, time.Second) + return cachemanager.GetBatchBy[models.Posts]("postData", ctx, ids, time.Second) } func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) { - ids, err := cachemanager.Get[dao.PostIds]("searchPostIds", ctx, key, time.Second, args...) + ids, err := cachemanager.GetBy[dao.PostIds]("searchPostIds", ctx, key, time.Second, args...) if err != nil { return } @@ -33,7 +33,7 @@ func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts, } func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) { - ids, err := cachemanager.Get[dao.PostIds]("listPostIds", ctx, key, time.Second, args...) + ids, err := cachemanager.GetBy[dao.PostIds]("listPostIds", ctx, key, time.Second, args...) if err != nil { return } @@ -59,7 +59,7 @@ func RecentPosts(ctx context.Context, n int) (r []models.Posts) { } func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.Posts, err error) { - postCtx, err := cachemanager.Get[dao.PostContext]("postContext", ctx, id, time.Second, date) + postCtx, err := cachemanager.GetBy[dao.PostContext]("postContext", ctx, id, time.Second, date) if err != nil { return models.Posts{}, models.Posts{}, err } @@ -69,7 +69,7 @@ func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next } func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.Posts, total int, err error) { - res, err := cachemanager.Get[[]uint64]("monthPostIds", ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month) + res, err := cachemanager.GetBy[[]uint64]("monthPostIds", ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month) if err != nil { return } diff --git a/app/pkg/cache/users.go b/app/pkg/cache/users.go index fb7af91..fdf8438 100644 --- a/app/pkg/cache/users.go +++ b/app/pkg/cache/users.go @@ -9,7 +9,7 @@ import ( ) func GetUserByName(ctx context.Context, username string) (models.Users, error) { - return cachemanager.Get[models.Users]("usernameMapToUserData", ctx, username, time.Second) + return cachemanager.GetBy[models.Users]("usernameMapToUserData", ctx, username, time.Second) } func GetAllUsername(ctx context.Context) (map[string]struct{}, error) { @@ -17,7 +17,7 @@ func GetAllUsername(ctx context.Context) (map[string]struct{}, error) { } func GetUserById(ctx context.Context, uid uint64) models.Users { - r, err := cachemanager.Get[models.Users]("userData", ctx, uid, time.Second) + r, err := cachemanager.GetBy[models.Users]("userData", ctx, uid, time.Second) logs.IfError(err, "get user", uid) return r } diff --git a/app/pkg/dao/comments.go b/app/pkg/dao/comments.go index 70c69d7..24c203f 100644 --- a/app/pkg/dao/comments.go +++ b/app/pkg/dao/comments.go @@ -91,25 +91,51 @@ func CommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) { return str.ToInteger(n, 0), err } -func PostCommentsIds(ctx context.Context, postId uint64, page, limit, totalRaw int, _ ...any) ([]uint64, int, error) { - order := wpconfig.GetOption("comment_order") +func PostTopCommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) { + v, err := model.GetField[models.Comments](ctx, "count(*) num", model.Conditions( + model.Where(postTopCommentNumWhere(postId)), + )) + if err != nil { + return 0, err + } + return str.ToInteger(v, 0), nil +} + +func postTopCommentNumWhere(postId uint64) model.SqlBuilder { threadComments := wpconfig.GetOption("thread_comments") + pageComments := wpconfig.GetOption("page_comments") where := model.SqlBuilder{ {"comment_approved", "1"}, {"comment_post_ID", "=", number.IntToString(postId), "int"}, } - if threadComments == "1" || "1" == wpconfig.GetOption("thread_comments_depth") { + if pageComments != "1" || threadComments == "1" || "1" == wpconfig.GetOption("thread_comments_depth") { where = append(where, []string{"comment_parent", "0"}) } - r, total, err := model.Pagination[models.Comments](ctx, model.Conditions( - model.Where(where), + return where +} + +func PostCommentsIds(ctx context.Context, postId uint64, page, limit, totalRaw int, _ ...any) ([]uint64, int, error) { + order := wpconfig.GetOption("comment_order") + pageComments := wpconfig.GetOption("page_comments") + condition := model.Conditions( + model.Where(postTopCommentNumWhere(postId)), model.TotalRaw(totalRaw), model.Fields("comment_ID"), model.Order(model.SqlBuilder{ {"comment_date_gmt", order}, {"comment_ID", "asc"}, }), - ), page, limit) + ) + var r []models.Comments + var total int + var err error + if pageComments != "1" { + r, err = model.ChunkFind[models.Comments](ctx, 300, condition) + total = len(r) + } else { + r, total, err = model.Pagination[models.Comments](ctx, condition, page, limit) + } + if err != nil && errors.Is(err, sql.ErrNoRows) { err = nil } @@ -133,8 +159,27 @@ func CommentChildren(ctx context.Context, commentIds []uint64, _ ...any) (r map[ } return } - r = slice.GroupBy(rr, func(v models.Comments) (uint64, uint64) { + rrr := slice.GroupBy(rr, func(v models.Comments) (uint64, uint64) { return v.CommentParent, v.CommentId }) + r = make(map[uint64][]uint64) + for _, id := range commentIds { + r[id] = rrr[id] + } return } + +func PreviousCommentNum(ctx context.Context, commentId, postId uint64) (int, error) { + v, err := model.GetField[models.Comments](ctx, "count(*)", model.Conditions( + model.Where(model.SqlBuilder{ + {"comment_approved", "1"}, + {"comment_post_ID", "=", number.IntToString(postId), "int"}, + {"comment_ID", "<", number.IntToString(commentId), "int"}, + {"comment_parent", "=", "0", "int"}, + }), + )) + if err != nil { + return 0, err + } + return str.ToInteger(v, 0), nil +} diff --git a/app/plugins/comment.go b/app/plugins/comment.go index 11cbd8e..def5a0b 100644 --- a/app/plugins/comment.go +++ b/app/plugins/comment.go @@ -29,7 +29,7 @@ type Comments struct { type CommentHtml interface { FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string - FloorOrder(wpOrder string, i, j models.Comments) bool + FloorOrder(i, j models.Comments) bool } func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string { @@ -146,7 +146,7 @@ func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, curr return FormatLi(li, m, respondsFn, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent) } -func (c CommonCommentFormat) FloorOrder(wpOrder string, i, j models.Comments) bool { +func (c CommonCommentFormat) FloorOrder(i, j models.Comments) bool { return i.CommentId > j.CommentId } diff --git a/app/plugins/digest.go b/app/plugins/digest.go index 4bf787c..4239b08 100644 --- a/app/plugins/digest.go +++ b/app/plugins/digest.go @@ -71,7 +71,7 @@ func PostsMore(id uint64, content, closeTag string) string { } func Digest(ctx context.Context, post *models.Posts, limit int) { - content, _ := cachemanager.Get[string]("digestPlugin", ctx, post.Id, time.Second, ctx, post.PostContent, post.Id, limit) + content, _ := cachemanager.GetBy[string]("digestPlugin", ctx, post.Id, time.Second, ctx, post.PostContent, post.Id, limit) post.PostContent = content } diff --git a/app/theme/wp/comments.go b/app/theme/wp/comments.go index 5a07ea0..4bf3069 100644 --- a/app/theme/wp/comments.go +++ b/app/theme/wp/comments.go @@ -41,11 +41,14 @@ type CommentHandle struct { threadComments bool } -func (c CommentHandle) findComments(ctx context.Context, timeout time.Duration, comments []models.Comments) ([]models.Comments, error) { +func (c CommentHandle) findGrandchildComments(ctx context.Context, timeout time.Duration, comments []models.Comments) ([]models.Comments, error) { parentIds := slice.Map(comments, func(t models.Comments) uint64 { return t.CommentId }) children, err := c.children.GetCacheBatch(ctx, parentIds, timeout) + if err != nil { + return nil, err + } rr := slice.FilterAndMap(children, func(t []uint64) ([]uint64, bool) { return t, len(t) > 0 }) @@ -57,13 +60,13 @@ func (c CommentHandle) findComments(ctx context.Context, timeout time.Duration, if err != nil { return nil, err } - rrr, err := c.findComments(ctx, timeout, r) + rrr, err := c.findGrandchildComments(ctx, timeout, r) if err != nil { return nil, err } comments = append(comments, rrr...) slice.Sort(comments, func(i, j models.Comments) bool { - return c.html.FloorOrder(c.order, i, j) + return c.html.FloorOrder(i, j) }) return comments, nil } @@ -75,12 +78,12 @@ func (c CommentHandle) formatComments(ctx context.Context, ids []uint64, timeout } if c.depth > 1 && c.depth < c.maxDepth { slice.Sort(comments, func(i, j models.Comments) bool { - return c.html.FloorOrder(c.order, i, j) + return c.html.FloorOrder(i, j) }) } fixChildren := false if c.depth >= c.maxDepth { - comments, err = c.findComments(ctx, timeout, comments) + comments, err = c.findGrandchildComments(ctx, timeout, comments) if err != nil { return "", err } @@ -97,10 +100,9 @@ func (c CommentHandle) formatComments(ctx context.Context, ids []uint64, timeout var children []uint64 if !fixChildren { children, err = c.children.GetCache(ctx, comment.CommentId, timeout) - } - - if err != nil { - return "", err + if err != nil { + return "", err + } } if c.threadComments && len(children) > 0 && c.depth < c.maxDepth+1 { parent = "parent" diff --git a/app/theme/wp/components/widget/recentcomments.go b/app/theme/wp/components/widget/recentcomments.go index 6ff4175..51e63ca 100644 --- a/app/theme/wp/components/widget/recentcomments.go +++ b/app/theme/wp/components/widget/recentcomments.go @@ -59,9 +59,9 @@ func RecentComments(h *wp.Handle, id string) string { comments := slice.Map(cache.RecentComments(h.C, int(conf["number"].(int64))), func(t models.Comments) string { return fmt.Sprintf(`