Files
backend/internal/cache/keys.go
lan ee78071d4d
All checks were successful
Build Backend / build (push) Successful in 3m2s
Build Backend / build-docker (push) Successful in 2m44s
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

235 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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"
// 用户相关
PrefixUserInfo = "users:info"
PrefixUserMe = "users:me"
// 频道相关(全量列表一条缓存,供 /channels、帖子 channel 填充共用)
PrefixChannelsAllList = "channels:all_list"
// 消息缓存相关
keyPrefixMsgHash = "msg_hash" // 消息详情 Hash
keyPrefixMsgIndex = "msg_index" // 消息索引 Sorted Set
keyPrefixMsgCount = "msg_count" // 消息计数
keyPrefixMsgSeq = "msg_seq" // Seq 计数器
keyPrefixMsgPage = "msg_page" // 分页缓存
keyPrefixMsgIdempotent = "msg_idem" // 消息幂等键
)
// 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)
}
// ConversationDetailKey 生成会话详情缓存键
func ConversationDetailKey(conversationID, userID string) string {
return fmt.Sprintf("%s:%s:%s", PrefixConversationDetail, conversationID, userID)
}
// GroupMembersKey 生成群组成员缓存键
func GroupMembersKey(groupID string, page, pageSize int) string {
return fmt.Sprintf("%s:%s:page:%d:size:%d", PrefixGroupMembers, groupID, page, pageSize)
}
// GroupMembersAllKey 生成群组全量成员ID列表缓存键
func GroupMembersAllKey(groupID string) string {
return fmt.Sprintf("%s:all:%s", PrefixGroupMembers, groupID)
}
// GroupInfoKey 生成群组信息缓存键
func GroupInfoKey(groupID string) string {
return fmt.Sprintf("%s:%s", PrefixGroupInfo, groupID)
}
// 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)
}
// UserInfoKey 生成用户信息缓存键
func UserInfoKey(userID string) string {
return fmt.Sprintf("%s:%s", PrefixUserInfo, userID)
}
// UserMeKey 生成当前用户信息缓存键
func UserMeKey(userID string) string {
return fmt.Sprintf("%s:%s", PrefixUserMe, userID)
}
// 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 + ":")
}
// InvalidateConversationDetail 失效会话详情缓存
func InvalidateConversationDetail(cache Cache, conversationID, userID string) {
cache.Delete(ConversationDetailKey(conversationID, userID))
}
// InvalidateGroupMembers 失效群组成员缓存
func InvalidateGroupMembers(cache Cache, groupID string) {
cache.DeleteByPrefix(PrefixGroupMembers + ":" + groupID)
}
// InvalidateGroupInfo 失效群组信息缓存
func InvalidateGroupInfo(cache Cache, groupID string) {
cache.Delete(GroupInfoKey(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))
}
// InvalidateUserInfo 失效用户信息缓存
func InvalidateUserInfo(cache Cache, userID string) {
cache.Delete(UserInfoKey(userID))
cache.Delete(UserMeKey(userID))
}
// ============================================================
// 消息缓存 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))
}
// MessageIdempotentKey 消息幂等键 (clientMsgID -> server MsgID)
func MessageIdempotentKey(senderID, clientMsgID string) string {
return fmt.Sprintf("%s:%s:%s", keyPrefixMsgIdempotent, senderID, clientMsgID)
}
// PushDedupKey 推送去重键,防止同一消息重复推送给同一用户
func PushDedupKey(userID, messageID string) string {
return fmt.Sprintf("push_dedup:%s:%s", userID, messageID)
}