spark-server/internal/module/conversation/logic/p2p.go
zhr ae9a71034d feat: implement Spark IM V3 modular monolith
Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 19:52:11 +08:00

73 lines
2.0 KiB
Go

package logic
import (
"context"
"errors"
"time"
"spark-server/internal/module/conversation/entity"
"spark-server/internal/pkg/errs"
)
type IDGen interface {
NextID() (int64, error)
}
type P2PRepository interface {
GetP2P(ctx context.Context, userA, userB int64) (*entity.Conversation, error)
CreateP2P(ctx context.Context, c *entity.Conversation, p2p *entity.P2P, members []*entity.Member) error
UpdateLastMsg(ctx context.Context, id, msgID, atMS int64) error
}
// P2PService opens the unique 1:1 conversation for a user pair.
type P2PService struct {
repo P2PRepository
ids IDGen
}
func NewP2PService(repo P2PRepository, ids IDGen) *P2PService {
return &P2PService{repo: repo, ids: ids}
}
func (s *P2PService) EnsureP2P(ctx context.Context, userA, userB int64) (int64, error) {
if s == nil || s.repo == nil || s.ids == nil || userA <= 0 || userB <= 0 || userA == userB {
return 0, errs.ErrInvalidArgument
}
if c, err := s.repo.GetP2P(ctx, userA, userB); err == nil {
return c.ID, nil
} else if !errors.Is(err, errs.ErrNotFound) {
return 0, err
}
id, err := s.ids.NextID()
if err != nil {
return 0, err
}
now := time.Now()
small, big := entity.OrderUsers(userA, userB)
conv := &entity.Conversation{ID: id, Type: entity.TypeP2P, LastMsgAt: now.UnixMilli()}
p2p := &entity.P2P{UserSmall: small, UserBig: big, ConversationID: id}
joined := now.UnixMilli()
members := []*entity.Member{
{ConversationID: id, UserID: userA, JoinedAtMS: joined},
{ConversationID: id, UserID: userB, JoinedAtMS: joined},
}
if err := s.repo.CreateP2P(ctx, conv, p2p, members); err != nil {
if errors.Is(err, errs.ErrDuplicate) {
c, err := s.repo.GetP2P(ctx, userA, userB)
if err != nil {
return 0, err
}
return c.ID, nil
}
return 0, err
}
return id, nil
}
func (s *P2PService) UpdateLastMsg(ctx context.Context, conversationID, msgID, atMS int64) error {
if s == nil || s.repo == nil {
return errs.ErrAvailable
}
return s.repo.UpdateLastMsg(ctx, conversationID, msgID, atMS)
}