using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Tiku.Application.Auth;
using Tiku.Domain.Tenancy;
namespace Tiku.Api.Contracts;
///
/// 手机号密码登录请求。
///
public sealed class PasswordLoginDto
{
[Required]
public AuthRealm? Realm { get; set; }
///
/// 平台控制域名登录时使用的租户代码;自定义域名登录可省略。
///
[StringLength(100)]
[Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")]
public string? TenantCode { get; set; }
///
/// 账号标识。tenant 可使用手机号,platform 可使用邮箱或用户名。
///
[StringLength(320)]
[Description("账号标识。tenant 可使用手机号,platform 可使用邮箱或用户名。")]
public string? Identifier { get; set; }
[StringLength(32)]
[Description("兼容手机号字段;新客户端应使用 identifier。")]
public string? Phone { get; set; }
///
/// 用户密码。
///
[Required]
[StringLength(128, MinimumLength = 10)]
[Description("用户密码。")]
public string Password { get; set; } = string.Empty;
}
///
/// 短信验证码登录请求。
///
public sealed class SmsLoginDto
{
[Required]
public AuthRealm? Realm { get; set; }
///
/// 平台控制域名登录时使用的租户代码;自定义域名登录可省略。
///
[StringLength(100)]
[Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")]
public string? TenantCode { get; set; }
///
/// 中国大陆手机号。
///
[Required]
[StringLength(32)]
[Description("中国大陆手机号。")]
public string Phone { get; set; } = string.Empty;
///
/// 收到的短信验证码。
///
[Required]
[StringLength(12, MinimumLength = 4)]
[Description("收到的短信验证码。")]
public string Code { get; set; } = string.Empty;
}
public sealed class SendSmsCodeDto
{
[Required]
public AuthRealm? Realm { get; set; }
[StringLength(100)]
public string? TenantCode { get; set; }
[Required]
[StringLength(32)]
public string Phone { get; set; } = string.Empty;
[StringLength(256)]
public string? DeviceId { get; set; }
}
///
/// OAuth code 登录请求。
///
public sealed class OAuthCodeDto
{
[Required]
public AuthRealm? Realm { get; set; }
///
/// 平台控制域名登录时使用的租户代码;自定义域名登录可省略。
///
[StringLength(100)]
[Description("平台控制域名登录时使用的租户代码;自定义域名登录可省略。")]
public string? TenantCode { get; set; }
///
/// OAuth 平台返回的一次性授权 code。
///
[Required]
[StringLength(512)]
[Description("OAuth 平台返回的一次性授权 code。")]
public string Code { get; set; } = string.Empty;
///
/// 客户端可提供的公开用户资料,不允许包含 token/secret。当前后端先保留模板字段,后续按业务需要逐步使用。
///
[Description("客户端可提供的公开用户资料,不允许包含 token/secret。当前后端先保留模板字段,后续按业务需要逐步使用。")]
public Dictionary? Profile { get; set; }
///
/// 微信用户资料语言,例如 zh_CN。当前微信小程序登录不会使用该字段。
///
[StringLength(20)]
[Description("微信用户资料语言,例如 zh_CN。当前微信小程序登录不会使用该字段。")]
public string? Lang { get; set; }
}
///
/// refresh token 请求。
///
public sealed class RefreshSessionDto
{
///
/// refresh token。服务端只存储哈希,明文只返回给客户端一次。
///
[Required]
[StringLength(2048)]
[Description("refresh token。服务端只存储哈希,明文只返回给客户端一次。")]
public string RefreshToken { get; set; } = string.Empty;
}
///
/// 登录成功后的用户、租户成员和令牌信息。
///
public sealed class AuthenticatedUserDto
{
///
/// 用户 ID。
///
public Guid UserId { get; init; }
///
/// 手机号。
///
public string? Phone { get; init; }
///
/// 邮箱。
///
public string? Email { get; init; }
///
/// 用户显示名称。
///
public string? Name { get; init; }
public AuthRealm Realm { get; init; }
///
/// 当前登录租户成员摘要。
///
public TenantMembershipSummary? Tenant { get; init; }
///
/// access token 和 refresh token。
///
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
};
}
}
public sealed class AuthenticationResultDto
{
public AuthenticationStatus Status { get; init; }
public AuthenticatedUserDto? User { get; init; }
public string? ChallengeToken { get; init; }
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
};
}
public sealed class MfaChallengeDto
{
[Required]
[StringLength(2048)]
public string ChallengeToken { get; set; } = string.Empty;
[StringLength(64)]
public string? Code { get; set; }
}
public sealed class MfaConfirmDto
{
public AuthenticationResultDto Authentication { get; init; } = default!;
public IReadOnlyList RecoveryCodes { get; init; } = [];
}
public sealed class RequiredPasswordChangeDto
{
[Required]
[StringLength(2048)]
public string ChallengeToken { get; set; } = string.Empty;
[Required]
[StringLength(128, MinimumLength = 10)]
public string NewPassword { get; set; } = string.Empty;
}