package logic import ( "context" "spark-server/internal/module/friend/entity" msgentity "spark-server/internal/module/message/entity" userentity "spark-server/internal/module/user/entity" ) // Repository is friend persistence. Model implements it; do not split this by table. type Repository interface { GetRelation(ctx context.Context, a, b int64) (*entity.Relation, error) ListFriends(ctx context.Context, userID int64, limit int) ([]*entity.Relation, error) GetRequest(ctx context.Context, id int64) (*entity.Request, error) GetRequestByPair(ctx context.Context, from, to int64) (*entity.Request, error) UpsertRequest(ctx context.Context, req *entity.Request) error ListIncoming(ctx context.Context, toUserID int64, status, limit int) ([]*entity.Request, error) GetRemark(ctx context.Context, userID, friendID int64) (string, error) ListRemarks(ctx context.Context, userID int64, friendIDs []int64) (map[int64]string, error) SetRemark(ctx context.Context, userID, friendID int64, remark string) error IsBlocked(ctx context.Context, userID, targetID int64) (bool, error) BlockedEither(ctx context.Context, a, b int64) (bool, error) CreateBlock(ctx context.Context, b *entity.Block) error DeleteBlock(ctx context.Context, userID, blockedID int64) error CreateReport(ctx context.Context, r *entity.Report) error AcceptFriend(ctx context.Context, in entity.AcceptParams) error DeleteFriend(ctx context.Context, a, b int64) error } // UserFinder resolves accounts by uid / email / spark_id / qrcode ticket. type UserFinder interface { Get(ctx context.Context, id int64) (*userentity.User, error) GetByEmail(ctx context.Context, email string) (*userentity.User, error) GetBySparkID(ctx context.Context, sparkID string) (*userentity.User, error) GetByQRCode(ctx context.Context, qrcode string) (*userentity.User, error) } // IDGen issues Snowflake ids for requests, reports, and system messages. type IDGen interface { NextID() (int64, error) } // Greeter writes the "became friends" system message. Nil skips greeting. type Greeter interface { BecameFriends(ctx context.Context, userA, userB int64) error } // P2PChats opens (or reuses) the 1:1 conversation for a pair. type P2PChats interface { EnsureP2P(ctx context.Context, userA, userB int64) (int64, error) UpdateLastMsg(ctx context.Context, conversationID, msgID, atMS int64) error } // Messages persists a row in messages. type Messages interface { Create(ctx context.Context, m *msgentity.Message) error } // Service is the friend use-case layer (HTTP and TCP share it). type Service struct { repo Repository users UserFinder ids IDGen greeter Greeter } func NewService(repo Repository, users UserFinder, ids IDGen, greeter Greeter) *Service { return &Service{repo: repo, users: users, ids: ids, greeter: greeter} } func (s *Service) ready() bool { return s != nil && s.repo != nil && s.users != nil && s.ids != nil }