Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
82 lines
2.4 KiB
Go
82 lines
2.4 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 UpdateMemberInput struct {
|
|
Role *int `json:"role" validate:"omitempty,oneof=2 3" label:"field.role" errmsg:"oneof:validate.group.role_invalid"`
|
|
SpeakMuted *bool `json:"speak_muted"`
|
|
Nickname *string `json:"nickname" validate:"omitempty,max=32" label:"field.group_nickname" errmsg:"max:validate.group.nickname_invalid"`
|
|
}
|
|
|
|
func (s *Service) UpdateMember(ctx context.Context, actorID, groupID, targetID int64, in *UpdateMemberInput) (*MemberView, error) {
|
|
if !s.ready() || actorID <= 0 {
|
|
return nil, errs.ErrUnauthorized
|
|
}
|
|
if in == nil || targetID <= 0 {
|
|
return nil, errs.ErrInvalidArgument
|
|
}
|
|
if err := validate.StructLang(reqctx.Lang(ctx), in); err != nil {
|
|
return nil, err
|
|
}
|
|
if in.Role == nil && in.SpeakMuted == nil && in.Nickname == nil {
|
|
return nil, errs.ErrInvalidArgument
|
|
}
|
|
g, err := s.loadActive(ctx, groupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
target, err := s.requireMember(ctx, groupID, targetID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.applyMemberPatch(ctx, g, actorID, target, in); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.repo.UpdateMember(ctx, target); err != nil {
|
|
return nil, err
|
|
}
|
|
u, _ := s.users.Get(ctx, target.UserID)
|
|
return memberViewOf(target, u), nil
|
|
}
|
|
|
|
func (s *Service) applyMemberPatch(ctx context.Context, g *entity.Group, actorID int64, target *entity.Member, in *UpdateMemberInput) error {
|
|
lang := reqctx.Lang(ctx)
|
|
self := actorID == target.UserID
|
|
if in.Nickname != nil {
|
|
if !self {
|
|
if _, err := s.requireManage(ctx, g.ID, actorID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
target.Nickname = strings.TrimSpace(*in.Nickname)
|
|
}
|
|
if in.Role != nil {
|
|
if err := s.requireOwner(ctx, g, actorID); err != nil {
|
|
return err
|
|
}
|
|
if self || (*in.Role != entity.RoleAdmin && *in.Role != entity.RoleMember) {
|
|
return errs.ErrForbidden.WithMsg(i18n.T(lang, i18n.KeyGroupRoleDenied, nil))
|
|
}
|
|
target.Role = *in.Role
|
|
}
|
|
if in.SpeakMuted != nil {
|
|
if _, err := s.requireManage(ctx, g.ID, actorID); err != nil {
|
|
return err
|
|
}
|
|
if target.Role == entity.RoleOwner {
|
|
return errs.ErrForbidden.WithMsg(i18n.T(lang, i18n.KeyGroupRoleDenied, nil))
|
|
}
|
|
target.SpeakMuted = *in.SpeakMuted
|
|
}
|
|
return nil
|
|
}
|