using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Api.Contracts; using Tiku.Application.Assets; using Tiku.Application.Catalog; using Tiku.Application.Content; using Tiku.Application.Security; using Tiku.Domain.Catalog; using Tiku.Domain.Content; namespace Tiku.Api.Controllers; [ApiController] [Authorize(Policy = BackendPermissions.TenantContentManage)] [Produces("application/json")] [Route("api/tenant-content")] public sealed class TenantContentDirectController( IDirectContentService directContentService, ICurrentUser currentUser, ITenantContext currentTenant) : ControllerBase { [HttpPost("questions")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("创建题目及首个版本")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> CreateQuestion( DirectQuestionWriteDto request, CancellationToken cancellationToken) { return Ok(await directContentService.CreateQuestionAsync(ResolveActor(), request.ToCommand(createVersionDefault: true), cancellationToken)); } [HttpPatch("questions")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("更新题目并可选择创建新版本")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpdateQuestion( DirectQuestionWriteDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpdateQuestionAsync(ResolveActor(), request.ToCommand(createVersionDefault: false), cancellationToken)); } [HttpGet("vocabulary-units")] [EndpointSummary("查询管理侧词汇单元")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetVocabularyUnits( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetVocabularyUnitsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("vocabulary-units")] [EndpointSummary("新增或更新词汇单元")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertVocabularyUnit( DirectVocabularyUnitDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertVocabularyUnitAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("vocabulary-words")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询管理侧词汇")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetVocabularyWords( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetVocabularyWordsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("vocabulary-words")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("新增或更新词汇")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertVocabularyWord( DirectVocabularyWordDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertVocabularyWordAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("handbook-subjects")] [EndpointSummary("查询管理侧知识手册科目")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetHandbookSubjects( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetHandbookSubjectsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("handbook-subjects")] [EndpointSummary("新增或更新知识手册科目")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertHandbookSubject( DirectHandbookSubjectDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertHandbookSubjectAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("handbook-chapters")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询管理侧知识手册章节")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetHandbookChapters( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetHandbookChaptersAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("handbook-chapters")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("新增或更新知识手册章节")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertHandbookChapter( DirectHandbookChapterDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertHandbookChapterAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("handbook-entries")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询管理侧知识手册条目")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetHandbookEntries( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetHandbookEntriesAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("handbook-entries")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("新增或更新知识手册条目")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertHandbookEntry( DirectHandbookEntryDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertHandbookEntryAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("scoreline/schools")] [EndpointSummary("查询管理侧分数线院校")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetSchools( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetSchoolsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("scoreline/schools")] [EndpointSummary("新增或更新分数线院校")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertSchool( DirectSchoolDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertSchoolAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("scoreline/majors")] [EndpointSummary("查询管理侧分数线专业")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetMajors( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetMajorsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("scoreline/majors")] [EndpointSummary("新增或更新分数线专业")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertMajor( DirectMajorDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertMajorAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("scoreline/fields")] [EndpointSummary("查询管理侧分数线字段")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetScorelineFields( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetScorelineFieldsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("scoreline/fields")] [EndpointSummary("新增或更新动态分数线字段")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertScorelineField( DirectScorelineFieldDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertScorelineFieldAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("scoreline/records")] [EndpointSummary("查询管理侧分数线记录")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetScorelineRecords( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetScorelineRecordsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("scoreline/records")] [EndpointSummary("新增或更新分数线记录")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertScorelineRecord( DirectScorelineRecordDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertScorelineRecordAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("scoreline/years")] [EndpointSummary("查询分数线年份")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetScorelineYears( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetScorelineYearsAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpGet("scoreline/trend")] [EndpointSummary("查询分数线趋势摘要")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetScorelineTrend( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetScorelineTrendAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpGet("videos")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询租户视频解析")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetVideos( [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetVideosAsync(ResolveActor(), query.ToFilter(), cancellationToken)); } [HttpPut("videos")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("新增或更新视频解析")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertVideo( DirectVideoDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertVideoAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpPost("question-videos")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("绑定题目与解析视频")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> BindQuestionVideo( DirectQuestionVideoDto request, CancellationToken cancellationToken) { return Ok(await directContentService.BindQuestionVideoAsync(ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("operations/{kind}")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询运营内容")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetOperationContent( string kind, [FromQuery] DirectContentQueryDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetOperationContentAsync(ResolveActor(), kind, query.ToFilter(), cancellationToken)); } [HttpPut("operations/{kind}")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("新增或更新运营内容")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> UpsertOperationContent( string kind, DirectOperationContentDto request, CancellationToken cancellationToken) { return Ok(await directContentService.UpsertOperationContentAsync(ResolveActor(), kind, request.ToCommand(), cancellationToken)); } [HttpPost("imports/preview/{importType}")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("预览内容导入数据")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> PreviewImport( string importType, DirectImportDto request, CancellationToken cancellationToken) { return Ok(await directContentService.PreviewImportAsync(ResolveActor(), request.ToCommand(importType, dryRun: true), cancellationToken)); } [HttpPost("imports/{importType}")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("执行同步内容导入")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ExecuteImport( string importType, DirectImportDto request, CancellationToken cancellationToken) { return Ok(await directContentService.ExecuteImportAsync(ResolveActor(), request.ToCommand(importType, dryRun: false), cancellationToken)); } [HttpGet("imports/issues")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询内容导入问题明细")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> GetImportIssues( [FromQuery] DirectImportJobDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetImportIssuesAsync(ResolveActor(), query.JobId, cancellationToken)); } [HttpPost("imports/post-check")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("执行内容导入后完整性检查")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> RunImportPostCheck( DirectImportJobDto request, CancellationToken cancellationToken) { return Ok(await directContentService.RunImportPostCheckAsync(ResolveActor(), request.JobId, cancellationToken)); } [HttpGet("imports/post-check")] [Authorize(Policy = TikuPolicies.TenantContentManageAllScope)] [EndpointSummary("查询内容导入后检查状态")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetImportPostCheck( [FromQuery] DirectImportJobDto query, CancellationToken cancellationToken) { return Ok(await directContentService.GetImportPostCheckAsync(ResolveActor(), query.JobId, cancellationToken)); } private DirectContentActor ResolveActor() { if (currentTenant.TenantId is null || currentUser.UserId is null) { throw new ContentManagementException("Tenant content actor was not resolved.", "tenant_content_access_denied"); } return new DirectContentActor(currentTenant.TenantId.Value, currentUser.UserId.Value); } }