271 lines
6.8 KiB
C#
271 lines
6.8 KiB
C#
using System.ComponentModel;
|
||
using System.ComponentModel.DataAnnotations;
|
||
using Tiku.Application.Auth;
|
||
using Tiku.Domain.Tenancy;
|
||
|
||
namespace Tiku.Api.Contracts;
|
||
|
||
/// <summary>
|
||
/// PasswordLogin请求 DTO。
|
||
/// </summary>
|
||
public sealed class PasswordLoginDto
|
||
{
|
||
/// <summary>
|
||
/// 认证域。
|
||
/// </summary>
|
||
[Required]
|
||
public AuthRealm? Realm { get; set; }
|
||
/// <summary>
|
||
/// 租户编码。
|
||
/// </summary>
|
||
[StringLength(100)]
|
||
[Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")]
|
||
public string? TenantCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// 账号标识。
|
||
/// </summary>
|
||
[StringLength(320)]
|
||
[Description("账号标识。tenant 可使用手机号,platform 可使用邮箱或用户名。")]
|
||
public string? Identifier { get; set; }
|
||
|
||
/// <summary>
|
||
/// 兼容手机号字段;新客户端应使用 identifier。
|
||
/// </summary>
|
||
[StringLength(32)]
|
||
[Description("兼容手机号字段;新客户端应使用 identifier。")]
|
||
public string? Phone { get; set; }
|
||
|
||
/// <summary>
|
||
/// 密码。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(128, MinimumLength = 8)]
|
||
[Description("用户密码。")]
|
||
public string Password { get; set; } = string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// SmsLogin请求 DTO。
|
||
/// </summary>
|
||
public sealed class SmsLoginDto
|
||
{
|
||
/// <summary>
|
||
/// 认证域。
|
||
/// </summary>
|
||
[Required]
|
||
public AuthRealm? Realm { get; set; }
|
||
/// <summary>
|
||
/// 租户编码。
|
||
/// </summary>
|
||
[StringLength(100)]
|
||
[Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")]
|
||
public string? TenantCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// 手机号。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(32)]
|
||
[Description("中国大陆手机号。")]
|
||
public string Phone { get; set; } = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 编码。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(12, MinimumLength = 4)]
|
||
[Description("收到的短信验证码。")]
|
||
public string Code { get; set; } = string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送短信验证码请求。
|
||
/// </summary>
|
||
public sealed class SendSmsCodeDto
|
||
{
|
||
/// <summary>
|
||
/// 认证域。
|
||
/// </summary>
|
||
[Required]
|
||
public AuthRealm? Realm { get; set; }
|
||
|
||
/// <summary>
|
||
/// 租户编码。
|
||
/// </summary>
|
||
[StringLength(100)]
|
||
public string? TenantCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// 手机号。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(32)]
|
||
public string Phone { get; set; } = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 设备 ID。
|
||
/// </summary>
|
||
[StringLength(256)]
|
||
public string? DeviceId { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// O认证编码请求 DTO。
|
||
/// </summary>
|
||
public sealed class OAuthCodeDto
|
||
{
|
||
/// <summary>
|
||
/// 认证域。
|
||
/// </summary>
|
||
[Required]
|
||
public AuthRealm? Realm { get; set; }
|
||
/// <summary>
|
||
/// 租户编码。
|
||
/// </summary>
|
||
[StringLength(100)]
|
||
[Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")]
|
||
public string? TenantCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// 编码。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(512)]
|
||
[Description("OAuth 平台返回的一次性授权 code。")]
|
||
public string Code { get; set; } = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 公开用户资料。
|
||
/// </summary>
|
||
[Description("客户端可提供的公开用户资料,不允许包含 token/secret。当前后端先保留模板字段,后续按业务需要逐步使用。")]
|
||
public Dictionary<string, object?>? Profile { get; set; }
|
||
|
||
/// <summary>
|
||
/// 语言。
|
||
/// </summary>
|
||
[StringLength(20)]
|
||
[Description("微信用户资料语言,例如 zh_CN。当前微信小程序登录不会使用该字段。")]
|
||
public string? Lang { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新会话请求 DTO。
|
||
/// </summary>
|
||
public sealed class RefreshSessionDto
|
||
{
|
||
/// <summary>
|
||
/// 刷新令牌。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(2048)]
|
||
[Description("refresh token。服务端只存储哈希,明文只返回给客户端一次。")]
|
||
public string RefreshToken { get; set; } = string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Authenticated用户请求 DTO。
|
||
/// </summary>
|
||
public sealed class AuthenticatedUserDto
|
||
{
|
||
/// <summary>
|
||
/// 用户 ID。
|
||
/// </summary>
|
||
public Guid UserId { get; init; }
|
||
|
||
/// <summary>
|
||
/// 手机号。
|
||
/// </summary>
|
||
public string? Phone { get; init; }
|
||
|
||
/// <summary>
|
||
/// 邮箱。
|
||
/// </summary>
|
||
public string? Email { get; init; }
|
||
|
||
/// <summary>
|
||
/// 名称。
|
||
/// </summary>
|
||
public string? Name { get; init; }
|
||
|
||
/// <summary>
|
||
/// 认证域。
|
||
/// </summary>
|
||
public AuthRealm Realm { get; init; }
|
||
|
||
/// <summary>
|
||
/// 租户成员摘要。
|
||
/// </summary>
|
||
public TenantMembershipSummary? Tenant { get; init; }
|
||
|
||
/// <summary>
|
||
/// 访问令牌和刷新令牌。
|
||
/// </summary>
|
||
public AuthTokenPair Tokens { get; init; } = default!;
|
||
|
||
public static AuthenticatedUserDto FromApplication(AuthenticatedUser user)
|
||
{
|
||
return new AuthenticatedUserDto
|
||
{
|
||
UserId = user.UserId,
|
||
Phone = user.Phone,
|
||
Email = user.Email,
|
||
Name = user.Name,
|
||
Realm = user.Realm,
|
||
Tenant = user.Tenant,
|
||
Tokens = user.Tokens
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登录认证结果。
|
||
/// </summary>
|
||
public sealed class AuthenticationResultDto
|
||
{
|
||
/// <summary>
|
||
/// 状态。
|
||
/// </summary>
|
||
public AuthenticationStatus Status { get; init; }
|
||
/// <summary>
|
||
/// 用户信息。
|
||
/// </summary>
|
||
public AuthenticatedUserDto? User { get; init; }
|
||
/// <summary>
|
||
/// 挑战令牌。
|
||
/// </summary>
|
||
public string? ChallengeToken { get; init; }
|
||
/// <summary>
|
||
/// 挑战令牌过期时间。
|
||
/// </summary>
|
||
public DateTimeOffset? ChallengeExpiresAt { get; init; }
|
||
|
||
public static AuthenticationResultDto FromApplication(AuthenticationResult result) => new()
|
||
{
|
||
Status = result.Status,
|
||
User = result.User is null ? null : AuthenticatedUserDto.FromApplication(result.User),
|
||
ChallengeToken = result.ChallengeToken,
|
||
ChallengeExpiresAt = result.ChallengeExpiresAt
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 首次登录必改密码请求。
|
||
/// </summary>
|
||
public sealed class RequiredPasswordChangeDto
|
||
{
|
||
/// <summary>
|
||
/// 挑战令牌。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(2048)]
|
||
public string ChallengeToken { get; set; } = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 新密码。
|
||
/// </summary>
|
||
[Required]
|
||
[StringLength(128, MinimumLength = 8)]
|
||
public string NewPassword { get; set; } = string.Empty;
|
||
}
|