Files
tiku-backend.net/Tiku.Api/Controllers/ReferralController.cs
2026-07-26 20:18:09 +08:00

128 lines
4.6 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Tiku.Api.Contracts;
using Tiku.Application.Growth;
using Tiku.Application.Security;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
namespace Tiku.Api.Controllers;
[ApiController]
[Produces("application/json")]
[Route("api/referral")]
public sealed class ReferralController(
IReferralService referralService,
ICurrentUser currentUser,
ICurrentTenant currentTenant,
TikuDbContext dbContext) : ControllerBase
{
[HttpPost("invite-code")]
[Authorize(Policy = TikuPolicies.CurrentTenantMember)]
[EndpointSummary("生成或查询当前成员邀请码")]
[ProducesResponseType<ReferralInviteItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralInviteItem>> InviteCode(
ReferralInviteDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetOrCreateInviteCodeAsync(
ResolveUserActor(),
request.ToCommand(),
cancellationToken));
}
[HttpPost("resolve")]
[AllowAnonymous]
[EndpointSummary("解析推荐邀请码")]
[ProducesResponseType<ReferralResolutionItem>(StatusCodes.Status200OK)]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ReferralResolutionItem>> Resolve(
ResolveReferralDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.ResolveAsync(
new ReferralActor(await ResolveTenantIdAsync(request.TenantCode, cancellationToken), currentUser.UserId),
request.ToCommand(),
cancellationToken));
}
[HttpPost("track-event")]
[AllowAnonymous]
[EndpointSummary("记录推荐行为")]
[ProducesResponseType<ReferralTrackResult>(StatusCodes.Status200OK)]
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ReferralTrackResult>> TrackEvent(
TrackReferralEventDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.TrackEventAsync(
new ReferralActor(await ResolveTenantIdAsync(request.TenantCode, cancellationToken), currentUser.UserId),
request.ToCommand(),
HttpContext.Connection.RemoteIpAddress?.ToString(),
Request.Headers.UserAgent.FirstOrDefault(),
cancellationToken));
}
[HttpPost("bind")]
[Authorize(Policy = TikuPolicies.CurrentTenantMember)]
[EndpointSummary("绑定当前用户推荐归属")]
[ProducesResponseType<ReferralBindResult>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralBindResult>> Bind(
BindReferralDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.BindAsync(
ResolveUserActor(),
request.ToCommand(),
cancellationToken));
}
[HttpPost("qrcode")]
[Authorize(Policy = TikuPolicies.CurrentTenantMember)]
[EndpointSummary("生成或查询推广二维码")]
[ProducesResponseType<ReferralQrcodeItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralQrcodeItem>> Qrcode(
ReferralQrcodeDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetOrCreateQrcodeAsync(
ResolveUserActor(),
request.ToCommand(),
cancellationToken));
}
private ReferralActor ResolveUserActor()
{
if (currentTenant.TenantId is null || currentUser.UserId is null)
{
throw new ReferralException("Current referral actor was not resolved.", "referral_access_denied");
}
return new ReferralActor(currentTenant.TenantId.Value, currentUser.UserId.Value);
}
private async Task<Guid> ResolveTenantIdAsync(string? tenantCode, CancellationToken cancellationToken)
{
if (currentTenant.TenantId.HasValue)
{
return currentTenant.TenantId.Value;
}
var resolvedTenantCode = tenantCode ?? Request.Headers["x-tenant-code"].FirstOrDefault();
if (string.IsNullOrWhiteSpace(resolvedTenantCode))
{
throw new TenantNotFoundException();
}
var tenantId = await dbContext.Tenants
.Where(tenant =>
tenant.Slug == resolvedTenantCode.Trim() &&
tenant.Status == TenantStatus.Active)
.Select(tenant => (Guid?)tenant.Id)
.SingleOrDefaultAsync(cancellationToken);
return tenantId ?? throw new TenantNotFoundException();
}
}