feat(posts): filter posts by blocked users
All checks were successful
Build Backend / build (push) Successful in 2m49s
Build Backend / build-docker (push) Successful in 1m19s

Add functionality to filter out posts from blocked users in post listing endpoints. Includes a new `IsBlockedBatch` method for efficient batch checking of blocked relationships and `is_blocked` field in API responses when viewing a blocked user's posts.
This commit is contained in:
lafay
2026-04-25 11:25:43 +08:00
parent 323f872aa9
commit e0f34653a2
5 changed files with 82 additions and 2 deletions

View File

@@ -1285,6 +1285,7 @@ type PostCursorPageResponse struct {
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
IsBlocked bool `json:"is_blocked,omitempty"`
}
// CommentCursorPageResponse 评论游标分页响应

View File

@@ -1,6 +1,7 @@
package handler
import (
"context"
"errors"
"fmt"
"strconv"
@@ -31,6 +32,41 @@ func NewPostHandler(postService service.PostService, userService service.UserSer
}
}
// collectAuthorIDs 从帖子列表中收集去重的作者ID
func collectAuthorIDs(posts []*model.Post) []string {
seen := make(map[string]struct{})
ids := make([]string, 0, len(posts))
for _, p := range posts {
if _, ok := seen[p.UserID]; !ok {
seen[p.UserID] = struct{}{}
ids = append(ids, p.UserID)
}
}
return ids
}
// filterBlockedPosts 过滤掉被当前用户拉黑的作者发布的帖子
func (h *PostHandler) filterBlockedPosts(ctx context.Context, posts []*model.Post, currentUserID string) []*model.Post {
if currentUserID == "" || len(posts) == 0 {
return posts
}
authorIDs := collectAuthorIDs(posts)
blockedMap, err := h.userService.IsBlockedBatch(ctx, currentUserID, authorIDs)
if err != nil {
return posts
}
filtered := make([]*model.Post, 0, len(posts))
for _, p := range posts {
if blockedMap[p.UserID] {
continue
}
filtered = append(filtered, p)
}
return filtered
}
// channelMapForPosts 批量解析帖子中的频道 ID用于填充响应里的 channel 名称
func (h *PostHandler) channelMapForPosts(posts []*model.Post) map[string]*model.Channel {
ids := dto.CollectPostChannelIDs(posts)
@@ -267,6 +303,8 @@ func (h *PostHandler) List(c *gin.Context) {
return
}
posts = h.filterBlockedPosts(c.Request.Context(), posts, currentUserID)
// 批量获取交互状态
postIDs := make([]string, len(posts))
for i, post := range posts {
@@ -324,6 +362,8 @@ func (h *PostHandler) ListByCursor(c *gin.Context) {
return
}
result.Items = h.filterBlockedPosts(c.Request.Context(), result.Items, currentUserID)
// 批量获取交互状态
postIDs := make([]string, len(result.Items))
for i, post := range result.Items {
@@ -577,6 +617,32 @@ func (h *PostHandler) GetUserPosts(c *gin.Context) {
return
}
// 检查是否拉黑了对方
if currentUserID != "" && currentUserID != userID {
isBlocked, _ := h.userService.IsBlocked(c.Request.Context(), currentUserID, userID)
if isBlocked {
// 检查是否使用游标分页
cursorStr := c.Query("cursor")
if cursorStr != "" {
response.Success(c, &dto.PostCursorPageResponse{
Items: []*dto.PostResponse{},
HasMore: false,
IsBlocked: true,
})
} else {
response.Success(c, gin.H{
"list": []*dto.PostResponse{},
"total": 0,
"page": 1,
"page_size": 20,
"total_pages": 0,
"is_blocked": true,
})
}
return
}
}
// 检查是否使用游标分页
cursorStr := c.Query("cursor")
if cursorStr != "" {
@@ -709,6 +775,9 @@ func (h *PostHandler) Search(c *gin.Context) {
// 批量获取交互状态
currentUserID := c.GetString("user_id")
posts = h.filterBlockedPosts(c.Request.Context(), posts, currentUserID)
postIDs := make([]string, len(posts))
for i, post := range posts {
postIDs[i] = post.ID
@@ -736,6 +805,8 @@ func (h *PostHandler) SearchByCursor(c *gin.Context) {
return
}
result.Items = h.filterBlockedPosts(c.Request.Context(), result.Items, currentUserID)
// 批量获取交互状态
postIDs := make([]string, len(result.Items))
for i, post := range result.Items {

View File

@@ -3,6 +3,7 @@
import (
"context"
"strconv"
"time"
"with_you/internal/model"
"with_you/internal/pkg/cursor"
@@ -88,7 +89,7 @@ func (r *systemNotificationRepository) GetUnreadCount(receiverID string) (int64,
// MarkAsRead 标记单条通知为已读
func (r *systemNotificationRepository) MarkAsRead(id int64, receiverID string) error {
now := model.SystemNotification{}.UpdatedAt
now := time.Now()
return r.db.Model(&model.SystemNotification{}).
Where("id = ? AND receiver_id = ?", id, receiverID).
Updates(map[string]any{
@@ -99,7 +100,7 @@ func (r *systemNotificationRepository) MarkAsRead(id int64, receiverID string) e
// MarkAllAsRead 标记用户所有通知为已读
func (r *systemNotificationRepository) MarkAllAsRead(receiverID string) error {
now := model.SystemNotification{}.UpdatedAt
now := time.Now()
return r.db.Model(&model.SystemNotification{}).
Where("receiver_id = ? AND is_read = ?", receiverID, false).
Updates(map[string]any{

View File

@@ -482,6 +482,7 @@ func (s *pushServiceImpl) pushSystemNotificationViaWebSocket(ctx context.Context
"type": string(notification.Type),
"title": notification.Title,
"content": notification.Content,
"is_read": notification.IsRead,
"extra": map[string]any{},
"created_at": notification.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
}

View File

@@ -53,6 +53,7 @@ type UserService interface {
UnblockUser(ctx context.Context, blockerID, blockedID string) error
GetBlockedUsers(ctx context.Context, blockerID string, page, pageSize int) ([]*model.User, int64, error)
IsBlocked(ctx context.Context, blockerID, blockedID string) (bool, error)
IsBlockedBatch(ctx context.Context, blockerID string, blockedIDs []string) (map[string]bool, error)
// 隐私设置
GetPrivacySettings(ctx context.Context, userID string) (*model.PrivacySettings, error)
@@ -583,6 +584,11 @@ func (s *userServiceImpl) IsBlocked(ctx context.Context, blockerID, blockedID st
return s.userRepo.IsBlocked(blockerID, blockedID)
}
// IsBlockedBatch 批量检查拉黑关系
func (s *userServiceImpl) IsBlockedBatch(ctx context.Context, blockerID string, blockedIDs []string) (map[string]bool, error) {
return s.userRepo.IsBlockedBatch(blockerID, blockedIDs)
}
// GetFollowingList 获取关注列表(字符串参数版本)
func (s *userServiceImpl) GetFollowingList(ctx context.Context, userID, page, pageSize string) ([]*model.User, error) {
// 转换字符串参数为整数