Files
tiku-backend.net/Tiku.Api/Controllers/PlatformAdminController.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

358 lines
16 KiB
C#

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Tiku.Api.Contracts;
using Tiku.Api.OpenApi;
using Tiku.Application.Auth;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Domain.Platform;
namespace Tiku.Api.Controllers;
[ApiController]
[Tags("平台端-平台管理")]
[Authorize(Policy = BackendPermissions.PlatformDashboardView)]
[Produces("application/json")]
[Route("api/platform")]
public sealed class PlatformAdminController(
IPlatformAdminService platformAdminService,
IPlatformApprovalService approvalService,
IAuthAdministrationService authAdministrationService,
ICurrentUser currentUser) : ControllerBase
{
[HttpGet("overview")]
[EndpointSummary("查询平台经营概览")]
[ProducesResponseType<PlatformOverview>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformOverview>> Overview(CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetOverviewAsync(ResolveActor(), cancellationToken));
}
[HttpGet("tenants")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("查询平台租户列表")]
[ProducesResponseType<PlatformTenantList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformTenantList>> Tenants(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetTenantsAsync(ResolveActor(), query.ToQuery(), cancellationToken));
}
[HttpPost("tenants")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("创建平台租户")]
[ProducesResponseType<PlatformTenantProvisioningResult>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformTenantProvisioningResult>> CreateTenant(
CreatePlatformTenantDto request,
[FromHeader(Name = "Idempotency-Key")] [Required]
string idempotencyKey,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.CreateTenantAsync(
ResolveActor(),
request.ToCommand(idempotencyKey),
cancellationToken));
}
[HttpPut("tenants/{tenantId:guid}/primary-domain")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("更正租户主域名")]
public Task<PlatformTenantDomainItem> ReplacePrimaryDomain(
Guid tenantId,
ReplacePlatformPrimaryDomainDto request,
CancellationToken cancellationToken)
{
return platformAdminService.ReplacePrimaryDomainAsync(ResolveActor(), request.ToCommand(tenantId),
cancellationToken);
}
[HttpPost("tenants/{tenantId:guid}/owner-activation-links")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("一次性领取租户 Owner 激活链接")]
[EndpointDescription("仅在主域名和试用/订阅有效时签发;幂等重放不会再次返回明文链接。")]
public Task<PlatformOwnerActivationLinkResult> IssueOwnerActivationLink(
Guid tenantId,
IssuePlatformOwnerActivationLinkDto request,
[FromHeader(Name = "Idempotency-Key")] [Required]
string idempotencyKey,
CancellationToken cancellationToken)
{
return platformAdminService.IssueOwnerActivationLinkAsync(
ResolveActor(), request.ToCommand(tenantId, idempotencyKey), cancellationToken);
}
[HttpGet("tenants/{tenantId:guid}/billing-policy")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("查询租户收款策略")]
public Task<TenantBillingPolicyItem> GetBillingPolicy(Guid tenantId, CancellationToken cancellationToken)
{
return platformAdminService.GetTenantBillingPolicyAsync(ResolveActor(), tenantId, cancellationToken);
}
[HttpPut("tenants/{tenantId:guid}/billing-policy")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("更新租户收款策略")]
public Task<TenantBillingPolicyItem> UpsertBillingPolicy(
Guid tenantId,
UpsertTenantBillingPolicyDto request,
CancellationToken cancellationToken)
{
return platformAdminService.UpsertTenantBillingPolicyAsync(ResolveActor(), request.ToCommand(tenantId),
cancellationToken);
}
[HttpGet("tenants/detail")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("查询平台租户详情")]
[ProducesResponseType<PlatformTenantDetail>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformTenantDetail>> TenantDetail(
[FromQuery] Guid tenantId,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetTenantDetailAsync(ResolveActor(), tenantId, cancellationToken));
}
[HttpPatch("tenants/status")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("更新租户业务状态")]
[PlatformOperationRisk("critical", PlatformApprovalPolicyCodes.TenantArchive)]
[ProducesResponseType<PlatformCommandSubmission>(StatusCodes.Status200OK)]
[ProducesResponseType<PlatformCommandSubmission>(StatusCodes.Status202Accepted)]
public async Task<ActionResult<PlatformCommandSubmission>> TenantStatus(
UpdatePlatformTenantStatusDto request,
[FromHeader(Name = "Idempotency-Key")] [Required]
string idempotencyKey,
CancellationToken cancellationToken)
{
var result = await approvalService.UpdateTenantStatusAsync(ResolveActor(), request.ToCommand(), idempotencyKey,
cancellationToken);
return result.ExecutionStatus == "pending_approval" ? Accepted(result) : Ok(result);
}
[HttpPut("tenants/billing-profile")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("保存租户账务与开票资料")]
[ProducesResponseType<TenantBillingProfileItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<TenantBillingProfileItem>> BillingProfile(
UpsertPlatformTenantBillingProfileDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.UpsertTenantBillingProfileAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpGet("domains")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("查询租户域名状态")]
[ProducesResponseType<PlatformDomainList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformDomainList>> Domains(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetDomainsAsync(ResolveActor(), query.ToQuery(), cancellationToken));
}
[HttpPost("domains/{domainId:guid}/recheck")]
[Authorize(Policy = BackendPermissions.PlatformTenantManage)]
[EndpointSummary("重新触发租户域名 DNS/TLS 验证")]
[ProducesResponseType<PlatformDomainRecheckResult>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformDomainRecheckResult>> RecheckDomain(
Guid domainId,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.RecheckDomainAsync(ResolveActor(), domainId, cancellationToken));
}
[HttpGet("staff")]
[Authorize(Policy = BackendPermissions.PlatformStaffManage)]
[EndpointSummary("查询平台员工列表")]
[ProducesResponseType<PlatformStaffList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformStaffList>> Staff(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetStaffAsync(ResolveActor(), query.ToQuery(), cancellationToken));
}
[HttpPut("staff")]
[Authorize(Policy = BackendPermissions.PlatformStaffManage)]
[EndpointSummary("创建或更新平台员工")]
[ProducesResponseType<PlatformStaffItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformStaffItem>> UpsertStaff(
UpsertPlatformStaffDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.UpsertStaffAsync(ResolveActor(), request.ToCommand(), cancellationToken));
}
[HttpPatch("staff/status")]
[Authorize(Policy = BackendPermissions.PlatformStaffManage)]
[EndpointSummary("启用或禁用平台员工")]
[ProducesResponseType<PlatformStaffItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformStaffItem>> StaffStatus(
UpdatePlatformStaffStatusDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.UpdateStaffStatusAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpPost("staff/{userId:guid}/password-reset")]
[Authorize(Policy = BackendPermissions.PlatformStaffManage)]
[EndpointSummary("为平台员工设置一次性临时密码")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> ResetStaffPassword(
Guid userId,
AdministrativePasswordResetDto request,
CancellationToken cancellationToken)
{
var actor = ResolveActor();
await authAdministrationService.ResetPasswordAsync(
new AdministrativePasswordResetRequest(
actor.UserId,
userId,
null,
request.TemporaryPassword,
request.Reason),
cancellationToken);
return NoContent();
}
[HttpGet("audit-logs")]
[Authorize(Policy = BackendPermissions.PlatformAuditView)]
[EndpointSummary("查询平台审计日志")]
[ProducesResponseType<PlatformAuditLogList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformAuditLogList>> AuditLogs(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetAuditLogsAsync(ResolveActor(), query.ToQuery(), cancellationToken));
}
[HttpGet("audit-alerts")]
[Authorize(Policy = BackendPermissions.PlatformAuditView)]
[EndpointSummary("查询平台审计告警")]
[ProducesResponseType<PlatformAuditAlertList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformAuditAlertList>> AuditAlerts(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetAuditAlertsAsync(ResolveActor(), query.ToQuery(), cancellationToken));
}
[HttpPost("audit-alerts/status")]
[Authorize(Policy = BackendPermissions.PlatformAuditView)]
[EndpointSummary("更新平台审计告警状态")]
[ProducesResponseType<PlatformAuditAlert>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformAuditAlert>> AuditAlertStatus(
UpdatePlatformAuditAlertStatusDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.UpdateAuditAlertStatusAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpGet("saas/dunning/channels")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("查询平台催缴通知渠道")]
[ProducesResponseType<PlatformBillingDunningChannelList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformBillingDunningChannelList>> BillingDunningChannels(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetBillingDunningChannelsAsync(ResolveActor(), query.ToQuery(),
cancellationToken));
}
[HttpPut("saas/dunning/channels")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("创建或更新平台催缴通知渠道")]
[ProducesResponseType<PlatformBillingDunningChannelItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformBillingDunningChannelItem>> UpsertBillingDunningChannel(
UpsertPlatformBillingDunningChannelDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.UpsertBillingDunningChannelAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpPost("saas/dunning/channels/disable")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("禁用平台催缴通知渠道")]
[ProducesResponseType<PlatformBillingDunningChannelItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformBillingDunningChannelItem>> DisableBillingDunningChannel(
DisablePlatformBillingDunningChannelDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.DisableBillingDunningChannelAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpGet("saas/dunning/events")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("查询平台催缴通知事件")]
[ProducesResponseType<PlatformBillingDunningEventList>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformBillingDunningEventList>> BillingDunningEvents(
[FromQuery] PlatformAdminQueryDto query,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetBillingDunningEventsAsync(ResolveActor(), query.ToQuery(),
cancellationToken));
}
[HttpGet("saas/dunning/events/detail")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("查询平台催缴通知事件详情")]
[ProducesResponseType<PlatformBillingDunningEventItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformBillingDunningEventItem>> BillingDunningEventDetail(
[FromQuery] Guid eventId,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.GetBillingDunningEventDetailAsync(ResolveActor(), eventId,
cancellationToken));
}
[HttpPost("saas/dunning/events/retry")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("重新标记平台催缴通知事件待发送")]
[ProducesResponseType<PlatformBillingDunningEventItem>(StatusCodes.Status200OK)]
public async Task<ActionResult<PlatformBillingDunningEventItem>> RetryBillingDunningEvent(
RetryPlatformBillingDunningEventDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.RetryBillingDunningEventAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpPost("saas/dunning/events/acknowledge")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("人工确认平台催缴通知事件")]
public async Task<ActionResult<PlatformBillingDunningEventItem>> AcknowledgeBillingDunningEvent(
ResolvePlatformBillingDunningEventDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.AcknowledgeBillingDunningEventAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
[HttpPost("saas/dunning/events/ignore")]
[Authorize(Policy = BackendPermissions.PlatformBillingNotification)]
[EndpointSummary("人工忽略平台催缴通知事件")]
public async Task<ActionResult<PlatformBillingDunningEventItem>> IgnoreBillingDunningEvent(
ResolvePlatformBillingDunningEventDto request,
CancellationToken cancellationToken)
{
return Ok(await platformAdminService.IgnoreBillingDunningEventAsync(ResolveActor(), request.ToCommand(),
cancellationToken));
}
private PlatformAdminActor ResolveActor()
{
if (currentUser.UserId is not { } userId)
throw new PlatformAdminException("Platform admin actor was not resolved.", "platform_access_denied");
return new PlatformAdminActor(userId);
}
}