spark-server/internal/module/group/logic/create.go
2026-09-18 14:50:41 +08:00

84 lines
2.8 KiB
Go

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.Member{UserID: ownerID, Role: 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
}