feat: enforce tenant isolation and shared question bank

This commit is contained in:
2026-07-27 16:59:12 +08:00
parent 28e9a9fa41
commit db4c7b4496
137 changed files with 6402 additions and 112274 deletions

View File

@@ -6,6 +6,7 @@ using System.Text.Json;
using Tiku.Api.Contracts;
using Tiku.Application.Commerce;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
namespace Tiku.Api.Controllers;
@@ -16,7 +17,9 @@ namespace Tiku.Api.Controllers;
public sealed class CommerceController(
ICommerceService commerceService,
ICurrentUser currentUser,
ICurrentTenant currentTenant) : ControllerBase
ITenantContext currentTenant,
ITenantContextInitializer tenantInitializer,
ITenantDirectory tenantDirectory) : ControllerBase
{
[HttpPost("orders")]
[EndpointSummary("创建学生端订单")]
@@ -125,10 +128,13 @@ public sealed class CommerceController(
[EndpointSummary("微信支付回调")]
[ProducesResponseType<PaymentNotificationProcessResult>(StatusCodes.Status200OK)]
public async Task<ActionResult<PaymentNotificationProcessResult>> WechatPayNotify(
[FromQuery] Guid tenantId,
[FromQuery] string? tenantCode,
CancellationToken cancellationToken)
{
return Ok(await ProcessNotificationAsync(tenantId, PaymentProviders.WechatPay, cancellationToken));
return Ok(await ProcessNotificationAsync(
await ResolveNotificationTenantAsync(tenantCode, cancellationToken),
PaymentProviders.WechatPay,
cancellationToken));
}
[AllowAnonymous]
@@ -136,10 +142,33 @@ public sealed class CommerceController(
[EndpointSummary("支付宝支付回调")]
[ProducesResponseType<PaymentNotificationProcessResult>(StatusCodes.Status200OK)]
public async Task<ActionResult<PaymentNotificationProcessResult>> AlipayNotify(
[FromQuery] Guid tenantId,
[FromQuery] string? tenantCode,
CancellationToken cancellationToken)
{
return Ok(await ProcessNotificationAsync(tenantId, PaymentProviders.Alipay, cancellationToken));
return Ok(await ProcessNotificationAsync(
await ResolveNotificationTenantAsync(tenantCode, cancellationToken),
PaymentProviders.Alipay,
cancellationToken));
}
private async Task<Guid> ResolveNotificationTenantAsync(
string? tenantCode,
CancellationToken cancellationToken)
{
if (currentTenant.TenantId.HasValue)
{
return currentTenant.TenantId.Value;
}
if (string.IsNullOrWhiteSpace(tenantCode))
{
throw new CommerceException("Tenant code is required for payment notification.", "tenant_required");
}
var tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), cancellationToken)
?? throw new CommerceException("Tenant was not found.", "tenant_not_found");
tenantInitializer.Initialize(tenant.TenantId, tenant.TenantCode, TenantResolutionSource.TenantCode);
return tenant.TenantId;
}
private CommerceActor ResolveActor()