Files
backend/internal/cache/keys.go

212 lines
6.7 KiB
Go
Raw Normal View History

package cache
import (
"fmt"
)
// 缓存键前缀常量
const (
// 帖子相关
PrefixPostList = "posts:list"
PrefixPost = "posts:detail"
// HotRankZSet 热门榜有序集合member=post_idscore=归一化热度,仅存 TopN
HotRankZSet = "posts:hot:zset"
// HotRankTopIDs 热门榜有序 post_id 列表JSON []string供分层缓存本地层命中
HotRankTopIDs = "posts:hot:top_ids"
// 会话相关
PrefixConversationList = "conversations:list"
PrefixConversationDetail = "conversations:detail"
// 群组相关
PrefixGroupMembers = "groups:members"
PrefixGroupInfo = "groups:info"
// 未读数相关
PrefixUnreadSystem = "unread:system"
PrefixUnreadConversation = "unread:conversation"
PrefixUnreadDetail = "unread:detail"
// 已读位置相关OpenIM 风格:缓存 hasReadSeq用于 O(1) 未读数计算)
PrefixUserReadSeq = "user_read_seq"
// 用户相关
PrefixUserInfo = "users:info"
PrefixUserMe = "users:me"
// 频道相关(全量列表一条缓存,供 /channels、帖子 channel 填充共用)
PrefixChannelsAllList = "channels:all_list"
// 消息缓存相关
refactor: improve system stability, performance, and code structure This commit introduces several architectural improvements and optimizations across the codebase: - **Performance & Reliability**: - Implemented Redis pipelining in `ConversationCache.CacheMessage` to reduce network round-trips. - Added a circuit breaker to the JPush client to prevent cascading failures. - Introduced batch deletion and batch member addition capabilities in repositories. - Added message idempotency support using `client_msg_id` and a Redis-based cache. - Optimized WebSocket handling with connection limits (total and per-user) and improved error logging. - **Code Refactoring**: - Refactored `Router` to use a `RouterDeps` struct, simplifying the constructor and improving maintainability. - Unified model ID generation logic using new `id_helper.go` (supporting UUID and Snowflake). - Standardized JSON serialization/deserialization in models using `json_helper.go`. - Refactored DTO conversion logic, specifically for `UserResponse` (using functional options) and `Report` responses. - Removed redundant/deprecated DTOs like `PostDetailResponse` and `TradeItemDetailResponse`. - **Cache Improvements**: - Enhanced `LayeredCache` with `SetRaw` to avoid double-encoding when promoting values from Redis to local cache. - Added `DeleteBatch` support to the cache interface. - **Other Changes**: - Cleaned up `config.go` by removing redundant default values and explicit environment variable overrides. - Improved WebSocket registration flow to handle connection limits gracefully.
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" // 消息幂等键
keyPrefixSeqBuffer = "seq_buf" // Seq 预分配缓冲区 Hash
)
// PostListKey 生成帖子列表缓存键
// postType: 帖子类型 (hot, follow, latest)
// page: 页码
// pageSize: 每页数量
// userID: 用户维度(仅在个性化列表如 follow 场景使用)
// HotRankZSetKey 热门榜 Redis ZSET 逻辑键(仅存 TopN会经 normalizeKey 加前缀)
func HotRankZSetKey() string {
return HotRankZSet
}
// HotRankTopIDsKey 热门榜 Top 顺序 ID 列表键(会经 normalizeKey 加前缀)
func HotRankTopIDsKey() string {
return HotRankTopIDs
}
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)
}
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)
}
// ChannelsAllListKey 频道全量列表缓存键(含未启用,供 MapByIDs 解析历史帖子 channel
func ChannelsAllListKey() string {
return PrefixChannelsAllList
}
// InvalidateChannelList 失效频道列表缓存(管理端增删改频道后调用)
func InvalidateChannelList(c Cache) {
c.Delete(ChannelsAllListKey())
}
// 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))
}
// ============================================================
// 消息缓存 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))
}
refactor: improve system stability, performance, and code structure This commit introduces several architectural improvements and optimizations across the codebase: - **Performance & Reliability**: - Implemented Redis pipelining in `ConversationCache.CacheMessage` to reduce network round-trips. - Added a circuit breaker to the JPush client to prevent cascading failures. - Introduced batch deletion and batch member addition capabilities in repositories. - Added message idempotency support using `client_msg_id` and a Redis-based cache. - Optimized WebSocket handling with connection limits (total and per-user) and improved error logging. - **Code Refactoring**: - Refactored `Router` to use a `RouterDeps` struct, simplifying the constructor and improving maintainability. - Unified model ID generation logic using new `id_helper.go` (supporting UUID and Snowflake). - Standardized JSON serialization/deserialization in models using `json_helper.go`. - Refactored DTO conversion logic, specifically for `UserResponse` (using functional options) and `Report` responses. - Removed redundant/deprecated DTOs like `PostDetailResponse` and `TradeItemDetailResponse`. - **Cache Improvements**: - Enhanced `LayeredCache` with `SetRaw` to avoid double-encoding when promoting values from Redis to local cache. - Added `DeleteBatch` support to the cache interface. - **Other Changes**: - Cleaned up `config.go` by removing redundant default values and explicit environment variable overrides. - Improved WebSocket registration flow to handle connection limits gracefully.
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)
}
// SeqBufferKey seq 预分配缓冲区 Hash key
func SeqBufferKey(convID string) string {
return fmt.Sprintf("%s:%s", keyPrefixSeqBuffer, convID)
}
// UserReadSeqKey 用户已读位置缓存键OpenIM 风格 SEQ_USER_READ:{convID}:{userID}
func UserReadSeqKey(convID, userID string) string {
return fmt.Sprintf("%s:%s:%s", PrefixUserReadSeq, convID, userID)
}