112 lines
4.7 KiB
C#
112 lines
4.7 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Tiku.Application.Growth;
|
|
using Tiku.Domain.Common;
|
|
using Tiku.Domain.Growth;
|
|
|
|
namespace Tiku.Infrastructure.Growth;
|
|
|
|
public sealed partial class ReferralService
|
|
{
|
|
public async Task<ReferralBindResult> ManualBindAsync(
|
|
ReferralAdminActor actor,
|
|
ManualBindReferralCommand command,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
if (command.StudentUserId == command.ReferrerUserId)
|
|
throw new ReferralException("User cannot bind to own referral code.", "self_referral_not_allowed");
|
|
|
|
var refCode = await dbContext.ReferralCodes
|
|
.Where(item =>
|
|
item.TenantId == actor.TenantId &&
|
|
item.UserId == command.ReferrerUserId &&
|
|
item.Status == ReferralCodeStatus.Active)
|
|
.Select(item => item.Code)
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
if (refCode is null)
|
|
refCode = (await GetOrCreateInviteCodeAsync(
|
|
new ReferralActor(actor.TenantId, command.ReferrerUserId),
|
|
new ReferralInviteCommand("manual"),
|
|
cancellationToken)).InviteCode;
|
|
|
|
var before = await dbContext.ReferralLeads
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(item =>
|
|
item.TenantId == actor.TenantId &&
|
|
item.StudentUserId == command.StudentUserId,
|
|
cancellationToken);
|
|
var lead = await BindLeadCoreAsync(
|
|
actor.TenantId,
|
|
command.StudentUserId,
|
|
command.ReferrerUserId,
|
|
refCode,
|
|
NormalizeChoice(command.Source, AllowedSources, "manual", "invalid_referral_source"),
|
|
ReferralLeadBindType.Manual,
|
|
command.Force,
|
|
command.Metadata,
|
|
cancellationToken);
|
|
lead.AssignedBy = actor.UserId;
|
|
lead.AssignedAt = DateTimeOffset.UtcNow;
|
|
var crmQueue = before?.ReferrerUserId == lead.ReferrerUserId
|
|
? null
|
|
: await EnqueueCrmIfEnabledAsync(actor.TenantId, lead, "referral.manual_bind", cancellationToken);
|
|
await dbContext.SaveChangesAsync(cancellationToken);
|
|
return new ReferralBindResult(ToLeadItem(lead, before?.ReferrerUserId != lead.ReferrerUserId),
|
|
ToQueuePreview(crmQueue));
|
|
}
|
|
|
|
public async Task<ReferralList<ReferralTeamItem>> GetTeamAsync(
|
|
ReferralAdminActor actor,
|
|
ReferralTeamQuery query,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
var edges = dbContext.ReferralTeamEdges
|
|
.AsNoTracking()
|
|
.Where(item => item.TenantId == actor.TenantId);
|
|
if (query.LeaderUserId.HasValue) edges = edges.Where(item => item.LeaderUserId == query.LeaderUserId.Value);
|
|
|
|
var items = await edges
|
|
.OrderBy(item => item.RelationType)
|
|
.ThenBy(item => item.MemberUserId)
|
|
.ToArrayAsync(cancellationToken);
|
|
return new ReferralList<ReferralTeamItem>(items.Select(ToTeamItem).ToArray());
|
|
}
|
|
|
|
public async Task<ReferralTeamItem> UpsertTeamAsync(
|
|
ReferralAdminActor actor,
|
|
UpsertReferralTeamCommand command,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await AssertAdminAsync(actor, cancellationToken);
|
|
await AssertActiveMemberAsync(actor.TenantId, command.MemberUserId, cancellationToken);
|
|
if (command.LeaderUserId.HasValue)
|
|
await AssertActiveMemberAsync(actor.TenantId, command.LeaderUserId.Value, cancellationToken);
|
|
|
|
var relationType = ParseEnum(command.RelationType, ReferralTeamRelationType.SalesTeam,
|
|
"invalid_referral_team_relation");
|
|
var status = ParseEnum(command.Status, ReferralTeamEdgeStatus.Active, "invalid_referral_team_status");
|
|
var edge = await dbContext.ReferralTeamEdges
|
|
.FirstOrDefaultAsync(item =>
|
|
item.TenantId == actor.TenantId &&
|
|
item.MemberUserId == command.MemberUserId &&
|
|
item.RelationType == relationType,
|
|
cancellationToken);
|
|
if (edge is null)
|
|
{
|
|
edge = new ReferralTeamEdge
|
|
{
|
|
TenantId = actor.TenantId,
|
|
MemberUserId = command.MemberUserId,
|
|
RelationType = relationType
|
|
};
|
|
dbContext.ReferralTeamEdges.Add(edge);
|
|
}
|
|
|
|
edge.LeaderUserId = command.LeaderUserId;
|
|
edge.Status = status;
|
|
edge.Metadata = command.Metadata ?? JsonDefaults.Object();
|
|
await dbContext.SaveChangesAsync(cancellationToken);
|
|
return ToTeamItem(edge);
|
|
}
|
|
} |