feat(chat): implement sequence-based unread count tracking
All checks were successful
Build Backend / build (push) Successful in 1m56s
Build Backend / build-docker (push) Successful in 1m9s

Introduce a new mechanism for tracking read positions using message
sequences (hasReadSeq), similar to OpenIM, to allow for O(1) unread
count calculations.

- Implement `UserReadSeq` caching in Redis with Lua scripts to prevent
  sequence regression (ensuring updates only occur if the new sequence
  is greater than the current one).
- Update `ConversationCache` to support sequence-based unread count
  computation (`maxSeq - hasReadSeq`).
- Refactor `MessageRepository` to use conditional updates for
  `last_read_seq` in the database.
- Update `ChatService` to automatically update the sender's read
  sequence when sending a message.
- Optimize `MarkAsRead` logic to update both database and Redis
  sequence caches and refine notification targets for private vs
  group chats.
- Update `jpush` client to use pointer types for boolean fields to
  properly handle optional values in JSON.
This commit is contained in:
2026-05-10 13:36:58 +08:00
parent 43348615c0
commit 628a6acbe9
6 changed files with 315 additions and 23 deletions

View File

@@ -29,6 +29,9 @@ const (
PrefixUnreadHash = "unread:hash"
PrefixUnreadTotal = "unread:total"
// 已读位置相关OpenIM 风格:缓存 hasReadSeq用于 O(1) 未读数计算)
PrefixUserReadSeq = "user_read_seq"
// 用户相关
PrefixUserInfo = "users:info"
PrefixUserMe = "users:me"
@@ -244,3 +247,13 @@ func MessageIdempotentKey(senderID, clientMsgID string) string {
func PushDedupKey(userID, messageID string) string {
return fmt.Sprintf("push_dedup:%s:%s", userID, messageID)
}
// UserReadSeqKey 用户已读位置缓存键OpenIM 风格 SEQ_USER_READ:{convID}:{userID}
func UserReadSeqKey(convID, userID string) string {
return fmt.Sprintf("%s:%s:%s", PrefixUserReadSeq, convID, userID)
}
// ConvMaxSeqKey 会话最大 seq 缓存键
func ConvMaxSeqKey(convID string) string {
return fmt.Sprintf("%s:%s", keyPrefixMsgSeq, convID)
}