初始化

This commit is contained in:
2025-10-16 09:56:36 +08:00
commit de704db577
272 changed files with 37331 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class WritingAssistantRequestDto
{
[Required(ErrorMessage = "提示内容是必填项")]
[StringLength(1000, ErrorMessage = "提示内容长度不能超过1000个字符")]
public string Prompt { get; set; } = string.Empty;
[Required(ErrorMessage = "辅助类型是必填项")]
[EnumDataType(typeof(WritingAssistantType), ErrorMessage = "无效的辅助类型")]
public WritingAssistantType Type { get; set; }
[EnumDataType(typeof(WritingTone), ErrorMessage = "无效的语气类型")]
public WritingTone Tone { get; set; } = WritingTone.CASUAL;
[EnumDataType(typeof(WritingLength), ErrorMessage = "无效的长度类型")]
public WritingLength Length { get; set; } = WritingLength.MEDIUM;
[StringLength(500, ErrorMessage = "上下文信息长度不能超过500个字符")]
public string? Context { get; set; }
}
public class WritingAssistantResponseDto
{
public string Content { get; set; } = string.Empty;
public List<string> Suggestions { get; set; } = new();
public int EstimatedTime { get; set; } // 预计写作时间(分钟)
}
public class SentimentAnalysisRequestDto
{
[Required(ErrorMessage = "内容是必填项")]
[StringLength(2000, ErrorMessage = "内容长度不能超过2000个字符")]
public string Content { get; set; } = string.Empty;
}
public class SentimentAnalysisResponseDto
{
public SentimentType Sentiment { get; set; }
public double Confidence { get; set; } // 0-1 置信度
public List<EmotionScore> Emotions { get; set; } = new();
public List<string> Keywords { get; set; } = new();
public string Summary { get; set; } = string.Empty;
}
public class EmotionScore
{
public EmotionType Type { get; set; }
public double Score { get; set; }
}
public class FuturePredictionRequestDto
{
[Required(ErrorMessage = "预测内容是必填项")]
[StringLength(1000, ErrorMessage = "预测内容长度不能超过1000个字符")]
public string Content { get; set; } = string.Empty;
[Required(ErrorMessage = "预测类型是必填项")]
[EnumDataType(typeof(PredictionType), ErrorMessage = "无效的预测类型")]
public PredictionType Type { get; set; }
[Range(1, 365, ErrorMessage = "预测天数必须在1-365之间")]
public int DaysAhead { get; set; } = 30;
}
public class FuturePredictionResponseDto
{
public string Prediction { get; set; } = string.Empty;
public double Confidence { get; set; } // 0-1 置信度
public List<string> Factors { get; set; } = new();
public List<string> Suggestions { get; set; } = new();
}
// 枚举定义
public enum WritingAssistantType
{
OUTLINE = 0,
DRAFT = 1,
COMPLETE = 2
}
public enum WritingTone
{
FORMAL = 0,
CASUAL = 1,
EMOTIONAL = 2,
INSPIRATIONAL = 3
}
public enum WritingLength
{
SHORT = 0,
MEDIUM = 1,
LONG = 2
}
public enum SentimentType
{
POSITIVE = 0,
NEUTRAL = 1,
NEGATIVE = 2,
MIXED = 3
}
public enum EmotionType
{
HAPPY = 0,
SAD = 1,
HOPEFUL = 2,
NOSTALGIC = 3,
EXCITED = 4,
ANXIOUS = 5,
GRATEFUL = 6,
CONFUSED = 7
}
public enum PredictionType
{
CAREER = 0,
RELATIONSHIP = 1,
HEALTH = 2,
FINANCIAL = 3,
PERSONAL_GROWTH = 4
}
}

View File

@@ -0,0 +1,60 @@
namespace FutureMailAPI.DTOs
{
public class ApiResponse<T>
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public T? Data { get; set; }
public List<string>? Errors { get; set; }
public static ApiResponse<T> SuccessResult(T data, string message = "操作成功")
{
return new ApiResponse<T>
{
Success = true,
Message = message,
Data = data
};
}
public static ApiResponse<T> ErrorResult(string message, List<string>? errors = null)
{
return new ApiResponse<T>
{
Success = false,
Message = message,
Errors = errors
};
}
}
public class PagedResponse<T>
{
public IEnumerable<T> Items { get; set; } = new List<T>();
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }
public int TotalPages { get; set; }
public bool HasPreviousPage { get; set; }
public bool HasNextPage { get; set; }
public PagedResponse(IEnumerable<T> items, int pageIndex, int pageSize, int totalCount)
{
Items = items;
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = totalCount;
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
HasPreviousPage = pageIndex > 1;
HasNextPage = pageIndex < TotalPages;
}
}
public class AuthResponseDto
{
public string Token { get; set; } = string.Empty;
public string? RefreshToken { get; set; }
public DateTime Expires { get; set; }
public UserResponseDto User { get; set; } = new();
}
}

View File

@@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Http;
namespace FutureMailAPI.DTOs
{
public class FileUploadDto
{
public string FileName { get; set; } = string.Empty;
public string ContentType { get; set; } = string.Empty;
public long FileSize { get; set; }
public string FilePath { get; set; } = string.Empty;
public string FileUrl { get; set; } = string.Empty;
public string ThumbnailUrl { get; set; } = string.Empty;
public AttachmentType Type { get; set; }
}
public class FileUploadRequestDto
{
public AttachmentType Type { get; set; }
public string? Category { get; set; } // 分类avatar, attachment等
}
public class FileUploadWithFileRequestDto : FileUploadRequestDto
{
public IFormFile File { get; set; } = null!;
}
public class FileUploadResponseDto
{
public string FileId { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
public string FileUrl { get; set; } = string.Empty;
public string ThumbnailUrl { get; set; } = string.Empty;
public long FileSize { get; set; }
public string ContentType { get; set; } = string.Empty;
public AttachmentType Type { get; set; }
public DateTime UploadedAt { get; set; }
}
public enum AttachmentType
{
IMAGE,
VOICE,
VIDEO,
DOCUMENT,
OTHER
}
}

View File

@@ -0,0 +1,115 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class SentMailCreateDto
{
[Required(ErrorMessage = "标题是必填项")]
[StringLength(200, ErrorMessage = "标题长度不能超过200个字符")]
public string Title { get; set; } = string.Empty;
[Required(ErrorMessage = "内容是必填项")]
public string Content { get; set; } = string.Empty;
// 收件人类型: 0-自己, 1-指定用户, 2-公开时间胶囊
[Required(ErrorMessage = "收件人类型是必填项")]
[Range(0, 2, ErrorMessage = "收件人类型必须是0、1或2")]
public int RecipientType { get; set; }
// 如果是指定用户,提供用户邮箱
public string? RecipientEmail { get; set; }
[Required(ErrorMessage = "投递时间是必填项")]
public DateTime DeliveryTime { get; set; }
// 触发条件类型: 0-时间, 1-地点, 2-事件
[Required(ErrorMessage = "触发条件类型是必填项")]
[Range(0, 2, ErrorMessage = "触发条件类型必须是0、1或2")]
public int TriggerType { get; set; } = 0;
// 触发条件详情(JSON格式)
public string? TriggerDetails { get; set; }
// 附件路径(JSON数组格式)
public string? Attachments { get; set; }
// 是否加密
public bool IsEncrypted { get; set; } = false;
// 加密密钥(如果使用端到端加密)
public string? EncryptionKey { get; set; }
// 邮件主题/胶囊皮肤
[StringLength(50, ErrorMessage = "主题长度不能超过50个字符")]
public string? Theme { get; set; }
}
public class SentMailUpdateDto
{
[StringLength(200, ErrorMessage = "标题长度不能超过200个字符")]
public string? Title { get; set; }
public string? Content { get; set; }
public DateTime? DeliveryTime { get; set; }
public string? TriggerDetails { get; set; }
public string? Attachments { get; set; }
public string? Theme { get; set; }
}
public class SentMailResponseDto
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public int SenderId { get; set; }
public string SenderUsername { get; set; } = string.Empty;
public int RecipientType { get; set; }
public int? RecipientId { get; set; }
public string? RecipientUsername { get; set; }
public DateTime SentAt { get; set; }
public DateTime DeliveryTime { get; set; }
public int Status { get; set; }
public int TriggerType { get; set; }
public string? TriggerDetails { get; set; }
public string? Attachments { get; set; }
public bool IsEncrypted { get; set; }
public string? Theme { get; set; }
// 计算属性
public string StatusText { get; set; } = string.Empty;
public string RecipientTypeText { get; set; } = string.Empty;
public string TriggerTypeText { get; set; } = string.Empty;
public int DaysUntilDelivery { get; set; }
}
public class ReceivedMailResponseDto
{
public int Id { get; set; }
public int SentMailId { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public string SenderUsername { get; set; } = string.Empty;
public DateTime SentAt { get; set; }
public DateTime ReceivedAt { get; set; }
public bool IsRead { get; set; }
public DateTime? ReadAt { get; set; }
public bool IsReplied { get; set; }
public int? ReplyMailId { get; set; }
public string? Theme { get; set; }
}
public class MailListQueryDto
{
public int PageIndex { get; set; } = 1;
public int PageSize { get; set; } = 10;
public int? Status { get; set; }
public int? RecipientType { get; set; }
public string? Keyword { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
}

View File

@@ -0,0 +1,70 @@
namespace FutureMailAPI.DTOs
{
public class NotificationDeviceDto
{
public string DeviceId { get; set; } = string.Empty;
public string DeviceType { get; set; } = string.Empty; // iOS, Android, Web
public string DeviceToken { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public DateTime RegisteredAt { get; set; }
public DateTime? LastActiveAt { get; set; }
}
public class NotificationSettingsDto
{
public bool EmailDelivery { get; set; } = true;
public bool PushNotification { get; set; } = true;
public bool InAppNotification { get; set; } = true;
public bool DeliveryReminder { get; set; } = true;
public bool ReceivedNotification { get; set; } = true;
public bool SystemUpdates { get; set; } = false;
public string QuietHoursStart { get; set; } = "22:00";
public string QuietHoursEnd { get; set; } = "08:00";
public bool EnableQuietHours { get; set; } = false;
}
public class NotificationMessageDto
{
public string Id { get; set; } = string.Empty;
public int UserId { get; set; }
public string Title { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; // DELIVERY, RECEIVED, SYSTEM, REMINDER
public string RelatedEntityId { get; set; } = string.Empty; // 邮件ID等
public bool IsRead { get; set; } = false;
public DateTime CreatedAt { get; set; }
public DateTime? ReadAt { get; set; }
public Dictionary<string, object> Data { get; set; } = new();
}
public class NotificationDeviceRequestDto
{
public string DeviceType { get; set; } = string.Empty;
public string DeviceToken { get; set; } = string.Empty;
}
public class NotificationDeviceResponseDto
{
public string DeviceId { get; set; } = string.Empty;
public string DeviceType { get; set; } = string.Empty;
public bool IsActive { get; set; }
public DateTime RegisteredAt { get; set; }
}
public class NotificationListQueryDto
{
public bool UnreadOnly { get; set; } = false;
public string? Type { get; set; }
public int Page { get; set; } = 1;
public int Size { get; set; } = 20;
}
public class NotificationListResponseDto
{
public List<NotificationMessageDto> Notifications { get; set; } = new();
public int Total { get; set; }
public int UnreadCount { get; set; }
public int Page { get; set; }
public int Size { get; set; }
}
}

View File

@@ -0,0 +1,105 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class OAuthAuthorizationRequestDto
{
[Required]
public string ResponseType { get; set; } = "code"; // code for authorization code flow
[Required]
public string ClientId { get; set; } = string.Empty;
[Required]
public string RedirectUri { get; set; } = string.Empty;
public string Scope { get; set; } = "read write"; // Default scopes
public string State { get; set; } = string.Empty; // CSRF protection
}
public class OAuthTokenRequestDto
{
[Required]
public string GrantType { get; set; } = string.Empty; // authorization_code, refresh_token, client_credentials, password
public string Code { get; set; } = string.Empty; // For authorization_code grant
public string RefreshToken { get; set; } = string.Empty; // For refresh_token grant
public string Username { get; set; } = string.Empty; // For password grant
public string Password { get; set; } = string.Empty; // For password grant
[Required]
public string ClientId { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty; // Optional for public clients
public string RedirectUri { get; set; } = string.Empty; // Required for authorization_code grant
public string Scope { get; set; } = string.Empty; // Optional, defaults to requested scopes
}
public class OAuthTokenResponseDto
{
public string AccessToken { get; set; } = string.Empty;
public string TokenType { get; set; } = "Bearer";
public int ExpiresIn { get; set; } // Seconds until expiration
public string RefreshToken { get; set; } = string.Empty;
public string Scope { get; set; } = string.Empty;
}
public class OAuthAuthorizationResponseDto
{
public string Code { get; set; } = string.Empty;
public string State { get; set; } = string.Empty;
}
public class OAuthClientDto
{
public int Id { get; set; }
public string ClientId { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string[] RedirectUris { get; set; } = Array.Empty<string>();
public string[] Scopes { get; set; } = Array.Empty<string>();
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
public class OAuthClientCreateDto
{
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[Required]
public string[] RedirectUris { get; set; } = Array.Empty<string>();
[Required]
public string[] Scopes { get; set; } = Array.Empty<string>();
}
public class OAuthClientSecretDto
{
public string ClientId { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty;
}
public class OAuthLoginDto
{
[Required]
public string UsernameOrEmail { get; set; } = string.Empty;
[Required]
public string Password { get; set; } = string.Empty;
[Required]
public string ClientId { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty; // Optional for public clients
public string Scope { get; set; } = "read write"; // Default scopes
}
}

View File

@@ -0,0 +1,115 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class TimelineQueryDto
{
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public TimelineType Type { get; set; } = TimelineType.ALL;
}
public class TimelineResponseDto
{
public List<TimelineDateDto> Timeline { get; set; } = new();
}
public class TimelineDateDto
{
public string Date { get; set; } = string.Empty; // YYYY-MM-DD格式
public List<TimelineEventDto> Events { get; set; } = new();
}
public class TimelineEventDto
{
public TimelineEventType Type { get; set; }
public int MailId { get; set; }
public string Title { get; set; } = string.Empty;
public string Time { get; set; } = string.Empty; // HH:mm格式
public UserInfoDto WithUser { get; set; } = new();
public string Emotion { get; set; } = string.Empty;
}
public class StatisticsResponseDto
{
public int TotalSent { get; set; }
public int TotalReceived { get; set; }
public int TimeTravelDuration { get; set; } // 总时间旅行时长(天)
public string MostFrequentRecipient { get; set; } = string.Empty;
public int MostCommonYear { get; set; }
public List<KeywordCloudDto> KeywordCloud { get; set; } = new();
public List<MonthlyStatsDto> MonthlyStats { get; set; } = new();
}
public class KeywordCloudDto
{
public string Word { get; set; } = string.Empty;
public int Count { get; set; }
public int Size { get; set; } // 用于显示的大小
}
public class MonthlyStatsDto
{
public string Month { get; set; } = string.Empty; // YYYY-MM格式
public int Sent { get; set; }
public int Received { get; set; }
}
public class UserInfoDto
{
public int UserId { get; set; }
public string Username { get; set; } = string.Empty;
public string? Avatar { get; set; }
}
public class SubscriptionResponseDto
{
public SubscriptionPlan Plan { get; set; }
public int RemainingMails { get; set; }
public long MaxAttachmentSize { get; set; } // 字节
public SubscriptionFeaturesDto Features { get; set; } = new();
public DateTime? ExpireDate { get; set; }
}
public class SubscriptionFeaturesDto
{
public bool AdvancedTriggers { get; set; }
public bool CustomCapsules { get; set; }
public bool AIAssistant { get; set; }
public bool UnlimitedStorage { get; set; }
public bool PriorityDelivery { get; set; }
}
public class UserProfileResponseDto
{
public int Id { get; set; }
public string Username { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string? Nickname { get; set; }
public string? Avatar { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? LastLoginAt { get; set; }
public SubscriptionResponseDto Subscription { get; set; } = new();
}
// 枚举定义
public enum TimelineType
{
ALL = 0,
SENT = 1,
RECEIVED = 2
}
public enum TimelineEventType
{
SENT = 0,
RECEIVED = 1
}
public enum SubscriptionPlan
{
FREE = 0,
PREMIUM = 1,
PRO = 2
}
}

View File

@@ -0,0 +1,130 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class TimeCapsuleCreateDto
{
[Required(ErrorMessage = "邮件ID是必填项")]
public int SentMailId { get; set; }
// 胶囊位置信息(X, Y, Z坐标)
public double PositionX { get; set; } = 0;
public double PositionY { get; set; } = 0;
public double PositionZ { get; set; } = 0;
// 胶囊大小
[Range(0.1, 5.0, ErrorMessage = "胶囊大小必须在0.1-5.0之间")]
public double Size { get; set; } = 1.0;
// 胶囊颜色
[StringLength(20, ErrorMessage = "颜色代码长度不能超过20个字符")]
public string? Color { get; set; }
// 胶囊透明度
[Range(0.1, 1.0, ErrorMessage = "透明度必须在0.1-1.0之间")]
public double Opacity { get; set; } = 1.0;
// 胶囊旋转角度
[Range(0, 360, ErrorMessage = "旋转角度必须在0-360度之间")]
public double Rotation { get; set; } = 0;
// 胶囊类型: 0-普通, 1-特殊, 2-限时
[Range(0, 2, ErrorMessage = "胶囊类型必须是0、1或2")]
public int Type { get; set; } = 0;
}
public class TimeCapsuleUpdateDto
{
// 胶囊位置信息(X, Y, Z坐标)
public double? PositionX { get; set; }
public double? PositionY { get; set; }
public double? PositionZ { get; set; }
// 胶囊大小
[Range(0.1, 5.0, ErrorMessage = "胶囊大小必须在0.1-5.0之间")]
public double? Size { get; set; }
// 胶囊颜色
[StringLength(20, ErrorMessage = "颜色代码长度不能超过20个字符")]
public string? Color { get; set; }
// 胶囊透明度
[Range(0.1, 1.0, ErrorMessage = "透明度必须在0.1-1.0之间")]
public double? Opacity { get; set; }
// 胶囊旋转角度
[Range(0, 360, ErrorMessage = "旋转角度必须在0-360度之间")]
public double? Rotation { get; set; }
}
public class TimeCapsuleResponseDto
{
public int Id { get; set; }
public int UserId { get; set; }
public int SentMailId { get; set; }
public string MailTitle { get; set; } = string.Empty;
public double PositionX { get; set; }
public double PositionY { get; set; }
public double PositionZ { get; set; }
public double Size { get; set; }
public string? Color { get; set; }
public double Opacity { get; set; }
public double Rotation { get; set; }
public int Status { get; set; }
public int Type { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime DeliveryTime { get; set; }
// 计算属性
public string StatusText { get; set; } = string.Empty;
public string TypeText { get; set; } = string.Empty;
public int DaysUntilDelivery { get; set; }
public double DistanceFromNow { get; set; } // 距离"现在"的距离用于3D可视化
}
public class TimeCapsuleListQueryDto
{
public int PageIndex { get; set; } = 1;
public int PageSize { get; set; } = 50;
public int? Status { get; set; }
public int? Type { get; set; }
public bool? IncludeExpired { get; set; } = false;
}
public class TimeCapsuleViewResponseDto
{
public List<TimeCapsuleViewDto> Capsules { get; set; } = new List<TimeCapsuleViewDto>();
public string Scene { get; set; } = "SPACE"; // 场景类型: SPACE | OCEAN
public string Background { get; set; } = string.Empty; // 背景配置
}
public class TimeCapsuleViewDto
{
public int CapsuleId { get; set; }
public int MailId { get; set; }
public string Title { get; set; } = string.Empty;
public DateTime SendTime { get; set; }
public DateTime DeliveryTime { get; set; }
public double Progress { get; set; } // 0-1 的进度
public TimeCapsulePosition Position { get; set; } = new TimeCapsulePosition();
public string Style { get; set; } = string.Empty;
public double GlowIntensity { get; set; } // 发光强度
}
public class TimeCapsulePosition
{
public double X { get; set; } // 0-1 相对位置
public double Y { get; set; }
public double Z { get; set; }
}
public class TimeCapsuleStyleUpdateDto
{
[Required(ErrorMessage = "样式是必填项")]
[StringLength(50, ErrorMessage = "样式长度不能超过50个字符")]
public string Style { get; set; } = string.Empty;
[Range(0.0, 1.0, ErrorMessage = "发光强度必须在0.0-1.0之间")]
public double? GlowIntensity { get; set; }
}
}

View File

@@ -0,0 +1,58 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class UserRegisterDto
{
[Required(ErrorMessage = "用户名是必填项")]
[StringLength(100, MinimumLength = 3, ErrorMessage = "用户名长度必须在3-100个字符之间")]
public string Username { get; set; } = string.Empty;
[Required(ErrorMessage = "邮箱是必填项")]
[EmailAddress(ErrorMessage = "请输入有效的邮箱地址")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "密码是必填项")]
[StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度必须在6-100个字符之间")]
public string Password { get; set; } = string.Empty;
[StringLength(100, ErrorMessage = "昵称长度不能超过100个字符")]
public string? Nickname { get; set; }
}
public class UserResponseDto
{
public int Id { get; set; }
public string Username { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string? Nickname { get; set; }
public string? Avatar { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? LastLoginAt { get; set; }
}
public class UserUpdateDto
{
[StringLength(100, ErrorMessage = "昵称长度不能超过100个字符")]
public string? Nickname { get; set; }
[StringLength(500, ErrorMessage = "头像URL长度不能超过500个字符")]
public string? Avatar { get; set; }
}
public class ChangePasswordDto
{
[Required(ErrorMessage = "当前密码是必填项")]
public string CurrentPassword { get; set; } = string.Empty;
[Required(ErrorMessage = "新密码是必填项")]
[StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度必须在6-100个字符之间")]
public string NewPassword { get; set; } = string.Empty;
}
public class RefreshTokenRequestDto
{
[Required(ErrorMessage = "令牌是必填项")]
public string Token { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace FutureMailAPI.DTOs
{
public class UserLoginDto
{
[Required(ErrorMessage = "用户名或邮箱是必填项")]
public string UsernameOrEmail { get; set; } = string.Empty;
[Required(ErrorMessage = "密码是必填项")]
public string Password { get; set; } = string.Empty;
}
}