评论feed 及缓存优化
This commit is contained in:
parent
111f83ec88
commit
3dff692c89
64
actions/common/comments.go
Normal file
64
actions/common/comments.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github/fthvgb1/wp-go/helper"
|
||||||
|
"github/fthvgb1/wp-go/logs"
|
||||||
|
"github/fthvgb1/wp-go/models"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RecentComments(ctx context.Context, n int) (r []models.WpComments) {
|
||||||
|
r, err := recentCommentsCaches.GetCache(ctx, time.Second)
|
||||||
|
if len(r) > n {
|
||||||
|
r = r[0:n]
|
||||||
|
}
|
||||||
|
logs.ErrPrintln(err, "get recent comment")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func recentComments(...any) (r []models.WpComments, err error) {
|
||||||
|
return models.Find[models.WpComments](models.SqlBuilder{
|
||||||
|
{"comment_approved", "1"},
|
||||||
|
{"post_status", "publish"},
|
||||||
|
}, "comment_ID,comment_author,comment_post_ID,post_title", "", models.SqlBuilder{{"comment_date_gmt", "desc"}}, models.SqlBuilder{
|
||||||
|
{"a", "left join", "wp_posts b", "a.comment_post_ID=b.ID"},
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostComments(ctx context.Context, Id uint64) ([]models.WpComments, error) {
|
||||||
|
return postCommentCaches.GetCache(ctx, Id, time.Second, Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postComments(args ...any) ([]models.WpComments, error) {
|
||||||
|
postId := args[0].(uint64)
|
||||||
|
return models.Find[models.WpComments](models.SqlBuilder{
|
||||||
|
{"comment_approved", "1"},
|
||||||
|
{"comment_post_ID", "=", strconv.FormatUint(postId, 10), "int"},
|
||||||
|
}, "*", "", models.SqlBuilder{
|
||||||
|
{"comment_date_gmt", "asc"},
|
||||||
|
{"comment_ID", "asc"},
|
||||||
|
}, nil, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommentById(ctx context.Context, id uint64) (models.WpComments, error) {
|
||||||
|
return commentsCache.GetCache(ctx, id, time.Second, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommentByIds(ctx context.Context, ids []uint64) ([]models.WpComments, error) {
|
||||||
|
return commentsCache.GetCacheBatch(ctx, ids, time.Second, ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCommentByIds(args ...any) (map[uint64]models.WpComments, error) {
|
||||||
|
ids := args[0].([]uint64)
|
||||||
|
m := make(map[uint64]models.WpComments)
|
||||||
|
r, err := models.Find[models.WpComments](models.SqlBuilder{
|
||||||
|
{"comment_ID", "in", ""}, {"comment_approved", "1"},
|
||||||
|
}, "*", "", nil, nil, 0, helper.SliceMap(ids, helper.ToAny[uint64]))
|
||||||
|
if err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
return helper.SliceToMap(r, func(t models.WpComments) uint64 {
|
||||||
|
return t.CommentId
|
||||||
|
}), err
|
||||||
|
}
|
|
@ -2,14 +2,11 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github/fthvgb1/wp-go/cache"
|
"github/fthvgb1/wp-go/cache"
|
||||||
"github/fthvgb1/wp-go/helper"
|
|
||||||
"github/fthvgb1/wp-go/logs"
|
"github/fthvgb1/wp-go/logs"
|
||||||
"github/fthvgb1/wp-go/models"
|
"github/fthvgb1/wp-go/models"
|
||||||
"github/fthvgb1/wp-go/vars"
|
"github/fthvgb1/wp-go/vars"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -27,6 +24,7 @@ var searchPostIdsCache *cache.MapCache[string, PostIds]
|
||||||
var maxPostIdCache *cache.SliceCache[uint64]
|
var maxPostIdCache *cache.SliceCache[uint64]
|
||||||
var TotalRaw int
|
var TotalRaw int
|
||||||
var usersCache *cache.MapCache[uint64, models.WpUsers]
|
var usersCache *cache.MapCache[uint64, models.WpUsers]
|
||||||
|
var commentsCache *cache.MapCache[uint64, models.WpComments]
|
||||||
|
|
||||||
func InitActionsCommonCache() {
|
func InitActionsCommonCache() {
|
||||||
archivesCaches = &Arch{
|
archivesCaches = &Arch{
|
||||||
|
@ -43,7 +41,6 @@ func InitActionsCommonCache() {
|
||||||
postContextCache = cache.NewMapCacheByFn[uint64, PostContext](getPostContext, vars.Conf.ContextPostCacheTime)
|
postContextCache = cache.NewMapCacheByFn[uint64, PostContext](getPostContext, vars.Conf.ContextPostCacheTime)
|
||||||
|
|
||||||
postsCache = cache.NewMapCacheByBatchFn[uint64, models.WpPosts](getPostsByIds, vars.Conf.PostDataCacheTime)
|
postsCache = cache.NewMapCacheByBatchFn[uint64, models.WpPosts](getPostsByIds, vars.Conf.PostDataCacheTime)
|
||||||
postsCache.SetCacheFunc(getPostById)
|
|
||||||
|
|
||||||
categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime)
|
categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime)
|
||||||
|
|
||||||
|
@ -56,7 +53,8 @@ func InitActionsCommonCache() {
|
||||||
maxPostIdCache = cache.NewSliceCache[uint64](getMaxPostId, vars.Conf.MaxPostIdCacheTime)
|
maxPostIdCache = cache.NewSliceCache[uint64](getMaxPostId, vars.Conf.MaxPostIdCacheTime)
|
||||||
|
|
||||||
usersCache = cache.NewMapCacheByBatchFn[uint64, models.WpUsers](getUsers, vars.Conf.UserInfoCacheTime)
|
usersCache = cache.NewMapCacheByBatchFn[uint64, models.WpUsers](getUsers, vars.Conf.UserInfoCacheTime)
|
||||||
usersCache.SetCacheFunc(getUser)
|
|
||||||
|
commentsCache = cache.NewMapCacheByBatchFn[uint64, models.WpComments](getCommentByIds, time.Hour)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClearCache() {
|
func ClearCache() {
|
||||||
|
@ -69,40 +67,6 @@ func ClearCache() {
|
||||||
usersCache.ClearExpired()
|
usersCache.ClearExpired()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.WpPosts, total int, err error) {
|
|
||||||
res, err := monthPostsCache.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if order == "desc" {
|
|
||||||
res = helper.SliceReverse(res)
|
|
||||||
}
|
|
||||||
total = len(res)
|
|
||||||
rr := helper.SlicePagination(res, page, limit)
|
|
||||||
r, err = GetPostsByIds(ctx, rr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func monthPost(args ...any) (r []uint64, err error) {
|
|
||||||
year, month := args[0].(string), args[1].(string)
|
|
||||||
where := models.SqlBuilder{
|
|
||||||
{"post_type", "in", ""},
|
|
||||||
{"post_status", "in", ""},
|
|
||||||
{"year(post_date)", year},
|
|
||||||
{"month(post_date)", month},
|
|
||||||
}
|
|
||||||
postType := []any{"post"}
|
|
||||||
status := []any{"publish"}
|
|
||||||
ids, err := models.Find[models.WpPosts](where, "ID", "", models.SqlBuilder{{"Id", "asc"}}, nil, 0, postType, status)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, post := range ids {
|
|
||||||
r = append(r, post.Id)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type PostIds struct {
|
type PostIds struct {
|
||||||
Ids []uint64
|
Ids []uint64
|
||||||
Length int
|
Length int
|
||||||
|
@ -137,79 +101,6 @@ type PostContext struct {
|
||||||
next models.WpPosts
|
next models.WpPosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostComments(ctx context.Context, Id uint64) ([]models.WpComments, error) {
|
|
||||||
return postCommentCaches.GetCache(ctx, Id, time.Second, Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func postComments(args ...any) ([]models.WpComments, error) {
|
|
||||||
postId := args[0].(uint64)
|
|
||||||
return models.Find[models.WpComments](models.SqlBuilder{
|
|
||||||
{"comment_approved", "1"},
|
|
||||||
{"comment_post_ID", "=", strconv.FormatUint(postId, 10), "int"},
|
|
||||||
}, "*", "", models.SqlBuilder{
|
|
||||||
{"comment_date_gmt", "asc"},
|
|
||||||
{"comment_ID", "asc"},
|
|
||||||
}, nil, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RecentComments(ctx context.Context, n int) (r []models.WpComments) {
|
|
||||||
r, err := recentCommentsCaches.GetCache(ctx, time.Second)
|
|
||||||
if len(r) > n {
|
|
||||||
r = r[0:n]
|
|
||||||
}
|
|
||||||
logs.ErrPrintln(err, "get recent comment")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func recentComments(...any) (r []models.WpComments, err error) {
|
|
||||||
return models.Find[models.WpComments](models.SqlBuilder{
|
|
||||||
{"comment_approved", "1"},
|
|
||||||
{"post_status", "publish"},
|
|
||||||
}, "comment_ID,comment_author,comment_post_ID,post_title", "", models.SqlBuilder{{"comment_date_gmt", "desc"}}, models.SqlBuilder{
|
|
||||||
{"a", "left join", "wp_posts b", "a.comment_post_ID=b.ID"},
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.WpPosts, err error) {
|
|
||||||
postCtx, err := postContextCache.GetCache(ctx, id, time.Second, date)
|
|
||||||
if err != nil {
|
|
||||||
return models.WpPosts{}, models.WpPosts{}, err
|
|
||||||
}
|
|
||||||
prev = postCtx.prev
|
|
||||||
next = postCtx.next
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPostContext(arg ...any) (r PostContext, err error) {
|
|
||||||
t := arg[0].(time.Time)
|
|
||||||
next, err := models.FirstOne[models.WpPosts](models.SqlBuilder{
|
|
||||||
{"post_date", ">", t.Format("2006-01-02 15:04:05")},
|
|
||||||
{"post_status", "in", ""},
|
|
||||||
{"post_type", "post"},
|
|
||||||
}, "ID,post_title,post_password", nil, []any{"publish"})
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
prev, err := models.FirstOne[models.WpPosts](models.SqlBuilder{
|
|
||||||
{"post_date", "<", t.Format("2006-01-02 15:04:05")},
|
|
||||||
{"post_status", "in", ""},
|
|
||||||
{"post_type", "post"},
|
|
||||||
}, "ID,post_title", models.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = PostContext{
|
|
||||||
prev: prev,
|
|
||||||
next: next,
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func archives() ([]models.PostArchive, error) {
|
func archives() ([]models.PostArchive, error) {
|
||||||
return models.Find[models.PostArchive](models.SqlBuilder{
|
return models.Find[models.PostArchive](models.SqlBuilder{
|
||||||
{"post_type", "post"}, {"post_status", "publish"},
|
{"post_type", "post"}, {"post_status", "publish"},
|
||||||
|
@ -247,26 +138,6 @@ func categories(...any) (terms []models.WpTermsMy, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecentPosts(ctx context.Context, n int) (r []models.WpPosts) {
|
|
||||||
r, err := recentPostsCaches.GetCache(ctx, time.Second)
|
|
||||||
if n < len(r) {
|
|
||||||
r = r[:n]
|
|
||||||
}
|
|
||||||
logs.ErrPrintln(err, "get recent post")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func recentPosts(...any) (r []models.WpPosts, err error) {
|
|
||||||
r, err = models.Find[models.WpPosts](models.SqlBuilder{{
|
|
||||||
"post_type", "post",
|
|
||||||
}, {"post_status", "publish"}}, "ID,post_title,post_password", "", models.SqlBuilder{{"post_date", "desc"}}, nil, 10)
|
|
||||||
for i, post := range r {
|
|
||||||
if post.PostPassword != "" {
|
|
||||||
PasswordProjectTitle(&r[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func PasswordProjectTitle(post *models.WpPosts) {
|
func PasswordProjectTitle(post *models.WpPosts) {
|
||||||
if post.PostPassword != "" {
|
if post.PostPassword != "" {
|
||||||
post.PostTitle = fmt.Sprintf("密码保护:%s", post.PostTitle)
|
post.PostTitle = fmt.Sprintf("密码保护:%s", post.PostTitle)
|
||||||
|
|
|
@ -2,16 +2,17 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github/fthvgb1/wp-go/helper"
|
"github/fthvgb1/wp-go/helper"
|
||||||
|
"github/fthvgb1/wp-go/logs"
|
||||||
"github/fthvgb1/wp-go/models"
|
"github/fthvgb1/wp-go/models"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostAndCache(ctx context.Context, id uint64) (models.WpPosts, error) {
|
func GetPostAndCache(ctx context.Context, id uint64) (models.WpPosts, error) {
|
||||||
|
|
||||||
return postsCache.GetCache(ctx, id, time.Second, id)
|
return postsCache.GetCache(ctx, id, time.Second, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,16 +34,6 @@ func SearchPost(ctx context.Context, key string, args ...any) (r []models.WpPost
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPostById(id ...any) (post models.WpPosts, err error) {
|
|
||||||
Id := id[0].(uint64)
|
|
||||||
posts, err := getPostsByIds([]uint64{Id})
|
|
||||||
if err != nil {
|
|
||||||
return models.WpPosts{}, err
|
|
||||||
}
|
|
||||||
post = posts[Id]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPostsByIds(ids ...any) (m map[uint64]models.WpPosts, err error) {
|
func getPostsByIds(ids ...any) (m map[uint64]models.WpPosts, err error) {
|
||||||
m = make(map[uint64]models.WpPosts)
|
m = make(map[uint64]models.WpPosts)
|
||||||
id := ids[0].([]uint64)
|
id := ids[0].([]uint64)
|
||||||
|
@ -134,3 +125,98 @@ func GetMaxPostId(ctx *gin.Context) (uint64, error) {
|
||||||
Id, err := maxPostIdCache.GetCache(ctx, time.Second)
|
Id, err := maxPostIdCache.GetCache(ctx, time.Second)
|
||||||
return Id[0], err
|
return Id[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RecentPosts(ctx context.Context, n int) (r []models.WpPosts) {
|
||||||
|
r, err := recentPostsCaches.GetCache(ctx, time.Second)
|
||||||
|
if n < len(r) {
|
||||||
|
r = r[:n]
|
||||||
|
}
|
||||||
|
logs.ErrPrintln(err, "get recent post")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func recentPosts(...any) (r []models.WpPosts, err error) {
|
||||||
|
r, err = models.Find[models.WpPosts](models.SqlBuilder{{
|
||||||
|
"post_type", "post",
|
||||||
|
}, {"post_status", "publish"}}, "ID,post_title,post_password", "", models.SqlBuilder{{"post_date", "desc"}}, nil, 10)
|
||||||
|
for i, post := range r {
|
||||||
|
if post.PostPassword != "" {
|
||||||
|
PasswordProjectTitle(&r[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.WpPosts, err error) {
|
||||||
|
postCtx, err := postContextCache.GetCache(ctx, id, time.Second, date)
|
||||||
|
if err != nil {
|
||||||
|
return models.WpPosts{}, models.WpPosts{}, err
|
||||||
|
}
|
||||||
|
prev = postCtx.prev
|
||||||
|
next = postCtx.next
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPostContext(arg ...any) (r PostContext, err error) {
|
||||||
|
t := arg[0].(time.Time)
|
||||||
|
next, err := models.FirstOne[models.WpPosts](models.SqlBuilder{
|
||||||
|
{"post_date", ">", t.Format("2006-01-02 15:04:05")},
|
||||||
|
{"post_status", "in", ""},
|
||||||
|
{"post_type", "post"},
|
||||||
|
}, "ID,post_title,post_password", nil, []any{"publish"})
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prev, err := models.FirstOne[models.WpPosts](models.SqlBuilder{
|
||||||
|
{"post_date", "<", t.Format("2006-01-02 15:04:05")},
|
||||||
|
{"post_status", "in", ""},
|
||||||
|
{"post_type", "post"},
|
||||||
|
}, "ID,post_title", models.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r = PostContext{
|
||||||
|
prev: prev,
|
||||||
|
next: next,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.WpPosts, total int, err error) {
|
||||||
|
res, err := monthPostsCache.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if order == "desc" {
|
||||||
|
res = helper.SliceReverse(res)
|
||||||
|
}
|
||||||
|
total = len(res)
|
||||||
|
rr := helper.SlicePagination(res, page, limit)
|
||||||
|
r, err = GetPostsByIds(ctx, rr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func monthPost(args ...any) (r []uint64, err error) {
|
||||||
|
year, month := args[0].(string), args[1].(string)
|
||||||
|
where := models.SqlBuilder{
|
||||||
|
{"post_type", "in", ""},
|
||||||
|
{"post_status", "in", ""},
|
||||||
|
{"year(post_date)", year},
|
||||||
|
{"month(post_date)", month},
|
||||||
|
}
|
||||||
|
postType := []any{"post"}
|
||||||
|
status := []any{"publish"}
|
||||||
|
ids, err := models.Find[models.WpPosts](where, "ID", "", models.SqlBuilder{{"Id", "asc"}}, nil, 0, postType, status)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, post := range ids {
|
||||||
|
r = append(r, post.Id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -16,11 +16,6 @@ func getUsers(...any) (m map[uint64]models.WpUsers, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUser(a ...any) (r models.WpUsers, err error) {
|
|
||||||
id := a[0].(uint64)
|
|
||||||
return models.FindOneById[models.WpUsers](id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUser(ctx *gin.Context, uid uint64) models.WpUsers {
|
func GetUser(ctx *gin.Context, uid uint64) models.WpUsers {
|
||||||
r, err := usersCache.GetCache(ctx, uid, time.Second, uid)
|
r, err := usersCache.GetCache(ctx, uid, time.Second, uid)
|
||||||
logs.ErrPrintln(err, "get user", uid)
|
logs.ErrPrintln(err, "get user", uid)
|
||||||
|
|
|
@ -16,10 +16,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var feedCache = cache.NewSliceCache[string](feed, time.Hour)
|
var feedCache = cache.NewSliceCache(feed, time.Hour)
|
||||||
var postFeedCache = cache.NewMapCacheByFn[string, string](postFeed, time.Hour)
|
var postFeedCache = cache.NewMapCacheByFn[string, string](postFeed, time.Hour)
|
||||||
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
|
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||||
var templateRss rss2.Rss2
|
var templateRss rss2.Rss2
|
||||||
|
var commentsFeedCache = cache.NewSliceCache(commentsFeed, time.Hour)
|
||||||
|
|
||||||
func InitFeed() {
|
func InitFeed() {
|
||||||
templateRss = rss2.Rss2{
|
templateRss = rss2.Rss2{
|
||||||
|
@ -85,7 +86,7 @@ func feed(arg ...any) (xml []string, err error) {
|
||||||
if t.PostPassword != "" {
|
if t.PostPassword != "" {
|
||||||
common.PasswdProjectContent(&t)
|
common.PasswdProjectContent(&t)
|
||||||
} else {
|
} else {
|
||||||
desc = plugins.DigestRaw(t.PostContent, 55, t.Id)
|
desc = plugins.DigestRaw(t.PostContent, 55, fmt.Sprintf("/p/%d", t.Id))
|
||||||
}
|
}
|
||||||
l := ""
|
l := ""
|
||||||
if t.CommentStatus == "open" && t.CommentCount > 0 {
|
if t.CommentStatus == "open" && t.CommentCount > 0 {
|
||||||
|
@ -201,3 +202,56 @@ func postFeed(arg ...any) (x string, err error) {
|
||||||
x = rs.GetXML()
|
x = rs.GetXML()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CommentsFeed(c *gin.Context) {
|
||||||
|
if !isCacheExpired(c, commentsFeedCache.SetTime()) {
|
||||||
|
c.Status(http.StatusNotModified)
|
||||||
|
} else {
|
||||||
|
r, err := commentsFeedCache.GetCache(c, time.Second, c)
|
||||||
|
if err != nil {
|
||||||
|
c.Status(http.StatusInternalServerError)
|
||||||
|
c.Abort()
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setFeed(r[0], c, commentsFeedCache.SetTime())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func commentsFeed(args ...any) (r []string, err error) {
|
||||||
|
c := args[0].(*gin.Context)
|
||||||
|
commens := common.RecentComments(c, 10)
|
||||||
|
rs := templateRss
|
||||||
|
rs.LastBuildDate = time.Now().Format(time.RFC1123Z)
|
||||||
|
rs.AtomLink = fmt.Sprintf("%s/comments/feed", models.Options["siteurl"])
|
||||||
|
com, err := common.GetCommentByIds(c, helper.SliceMap(commens, func(t models.WpComments) uint64 {
|
||||||
|
return t.CommentId
|
||||||
|
}))
|
||||||
|
if nil != err {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
rs.Items = helper.SliceMap(com, func(t models.WpComments) rss2.Item {
|
||||||
|
post, _ := common.GetPostAndCache(c, t.CommentPostId)
|
||||||
|
common.PasswordProjectTitle(&post)
|
||||||
|
desc := "评论受保护:要查看请输入密码。"
|
||||||
|
content := t.CommentContent
|
||||||
|
if post.PostPassword != "" {
|
||||||
|
common.PasswdProjectContent(&post)
|
||||||
|
content = post.PostContent
|
||||||
|
} else {
|
||||||
|
desc = plugins.DigestRaw(t.CommentContent, 55, fmt.Sprintf("%s/p/%d#comment-%d", models.Options["siteurl"], post.Id, t.CommentId))
|
||||||
|
content = t.CommentContent
|
||||||
|
}
|
||||||
|
return rss2.Item{
|
||||||
|
Title: fmt.Sprintf("《%s》的评论", post.PostTitle),
|
||||||
|
Link: fmt.Sprintf("%s/p/%d#comment-%d", models.Options["siteurl"], post.Id, t.CommentId),
|
||||||
|
Creator: t.CommentAuthor,
|
||||||
|
Description: desc,
|
||||||
|
PubDate: t.CommentDateGmt.Format(time.RFC1123Z),
|
||||||
|
Guid: fmt.Sprintf("%s#commment-%d", post.Guid, t.CommentId),
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
r = []string{rs.GetXML()}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
57
cache/map.go
vendored
57
cache/map.go
vendored
|
@ -9,11 +9,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type MapCache[K comparable, V any] struct {
|
type MapCache[K comparable, V any] struct {
|
||||||
data map[K]mapCacheStruct[V]
|
data map[K]mapCacheStruct[V]
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
setCacheFunc func(...any) (V, error)
|
cacheFunc func(...any) (V, error)
|
||||||
setBatchCacheFn func(...any) (map[K]V, error)
|
batchCacheFn func(...any) (map[K]V, error)
|
||||||
expireTime time.Duration
|
expireTime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMapCache[K comparable, V any](expireTime time.Duration) *MapCache[K, V] {
|
func NewMapCache[K comparable, V any](expireTime time.Duration) *MapCache[K, V] {
|
||||||
|
@ -27,7 +27,7 @@ type mapCacheStruct[T any] struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
|
func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
|
||||||
m.setCacheFunc = fn
|
m.cacheFunc = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) GetSetTime(k K) (t time.Time) {
|
func (m *MapCache[K, V]) GetSetTime(k K) (t time.Time) {
|
||||||
|
@ -39,24 +39,41 @@ func (m *MapCache[K, V]) GetSetTime(k K) (t time.Time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) SetCacheBatchFunc(fn func(...any) (map[K]V, error)) {
|
func (m *MapCache[K, V]) SetCacheBatchFunc(fn func(...any) (map[K]V, error)) {
|
||||||
m.setBatchCacheFn = fn
|
m.batchCacheFn = fn
|
||||||
|
if m.cacheFunc == nil {
|
||||||
|
m.setCacheFn(fn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMapCacheByFn[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
|
func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
|
||||||
|
m.cacheFunc = func(a ...any) (V, error) {
|
||||||
|
id := a[0].(K)
|
||||||
|
r, err := fn([]K{id})
|
||||||
|
if err != nil {
|
||||||
|
var rr V
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
return r[id], err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMapCacheByFn[K comparable, V any](fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
|
||||||
return &MapCache[K, V]{
|
return &MapCache[K, V]{
|
||||||
mutex: &sync.Mutex{},
|
mutex: &sync.Mutex{},
|
||||||
setCacheFunc: fun,
|
cacheFunc: fn,
|
||||||
expireTime: expireTime,
|
expireTime: expireTime,
|
||||||
data: make(map[K]mapCacheStruct[V]),
|
data: make(map[K]mapCacheStruct[V]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func NewMapCacheByBatchFn[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
|
func NewMapCacheByBatchFn[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
|
||||||
return &MapCache[K, V]{
|
r := &MapCache[K, V]{
|
||||||
mutex: &sync.Mutex{},
|
mutex: &sync.Mutex{},
|
||||||
setBatchCacheFn: fn,
|
batchCacheFn: fn,
|
||||||
expireTime: expireTime,
|
expireTime: expireTime,
|
||||||
data: make(map[K]mapCacheStruct[V]),
|
data: make(map[K]mapCacheStruct[V]),
|
||||||
}
|
}
|
||||||
|
r.setCacheFn(fn)
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) Flush() {
|
func (m *MapCache[K, V]) Flush() {
|
||||||
|
@ -79,7 +96,7 @@ func (m *MapCache[K, V]) SetByBatchFn(params ...any) error {
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
r, err := m.setBatchCacheFn(params...)
|
r, err := m.batchCacheFn(params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -122,7 +139,7 @@ func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duratio
|
||||||
if data.incr > t {
|
if data.incr > t {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r, er := m.setCacheFunc(params...)
|
r, er := m.cacheFunc(params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = er
|
err = er
|
||||||
return
|
return
|
||||||
|
@ -185,7 +202,7 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.
|
||||||
if tt > t {
|
if tt > t {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r, er := m.setBatchCacheFn(params...)
|
r, er := m.batchCacheFn(params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = er
|
err = er
|
||||||
return
|
return
|
||||||
|
|
|
@ -230,3 +230,12 @@ func SliceSelfReverse[T any](arr []T) []T {
|
||||||
}
|
}
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SliceToMap[K comparable, V any](arr []V, fn func(V) K) map[K]V {
|
||||||
|
m := make(map[K]V)
|
||||||
|
for _, v := range arr {
|
||||||
|
k := fn(v)
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
|
@ -505,3 +505,37 @@ func TestSliceSelfReverse(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSliceToMap(t *testing.T) {
|
||||||
|
type ss struct {
|
||||||
|
id int
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
arr []ss
|
||||||
|
fn func(ss) int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want map[int]ss
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: []ss{{1, "k1"}, {2, "v2"}},
|
||||||
|
fn: func(s ss) int {
|
||||||
|
return s.id
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: map[int]ss{1: {1, "k1"}, 2: {2, "v2"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceToMap(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceToMap() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ func digestRaw(arg ...any) (string, error) {
|
||||||
} else if limit == 0 {
|
} else if limit == 0 {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return DigestRaw(str, limit, id), nil
|
return DigestRaw(str, limit, fmt.Sprintf("/p/%d", id)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DigestCache(ctx *gin.Context, id uint64, str string) string {
|
func DigestCache(ctx *gin.Context, id uint64, str string) string {
|
||||||
|
@ -43,7 +43,7 @@ func DigestCache(ctx *gin.Context, id uint64, str string) string {
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
func DigestRaw(str string, limit int, id uint64) string {
|
func DigestRaw(str string, limit int, u string) string {
|
||||||
if r := more.FindString(str); r != "" {
|
if r := more.FindString(str); r != "" {
|
||||||
m := strings.Split(str, r)
|
m := strings.Split(str, r)
|
||||||
str = m[0]
|
str = m[0]
|
||||||
|
@ -98,11 +98,11 @@ func DigestRaw(str string, limit int, id uint64) string {
|
||||||
|
|
||||||
content = string(ru[:i])
|
content = string(ru[:i])
|
||||||
closeTag := helper.CloseHtmlTag(content)
|
closeTag := helper.CloseHtmlTag(content)
|
||||||
tmp := `%s......%s<p class="read-more"><a href="/p/%d">继续阅读</a></p>`
|
tmp := `%s......%s<p class="read-more"><a href="%s">继续阅读</a></p>`
|
||||||
if strings.Contains(closeTag, "pre") || strings.Contains(closeTag, "code") {
|
if strings.Contains(closeTag, "pre") || strings.Contains(closeTag, "code") {
|
||||||
tmp = `%s%s......<p class="read-more"><a href="/p/%d">继续阅读</a></p>`
|
tmp = `%s%s......<p class="read-more"><a href="%s">继续阅读</a></p>`
|
||||||
}
|
}
|
||||||
content = fmt.Sprintf(tmp, content, closeTag, id)
|
content = fmt.Sprintf(tmp, content, closeTag, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
|
@ -57,6 +57,7 @@ func SetupRouter() *gin.Engine {
|
||||||
r.GET("/p/:id", actions.Detail)
|
r.GET("/p/:id", actions.Detail)
|
||||||
r.GET("/p/:id/feed", actions.PostFeed)
|
r.GET("/p/:id/feed", actions.PostFeed)
|
||||||
r.GET("/feed", actions.Feed)
|
r.GET("/feed", actions.Feed)
|
||||||
|
r.GET("/comments/feed", actions.CommentsFeed)
|
||||||
if helper.IsContainInArr(gin.Mode(), []string{gin.DebugMode, gin.TestMode}) {
|
if helper.IsContainInArr(gin.Mode(), []string{gin.DebugMode, gin.TestMode}) {
|
||||||
pprof.Register(r, "dev/pprof")
|
pprof.Register(r, "dev/pprof")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user