You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
5.9 KiB

4 years ago
package top
import (
"context"
"fmt"
4 years ago
"strconv"
"strings"
4 years ago
"time"
"github.com/go-redis/redis/v8"
"github.com/imdotdev/im.dev/server/pkg/db"
"github.com/imdotdev/im.dev/server/pkg/log"
"github.com/imdotdev/im.dev/server/pkg/models"
)
var logger = log.RootLogger.New("logger", "hot")
type HotData struct {
Key string
Data *redis.Z
}
const GlobalPrefix = "im.dev-global-"
const TagFormat = "im.dev-tag-%s-%s"
const (
TopRecent = models.FilterRecent
TopWeek = models.FilterWeek
TopMonth = models.FilterMonth
TopYear = models.FilterYear
)
// 更新文章的HOT列表
// 时间维度yestoday, week, month, year,infinity
// 范围维度: 全局、Tag
func Update(storyID string, count int) {
var created time.Time
err := db.Conn.QueryRow("SELECT created FROM story WHERE id=?", storyID).Scan(&created)
if err != nil {
logger.Warn("select story created error", "error", err)
return
}
tillNow := time.Now().Sub(created).Hours()
hots := make([]*HotData, 0)
if tillNow >= 365*24 {
return
}
ts, _, err := models.GetTargetTags(storyID)
if err != nil {
logger.Warn("get tags error", "error", err)
return
}
if tillNow < 365*24 {
hots = append(hots, &HotData{GlobalPrefix + TopYear, &redis.Z{float64(count), storyID}})
for _, tag := range ts {
hots = append(hots, &HotData{fmt.Sprintf(TagFormat, tag, TopYear), &redis.Z{float64(count), storyID}})
}
}
if tillNow < 30*24 {
hots = append(hots, &HotData{GlobalPrefix + TopMonth, &redis.Z{float64(count), storyID}})
for _, tag := range ts {
hots = append(hots, &HotData{fmt.Sprintf(TagFormat, tag, TopMonth), &redis.Z{float64(count), storyID}})
}
}
if tillNow < 7*24 {
hots = append(hots, &HotData{GlobalPrefix + TopWeek, &redis.Z{float64(count), storyID}})
for _, tag := range ts {
hots = append(hots, &HotData{fmt.Sprintf(TagFormat, tag, TopWeek), &redis.Z{float64(count), storyID}})
}
}
if tillNow < 2*24 {
hots = append(hots, &HotData{GlobalPrefix + TopRecent, &redis.Z{float64(count), storyID}})
for _, tag := range ts {
hots = append(hots, &HotData{fmt.Sprintf(TagFormat, tag, TopRecent), &redis.Z{float64(count), storyID}})
}
}
ctx := context.Background()
for _, hot := range hots {
4 years ago
if count > 1 {
4 years ago
err = db.Redis.ZAdd(ctx, hot.Key, hot.Data).Err()
} else {
err = db.Redis.ZRem(ctx, hot.Key, hot.Data.Member).Err()
}
if err != nil {
logger.Warn("update hot error", "error", err, "key", hot.Key, "score", hot.Data.Score)
continue
}
}
}
4 years ago
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
}
}
}
4 years ago
func GetTopList(key string, start, end int64) []string {
ids := make([]string, 0)
ctx := context.Background()
keys, err := db.Redis.ZRevRange(ctx, key, start, end-1).Result()
if err != nil {
logger.Warn("scan top list error", "error", err, "key", key)
return ids
}
for _, key := range keys {
ids = append(ids, key)
}
return ids
}
4 years ago
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)
}
}
4 years ago
func RemoveTag(tagID string) error {
hots := make([]string, 0)
hots = append(hots, fmt.Sprintf(TagFormat, tagID, TopYear))
hots = append(hots, fmt.Sprintf(TagFormat, tagID, TopMonth))
hots = append(hots, fmt.Sprintf(TagFormat, tagID, TopWeek))
hots = append(hots, fmt.Sprintf(TagFormat, tagID, TopRecent))
ctx := context.Background()
for _, hot := range hots {
err := db.Redis.Del(ctx, hot).Err()
if err != nil {
logger.Warn("delete top error", "error", err, "key", hot, "tag_id", tagID)
return err
}
}
return nil
}