using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Api.Contracts; using Tiku.Application.Profile; using Tiku.Application.Security; using Tiku.Infrastructure.Profile; namespace Tiku.Api.Controllers; [ApiController] [Authorize(Policy = TikuPolicies.CurrentTenantMember)] [Produces("application/json")] [Route("api/profile")] public sealed class ProfileController( IProfileService profileService, ICurrentUser currentUser, ICurrentTenant currentTenant) : ControllerBase { [HttpGet("me")] [EndpointSummary("获取当前学生资料")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> Me( [FromQuery] ProfileQueryDto query, CancellationToken cancellationToken) { return Ok(await profileService.GetMeAsync( ResolveActor(), new ProfileQuery(query.RecentLimit), cancellationToken)); } [HttpPatch("me")] [EndpointSummary("更新当前学生资料")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> UpdateMe( UpdateProfileDto request, CancellationToken cancellationToken) { return Ok(await profileService.UpdateMeAsync( ResolveActor(), request.ToCommand(), cancellationToken)); } [HttpGet("exam-countdowns")] [EndpointSummary("查询考试倒计时")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ExamCountdowns( [FromQuery] ProfileQueryDto query, CancellationToken cancellationToken) { return Ok(await profileService.GetExamCountdownsAsync( ResolveActor(), new ProfileQuery(Limit: query.Limit), cancellationToken)); } private ProfileActor ResolveActor() { if (currentTenant.TenantId is null || currentUser.UserId is null) { throw new ProfileException("Current profile actor was not resolved.", "profile_access_denied"); } return new ProfileActor(currentTenant.TenantId.Value, currentUser.UserId.Value); } }