package logic import ( "context" friendentity "spark-server/internal/module/friend/entity" "spark-server/internal/module/square/entity" userentity "spark-server/internal/module/user/entity" "spark-server/internal/pkg/media" "go.uber.org/zap" ) // Repository persists square data. Model implements it. type Repository interface { CreatePost(ctx context.Context, post *entity.Post, audience []int64) error GetPost(ctx context.Context, id int64) (*entity.Post, error) ListPublicFeed(ctx context.Context, cursor int64, limit int) ([]*entity.Post, error) ListByAuthor(ctx context.Context, authorID, cursor int64, limit int) ([]*entity.Post, error) SoftDeletePost(ctx context.Context, userID, postID int64) (*entity.Post, error) ListAudience(ctx context.Context, postIDs []int64) (map[int64][]int64, error) ListLikes(ctx context.Context, postIDs []int64) (map[int64][]*entity.Like, error) AddLike(ctx context.Context, like *entity.Like) (created bool, err error) RemoveLike(ctx context.Context, postID, userID int64) error ListComments(ctx context.Context, postIDs []int64) (map[int64][]*entity.Comment, error) GetComment(ctx context.Context, id int64) (*entity.Comment, error) AddComment(ctx context.Context, c *entity.Comment) error SoftDeleteComment(ctx context.Context, id int64) error CreateNotices(ctx context.Context, rows []*entity.Notice) error ListNotices(ctx context.Context, userID, cursor int64, limit int) ([]*entity.Notice, error) MarkNoticesRead(ctx context.Context, userID, cursor int64) error GetNoticeState(ctx context.Context, userID int64) (*entity.NoticeState, error) LatestNoticeID(ctx context.Context, userID int64) (int64, error) AdvanceNoticeRead(ctx context.Context, userID, noticeID int64) error ListAllByAuthor(ctx context.Context, authorID int64) ([]*entity.Post, error) PurgeUser(ctx context.Context, userID int64) error } // Friends is the friend graph used for audience and block checks. type Friends interface { AreFriends(ctx context.Context, a, b int64) (bool, error) ListBlockedPeerIDs(ctx context.Context, userID int64) ([]int64, error) Relation(ctx context.Context, userID, peerID int64) (*friendentity.Relation, error) BlockedEither(ctx context.Context, a, b int64) (bool, error) } // Users loads profile cards. type Users interface { Get(ctx context.Context, id int64) (*userentity.User, error) } // Objects deletes square media. Nil skips GC. type Objects interface { Delete(ctx context.Context, key string) error } // IDGen issues Snowflake ids. type IDGen interface { NextID() (int64, error) } // Service is the square use-case layer. type Service struct { repo Repository friends Friends users Users objects Objects ids IDGen burst *burst media media.Policy log *zap.Logger } func NewService(repo Repository, friends Friends, users Users, objects Objects, ids IDGen) *Service { return &Service{repo: repo, friends: friends, users: users, objects: objects, ids: ids, burst: newBurst(), log: zap.NewNop()} } // ConfigureMedia sets user-path media detection (bots compress via squarebot MediaCompressor). func (s *Service) ConfigureMedia(p media.Policy, log *zap.Logger) { if s == nil { return } s.media = p if log != nil { s.log = log } } func (s *Service) ready() bool { return s != nil && s.repo != nil && s.friends != nil && s.users != nil && s.ids != nil }