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(StatusCodes.Status200OK)] public async Task> InviteCode( ReferralInviteDto request, CancellationToken cancellationToken) { return Ok(await referralService.GetOrCreateInviteCodeAsync( ResolveUserActor(), request.ToCommand(), cancellationToken)); } [HttpPost("resolve")] [AllowAnonymous] [EndpointSummary("解析推荐邀请码")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> 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(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> 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(StatusCodes.Status200OK)] public async Task> Bind( BindReferralDto request, CancellationToken cancellationToken) { return Ok(await referralService.BindAsync( ResolveUserActor(), request.ToCommand(), cancellationToken)); } [HttpPost("qrcode")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [EndpointSummary("生成或查询推广二维码")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> 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 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(); } }