2026-03-09 21:28:58 +08:00
|
|
|
|
package cache
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 缓存键前缀常量
|
|
|
|
|
|
const (
|
|
|
|
|
|
// 帖子相关
|
|
|
|
|
|
PrefixPostList = "posts:list"
|
|
|
|
|
|
PrefixPost = "posts:detail"
|
2026-03-24 05:18:30 +08:00
|
|
|
|
// HotRankZSet 热门榜(有序集合,member=post_id,score=归一化热度,仅存 TopN)
|
|
|
|
|
|
HotRankZSet = "posts:hot:zset"
|
|
|
|
|
|
// HotRankTopIDs 热门榜有序 post_id 列表(JSON []string,供分层缓存本地层命中)
|
|
|
|
|
|
HotRankTopIDs = "posts:hot:top_ids"
|
2026-03-09 21:28:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 会话相关
|
|
|
|
|
|
PrefixConversationList = "conversations:list"
|
|
|
|
|
|
PrefixConversationDetail = "conversations:detail"
|
|
|
|
|
|
|
|
|
|
|
|
// 群组相关
|
|
|
|
|
|
PrefixGroupMembers = "groups:members"
|
|
|
|
|
|
PrefixGroupInfo = "groups:info"
|
|
|
|
|
|
|
|
|
|
|
|
// 未读数相关
|
|
|
|
|
|
PrefixUnreadSystem = "unread:system"
|
|
|
|
|
|
PrefixUnreadConversation = "unread:conversation"
|
|
|
|
|
|
PrefixUnreadDetail = "unread:detail"
|
2026-05-12 18:26:31 +08:00
|
|
|
|
|
2026-05-10 13:36:58 +08:00
|
|
|
|
// 已读位置相关(OpenIM 风格:缓存 hasReadSeq,用于 O(1) 未读数计算)
|
|
|
|
|
|
PrefixUserReadSeq = "user_read_seq"
|
|
|
|
|
|
|
2026-03-09 21:28:58 +08:00
|
|
|
|
// 用户相关
|
|
|
|
|
|
PrefixUserInfo = "users:info"
|
|
|
|
|
|
PrefixUserMe = "users:me"
|
2026-03-12 08:38:14 +08:00
|
|
|
|
|
2026-03-25 01:03:34 +08:00
|
|
|
|
// 频道相关(全量列表一条缓存,供 /channels、帖子 channel 填充共用)
|
|
|
|
|
|
PrefixChannelsAllList = "channels:all_list"
|
|
|
|
|
|
|
2026-03-12 08:38:14 +08:00
|
|
|
|
// 消息缓存相关
|
2026-05-04 13:07:03 +08:00
|
|
|
|
keyPrefixMsgHash = "msg_hash" // 消息详情 Hash
|
|
|
|
|
|
keyPrefixMsgIndex = "msg_index" // 消息索引 Sorted Set
|
|
|
|
|
|
keyPrefixMsgCount = "msg_count" // 消息计数
|
|
|
|
|
|
keyPrefixMsgSeq = "msg_seq" // Seq 计数器
|
|
|
|
|
|
keyPrefixMsgPage = "msg_page" // 分页缓存
|
|
|
|
|
|
keyPrefixMsgIdempotent = "msg_idem" // 消息幂等键
|
2026-05-17 23:38:04 +08:00
|
|
|
|
keyPrefixSeqBuffer = "seq_buf" // Seq 预分配缓冲区 Hash
|
2026-03-09 21:28:58 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// PostListKey 生成帖子列表缓存键
|
2026-03-24 05:18:30 +08:00
|
|
|
|
// postType: 帖子类型 (hot, follow, latest)
|
2026-03-09 21:28:58 +08:00
|
|
|
|
// page: 页码
|
|
|
|
|
|
// pageSize: 每页数量
|
|
|
|
|
|
// userID: 用户维度(仅在个性化列表如 follow 场景使用)
|
2026-03-24 05:18:30 +08:00
|
|
|
|
// HotRankZSetKey 热门榜 Redis ZSET 逻辑键(仅存 TopN,会经 normalizeKey 加前缀)
|
|
|
|
|
|
func HotRankZSetKey() string {
|
|
|
|
|
|
return HotRankZSet
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HotRankTopIDsKey 热门榜 Top 顺序 ID 列表键(会经 normalizeKey 加前缀)
|
|
|
|
|
|
func HotRankTopIDsKey() string {
|
|
|
|
|
|
return HotRankTopIDs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 11:39:41 +08:00
|
|
|
|
func HotRankZSetChannelKey(channelID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:ch:%s", HotRankZSet, channelID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func HotRankTopIDsChannelKey(channelID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:ch:%s", HotRankTopIDs, channelID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:28:58 +08:00
|
|
|
|
func PostListKey(postType string, userID string, page, pageSize int) string {
|
|
|
|
|
|
if userID == "" {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%d:%d", PrefixPostList, postType, page, pageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%s:%d:%d", PrefixPostList, postType, userID, page, pageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PostDetailKey 生成帖子详情缓存键
|
|
|
|
|
|
func PostDetailKey(postID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", PrefixPost, postID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConversationListKey 生成会话列表缓存键
|
|
|
|
|
|
func ConversationListKey(userID string, page, pageSize int) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%d:%d", PrefixConversationList, userID, page, pageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// GroupMembersKey 生成群组成员缓存键
|
|
|
|
|
|
func GroupMembersKey(groupID string, page, pageSize int) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:page:%d:size:%d", PrefixGroupMembers, groupID, page, pageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// UnreadSystemKey 生成系统消息未读数缓存键
|
|
|
|
|
|
func UnreadSystemKey(userID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", PrefixUnreadSystem, userID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UnreadConversationKey 生成会话未读总数缓存键
|
|
|
|
|
|
func UnreadConversationKey(userID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", PrefixUnreadConversation, userID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UnreadDetailKey 生成单个会话未读数缓存键
|
|
|
|
|
|
func UnreadDetailKey(userID, conversationID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%s", PrefixUnreadDetail, userID, conversationID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-25 01:03:34 +08:00
|
|
|
|
// ChannelsAllListKey 频道全量列表缓存键(含未启用,供 MapByIDs 解析历史帖子 channel)
|
|
|
|
|
|
func ChannelsAllListKey() string {
|
|
|
|
|
|
return PrefixChannelsAllList
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateChannelList 失效频道列表缓存(管理端增删改频道后调用)
|
|
|
|
|
|
func InvalidateChannelList(c Cache) {
|
|
|
|
|
|
c.Delete(ChannelsAllListKey())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:28:58 +08:00
|
|
|
|
// InvalidatePostList 失效帖子列表缓存
|
|
|
|
|
|
func InvalidatePostList(cache Cache) {
|
|
|
|
|
|
cache.DeleteByPrefix(PrefixPostList)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidatePostDetail 失效帖子详情缓存
|
|
|
|
|
|
func InvalidatePostDetail(cache Cache, postID string) {
|
|
|
|
|
|
cache.Delete(PostDetailKey(postID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateConversationList 失效会话列表缓存
|
|
|
|
|
|
func InvalidateConversationList(cache Cache, userID string) {
|
|
|
|
|
|
cache.DeleteByPrefix(PrefixConversationList + ":" + userID + ":")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateGroupMembers 失效群组成员缓存
|
|
|
|
|
|
func InvalidateGroupMembers(cache Cache, groupID string) {
|
|
|
|
|
|
cache.DeleteByPrefix(PrefixGroupMembers + ":" + groupID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateUnreadSystem 失效系统消息未读数缓存
|
|
|
|
|
|
func InvalidateUnreadSystem(cache Cache, userID string) {
|
|
|
|
|
|
cache.Delete(UnreadSystemKey(userID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateUnreadConversation 失效会话未读数缓存
|
|
|
|
|
|
func InvalidateUnreadConversation(cache Cache, userID string) {
|
|
|
|
|
|
cache.Delete(UnreadConversationKey(userID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateUnreadDetail 失效单个会话未读数缓存
|
|
|
|
|
|
func InvalidateUnreadDetail(cache Cache, userID, conversationID string) {
|
|
|
|
|
|
cache.Delete(UnreadDetailKey(userID, conversationID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 08:38:14 +08:00
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
// 消息缓存 Key 生成函数
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
// MessageHashKey 消息详情 Hash key
|
|
|
|
|
|
func MessageHashKey(convID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", keyPrefixMsgHash, convID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageIndexKey 消息索引 Sorted Set key
|
|
|
|
|
|
func MessageIndexKey(convID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", keyPrefixMsgIndex, convID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageCountKey 消息计数 key
|
|
|
|
|
|
func MessageCountKey(convID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", keyPrefixMsgCount, convID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageSeqKey Seq 计数器 key
|
|
|
|
|
|
func MessageSeqKey(convID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", keyPrefixMsgSeq, convID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessagePageKey 分页缓存 key
|
|
|
|
|
|
func MessagePageKey(convID string, page, pageSize int) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%d:%d", keyPrefixMsgPage, convID, page, pageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InvalidateMessagePages 失效会话消息分页缓存
|
|
|
|
|
|
func InvalidateMessagePages(cache Cache, conversationID string) {
|
|
|
|
|
|
cache.DeleteByPrefix(fmt.Sprintf("%s:%s:", keyPrefixMsgPage, conversationID))
|
|
|
|
|
|
}
|
2026-05-04 13:07:03 +08:00
|
|
|
|
|
|
|
|
|
|
// MessageIdempotentKey 消息幂等键 (clientMsgID -> server MsgID)
|
|
|
|
|
|
func MessageIdempotentKey(senderID, clientMsgID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%s", keyPrefixMsgIdempotent, senderID, clientMsgID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-17 23:38:04 +08:00
|
|
|
|
// SeqBufferKey seq 预分配缓冲区 Hash key
|
|
|
|
|
|
func SeqBufferKey(convID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s", keyPrefixSeqBuffer, convID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 13:36:58 +08:00
|
|
|
|
|
|
|
|
|
|
// UserReadSeqKey 用户已读位置缓存键(OpenIM 风格 SEQ_USER_READ:{convID}:{userID})
|
|
|
|
|
|
func UserReadSeqKey(convID, userID string) string {
|
|
|
|
|
|
return fmt.Sprintf("%s:%s:%s", PrefixUserReadSeq, convID, userID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|