package redisdao import ( "context" "fmt" "time" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/global" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/redismodel" "github.com/redis/go-redis/v9" ) // notificationUnreadTTL 未读数 key 的过期时间,避免长期驻留与 MySQL 弱一致漂移。 const notificationUnreadTTL = 7 * 24 * time.Hour // notificationUnreadIncrScript 首次自增时设置过期时间,保证 INCR 与 EXPIRE 原子执行。 var notificationUnreadIncrScript = redis.NewScript(` local v = redis.call('INCR', KEYS[1]) if v == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end return v `) // notificationUnreadDecrScript 未读数 -1,保证结果不低于 0 var notificationUnreadDecrScript = redis.NewScript(` local v = redis.call('GET', KEYS[1]) if v and tonumber(v) > 0 then return redis.call('DECRBY', KEYS[1], 1) end return 0 `) // NotificationUnreadIncr 用户未读消息数 +1,返回最新值 func NotificationUnreadIncr(ctx context.Context, userID int64) (int64, error) { ctx, span := global.Tracer.Start(ctx, "redisdao.NotificationUnreadIncr") defer span.End() key := fmt.Sprintf(redismodel.KeyNotificationUnread, userID) val, err := notificationUnreadIncrScript.Run(ctx, global.RedisEngine, []string{key}, int(notificationUnreadTTL.Seconds())).Int64() if err != nil { span.RecordError(err) return 0, err } return val, nil } // NotificationUnreadDecr 用户未读消息数 -1,返回扣减后的值(不低于 0) func NotificationUnreadDecr(ctx context.Context, userID int64) (int64, error) { ctx, span := global.Tracer.Start(ctx, "redisdao.NotificationUnreadDecr") defer span.End() key := fmt.Sprintf(redismodel.KeyNotificationUnread, userID) val, err := notificationUnreadDecrScript.Run(ctx, global.RedisEngine, []string{key}).Int64() if err != nil { span.RecordError(err) return 0, err } return val, nil } // NotificationUnreadGet 获取用户未读消息数,不存在时返回 0 func NotificationUnreadGet(ctx context.Context, userID int64) (int64, error) { ctx, span := global.Tracer.Start(ctx, "redisdao.NotificationUnreadGet") defer span.End() key := fmt.Sprintf(redismodel.KeyNotificationUnread, userID) v, err := global.RedisEngine.Get(ctx, key).Int64() if err == redis.Nil { return 0, nil } if err != nil { span.RecordError(err) } return v, err } // NotificationUnreadSet 回填未读数缓存(DB 对账回源后使用),避免 key 过期导致后续扣减被吞。 func NotificationUnreadSet(ctx context.Context, userID int64, cnt int64) error { ctx, span := global.Tracer.Start(ctx, "redisdao.NotificationUnreadSet") defer span.End() key := fmt.Sprintf(redismodel.KeyNotificationUnread, userID) if err := global.RedisEngine.Set(ctx, key, cnt, notificationUnreadTTL).Err(); err != nil { span.RecordError(err) return err } return nil }