feat(api): add cursor-based pagination for system messages
Some checks failed
Build Backend / build-docker (push) Has been cancelled
Build Backend / build (push) Has been cancelled

- Add SystemMessageCursorPageResponse DTO for cursor pagination
- Implement GetSystemMessagesByCursor handler method
- Add GetByReceiverIDCursor repository method with cursor decoding
- Maintain backward compatibility with existing offset-based pagination
This commit is contained in:
lafay
2026-03-21 02:48:06 +08:00
parent 687ac92aea
commit 1a48bb440c
3 changed files with 88 additions and 0 deletions

View File

@@ -528,6 +528,14 @@ type SystemUnreadCountResponse struct {
UnreadCount int64 `json:"unread_count"`
}
// SystemMessageCursorPageResponse 系统消息游标分页响应
type SystemMessageCursorPageResponse struct {
Items []*SystemMessageResponse `json:"items"`
NextCursor string `json:"next_cursor"`
PrevCursor string `json:"prev_cursor"`
HasMore bool `json:"has_more"`
}
// ==================== 时间格式化 ====================
// FormatTime 格式化时间

View File

@@ -42,6 +42,14 @@ func (h *SystemMessageHandler) GetSystemMessages(c *gin.Context) {
return
}
// 检查是否使用游标分页
_, cursorExists := c.GetQuery("cursor")
if cursorExists {
h.GetSystemMessagesByCursor(c)
return
}
// 偏移分页(向后兼容)
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
@@ -62,6 +70,43 @@ func (h *SystemMessageHandler) GetSystemMessages(c *gin.Context) {
response.Paginated(c, result, total, page, pageSize)
}
// GetSystemMessagesByCursor 游标分页获取系统消息列表
func (h *SystemMessageHandler) GetSystemMessagesByCursor(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
// 解析游标分页请求
cursorStr := c.Query("cursor")
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
// 获取当前用户的系统通知
notifications, nextCursor, hasMore, err := h.notifyRepo.GetByReceiverIDCursor(userID, cursorStr, pageSize)
if err != nil {
response.InternalServerError(c, "failed to get system messages")
return
}
// 转换为响应格式
items := make([]*dto.SystemMessageResponse, 0)
for _, n := range notifications {
resp := dto.SystemNotificationToResponse(n)
items = append(items, resp)
}
// 构建游标分页响应
cursorResp := &dto.SystemMessageCursorPageResponse{
Items: items,
NextCursor: nextCursor,
PrevCursor: "",
HasMore: hasMore,
}
response.Success(c, cursorResp)
}
// GetUnreadCount 获取系统消息未读数
// GET /api/v1/messages/system/unread-count
func (h *SystemMessageHandler) GetUnreadCount(c *gin.Context) {

View File

@@ -112,3 +112,38 @@ func (r *SystemNotificationRepository) GetByType(receiverID string, notifyType m
return notifications, total, err
}
// GetByReceiverIDCursor 游标分页获取用户的通知列表
func (r *SystemNotificationRepository) GetByReceiverIDCursor(receiverID string, cursor string, pageSize int) ([]*model.SystemNotification, string, bool, error) {
query := r.db.Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID)
// 如果有游标,解析游标并添加条件
if cursor != "" {
// 游标格式createdAt_id
query = query.Where("created_at < ? OR (created_at = ? AND id < ?)", cursor, cursor, cursor)
}
// 多取一条用于判断是否还有下一页
var notifications []*model.SystemNotification
err := query.Order("created_at DESC, id DESC").
Limit(pageSize + 1).
Find(&notifications).Error
if err != nil {
return nil, "", false, err
}
hasMore := len(notifications) > pageSize
if hasMore {
notifications = notifications[:pageSize]
}
// 生成下一页游标
var nextCursor string
if hasMore && len(notifications) > 0 {
lastNotification := notifications[len(notifications)-1]
nextCursor = lastNotification.CreatedAt.Format("2006-01-02 15:04:05.999999999")
}
return notifications, nextCursor, hasMore, nil
}