using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Application.Auth; using Tiku.Application.Security; using Tiku.Domain.Tenancy; namespace Tiku.Api.Controllers; [ApiController] [Tags("租户端-当前用户")] [Authorize(Policy = TikuPolicies.AuthenticatedUser)] [Route("api/tenant/me")] public sealed class MeController( ICurrentUser currentUser, IAuthSessionStore sessionStore, ICurrentIdentityQueryService identityQueries) : ControllerBase { [HttpGet] [EndpointSummary("获取当前登录用户")] [EndpointDescription("根据 Bearer Token 返回当前用户基础信息和活跃租户成员摘要。")] public async Task> Get(CancellationToken cancellationToken) { if (currentUser.UserId is null) return Unauthorized(); var user = await identityQueries.GetUserAsync(currentUser.UserId.Value, cancellationToken); if (user is null) return Unauthorized(); return Ok(new MeResponse( user.UserId, user.Phone, user.Email, user.Name, user.Tenants.Select(item => new TenantMembershipResponse( item.TenantId, item.TenantName, item.TenantSlug, item.Role, item.Status)).ToArray())); } [HttpGet("sessions")] [EndpointSummary("查询当前授权域的登录设备")] [ProducesResponseType>(StatusCodes.Status200OK)] public async Task>> Sessions( CancellationToken cancellationToken) { if (currentUser.UserId is not { } userId || currentUser.SessionId is not { } sessionId) return Unauthorized(); return Ok(await sessionStore.ListActiveAsync(userId, sessionId, cancellationToken)); } [HttpDelete("sessions/{sessionFamilyId:guid}")] [EndpointSummary("撤销其他设备的登录会话")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task RevokeSession( Guid sessionFamilyId, CancellationToken cancellationToken) { if (currentUser.UserId is not { } userId || currentUser.SessionId is not { } sessionId) return Unauthorized(); await sessionStore.RevokeOwnedFamilyAsync(userId, sessionId, sessionFamilyId, cancellationToken); return NoContent(); } } /// /// 当前用户基础信息和租户成员摘要。 /// /// 用户 ID。 /// 手机号。 /// 邮箱。 /// 用户显示名称。 /// 当前用户拥有的活跃租户成员列表。 public sealed record MeResponse( Guid UserId, string? Phone, string? Email, string? Name, IReadOnlyCollection Tenants); /// /// 用户在某个租户内的成员摘要。 /// /// 租户 ID。 /// 租户名称。 /// 租户编码。 /// 当前用户在该租户内的角色。 /// 租户成员状态。 public sealed record TenantMembershipResponse( Guid TenantId, string TenantName, string TenantSlug, TenantRole Role, MembershipStatus Status);