Files
tiku-backend.net/Tiku.Api/Controllers/ReferralController.cs
xiong 4bea745b79 feat: Add tags and endpoint summaries to various controllers for better API documentation
- Added tags to BrowserAuthController for browser authentication endpoints.
- Added tags to CatalogController for public catalog access.
- Added tags to CommerceController for student transaction operations.
- Added tags to CommissionController for tenant commission management.
- Added tags to CrmController for tenant CRM functionalities.
- Added tags to HealthController for system health checks.
- Added tags to LearningController for student learning resources.
- Added tags to MeController for current user information.
- Added tags to PlatformAdminController for platform management.
- Introduced PlatformBackofficeController for backend permissions management.
- Added tags to PlatformBillingCallbackController for billing callbacks.
- Added tags to PlatformPaymentSettingsController for payment settings management.
- Added tags to PlatformSaasController for SaaS package management.
- Added tags to PlatformTenantCapabilitiesController for tenant capabilities.
- Added tags to PointsController for student points management.
- Added tags to ProfileController for student profile management.
- Added tags to QuestionVideosController for question video resources.
- Added tags to ReferralController for referral growth management.
- Added tags to RuntimeController for runtime configurations.
- Added tags to ScorelineController for scoreline management.
- Added tags to TaxonomyController for category management.
- Added tags to TenantAdminDirectController for tenant operations management.
- Introduced TenantBackofficeController for tenant backend permissions.
- Added tags to TenantBillingController for tenant billing operations.
- Added tags to TenantCommerceController for tenant commerce operations.
- Added tags to TenantContentController for tenant content management.
- Added tags to TenantContentDirectController for direct content management.
- Added tags to TenantFrontendConfigController for frontend configurations.
- Added tags to TenantOnboardingController for onboarding guidance.
- Added tags to TenantPublicController for public tenant configurations.
- Added tags to TenantsController for current tenant information.
- Added tags to VideosController for student video resources.
2026-07-29 16:16:27 +08:00

243 lines
9.2 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.Application.Tenancy;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
namespace Tiku.Api.Controllers;
[ApiController]
[Tags("学生端-推荐增长")]
[Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.ReferralCommission)]
[Produces("application/json")]
[Route("api/referral")]
public sealed class ReferralController(
IReferralService referralService,
ICurrentUser currentUser,
ITenantContext currentTenant,
ITenantContextInitializer tenantInitializer,
ITenantDirectory tenantDirectory) : 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));
}
[HttpGet("stats")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("查询推荐人个人统计")]
[ProducesResponseType<ReferralStatsItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralStatsItem>> Stats(
[FromQuery] ReferralStatsQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetStatsAsync(
ResolveAdminActor(),
query.ToQuery(),
cancellationToken));
}
[HttpGet("sales-stats")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("查询销售推荐统计排行")]
[ProducesResponseType<ReferralList<ReferralStatsItem>>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralList<ReferralStatsItem>>> SalesStats(
[FromQuery] ReferralStatsQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetSalesStatsAsync(
ResolveAdminActor(),
query.ToQuery(),
cancellationToken));
}
[HttpGet("conversion-report")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("查询推荐转化报告")]
[ProducesResponseType<ReferralConversionReport>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralConversionReport>> ConversionReport(
[FromQuery] ReferralConversionQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetConversionReportAsync(
ResolveAdminActor(),
query.ToQuery(),
cancellationToken));
}
[HttpGet("sales-clients")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("查询推荐人名下客户")]
[ProducesResponseType<ReferralList<ReferralLeadItem>>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralList<ReferralLeadItem>>> SalesClients(
[FromQuery] ReferralStatsQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetClientsAsync(
ResolveAdminActor(),
query.ToQuery(),
cancellationToken));
}
[HttpPost("manual-bind")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("人工调整学生推荐归属")]
[ProducesResponseType<ReferralBindResult>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralBindResult>> ManualBind(
ManualBindReferralDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.ManualBindAsync(
ResolveAdminActor(),
request.ToCommand(),
cancellationToken));
}
[HttpGet("team")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("查询推荐团队成员")]
[ProducesResponseType<ReferralList<ReferralTeamItem>>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralList<ReferralTeamItem>>> Team(
[FromQuery] ReferralTeamQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await referralService.GetTeamAsync(
ResolveAdminActor(),
query.ToQuery(),
cancellationToken));
}
[HttpPut("team")]
[Tags("租户端-推荐增长")]
[Authorize(Policy = BackendPermissions.TenantCrmManage)]
[EndpointSummary("新增或更新推荐团队关系")]
[ProducesResponseType<ReferralTeamItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<ReferralTeamItem>> UpsertTeam(
UpsertReferralTeamDto request,
CancellationToken cancellationToken)
{
return Ok(await referralService.UpsertTeamAsync(
ResolveAdminActor(),
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 ReferralAdminActor ResolveAdminActor()
{
if (currentTenant.TenantId is null || currentUser.UserId is null)
{
throw new ReferralException("Referral admin actor was not resolved.", "referral_access_denied");
}
return new ReferralAdminActor(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 tenant = await tenantDirectory.FindByCodeAsync(resolvedTenantCode.Trim(), cancellationToken)
?? throw new TenantNotFoundException();
tenantInitializer.Initialize(tenant.TenantId, tenant.TenantCode, TenantResolutionSource.TenantCode);
return tenant.TenantId;
}
}