package logic import ( "context" "strings" "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 CreateInput struct { Name string `json:"name" validate:"required,min=1,max=32" label:"field.group_name" errmsg:"required:validate.group.name_required;min:validate.group.name_invalid;max:validate.group.name_invalid"` MemberUIDs []int64 `json:"member_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) Create(ctx context.Context, ownerID int64, in *CreateInput) (*View, error) { if !s.ready() || ownerID <= 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 } invitees, err := s.collectInvitees(ctx, ownerID, in.MemberUIDs) if err != nil { return nil, err } convID, err := s.ids.NextID() if err != nil { return nil, err } now := nowMS() g := &entity.Group{ ConversationID: convID, Name: strings.TrimSpace(in.Name), OwnerID: ownerID, MemberCount: 1 + len(invitees), Status: entity.StatusActive, CreatedAtMS: now, UpdatedAtMS: now, } members := ownerAndInvitees(g.ID, ownerID, invitees, now) if err := s.repo.CreateGroup(ctx, g, members); err != nil { return nil, err } s.invalidateMembers(g.ConversationID) s.stampAvatar(ctx, g, ownerID, invitees) _ = s.notice(ctx, g, ownerID, entity.SystemKindCreated, invitees) return viewOf(g, entity.RoleOwner), nil } func (s *Service) collectInvitees(ctx context.Context, ownerID int64, uids []int64) ([]int64, error) { lang := reqctx.Lang(ctx) out := make([]int64, 0, len(uids)) for _, uid := range entity.UniqueUIDs(uids) { if uid == ownerID { continue } if err := s.activeUser(ctx, uid); err != nil { return nil, err } if err := s.requireFriend(ctx, ownerID, uid); err != nil { return nil, err } out = append(out, uid) } if len(out) == 0 { return nil, errs.ErrInvalidArgument.WithMsg(i18n.T(lang, i18n.KeyGroupMembersRequired, nil)) } if 1+len(out) > entity.MaxMembers { return nil, errs.ErrInvalidArgument.WithMsg(i18n.T(lang, i18n.KeyGroupFull, nil)) } return out, nil } func ownerAndInvitees(groupID, ownerID int64, invitees []int64, now int64) []*entity.Member { members := []*entity.Member{{GroupID: groupID, UserID: ownerID, Role: entity.RoleOwner, JoinedAtMS: now}} for _, uid := range invitees { members = append(members, &entity.Member{GroupID: groupID, UserID: uid, Role: entity.RoleMember, JoinedAtMS: now}) } return members }