/** * 登录业务数据传输对象 * * 功能描述: * - 定义登录相关API的请求数据结构 * - 提供数据验证规则和错误提示 * - 确保API接口的数据格式一致性 * * 职责分离: * - 专注于数据结构定义和验证规则 * - 提供Swagger文档生成支持 * - 确保类型安全和数据完整性 * * 最近修改: * - 2026-01-07: 代码规范优化 - 文件夹扁平化,移除单文件文件夹结构 * - 2026-01-07: 代码规范优化 - 更新注释规范,修正作者信息 * * @author moyin * @version 1.0.2 * @since 2025-12-17 * @lastModified 2026-01-07 */ import { IsString, IsEmail, IsPhoneNumber, IsNotEmpty, Length, IsOptional, Matches, IsNumberString } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; /** * 登录请求DTO */ export class LoginDto { /** * 登录标识符 * 支持用户名、邮箱或手机号登录 */ @ApiProperty({ description: '登录标识符,支持用户名、邮箱或手机号', example: 'testuser', minLength: 1, maxLength: 100 }) @IsString({ message: '登录标识符必须是字符串' }) @IsNotEmpty({ message: '登录标识符不能为空' }) @Length(1, 100, { message: '登录标识符长度需在1-100字符之间' }) identifier: string; /** * 密码 */ @ApiProperty({ description: '用户密码', example: 'password123', minLength: 1, maxLength: 128 }) @IsString({ message: '密码必须是字符串' }) @IsNotEmpty({ message: '密码不能为空' }) @Length(1, 128, { message: '密码长度需在1-128字符之间' }) password: string; } /** * 注册请求DTO */ export class RegisterDto { /** * 用户名 */ @ApiProperty({ description: '用户名,只能包含字母、数字和下划线', example: 'testuser', minLength: 1, maxLength: 50, pattern: '^[a-zA-Z0-9_]+$' }) @IsString({ message: '用户名必须是字符串' }) @IsNotEmpty({ message: '用户名不能为空' }) @Length(1, 50, { message: '用户名长度需在1-50字符之间' }) @Matches(/^[a-zA-Z0-9_]+$/, { message: '用户名只能包含字母、数字和下划线' }) username: string; /** * 密码 */ @ApiProperty({ description: '密码,必须包含字母和数字,长度8-128字符', example: 'password123', minLength: 8, maxLength: 128 }) @IsString({ message: '密码必须是字符串' }) @IsNotEmpty({ message: '密码不能为空' }) @Length(8, 128, { message: '密码长度需在8-128字符之间' }) @Matches(/^(?=.*[a-zA-Z])(?=.*\d)/, { message: '密码必须包含字母和数字' }) password: string; /** * 昵称 */ @ApiProperty({ description: '用户昵称', example: '测试用户', minLength: 1, maxLength: 50 }) @IsString({ message: '昵称必须是字符串' }) @IsNotEmpty({ message: '昵称不能为空' }) @Length(1, 50, { message: '昵称长度需在1-50字符之间' }) nickname: string; /** * 邮箱(可选) */ @ApiProperty({ description: '邮箱地址(可选)', example: 'test@example.com', required: false }) @IsOptional() @IsEmail({}, { message: '邮箱格式不正确' }) email?: string; /** * 手机号(可选) */ @ApiProperty({ description: '手机号码(可选)', example: '+8613800138000', required: false }) @IsOptional() @IsPhoneNumber(null, { message: '手机号格式不正确' }) phone?: string; /** * 邮箱验证码(当提供邮箱时必填) */ @ApiProperty({ description: '邮箱验证码,当提供邮箱时必填', example: '123456', pattern: '^\\d{6}$', required: false }) @IsOptional() @IsString({ message: '验证码必须是字符串' }) @Matches(/^\d{6}$/, { message: '验证码必须是6位数字' }) email_verification_code?: string; } /** * GitHub OAuth登录请求DTO */ export class GitHubOAuthDto { /** * GitHub用户ID */ @ApiProperty({ description: 'GitHub用户ID', example: '12345678', minLength: 1, maxLength: 100 }) @IsString({ message: 'GitHub ID必须是字符串' }) @IsNotEmpty({ message: 'GitHub ID不能为空' }) @Length(1, 100, { message: 'GitHub ID长度需在1-100字符之间' }) github_id: string; /** * 用户名 */ @ApiProperty({ description: 'GitHub用户名', example: 'octocat', minLength: 1, maxLength: 50 }) @IsString({ message: '用户名必须是字符串' }) @IsNotEmpty({ message: '用户名不能为空' }) @Length(1, 50, { message: '用户名长度需在1-50字符之间' }) username: string; /** * 昵称 */ @ApiProperty({ description: 'GitHub显示名称', example: 'The Octocat', minLength: 1, maxLength: 50 }) @IsString({ message: '昵称必须是字符串' }) @IsNotEmpty({ message: '昵称不能为空' }) @Length(1, 50, { message: '昵称长度需在1-50字符之间' }) nickname: string; /** * 邮箱(可选) */ @ApiProperty({ description: 'GitHub邮箱地址(可选)', example: 'octocat@github.com', required: false }) @IsOptional() @IsEmail({}, { message: '邮箱格式不正确' }) email?: string; /** * 头像URL(可选) */ @ApiProperty({ description: 'GitHub头像URL(可选)', example: 'https://github.com/images/error/octocat_happy.gif', required: false }) @IsOptional() @IsString({ message: '头像URL必须是字符串' }) avatar_url?: string; } /** * 忘记密码请求DTO */ export class ForgotPasswordDto { /** * 邮箱或手机号 */ @ApiProperty({ description: '邮箱或手机号', example: 'test@example.com', minLength: 1, maxLength: 100 }) @IsString({ message: '标识符必须是字符串' }) @IsNotEmpty({ message: '邮箱或手机号不能为空' }) @Length(1, 100, { message: '标识符长度需在1-100字符之间' }) identifier: string; } /** * 重置密码请求DTO */ export class ResetPasswordDto { /** * 邮箱或手机号 */ @ApiProperty({ description: '邮箱或手机号', example: 'test@example.com', minLength: 1, maxLength: 100 }) @IsString({ message: '标识符必须是字符串' }) @IsNotEmpty({ message: '邮箱或手机号不能为空' }) @Length(1, 100, { message: '标识符长度需在1-100字符之间' }) identifier: string; /** * 验证码 */ @ApiProperty({ description: '6位数字验证码', example: '123456', pattern: '^\\d{6}$' }) @IsString({ message: '验证码必须是字符串' }) @IsNotEmpty({ message: '验证码不能为空' }) @Matches(/^\d{6}$/, { message: '验证码必须是6位数字' }) verification_code: string; /** * 新密码 */ @ApiProperty({ description: '新密码,必须包含字母和数字,长度8-128字符', example: 'newpassword123', minLength: 8, maxLength: 128 }) @IsString({ message: '新密码必须是字符串' }) @IsNotEmpty({ message: '新密码不能为空' }) @Length(8, 128, { message: '新密码长度需在8-128字符之间' }) @Matches(/^(?=.*[a-zA-Z])(?=.*\d)/, { message: '新密码必须包含字母和数字' }) new_password: string; } /** * 修改密码请求DTO */ export class ChangePasswordDto { /** * 用户ID * 实际应用中应从JWT令牌中获取,这里为了演示放在请求体中 */ @ApiProperty({ description: '用户ID(实际应用中应从JWT令牌中获取)', example: '1' }) @IsNumberString({}, { message: '用户ID必须是数字字符串' }) @IsNotEmpty({ message: '用户ID不能为空' }) user_id: string; /** * 旧密码 */ @ApiProperty({ description: '当前密码', example: 'oldpassword123', minLength: 1, maxLength: 128 }) @IsString({ message: '旧密码必须是字符串' }) @IsNotEmpty({ message: '旧密码不能为空' }) @Length(1, 128, { message: '旧密码长度需在1-128字符之间' }) old_password: string; /** * 新密码 */ @ApiProperty({ description: '新密码,必须包含字母和数字,长度8-128字符', example: 'newpassword123', minLength: 8, maxLength: 128 }) @IsString({ message: '新密码必须是字符串' }) @IsNotEmpty({ message: '新密码不能为空' }) @Length(8, 128, { message: '新密码长度需在8-128字符之间' }) @Matches(/^(?=.*[a-zA-Z])(?=.*\d)/, { message: '新密码必须包含字母和数字' }) new_password: string; } /** * 邮箱验证请求DTO */ export class EmailVerificationDto { /** * 邮箱地址 */ @ApiProperty({ description: '邮箱地址', example: 'test@example.com' }) @IsEmail({}, { message: '邮箱格式不正确' }) @IsNotEmpty({ message: '邮箱不能为空' }) email: string; /** * 验证码 */ @ApiProperty({ description: '6位数字验证码', example: '123456', pattern: '^\\d{6}$' }) @IsString({ message: '验证码必须是字符串' }) @IsNotEmpty({ message: '验证码不能为空' }) @Matches(/^\d{6}$/, { message: '验证码必须是6位数字' }) verification_code: string; } /** * 发送邮箱验证码请求DTO */ export class SendEmailVerificationDto { /** * 邮箱地址 */ @ApiProperty({ description: '邮箱地址', example: 'test@example.com' }) @IsEmail({}, { message: '邮箱格式不正确' }) @IsNotEmpty({ message: '邮箱不能为空' }) email: string; } /** * 验证码登录请求DTO */ export class VerificationCodeLoginDto { /** * 登录标识符 * 支持邮箱或手机号登录 */ @ApiProperty({ description: '登录标识符,支持邮箱或手机号', example: 'test@example.com', minLength: 1, maxLength: 100 }) @IsString({ message: '登录标识符必须是字符串' }) @IsNotEmpty({ message: '登录标识符不能为空' }) @Length(1, 100, { message: '登录标识符长度需在1-100字符之间' }) identifier: string; /** * 验证码 */ @ApiProperty({ description: '6位数字验证码', example: '123456', pattern: '^\\d{6}$' }) @IsString({ message: '验证码必须是字符串' }) @IsNotEmpty({ message: '验证码不能为空' }) @Matches(/^\d{6}$/, { message: '验证码必须是6位数字' }) verification_code: string; } /** * 发送登录验证码请求DTO */ export class SendLoginVerificationCodeDto { /** * 登录标识符 * 支持邮箱或手机号 */ @ApiProperty({ description: '登录标识符,支持邮箱或手机号', example: 'test@example.com', minLength: 1, maxLength: 100 }) @IsString({ message: '登录标识符必须是字符串' }) @IsNotEmpty({ message: '登录标识符不能为空' }) @Length(1, 100, { message: '登录标识符长度需在1-100字符之间' }) identifier: string; } /** * 刷新令牌请求DTO */ export class RefreshTokenDto { /** * 刷新令牌 */ @ApiProperty({ description: 'JWT刷新令牌', example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', minLength: 1 }) @IsString({ message: '刷新令牌必须是字符串' }) @IsNotEmpty({ message: '刷新令牌不能为空' }) refresh_token: string; }