package logic import ( "context" "spark-server/internal/module/group/entity" "spark-server/internal/pkg/errs" "spark-server/internal/pkg/i18n" "spark-server/internal/pkg/reqctx" "spark-server/internal/pkg/validate" ) type InviteInput struct { UIDs []int64 `json:"uids" validate:"required,min=1,max=199,dive,gt=0" label:"field.member_uids" errmsg:"required:validate.group.members_required;min:validate.group.members_required;max:validate.group.members_invalid"` } func (s *Service) Invite(ctx context.Context, actorID, groupID int64, in *InviteInput) (*View, error) { if !s.ready() || actorID <= 0 { return nil, errs.ErrUnauthorized } if in == nil { return nil, errs.ErrInvalidArgument } if err := validate.StructLang(reqctx.Lang(ctx), in); err != nil { return nil, err } g, err := s.loadActive(ctx, groupID) if err != nil { return nil, err } if _, err := s.requireMember(ctx, groupID, actorID); err != nil { return nil, err } added, err := s.newMembers(ctx, g, actorID, in.UIDs) if err != nil { return nil, err } if err := s.repo.AddMembers(ctx, groupID, added); err != nil { return nil, err } g.MemberCount += len(added) s.invalidateMembers(g.ConversationID) ids := make([]int64, 0, len(added)) for _, mem := range added { ids = append(ids, mem.UserID) } _ = s.notice(ctx, g, actorID, entity.SystemKindInvite, ids) mem, err := s.requireMember(ctx, groupID, actorID) if err != nil { return viewOf(g, entity.RoleMember), nil } return viewOf(g, mem.Role), nil } func (s *Service) newMembers(ctx context.Context, g *entity.Group, actorID int64, uids []int64) ([]*entity.Member, error) { lang := reqctx.Lang(ctx) now := nowMS() added := make([]*entity.Member, 0, len(uids)) for _, uid := range entity.UniqueUIDs(uids) { if uid == actorID { continue } if _, err := s.repo.GetMember(ctx, g.ID, uid); err == nil { continue } if err := s.activeUser(ctx, uid); err != nil { return nil, err } if err := s.requireFriend(ctx, actorID, uid); err != nil { return nil, err } added = append(added, &entity.Member{GroupID: g.ID, UserID: uid, Role: entity.RoleMember, JoinedAtMS: now}) } if len(added) == 0 { return nil, errs.ErrDuplicate.WithMsg(i18n.T(lang, i18n.KeyGroupAlreadyMember, nil)) } if g.MemberCount+len(added) > entity.MaxMembers { return nil, errs.ErrInvalidArgument.WithMsg(i18n.T(lang, i18n.KeyGroupFull, nil)) } return added, nil }