433 lines
20 KiB
Go
433 lines
20 KiB
Go
package host
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
|
||
"hyapp/pkg/xerr"
|
||
hostdomain "hyapp/services/user-service/internal/domain/host"
|
||
hostservice "hyapp/services/user-service/internal/service/host"
|
||
)
|
||
|
||
// InviteAgency 创建 Agency 角色邀请,校验 inviter BD 身份和目标用户区域。
|
||
func (r *Repository) InviteAgency(ctx context.Context, command hostservice.InviteAgencyCommand) (hostdomain.RoleInvitation, error) {
|
||
tx, err := r.db.BeginTx(ctx, nil)
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
defer tx.Rollback()
|
||
|
||
// 邀请是可重试写命令;命中幂等结果时返回既有邀请,
|
||
// 不重新校验当前 BD/用户状态,保持“已提交结果可回放”的语义。
|
||
if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
} else if ok {
|
||
if err := requireCommandResult(existing, hostdomain.CommandTypeInviteAgency, hostdomain.ResultTypeInvitation); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
return queryRoleInvitation(ctx, tx, "WHERE invitation_id = ?", existing.ResultID)
|
||
}
|
||
|
||
// InviteAgency 没有显式角色字段;普通 BD 行优先承载 BD Center 邀请,普通 BD 不可用时再使用 Leader 行承载负责人直属邀请。
|
||
// 两种身份都用 FOR UPDATE 串行化停用/换区和邀请创建,避免旧权限在同一事务窗口内继续写入。
|
||
inviter, err := queryActiveAgencyInviterProfile(ctx, tx, command.InviterUserID)
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
// 目标用户还不是 Host/Agency 时没有领域表行,所以锁 users 行来串行化目标身份变更。
|
||
targetRegionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if targetRegionID != inviter.RegionID {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.PermissionDenied, "target region does not match inviter region")
|
||
}
|
||
// Agency 拥有者必须没有当前有效 Agency 归属;已有 Host 身份但未归属 Agency 时可以复用身份。
|
||
if _, ok, err := queryActiveMembershipByHost(ctx, tx, command.TargetUserID, true); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
} else if ok {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.Conflict, "target already has active agency membership")
|
||
}
|
||
// 一个用户只能拥有一个有效 Agency;这里先拦截,最终仍依赖唯一索引兜底并发。
|
||
if _, ok, err := queryActiveAgencyByOwner(ctx, tx, command.TargetUserID, true); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
} else if ok {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.Conflict, "target already owns active agency")
|
||
}
|
||
|
||
// BD 发出的 Agency 邀请要记录直接上级 BD 和负责人链路;
|
||
// 如果本次以负责人身份邀请,则直接 BD 和负责人都指向 Leader 自己。
|
||
parentLeaderUserID := inviter.ParentLeaderUserID
|
||
if inviter.Role == hostdomain.BDRoleLeader {
|
||
parentLeaderUserID = inviter.UserID
|
||
}
|
||
invitation := hostdomain.RoleInvitation{
|
||
InvitationID: command.InvitationID,
|
||
CommandID: command.CommandID,
|
||
InvitationType: hostdomain.InvitationTypeAgency,
|
||
Status: hostdomain.InvitationStatusAccepted,
|
||
InviterUserID: command.InviterUserID,
|
||
InviterBDUserID: inviter.UserID,
|
||
TargetUserID: command.TargetUserID,
|
||
RegionID: inviter.RegionID,
|
||
AgencyName: command.AgencyName,
|
||
ParentBDUserID: inviter.UserID,
|
||
ParentLeaderUserID: parentLeaderUserID,
|
||
ProcessedByUserID: command.InviterUserID,
|
||
ProcessReason: "direct_invite",
|
||
ProcessedAtMs: command.NowMs,
|
||
CreatedAtMs: command.NowMs,
|
||
UpdatedAtMs: command.NowMs,
|
||
}
|
||
// 新邀请不再等待目标用户确认:accepted invitation、Agency、owner Host 和 owner membership 必须同事务提交。
|
||
if err := insertRoleInvitation(ctx, tx, invitation); err != nil {
|
||
return hostdomain.RoleInvitation{}, mapHostDuplicateError(err)
|
||
}
|
||
result := hostdomain.ProcessRoleInvitationResult{Invitation: invitation}
|
||
hostProfileCreated, err := r.acceptAgencyInvitation(ctx, tx, hostservice.ProcessRoleInvitationCommand{
|
||
CommandID: command.CommandID,
|
||
ActorUserID: command.InviterUserID,
|
||
InvitationID: command.InvitationID,
|
||
Action: hostdomain.InvitationActionAccept,
|
||
Reason: "direct_invite",
|
||
AgencyID: command.AgencyID,
|
||
MembershipID: command.MembershipID,
|
||
EventID: command.EventID,
|
||
NowMs: command.NowMs,
|
||
}, &invitation, &result)
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if hostProfileCreated {
|
||
if err := insertHostOutbox(ctx, tx, command.EventID, "HostCreated", "host_profile", invitation.TargetUserID, command.NowMs); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
}
|
||
if err := insertHostOutbox(ctx, tx, command.EventID+1, "AgencyCreated", "agency", command.AgencyID, command.NowMs); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if err := insertHostOutbox(ctx, tx, command.EventID+2, "AgencyMembershipChanged", "agency_membership", command.MembershipID, command.NowMs); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{
|
||
CommandID: command.CommandID,
|
||
CommandType: hostdomain.CommandTypeInviteAgency,
|
||
ResultType: hostdomain.ResultTypeInvitation,
|
||
ResultID: invitation.InvitationID,
|
||
CreatedAtMs: command.NowMs,
|
||
}); err != nil {
|
||
return hostdomain.RoleInvitation{}, mapHostDuplicateError(err)
|
||
}
|
||
if err := tx.Commit(); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
|
||
return invitation, nil
|
||
}
|
||
|
||
// InviteBD 创建 BD Leader 到 BD 的邀请。
|
||
func (r *Repository) InviteBD(ctx context.Context, command hostservice.InviteBDCommand) (hostdomain.RoleInvitation, error) {
|
||
tx, err := r.db.BeginTx(ctx, nil)
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
defer tx.Rollback()
|
||
|
||
// BD 邀请也以 command_id 去重;已成功的邀请不会因为负责人后续状态变化而回放失败。
|
||
if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
} else if ok {
|
||
if err := requireCommandResult(existing, hostdomain.CommandTypeInviteBD, hostdomain.ResultTypeInvitation); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
return queryRoleInvitation(ctx, tx, "WHERE invitation_id = ?", existing.ResultID)
|
||
}
|
||
|
||
// 只有有效 BD 负责人能发展 BD;锁住独立 Leader 表行让停用和邀请创建串行。
|
||
inviter, err := queryBDLeaderProfile(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.InviterUserID)
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if inviter.Status != hostdomain.BDStatusActive {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.PermissionDenied, "inviter is not active bd leader")
|
||
}
|
||
// BD 从属于负责人的区域,邀请创建时先锁目标用户当前区域。
|
||
targetRegionID, err := r.userRegion(ctx, tx, command.TargetUserID, "FOR UPDATE")
|
||
if err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if targetRegionID != inviter.RegionID {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.PermissionDenied, "target region does not match leader region")
|
||
}
|
||
// 目标已有普通 BD 身份行时不再创建新邀请,即使已停用也不能再插入第二条普通 BD 事实。
|
||
// BD Leader 自邀也必须新增普通 BD 行,后续才能获得 BD_SALARY_USD 钱包鉴权。
|
||
if _, ok, err := queryBDProfileByUserMaybe(ctx, tx, command.TargetUserID, true); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
} else if ok {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.Conflict, "target already has bd profile")
|
||
}
|
||
|
||
invitation := hostdomain.RoleInvitation{
|
||
InvitationID: command.InvitationID,
|
||
CommandID: command.CommandID,
|
||
InvitationType: hostdomain.InvitationTypeBD,
|
||
Status: hostdomain.InvitationStatusAccepted,
|
||
InviterUserID: command.InviterUserID,
|
||
InviterBDUserID: inviter.UserID,
|
||
TargetUserID: command.TargetUserID,
|
||
RegionID: inviter.RegionID,
|
||
ParentLeaderUserID: inviter.UserID,
|
||
ProcessedByUserID: command.InviterUserID,
|
||
ProcessReason: "direct_invite",
|
||
ProcessedAtMs: command.NowMs,
|
||
CreatedAtMs: command.NowMs,
|
||
UpdatedAtMs: command.NowMs,
|
||
}
|
||
// 新 BD 邀请直接创建 BD 身份;accepted invitation 只作为审计事实和幂等回放结果。
|
||
if err := insertRoleInvitation(ctx, tx, invitation); err != nil {
|
||
return hostdomain.RoleInvitation{}, mapHostDuplicateError(err)
|
||
}
|
||
recordCommandResult := func() error {
|
||
return insertCommandResult(ctx, tx, hostdomain.CommandResult{
|
||
CommandID: command.CommandID,
|
||
CommandType: hostdomain.CommandTypeInviteBD,
|
||
ResultType: hostdomain.ResultTypeInvitation,
|
||
ResultID: invitation.InvitationID,
|
||
CreatedAtMs: command.NowMs,
|
||
})
|
||
}
|
||
result := hostdomain.ProcessRoleInvitationResult{Invitation: invitation}
|
||
if err := r.acceptBDInvitation(ctx, tx, hostservice.ProcessRoleInvitationCommand{
|
||
CommandID: command.CommandID,
|
||
ActorUserID: command.InviterUserID,
|
||
InvitationID: command.InvitationID,
|
||
Action: hostdomain.InvitationActionAccept,
|
||
Reason: "direct_invite",
|
||
EventID: command.EventID,
|
||
NowMs: command.NowMs,
|
||
}, &invitation, &result); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if err := insertHostOutbox(ctx, tx, command.EventID, "BDCreated", "bd_profile", invitation.TargetUserID, command.NowMs); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
if err := recordCommandResult(); err != nil {
|
||
return hostdomain.RoleInvitation{}, mapHostDuplicateError(err)
|
||
}
|
||
if err := tx.Commit(); err != nil {
|
||
return hostdomain.RoleInvitation{}, err
|
||
}
|
||
|
||
return invitation, nil
|
||
}
|
||
|
||
func queryActiveAgencyInviterProfile(ctx context.Context, tx *sql.Tx, userID int64) (hostdomain.BDProfile, error) {
|
||
if profile, ok, err := queryBDProfileMaybe(ctx, tx, userID, true); err != nil || ok {
|
||
return profile, err
|
||
}
|
||
if profile, ok, err := queryBDLeaderProfileMaybe(ctx, tx, userID, true); err != nil || ok {
|
||
return profile, err
|
||
}
|
||
return hostdomain.BDProfile{}, xerr.New(xerr.PermissionDenied, "inviter bd is not active")
|
||
}
|
||
|
||
// ProcessRoleInvitation 处理角色邀请,接受路径在同一事务内创建 Agency/BD 角色事实。
|
||
func (r *Repository) ProcessRoleInvitation(ctx context.Context, command hostservice.ProcessRoleInvitationCommand) (hostdomain.ProcessRoleInvitationResult, error) {
|
||
tx, err := r.db.BeginTx(ctx, nil)
|
||
if err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
defer tx.Rollback()
|
||
|
||
// 接受、拒绝、取消共用同一个处理命令类型;重试时通过 invitation_id 重建结果,
|
||
// 不重复创建 Agency、成员关系或 BD 身份行。
|
||
if existing, ok, err := commandResult(ctx, tx, command.CommandID); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
} else if ok {
|
||
if err := requireCommandResult(existing, hostdomain.CommandTypeProcessRoleInvitation, hostdomain.ResultTypeInvitation); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
return processResultByInvitationID(ctx, tx, existing.ResultID)
|
||
}
|
||
|
||
// 邀请行是邀请状态机的唯一锁;待处理状态只能被推进一次。
|
||
invitation, err := queryRoleInvitation(ctx, tx, "WHERE invitation_id = ? FOR UPDATE", command.InvitationID)
|
||
if err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
if invitation.Status != hostdomain.InvitationStatusPending {
|
||
return hostdomain.ProcessRoleInvitationResult{}, xerr.New(xerr.Conflict, "invitation is not pending")
|
||
}
|
||
// 取消属于发起人撤回;接受/拒绝属于目标用户处理。
|
||
// 这两个授权分支不要下沉到传输层,否则批处理或内部调用会绕过规则。
|
||
if command.Action == hostdomain.InvitationActionCancel && command.ActorUserID != invitation.InviterUserID {
|
||
return hostdomain.ProcessRoleInvitationResult{}, xerr.New(xerr.PermissionDenied, "only inviter can cancel invitation")
|
||
}
|
||
if command.Action != hostdomain.InvitationActionCancel && command.ActorUserID != invitation.TargetUserID {
|
||
return hostdomain.ProcessRoleInvitationResult{}, xerr.New(xerr.PermissionDenied, "only target can process invitation")
|
||
}
|
||
|
||
invitation.ProcessedByUserID = command.ActorUserID
|
||
invitation.ProcessReason = command.Reason
|
||
invitation.ProcessedAtMs = command.NowMs
|
||
invitation.UpdatedAtMs = command.NowMs
|
||
result := hostdomain.ProcessRoleInvitationResult{Invitation: invitation}
|
||
|
||
switch command.Action {
|
||
case hostdomain.InvitationActionReject:
|
||
// 拒绝只改变邀请终态,不创建任何角色事实。
|
||
invitation.Status = hostdomain.InvitationStatusRejected
|
||
case hostdomain.InvitationActionCancel:
|
||
// 取消同样只关闭邀请,用于邀请人在目标处理前撤回。
|
||
invitation.Status = hostdomain.InvitationStatusCancelled
|
||
case hostdomain.InvitationActionAccept:
|
||
invitation.Status = hostdomain.InvitationStatusAccepted
|
||
if invitation.InvitationType == hostdomain.InvitationTypeAgency {
|
||
// Agency 邀请的接受会一次性创建 Agency 拥有者身份、Agency 和拥有者成员关系。
|
||
hostProfileCreated, err := r.acceptAgencyInvitation(ctx, tx, command, &invitation, &result)
|
||
if err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
// 事件 ID 在业务层预分配;这里按固定偏移写多条事件箱记录,
|
||
// 让同一个接受事务中的派生事件保持可追踪顺序。
|
||
if hostProfileCreated {
|
||
if err := insertHostOutbox(ctx, tx, command.EventID, "HostCreated", "host_profile", invitation.TargetUserID, command.NowMs); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
}
|
||
if err := insertHostOutbox(ctx, tx, command.EventID+1, "AgencyCreated", "agency", command.AgencyID, command.NowMs); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
if err := insertHostOutbox(ctx, tx, command.EventID+2, "AgencyMembershipChanged", "agency_membership", command.MembershipID, command.NowMs); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
} else if invitation.InvitationType == hostdomain.InvitationTypeBD {
|
||
// BD 邀请的接受只创建 bd_profile;BD 不需要 host_profile。
|
||
if err := r.acceptBDInvitation(ctx, tx, command, &invitation, &result); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
if err := insertHostOutbox(ctx, tx, command.EventID, "BDCreated", "bd_profile", invitation.TargetUserID, command.NowMs); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
} else {
|
||
return hostdomain.ProcessRoleInvitationResult{}, xerr.New(xerr.InvalidArgument, "invitation type is invalid")
|
||
}
|
||
}
|
||
// 邀请终态必须在角色事实之后更新;如果事实创建失败,待处理状态保留给后续重试/处理。
|
||
if err := updateRoleInvitationProcessed(ctx, tx, invitation); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
result.Invitation = invitation
|
||
if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{
|
||
CommandID: command.CommandID,
|
||
CommandType: hostdomain.CommandTypeProcessRoleInvitation,
|
||
ResultType: hostdomain.ResultTypeInvitation,
|
||
ResultID: invitation.InvitationID,
|
||
CreatedAtMs: command.NowMs,
|
||
}); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, mapHostDuplicateError(err)
|
||
}
|
||
if err := tx.Commit(); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
func (r *Repository) acceptAgencyInvitation(ctx context.Context, tx *sql.Tx, command hostservice.ProcessRoleInvitationCommand, invitation *hostdomain.RoleInvitation, result *hostdomain.ProcessRoleInvitationResult) (bool, error) {
|
||
// 邀请可能放置一段时间后才被接受,所以必须用当前用户区域再校验一次。
|
||
regionID, err := r.userRegion(ctx, tx, invitation.TargetUserID, "FOR UPDATE")
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
if regionID != invitation.RegionID {
|
||
return false, xerr.New(xerr.PermissionDenied, "target region no longer matches invitation")
|
||
}
|
||
// 目标用户在邀请创建后可能已经加入其他 Agency;接受前必须再次阻断有效归属。
|
||
if _, ok, err := queryActiveMembershipByHost(ctx, tx, invitation.TargetUserID, true); err != nil {
|
||
return false, err
|
||
} else if ok {
|
||
return false, xerr.New(xerr.Conflict, "target already has active agency membership")
|
||
}
|
||
// 同一用户不能拥有多个有效 Agency;这里和唯一索引共同处理并发接受。
|
||
if _, ok, err := queryActiveAgencyByOwner(ctx, tx, invitation.TargetUserID, true); err != nil {
|
||
return false, err
|
||
} else if ok {
|
||
return false, xerr.New(xerr.Conflict, "target already owns active agency")
|
||
}
|
||
|
||
// Agency、拥有者 host_profile、拥有者成员关系是同一业务事实的三张表投影,
|
||
// 必须全部创建成功后邀请才能进入已接受状态。
|
||
agency := hostdomain.Agency{
|
||
AgencyID: command.AgencyID,
|
||
OwnerUserID: invitation.TargetUserID,
|
||
RegionID: invitation.RegionID,
|
||
ParentBDUserID: invitation.ParentBDUserID,
|
||
Name: invitation.AgencyName,
|
||
Status: hostdomain.AgencyStatusActive,
|
||
JoinEnabled: true,
|
||
// max_hosts 是历史兼容字段,当前业务不再限制主播数量。
|
||
MaxHosts: 0,
|
||
CreatedByUserID: invitation.InviterUserID,
|
||
CreatedAtMs: command.NowMs,
|
||
UpdatedAtMs: command.NowMs,
|
||
}
|
||
if err := insertAgency(ctx, tx, agency); err != nil {
|
||
return false, mapHostDuplicateError(err)
|
||
}
|
||
hostProfile, hostProfileCreated, err := ensureHostProfileForMembership(ctx, tx, invitation.TargetUserID, invitation.RegionID, command.MembershipID, agency.AgencyID, hostdomain.HostSourceAgencyInvitation, command.NowMs)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
membership := hostdomain.AgencyMembership{
|
||
MembershipID: command.MembershipID,
|
||
AgencyID: agency.AgencyID,
|
||
HostUserID: invitation.TargetUserID,
|
||
RegionID: invitation.RegionID,
|
||
MembershipType: hostdomain.MembershipTypeOwner,
|
||
Status: hostdomain.MembershipStatusActive,
|
||
JoinedAtMs: command.NowMs,
|
||
CreatedAtMs: command.NowMs,
|
||
UpdatedAtMs: command.NowMs,
|
||
}
|
||
if err := insertAgencyMembership(ctx, tx, membership); err != nil {
|
||
return false, mapHostDuplicateError(err)
|
||
}
|
||
result.HostProfile = hostProfile
|
||
result.Agency = agency
|
||
result.Membership = membership
|
||
return hostProfileCreated, nil
|
||
}
|
||
|
||
func (r *Repository) acceptBDInvitation(ctx context.Context, tx *sql.Tx, command hostservice.ProcessRoleInvitationCommand, invitation *hostdomain.RoleInvitation, result *hostdomain.ProcessRoleInvitationResult) error {
|
||
// BD 邀请同样以当前用户区域为准;跨区调整后不能接受旧区域邀请。
|
||
regionID, err := r.userRegion(ctx, tx, invitation.TargetUserID, "FOR UPDATE")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if regionID != invitation.RegionID {
|
||
return xerr.New(xerr.PermissionDenied, "target region no longer matches invitation")
|
||
}
|
||
// BD 身份是独立角色事实,不依赖 host_profile;任何已有普通 BD 行都不能重复插入。
|
||
if _, ok, err := queryBDProfileByUserMaybe(ctx, tx, invitation.TargetUserID, true); err != nil {
|
||
return err
|
||
} else if ok {
|
||
return xerr.New(xerr.Conflict, "target already has bd profile")
|
||
}
|
||
bd := hostdomain.BDProfile{
|
||
UserID: invitation.TargetUserID,
|
||
Role: hostdomain.BDRoleBD,
|
||
RegionID: invitation.RegionID,
|
||
ParentLeaderUserID: invitation.ParentLeaderUserID,
|
||
Status: hostdomain.BDStatusActive,
|
||
CreatedByUserID: invitation.InviterUserID,
|
||
CreatedAtMs: command.NowMs,
|
||
UpdatedAtMs: command.NowMs,
|
||
}
|
||
if err := insertBDProfile(ctx, tx, bd); err != nil {
|
||
return mapHostDuplicateError(err)
|
||
}
|
||
result.BDProfile = bd
|
||
return nil
|
||
}
|