owpengram-server/internal/app/communities/service.go
A da6a57e1a3 feat: sync community aggregates
Sync telesrv 36eda30 (feat(communities): implement Layer 228 community aggregates).

Skipped telesrv docs changes per public sync rules; normalized the public appearance seed label.
2026-07-20 16:46:02 +08:00

185 lines
8.3 KiB
Go

package communities
import (
"context"
"strings"
"unicode/utf8"
"telesrv/internal/domain"
"telesrv/internal/store"
)
// Service owns Community business validation. The store is the aggregate
// transaction boundary because link changes span community/link and peer rows.
type Service struct {
communities store.CommunityStore
}
func NewService(communities store.CommunityStore) *Service {
return &Service{communities: communities}
}
func validPeer(peer domain.Peer) bool {
return peer.ID > 0 && (peer.Type == domain.PeerTypeChannel || peer.Type == domain.PeerTypeUser)
}
func validVisibility(v domain.CommunityPeerVisibility) bool {
return v == domain.CommunityPeerVisible || v == domain.CommunityPeerHidden
}
func (s *Service) Create(ctx context.Context, userID int64, req domain.CreateCommunityRequest) (domain.CommunityView, error) {
if s == nil || s.communities == nil || userID == 0 || !validPeer(req.InitialPeer) || !validVisibility(req.Visibility) {
return domain.CommunityView{}, domain.ErrCommunityInvalid
}
req.CreatorUserID = userID
req.Title = strings.TrimSpace(req.Title)
req.About = strings.TrimSpace(req.About)
if req.Title == "" || utf8.RuneCountInString(req.Title) > domain.MaxCommunityTitleRunes {
return domain.CommunityView{}, domain.ErrChannelTitleInvalid
}
if utf8.RuneCountInString(req.About) > domain.MaxCommunityAboutRunes {
return domain.CommunityView{}, domain.ErrAboutTooLong
}
return s.communities.CreateCommunity(ctx, req)
}
func (s *Service) Get(ctx context.Context, userID, communityID int64) (domain.CommunityView, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 {
return domain.CommunityView{}, domain.ErrCommunityInvalid
}
return s.communities.GetCommunity(ctx, userID, communityID)
}
func (s *Service) GetMany(ctx context.Context, userID int64, ids []int64) ([]domain.CommunityView, error) {
if s == nil || s.communities == nil || userID == 0 {
return nil, domain.ErrCommunityInvalid
}
return s.communities.GetCommunities(ctx, userID, ids)
}
func (s *Service) ListJoined(ctx context.Context, userID int64) ([]domain.CommunityView, error) {
if s == nil || s.communities == nil || userID == 0 {
return nil, domain.ErrCommunityInvalid
}
return s.communities.ListJoinedCommunities(ctx, userID)
}
func (s *Service) TogglePeerLink(ctx context.Context, userID int64, req domain.CommunityTogglePeerLinkRequest) (domain.CommunityTogglePeerLinkResult, error) {
if s == nil || s.communities == nil || userID == 0 || req.CommunityID == 0 || !validPeer(req.Peer) {
return domain.CommunityTogglePeerLinkResult{}, domain.ErrCommunityInvalid
}
if !req.Deleted && !validVisibility(req.Visibility) {
return domain.CommunityTogglePeerLinkResult{}, domain.ErrCommunityPeerInvalid
}
req.ActorUserID = userID
return s.communities.ToggleCommunityPeerLink(ctx, req)
}
func (s *Service) SetCollapsed(ctx context.Context, userID, communityID int64, collapsed bool) (domain.CommunityView, bool, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 {
return domain.CommunityView{}, false, domain.ErrCommunityInvalid
}
return s.communities.SetCommunityCollapsed(ctx, userID, communityID, collapsed)
}
func (s *Service) ListPeerLinkRequests(ctx context.Context, userID, communityID int64, offset string, limit int) (domain.CommunityPeerLinkRequestPage, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 {
return domain.CommunityPeerLinkRequestPage{}, domain.ErrCommunityInvalid
}
if limit <= 0 || limit > domain.MaxCommunityLinkRequests {
limit = domain.MaxCommunityLinkRequests
}
return s.communities.ListCommunityPeerLinkRequests(ctx, userID, communityID, offset, limit)
}
func (s *Service) DecidePeerLinkRequest(ctx context.Context, userID, communityID int64, peer domain.Peer, reject bool, date int) (domain.CommunityTogglePeerLinkResult, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 || !validPeer(peer) {
return domain.CommunityTogglePeerLinkResult{}, domain.ErrCommunityInvalid
}
return s.communities.DecideCommunityPeerLinkRequest(ctx, userID, communityID, peer, reject, date)
}
func (s *Service) DecideAllPeerLinkRequests(ctx context.Context, userID, communityID int64, reject bool, date int) ([]domain.CommunityTogglePeerLinkResult, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 {
return nil, domain.ErrCommunityInvalid
}
return s.communities.DecideAllCommunityPeerLinkRequests(ctx, userID, communityID, reject, date)
}
func (s *Service) ToggleParticipantBanned(ctx context.Context, userID, communityID, participantUserID int64, unban bool, date int) (domain.CommunityParticipantBanResult, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 || participantUserID == 0 {
return domain.CommunityParticipantBanResult{}, domain.ErrCommunityInvalid
}
return s.communities.ToggleCommunityParticipantBanned(ctx, userID, communityID, participantUserID, unban, date)
}
func (s *Service) ParticipantJoinedChats(ctx context.Context, userID, communityID, participantUserID int64) (domain.CommunityParticipantJoinedChats, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 || participantUserID == 0 {
return domain.CommunityParticipantJoinedChats{}, domain.ErrCommunityInvalid
}
return s.communities.GetCommunityParticipantJoinedChats(ctx, userID, communityID, participantUserID)
}
func (s *Service) Participants(ctx context.Context, userID, communityID int64, filter domain.ChannelParticipantsFilter, offset, limit int) (domain.CommunityParticipantList, error) {
if s == nil || s.communities == nil || userID == 0 || communityID == 0 {
return domain.CommunityParticipantList{}, domain.ErrCommunityInvalid
}
if offset < 0 {
offset = 0
}
if offset > domain.MaxChannelParticipantsOffset {
offset = domain.MaxChannelParticipantsOffset
}
if limit <= 0 || limit > domain.MaxCommunityParticipants {
limit = domain.MaxCommunityParticipants
}
return s.communities.ListCommunityParticipants(ctx, userID, communityID, filter, offset, limit)
}
func (s *Service) EditTitle(ctx context.Context, userID, communityID int64, title string) (domain.CommunityView, bool, error) {
title = strings.TrimSpace(title)
if title == "" || utf8.RuneCountInString(title) > domain.MaxCommunityTitleRunes {
return domain.CommunityView{}, false, domain.ErrChannelTitleInvalid
}
return s.communities.EditCommunityTitle(ctx, userID, communityID, title)
}
func (s *Service) EditAbout(ctx context.Context, userID, communityID int64, about string) (domain.CommunityView, bool, error) {
about = strings.TrimSpace(about)
if utf8.RuneCountInString(about) > domain.MaxCommunityAboutRunes {
return domain.CommunityView{}, false, domain.ErrAboutTooLong
}
return s.communities.EditCommunityAbout(ctx, userID, communityID, about)
}
func (s *Service) EditAdmin(ctx context.Context, userID int64, req domain.CommunityEditAdminRequest) (domain.CommunityView, bool, error) {
if req.CommunityID == 0 || req.UserID == 0 || userID == 0 {
return domain.CommunityView{}, false, domain.ErrCommunityInvalid
}
req.ActorUserID = userID
return s.communities.EditCommunityAdmin(ctx, req)
}
func (s *Service) EditDefaultBannedRights(ctx context.Context, userID, communityID int64, rights domain.ChannelBannedRights) (domain.CommunityView, bool, error) {
return s.communities.EditCommunityDefaultBannedRights(ctx, userID, communityID, rights)
}
func (s *Service) SetPhoto(ctx context.Context, userID, communityID int64, photo *domain.Photo, date int) (domain.CommunityView, bool, error) {
return s.communities.SetCommunityPhoto(ctx, userID, communityID, photo, date)
}
func (s *Service) Delete(ctx context.Context, userID, communityID int64, date int) (domain.CommunityView, []domain.Peer, error) {
return s.communities.DeleteCommunity(ctx, userID, communityID, date)
}
func (s *Service) SetPinned(ctx context.Context, userID, communityID int64, pinned bool) (bool, error) {
return s.communities.SetCommunityPinned(ctx, userID, communityID, pinned)
}
func (s *Service) ReorderPinned(ctx context.Context, userID int64, order []domain.Peer, force bool) (bool, error) {
return s.communities.ReorderCommunityPinned(ctx, userID, order, force)
}
func (s *Service) SearchScope(ctx context.Context, userID, communityID int64) (domain.CommunitySearchScope, error) {
return s.communities.CommunitySearchScope(ctx, userID, communityID)
}