using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Api.Contracts; using Tiku.Application.Catalog; using Tiku.Application.Content; using Tiku.Application.Security; using Tiku.Application.TenantAdmin; using Tiku.Domain.Tenancy; namespace Tiku.Api.Controllers; [ApiController] [Authorize(Policy = TikuPolicies.TenantAdmin)] [Produces("application/json")] [Route("api/tenant-admin")] public sealed class TenantAdminDirectController( ITenantAdminDirectService tenantAdminService, ICurrentUser currentUser, ICurrentTenant currentTenant) : ControllerBase { [HttpGet("classes")] [EndpointSummary("查询租户班级")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetClasses( [FromQuery] TenantAdminClassQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetClassesAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("classes")] [EndpointSummary("新增或更新租户班级")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertClass( UpsertTenantAdminClassDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertClassAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("classes/disable")] [EndpointSummary("停用租户班级")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> DisableClass( [FromBody] Guid classId, CancellationToken cancellationToken) { return Ok(await tenantAdminService.DisableClassAsync(ResolveActor(), classId, cancellationToken)); } [HttpGet("classes/members")] [EndpointSummary("查询班级成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetClassMembers( [FromQuery] TenantAdminClassMemberQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetClassMembersAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("classes/members")] [EndpointSummary("新增或更新班级成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertClassMember( UpsertTenantAdminClassMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertClassMemberAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("classes/members/remove")] [EndpointSummary("移除班级成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> RemoveClassMember( RemoveTenantAdminClassMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.RemoveClassMemberAsync(ResolveActor(), request.ClassMemberId, cancellationToken)); } [HttpGet("students")] [EndpointSummary("查询租户学生")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetStudents( [FromQuery] TenantAdminStudentQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetStudentsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("students")] [EndpointSummary("新增或更新租户学生档案")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertStudent( UpsertTenantAdminStudentDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertStudentAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("students/status")] [EndpointSummary("更新租户学生状态")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpdateStudentStatus( UpdateTenantAdminStudentStatusDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpdateStudentStatusAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("student-notes")] [EndpointSummary("查询学生备注")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetStudentNotes( [FromQuery] TenantAdminStudentActivityQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetStudentNotesAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("student-notes")] [EndpointSummary("新增或更新学生备注")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertStudentNote( UpsertTenantAdminStudentNoteDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertStudentNoteAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("student-followups")] [EndpointSummary("查询学生跟进")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetStudentFollowups( [FromQuery] TenantAdminStudentActivityQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetStudentFollowupsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("student-followups")] [EndpointSummary("新增或更新学生跟进")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertStudentFollowup( UpsertTenantAdminStudentFollowupDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertStudentFollowupAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("members")] [EndpointSummary("查询租户成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetMembers( [FromQuery] TenantAdminMemberQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetMembersAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("members")] [EndpointSummary("新增或更新租户成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertMember( UpsertTenantAdminMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertMemberAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("members/disable")] [EndpointSummary("停用租户成员并撤销会话")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> DisableMember( DisableTenantAdminMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.DisableMemberAsync(ResolveActor(), request.MembershipId, cancellationToken)); } [HttpGet("audit-logs")] [EndpointSummary("查询租户审计日志")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetAuditLogs( [FromQuery] TenantAdminAuditLogQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetAuditLogsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpGet("permissions")] [EndpointSummary("查询租户后台权限矩阵")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPermissions(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetPermissionMatrixAsync(ResolveActor(), cancellationToken)); } [HttpGet("role-templates")] [EndpointSummary("查询租户角色模板")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetRoleTemplates( [FromQuery] TenantAdminRoleTemplateQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetRoleTemplatesAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("role-templates")] [EndpointSummary("新增或更新租户角色模板")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertRoleTemplate( UpsertTenantAdminRoleTemplateDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertRoleTemplateAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("role-templates/disable")] [EndpointSummary("停用租户角色模板")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> DisableRoleTemplate( DisableTenantAdminRoleTemplateDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.DisableRoleTemplateAsync(ResolveActor(), request.RoleTemplateId, cancellationToken)); } [HttpPut("branding")] [EndpointSummary("更新租户品牌信息")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertBranding( UpsertTenantBrandingDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertBrandingAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPut("settings")] [EndpointSummary("更新租户公开设置与功能开关")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertSettings( UpsertTenantSettingsDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertSettingsAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("theme-templates")] [EndpointSummary("查询可用租户主题模板")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetThemeTemplates(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetThemeTemplatesAsync(ResolveActor(), cancellationToken)); } [HttpGet("theme")] [EndpointSummary("查询租户当前主题与草稿")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetTheme(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetThemeAsync(ResolveActor(), cancellationToken)); } [HttpPost("theme/preview")] [EndpointSummary("生成租户主题草稿")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> PreviewTheme( PreviewTenantThemeDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.PreviewThemeAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("theme/publish")] [EndpointSummary("发布租户主题")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> PublishTheme( PublishTenantThemeDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.PublishThemeAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("domains")] [EndpointSummary("查询租户域名")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetDomains(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetDomainsAsync(ResolveActor(), cancellationToken)); } [HttpPost("domains")] [EndpointSummary("添加租户域名")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> CreateDomain( CreateTenantDomainDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.CreateDomainAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("auth-providers")] [EndpointSummary("查询租户登录 Provider 公开配置")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetAuthProviders(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetAuthProvidersAsync(ResolveActor(), cancellationToken)); } [HttpPut("auth-providers")] [EndpointSummary("新增或更新租户登录 Provider")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertAuthProvider( UpsertTenantAuthProviderDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertAuthProviderAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } private TenantAdminActor ResolveActor() { if (currentTenant.TenantId is null || currentUser.UserId is null) { throw new TenantAdminDirectException("Tenant admin actor was not resolved.", "tenant_admin_access_denied"); } var role = Enum.TryParse( currentTenant.Role?.Replace("_", string.Empty, StringComparison.Ordinal), ignoreCase: true, out var parsedRole) ? parsedRole : TenantRole.TenantAdmin; return new TenantAdminActor(currentTenant.TenantId.Value, currentUser.UserId.Value, role); } }