fix(user): make agency invitations retry-safe
This commit is contained in:
parent
c9d8c75d59
commit
1c45fdc65b
@ -314,6 +314,20 @@ func (r *Repository) CreateAgency(ctx context.Context, command hostservice.Creat
|
||||
} else if ok {
|
||||
return hostdomain.CreateAgencyResult{}, xerr.New(xerr.Conflict, "owner already has active agency")
|
||||
}
|
||||
// 后台直建 Agency 是对角色归属的最终确认;目标先前收到的 Agency 邀请已经不可再接受。
|
||||
// 必须在同一事务关闭 pending 消息,否则消息中心会长期展示一个点击后必然冲突的旧邀请。
|
||||
if pending, ok, err := queryPendingRoleInvitationByTarget(ctx, tx, hostdomain.InvitationTypeAgency, command.OwnerUserID, true); err != nil {
|
||||
return hostdomain.CreateAgencyResult{}, err
|
||||
} else if ok {
|
||||
pending.Status = hostdomain.InvitationStatusCancelled
|
||||
pending.ProcessedByUserID = command.AdminUserID
|
||||
pending.ProcessReason = "superseded by admin agency creation"
|
||||
pending.ProcessedAtMs = command.NowMs
|
||||
pending.UpdatedAtMs = command.NowMs
|
||||
if err := updateRoleInvitationProcessed(ctx, tx, pending); err != nil {
|
||||
return hostdomain.CreateAgencyResult{}, err
|
||||
}
|
||||
}
|
||||
activeMembership, hasActiveMembership, err := queryActiveMembershipByHost(ctx, tx, command.OwnerUserID, true)
|
||||
if err != nil {
|
||||
return hostdomain.CreateAgencyResult{}, err
|
||||
|
||||
@ -54,6 +54,28 @@ func (r *Repository) InviteAgency(ctx context.Context, command hostservice.Invit
|
||||
} else if ok {
|
||||
return hostdomain.RoleInvitation{}, xerr.New(xerr.Conflict, "target already owns active agency")
|
||||
}
|
||||
// H5 在首个响应丢失、弹窗重新打开等情况下可能为同一目标生成新的 command_id。
|
||||
// 同一 BD 的 pending 邀请属于同一业务动作,记录新命令到既有结果并按成功回放;其他 BD 仍不能覆盖目标已收到的邀请。
|
||||
if pending, ok, err := queryPendingRoleInvitationByTarget(ctx, tx, hostdomain.InvitationTypeAgency, command.TargetUserID, true); err != nil {
|
||||
return hostdomain.RoleInvitation{}, err
|
||||
} else if ok {
|
||||
if pending.InviterBDUserID != inviter.UserID {
|
||||
return hostdomain.RoleInvitation{}, xerr.New(xerr.Conflict, "target already has pending agency invitation")
|
||||
}
|
||||
if err := insertCommandResult(ctx, tx, hostdomain.CommandResult{
|
||||
CommandID: command.CommandID,
|
||||
CommandType: hostdomain.CommandTypeInviteAgency,
|
||||
ResultType: hostdomain.ResultTypeInvitation,
|
||||
ResultID: pending.InvitationID,
|
||||
CreatedAtMs: command.NowMs,
|
||||
}); err != nil {
|
||||
return hostdomain.RoleInvitation{}, mapHostDuplicateError(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return hostdomain.RoleInvitation{}, err
|
||||
}
|
||||
return pending, nil
|
||||
}
|
||||
|
||||
// BD 发出的 Agency 邀请要记录直接上级 BD 和负责人链路;
|
||||
// 如果本次以负责人身份邀请,则直接 BD 和负责人都指向 Leader 自己。
|
||||
|
||||
@ -602,6 +602,23 @@ func (r *Repository) ListRoleInvitations(ctx context.Context, command hostservic
|
||||
return queryRoleInvitations(ctx, r.db, query, args...)
|
||||
}
|
||||
|
||||
// queryPendingRoleInvitationByTarget 通过 pending_target_key 唯一索引读取目标当前邀请。
|
||||
// 邀请创建事务传入 forUpdate=true,把“检查 pending + 创建/回放邀请”串行化,避免连续点击用不同 command_id 撞成泛化 409。
|
||||
func queryPendingRoleInvitationByTarget(ctx context.Context, q sqlQueryer, invitationType string, targetUserID int64, forUpdate bool) (hostdomain.RoleInvitation, bool, error) {
|
||||
query := fmt.Sprintf(`SELECT %s FROM role_invitations WHERE app_code = ? AND pending_target_key = ?`, roleInvitationColumns)
|
||||
if forUpdate {
|
||||
query += " FOR UPDATE"
|
||||
}
|
||||
invitation, err := scanRoleInvitation(q.QueryRowContext(ctx, query, appcode.FromContext(ctx), fmt.Sprintf("%s:%d", invitationType, targetUserID)))
|
||||
if err == sql.ErrNoRows {
|
||||
return hostdomain.RoleInvitation{}, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return hostdomain.RoleInvitation{}, false, err
|
||||
}
|
||||
return invitation, true, nil
|
||||
}
|
||||
|
||||
// ListAgencyMembers 读取 Agency 成员关系列表。
|
||||
func (r *Repository) ListAgencyMembers(ctx context.Context, agencyID int64, status string) ([]hostdomain.AgencyMembership, error) {
|
||||
query := fmt.Sprintf(`SELECT %s FROM agency_memberships WHERE app_code = ? AND agency_id = ?`, agencyMembershipColumns)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user