Compare commits

..

No commits in common. "088fc306de1d6158b6efe1261a6cbf68a7df4f1b" and "f257a966e6b9a17590831f2f47ffc43502eb9e1e" have entirely different histories.

23 changed files with 113 additions and 690 deletions

View File

@ -134,14 +134,9 @@ func PostComment(c *gin.Context) {
err = er err = er
return return
} }
uuu := ""
uu, _ := url.Parse(res.Header.Get("Location"))
if up.RawQuery != "" {
cache.NewCommentCache().Set(c, up.RawQuery, string(s)) cache.NewCommentCache().Set(c, up.RawQuery, string(s))
uuu = str.Join(uu.Path, "?", uu.RawQuery) uu, _ := url.Parse(res.Header.Get("Location"))
} else { uuu := str.Join(uu.Path, "?", uu.RawQuery)
uuu = str.Join(uu.Path)
}
c.Redirect(http.StatusFound, uuu) c.Redirect(http.StatusFound, uuu)
return return
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/cachemanager" "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/helper/slice"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"time" "time"
@ -53,30 +52,15 @@ func InitActionsCommonCache() {
return config.GetConfig().CacheTime.RecentCommentsCacheTime return config.GetConfig().CacheTime.RecentCommentsCacheTime
}) })
cachemanager.NewMemoryMapCache(nil, dao.CommentNum, 30*time.Second, "commentNumber", func() time.Duration { cachemanager.NewMemoryMapCache(nil, dao.PostComments, c.CacheTime.PostCommentsCacheTime,
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime "postCommentIds",
}) func() time.Duration {
cachemanager.NewPaginationCache(
cachemanager.NewMemoryMapCache[string, helper.PaginationData[uint64]](nil, nil, 30*time.Second,
"PostCommentsIds", func() time.Duration {
return config.GetConfig().CacheTime.PostCommentsCacheTime return config.GetConfig().CacheTime.PostCommentsCacheTime
}, },
cache.NewIncreaseUpdate("PostCommentsIds-increaseUpdate", CommentDataIncreaseUpdate, 30*time.Second, cache.NewIncreaseUpdate("comment-increaseUpdate", dao.GetIncreaseComment, 30*time.Second,
func() time.Duration { func() time.Duration {
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime 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 { cachemanager.NewVarMemoryCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime, "maxPostId", func() time.Duration {
return config.GetConfig().CacheTime.MaxPostIdCacheTime return config.GetConfig().CacheTime.MaxPostIdCacheTime
@ -90,6 +74,10 @@ func InitActionsCommonCache() {
return config.GetConfig().CacheTime.UserInfoCacheTime 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 { cachemanager.NewVarMemoryCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime, "allUsername", func() time.Duration {
return config.GetConfig().CacheTime.UserInfoCacheTime return config.GetConfig().CacheTime.UserInfoCacheTime
}) })

View File

@ -2,16 +2,11 @@ package cache
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/app/pkg/dao"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "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"
"github.com/fthvgb1/wp-go/cache/cachemanager" "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/number"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"time" "time"
) )
@ -25,104 +20,23 @@ func RecentComments(ctx context.Context, n int) (r []models.Comments) {
return return
} }
func PostComments(ctx context.Context, Id uint64) ([]models.PostComments, error) { func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) {
ids, err := cachemanager.Get[[]uint64]("PostCommentsIds", ctx, Id, time.Second) ids, err := cachemanager.Get[[]uint64]("postCommentIds", ctx, Id, time.Second)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return GetCommentDataByIds(ctx, ids) return GetCommentByIds(ctx, ids)
} }
func GetCommentById(ctx context.Context, id uint64) (models.PostComments, error) { func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) {
return cachemanager.Get[models.PostComments]("postCommentData", ctx, id, time.Second) return cachemanager.Get[models.Comments]("commentData", ctx, id, time.Second)
} }
func GetCommentDataByIds(ctx context.Context, ids []uint64) ([]models.PostComments, error) { func GetCommentByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
return cachemanager.GetMultiple[models.PostComments]("postCommentData", ctx, ids, time.Second) return cachemanager.GetMultiple[models.Comments]("commentData", ctx, ids, time.Second)
} }
func NewCommentCache() *cache.MapCache[string, string] { func NewCommentCache() *cache.MapCache[string, string] {
r, _ := cachemanager.GetMapCache[string, string]("NewComment") r, _ := cachemanager.GetMapCache[string, string]("NewComment")
return r 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
}

13
app/pkg/cache/feed.go vendored
View File

@ -106,12 +106,7 @@ func postFeed(c context.Context, id string, _ ...any) (x string, err error) {
if post.Id == 0 || err != nil { if post.Id == 0 || err != nil {
return return
} }
limit := str.ToInteger(wpconfig.GetOption("comments_per_page"), 10) comments, err := PostComments(c, post.Id)
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 { if err != nil {
return return
} }
@ -140,7 +135,7 @@ func postFeed(c context.Context, id string, _ ...any) (x string, err error) {
} }
} }
} else { } else {
rs.Items = slice.Map(comments, func(t models.PostComments) rss2.Item { rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item {
return rss2.Item{ return rss2.Item{
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor), Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId), Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
@ -163,13 +158,13 @@ func commentsFeed(c context.Context, _ ...any) (r []string, err error) {
rs.LastBuildDate = time.Now().Format(timeFormat) rs.LastBuildDate = time.Now().Format(timeFormat)
site := wpconfig.GetOption("siteurl") site := wpconfig.GetOption("siteurl")
rs.AtomLink = fmt.Sprintf("%s/comments/feed", site) rs.AtomLink = fmt.Sprintf("%s/comments/feed", site)
com, err := GetCommentDataByIds(c, slice.Map(commens, func(t models.Comments) uint64 { com, err := GetCommentByIds(c, slice.Map(commens, func(t models.Comments) uint64 {
return t.CommentId return t.CommentId
})) }))
if nil != err { if nil != err {
return []string{}, err return []string{}, err
} }
rs.Items = slice.Map(com, func(t models.PostComments) rss2.Item { rs.Items = slice.Map(com, func(t models.Comments) rss2.Item {
post, _ := GetPostById(c, t.CommentPostId) post, _ := GetPostById(c, t.CommentPostId)
desc := "评论受保护:要查看请输入密码。" desc := "评论受保护:要查看请输入密码。"
content := t.CommentContent content := t.CommentContent

View File

@ -5,11 +5,9 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"github.com/fthvgb1/wp-go/app/pkg/models" "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"
"github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
"time" "time"
) )
@ -83,13 +81,12 @@ func GetCommentByIds(ctx context.Context, ids []uint64, _ ...any) (map[uint64]mo
return m, nil return m, nil
} }
func GetIncreaseComment(ctx context.Context, currentData []uint64, k uint64, _ time.Time, _ ...any) (data []uint64, save bool, refresh bool, err error) { func GetIncreaseComment(ctx context.Context, currentData []uint64, k uint64, t time.Time, _ ...any) (data []uint64, save bool, refresh bool, err error) {
r, err := model.ChunkFind[models.Comments](ctx, 1000, model.Conditions( r, err := model.ChunkFind[models.Comments](ctx, 1000, model.Conditions(
model.Where(model.SqlBuilder{ model.Where(model.SqlBuilder{
{"comment_approved", "1"}, {"comment_approved", "1"},
{"comment_post_ID", "=", number.IntToString(k), "int"}, {"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.Fields("comment_ID"),
model.Order(model.SqlBuilder{ model.Order(model.SqlBuilder{
@ -115,156 +112,3 @@ func GetIncreaseComment(ctx context.Context, currentData []uint64, k uint64, _ t
save = true save = true
return 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
}

View File

@ -3,17 +3,14 @@ package db
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/app/pkg/config" "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/model"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"log" "log"
"runtime"
) )
var safeDb = safety.NewVar[*sqlx.DB](nil) var safeDb = safety.NewVar[*sqlx.DB](nil)
var showQuerySql func() bool
func InitDb() (*safety.Var[*sqlx.DB], error) { func InitDb() (*safety.Var[*sqlx.DB], error) {
c := config.GetConfig() c := config.GetConfig()
@ -39,9 +36,6 @@ func InitDb() (*safety.Var[*sqlx.DB], error) {
if preDb != nil { if preDb != nil {
_ = preDb.Close() _ = preDb.Close()
} }
showQuerySql = reload.FnVal("showQuerySql", false, func() bool {
return config.GetConfig().ShowQuerySql
})
return safeDb, err return safeDb, err
} }
@ -50,20 +44,14 @@ func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery {
nil, nil,
nil)) nil))
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error { model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
if showQuerySql() { if config.GetConfig().ShowQuerySql {
_, f, l, _ := runtime.Caller(5) go log.Println(model.FormatSql(s, args...))
go func() {
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
}()
} }
return query.Selects(ctx, a, s, args...) return query.Selects(ctx, a, s, args...)
}) })
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error { model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
if showQuerySql() { if config.GetConfig().ShowQuerySql {
_, f, l, _ := runtime.Caller(5) go log.Println(model.FormatSql(s, args...))
go func() {
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
}()
} }
return query.Gets(ctx, a, s, args...) return query.Gets(ctx, a, s, args...)
}) })

View File

@ -29,8 +29,3 @@ func (w Comments) PrimaryKey() string {
func (w Comments) Table() string { func (w Comments) Table() string {
return "wp_comments" return "wp_comments"
} }
type PostComments struct {
Comments
Children []uint64
}

View File

@ -1,10 +1,8 @@
package plugins package plugins
import ( import (
"context"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -19,7 +17,6 @@ type CommentHandler struct {
depth int depth int
isTls bool isTls bool
i CommentHtml i CommentHtml
isThreadComments bool
} }
type Comments struct { type Comments struct {
@ -28,8 +25,7 @@ type Comments struct {
} }
type CommentHtml interface { type CommentHtml interface {
FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string FormatLi(c *gin.Context, m models.Comments, depth int, isTls 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 { func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string {
@ -49,10 +45,10 @@ func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, m
isTls: isTls, isTls: isTls,
i: i, i: i,
} }
return h.formatComment(h.comments) return h.formatComment(h.comments, true)
} }
func (d CommentHandler) formatComment(comments []*Comments) (html string) { func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html string) {
s := str.NewBuilder() s := str.NewBuilder()
if d.depth >= d.maxDepth { if d.depth >= d.maxDepth {
comments = d.findComments(comments) comments = d.findComments(comments)
@ -75,11 +71,13 @@ func (d CommentHandler) formatComment(comments []*Comments) (html string) {
parent = "parent" parent = "parent"
fl = true fl = true
} }
s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.maxDepth, 1, d.isTls, d.isThreadComments, eo, parent)) s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.isTls, eo, parent))
if fl { if fl {
d.depth++ d.depth++
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children), `</ol>`) s.WriteString(`<ol class="children">`, d.formatComment(comment.Children, false), `</ol>`)
d.depth-- if isTop {
d.depth = 1
}
} }
s.WriteString("</li><!-- #comment-## -->") s.WriteString("</li><!-- #comment-## -->")
} }
@ -142,24 +140,8 @@ func CommentRender() CommonCommentFormat {
type CommonCommentFormat struct { type CommonCommentFormat struct {
} }
func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { func (c CommonCommentFormat) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
return FormatLi(CommonLi(), m, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent) return FormatLi(CommonLi(), ctx, m, depth, isTls, 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 = ` var li = `
@ -171,11 +153,14 @@ var li = `
src="{{Gravatar}}" src="{{Gravatar}}"
srcset="{{Gravatar}} 2x" srcset="{{Gravatar}} 2x"
class="avatar avatar-56 photo" height="56" width="56" loading="lazy"> class="avatar avatar-56 photo" height="56" width="56" loading="lazy">
<b class="fn">{{CommentAuthor}}</b> <b class="fn">
<a href="{{CommentAuthorUrl}}" rel="external nofollow ugc"
class="url">{{CommentAuthor}}</a>
</b>
<span class="says">说道</span></div><!-- .comment-author --> <span class="says">说道</span></div><!-- .comment-author -->
<div class="comment-metadata"> <div class="comment-metadata">
<a href="/p/{{PostId}}/comment-page-{{page}}#comment-{{CommentId}}"> <a href="/p/{{PostId}}#comment-{{CommentId}}">
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time> <time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
</a></div><!-- .comment-metadata --> </a></div><!-- .comment-metadata -->
@ -185,38 +170,30 @@ var li = `
<p>{{CommentContent}}</p> <p>{{CommentContent}}</p>
</div><!-- .comment-content --> </div><!-- .comment-content -->
{{respond}} <div class="reply">
</article><!-- .comment-body -->
`
var respondTml = `<div class="reply">
<a rel="nofollow" class="comment-reply-link" <a rel="nofollow" class="comment-reply-link"
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}" href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond" data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
data-replyto="回复给{{CommentAuthor}}" data-replyto="回复给{{CommentAuthor}}"
aria-label="回复给{{CommentAuthor}}">回复</a> aria-label="回复给{{CommentAuthor}}">回复</a>
</div>` </div>
</article><!-- .comment-body -->
func FormatLi(li string, comments models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { `
isShow := false
if isThreadComments && currentDepth < maxDepth { func FormatLi(li string, c *gin.Context, comments models.Comments, depth int, isTls bool, eo, parent string) string {
isShow = true
}
for k, v := range map[string]string{ for k, v := range map[string]string{
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10), "{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
"{{Depth}}": strconv.Itoa(currentDepth), "{{Depth}}": strconv.Itoa(depth),
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls), "{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl, "{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
"{{CommentAuthor}}": comments.CommentAuthor, "{{CommentAuthor}}": comments.CommentAuthor,
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10), "{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
"{{page}}": strconv.Itoa(page),
"{{CommentDateGmt}}": comments.CommentDateGmt.String(), "{{CommentDateGmt}}": comments.CommentDateGmt.String(),
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"), "{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
"{{CommentContent}}": comments.CommentContent, "{{CommentContent}}": comments.CommentContent,
"{{eo}}": eo, "{{eo}}": eo,
"{{parent}}": parent, "{{parent}}": parent,
"{{respond}}": respond(comments, isShow),
} { } {
li = strings.Replace(li, k, v, -1) li = strings.Replace(li, k, v, -1)
} }

View File

@ -2,8 +2,6 @@ package plugins
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"regexp" "regexp"
"strings" "strings"
@ -20,13 +18,6 @@ type PageEle struct {
func TwentyFifteenPagination() PageEle { func TwentyFifteenPagination() PageEle {
return twentyFifteen return twentyFifteen
} }
func TwentyFifteenCommentPagination() CommentPageEle {
return twentyFifteenComment
}
type CommentPageEle struct {
PageEle
}
var twentyFifteen = PageEle{ var twentyFifteen = PageEle{
PrevEle: `<a class="prev page-numbers" href="%s">上一页</a>`, PrevEle: `<a class="prev page-numbers" href="%s">上一页</a>`,
@ -37,12 +28,6 @@ var twentyFifteen = PageEle{
CurrentEle: `<span aria-current="page" class="page-numbers current"> CurrentEle: `<span aria-current="page" class="page-numbers current">
<span class="meta-nav screen-reader-text"> </span>%d</span>`, <span class="meta-nav screen-reader-text"> </span>%d</span>`,
} }
var twentyFifteenComment = CommentPageEle{
PageEle{
PrevEle: `<div class="nav-previous"><a href="%s#comments">%s</a></div>`,
NextEle: `<div class="nav-next"><a href="%s#comments">%s</a></div>`,
},
}
func (p PageEle) Current(page, totalPage, totalRow int) string { func (p PageEle) Current(page, totalPage, totalRow int) string {
return fmt.Sprintf(p.CurrentEle, page) return fmt.Sprintf(p.CurrentEle, page)
@ -65,7 +50,6 @@ func (p PageEle) Middle(page int, url string) string {
} }
var reg = regexp.MustCompile(`(/page)/(\d+)`) var reg = regexp.MustCompile(`(/page)/(\d+)`)
var commentReg = regexp.MustCompile(`/comment-page-(\d+)`)
func (p PageEle) Url(path, query string, page int) string { func (p PageEle) Url(path, query string, page int) string {
if !strings.Contains(path, "/page/") { if !strings.Contains(path, "/page/") {
@ -83,32 +67,3 @@ func (p PageEle) Url(path, query string, page int) string {
} }
return str.Join(path, query) 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", "较新评论", "较早评论"))
}

View File

@ -52,30 +52,14 @@
{{ if .showComment}} {{ if .showComment}}
<div id="comments" class="comments-area"> <div id="comments" class="comments-area">
{{ if ne .comments ""}} {{ if gt .post.CommentCount 0}}
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论 </h2> <h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
{{if gt .totalCommentPage 1}}
<nav class="navigation comment-navigation">
<h2 class="screen-reader-text">评论导航</h2>
<div class="nav-links">
{{ .commentPageNav|unescaped}}
</div><!-- .nav-links -->
</nav>
{{end}}
<ol class="comment-list"> <ol class="comment-list">
{{.comments|unescaped}} {{.comments|unescaped}}
</ol> </ol>
{{if gt .totalCommentPage 1}}
<nav class="navigation comment-navigation">
<h2 class="screen-reader-text">评论导航</h2>
<div class="nav-links">
{{ .commentPageNav|unescaped}}
</div><!-- .nav-links -->
</nav>
{{end}}
{{end}} {{end}}
{{if eq .post.CommentStatus "open"}} {{if and (eq .post.CommentStatus "open") (eq ( "thread_comments" |getOption ) "1")}}
<div id="respond" class="comment-respond"> <div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">发表回复 <h3 id="reply-title" class="comment-reply-title">发表回复
<small> <small>

View File

@ -1,7 +1,6 @@
package twentyseventeen package twentyseventeen
import ( import (
"context"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
@ -15,6 +14,7 @@ import (
"github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin"
"strings" "strings"
) )
@ -103,7 +103,7 @@ type comment struct {
plugins.CommonCommentFormat plugins.CommonCommentFormat
} }
func (c comment) FormatLi(_ context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { func (c comment) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
templ := plugins.CommonLi() templ := plugins.CommonLi()
templ = strings.ReplaceAll(templ, `<a rel="nofollow" class="comment-reply-link" templ = strings.ReplaceAll(templ, `<a rel="nofollow" class="comment-reply-link"
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}" href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
@ -114,7 +114,7 @@ func (c comment) FormatLi(_ context.Context, m models.Comments, depth, maxDepth,
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond" data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
data-replyto="回复给{{CommentAuthor}}" data-replyto="回复给{{CommentAuthor}}"
aria-label="回复给{{CommentAuthor}}"><svg class="icon icon-mail-reply" aria-hidden="true" role="img"> <use href="#icon-mail-reply" xlink:href="#icon-mail-reply"></use> </svg>回复</a>`) aria-label="回复给{{CommentAuthor}}"><svg class="icon icon-mail-reply" aria-hidden="true" role="img"> <use href="#icon-mail-reply" xlink:href="#icon-mail-reply"></use> </svg>回复</a>`)
return plugins.FormatLi(templ, m, depth, maxDepth, page, isTls, isThreadComments, eo, parent) return plugins.FormatLi(templ, ctx, m, depth, isTls, eo, parent)
} }
func postThumbnail(h *wp.Handle, posts *models.Posts) { func postThumbnail(h *wp.Handle, posts *models.Posts) {

View File

@ -1,103 +0,0 @@
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(`<ol class="children">`, ss, `</ol>`)
c.depth--
}
s.WriteString("</li><!-- #comment-## -->")
}
html = s.String()
return
}

View File

@ -10,22 +10,15 @@ import (
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/plugins/wpposts" "github.com/fthvgb1/wp-go/app/plugins/wpposts"
"github.com/fthvgb1/wp-go/app/wpconfig" "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" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/plugin/pagination"
"net/http" "net/http"
"time"
) )
type DetailHandle struct { type DetailHandle struct {
*Handle *Handle
CommentRender plugins.CommentHtml CommentRender plugins.CommentHtml
Comments []uint64 Comments []models.Comments
Page int
Limit int
Post models.Posts Post models.Posts
TotalRaw int
} }
func NewDetailHandle(handle *Handle) *DetailHandle { func NewDetailHandle(handle *Handle) *DetailHandle {
@ -87,29 +80,11 @@ func (d *DetailHandle) PasswordProject() {
} }
} }
func (d *DetailHandle) Comment() { func (d *DetailHandle) Comment() {
err := cache.UpdateCommentCache(d.C, time.Second, d.Post.Id) comments, err := cache.PostComments(d.C, d.Post.Id)
d.ginH["totalCommentNum"] = 0 logs.IfError(err, "get d.Post comment", d.Post.Id)
d.ginH["totalCommentPage"] = 1 d.ginH["comments"] = comments
d.ginH["commentPageNav"] = "" d.Comments = comments
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() { func (d *DetailHandle) RenderComment() {
@ -122,19 +97,10 @@ func (d *DetailHandle) RenderComment() {
ableComment = false ableComment = false
} }
d.ginH["showComment"] = ableComment d.ginH["showComment"] = ableComment
d.ginH["comments"] = "" if len(d.Comments) > 0 && ableComment {
if len(d.Comments) < 0 || !ableComment { dep := str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5)
return d.ginH["comments"] = plugins.FormatComments(d.C, d.CommentRender, d.Comments, dep)
} }
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() { func (d *DetailHandle) ContextPost() {

View File

@ -185,9 +185,7 @@ func NewPaginationCache[K comparable, V any](m *cache.MapCache[string, helper.Pa
if fet == nil { if fet == nil {
fet = reload.FnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil) fet = reload.FnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil)
} }
p := cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name) return 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] { 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] {
@ -221,9 +219,9 @@ func SetExpireTime(c cache.SetTime, name string, expireTime time.Duration, expir
c.SetExpiredTime(fn) c.SetExpiredTime(fn)
} }
func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) { func ChangeExpireTime(t time.Duration, name ...string) {
for _, s := range name { for _, s := range name {
reload.ChangeFnVal(s, t, coverConf) reload.ChangeFnVal(s, t)
} }
} }
@ -293,28 +291,3 @@ func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool)
vv, err := getMap[K, V](name) vv, err := getMap[K, V](name)
return vv, err == nil 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
}

View File

@ -72,7 +72,7 @@ func TestSetExpireTime(t *testing.T) {
fmt.Println(c.Get(ctx, "xx")) fmt.Println(c.Get(ctx, "xx"))
time.Sleep(time.Second) time.Sleep(time.Second)
fmt.Println(c.Get(ctx, "xx")) fmt.Println(c.Get(ctx, "xx"))
ChangeExpireTime(3*time.Second, true, "xx") ChangeExpireTime(3*time.Second, "xx")
c.Set(ctx, "xx", "yyy") c.Set(ctx, "xx", "yyy")
time.Sleep(time.Second) time.Sleep(time.Second)
fmt.Println(c.Get(ctx, "xx")) fmt.Println(c.Get(ctx, "xx"))

31
cache/pagination.go vendored
View File

@ -30,14 +30,7 @@ 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) type LocalFn[K comparable, V any] func(ctx context.Context, data []V, k K, page, limit int, a ...any) ([]V, int, error)
func (p *Pagination[K, V]) IsSwitchDB(k K) bool { 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] {
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 { if dbKeyFn == nil {
dbKeyFn = func(k K, a ...any) string { dbKeyFn = func(k K, a ...any) string {
s := str.NewBuilder() s := str.NewBuilder()
@ -73,7 +66,6 @@ func (p *Pagination[K, V]) Pagination(ctx context.Context, timeout time.Duration
data, total, err := p.paginationByLocal(ctx, timeout, k, page, limit, a...) data, total, err := p.paginationByLocal(ctx, timeout, k, page, limit, a...)
if err != nil { if err != nil {
if errors.Is(err, switchDb) { if errors.Is(err, switchDb) {
p.isSwitch.Store(k, true)
err = nil err = nil
return p.paginationByDB(ctx, timeout, k, page, limit, total, a...) return p.paginationByDB(ctx, timeout, k, page, limit, total, a...)
} }
@ -109,15 +101,11 @@ func (p *Pagination[K, V]) paginationByLocal(ctx context.Context, timeout time.D
if err != nil { if err != nil {
return nil, 0, err 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() { if totalRaw >= p.maxNum() {
p.isSwitch.Store(k, true)
return nil, totalRaw, switchDb return nil, totalRaw, switchDb
} }
if len(da) < totalRaw {
totalPage := number.DivideCeil(totalRaw, batchNum) totalPage := number.DivideCeil(totalRaw, batchNum)
for i := 1; i <= totalPage; i++ { for i := 1; i <= totalPage; i++ {
daa, _, err := p.fetchDb(ctx, timeout, k, i, batchNum, totalRaw, a...) daa, _, err := p.fetchDb(ctx, timeout, k, i, batchNum, totalRaw, a...)
@ -129,27 +117,20 @@ func (p *Pagination[K, V]) paginationByLocal(ctx context.Context, timeout time.D
data.Data = da data.Data = da
data.TotalRaw = totalRaw data.TotalRaw = totalRaw
p.Set(ctx, key, data) p.Set(ctx, key, data)
}
return p.localFn(ctx, data.Data, k, page, limit, a...) 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) { 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...)...) key := p.dbKeyFn(k, append([]any{page, limit}, a...)...)
data, ok := p.dbGet(ctx, key) data, ok := p.Get(ctx, key)
if ok { if ok {
return data.Data, data.TotalRaw, nil return data.Data, data.TotalRaw, nil
} }
p.mux.Lock() p.mux.Lock()
defer p.mux.Unlock() defer p.mux.Unlock()
data, ok = p.dbGet(ctx, key) data, ok = p.Get(ctx, key)
if ok { if ok {
return data.Data, data.TotalRaw, nil return data.Data, data.TotalRaw, nil
} }

View File

@ -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, coverConf bool) { func ChangeFnVal[T any](name string, val T) {
v, ok := anyMap.Load(name) v, ok := anyMap.Load(name)
if !ok { if !ok {
return return
@ -313,7 +313,7 @@ func ChangeFnVal[T any](name string, val T, coverConf bool) {
if !ok { if !ok {
return return
} }
if coverConf && !vv.isUseManger.Load() { if !vv.isUseManger.Load() {
vv.isUseManger.Store(true) vv.isUseManger.Store(true)
} }
vv.v.Store(val) vv.v.Store(val)

View File

@ -8,22 +8,22 @@ import (
func TestFlushMapVal(t *testing.T) { func TestFlushMapVal(t *testing.T) {
t.Run("t1", func(t *testing.T) { t.Run("t1", func(t *testing.T) {
c := 0 c := 0
v := GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) (int, bool) { v := GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) int {
c++ c++
return 33, true return 33
}) })
fmt.Println(v) fmt.Println(v)
FlushMapVal("key", 2) FlushMapVal("key", 2)
v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) (int, bool) { v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) int {
fmt.Println("xxxxx") fmt.Println("xxxxx")
return 33, true return 33
}) })
fmt.Println(v) fmt.Println(v)
FlushAnyVal("key") FlushAnyVal("key")
v = GetAnyValMapBy[int, int, struct{}]("key", 2, struct{}{}, func(a struct{}) (int, bool) { v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) int {
fmt.Println("yyyy") fmt.Println("yyyy")
return 33, true return 33
}) })
fmt.Println(v) fmt.Println(v)
}) })

3
cache/vars.go vendored
View File

@ -156,10 +156,7 @@ type VarMemoryCache[T any] struct {
} }
func (c *VarMemoryCache[T]) ClearExpired(ctx context.Context) { func (c *VarMemoryCache[T]) ClearExpired(ctx context.Context) {
_, ok := c.Get(ctx)
if !ok {
c.Flush(ctx) c.Flush(ctx)
}
} }
func NewVarMemoryCache[T any](expireTime func() time.Duration) *VarMemoryCache[T] { func NewVarMemoryCache[T any](expireTime func() time.Duration) *VarMemoryCache[T] {

View File

@ -185,7 +185,7 @@ func DescEahByKey[K constraints.Ordered, V any](m map[K]V, fn func(K, V)) {
orderedEahByKey(m, slice.ASC, fn) orderedEahByKey(m, slice.ASC, fn)
} }
func orderedEahByKey[K constraints.Ordered, V any](m map[K]V, ordered string, fn func(K, V)) { func orderedEahByKey[K constraints.Ordered, V any](m map[K]V, ordered int, fn func(K, V)) {
keys := Keys(m) keys := Keys(m)
slice.Sorts(keys, ordered) slice.Sorts(keys, ordered)
for _, key := range keys { for _, key := range keys {

View File

@ -125,23 +125,12 @@ func Pagination[T any](arr []T, page, pageSize int) []T {
if start > l { if start > l {
start = l start = l
} }
end := start + pageSize end := page * pageSize
if l < end { if l < end {
end = l end = l
} }
return arr[start:end] 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 { func Chunk[T any](arr []T, size int) [][]T {
var r [][]T var r [][]T

View File

@ -6,8 +6,8 @@ import (
) )
const ( const (
ASC = "asc" ASC = iota
DESC = "desc" DESC
) )
type anyArr[T any] struct { type anyArr[T any] struct {
@ -44,7 +44,7 @@ func StableSort[T any](arr []T, fn func(i, j T) bool) {
sort.Stable(slice) sort.Stable(slice)
} }
func Sorts[T constraints.Ordered](a []T, order string) { func Sorts[T constraints.Ordered](a []T, order int) {
slice := anyArr[T]{ slice := anyArr[T]{
data: a, data: a,
fn: func(i, j T) bool { fn: func(i, j T) bool {
@ -56,23 +56,8 @@ func Sorts[T constraints.Ordered](a []T, order string) {
} }
sort.Sort(slice) 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 string, fn func(t T) O) { func SimpleSort[T any, O constraints.Ordered](a []T, order int, fn func(t T) O) {
slice := anyArr[T]{ slice := anyArr[T]{
data: a, data: a,
fn: func(i, j T) bool { fn: func(i, j T) bool {

View File

@ -74,7 +74,7 @@ func TestSort(t *testing.T) {
func TestSorts(t *testing.T) { func TestSorts(t *testing.T) {
type args[T constraints.Ordered] struct { type args[T constraints.Ordered] struct {
a []T a []T
order string order int
} }
type testCase[T constraints.Ordered] struct { type testCase[T constraints.Ordered] struct {
name string name string
@ -169,7 +169,7 @@ func TestSimpleSort(t *testing.T) {
type args[T any, O constraints.Ordered] struct { type args[T any, O constraints.Ordered] struct {
a []T a []T
order string order int
fn func(t T) O fn func(t T) O
} }
type testCase[T any, O constraints.Ordered] struct { type testCase[T any, O constraints.Ordered] struct {