From 088fc306de1d6158b6efe1261a6cbf68a7df4f1b Mon Sep 17 00:00:00 2001 From: xing Date: Wed, 20 Dec 2023 22:30:55 +0800 Subject: [PATCH] fix bug,improve pagination cache and revise comment render --- app/actions/comment.go | 9 +- app/pkg/cache/cache.go | 38 +++-- app/pkg/cache/comments.go | 100 +++++++++++- app/pkg/cache/feed.go | 13 +- app/pkg/dao/comments.go | 160 ++++++++++++++++++- app/pkg/db/db.go | 20 ++- app/pkg/models/wp_comments.go | 5 + app/plugins/comment.go | 77 +++++---- app/plugins/pagination.go | 45 ++++++ app/theme/twentyfifteen/posts/detail.gohtml | 28 +++- app/theme/twentyseventeen/twentyseventeen.go | 6 +- app/theme/wp/comments.go | 103 ++++++++++++ app/theme/wp/detail.go | 52 ++++-- cache/cachemanager/manger.go | 33 +++- cache/cachemanager/manger_test.go | 2 +- cache/pagination.go | 49 ++++-- cache/reload/reload.go | 4 +- cache/reload/reload_test.go | 12 +- helper/maps/function.go | 2 +- helper/slice/slice.go | 13 +- helper/slice/sort.go | 23 ++- helper/slice/sort_test.go | 4 +- 22 files changed, 686 insertions(+), 112 deletions(-) create mode 100644 app/theme/wp/comments.go diff --git a/app/actions/comment.go b/app/actions/comment.go index 27be4bf..a8ef872 100644 --- a/app/actions/comment.go +++ b/app/actions/comment.go @@ -134,9 +134,14 @@ func PostComment(c *gin.Context) { err = er return } - cache.NewCommentCache().Set(c, up.RawQuery, string(s)) + uuu := "" uu, _ := url.Parse(res.Header.Get("Location")) - uuu := str.Join(uu.Path, "?", uu.RawQuery) + if up.RawQuery != "" { + cache.NewCommentCache().Set(c, up.RawQuery, string(s)) + uuu = str.Join(uu.Path, "?", uu.RawQuery) + } else { + uuu = str.Join(uu.Path) + } c.Redirect(http.StatusFound, uuu) return } diff --git a/app/pkg/cache/cache.go b/app/pkg/cache/cache.go index 1813010..bdc1476 100644 --- a/app/pkg/cache/cache.go +++ b/app/pkg/cache/cache.go @@ -8,6 +8,7 @@ import ( "github.com/fthvgb1/wp-go/app/pkg/models" "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/slice" "github.com/fthvgb1/wp-go/safety" "time" @@ -52,15 +53,30 @@ func InitActionsCommonCache() { return config.GetConfig().CacheTime.RecentCommentsCacheTime }) - cachemanager.NewMemoryMapCache(nil, dao.PostComments, c.CacheTime.PostCommentsCacheTime, - "postCommentIds", - func() time.Duration { - return config.GetConfig().CacheTime.PostCommentsCacheTime - }, - cache.NewIncreaseUpdate("comment-increaseUpdate", dao.GetIncreaseComment, 30*time.Second, - func() time.Duration { - return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime - })) + cachemanager.NewMemoryMapCache(nil, dao.CommentNum, 30*time.Second, "commentNumber", func() time.Duration { + return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime + }) + + cachemanager.NewPaginationCache( + cachemanager.NewMemoryMapCache[string, helper.PaginationData[uint64]](nil, nil, 30*time.Second, + "PostCommentsIds", func() time.Duration { + return config.GetConfig().CacheTime.PostCommentsCacheTime + }, + cache.NewIncreaseUpdate("PostCommentsIds-increaseUpdate", CommentDataIncreaseUpdate, 30*time.Second, + func() time.Duration { + return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime + }), + ), + + 1000, dao.PostCommentsIds, dao.PostCommentLocal, nil, nil, 300, "PostCommentsIds") + + cachemanager.NewMemoryMapCache(dao.CommentDates, nil, time.Hour, "postCommentData", func() time.Duration { + return config.GetConfig().CacheTime.CommentsCacheTime + }) + + cachemanager.NewMemoryMapCache[uint64, []models.Comments](nil, CommentDataIncreaseUpdates, time.Hour, func() time.Duration { + return config.GetConfig().CacheTime.CommentsCacheTime + }, "increaseComment30s", cache.NewIncreaseUpdate("increaseComment30s", IncreaseUpdates, 30*time.Second, nil)) cachemanager.NewVarMemoryCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime, "maxPostId", func() time.Duration { return config.GetConfig().CacheTime.MaxPostIdCacheTime @@ -74,10 +90,6 @@ func InitActionsCommonCache() { return config.GetConfig().CacheTime.UserInfoCacheTime }) - cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, c.CacheTime.CommentsCacheTime, "commentData", func() time.Duration { - return config.GetConfig().CacheTime.CommentsCacheTime - }) - cachemanager.NewVarMemoryCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime, "allUsername", func() time.Duration { return config.GetConfig().CacheTime.UserInfoCacheTime }) diff --git a/app/pkg/cache/comments.go b/app/pkg/cache/comments.go index ec51b3e..61efb6b 100644 --- a/app/pkg/cache/comments.go +++ b/app/pkg/cache/comments.go @@ -2,11 +2,16 @@ package cache import ( "context" + "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" + "github.com/fthvgb1/wp-go/helper/slice" + str "github.com/fthvgb1/wp-go/helper/strings" "time" ) @@ -20,23 +25,104 @@ func RecentComments(ctx context.Context, n int) (r []models.Comments) { return } -func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) { - ids, err := cachemanager.Get[[]uint64]("postCommentIds", ctx, Id, time.Second) +func PostComments(ctx context.Context, Id uint64) ([]models.PostComments, error) { + ids, err := cachemanager.Get[[]uint64]("PostCommentsIds", ctx, Id, time.Second) if err != nil { return nil, err } - return GetCommentByIds(ctx, ids) + return GetCommentDataByIds(ctx, ids) } -func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) { - return cachemanager.Get[models.Comments]("commentData", ctx, id, time.Second) +func GetCommentById(ctx context.Context, id uint64) (models.PostComments, error) { + return cachemanager.Get[models.PostComments]("postCommentData", ctx, id, time.Second) } -func GetCommentByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) { - return cachemanager.GetMultiple[models.Comments]("commentData", ctx, ids, time.Second) +func GetCommentDataByIds(ctx context.Context, ids []uint64) ([]models.PostComments, error) { + return cachemanager.GetMultiple[models.PostComments]("postCommentData", ctx, ids, time.Second) } func NewCommentCache() *cache.MapCache[string, string] { r, _ := cachemanager.GetMapCache[string, string]("NewComment") return r } + +func CommentDataIncreaseUpdates(_ context.Context, _ uint64, _ ...any) ([]models.Comments, error) { + return nil, nil +} +func IncreaseUpdates(ctx context.Context, currentData []models.Comments, postId uint64, t time.Time, _ ...any) ([]models.Comments, bool, bool, error) { + var maxId uint64 + if len(currentData) > 0 { + maxId = currentData[len(currentData)-1].CommentId + } else { + maxId, err := dao.LatestCommentId(ctx, postId) + return []models.Comments{{CommentId: maxId}}, true, false, err + } + v, err := dao.IncreaseCommentData(ctx, postId, maxId, t) + if err != nil { + return nil, false, false, err + } + if len(v) < 1 { + return nil, false, true, nil + } + m, err := dao.CommentDates(ctx, v) + if err != nil { + return nil, false, false, err + } + CommentData, _ := cachemanager.GetMapCache[uint64, models.PostComments]("postCommentData") + data := slice.Map(v, func(t uint64) models.Comments { + comments := m[t].Comments + if comments.CommentParent > 0 { + vv, ok := CommentData.Get(ctx, comments.CommentParent) + if ok && !slice.IsContained(vv.Children, comments.CommentId) { + vv.Children = append(vv.Children, comments.CommentId) + CommentData.Set(ctx, comments.CommentParent, vv) + } + } + CommentData.Set(ctx, comments.CommentId, models.PostComments{Comments: comments}) + return comments + }) + return data, true, false, nil +} + +func CommentDataIncreaseUpdate(ctx context.Context, currentData helper.PaginationData[uint64], postId string, _ time.Time, _ ...any) (data helper.PaginationData[uint64], save bool, refresh bool, err error) { + refresh = true + increaseUpdateData, _ := cachemanager.GetMapCache[uint64, []models.Comments]("increaseComment30s") + v, ok := increaseUpdateData.Get(ctx, str.ToInt[uint64](postId)) + if !ok { + return + } + if len(v) < 1 { + return + } + if len(currentData.Data) > 0 { + if slice.IsContained(currentData.Data, v[0].CommentId) { + return + } + } + + dat := slice.FilterAndMap(v, func(t models.Comments) (uint64, bool) { + if wpconfig.GetOption("thread_comments") != "1" || "1" == wpconfig.GetOption("thread_comments_depth") { + return t.CommentId, t.CommentId > 0 + } + return t.CommentId, t.CommentId > 0 && t.CommentParent == 0 + }) + if len(dat) > 0 { + save = true + refresh = false + var a []uint64 + a = append(currentData.Data, dat...) + slice.Sorts(a, wpconfig.GetOption("comment_order")) + data.Data = a + data.TotalRaw = len(data.Data) + } + return data, save, refresh, err +} + +func UpdateCommentCache(ctx context.Context, timeout time.Duration, postId uint64) (err error) { + c, _ := cachemanager.GetPaginationCache[uint64, uint64]("PostCommentsIds") + if c.IsSwitchDB(postId) { + return + } + _, err = cachemanager.Get[[]models.Comments]("increaseComment30s", ctx, postId, timeout) + return +} diff --git a/app/pkg/cache/feed.go b/app/pkg/cache/feed.go index 831b398..2ed9cdc 100644 --- a/app/pkg/cache/feed.go +++ b/app/pkg/cache/feed.go @@ -106,7 +106,12 @@ func postFeed(c context.Context, id string, _ ...any) (x string, err error) { if post.Id == 0 || err != nil { return } - comments, err := PostComments(c, post.Id) + limit := str.ToInteger(wpconfig.GetOption("comments_per_page"), 10) + ids, _, err := cachemanager.Pagination[uint64]("PostCommentsIds", c, time.Second, ID, 1, limit, "desc") + if err != nil { + return + } + comments, err := cachemanager.GetMultiple[models.PostComments]("postCommentData", c, ids, time.Second) if err != nil { return } @@ -135,7 +140,7 @@ func postFeed(c context.Context, id string, _ ...any) (x string, err error) { } } } else { - rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item { + rs.Items = slice.Map(comments, func(t models.PostComments) rss2.Item { return rss2.Item{ Title: fmt.Sprintf("评价者:%s", t.CommentAuthor), Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId), @@ -158,13 +163,13 @@ func commentsFeed(c context.Context, _ ...any) (r []string, err error) { rs.LastBuildDate = time.Now().Format(timeFormat) site := wpconfig.GetOption("siteurl") rs.AtomLink = fmt.Sprintf("%s/comments/feed", site) - com, err := GetCommentByIds(c, slice.Map(commens, func(t models.Comments) uint64 { + com, err := GetCommentDataByIds(c, slice.Map(commens, func(t models.Comments) uint64 { return t.CommentId })) if nil != err { return []string{}, err } - rs.Items = slice.Map(com, func(t models.Comments) rss2.Item { + rs.Items = slice.Map(com, func(t models.PostComments) rss2.Item { post, _ := GetPostById(c, t.CommentPostId) desc := "评论受保护:要查看请输入密码。" content := t.CommentContent diff --git a/app/pkg/dao/comments.go b/app/pkg/dao/comments.go index 448d2e0..5866a26 100644 --- a/app/pkg/dao/comments.go +++ b/app/pkg/dao/comments.go @@ -5,9 +5,11 @@ import ( "database/sql" "errors" "github.com/fthvgb1/wp-go/app/pkg/models" + "github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/slice" + str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/model" "time" ) @@ -81,12 +83,13 @@ func GetCommentByIds(ctx context.Context, ids []uint64, _ ...any) (map[uint64]mo return m, nil } -func GetIncreaseComment(ctx context.Context, currentData []uint64, k uint64, t time.Time, _ ...any) (data []uint64, save bool, refresh bool, err error) { +func GetIncreaseComment(ctx context.Context, currentData []uint64, k uint64, _ time.Time, _ ...any) (data []uint64, save bool, refresh bool, err error) { r, err := model.ChunkFind[models.Comments](ctx, 1000, model.Conditions( model.Where(model.SqlBuilder{ {"comment_approved", "1"}, {"comment_post_ID", "=", number.IntToString(k), "int"}, - {"comment_date", ">=", t.Format(time.DateTime)}, + //{"comment_date", ">=", t.Format(time.DateTime)}, + {"comment_ID", ">", number.IntToString(currentData[len(currentData)-1])}, }), model.Fields("comment_ID"), model.Order(model.SqlBuilder{ @@ -112,3 +115,156 @@ func GetIncreaseComment(ctx context.Context, currentData []uint64, k uint64, t t save = true return } + +func CommentDates(ctx context.Context, CommentIds []uint64, _ ...any) (map[uint64]models.PostComments, error) { + if len(CommentIds) < 1 { + return nil, nil + } + m := make(map[uint64]models.PostComments) + off := 0 + threadComments := wpconfig.GetOption("thread_comments") + where := model.SqlBuilder{ + {"comment_approved", "1"}, + } + var in [][]any + if threadComments == "1" { + where = append(where, []string{"and", "comment_ID", "in", "", "", "or", "comment_parent", "in", "", ""}) + } else { + where = append(where, []string{"comment_ID", "in", ""}) + } + for { + id := slice.Slice(CommentIds, off, 200) + if len(id) < 1 { + break + } + if threadComments == "1" { + in = [][]any{slice.ToAnySlice(id), slice.ToAnySlice(id)} + } else { + in = [][]any{slice.ToAnySlice(id)} + } + r, err := model.Finds[models.Comments](ctx, model.Conditions( + model.Where(where), + model.Fields("*"), + model.In(in...), + )) + if err != nil { + return m, err + } + rr := slice.GroupBy(r, func(t models.Comments) (uint64, models.Comments) { + return t.CommentParent, t + }) + mm := map[uint64][]uint64{} + for u, comments := range rr { + slice.SimpleSort(comments, slice.ASC, func(t models.Comments) uint64 { + return t.CommentId + }) + mm[u] = slice.Map(comments, func(t models.Comments) uint64 { + return t.CommentId + }) + } + for _, comments := range r { + var children []uint64 + if threadComments == "1" { + children = mm[comments.CommentId] + } + v := models.PostComments{ + Comments: comments, + Children: children, + } + m[comments.CommentId] = v + } + off += 200 + } + return m, nil +} + +func CommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) { + n, err := model.GetField[models.Posts](ctx, "comment_count", model.Conditions( + model.Where(model.SqlBuilder{{"ID", "=", number.IntToString(postId), "int"}}))) + if err != nil { + return 0, err + } + return str.ToInteger(n, 0), err +} +func PostCommentLocal(_ context.Context, data []uint64, _ uint64, page, limit int, _ ...any) ([]uint64, int, error) { + /*order := wpconfig.GetOption("comment_order") + if order == "desc" { + r := slice.ReversePagination(data, page, limit) + if len(r) < 1 { + return nil, 0, nil + } + r = slice.SortsNew(r, slice.DESC) + return r, len(data), nil + }*/ + return slice.Pagination(data, page, limit), len(data), nil +} + +func PostCommentsIds(ctx context.Context, postId uint64, page, limit, totalRaw int, _ ...any) ([]uint64, int, error) { + order := wpconfig.GetOption("comment_order") + threadComments := wpconfig.GetOption("thread_comments") + where := model.SqlBuilder{ + {"comment_approved", "1"}, + {"comment_post_ID", "=", number.IntToString(postId), "int"}, + } + if 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), + model.TotalRaw(totalRaw), + model.Fields("comment_ID"), + model.Order(model.SqlBuilder{ + {"comment_date_gmt", order}, + {"comment_ID", "asc"}, + }), + ), page, limit) + if err != nil && errors.Is(err, sql.ErrNoRows) { + err = nil + } + return slice.Map(r, func(t models.Comments) uint64 { + return t.CommentId + }), total, err +} + +func IncreaseCommentData(ctx context.Context, postId, maxCommentId uint64, _ time.Time) ([]uint64, error) { + r, err := model.ChunkFind[models.Comments](ctx, 1000, model.Conditions( + model.Where(model.SqlBuilder{ + {"comment_approved", "1"}, + {"comment_post_ID", "=", number.IntToString(postId), "int"}, + {"comment_ID", ">", number.IntToString(maxCommentId), "int"}, + }), + model.Fields("comment_ID"), + model.Order(model.SqlBuilder{ + {"comment_date_gmt", "asc"}, + {"comment_ID", "asc"}, + })), + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + err = nil + return nil, nil + } + return nil, err + } + return slice.Map(r, func(t models.Comments) uint64 { + return t.CommentId + }), err +} + +func LatestCommentId(ctx context.Context, postId uint64) (uint64, error) { + v, err := model.GetField[models.Comments](ctx, "comment_ID", model.Conditions( + model.Where(model.SqlBuilder{ + {"comment_approved", "1"}, + {"comment_post_ID", "=", number.IntToString(postId), "int"}, + }), + model.Order(model.SqlBuilder{{"comment_ID", "desc"}}), + model.Limit(1), + )) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + return 0, err + } + return str.ToInteger[uint64](v, 0), err +} diff --git a/app/pkg/db/db.go b/app/pkg/db/db.go index 50995ad..db88b78 100644 --- a/app/pkg/db/db.go +++ b/app/pkg/db/db.go @@ -3,14 +3,17 @@ package db import ( "context" "github.com/fthvgb1/wp-go/app/pkg/config" + "github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/safety" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" "log" + "runtime" ) var safeDb = safety.NewVar[*sqlx.DB](nil) +var showQuerySql func() bool func InitDb() (*safety.Var[*sqlx.DB], error) { c := config.GetConfig() @@ -36,6 +39,9 @@ func InitDb() (*safety.Var[*sqlx.DB], error) { if preDb != nil { _ = preDb.Close() } + showQuerySql = reload.FnVal("showQuerySql", false, func() bool { + return config.GetConfig().ShowQuerySql + }) return safeDb, err } @@ -44,14 +50,20 @@ func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery { nil, nil)) model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error { - if config.GetConfig().ShowQuerySql { - go log.Println(model.FormatSql(s, args...)) + if showQuerySql() { + _, f, l, _ := runtime.Caller(5) + go func() { + log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...)) + }() } return query.Selects(ctx, a, s, args...) }) model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error { - if config.GetConfig().ShowQuerySql { - go log.Println(model.FormatSql(s, args...)) + if showQuerySql() { + _, f, l, _ := runtime.Caller(5) + go func() { + log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...)) + }() } return query.Gets(ctx, a, s, args...) }) diff --git a/app/pkg/models/wp_comments.go b/app/pkg/models/wp_comments.go index 9ef96e0..cee8f7d 100644 --- a/app/pkg/models/wp_comments.go +++ b/app/pkg/models/wp_comments.go @@ -29,3 +29,8 @@ func (w Comments) PrimaryKey() string { func (w Comments) Table() string { return "wp_comments" } + +type PostComments struct { + Comments + Children []uint64 +} diff --git a/app/plugins/comment.go b/app/plugins/comment.go index 284ed72..c35d9ef 100644 --- a/app/plugins/comment.go +++ b/app/plugins/comment.go @@ -1,8 +1,10 @@ package plugins import ( + "context" "github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/wpconfig" + "github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/slice" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/gin-gonic/gin" @@ -12,11 +14,12 @@ import ( type CommentHandler struct { *gin.Context - comments []*Comments - maxDepth int - depth int - isTls bool - i CommentHtml + comments []*Comments + maxDepth int + depth int + isTls bool + i CommentHtml + isThreadComments bool } type Comments struct { @@ -25,7 +28,8 @@ type Comments struct { } type CommentHtml interface { - FormatLi(c *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string + FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string + FloorOrder(wpOrder string, i, j models.PostComments) bool } func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string { @@ -45,10 +49,10 @@ func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, m isTls: isTls, i: i, } - return h.formatComment(h.comments, true) + return h.formatComment(h.comments) } -func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html string) { +func (d CommentHandler) formatComment(comments []*Comments) (html string) { s := str.NewBuilder() if d.depth >= d.maxDepth { comments = d.findComments(comments) @@ -71,13 +75,11 @@ func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html st parent = "parent" fl = true } - s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.isTls, eo, parent)) + s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.maxDepth, 1, d.isTls, d.isThreadComments, eo, parent)) if fl { d.depth++ - s.WriteString(`
    `, d.formatComment(comment.Children, false), `
`) - if isTop { - d.depth = 1 - } + s.WriteString(`
    `, d.formatComment(comment.Children), `
`) + d.depth-- } s.WriteString("") } @@ -140,8 +142,24 @@ func CommentRender() CommonCommentFormat { type CommonCommentFormat struct { } -func (c CommonCommentFormat) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string { - return FormatLi(CommonLi(), ctx, m, depth, isTls, eo, parent) +func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { + return FormatLi(CommonLi(), m, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent) +} + +func (c CommonCommentFormat) FloorOrder(wpOrder string, i, j models.PostComments) bool { + return i.CommentId > j.CommentId +} +func respond(m models.Comments, isShow bool) string { + if !isShow { + return "" + } + pId := number.IntToString(m.CommentPostId) + cId := number.IntToString(m.CommentId) + return str.Replace(respondTml, map[string]string{ + "{{PostId}}": pId, + "{{CommentId}}": cId, + "{{CommentAuthor}}": m.CommentAuthor, + }) } var li = ` @@ -153,14 +171,11 @@ var li = ` src="{{Gravatar}}" srcset="{{Gravatar}} 2x" class="avatar avatar-56 photo" height="56" width="56" loading="lazy"> - - {{CommentAuthor}} - + {{CommentAuthor}} 说道:
- +
@@ -170,30 +185,38 @@ var li = `

{{CommentContent}}

-
+ {{respond}} + + +` + +var respondTml = `
回复 -
- +
` -` - -func FormatLi(li string, c *gin.Context, comments models.Comments, depth int, isTls bool, eo, parent string) string { +func FormatLi(li string, comments models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { + isShow := false + if isThreadComments && currentDepth < maxDepth { + isShow = true + } for k, v := range map[string]string{ "{{CommentId}}": strconv.FormatUint(comments.CommentId, 10), - "{{Depth}}": strconv.Itoa(depth), + "{{Depth}}": strconv.Itoa(currentDepth), "{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls), "{{CommentAuthorUrl}}": comments.CommentAuthorUrl, "{{CommentAuthor}}": comments.CommentAuthor, "{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10), + "{{page}}": strconv.Itoa(page), "{{CommentDateGmt}}": comments.CommentDateGmt.String(), "{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"), "{{CommentContent}}": comments.CommentContent, "{{eo}}": eo, "{{parent}}": parent, + "{{respond}}": respond(comments, isShow), } { li = strings.Replace(li, k, v, -1) } diff --git a/app/plugins/pagination.go b/app/plugins/pagination.go index 3dc95ac..4ce0e65 100644 --- a/app/plugins/pagination.go +++ b/app/plugins/pagination.go @@ -2,6 +2,8 @@ package plugins import ( "fmt" + "github.com/fthvgb1/wp-go/app/wpconfig" + "github.com/fthvgb1/wp-go/helper" str "github.com/fthvgb1/wp-go/helper/strings" "regexp" "strings" @@ -18,6 +20,13 @@ type PageEle struct { func TwentyFifteenPagination() PageEle { return twentyFifteen } +func TwentyFifteenCommentPagination() CommentPageEle { + return twentyFifteenComment +} + +type CommentPageEle struct { + PageEle +} var twentyFifteen = PageEle{ PrevEle: ``, @@ -28,6 +37,12 @@ var twentyFifteen = PageEle{ CurrentEle: ` %d`, } +var twentyFifteenComment = CommentPageEle{ + PageEle{ + PrevEle: ``, + NextEle: ``, + }, +} func (p PageEle) Current(page, totalPage, totalRow int) string { return fmt.Sprintf(p.CurrentEle, page) @@ -50,6 +65,7 @@ func (p PageEle) Middle(page int, url string) string { } var reg = regexp.MustCompile(`(/page)/(\d+)`) +var commentReg = regexp.MustCompile(`/comment-page-(\d+)`) func (p PageEle) Url(path, query string, page int) string { if !strings.Contains(path, "/page/") { @@ -67,3 +83,32 @@ func (p PageEle) Url(path, query string, page int) string { } return str.Join(path, query) } + +func (p CommentPageEle) Url(path, query string, page int) string { + if !strings.Contains(path, "/comment-page-") { + path = fmt.Sprintf("%s%s", path, "/comment-page-1") + } + path = commentReg.ReplaceAllString(path, fmt.Sprintf("/comment-page-%d", page)) + path = strings.Replace(path, "//", "/", -1) + if path == "" { + path = "/" + } + return str.Join(path, query) +} + +func (p CommentPageEle) Middle(page int, url string) string { + return "" +} +func (p CommentPageEle) Dots() string { + return "" +} +func (p CommentPageEle) Current(page, totalPage, totalRow int) string { + return "" +} +func (p CommentPageEle) Prev(url string) string { + return fmt.Sprintf(p.PrevEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较早评论", "较新评论")) +} + +func (p CommentPageEle) Next(url string) string { + return fmt.Sprintf(p.NextEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较新评论", "较早评论")) +} diff --git a/app/theme/twentyfifteen/posts/detail.gohtml b/app/theme/twentyfifteen/posts/detail.gohtml index 5d89393..25ce4cd 100644 --- a/app/theme/twentyfifteen/posts/detail.gohtml +++ b/app/theme/twentyfifteen/posts/detail.gohtml @@ -52,14 +52,30 @@ {{ if .showComment}}
- {{ if gt .post.CommentCount 0}} -

《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论

-
    - {{.comments|unescaped}} -
+ {{ if ne .comments ""}} +

《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论

+ {{if gt .totalCommentPage 1}} + + {{end}} +
    + {{.comments|unescaped}} +
+ {{if gt .totalCommentPage 1}} + + {{end}} {{end}} - {{if and (eq .post.CommentStatus "open") (eq ( "thread_comments" |getOption ) "1")}} + {{if eq .post.CommentStatus "open"}}

发表回复 diff --git a/app/theme/twentyseventeen/twentyseventeen.go b/app/theme/twentyseventeen/twentyseventeen.go index f37d6e8..d898976 100644 --- a/app/theme/twentyseventeen/twentyseventeen.go +++ b/app/theme/twentyseventeen/twentyseventeen.go @@ -1,6 +1,7 @@ package twentyseventeen import ( + "context" "fmt" "github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" @@ -14,7 +15,6 @@ import ( "github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper" str "github.com/fthvgb1/wp-go/helper/strings" - "github.com/gin-gonic/gin" "strings" ) @@ -103,7 +103,7 @@ type comment struct { plugins.CommonCommentFormat } -func (c comment) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string { +func (c comment) FormatLi(_ context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { templ := plugins.CommonLi() templ = strings.ReplaceAll(templ, `回复`) - return plugins.FormatLi(templ, ctx, m, depth, isTls, eo, parent) + return plugins.FormatLi(templ, m, depth, maxDepth, page, isTls, isThreadComments, eo, parent) } func postThumbnail(h *wp.Handle, posts *models.Posts) { diff --git a/app/theme/wp/comments.go b/app/theme/wp/comments.go new file mode 100644 index 0000000..1b8e77a --- /dev/null +++ b/app/theme/wp/comments.go @@ -0,0 +1,103 @@ +package wp + +import ( + "context" + "github.com/fthvgb1/wp-go/app/pkg/models" + "github.com/fthvgb1/wp-go/app/plugins" + "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/slice" + str "github.com/fthvgb1/wp-go/helper/strings" + "time" +) + +func RenderComment(ctx context.Context, page int, render plugins.CommentHtml, ids []uint64, timeout time.Duration, isTLS bool) (string, error) { + ca, _ := cachemanager.GetMapCache[uint64, models.PostComments]("postCommentData") + h := CommentHandle{ + maxDepth: str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5), + depth: 1, + isTls: isTLS, + html: render, + order: wpconfig.GetOption("comment_order"), + ca: ca, + threadComments: wpconfig.GetOption("thread_comments") == "1", + page: page, + } + return h.formatComments(ctx, ids, timeout) +} + +type CommentHandle struct { + maxDepth int + depth int + isTls bool + html plugins.CommentHtml + order string + page int + ca *cache.MapCache[uint64, models.PostComments] + threadComments bool +} + +func (c CommentHandle) findComments(ctx context.Context, timeout time.Duration, comments []models.PostComments) ([]models.PostComments, error) { + rr := slice.FilterAndMap(comments, func(t models.PostComments) ([]uint64, bool) { + return t.Children, len(t.Children) > 0 + }) + if len(rr) < 1 { + slice.Sort(comments, func(i, j models.PostComments) bool { + return c.html.FloorOrder(c.order, i, j) + }) + return comments, nil + } + ids := slice.Decompress(rr) + r, err := c.ca.GetCacheBatch(ctx, ids, timeout) + if err != nil { + return nil, err + } + rrr, err := c.findComments(ctx, timeout, r) + if err != nil { + return nil, err + } + comments = slice.Map(comments, func(t models.PostComments) models.PostComments { + t.Children = nil + return t + }) + comments = append(comments, rrr...) + return comments, nil +} + +func (c CommentHandle) formatComments(ctx context.Context, ids []uint64, timeout time.Duration) (html string, err error) { + comments, err := c.ca.GetCacheBatch(ctx, ids, timeout) + if err != nil { + return "", err + } + if c.depth >= c.maxDepth { + comments, err = c.findComments(ctx, timeout, comments) + } + s := str.NewBuilder() + for i, comment := range comments { + eo := "even" + if (i+1)%2 == 0 { + eo = "odd" + } + parent := "" + fl := false + if c.threadComments && len(comment.Children) > 0 && c.depth < c.maxDepth+1 { + parent = "parent" + fl = true + } + s.WriteString(c.html.FormatLi(ctx, comment.Comments, c.depth, c.maxDepth, c.page, c.isTls, c.threadComments, eo, parent)) + if fl { + c.depth++ + ss, err := c.formatComments(ctx, comment.Children, timeout) + if err != nil { + return "", err + } + s.WriteString(`
    `, ss, `
`) + c.depth-- + } + s.WriteString("") + } + + html = s.String() + return +} diff --git a/app/theme/wp/detail.go b/app/theme/wp/detail.go index 693a8dc..6ebf131 100644 --- a/app/theme/wp/detail.go +++ b/app/theme/wp/detail.go @@ -10,15 +10,22 @@ import ( "github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins/wpposts" "github.com/fthvgb1/wp-go/app/wpconfig" + "github.com/fthvgb1/wp-go/cache/cachemanager" + "github.com/fthvgb1/wp-go/helper/number" str "github.com/fthvgb1/wp-go/helper/strings" + "github.com/fthvgb1/wp-go/plugin/pagination" "net/http" + "time" ) type DetailHandle struct { *Handle CommentRender plugins.CommentHtml - Comments []models.Comments + Comments []uint64 + Page int + Limit int Post models.Posts + TotalRaw int } func NewDetailHandle(handle *Handle) *DetailHandle { @@ -80,11 +87,29 @@ func (d *DetailHandle) PasswordProject() { } } func (d *DetailHandle) Comment() { - comments, err := cache.PostComments(d.C, d.Post.Id) - logs.IfError(err, "get d.Post comment", d.Post.Id) - d.ginH["comments"] = comments - d.Comments = comments - + err := cache.UpdateCommentCache(d.C, time.Second, d.Post.Id) + d.ginH["totalCommentNum"] = 0 + d.ginH["totalCommentPage"] = 1 + d.ginH["commentPageNav"] = "" + d.ginH["commentOrder"] = wpconfig.GetOption("comment_order") + logs.IfError(err, "increase update comments err") + d.Page = str.ToInteger(d.C.Param("page"), 1) + d.ginH["currentPage"] = d.Page + d.Limit = str.ToInteger(wpconfig.GetOption("comments_per_page"), 5) + ids, totalCommentNum, err := cachemanager.Pagination[uint64]("PostCommentsIds", d.C, time.Second, d.Post.Id, d.Page, d.Limit, "desc") + if err != nil { + d.SetErr(err) + return + } + d.TotalRaw = totalCommentNum + num, err := cachemanager.Get[int]("commentNumber", d.C, d.Post.Id, time.Second) + if err != nil { + d.SetErr(err) + return + } + d.ginH["totalCommentNum"] = num + d.ginH["totalCommentPage"] = number.DivideCeil(totalCommentNum, d.Limit) + d.Comments = ids } func (d *DetailHandle) RenderComment() { @@ -97,10 +122,19 @@ func (d *DetailHandle) RenderComment() { ableComment = false } d.ginH["showComment"] = ableComment - if len(d.Comments) > 0 && ableComment { - dep := str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5) - d.ginH["comments"] = plugins.FormatComments(d.C, d.CommentRender, d.Comments, dep) + d.ginH["comments"] = "" + if len(d.Comments) < 0 || !ableComment { + return } + var err error + d.ginH["comments"], err = RenderComment(d.C, d.Page, d.CommentRender, d.Comments, 2*time.Second, d.IsHttps()) + if err != nil { + d.SetErr(err) + return + } + paginations := pagination.NewParsePagination(d.TotalRaw, d.Limit, d.Page, 1, d.C.Request.URL.RawQuery, d.C.Request.URL.Path) + d.ginH["commentPageNav"] = pagination.Paginate(plugins.TwentyFifteenCommentPagination(), paginations) + } func (d *DetailHandle) ContextPost() { diff --git a/cache/cachemanager/manger.go b/cache/cachemanager/manger.go index e0090ea..6910978 100644 --- a/cache/cachemanager/manger.go +++ b/cache/cachemanager/manger.go @@ -185,7 +185,9 @@ func NewPaginationCache[K comparable, V any](m *cache.MapCache[string, helper.Pa if fet == nil { fet = reload.FnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil) } - return cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name) + p := cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name) + mapCache.Store(name, p) + return p } func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapBatchFn[K, V], fn cache.MapSingleFn[K, V], args ...any) *cache.MapCache[K, V] { @@ -219,9 +221,9 @@ func SetExpireTime(c cache.SetTime, name string, expireTime time.Duration, expir c.SetExpiredTime(fn) } -func ChangeExpireTime(t time.Duration, name ...string) { +func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) { for _, s := range name { - reload.ChangeFnVal(s, t) + reload.ChangeFnVal(s, t, coverConf) } } @@ -291,3 +293,28 @@ func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool) vv, err := getMap[K, V](name) return vv, err == nil } + +func GetPaginationCache[K comparable, V any](name string) (*cache.Pagination[K, V], bool) { + v, err := getPagination[K, V](name) + return v, err == nil +} + +func Pagination[V any, K comparable](name string, ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) { + v, err := getPagination[K, V](name) + if err != nil { + return nil, 0, err + } + return v.Pagination(ctx, timeout, k, page, limit, a...) +} + +func getPagination[K comparable, T any](name string) (*cache.Pagination[K, T], error) { + m, ok := mapCache.Load(name) + if !ok { + return nil, errors.New(str.Join("cache ", name, " doesn't exist")) + } + vk, ok := m.(*cache.Pagination[K, T]) + if !ok { + return nil, errors.New(str.Join("cache ", name, " type error")) + } + return vk, nil +} diff --git a/cache/cachemanager/manger_test.go b/cache/cachemanager/manger_test.go index 7bc9c5a..2dd0f92 100644 --- a/cache/cachemanager/manger_test.go +++ b/cache/cachemanager/manger_test.go @@ -72,7 +72,7 @@ func TestSetExpireTime(t *testing.T) { fmt.Println(c.Get(ctx, "xx")) time.Sleep(time.Second) fmt.Println(c.Get(ctx, "xx")) - ChangeExpireTime(3*time.Second, "xx") + ChangeExpireTime(3*time.Second, true, "xx") c.Set(ctx, "xx", "yyy") time.Sleep(time.Second) fmt.Println(c.Get(ctx, "xx")) diff --git a/cache/pagination.go b/cache/pagination.go index c62183f..d1be523 100644 --- a/cache/pagination.go +++ b/cache/pagination.go @@ -30,7 +30,14 @@ type DbFn[K comparable, V any] func(ctx context.Context, k K, page, limit, total type LocalFn[K comparable, V any] func(ctx context.Context, data []V, k K, page, limit int, a ...any) ([]V, int, error) -func NewPagination[K comparable, V any](m *MapCache[string, helper.PaginationData[V]], maxNum func() int, dbFn DbFn[K, V], localFn LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string, batchFetchNum func() int, name string) *Pagination[K, V] { +func (p *Pagination[K, V]) IsSwitchDB(k K) bool { + v, _ := p.isSwitch.Load(k) + return v == true +} + +func NewPagination[K comparable, V any](m *MapCache[string, helper.PaginationData[V]], maxNum func() int, + dbFn DbFn[K, V], localFn LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string, + batchFetchNum func() int, name string) *Pagination[K, V] { if dbKeyFn == nil { dbKeyFn = func(k K, a ...any) string { s := str.NewBuilder() @@ -66,6 +73,7 @@ func (p *Pagination[K, V]) Pagination(ctx context.Context, timeout time.Duration data, total, err := p.paginationByLocal(ctx, timeout, k, page, limit, a...) if err != nil { if errors.Is(err, switchDb) { + p.isSwitch.Store(k, true) err = nil return p.paginationByDB(ctx, timeout, k, page, limit, total, a...) } @@ -101,36 +109,47 @@ func (p *Pagination[K, V]) paginationByLocal(ctx context.Context, timeout time.D if err != nil { return nil, 0, err } + if totalRaw < 1 { + data.Data = nil + data.TotalRaw = 0 + p.Set(ctx, key, data) + return nil, 0, nil + } if totalRaw >= p.maxNum() { - p.isSwitch.Store(k, true) return nil, totalRaw, switchDb } - if len(da) < totalRaw { - totalPage := number.DivideCeil(totalRaw, batchNum) - for i := 1; i <= totalPage; i++ { - daa, _, err := p.fetchDb(ctx, timeout, k, i, batchNum, totalRaw, a...) - if err != nil { - return nil, 0, err - } - da = append(da, daa...) + totalPage := number.DivideCeil(totalRaw, batchNum) + for i := 1; i <= totalPage; i++ { + daa, _, err := p.fetchDb(ctx, timeout, k, i, batchNum, totalRaw, a...) + if err != nil { + return nil, 0, err } - data.Data = da - data.TotalRaw = totalRaw - p.Set(ctx, key, data) + da = append(da, daa...) } + data.Data = da + data.TotalRaw = totalRaw + p.Set(ctx, key, data) return p.localFn(ctx, data.Data, k, page, limit, a...) } +func (p *Pagination[K, V]) dbGet(ctx context.Context, key string) (helper.PaginationData[V], bool) { + data, ok := p.Get(ctx, key) + if ok && p.increaseUpdate != nil && p.increaseUpdate.CycleTime() > p.GetExpireTime(ctx)-p.Ttl(ctx, key) { + return data, true + } + return data, false +} + func (p *Pagination[K, V]) paginationByDB(ctx context.Context, timeout time.Duration, k K, page, limit, totalRaw int, a ...any) ([]V, int, error) { key := p.dbKeyFn(k, append([]any{page, limit}, a...)...) - data, ok := p.Get(ctx, key) + data, ok := p.dbGet(ctx, key) if ok { return data.Data, data.TotalRaw, nil } p.mux.Lock() defer p.mux.Unlock() - data, ok = p.Get(ctx, key) + data, ok = p.dbGet(ctx, key) if ok { return data.Data, data.TotalRaw, nil } diff --git a/cache/reload/reload.go b/cache/reload/reload.go index f92559e..a16a741 100644 --- a/cache/reload/reload.go +++ b/cache/reload/reload.go @@ -304,7 +304,7 @@ func FnVal[T any](name string, t T, fn func() T) func() T { } } -func ChangeFnVal[T any](name string, val T) { +func ChangeFnVal[T any](name string, val T, coverConf bool) { v, ok := anyMap.Load(name) if !ok { return @@ -313,7 +313,7 @@ func ChangeFnVal[T any](name string, val T) { if !ok { return } - if !vv.isUseManger.Load() { + if coverConf && !vv.isUseManger.Load() { vv.isUseManger.Store(true) } vv.v.Store(val) diff --git a/cache/reload/reload_test.go b/cache/reload/reload_test.go index a19ce64..9267490 100644 --- a/cache/reload/reload_test.go +++ b/cache/reload/reload_test.go @@ -8,22 +8,22 @@ import ( func TestFlushMapVal(t *testing.T) { t.Run("t1", func(t *testing.T) { c := 0 - v := GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) int { + v := GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) (int, bool) { c++ - return 33 + return 33, true }) fmt.Println(v) FlushMapVal("key", 2) - v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) int { + v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) (int, bool) { fmt.Println("xxxxx") - return 33 + return 33, true }) fmt.Println(v) FlushAnyVal("key") - v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) int { + v = GetAnyValMapBy[int, int, struct{}]("key", 2, struct{}{}, func(a struct{}) (int, bool) { fmt.Println("yyyy") - return 33 + return 33, true }) fmt.Println(v) }) diff --git a/helper/maps/function.go b/helper/maps/function.go index 2323a7a..f835170 100644 --- a/helper/maps/function.go +++ b/helper/maps/function.go @@ -185,7 +185,7 @@ func DescEahByKey[K constraints.Ordered, V any](m map[K]V, fn func(K, V)) { orderedEahByKey(m, slice.ASC, fn) } -func orderedEahByKey[K constraints.Ordered, V any](m map[K]V, ordered int, fn func(K, V)) { +func orderedEahByKey[K constraints.Ordered, V any](m map[K]V, ordered string, fn func(K, V)) { keys := Keys(m) slice.Sorts(keys, ordered) for _, key := range keys { diff --git a/helper/slice/slice.go b/helper/slice/slice.go index afa88ff..61ea503 100644 --- a/helper/slice/slice.go +++ b/helper/slice/slice.go @@ -125,12 +125,23 @@ func Pagination[T any](arr []T, page, pageSize int) []T { if start > l { start = l } - end := page * pageSize + end := start + pageSize if l < end { end = l } return arr[start:end] } +func ReversePagination[T any](arr []T, page, pageSize int) []T { + end := len(arr) - (page-1)*pageSize + if end <= 0 { + return nil + } + start := end - pageSize + if start < 0 { + start = 0 + } + return arr[start:end] +} func Chunk[T any](arr []T, size int) [][]T { var r [][]T diff --git a/helper/slice/sort.go b/helper/slice/sort.go index 9efbec6..646a877 100644 --- a/helper/slice/sort.go +++ b/helper/slice/sort.go @@ -6,8 +6,8 @@ import ( ) const ( - ASC = iota - DESC + ASC = "asc" + DESC = "desc" ) type anyArr[T any] struct { @@ -44,7 +44,7 @@ func StableSort[T any](arr []T, fn func(i, j T) bool) { sort.Stable(slice) } -func Sorts[T constraints.Ordered](a []T, order int) { +func Sorts[T constraints.Ordered](a []T, order string) { slice := anyArr[T]{ data: a, fn: func(i, j T) bool { @@ -56,8 +56,23 @@ func Sorts[T constraints.Ordered](a []T, order int) { } sort.Sort(slice) } +func SortsNew[T constraints.Ordered](a []T, order string) []T { + r := make([]T, len(a)) + copy(r, a) + slice := anyArr[T]{ + data: r, + fn: func(i, j T) bool { + if order == DESC { + return i > j + } + return i < j + }, + } + sort.Sort(slice) + return r +} -func SimpleSort[T any, O constraints.Ordered](a []T, order int, fn func(t T) O) { +func SimpleSort[T any, O constraints.Ordered](a []T, order string, fn func(t T) O) { slice := anyArr[T]{ data: a, fn: func(i, j T) bool { diff --git a/helper/slice/sort_test.go b/helper/slice/sort_test.go index a8805be..893aec2 100644 --- a/helper/slice/sort_test.go +++ b/helper/slice/sort_test.go @@ -74,7 +74,7 @@ func TestSort(t *testing.T) { func TestSorts(t *testing.T) { type args[T constraints.Ordered] struct { a []T - order int + order string } type testCase[T constraints.Ordered] struct { name string @@ -169,7 +169,7 @@ func TestSimpleSort(t *testing.T) { type args[T any, O constraints.Ordered] struct { a []T - order int + order string fn func(t T) O } type testCase[T any, O constraints.Ordered] struct {