diff --git a/actions/common/common.go b/actions/common/common.go index ce51051..e0eb0c2 100644 --- a/actions/common/common.go +++ b/actions/common/common.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "github/fthvgb1/wp-go/cache" + "github/fthvgb1/wp-go/helper" "github/fthvgb1/wp-go/logs" "github/fthvgb1/wp-go/models" "github/fthvgb1/wp-go/vars" @@ -20,6 +21,7 @@ var recentPostsCaches *cache.SliceCache[models.WpPosts] var recentCommentsCaches *cache.SliceCache[models.WpComments] var postCommentCaches *cache.MapCache[uint64, []models.WpComments] var postsCache *cache.MapCache[uint64, models.WpPosts] +var monthPostsCache *cache.MapCache[string, []uint64] func InitActionsCommonCache() { archivesCaches = &Arch{ @@ -27,9 +29,12 @@ func InitActionsCommonCache() { setCacheFunc: archives, } + monthPostsCache = cache.NewMapCache[string, []uint64](monthPost, time.Hour) + postContextCache = cache.NewMapCache[uint64, PostContext](getPostContext, vars.Conf.ContextPostCacheTime) postsCache = cache.NewMapBatchCache[uint64, models.WpPosts](getPosts, time.Hour) + postsCache.SetCacheFunc(getPost) categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime) @@ -39,6 +44,45 @@ func InitActionsCommonCache() { postCommentCaches = cache.NewMapCache[uint64, []models.WpComments](postComments, time.Minute*5) } +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 = GetPosts(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 { + Ids []uint64 + Length int +} + type Arch struct { data []models.PostArchive mutex *sync.Mutex diff --git a/actions/common/posts.go b/actions/common/posts.go index 04bf3f7..670315c 100644 --- a/actions/common/posts.go +++ b/actions/common/posts.go @@ -9,15 +9,19 @@ import ( "time" ) -func GetPostAndCache(ctx context.Context, id uint64, ids ...uint64) (models.WpPosts, error) { +func GetPostAndCache(ctx context.Context, id uint64) (models.WpPosts, error) { - return postsCache.GetCacheBatch(ctx, id, time.Second, ids) + return postsCache.GetCache(ctx, id, time.Second, id) } func GetPost(id uint64) models.WpPosts { return postsCache.Get(id) } +func GetPosts(ctx context.Context, ids []uint64) ([]models.WpPosts, error) { + return postsCache.GetCacheBatch(ctx, ids, time.Second, ids) +} + func SetPostCache(ids []models.WpPosts) error { var arg []uint64 for _, posts := range ids { @@ -26,6 +30,16 @@ func SetPostCache(ids []models.WpPosts) error { return postsCache.SetByBatchFn(arg) } +func getPost(id ...any) (post models.WpPosts, err error) { + Id := id[0].(uint64) + posts, err := getPosts([]uint64{Id}) + if err != nil { + return models.WpPosts{}, err + } + post = posts[Id] + return +} + func getPosts(ids ...any) (m map[uint64]models.WpPosts, err error) { m = make(map[uint64]models.WpPosts) id := ids[0].([]uint64) diff --git a/actions/detail.go b/actions/detail.go index fd9666a..513430f 100644 --- a/actions/detail.go +++ b/actions/detail.go @@ -56,7 +56,7 @@ func Detail(c *gin.Context) { } } ID := uint64(Id) - post, err := common.GetPostAndCache(c, ID, ID) + post, err := common.GetPostAndCache(c, ID) if post.Id == 0 || err != nil { return } diff --git a/actions/index.go b/actions/index.go index a20944b..c89f23b 100644 --- a/actions/index.go +++ b/actions/index.go @@ -168,12 +168,25 @@ func Index(c *gin.Context) { "title": h.getTitle(), "recentComments": recentComments, } - postIds, totalRaw, err := models.SimplePagination[models.WpPosts](h.where, "ID", "", h.page, h.pageSize, h.orderBy, h.join, h.postType, h.status) + var postIds []models.WpPosts + var totalRaw int + var err error + if c.Param("month") != "" { + postIds, totalRaw, err = common.GetMonthPostIds(c, c.Param("year"), c.Param("month"), h.page, h.pageSize, h.order) + if err != nil { + return + } + } else { + postIds, totalRaw, err = models.SimplePagination[models.WpPosts](h.where, "ID", "", h.page, h.pageSize, h.orderBy, h.join, h.postType, h.status) + } + defer func() { - c.HTML(http.StatusOK, "posts/index.gohtml", ginH) + stat := http.StatusOK if err != nil { c.Error(err) + stat = http.StatusInternalServerError } + c.HTML(stat, "posts/index.gohtml", ginH) }() if err != nil { return @@ -181,10 +194,7 @@ func Index(c *gin.Context) { if len(postIds) < 1 && h.category != "" { h.titleL = "未找到页面" } - err = common.SetPostCache(postIds) - if err != nil { - return - } + pw := h.session.Get("post_password") plug := plugins.NewPostPlugin(c, h.scene) for i, v := range postIds { @@ -203,6 +213,9 @@ func Index(c *gin.Context) { } } q := c.Request.URL.Query().Encode() + if q != "" { + q = fmt.Sprintf("?%s", q) + } ginH["posts"] = postIds ginH["totalPage"] = h.getTotalPage(totalRaw) ginH["pagination"] = pagination(h.page, h.totalPage, h.paginationStep, c.Request.URL.Path, q) diff --git a/cache/map.go b/cache/map.go index cd08979..b7fe63e 100644 --- a/cache/map.go +++ b/cache/map.go @@ -22,6 +22,14 @@ type mapCacheStruct[T any] struct { data T } +func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) { + m.setCacheFunc = fn +} + +func (m *MapCache[K, V]) SetCacheBatchFunc(fn func(...any) (map[K]V, error)) { + m.setBatchCacheFn = fn +} + func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] { return &MapCache[K, V]{ mutex: &sync.Mutex{}, @@ -133,20 +141,35 @@ func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duratio return data.data, err } -func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { - data, ok := m.data[key] - if !ok { - data = mapCacheStruct[V]{} +func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) { + var needFlush []K + var res []V + t := 0 + for _, k := range key { + d, ok := m.data[k] + if !ok { + needFlush = append(needFlush, k) + continue + } + expired := time.Duration(d.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix()) + if expired { + needFlush = append(needFlush, k) + } + t = t + d.incr } var err error - expired := time.Duration(data.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix()) //todo 这里应该判断下取出的值是否为零值,不过怎么操作呢? - if !ok || (ok && m.expireTime >= 0 && expired) { - t := data.incr + if len(needFlush) > 0 { call := func() { m.mutex.Lock() defer m.mutex.Unlock() - if data.incr > t { + tt := 0 + for _, dd := range needFlush { + if ddd, ok := m.data[dd]; ok { + tt = tt + ddd.incr + } + } + if tt > t { return } r, er := m.setBatchCacheFn(params...) @@ -157,7 +180,6 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Du for k, v := range r { m.set(k, v) } - data.data = m.data[key].data } if timeout > 0 { ctx, cancel := context.WithTimeout(c, timeout) @@ -175,7 +197,10 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Du } else { call() } - } - return data.data, err + for _, k := range key { + d := m.data[k] + res = append(res, d.data) + } + return res, err } diff --git a/helper/func.go b/helper/func.go index 3ec8a6e..99d0d9d 100644 --- a/helper/func.go +++ b/helper/func.go @@ -34,6 +34,9 @@ func StructColumn[T any, M any](arr []M, field string) (r []T) { } func RangeSlice[T ~int | ~uint | ~int64 | ~int8 | ~int16 | ~int32 | ~uint64](start, end, step T) []T { + if step == 0 { + panic("step can't be 0") + } l := int((end-start+1)/step + 1) if l < 0 { l = 0 - l @@ -218,3 +221,12 @@ func SliceReverse[T any](arr []T) []T { } return r } + +func SliceSelfReverse[T any](arr []T) []T { + l := len(arr) + half := l / 2 + for i := 0; i < half; i++ { + arr[i], arr[l-i-1] = arr[l-i-1], arr[i] + } + return arr +} diff --git a/helper/func_test.go b/helper/func_test.go index 1071998..063ca15 100644 --- a/helper/func_test.go +++ b/helper/func_test.go @@ -473,3 +473,35 @@ func TestToInterface(t *testing.T) { }) } } + +func TestSliceSelfReverse(t *testing.T) { + type args struct { + arr []int + } + tests := []struct { + name string + args args + want []int + }{ + { + name: "t1", + args: args{ + arr: RangeSlice(1, 10, 1), + }, + want: RangeSlice(10, 1, -1), + }, { + name: "t2", + args: args{ + arr: RangeSlice(1, 9, 1), + }, + want: RangeSlice(9, 1, -1), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SliceSelfReverse(tt.args.arr); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SliceSelfReverse() = %v, want %v", got, tt.want) + } + }) + } +}