package logic import ( "context" "errors" conventity "spark-server/internal/module/conversation/entity" "spark-server/internal/pkg/errs" ) type HistoryRequest struct { ConversationID int64 LastMsgID int64 Limit int } type HistoryResponse struct { Items []*Item `json:"items"` HasMore bool `json:"has_more"` } func (s *Service) GetHistory(ctx context.Context, userID int64, req HistoryRequest) (*HistoryResponse, error) { if !s.ready() || userID <= 0 { return nil, errs.ErrUnauthorized } if req.ConversationID <= 0 { return nil, errs.ErrInvalidArgument } if _, err := s.chats.GetMember(ctx, req.ConversationID, userID); err != nil { if errors.Is(err, errs.ErrNotFound) { return nil, errs.ErrForbidden } return nil, err } limit := req.Limit if limit <= 0 || limit > defaultHistoryLimit { limit = defaultHistoryLimit } rows, err := s.repo.ListByConversation(ctx, req.ConversationID, userID, req.LastMsgID, limit+1) if err != nil { return nil, err } hasMore := len(rows) > limit if hasMore { rows = rows[:limit] } peer := s.viewerPeer(ctx, userID, req.ConversationID) items := make([]*Item, 0, len(rows)) for _, row := range rows { item := itemOf(row) item.PeerID = peer items = append(items, item) } return &HistoryResponse{Items: items, HasMore: hasMore}, nil } // viewerPeer is the other member of a P2P conversation. Group chats stay 0; use group_id. // System messages keep sender_id=0 and receiver_id=0; this field is how the viewer finds the peer. func (s *Service) viewerPeer(ctx context.Context, viewerID, conversationID int64) int64 { conv, err := s.chats.Get(ctx, conversationID) if err != nil || conv == nil || conv.Type != conventity.TypeP2P { return 0 } ids, err := s.chats.ListMemberIDs(ctx, conversationID) if err != nil { return 0 } for _, id := range ids { if id > 0 && id != viewerID { return id } } return 0 }