using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Api.Contracts; using Tiku.Api.Security; using Tiku.Application.Content; using Tiku.Application.Security; namespace Tiku.Api.Controllers; [ApiController] [Tags("平台端-公共内容 V2")] [Authorize(Policy = TikuPolicies.PlatformBackofficeBootstrap)] [Authorize(Policy = BackendPermissions.PlatformQuestionBankManage)] [Produces("application/json")] [Route("api/platform/content-v2")] public sealed class PlatformContentV2AuthoringController( IContentV2AuthoringService service, IContentReleaseCompiler releaseCompiler, IPlatformContentActorResolver actorResolver, ICurrentUser currentUser) : ControllerBase { [HttpPost("questions/duplicate-search")] [EndpointSummary("查询平台公共题范围内的精确和相似题")] public async Task SearchDuplicates( QuestionDuplicateSearchDto request, CancellationToken cancellationToken) { return await service.SearchDuplicatesAsync( await ActorAsync(cancellationToken), request.Draft.ToCommand(), request.Limit, cancellationToken); } [HttpPost("questions/execute")] [EndpointSummary("按审核决策录入平台公共题")] public async Task Execute( ExecuteQuestionAuthoringDto request, CancellationToken cancellationToken) { return await service.ExecuteAsync( await ActorAsync(cancellationToken), request.ToCommand(), cancellationToken); } [HttpPost("questions/{questionAssetId:guid}/revisions")] [EndpointSummary("为平台公共题创建不可变 Revision")] public async Task CreateRevision( Guid questionAssetId, CreateQuestionRevisionV2Dto request, CancellationToken cancellationToken) { return await service.CreateRevisionAsync( await ActorAsync(cancellationToken), questionAssetId, request.Draft.ToCommand(), request.PublishAsset, cancellationToken); } [HttpPost("questions/placements")] [EndpointSummary("把平台公共题放置到大纲并配置目标规则")] public async Task CreatePlacement( CreateQuestionPlacementDto request, CancellationToken cancellationToken) { return await service.CreatePlacementAsync( await ActorAsync(cancellationToken), request.ToCommand(), cancellationToken); } [HttpPost("imports/questions/preview")] [EndpointSummary("预览平台公共题批量导入")] public async Task PreviewImport( PreviewQuestionImportV2Dto request, CancellationToken cancellationToken) { return await service.PreviewImportAsync( await ActorAsync(cancellationToken), request.Rows.Select(row => (row.RowKey, row.Draft.ToCommand())).ToArray(), cancellationToken); } [HttpPost("imports/questions/execute")] [EndpointSummary("执行平台公共题批量导入决策")] public async Task ExecuteImport( ExecuteQuestionImportV2Dto request, CancellationToken cancellationToken) { return await service.ExecuteImportAsync( await ActorAsync(cancellationToken), request.Rows.Select(row => new ExecuteQuestionImportRow(row.RowKey, row.Command.ToCommand())).ToArray(), cancellationToken); } [HttpPost("releases/publish")] [EndpointSummary("校验并发布平台公共不可变 Release")] public async Task PublishRelease( PublishContentReleaseDto request, CancellationToken cancellationToken) { var actor = await ActorAsync(cancellationToken); return await releaseCompiler.PublishAsync( new PublishContentReleaseCommand(actor.TenantId, actor.UserId, request.CurriculumVersionId, request.Name), cancellationToken); } private Task ActorAsync(CancellationToken cancellationToken) { return currentUser.UserId is { } userId ? actorResolver.ResolveAsync(userId, cancellationToken) : throw new ContentV2Exception("platform_access_denied", "The platform actor was not resolved."); } }