Compare commits
2 Commits
f257a966e6
...
088fc306de
Author | SHA1 | Date | |
---|---|---|---|
088fc306de | |||
57d345e445 |
@ -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
|
||||
}
|
||||
|
38
app/pkg/cache/cache.go
vendored
38
app/pkg/cache/cache.go
vendored
@ -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
|
||||
})
|
||||
|
100
app/pkg/cache/comments.go
vendored
100
app/pkg/cache/comments.go
vendored
@ -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
|
||||
}
|
||||
|
13
app/pkg/cache/feed.go
vendored
13
app/pkg/cache/feed.go
vendored
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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...)
|
||||
})
|
||||
|
@ -29,3 +29,8 @@ func (w Comments) PrimaryKey() string {
|
||||
func (w Comments) Table() string {
|
||||
return "wp_comments"
|
||||
}
|
||||
|
||||
type PostComments struct {
|
||||
Comments
|
||||
Children []uint64
|
||||
}
|
||||
|
@ -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(`<ol class="children">`, d.formatComment(comment.Children, false), `</ol>`)
|
||||
if isTop {
|
||||
d.depth = 1
|
||||
}
|
||||
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children), `</ol>`)
|
||||
d.depth--
|
||||
}
|
||||
s.WriteString("</li><!-- #comment-## -->")
|
||||
}
|
||||
@ -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">
|
||||
<b class="fn">
|
||||
<a href="{{CommentAuthorUrl}}" rel="external nofollow ugc"
|
||||
class="url">{{CommentAuthor}}</a>
|
||||
</b>
|
||||
<b class="fn">{{CommentAuthor}}</b>
|
||||
<span class="says">说道:</span></div><!-- .comment-author -->
|
||||
|
||||
<div class="comment-metadata">
|
||||
<a href="/p/{{PostId}}#comment-{{CommentId}}">
|
||||
<a href="/p/{{PostId}}/comment-page-{{page}}#comment-{{CommentId}}">
|
||||
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
|
||||
</a></div><!-- .comment-metadata -->
|
||||
|
||||
@ -170,30 +185,38 @@ var li = `
|
||||
<p>{{CommentContent}}</p>
|
||||
</div><!-- .comment-content -->
|
||||
|
||||
<div class="reply">
|
||||
{{respond}}
|
||||
</article><!-- .comment-body -->
|
||||
|
||||
`
|
||||
|
||||
var respondTml = `<div class="reply">
|
||||
<a rel="nofollow" class="comment-reply-link"
|
||||
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
||||
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
||||
data-replyto="回复给{{CommentAuthor}}"
|
||||
aria-label="回复给{{CommentAuthor}}">回复</a>
|
||||
</div>
|
||||
</article><!-- .comment-body -->
|
||||
</div>`
|
||||
|
||||
`
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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: `<a class="prev page-numbers" href="%s">上一页</a>`,
|
||||
@ -28,6 +37,12 @@ var twentyFifteen = PageEle{
|
||||
CurrentEle: `<span aria-current="page" class="page-numbers current">
|
||||
<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 {
|
||||
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", "较新评论", "较早评论"))
|
||||
}
|
||||
|
@ -52,14 +52,30 @@
|
||||
|
||||
{{ if .showComment}}
|
||||
<div id="comments" class="comments-area">
|
||||
{{ if gt .post.CommentCount 0}}
|
||||
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
|
||||
<ol class="comment-list">
|
||||
{{.comments|unescaped}}
|
||||
</ol>
|
||||
{{ if ne .comments ""}}
|
||||
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论 </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">
|
||||
{{.comments|unescaped}}
|
||||
</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}}
|
||||
{{if and (eq .post.CommentStatus "open") (eq ( "thread_comments" |getOption ) "1")}}
|
||||
{{if eq .post.CommentStatus "open"}}
|
||||
<div id="respond" class="comment-respond">
|
||||
<h3 id="reply-title" class="comment-reply-title">发表回复
|
||||
<small>
|
||||
|
@ -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, `<a rel="nofollow" class="comment-reply-link"
|
||||
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
||||
@ -114,7 +114,7 @@ func (c comment) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls
|
||||
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
||||
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>`)
|
||||
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) {
|
||||
|
103
app/theme/wp/comments.go
Normal file
103
app/theme/wp/comments.go
Normal file
@ -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(`<ol class="children">`, ss, `</ol>`)
|
||||
c.depth--
|
||||
}
|
||||
s.WriteString("</li><!-- #comment-## -->")
|
||||
}
|
||||
|
||||
html = s.String()
|
||||
return
|
||||
}
|
@ -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() {
|
||||
|
33
cache/cachemanager/manger.go
vendored
33
cache/cachemanager/manger.go
vendored
@ -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
|
||||
}
|
||||
|
2
cache/cachemanager/manger_test.go
vendored
2
cache/cachemanager/manger_test.go
vendored
@ -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"))
|
||||
|
49
cache/pagination.go
vendored
49
cache/pagination.go
vendored
@ -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
|
||||
}
|
||||
|
4
cache/reload/reload.go
vendored
4
cache/reload/reload.go
vendored
@ -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)
|
||||
|
12
cache/reload/reload_test.go
vendored
12
cache/reload/reload_test.go
vendored
@ -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)
|
||||
})
|
||||
|
5
cache/vars.go
vendored
5
cache/vars.go
vendored
@ -156,7 +156,10 @@ type VarMemoryCache[T any] struct {
|
||||
}
|
||||
|
||||
func (c *VarMemoryCache[T]) ClearExpired(ctx context.Context) {
|
||||
c.Flush(ctx)
|
||||
_, ok := c.Get(ctx)
|
||||
if !ok {
|
||||
c.Flush(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func NewVarMemoryCache[T any](expireTime func() time.Duration) *VarMemoryCache[T] {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user