using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Application.Auth; using Tiku.Api.Contracts; namespace Tiku.Api.Controllers; [ApiController] [Route("api/auth")] [Produces("application/json")] public sealed class AuthController(IAuthService authService) : ControllerBase { [AllowAnonymous] [HttpPost("login/password")] [EndpointSummary("手机号密码登录")] [EndpointDescription("使用本地手机号和密码登录,签发 JWT access token 与数据库 refresh/session。")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> LoginWithPassword( [FromBody] PasswordLoginDto request, CancellationToken cancellationToken) { var result = await authService.LoginWithPasswordAsync( new PasswordLoginRequest( request.TenantId, request.Phone, request.Password, GetIpAddress(), Request.Headers.UserAgent.ToString()), cancellationToken); return Ok(AuthenticatedUserDto.FromApplication(result)); } [AllowAnonymous] [HttpPost("login/sms")] [EndpointSummary("短信验证码登录")] [EndpointDescription("校验已发送的登录用途短信验证码,成功后签发 JWT access token 与数据库 refresh/session。")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> LoginWithSms( [FromBody] SmsLoginDto request, CancellationToken cancellationToken) { var result = await authService.LoginWithSmsAsync( new SmsLoginRequest( request.TenantId, request.Phone, request.Code, GetIpAddress(), Request.Headers.UserAgent.ToString()), cancellationToken); return Ok(AuthenticatedUserDto.FromApplication(result)); } [AllowAnonymous] [HttpPost("oauth/wechat")] [EndpointSummary("微信网页 OAuth 登录")] [EndpointDescription("使用微信网页授权 code 换取 openid/unionid,upsert 用户身份并创建应用会话。")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)] public async Task> LoginWithWechatWeb( [FromBody] OAuthCodeDto request, CancellationToken cancellationToken) { var result = await authService.LoginWithWechatWebAsync( new WechatLoginRequest( request.TenantId, request.Code, GetIpAddress(), Request.Headers.UserAgent.ToString()), cancellationToken); return Ok(AuthenticatedUserDto.FromApplication(result)); } [AllowAnonymous] [HttpPost("oauth/wechat-miniapp")] [EndpointSummary("微信小程序登录")] [EndpointDescription("使用小程序 wx.login 返回的 code 换取 openid/session_key,upsert 用户身份并创建应用会话。")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)] public async Task> LoginWithWechatMiniApp( [FromBody] OAuthCodeDto request, CancellationToken cancellationToken) { var result = await authService.LoginWithWechatMiniAppAsync( new WechatLoginRequest( request.TenantId, request.Code, GetIpAddress(), Request.Headers.UserAgent.ToString()), cancellationToken); return Ok(AuthenticatedUserDto.FromApplication(result)); } [AllowAnonymous] [HttpPost("refresh")] [EndpointSummary("刷新登录会话")] [EndpointDescription("使用 refresh token 轮换数据库 session,并签发新的 access/refresh token。")] public async Task> Refresh( [FromBody] RefreshSessionDto request, CancellationToken cancellationToken) { var result = await authService.RefreshAsync( new RefreshSessionRequest( request.RefreshToken, GetIpAddress(), Request.Headers.UserAgent.ToString()), cancellationToken); return Ok(result); } [AllowAnonymous] [HttpPost("logout")] [EndpointSummary("退出登录")] [EndpointDescription("撤销 refresh token 对应的数据库 session;session 校验开启时,旧 access token 也会被拒绝。")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task Logout( [FromBody] RefreshSessionDto request, CancellationToken cancellationToken) { await authService.LogoutAsync( new LogoutSessionRequest(request.RefreshToken), cancellationToken); return NoContent(); } private string? GetIpAddress() { return HttpContext.Connection.RemoteIpAddress?.ToString(); } }