feat(chat): implement batch mark-as-read and message sync data
All checks were successful
Build Backend / build (push) Successful in 2m2s
Build Backend / build-docker (push) Successful in 1m41s

Refactor unread count logic to use arithmetic calculation (maxSeq - readSeq)
instead of manual incrementing/decrementing. This simplifies the cache
management and improves consistency.

New features:
- Batch mark-as-read functionality for multiple conversations.
- Lightweight sync data endpoint to retrieve conversation metadata
  (maxSeq and last message timestamp) for client synchronization.
- Optimized batch retrieval of conversation max sequences using Redis MGet.

Technical changes:
- Deprecated `IncrementUnread` and `ClearUnread` in `ConversationCache`.
- Added `GetConvMaxSeqBatch` to reduce network round-trips.
- Added `HandleMarkReadAll` and `HandleGetSyncData` handlers.
- Updated `ChatService` to support batch operations and sync data retrieval.
This commit is contained in:
2026-05-12 18:04:59 +08:00
parent 2f2bbc646e
commit 8d7e8c427b
5 changed files with 236 additions and 175 deletions

View File

@@ -565,6 +565,26 @@ func (h *MessageHandler) GetUnreadCount(c *gin.Context) {
})
}
// HandleGetSyncData 获取同步元数据(轻量级 seq + 时间)
// GET /api/v1/conversations/sync-data
func (h *MessageHandler) HandleGetSyncData(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
items, err := h.chatService.GetSyncData(c.Request.Context(), userID)
if err != nil {
response.InternalServerError(c, "failed to get sync data")
return
}
response.Success(c, &dto.SyncDataResponse{
Conversations: items,
})
}
// GetConversationUnreadCount 获取单个会话的未读数
// GET /api/conversations/:id/unread/count
func (h *MessageHandler) GetConversationUnreadCount(c *gin.Context) {
@@ -900,6 +920,33 @@ func (h *MessageHandler) HandleMarkRead(c *gin.Context) {
response.SuccessWithMessage(c, "marked as read", nil)
}
// HandleMarkReadAll 批量标记所有会话已读
// POST /api/v1/conversations/read-all
func (h *MessageHandler) HandleMarkReadAll(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
var req dto.BatchMarkReadRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error())
return
}
successCount, err := h.chatService.MarkAsReadBatch(c.Request.Context(), userID, req.Conversations)
if err != nil {
response.InternalServerError(c, "failed to mark all as read")
return
}
response.Success(c, &dto.BatchMarkReadResponse{
SuccessCount: successCount,
TotalCount: len(req.Conversations),
})
}
// HandleSetConversationPinned 设置会话置顶
// PUT /api/v1/conversations/:id/pinned
func (h *MessageHandler) HandleSetConversationPinned(c *gin.Context) {