From 83ecc2d214891a071ebe995d91cde99bea445026 Mon Sep 17 00:00:00 2001 From: sunface Date: Fri, 26 Mar 2021 13:47:28 +0800 Subject: [PATCH] update --- server/internal/api/tag.go | 2 +- server/internal/interaction/like.go | 4 +- server/internal/server.go | 3 + server/internal/story/post.go | 9 ++- server/internal/tags/tags.go | 35 --------- server/internal/top/top.go | 108 ++++++++++++++++++++++++++++ server/pkg/models/id_type.go | 5 ++ server/pkg/models/story.go | 7 ++ server/pkg/models/tag.go | 37 ++++++++++ 9 files changed, 171 insertions(+), 39 deletions(-) diff --git a/server/internal/api/tag.go b/server/internal/api/tag.go index 8dccb7b8..997f2d73 100644 --- a/server/internal/api/tag.go +++ b/server/internal/api/tag.go @@ -24,7 +24,7 @@ func GetTag(c *gin.Context) { } func GetTags(c *gin.Context) { - res, err := tags.GetTags() + res, err := models.GetTags() if err != nil { c.JSON(err.Status, common.RespError(err.Message)) return diff --git a/server/internal/interaction/like.go b/server/internal/interaction/like.go index 40ce8af7..90eb8dae 100644 --- a/server/internal/interaction/like.go +++ b/server/internal/interaction/like.go @@ -67,7 +67,9 @@ func Like(storyID string, userId string) *e.Error { tx.Commit() - top.Update(storyID, count) + if models.IsIDStory(storyID) { + top.Update(storyID, count) + } return nil } diff --git a/server/internal/server.go b/server/internal/server.go index 81a93053..d1d3d0a4 100644 --- a/server/internal/server.go +++ b/server/internal/server.go @@ -10,6 +10,7 @@ import ( "github.com/imdotdev/im.dev/server/internal/api" "github.com/imdotdev/im.dev/server/internal/cache" "github.com/imdotdev/im.dev/server/internal/storage" + "github.com/imdotdev/im.dev/server/internal/top" "github.com/imdotdev/im.dev/server/internal/user" "github.com/imdotdev/im.dev/server/pkg/common" "github.com/imdotdev/im.dev/server/pkg/config" @@ -46,6 +47,8 @@ func (s *Server) Start() error { // } go cache.Init() + go top.Init() + go func() { router := gin.New() router.Use(Cors()) diff --git a/server/internal/story/post.go b/server/internal/story/post.go index 2565c04f..e050ebbb 100644 --- a/server/internal/story/post.go +++ b/server/internal/story/post.go @@ -108,6 +108,7 @@ func SubmitStory(c *gin.Context) (map[string]string, *e.Error) { return nil, e.New(http.StatusInternalServerError, e.Internal) } } else { + post.Updated = now // 只有创建者自己才能更新内容 creator, _ := GetPostCreator(post.ID) if creator != post.CreatorID { @@ -115,12 +116,13 @@ func SubmitStory(c *gin.Context) (map[string]string, *e.Error) { } if post.Status == models.StatusDraft { + post.Created = now // 首次发布,需要更新创建时间 _, err = db.Conn.Exec("UPDATE story SET owner=?, slug=?, title=?, md=?, url=?, cover=?, brief=?,created=?,updated=?,status=? WHERE id=?", - post.OwnerID, post.Slug, post.Title, md, post.URL, post.Cover, post.Brief, now, now, models.StatusPublished, post.ID) + post.OwnerID, post.Slug, post.Title, md, post.URL, post.Cover, post.Brief, post.Created, post.Updated, models.StatusPublished, post.ID) } else { _, err = db.Conn.Exec("UPDATE story SET owner=?, slug=?, title=?, md=?, url=?, cover=?, brief=?, updated=? WHERE id=?", - post.OwnerID, post.Slug, post.Title, md, post.URL, post.Cover, post.Brief, now, post.ID) + post.OwnerID, post.Slug, post.Title, md, post.URL, post.Cover, post.Brief, post.Updated, post.ID) } if err != nil { @@ -130,11 +132,14 @@ func SubmitStory(c *gin.Context) (map[string]string, *e.Error) { } //update tags + top.RemoveTagTop(post.ID) err = tags.UpdateTargetTags(user.ID, post.ID, post.Tags, post.Created) if err != nil { logger.Warn("upate tags error", "error", err) return nil, e.New(http.StatusInternalServerError, e.Internal) } + likes := interaction.GetLikes(post.ID) + top.Update(post.ID, likes) return map[string]string{ "username": user.Username, diff --git a/server/internal/tags/tags.go b/server/internal/tags/tags.go index 5e2fdc1b..3a384187 100644 --- a/server/internal/tags/tags.go +++ b/server/internal/tags/tags.go @@ -68,41 +68,6 @@ func SubmitTag(tag *models.Tag) *e.Error { return nil } -func GetTags() (models.Tags, *e.Error) { - tags := make(models.Tags, 0) - - rows, err := db.Conn.Query("SELECT id,creator,title,md,name,icon,cover from tags") - if err != nil { - if err == sql.ErrNoRows { - return tags, nil - } - logger.Warn("get tags error", "error", err) - return tags, e.New(http.StatusInternalServerError, e.Internal) - } - - for rows.Next() { - var rawMd []byte - tag := &models.Tag{} - err := rows.Scan(&tag.ID, &tag.Creator, &tag.Title, &rawMd, &tag.Name, &tag.Icon, &tag.Cover) - if err != nil { - logger.Warn("scan tags error", "error", err) - continue - } - - md, _ := utils.Uncompress(rawMd) - tag.Md = string(md) - - tag.SetCover() - tags = append(tags, tag) - - db.Conn.QueryRow("SELECT count(*) FROM tags_using WHERE tag_id=? and target_type != ?", tag.ID, models.IDTypeUser).Scan(&tag.Posts) - } - - sort.Sort(tags) - - return tags, nil -} - func GetTagsByIDs(ids []string) ([]*models.Tag, *e.Error) { tags := make([]*models.Tag, 0, len(ids)) for _, id := range ids { diff --git a/server/internal/top/top.go b/server/internal/top/top.go index 5eae4fa5..bd205ca8 100644 --- a/server/internal/top/top.go +++ b/server/internal/top/top.go @@ -3,6 +3,8 @@ package top import ( "context" "fmt" + "strconv" + "strings" "time" "github.com/go-redis/redis/v8" @@ -94,6 +96,50 @@ func Update(storyID string, count int) { } } +func RemoveGlobalTop(storyID string) { + hots := make([]string, 0) + + hots = append(hots, GlobalPrefix+TopYear) + hots = append(hots, GlobalPrefix+TopMonth) + hots = append(hots, GlobalPrefix+TopWeek) + hots = append(hots, GlobalPrefix+TopRecent) + + ctx := context.Background() + for _, hot := range hots { + err := db.Redis.ZRem(ctx, hot, storyID).Err() + if err != nil { + logger.Warn("update hot error", "error", err, "key", hot, "storyID", storyID) + continue + } + } +} + +func RemoveTagTop(storyID string) { + ts, _, err := models.GetTargetTags(storyID) + if err != nil { + logger.Warn("get tags error", "error", err) + return + } + + hots := make([]string, 0) + + for _, tag := range ts { + hots = append(hots, fmt.Sprintf(TagFormat, tag, TopYear)) + hots = append(hots, fmt.Sprintf(TagFormat, tag, TopMonth)) + hots = append(hots, fmt.Sprintf(TagFormat, tag, TopWeek)) + hots = append(hots, fmt.Sprintf(TagFormat, tag, TopRecent)) + } + + ctx := context.Background() + for _, hot := range hots { + err = db.Redis.ZRem(ctx, hot, storyID).Err() + if err != nil { + logger.Warn("update hot error", "error", err, "key", hot, "storyID", storyID) + continue + } + } +} + func GetTopList(key string, start, end int64) []string { ids := make([]string, 0) ctx := context.Background() @@ -109,3 +155,65 @@ func GetTopList(key string, start, end int64) []string { return ids } + +func Init() { + // 检查是否凌晨 + for { + now := time.Now() + if now.Local().Hour() == 0 { + break + } + + time.Sleep(1 * time.Hour) + } + + // 从凌晨开始,每隔24小时检查一次 + for { + + ctx := context.Background() + keys := []string{GlobalPrefix + TopYear, GlobalPrefix + TopMonth, GlobalPrefix + TopWeek, GlobalPrefix + TopRecent} + tags, _ := models.GetTags() + for _, tag := range tags { + keys = append(keys, fmt.Sprintf(TagFormat, tag, TopYear)) + keys = append(keys, fmt.Sprintf(TagFormat, tag, TopMonth)) + keys = append(keys, fmt.Sprintf(TagFormat, tag, TopWeek)) + keys = append(keys, fmt.Sprintf(TagFormat, tag, TopRecent)) + } + + now := time.Now() + for _, k := range keys { + var maxDuration float64 + if strings.HasSuffix(k, TopYear) { + maxDuration = 365 * 24 + } + if strings.HasSuffix(k, TopMonth) { + maxDuration = 30 * 24 + } + if strings.HasSuffix(k, TopWeek) { + maxDuration = 7 * 24 + } + if strings.HasSuffix(k, TopRecent) { + maxDuration = 2 * 24 + } + + iter := db.Redis.ZScan(ctx, k, 0, "", 0).Iterator() + for iter.Next(ctx) { + id := iter.Val() + _, err := strconv.Atoi(id) + if err == nil { + //若是数字,则是score,跳过 + continue + } + + created := models.GetStoryCreated(id) + if now.Sub(created).Hours() > maxDuration { + err = db.Redis.ZRem(ctx, k, id).Err() + if err != nil { + logger.Warn("delete top error", "error", err, "key", k, "storyID", id) + } + } + } + } + time.Sleep(24 * time.Hour) + } +} diff --git a/server/pkg/models/id_type.go b/server/pkg/models/id_type.go index 7883caaa..04570250 100644 --- a/server/pkg/models/id_type.go +++ b/server/pkg/models/id_type.go @@ -48,6 +48,11 @@ func GetIdTypeTable(id string) string { } } +func IsIDStory(id string) bool { + tp := GetIDType(id) + return tp == IDTypePost || tp == IDTypeSeries || tp == IDTypeBook +} + func IdExist(id string) bool { if id == "" { return false diff --git a/server/pkg/models/story.go b/server/pkg/models/story.go index a6420a24..6dac495d 100644 --- a/server/pkg/models/story.go +++ b/server/pkg/models/story.go @@ -90,3 +90,10 @@ func IsStoryCreator(userID string, storyID string) bool { return false } + +func GetStoryCreated(storyID string) time.Time { + var t time.Time + db.Conn.QueryRow("SELECT created FROM story WHERE id=?", storyID).Scan(&t) + + return t +} diff --git a/server/pkg/models/tag.go b/server/pkg/models/tag.go index 905272f8..23a289d6 100644 --- a/server/pkg/models/tag.go +++ b/server/pkg/models/tag.go @@ -3,9 +3,11 @@ package models import ( "database/sql" "net/http" + "sort" "github.com/imdotdev/im.dev/server/pkg/db" "github.com/imdotdev/im.dev/server/pkg/e" + "github.com/imdotdev/im.dev/server/pkg/utils" ) type Tag struct { @@ -73,3 +75,38 @@ func GetSimpleTag(id string, name string) (*Tag, *e.Error) { return tag, nil } + +func GetTags() (Tags, *e.Error) { + tags := make(Tags, 0) + + rows, err := db.Conn.Query("SELECT id,creator,title,md,name,icon,cover from tags") + if err != nil { + if err == sql.ErrNoRows { + return tags, nil + } + logger.Warn("get tags error", "error", err) + return tags, e.New(http.StatusInternalServerError, e.Internal) + } + + for rows.Next() { + var rawMd []byte + tag := &Tag{} + err := rows.Scan(&tag.ID, &tag.Creator, &tag.Title, &rawMd, &tag.Name, &tag.Icon, &tag.Cover) + if err != nil { + logger.Warn("scan tags error", "error", err) + continue + } + + md, _ := utils.Uncompress(rawMd) + tag.Md = string(md) + + tag.SetCover() + tags = append(tags, tag) + + db.Conn.QueryRow("SELECT count(*) FROM tags_using WHERE tag_id=? and target_type != ?", tag.ID, IDTypeUser).Scan(&tag.Posts) + } + + sort.Sort(tags) + + return tags, nil +}