130 lines
4.4 KiB
Go
130 lines
4.4 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
member "gateway/internal/model/member/domain"
|
|
"gateway/internal/model/member/domain/entity"
|
|
"gateway/internal/model/member/domain/enum"
|
|
domrepo "gateway/internal/model/member/domain/repository"
|
|
domusecase "gateway/internal/model/member/domain/usecase"
|
|
)
|
|
|
|
type lifecycleUseCase struct {
|
|
members domrepo.MemberRepository
|
|
tenants domrepo.TenantRepository
|
|
uidGen domrepo.UIDGenerator
|
|
}
|
|
|
|
// LifecycleUseCaseParam wires LifecycleUseCase.
|
|
type LifecycleUseCaseParam struct {
|
|
Members domrepo.MemberRepository
|
|
Tenants domrepo.TenantRepository
|
|
UIDGen domrepo.UIDGenerator
|
|
}
|
|
|
|
// MustLifecycleUseCase constructs LifecycleUseCase.
|
|
func MustLifecycleUseCase(param LifecycleUseCaseParam) domusecase.LifecycleUseCase {
|
|
if param.Members == nil || param.Tenants == nil || param.UIDGen == nil {
|
|
panic("member: lifecycle dependencies are required")
|
|
}
|
|
return &lifecycleUseCase{
|
|
members: param.Members,
|
|
tenants: param.Tenants,
|
|
uidGen: param.UIDGen,
|
|
}
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) CreateUnverified(ctx context.Context, req *domusecase.CreatePlatformMemberRequest) (*domusecase.MemberDTO, error) {
|
|
if req == nil || req.TenantID == "" || req.Email == "" {
|
|
return nil, errb.InputMissingRequired("tenant_id and email are required")
|
|
}
|
|
tenant, err := uc.tenants.GetByTenantID(ctx, req.TenantID)
|
|
if err != nil {
|
|
if errors.Is(err, member.ErrTenantNotFound) {
|
|
return nil, errb.ResNotFound("tenant", req.TenantID).WithCause(err)
|
|
}
|
|
return nil, wrapRepoErr(err, "read tenant failed")
|
|
}
|
|
uid, err := uc.uidGen.Next(ctx, req.TenantID, tenant.UIDPrefix)
|
|
if err != nil {
|
|
return nil, wrapRepoErr(err, "allocate uid failed")
|
|
}
|
|
now := time.Now().UTC().UnixMilli()
|
|
rec := &entity.Member{
|
|
TenantID: req.TenantID,
|
|
UID: uid,
|
|
ZitadelUserID: req.ZitadelUserID,
|
|
ZitadelEmail: req.Email,
|
|
DisplayName: req.DisplayName,
|
|
Language: defaultLanguage(req.Language),
|
|
Status: enum.MemberStatusUnverified,
|
|
Origin: enum.MemberOriginPlatformNative,
|
|
PasswordHash: req.PasswordHash,
|
|
CreateAt: now,
|
|
UpdateAt: now,
|
|
}
|
|
if err := uc.members.Insert(ctx, rec); err != nil {
|
|
if errors.Is(err, member.ErrDuplicateMember) {
|
|
return nil, errb.ResAlreadyExist("member already exists").WithCause(err)
|
|
}
|
|
return nil, wrapRepoErr(err, "create member failed")
|
|
}
|
|
return memberToDTO(rec), nil
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) Activate(ctx context.Context, tenantID, uid string) error {
|
|
return uc.transition(ctx, tenantID, uid, enum.MemberStatusUnverified, enum.MemberStatusActive, "")
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) Suspend(ctx context.Context, tenantID, uid, reason string) error {
|
|
return uc.transition(ctx, tenantID, uid, enum.MemberStatusActive, enum.MemberStatusSuspended, reason)
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) Reactivate(ctx context.Context, tenantID, uid string) error {
|
|
return uc.transition(ctx, tenantID, uid, enum.MemberStatusSuspended, enum.MemberStatusActive, "")
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) SoftDelete(ctx context.Context, tenantID, uid string) error {
|
|
rec, err := uc.members.GetByUID(ctx, tenantID, uid)
|
|
if err != nil {
|
|
if errors.Is(err, member.ErrNotFound) {
|
|
return errb.ResNotFound("member", uid).WithCause(err)
|
|
}
|
|
return wrapRepoErr(err, "read member failed")
|
|
}
|
|
if rec.Status == enum.MemberStatusDeleted {
|
|
return nil
|
|
}
|
|
if err := uc.members.UpdateStatus(ctx, tenantID, uid, enum.MemberStatusDeleted, ""); err != nil {
|
|
return wrapRepoErr(err, "soft delete member failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) AbortPending(ctx context.Context, tenantID, uid string) error {
|
|
return uc.transition(ctx, tenantID, uid, enum.MemberStatusUnverified, enum.MemberStatusDeleted, "")
|
|
}
|
|
|
|
func (uc *lifecycleUseCase) transition(ctx context.Context, tenantID, uid string, from, to enum.MemberStatus, reason string) error {
|
|
if tenantID == "" || uid == "" {
|
|
return errb.InputMissingRequired("tenant_id and uid are required")
|
|
}
|
|
rec, err := uc.members.GetByUID(ctx, tenantID, uid)
|
|
if err != nil {
|
|
if errors.Is(err, member.ErrNotFound) {
|
|
return errb.ResNotFound("member", uid).WithCause(err)
|
|
}
|
|
return wrapRepoErr(err, "read member failed")
|
|
}
|
|
if rec.Status != from {
|
|
return errb.ResInvalidState("invalid member status transition").WithCause(member.ErrInvalidStatus)
|
|
}
|
|
if err := uc.members.UpdateStatus(ctx, tenantID, uid, to, reason); err != nil {
|
|
return wrapRepoErr(err, "update member status failed")
|
|
}
|
|
return nil
|
|
}
|