From 4bea745b79f76ea3de1e36e00944fc9c48ecdf4f Mon Sep 17 00:00:00 2001 From: xiong Date: Wed, 29 Jul 2026 16:16:27 +0800 Subject: [PATCH] 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. --- .../PlatformTenantCapabilitiesDtos.cs | 231 ++++++++++++++++++ Tiku.Api/Controllers/AssetsController.cs | 1 + Tiku.Api/Controllers/AuthController.cs | 1 + .../Controllers/BackgroundJobsController.cs | 1 + Tiku.Api/Controllers/BackofficeController.cs | 138 ----------- Tiku.Api/Controllers/BrowserAuthController.cs | 11 + Tiku.Api/Controllers/CatalogController.cs | 1 + Tiku.Api/Controllers/CommerceController.cs | 1 + Tiku.Api/Controllers/CommissionController.cs | 1 + Tiku.Api/Controllers/CrmController.cs | 1 + Tiku.Api/Controllers/HealthController.cs | 1 + Tiku.Api/Controllers/LearningController.cs | 1 + Tiku.Api/Controllers/MeController.cs | 1 + .../Controllers/PlatformAdminController.cs | 1 + .../PlatformBackofficeController.cs | 77 ++++++ .../PlatformBillingCallbackController.cs | 3 + .../PlatformPaymentSettingsController.cs | 8 + .../Controllers/PlatformSaasController.cs | 16 ++ .../PlatformTenantCapabilitiesController.cs | 19 ++ Tiku.Api/Controllers/PointsController.cs | 1 + Tiku.Api/Controllers/ProfileController.cs | 1 + .../Controllers/QuestionVideosController.cs | 1 + Tiku.Api/Controllers/ReferralController.cs | 8 + Tiku.Api/Controllers/RuntimeController.cs | 1 + Tiku.Api/Controllers/ScorelineController.cs | 1 + Tiku.Api/Controllers/TaxonomyController.cs | 1 + .../TenantAdminDirectController.cs | 1 + .../Controllers/TenantBackofficeController.cs | 77 ++++++ .../Controllers/TenantBillingController.cs | 13 + .../Controllers/TenantCommerceController.cs | 1 + .../Controllers/TenantContentController.cs | 1 + .../TenantContentDirectController.cs | 1 + .../TenantFrontendConfigController.cs | 1 + .../Controllers/TenantOnboardingController.cs | 2 + .../Controllers/TenantPublicController.cs | 1 + Tiku.Api/Controllers/TenantsController.cs | 1 + Tiku.Api/Controllers/VideosController.cs | 1 + 37 files changed, 490 insertions(+), 138 deletions(-) delete mode 100644 Tiku.Api/Controllers/BackofficeController.cs create mode 100644 Tiku.Api/Controllers/PlatformBackofficeController.cs create mode 100644 Tiku.Api/Controllers/TenantBackofficeController.cs diff --git a/Tiku.Api/Contracts/PlatformTenantCapabilitiesDtos.cs b/Tiku.Api/Contracts/PlatformTenantCapabilitiesDtos.cs index b4fd196..4e77481 100644 --- a/Tiku.Api/Contracts/PlatformTenantCapabilitiesDtos.cs +++ b/Tiku.Api/Contracts/PlatformTenantCapabilitiesDtos.cs @@ -8,166 +8,397 @@ using Tiku.Domain.Tenancy; namespace Tiku.Api.Contracts; +/// +/// 平台租户能力通用查询参数。 +/// public class PlatformCapabilityQueryDto { + /// + /// 租户 ID。 + /// public Guid? TenantId { get; set; } + /// + /// 状态。 + /// [StringLength(50)] public string? Status { get; set; } + /// + /// 返回数量上限。 + /// [Range(1, 500)] public int Limit { get; set; } = 100; public PlatformCapabilityQuery ToQuery() => new(TenantId, Status, Limit); } +/// +/// 新增或更新租户 CRM 配置请求。 +/// public sealed class UpsertPlatformCrmConfigDto { + /// + /// ID。 + /// public Guid? Id { get; set; } + /// + /// 租户 ID。 + /// [Required] public Guid TenantId { get; set; } + /// + /// 是否启用。 + /// public bool Enabled { get; set; } + /// + /// 回调地址。 + /// [StringLength(2048)] public string? Url { get; set; } + /// + /// 密钥引用。 + /// [StringLength(300)] public string? SecretRef { get; set; } + /// + /// 表单名称。 + /// [StringLength(200)] public string? FormName { get; set; } + /// + /// 考试类型。 + /// [StringLength(100)] public string? ExamType { get; set; } + /// + /// 超时时长,单位为秒。 + /// [Range(1, 120)] public int? TimeoutSeconds { get; set; } + /// + /// 延迟秒数。 + /// [Range(0, 86400)] public int? DelaySeconds { get; set; } + /// + /// 分配模式。 + /// [StringLength(50)] public string? AssignmentMode { get; set; } + /// + /// 分配池。 + /// public JsonElement? AssignmentPool { get; set; } + /// + /// 分配配置。 + /// public JsonElement? AssignmentConfig { get; set; } public UpsertPlatformCrmConfigCommand ToCommand() => new(Id, TenantId, Enabled, Url, SecretRef, FormName, ExamType, TimeoutSeconds, DelaySeconds, AssignmentMode, AssignmentPool, AssignmentConfig); } +/// +/// 重试租户 CRM 线索推送请求。 +/// public sealed class RetryPlatformCrmLeadDto { + /// + /// 队列任务 ID。 + /// [Required] public Guid QueueId { get; set; } + /// + /// 备注。 + /// [StringLength(500)] public string? Note { get; set; } public PlatformCrmLeadRetryCommand ToCommand() => new(QueueId, Note); } +/// +/// 平台租户 CRM 日志查询参数。 +/// public sealed class PlatformCrmLogQueryDto : PlatformCapabilityQueryDto { + /// + /// 队列任务 ID。 + /// public Guid? QueueId { get; set; } } +/// +/// 新增或更新租户短信渠道请求。 +/// public sealed class UpsertPlatformSmsChannelDto { + /// + /// ID。 + /// public Guid? Id { get; set; } + /// + /// 租户 ID。 + /// [Required] public Guid TenantId { get; set; } + /// + /// 服务提供方。 + /// [Required, StringLength(80)] public string Provider { get; set; } = "generic"; + /// + /// 名称。 + /// [Required, StringLength(200)] public string Name { get; set; } = string.Empty; + /// + /// 短信签名。 + /// [Required, StringLength(100)] public string Signature { get; set; } = string.Empty; + /// + /// 使用场景。 + /// [Required, StringLength(100)] public string Scene { get; set; } = "login"; + /// + /// 状态。 + /// public TenantExternalProviderStatus Status { get; set; } = TenantExternalProviderStatus.Disabled; + /// + /// 密钥引用。 + /// [StringLength(300)] public string? SecretRef { get; set; } + /// + /// 优先级。 + /// public int? Priority { get; set; } + /// + /// 月度配额。 + /// [Range(0, int.MaxValue)] public int? MonthlyQuota { get; set; } + /// + /// 公开配置内容。 + /// public JsonElement ConfigPublic { get; set; } = JsonDefaults.Object(); + /// + /// 扩展元数据。 + /// public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertPlatformSmsChannelCommand ToCommand() => new(Id, TenantId, Provider, Name, Signature, Scene, Status, SecretRef, Priority, MonthlyQuota, ConfigPublic, Metadata); } +/// +/// 新增或更新租户短信模板请求。 +/// public sealed class UpsertPlatformSmsTemplateDto { + /// + /// ID。 + /// public Guid? Id { get; set; } + /// + /// 租户 ID。 + /// [Required] public Guid TenantId { get; set; } + /// + /// 短信渠道 ID。 + /// [Required] public Guid ChannelId { get; set; } + /// + /// 编码。 + /// [Required, StringLength(120)] public string Code { get; set; } = string.Empty; + /// + /// 名称。 + /// [Required, StringLength(200)] public string Name { get; set; } = string.Empty; + /// + /// 类型。 + /// public SmsTemplateType Type { get; set; } = SmsTemplateType.Notification; + /// + /// 审核状态。 + /// public SmsTemplateAuditStatus AuditStatus { get; set; } = SmsTemplateAuditStatus.Draft; + /// + /// 状态。 + /// public SmsTemplateStatus Status { get; set; } = SmsTemplateStatus.Active; + /// + /// 渠道模板编码。 + /// [StringLength(120)] public string? ProviderTemplateCode { get; set; } + /// + /// 模板内容。 + /// [Required, StringLength(1000)] public string Content { get; set; } = string.Empty; + /// + /// Remark。 + /// [StringLength(500)] public string? Remark { get; set; } + /// + /// 扩展元数据。 + /// public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertPlatformSmsTemplateCommand ToCommand() => new(Id, TenantId, ChannelId, Code, Name, Type, AuditStatus, Status, ProviderTemplateCode, Content, Remark, Metadata); } +/// +/// 新增或更新平台支付应用请求。 +/// public sealed class UpsertPlatformPaymentAppDto { + /// + /// ID。 + /// public Guid? Id { get; set; } + /// + /// 支付应用编码。 + /// [Required, StringLength(100)] public string AppCode { get; set; } = string.Empty; + /// + /// 支付应用名称。 + /// [Required, StringLength(200)] public string AppName { get; set; } = string.Empty; + /// + /// 状态。 + /// public PlatformPaymentAppStatus Status { get; set; } = PlatformPaymentAppStatus.Disabled; + /// + /// 结算模式。 + /// [StringLength(100)] public string SettlementMode { get; set; } = "PlatformCollect"; + /// + /// 说明。 + /// [StringLength(500)] public string? Description { get; set; } + /// + /// 扩展元数据。 + /// public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertPlatformPaymentAppCommand ToCommand() => new(Id, AppCode, AppName, Status, SettlementMode, Description, Metadata); } +/// +/// 新增或更新平台支付渠道请求。 +/// public sealed class UpsertPlatformPaymentChannelDto { + /// + /// ID。 + /// public Guid? Id { get; set; } + /// + /// 支付应用 ID。 + /// [Required] public Guid AppId { get; set; } + /// + /// 服务提供方。 + /// [Required, StringLength(80)] public string Provider { get; set; } = "manual"; + /// + /// 模式。 + /// [StringLength(100)] public string Mode { get; set; } = "PlatformCollect"; + /// + /// 状态。 + /// public PlatformPaymentChannelStatus Status { get; set; } = PlatformPaymentChannelStatus.Disabled; + /// + /// 显示名称。 + /// [Required, StringLength(200)] public string DisplayName { get; set; } = string.Empty; + /// + /// 密钥引用。 + /// [StringLength(300)] public string? SecretRef { get; set; } + /// + /// 回调路径。 + /// [StringLength(500)] public string? CallbackPath { get; set; } + /// + /// 优先级。 + /// public int? Priority { get; set; } + /// + /// 公开配置内容。 + /// public JsonElement ConfigPublic { get; set; } = JsonDefaults.Object(); + /// + /// 扩展元数据。 + /// public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertPlatformPaymentChannelCommand ToCommand() => new(Id, AppId, Provider, Mode, Status, DisplayName, SecretRef, CallbackPath, Priority, ConfigPublic, Metadata); } +/// +/// 新增或更新租户支付应用请求。 +/// public sealed class UpsertPlatformTenantPaymentAppDto { + /// + /// 租户 ID。 + /// [Required] public Guid TenantId { get; set; } + /// + /// 服务提供方。 + /// [Required, StringLength(80)] public string Provider { get; set; } = "manual"; + /// + /// 状态。 + /// public TenantExternalProviderStatus Status { get; set; } = TenantExternalProviderStatus.Disabled; + /// + /// 显示名称。 + /// [StringLength(200)] public string? DisplayName { get; set; } + /// + /// 密钥引用。 + /// [StringLength(300)] public string? SecretRef { get; set; } + /// + /// 优先级。 + /// public int? Priority { get; set; } + /// + /// 公开配置内容。 + /// public JsonElement ConfigPublic { get; set; } = JsonDefaults.Object(); + /// + /// 扩展元数据。 + /// public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertTenantExternalProviderCommand ToCommand() => diff --git a/Tiku.Api/Controllers/AssetsController.cs b/Tiku.Api/Controllers/AssetsController.cs index f281f4a..b13f93e 100644 --- a/Tiku.Api/Controllers/AssetsController.cs +++ b/Tiku.Api/Controllers/AssetsController.cs @@ -10,6 +10,7 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-资源访问")] [AllowAnonymous] [Produces("application/json")] [Route("api/assets")] diff --git a/Tiku.Api/Controllers/AuthController.cs b/Tiku.Api/Controllers/AuthController.cs index 9a77211..e61742f 100644 --- a/Tiku.Api/Controllers/AuthController.cs +++ b/Tiku.Api/Controllers/AuthController.cs @@ -13,6 +13,7 @@ using Tiku.Domain.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-认证")] [Route("api/auth")] [Produces("application/json")] public sealed class AuthController( diff --git a/Tiku.Api/Controllers/BackgroundJobsController.cs b/Tiku.Api/Controllers/BackgroundJobsController.cs index aaaaf74..3ba048a 100644 --- a/Tiku.Api/Controllers/BackgroundJobsController.cs +++ b/Tiku.Api/Controllers/BackgroundJobsController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-后台任务")] [Route("api/backoffice/tenant/jobs")] [Authorize(Policy = BackendPermissions.TenantJobManage)] public sealed class BackgroundJobsController( diff --git a/Tiku.Api/Controllers/BackofficeController.cs b/Tiku.Api/Controllers/BackofficeController.cs deleted file mode 100644 index 77d519b..0000000 --- a/Tiku.Api/Controllers/BackofficeController.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Tiku.Api.Contracts; -using Tiku.Application.Backoffice; -using Tiku.Application.Security; - -namespace Tiku.Api.Controllers; - -[ApiController] -[Route("api/backoffice")] -public sealed class BackofficeController( - IBackofficeService backofficeService, - ICurrentAccessContext currentAccessContext) : ControllerBase -{ - [HttpGet("tenant/ui-bootstrap")] - [Authorize(Policy = TikuPolicies.TenantBackofficeBootstrap)] - [EndpointSummary("查询租户后台菜单与权限")] - [EndpointDescription("返回当前租户管理员可见的后台菜单、权限和模块启用状态。")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetTenantUiBootstrap(CancellationToken cancellationToken) - { - return Ok(await backofficeService.GetTenantUiBootstrapAsync( - await currentAccessContext.GetAsync(cancellationToken), - cancellationToken)); - } - - [HttpGet("tenant/bootstrap")] - [Authorize(Policy = BackendPermissions.TenantRoleManage)] - [EndpointSummary("查询租户角色管理初始化数据")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetTenantBootstrap(CancellationToken cancellationToken) - { - return Ok(await backofficeService.GetTenantBootstrapAsync(await ResolveTenantActorAsync(cancellationToken), cancellationToken)); - } - - [HttpGet("platform/ui-bootstrap")] - [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] - [EndpointSummary("查询平台后台菜单与权限")] - [EndpointDescription("返回当前平台管理员可见的后台菜单、权限和模块启用状态。")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetPlatformUiBootstrap(CancellationToken cancellationToken) - { - return Ok(await backofficeService.GetPlatformUiBootstrapAsync( - await currentAccessContext.GetAsync(cancellationToken), - cancellationToken)); - } - - [HttpPost("tenant/roles")] - [Authorize(Policy = BackendPermissions.TenantRoleManage)] - [EndpointSummary("创建或更新租户后台角色")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> UpsertTenantRole( - UpsertBackofficeRoleDto request, - CancellationToken cancellationToken) - { - return Ok(await backofficeService.UpsertTenantRoleAsync(await ResolveTenantActorAsync(cancellationToken), request.ToCommand(), cancellationToken)); - } - - [HttpPut("tenant/roles/{roleId:guid}/bindings")] - [Authorize(Policy = BackendPermissions.TenantRoleManage)] - [EndpointSummary("替换租户后台角色权限绑定")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> ReplaceTenantRoleBindings( - Guid roleId, - ReplaceRoleBindingsDto request, - CancellationToken cancellationToken) - { - return Ok(await backofficeService.ReplaceTenantRoleBindingsAsync(await ResolveTenantActorAsync(cancellationToken), request.ToCommand(roleId), cancellationToken)); - } - - [HttpPut("tenant/users/{userId:guid}/roles")] - [Authorize(Policy = BackendPermissions.TenantRoleManage)] - [EndpointSummary("替换租户用户后台角色")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task ReplaceTenantUserRoles( - Guid userId, - ReplaceUserRolesDto request, - CancellationToken cancellationToken) - { - await backofficeService.ReplaceTenantUserRolesAsync(await ResolveTenantActorAsync(cancellationToken), request.ToCommand(userId), cancellationToken); - return NoContent(); - } - - [HttpGet("platform/bootstrap")] - [Authorize(Policy = BackendPermissions.PlatformRoleManage)] - [EndpointSummary("查询平台角色管理初始化数据")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetPlatformBootstrap(CancellationToken cancellationToken) - { - return Ok(await backofficeService.GetPlatformBootstrapAsync(await ResolvePlatformActorAsync(cancellationToken), cancellationToken)); - } - - [HttpPost("platform/roles")] - [Authorize(Policy = BackendPermissions.PlatformRoleManage)] - [EndpointSummary("创建或更新平台后台角色")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> UpsertPlatformRole( - UpsertBackofficeRoleDto request, - CancellationToken cancellationToken) - { - return Ok(await backofficeService.UpsertPlatformRoleAsync(await ResolvePlatformActorAsync(cancellationToken), request.ToCommand(), cancellationToken)); - } - - [HttpPut("platform/roles/{roleId:guid}/bindings")] - [Authorize(Policy = BackendPermissions.PlatformRoleManage)] - [EndpointSummary("替换平台后台角色权限绑定")] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> ReplacePlatformRoleBindings( - Guid roleId, - ReplaceRoleBindingsDto request, - CancellationToken cancellationToken) - { - return Ok(await backofficeService.ReplacePlatformRoleBindingsAsync(await ResolvePlatformActorAsync(cancellationToken), request.ToCommand(roleId), cancellationToken)); - } - - [HttpPut("platform/users/{userId:guid}/roles")] - [Authorize(Policy = BackendPermissions.PlatformRoleManage)] - [EndpointSummary("替换平台用户后台角色")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task ReplacePlatformUserRoles( - Guid userId, - ReplaceUserRolesDto request, - CancellationToken cancellationToken) - { - await backofficeService.ReplacePlatformUserRolesAsync(await ResolvePlatformActorAsync(cancellationToken), request.ToCommand(userId), cancellationToken); - return NoContent(); - } - - private async Task ResolveTenantActorAsync(CancellationToken cancellationToken) - { - return BackofficeActor.FromTenantAccess(await currentAccessContext.GetAsync(cancellationToken)); - } - - private async Task ResolvePlatformActorAsync(CancellationToken cancellationToken) - { - return BackofficeActor.FromPlatformAccess(await currentAccessContext.GetAsync(cancellationToken)); - } -} diff --git a/Tiku.Api/Controllers/BrowserAuthController.cs b/Tiku.Api/Controllers/BrowserAuthController.cs index c2b77fd..85a2245 100644 --- a/Tiku.Api/Controllers/BrowserAuthController.cs +++ b/Tiku.Api/Controllers/BrowserAuthController.cs @@ -14,6 +14,7 @@ using Tiku.Infrastructure.Content; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-浏览器认证")] [Route("api/browser-auth")] [Produces("application/json")] public sealed class BrowserAuthController( @@ -28,6 +29,7 @@ public sealed class BrowserAuthController( [AllowAnonymous] [EnableRateLimiting(AuthRateLimitPolicies.Sms)] [HttpPost("sms/send")] + [EndpointSummary("发送浏览器短信验证码")] public async Task> SendSmsCode( [FromBody] SendSmsCodeDto request, CancellationToken cancellationToken) @@ -52,6 +54,7 @@ public sealed class BrowserAuthController( [AllowAnonymous] [HttpPost("login/password")] + [EndpointSummary("浏览器手机号密码登录")] public async Task> LoginWithPassword( [FromBody] PasswordLoginDto request, CancellationToken cancellationToken) @@ -73,6 +76,7 @@ public sealed class BrowserAuthController( [AllowAnonymous] [EnableRateLimiting(AuthRateLimitPolicies.Sms)] [HttpPost("login/sms")] + [EndpointSummary("浏览器短信验证码登录")] public async Task> LoginWithSms( [FromBody] SmsLoginDto request, CancellationToken cancellationToken) @@ -91,6 +95,7 @@ public sealed class BrowserAuthController( [AllowAnonymous] [HttpPost("oauth/wechat")] + [EndpointSummary("浏览器微信网页 OAuth 登录")] public async Task> LoginWithWechatWeb( [FromBody] OAuthCodeDto request, CancellationToken cancellationToken) @@ -108,6 +113,7 @@ public sealed class BrowserAuthController( [AllowAnonymous] [HttpPost("oauth/wechat-miniapp")] + [EndpointSummary("浏览器微信小程序登录")] public async Task> LoginWithWechatMiniApp( [FromBody] OAuthCodeDto request, CancellationToken cancellationToken) @@ -125,6 +131,8 @@ public sealed class BrowserAuthController( [AllowAnonymous] [HttpPost("refresh")] + [EndpointSummary("刷新浏览器登录会话")] + [EndpointDescription("读取浏览器 refresh cookie,轮换会话并重新写入认证 cookie。")] public async Task> Refresh(CancellationToken cancellationToken) { var refreshToken = Request.Cookies[BrowserAuthOptions.RefreshCookie]; @@ -139,6 +147,8 @@ public sealed class BrowserAuthController( [AllowAnonymous] [HttpPost("logout")] + [EndpointSummary("退出浏览器登录")] + [EndpointDescription("撤销浏览器 refresh cookie 对应会话,并清理 access、refresh 与 CSRF cookie。")] public async Task Logout(CancellationToken cancellationToken) { var refreshToken = Request.Cookies[BrowserAuthOptions.RefreshCookie]; @@ -152,6 +162,7 @@ public sealed class BrowserAuthController( [Authorize] [HttpPost("logout-all")] + [EndpointSummary("退出全部浏览器登录会话")] public async Task LogoutAll(CancellationToken cancellationToken) { if (currentUser.UserId is not { } userId) return Unauthorized(); diff --git a/Tiku.Api/Controllers/CatalogController.cs b/Tiku.Api/Controllers/CatalogController.cs index da4f379..1dff021 100644 --- a/Tiku.Api/Controllers/CatalogController.cs +++ b/Tiku.Api/Controllers/CatalogController.cs @@ -14,6 +14,7 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-公开目录")] [AllowAnonymous] [Produces("application/json")] [Route("api/catalog")] diff --git a/Tiku.Api/Controllers/CommerceController.cs b/Tiku.Api/Controllers/CommerceController.cs index 1d2cc88..ca83e6a 100644 --- a/Tiku.Api/Controllers/CommerceController.cs +++ b/Tiku.Api/Controllers/CommerceController.cs @@ -12,6 +12,7 @@ using Tiku.Domain.Commerce; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-交易")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.StudentStore)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/CommissionController.cs b/Tiku.Api/Controllers/CommissionController.cs index 91075a5..8689d3c 100644 --- a/Tiku.Api/Controllers/CommissionController.cs +++ b/Tiku.Api/Controllers/CommissionController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-佣金")] [Authorize(Policy = BackendPermissions.TenantCommissionManage)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.ReferralCommission)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/CrmController.cs b/Tiku.Api/Controllers/CrmController.cs index 9c2b678..b9c7065 100644 --- a/Tiku.Api/Controllers/CrmController.cs +++ b/Tiku.Api/Controllers/CrmController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-CRM")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Crm)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/HealthController.cs b/Tiku.Api/Controllers/HealthController.cs index 12e4799..e49aba5 100644 --- a/Tiku.Api/Controllers/HealthController.cs +++ b/Tiku.Api/Controllers/HealthController.cs @@ -11,6 +11,7 @@ using Tiku.Infrastructure.Messaging; namespace Tiku.Api.Controllers; [ApiController] +[Tags("平台端-系统健康")] [AllowAnonymous] [Produces("application/json")] [Route("api/health")] diff --git a/Tiku.Api/Controllers/LearningController.cs b/Tiku.Api/Controllers/LearningController.cs index 06835aa..f12522d 100644 --- a/Tiku.Api/Controllers/LearningController.cs +++ b/Tiku.Api/Controllers/LearningController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-学习")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Practice)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/MeController.cs b/Tiku.Api/Controllers/MeController.cs index bbd81e3..b5f540b 100644 --- a/Tiku.Api/Controllers/MeController.cs +++ b/Tiku.Api/Controllers/MeController.cs @@ -8,6 +8,7 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-当前用户")] [Authorize(Policy = TikuPolicies.AuthenticatedUser)] [Route("api/me")] public sealed class MeController( diff --git a/Tiku.Api/Controllers/PlatformAdminController.cs b/Tiku.Api/Controllers/PlatformAdminController.cs index 8613580..58448aa 100644 --- a/Tiku.Api/Controllers/PlatformAdminController.cs +++ b/Tiku.Api/Controllers/PlatformAdminController.cs @@ -8,6 +8,7 @@ using Tiku.Domain.Platform; namespace Tiku.Api.Controllers; [ApiController] +[Tags("平台端-平台管理")] [Authorize(Policy = BackendPermissions.PlatformDashboardView)] [Produces("application/json")] [Route("api/platform-admin")] diff --git a/Tiku.Api/Controllers/PlatformBackofficeController.cs b/Tiku.Api/Controllers/PlatformBackofficeController.cs new file mode 100644 index 0000000..ede9e67 --- /dev/null +++ b/Tiku.Api/Controllers/PlatformBackofficeController.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Tiku.Api.Contracts; +using Tiku.Application.Backoffice; +using Tiku.Application.Security; + +namespace Tiku.Api.Controllers; + +[ApiController] +[Tags("平台端-后台权限")] +[Route("api/backoffice/platform")] +public sealed class PlatformBackofficeController( + IBackofficeService backofficeService, + ICurrentAccessContext currentAccessContext) : ControllerBase +{ + [HttpGet("ui-bootstrap")] + [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] + [EndpointSummary("查询平台后台菜单与权限")] + [EndpointDescription("返回当前平台管理员可见的后台菜单、权限和模块启用状态。")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetUiBootstrap(CancellationToken cancellationToken) + { + return Ok(await backofficeService.GetPlatformUiBootstrapAsync( + await currentAccessContext.GetAsync(cancellationToken), + cancellationToken)); + } + + [HttpGet("bootstrap")] + [Authorize(Policy = BackendPermissions.PlatformRoleManage)] + [EndpointSummary("查询平台角色管理初始化数据")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetBootstrap(CancellationToken cancellationToken) + { + return Ok(await backofficeService.GetPlatformBootstrapAsync(await ResolveActorAsync(cancellationToken), cancellationToken)); + } + + [HttpPost("roles")] + [Authorize(Policy = BackendPermissions.PlatformRoleManage)] + [EndpointSummary("创建或更新平台后台角色")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> UpsertRole( + UpsertBackofficeRoleDto request, + CancellationToken cancellationToken) + { + return Ok(await backofficeService.UpsertPlatformRoleAsync(await ResolveActorAsync(cancellationToken), request.ToCommand(), cancellationToken)); + } + + [HttpPut("roles/{roleId:guid}/bindings")] + [Authorize(Policy = BackendPermissions.PlatformRoleManage)] + [EndpointSummary("替换平台后台角色权限绑定")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> ReplaceRoleBindings( + Guid roleId, + ReplaceRoleBindingsDto request, + CancellationToken cancellationToken) + { + return Ok(await backofficeService.ReplacePlatformRoleBindingsAsync(await ResolveActorAsync(cancellationToken), request.ToCommand(roleId), cancellationToken)); + } + + [HttpPut("users/{userId:guid}/roles")] + [Authorize(Policy = BackendPermissions.PlatformRoleManage)] + [EndpointSummary("替换平台用户后台角色")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task ReplaceUserRoles( + Guid userId, + ReplaceUserRolesDto request, + CancellationToken cancellationToken) + { + await backofficeService.ReplacePlatformUserRolesAsync(await ResolveActorAsync(cancellationToken), request.ToCommand(userId), cancellationToken); + return NoContent(); + } + + private async Task ResolveActorAsync(CancellationToken cancellationToken) + { + return BackofficeActor.FromPlatformAccess(await currentAccessContext.GetAsync(cancellationToken)); + } +} diff --git a/Tiku.Api/Controllers/PlatformBillingCallbackController.cs b/Tiku.Api/Controllers/PlatformBillingCallbackController.cs index 668fa62..796adbf 100644 --- a/Tiku.Api/Controllers/PlatformBillingCallbackController.cs +++ b/Tiku.Api/Controllers/PlatformBillingCallbackController.cs @@ -7,12 +7,15 @@ using Tiku.Application.PlatformBilling; namespace Tiku.Api.Controllers; [ApiController] +[Tags("平台端-账务回调")] [Route("api/platform-billing/callbacks")] public sealed class PlatformBillingCallbackController( IPlatformBillingNotificationService notificationService) : ControllerBase { [AllowAnonymous] [HttpPost("{provider}")] + [EndpointSummary("处理平台账务支付回调")] + [EndpointDescription("接收平台账务支付渠道回调,按 provider 归一化后写入通知处理流程。")] public async Task Notify(string provider, CancellationToken cancellationToken) { Request.EnableBuffering(); diff --git a/Tiku.Api/Controllers/PlatformPaymentSettingsController.cs b/Tiku.Api/Controllers/PlatformPaymentSettingsController.cs index dc65aac..f976736 100644 --- a/Tiku.Api/Controllers/PlatformPaymentSettingsController.cs +++ b/Tiku.Api/Controllers/PlatformPaymentSettingsController.cs @@ -8,6 +8,7 @@ using Tiku.Domain.Platform; namespace Tiku.Api.Controllers; [ApiController] +[Tags("平台端-支付设置")] [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] [Produces("application/json")] [Route("api/platform-admin/payment-settings")] @@ -16,36 +17,43 @@ public sealed class PlatformPaymentSettingsController( ICurrentUser currentUser) : ControllerBase { [HttpGet("apps")] + [EndpointSummary("查询平台支付应用")] [Authorize(Policy = BackendPermissions.PlatformPaymentRead)] public Task> Apps(string? status, int limit = 100, CancellationToken cancellationToken = default) => paymentSettingsService.GetAppsAsync(Actor(), status, limit, cancellationToken); [HttpPut("apps")] + [EndpointSummary("新增或更新平台支付应用")] [Authorize(Policy = BackendPermissions.PlatformPaymentWrite)] public Task UpsertApp(UpsertPlatformPaymentAppDto request, CancellationToken cancellationToken) => paymentSettingsService.UpsertAppAsync(Actor(), request.ToCommand(), cancellationToken); [HttpGet("channels")] + [EndpointSummary("查询平台支付渠道")] [Authorize(Policy = BackendPermissions.PlatformPaymentRead)] public Task> Channels(Guid? appId, string? status, int limit = 100, CancellationToken cancellationToken = default) => paymentSettingsService.GetChannelsAsync(Actor(), appId, status, limit, cancellationToken); [HttpPut("channels")] + [EndpointSummary("新增或更新平台支付渠道")] [Authorize(Policy = BackendPermissions.PlatformPaymentWrite)] public Task UpsertChannel(UpsertPlatformPaymentChannelDto request, CancellationToken cancellationToken) => paymentSettingsService.UpsertChannelAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPost("channels/{id:guid}/disable")] + [EndpointSummary("禁用平台支付渠道")] [Authorize(Policy = BackendPermissions.PlatformPaymentWrite)] public Task DisableChannel(Guid id, CancellationToken cancellationToken) => paymentSettingsService.DisableChannelAsync(Actor(), id, cancellationToken); [HttpGet("events")] + [EndpointSummary("查询平台支付事件")] [Authorize(Policy = BackendPermissions.PlatformPaymentRead)] public Task> Events(string? status, int limit = 100, CancellationToken cancellationToken = default) => paymentSettingsService.GetEventsAsync(Actor(), status, limit, cancellationToken); [HttpGet("rebates/summary")] + [EndpointSummary("查询平台返佣汇总")] [Authorize(Policy = BackendPermissions.PlatformPaymentRead)] public Task RebateSummary(CancellationToken cancellationToken) => paymentSettingsService.GetRebateSummaryAsync(Actor(), cancellationToken); diff --git a/Tiku.Api/Controllers/PlatformSaasController.cs b/Tiku.Api/Controllers/PlatformSaasController.cs index 2d39449..9189c74 100644 --- a/Tiku.Api/Controllers/PlatformSaasController.cs +++ b/Tiku.Api/Controllers/PlatformSaasController.cs @@ -8,6 +8,7 @@ using Tiku.Domain.Platform; namespace Tiku.Api.Controllers; [ApiController] +[Tags("平台端-SaaS 套餐")] [Route("api/platform-admin/saas")] [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] public sealed class PlatformSaasController( @@ -16,16 +17,19 @@ public sealed class PlatformSaasController( ICurrentUser currentUser) : ControllerBase { [HttpGet("catalog")] + [EndpointSummary("查询平台 SaaS 商品目录")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task Catalog(CancellationToken cancellationToken) => catalogService.GetCatalogAsync(Actor(), cancellationToken); [HttpPut("features")] + [EndpointSummary("新增或更新 SaaS 功能")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task UpsertFeature(UpsertSaasFeatureDto request, CancellationToken cancellationToken) => catalogService.UpsertFeatureAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPut("feature-limits")] + [EndpointSummary("新增或更新 SaaS 功能限额")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task UpsertFeatureLimit( UpsertSaasFeatureLimitDto request, @@ -33,61 +37,73 @@ public sealed class PlatformSaasController( catalogService.UpsertLimitDefinitionAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPut("offerings")] + [EndpointSummary("新增或更新 SaaS 套餐")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task UpsertOffering(UpsertSaasOfferingDto request, CancellationToken cancellationToken) => catalogService.UpsertOfferingAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPut("offering-versions")] + [EndpointSummary("新增或更新 SaaS 套餐版本草稿")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task UpsertVersion(UpsertSaasOfferingVersionDto request, CancellationToken cancellationToken) => catalogService.UpsertDraftVersionAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPost("offering-versions/{versionId:guid}/publish")] + [EndpointSummary("发布 SaaS 套餐版本")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task PublishVersion(Guid versionId, CancellationToken cancellationToken) => catalogService.PublishVersionAsync(Actor(), versionId, cancellationToken); [HttpPost("offering-versions/{versionId:guid}/clone")] + [EndpointSummary("克隆 SaaS 套餐版本")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task CloneVersion(Guid versionId, CancellationToken cancellationToken) => catalogService.CloneVersionAsync(Actor(), versionId, cancellationToken); [HttpPost("offering-versions/{versionId:guid}/retire")] + [EndpointSummary("下架 SaaS 套餐版本")] [Authorize(Policy = BackendPermissions.PlatformSaasCatalogManage)] public Task RetireVersion(Guid versionId, CancellationToken cancellationToken) => catalogService.RetireVersionAsync(Actor(), versionId, cancellationToken); [HttpGet("orders")] + [EndpointSummary("查询平台 SaaS 订单")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task> Orders(Guid? tenantId, string? status, int limit = 100, CancellationToken cancellationToken = default) => billingService.GetOrdersAsync(Actor(), new PlatformBillingAdminQuery(tenantId, status, limit), cancellationToken); [HttpGet("payments")] + [EndpointSummary("查询平台 SaaS 支付记录")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task> Payments(Guid? tenantId, string? status, int limit = 100, CancellationToken cancellationToken = default) => billingService.GetPaymentsAsync(Actor(), new PlatformBillingAdminQuery(tenantId, status, limit), cancellationToken); [HttpGet("refunds")] + [EndpointSummary("查询平台 SaaS 退款记录")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task> Refunds(Guid? tenantId, string? status, int limit = 100, CancellationToken cancellationToken = default) => billingService.GetRefundsAsync(Actor(), new PlatformBillingAdminQuery(tenantId, status, limit), cancellationToken); [HttpGet("invoices")] + [EndpointSummary("查询平台 SaaS 发票")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task> Invoices(Guid? tenantId, string? status, int limit = 100, CancellationToken cancellationToken = default) => billingService.GetInvoicesAsync(Actor(), new PlatformBillingAdminQuery(tenantId, status, limit), cancellationToken); [HttpGet("subscriptions")] + [EndpointSummary("查询租户 SaaS 订阅")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task> Subscriptions(Guid? tenantId, string? status, int limit = 100, CancellationToken cancellationToken = default) => billingService.GetSubscriptionsAsync(Actor(), new PlatformBillingAdminQuery(tenantId, status, limit), cancellationToken); [HttpPost("payments/manual/confirm")] + [EndpointSummary("确认平台手工支付")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task ConfirmManualPayment(ConfirmManualPlatformPaymentDto request, CancellationToken cancellationToken) => billingService.ConfirmManualPaymentAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPut("tenant-feature-overrides")] + [EndpointSummary("新增或更新租户功能覆盖规则")] [Authorize(Policy = BackendPermissions.PlatformSaasBillingManage)] public Task UpsertFeatureOverride(UpsertTenantFeatureOverrideDto request, CancellationToken cancellationToken) => billingService.UpsertFeatureOverrideAsync(Actor(), request.ToCommand(), cancellationToken); diff --git a/Tiku.Api/Controllers/PlatformTenantCapabilitiesController.cs b/Tiku.Api/Controllers/PlatformTenantCapabilitiesController.cs index 99b45f4..309e8d3 100644 --- a/Tiku.Api/Controllers/PlatformTenantCapabilitiesController.cs +++ b/Tiku.Api/Controllers/PlatformTenantCapabilitiesController.cs @@ -10,6 +10,7 @@ using Tiku.Domain.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("平台端-租户 CRM 能力")] [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] [Produces("application/json")] [Route("api/platform-admin/tenant-capabilities/crm")] @@ -18,26 +19,31 @@ public sealed class PlatformAdminCrmController( ICurrentUser currentUser) : ControllerBase { [HttpGet("configs")] + [EndpointSummary("查询租户 CRM 配置")] [Authorize(Policy = BackendPermissions.PlatformCrmRead)] public Task> Configs([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => crmService.GetConfigsAsync(Actor(), query.ToQuery(), cancellationToken); [HttpPut("configs")] + [EndpointSummary("新增或更新租户 CRM 配置")] [Authorize(Policy = BackendPermissions.PlatformCrmWrite)] public Task UpsertConfig(UpsertPlatformCrmConfigDto request, CancellationToken cancellationToken) => crmService.UpsertConfigAsync(Actor(), request.ToCommand(), cancellationToken); [HttpGet("leads")] + [EndpointSummary("查询租户 CRM 线索队列")] [Authorize(Policy = BackendPermissions.PlatformCrmRead)] public Task> Leads([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => crmService.GetLeadsAsync(Actor(), query.ToQuery(), cancellationToken); [HttpPost("leads/retry")] + [EndpointSummary("重试租户 CRM 线索推送")] [Authorize(Policy = BackendPermissions.PlatformCrmWrite)] public Task RetryLead(RetryPlatformCrmLeadDto request, CancellationToken cancellationToken) => crmService.RetryLeadAsync(Actor(), request.ToCommand(), cancellationToken); [HttpGet("logs")] + [EndpointSummary("查询租户 CRM 推送日志")] [Authorize(Policy = BackendPermissions.PlatformCrmRead)] public Task> Logs([FromQuery] PlatformCrmLogQueryDto query, CancellationToken cancellationToken) => crmService.GetLogsAsync(Actor(), query.TenantId, query.QueueId, query.Limit, cancellationToken); @@ -48,6 +54,7 @@ public sealed class PlatformAdminCrmController( } [ApiController] +[Tags("平台端-租户短信能力")] [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] [Produces("application/json")] [Route("api/platform-admin/tenant-capabilities/sms")] @@ -56,41 +63,49 @@ public sealed class PlatformAdminSmsController( ICurrentUser currentUser) : ControllerBase { [HttpGet("channels")] + [EndpointSummary("查询租户短信渠道")] [Authorize(Policy = BackendPermissions.PlatformSmsRead)] public Task> Channels([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => smsService.GetChannelsAsync(Actor(), query.ToQuery(), cancellationToken); [HttpPut("channels")] + [EndpointSummary("新增或更新租户短信渠道")] [Authorize(Policy = BackendPermissions.PlatformSmsWrite)] public Task UpsertChannel(UpsertPlatformSmsChannelDto request, CancellationToken cancellationToken) => smsService.UpsertChannelAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPost("channels/{id:guid}/disable")] + [EndpointSummary("禁用租户短信渠道")] [Authorize(Policy = BackendPermissions.PlatformSmsWrite)] public Task DisableChannel(Guid id, CancellationToken cancellationToken) => smsService.DisableChannelAsync(Actor(), id, cancellationToken); [HttpGet("templates")] + [EndpointSummary("查询租户短信模板")] [Authorize(Policy = BackendPermissions.PlatformSmsRead)] public Task> Templates([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => smsService.GetTemplatesAsync(Actor(), query.ToQuery(), cancellationToken); [HttpPut("templates")] + [EndpointSummary("新增或更新租户短信模板")] [Authorize(Policy = BackendPermissions.PlatformSmsWrite)] public Task UpsertTemplate(UpsertPlatformSmsTemplateDto request, CancellationToken cancellationToken) => smsService.UpsertTemplateAsync(Actor(), request.ToCommand(), cancellationToken); [HttpPost("templates/{id:guid}/submit-review")] + [EndpointSummary("提交租户短信模板审核")] [Authorize(Policy = BackendPermissions.PlatformSmsWrite)] public Task SubmitTemplateReview(Guid id, CancellationToken cancellationToken) => smsService.SubmitTemplateReviewAsync(Actor(), id, cancellationToken); [HttpPost("templates/{id:guid}/disable")] + [EndpointSummary("禁用租户短信模板")] [Authorize(Policy = BackendPermissions.PlatformSmsWrite)] public Task DisableTemplate(Guid id, CancellationToken cancellationToken) => smsService.DisableTemplateAsync(Actor(), id, cancellationToken); [HttpGet("logs")] + [EndpointSummary("查询租户短信发送日志")] [Authorize(Policy = BackendPermissions.PlatformSmsRead)] public Task> Logs([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => smsService.GetLogsAsync(Actor(), query.ToQuery(), cancellationToken); @@ -101,6 +116,7 @@ public sealed class PlatformAdminSmsController( } [ApiController] +[Tags("平台端-租户支付能力")] [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] [Produces("application/json")] [Route("api/platform-admin/tenant-capabilities/payments")] @@ -109,16 +125,19 @@ public sealed class PlatformAdminTenantPaymentSettingsController( ICurrentUser currentUser) : ControllerBase { [HttpGet("apps")] + [EndpointSummary("查询租户支付应用")] [Authorize(Policy = BackendPermissions.PlatformPaymentRead)] public Task> Apps([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => paymentService.GetAppsAsync(Actor(), query.ToQuery(), cancellationToken); [HttpPut("apps")] + [EndpointSummary("新增或更新租户支付应用")] [Authorize(Policy = BackendPermissions.PlatformPaymentWrite)] public Task UpsertApp(UpsertPlatformTenantPaymentAppDto request, CancellationToken cancellationToken) => paymentService.UpsertAppAsync(Actor(), request.TenantId, request.ToCommand(), cancellationToken); [HttpGet("events")] + [EndpointSummary("查询租户支付事件")] [Authorize(Policy = BackendPermissions.PlatformPaymentRead)] public Task> Events([FromQuery] PlatformCapabilityQueryDto query, CancellationToken cancellationToken) => paymentService.GetEventsAsync(Actor(), query.ToQuery(), cancellationToken); diff --git a/Tiku.Api/Controllers/PointsController.cs b/Tiku.Api/Controllers/PointsController.cs index 8154a28..f49bfb9 100644 --- a/Tiku.Api/Controllers/PointsController.cs +++ b/Tiku.Api/Controllers/PointsController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-积分")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.StudentStore)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/ProfileController.cs b/Tiku.Api/Controllers/ProfileController.cs index f58420e..a2022d3 100644 --- a/Tiku.Api/Controllers/ProfileController.cs +++ b/Tiku.Api/Controllers/ProfileController.cs @@ -8,6 +8,7 @@ using Tiku.Infrastructure.Profile; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-个人中心")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Produces("application/json")] [Route("api/profile")] diff --git a/Tiku.Api/Controllers/QuestionVideosController.cs b/Tiku.Api/Controllers/QuestionVideosController.cs index ff498e3..0268fed 100644 --- a/Tiku.Api/Controllers/QuestionVideosController.cs +++ b/Tiku.Api/Controllers/QuestionVideosController.cs @@ -8,6 +8,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-题目视频")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Video)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/ReferralController.cs b/Tiku.Api/Controllers/ReferralController.cs index bc6205e..3193a96 100644 --- a/Tiku.Api/Controllers/ReferralController.cs +++ b/Tiku.Api/Controllers/ReferralController.cs @@ -11,6 +11,7 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-推荐增长")] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.ReferralCommission)] [Produces("application/json")] [Route("api/referral")] @@ -96,6 +97,7 @@ public sealed class ReferralController( } [HttpGet("stats")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("查询推荐人个人统计")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -110,6 +112,7 @@ public sealed class ReferralController( } [HttpGet("sales-stats")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("查询销售推荐统计排行")] [ProducesResponseType>(StatusCodes.Status200OK)] @@ -124,6 +127,7 @@ public sealed class ReferralController( } [HttpGet("conversion-report")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("查询推荐转化报告")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -138,6 +142,7 @@ public sealed class ReferralController( } [HttpGet("sales-clients")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("查询推荐人名下客户")] [ProducesResponseType>(StatusCodes.Status200OK)] @@ -152,6 +157,7 @@ public sealed class ReferralController( } [HttpPost("manual-bind")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("人工调整学生推荐归属")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -166,6 +172,7 @@ public sealed class ReferralController( } [HttpGet("team")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("查询推荐团队成员")] [ProducesResponseType>(StatusCodes.Status200OK)] @@ -180,6 +187,7 @@ public sealed class ReferralController( } [HttpPut("team")] + [Tags("租户端-推荐增长")] [Authorize(Policy = BackendPermissions.TenantCrmManage)] [EndpointSummary("新增或更新推荐团队关系")] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/Tiku.Api/Controllers/RuntimeController.cs b/Tiku.Api/Controllers/RuntimeController.cs index 005c24a..cfe6db2 100644 --- a/Tiku.Api/Controllers/RuntimeController.cs +++ b/Tiku.Api/Controllers/RuntimeController.cs @@ -6,6 +6,7 @@ using Tiku.Application.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-运行时配置")] [AllowAnonymous] [Produces("application/json")] [Route("api/runtime")] diff --git a/Tiku.Api/Controllers/ScorelineController.cs b/Tiku.Api/Controllers/ScorelineController.cs index f0801b0..e0a4e12 100644 --- a/Tiku.Api/Controllers/ScorelineController.cs +++ b/Tiku.Api/Controllers/ScorelineController.cs @@ -11,6 +11,7 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-分数线")] [AllowAnonymous] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Scoreline)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/TaxonomyController.cs b/Tiku.Api/Controllers/TaxonomyController.cs index 46044bc..d6de84a 100644 --- a/Tiku.Api/Controllers/TaxonomyController.cs +++ b/Tiku.Api/Controllers/TaxonomyController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-分类管理")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Produces("application/json")] [Route("api/taxonomy/nodes")] diff --git a/Tiku.Api/Controllers/TenantAdminDirectController.cs b/Tiku.Api/Controllers/TenantAdminDirectController.cs index 5f9b669..f28759f 100644 --- a/Tiku.Api/Controllers/TenantAdminDirectController.cs +++ b/Tiku.Api/Controllers/TenantAdminDirectController.cs @@ -10,6 +10,7 @@ using Tiku.Domain.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-运营管理")] [Produces("application/json")] [Route("api/tenant-admin")] public sealed class TenantAdminDirectController( diff --git a/Tiku.Api/Controllers/TenantBackofficeController.cs b/Tiku.Api/Controllers/TenantBackofficeController.cs new file mode 100644 index 0000000..681a3bf --- /dev/null +++ b/Tiku.Api/Controllers/TenantBackofficeController.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Tiku.Api.Contracts; +using Tiku.Application.Backoffice; +using Tiku.Application.Security; + +namespace Tiku.Api.Controllers; + +[ApiController] +[Tags("租户端-后台权限")] +[Route("api/backoffice/tenant")] +public sealed class TenantBackofficeController( + IBackofficeService backofficeService, + ICurrentAccessContext currentAccessContext) : ControllerBase +{ + [HttpGet("ui-bootstrap")] + [Authorize(Policy = TikuPolicies.TenantBackofficeBootstrap)] + [EndpointSummary("查询租户后台菜单与权限")] + [EndpointDescription("返回当前租户管理员可见的后台菜单、权限和模块启用状态。")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetUiBootstrap(CancellationToken cancellationToken) + { + return Ok(await backofficeService.GetTenantUiBootstrapAsync( + await currentAccessContext.GetAsync(cancellationToken), + cancellationToken)); + } + + [HttpGet("bootstrap")] + [Authorize(Policy = BackendPermissions.TenantRoleManage)] + [EndpointSummary("查询租户角色管理初始化数据")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetBootstrap(CancellationToken cancellationToken) + { + return Ok(await backofficeService.GetTenantBootstrapAsync(await ResolveActorAsync(cancellationToken), cancellationToken)); + } + + [HttpPost("roles")] + [Authorize(Policy = BackendPermissions.TenantRoleManage)] + [EndpointSummary("创建或更新租户后台角色")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> UpsertRole( + UpsertBackofficeRoleDto request, + CancellationToken cancellationToken) + { + return Ok(await backofficeService.UpsertTenantRoleAsync(await ResolveActorAsync(cancellationToken), request.ToCommand(), cancellationToken)); + } + + [HttpPut("roles/{roleId:guid}/bindings")] + [Authorize(Policy = BackendPermissions.TenantRoleManage)] + [EndpointSummary("替换租户后台角色权限绑定")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> ReplaceRoleBindings( + Guid roleId, + ReplaceRoleBindingsDto request, + CancellationToken cancellationToken) + { + return Ok(await backofficeService.ReplaceTenantRoleBindingsAsync(await ResolveActorAsync(cancellationToken), request.ToCommand(roleId), cancellationToken)); + } + + [HttpPut("users/{userId:guid}/roles")] + [Authorize(Policy = BackendPermissions.TenantRoleManage)] + [EndpointSummary("替换租户用户后台角色")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task ReplaceUserRoles( + Guid userId, + ReplaceUserRolesDto request, + CancellationToken cancellationToken) + { + await backofficeService.ReplaceTenantUserRolesAsync(await ResolveActorAsync(cancellationToken), request.ToCommand(userId), cancellationToken); + return NoContent(); + } + + private async Task ResolveActorAsync(CancellationToken cancellationToken) + { + return BackofficeActor.FromTenantAccess(await currentAccessContext.GetAsync(cancellationToken)); + } +} diff --git a/Tiku.Api/Controllers/TenantBillingController.cs b/Tiku.Api/Controllers/TenantBillingController.cs index 3f057a9..3ed50c1 100644 --- a/Tiku.Api/Controllers/TenantBillingController.cs +++ b/Tiku.Api/Controllers/TenantBillingController.cs @@ -8,6 +8,7 @@ using Tiku.Domain.Platform; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-账务")] [Route("api/tenant-billing")] [Authorize(Policy = BackendPermissions.TenantBillingManage)] public sealed class TenantBillingController( @@ -15,50 +16,62 @@ public sealed class TenantBillingController( ICurrentAccessContext accessContext) : ControllerBase { [HttpGet("catalog")] + [EndpointSummary("查询租户可购买 SaaS 目录")] public async Task Catalog(CancellationToken cancellationToken) => await billingService.GetCatalogAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpPost("quotes")] + [EndpointSummary("创建租户账务报价")] public async Task Quote(CreatePlatformBillingQuoteDto request, CancellationToken cancellationToken) => await billingService.CreateQuoteAsync(await ActorAsync(cancellationToken), request.ToCommand(), cancellationToken); [HttpPost("orders")] + [EndpointSummary("创建租户账务订单")] public async Task CreateOrder(CreatePlatformBillingOrderDto request, CancellationToken cancellationToken) => await billingService.CreateOrderAsync(await ActorAsync(cancellationToken), request.ToCommand(), cancellationToken); [HttpPost("orders/{orderNo}/payments")] + [EndpointSummary("创建租户账务订单支付")] public async Task CreatePayment(string orderNo, CreatePlatformBillingPaymentDto request, CancellationToken cancellationToken) => await billingService.CreatePaymentAsync(await ActorAsync(cancellationToken), request.ToCommand(orderNo), cancellationToken); [HttpGet("orders")] + [EndpointSummary("查询租户账务订单")] public async Task> Orders(int limit = 100, CancellationToken cancellationToken = default) => await billingService.GetOrdersAsync(await ActorAsync(cancellationToken), limit, cancellationToken); [HttpGet("orders/{orderNo}")] + [EndpointSummary("查询租户账务订单详情")] public async Task Order(string orderNo, CancellationToken cancellationToken) => await billingService.GetOrderAsync(await ActorAsync(cancellationToken), orderNo, cancellationToken); [HttpGet("subscription")] + [EndpointSummary("查询当前租户 SaaS 订阅")] public async Task Subscription(CancellationToken cancellationToken) => await billingService.GetSubscriptionAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpPost("subscription/change")] + [EndpointSummary("变更当前租户 SaaS 订阅")] public async Task Change(ChangeTenantSubscriptionDto request, CancellationToken cancellationToken) => await billingService.ChangeSubscriptionAsync(await ActorAsync(cancellationToken), request.ToCommand(), request.IdempotencyKey, cancellationToken); [HttpPost("subscription/renew")] + [EndpointSummary("续费当前租户 SaaS 订阅")] public async Task Renew(IdempotentTenantBillingDto request, CancellationToken cancellationToken) => await billingService.RenewSubscriptionAsync(await ActorAsync(cancellationToken), request.IdempotencyKey, cancellationToken); [HttpPost("subscription/cancel")] + [EndpointSummary("取消当前租户 SaaS 订阅")] public async Task Cancel(CancellationToken cancellationToken) => await billingService.CancelSubscriptionAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpGet("usage")] + [EndpointSummary("查询当前租户功能用量")] public async Task> Usage(CancellationToken cancellationToken) => await billingService.GetUsageAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpGet("invoices")] + [EndpointSummary("查询当前租户发票")] public async Task> Invoices(int limit = 100, CancellationToken cancellationToken = default) => await billingService.GetInvoicesAsync(await ActorAsync(cancellationToken), limit, cancellationToken); diff --git a/Tiku.Api/Controllers/TenantCommerceController.cs b/Tiku.Api/Controllers/TenantCommerceController.cs index 5c16cb6..c58b1d1 100644 --- a/Tiku.Api/Controllers/TenantCommerceController.cs +++ b/Tiku.Api/Controllers/TenantCommerceController.cs @@ -9,6 +9,7 @@ using Tiku.Domain.Commerce; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-交易运营")] [Authorize(Policy = BackendPermissions.TenantCommerceOperate)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.StudentStore)] [Produces("application/json")] diff --git a/Tiku.Api/Controllers/TenantContentController.cs b/Tiku.Api/Controllers/TenantContentController.cs index 4579efe..be97f09 100644 --- a/Tiku.Api/Controllers/TenantContentController.cs +++ b/Tiku.Api/Controllers/TenantContentController.cs @@ -9,6 +9,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-内容管理")] [Authorize(Policy = BackendPermissions.TenantContentManage)] [Produces("application/json")] [Route("api/tenant-content")] diff --git a/Tiku.Api/Controllers/TenantContentDirectController.cs b/Tiku.Api/Controllers/TenantContentDirectController.cs index 470ca8a..4bed5b7 100644 --- a/Tiku.Api/Controllers/TenantContentDirectController.cs +++ b/Tiku.Api/Controllers/TenantContentDirectController.cs @@ -12,6 +12,7 @@ using Tiku.Domain.Content; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-内容直接管理")] [Produces("application/json")] [Route("api/tenant-content")] public sealed class TenantContentDirectController( diff --git a/Tiku.Api/Controllers/TenantFrontendConfigController.cs b/Tiku.Api/Controllers/TenantFrontendConfigController.cs index 0ab213c..93c0a31 100644 --- a/Tiku.Api/Controllers/TenantFrontendConfigController.cs +++ b/Tiku.Api/Controllers/TenantFrontendConfigController.cs @@ -7,6 +7,7 @@ using Tiku.Application.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-前端配置")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [Produces("application/json")] [Route("api/tenant-admin/frontend-config")] diff --git a/Tiku.Api/Controllers/TenantOnboardingController.cs b/Tiku.Api/Controllers/TenantOnboardingController.cs index ba3fdc7..3d8cfdf 100644 --- a/Tiku.Api/Controllers/TenantOnboardingController.cs +++ b/Tiku.Api/Controllers/TenantOnboardingController.cs @@ -6,6 +6,7 @@ using Tiku.Application.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-开通引导")] [Route("api/tenant-onboarding")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] public sealed class TenantOnboardingController( @@ -13,6 +14,7 @@ public sealed class TenantOnboardingController( ICurrentAccessContext accessContext) : ControllerBase { [HttpGet("status")] + [EndpointSummary("查询租户开通引导状态")] public async Task Status(CancellationToken cancellationToken) { var access = await accessContext.GetAsync(cancellationToken); diff --git a/Tiku.Api/Controllers/TenantPublicController.cs b/Tiku.Api/Controllers/TenantPublicController.cs index 4d702cd..3696b56 100644 --- a/Tiku.Api/Controllers/TenantPublicController.cs +++ b/Tiku.Api/Controllers/TenantPublicController.cs @@ -13,6 +13,7 @@ using Tiku.Application.Tenancy; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-公开配置")] [AllowAnonymous] [Produces("application/json")] [Route("api/tenant")] diff --git a/Tiku.Api/Controllers/TenantsController.cs b/Tiku.Api/Controllers/TenantsController.cs index 39c3250..b324c77 100644 --- a/Tiku.Api/Controllers/TenantsController.cs +++ b/Tiku.Api/Controllers/TenantsController.cs @@ -9,6 +9,7 @@ using Tiku.Infrastructure.Persistence; namespace Tiku.Api.Controllers; [ApiController] +[Tags("租户端-当前租户")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Route("api/tenants")] public sealed class TenantsController( diff --git a/Tiku.Api/Controllers/VideosController.cs b/Tiku.Api/Controllers/VideosController.cs index 361aa8b..4cd9861 100644 --- a/Tiku.Api/Controllers/VideosController.cs +++ b/Tiku.Api/Controllers/VideosController.cs @@ -8,6 +8,7 @@ using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] +[Tags("学生端-视频")] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Tiku.Api.Security.RequireSaasFeature(SaasFeatureCatalog.Video)] [Produces("application/json")]