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] [Produces("application/json")] [Route("api/tenant-admin")] public sealed class TenantAdminDirectController( ITenantAdminDirectService tenantAdminService, ICurrentUser currentUser, ITenantContext currentTenant) : ControllerBase { [HttpGet("classes")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("查询租户班级")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetClasses( [FromQuery] TenantAdminClassQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetClassesAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("classes")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("新增或更新租户班级")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertClass( UpsertTenantAdminClassDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertClassAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("classes/disable")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("停用租户班级")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> DisableClass( [FromBody] Guid classId, CancellationToken cancellationToken) { return Ok(await tenantAdminService.DisableClassAsync(ResolveActor(), classId, cancellationToken)); } [HttpGet("classes/members")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [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")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [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")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("移除班级成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> RemoveClassMember( RemoveTenantAdminClassMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.RemoveClassMemberAsync(ResolveActor(), request.ClassMemberId, cancellationToken)); } [HttpGet("students")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("查询租户学生")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetStudents( [FromQuery] TenantAdminStudentQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetStudentsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("students")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("新增或更新租户学生档案")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertStudent( UpsertTenantAdminStudentDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertStudentAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("students/status")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("更新租户学生状态")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpdateStudentStatus( UpdateTenantAdminStudentStatusDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpdateStudentStatusAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("student-notes")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [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")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("新增或更新学生备注")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertStudentNote( UpsertTenantAdminStudentNoteDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertStudentNoteAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("student-followups")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [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")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("新增或更新学生跟进")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertStudentFollowup( UpsertTenantAdminStudentFollowupDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertStudentFollowupAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("members")] [Authorize(Policy = BackendPermissions.TenantStaffManage)] [EndpointSummary("查询租户成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetMembers( [FromQuery] TenantAdminMemberQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetMembersAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("members")] [Authorize(Policy = BackendPermissions.TenantStaffManage)] [EndpointSummary("新增或更新租户成员")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertMember( UpsertTenantAdminMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertMemberAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("members/disable")] [Authorize(Policy = BackendPermissions.TenantStaffManage)] [EndpointSummary("停用租户成员并撤销会话")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> DisableMember( DisableTenantAdminMemberDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.DisableMemberAsync(ResolveActor(), request.MembershipId, cancellationToken)); } [HttpGet("audit-logs")] [Authorize(Policy = BackendPermissions.TenantStaffManage)] [EndpointSummary("查询租户审计日志")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetAuditLogs( [FromQuery] TenantAdminAuditLogQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetAuditLogsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("branding")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("更新租户品牌信息")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertBranding( UpsertTenantBrandingDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertBrandingAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPut("settings")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("更新租户公开设置与功能开关")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertSettings( UpsertTenantSettingsDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertSettingsAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("theme-templates")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("查询可用租户主题模板")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetThemeTemplates(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetThemeTemplatesAsync(ResolveActor(), cancellationToken)); } [HttpGet("theme")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("查询租户当前主题与草稿")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetTheme(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetThemeAsync(ResolveActor(), cancellationToken)); } [HttpPost("theme/preview")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("生成租户主题草稿")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> PreviewTheme( PreviewTenantThemeDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.PreviewThemeAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("theme/publish")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("发布租户主题")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> PublishTheme( PublishTenantThemeDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.PublishThemeAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("domains")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("查询租户域名")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetDomains(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetDomainsAsync(ResolveActor(), cancellationToken)); } [HttpPost("domains")] [Authorize(Policy = BackendPermissions.TenantSettingsManage)] [EndpointSummary("添加租户域名")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> CreateDomain( CreateTenantDomainDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.CreateDomainAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("auth-providers")] [Authorize(Policy = BackendPermissions.TenantProviderManage)] [EndpointSummary("查询租户登录 Provider 公开配置")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetAuthProviders(CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetAuthProvidersAsync(ResolveActor(), cancellationToken)); } [HttpPut("auth-providers")] [Authorize(Policy = BackendPermissions.TenantProviderManage)] [EndpointSummary("新增或更新租户登录 Provider")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertAuthProvider( UpsertTenantIdentityProviderDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertAuthProviderAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("badges")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("查询租户勋章")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetBadges( [FromQuery] TenantAdminBadgeQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetBadgesAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("badges")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("新增或更新租户勋章")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertBadge( UpsertTenantAdminBadgeDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertBadgeAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("badge-grants")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("查询勋章发放记录")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetBadgeGrants( [FromQuery] TenantAdminBadgeGrantQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetBadgeGrantsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPost("badge-grants")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("向租户成员发放勋章")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GrantBadge( GrantTenantAdminBadgeDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GrantBadgeAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("notifications")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("查询用户站内通知")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetNotifications( [FromQuery] TenantAdminNotificationQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetNotificationsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("notifications")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("新增或更新用户站内通知")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertNotification( UpsertTenantAdminNotificationDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpsertNotificationAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("feedbacks")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("查询用户反馈")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetFeedbacks( [FromQuery] TenantAdminFeedbackQueryDto query, CancellationToken cancellationToken) { return Ok(await tenantAdminService.GetFeedbacksAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPost("feedbacks/status")] [Authorize(Policy = BackendPermissions.TenantStudentManage)] [EndpointSummary("处理用户反馈")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpdateFeedback( UpdateTenantAdminFeedbackDto request, CancellationToken cancellationToken) { return Ok(await tenantAdminService.UpdateFeedbackAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } private TenantAdminActor ResolveActor() { try { return TenantAdminActor.FromResolvedIdentity(currentTenant.TenantId, currentUser.UserId); } catch (InvalidOperationException exception) { throw new TenantAdminDirectException(exception.Message, "tenant_admin_access_denied"); } } }