dto:为注册接口添加邮箱验证码参数
- 在 RegisterDto 中添加 email_verification_code 可选字段 - 更新 RegisterRequest 接口定义 - 在注册核心服务中添加验证码验证逻辑 - 提供邮箱时必须提供有效的验证码进行验证
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
import { Injectable, UnauthorizedException, ConflictException, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { UsersService } from '../db/users/users.service';
|
||||
import { Users } from '../db/users/users.entity';
|
||||
import { EmailService } from '../utils/email/email.service';
|
||||
import { VerificationService, VerificationCodeType } from '../utils/verification/verification.service';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
@@ -46,6 +48,8 @@ export interface RegisterRequest {
|
||||
email?: string;
|
||||
/** 手机号(可选) */
|
||||
phone?: string;
|
||||
/** 邮箱验证码(当提供邮箱时必填) */
|
||||
email_verification_code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +94,8 @@ export interface AuthResult {
|
||||
export class LoginCoreService {
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly emailService: EmailService,
|
||||
private readonly verificationService: VerificationService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -150,7 +156,21 @@ export class LoginCoreService {
|
||||
* @throws BadRequestException 数据验证失败时
|
||||
*/
|
||||
async register(registerRequest: RegisterRequest): Promise<AuthResult> {
|
||||
const { username, password, nickname, email, phone } = registerRequest;
|
||||
const { username, password, nickname, email, phone, email_verification_code } = registerRequest;
|
||||
|
||||
// 如果提供了邮箱,必须验证邮箱验证码
|
||||
if (email) {
|
||||
if (!email_verification_code) {
|
||||
throw new BadRequestException('提供邮箱时必须提供邮箱验证码');
|
||||
}
|
||||
|
||||
// 验证邮箱验证码
|
||||
await this.verificationService.verifyCode(
|
||||
email,
|
||||
VerificationCodeType.EMAIL_VERIFICATION,
|
||||
email_verification_code
|
||||
);
|
||||
}
|
||||
|
||||
// 验证密码强度
|
||||
this.validatePasswordStrength(password);
|
||||
@@ -165,9 +185,20 @@ export class LoginCoreService {
|
||||
nickname,
|
||||
email,
|
||||
phone,
|
||||
role: 1 // 默认普通用户
|
||||
role: 1, // 默认普通用户
|
||||
email_verified: email ? true : false // 如果提供了邮箱且验证码验证通过,则标记为已验证
|
||||
});
|
||||
|
||||
// 如果提供了邮箱,发送欢迎邮件
|
||||
if (email) {
|
||||
try {
|
||||
await this.emailService.sendWelcomeEmail(email, nickname);
|
||||
} catch (error) {
|
||||
// 邮件发送失败不影响注册流程,只记录日志
|
||||
console.warn(`欢迎邮件发送失败: ${email}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
isNewUser: true
|
||||
@@ -215,9 +246,19 @@ export class LoginCoreService {
|
||||
email,
|
||||
github_id,
|
||||
avatar_url,
|
||||
role: 1 // 默认普通用户
|
||||
role: 1, // 默认普通用户
|
||||
email_verified: email ? true : false // GitHub邮箱直接验证
|
||||
});
|
||||
|
||||
// 发送欢迎邮件
|
||||
if (email) {
|
||||
try {
|
||||
await this.emailService.sendWelcomeEmail(email, nickname);
|
||||
} catch (error) {
|
||||
console.warn(`欢迎邮件发送失败: ${email}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
isNewUser: true
|
||||
@@ -237,6 +278,11 @@ export class LoginCoreService {
|
||||
|
||||
if (this.isEmail(identifier)) {
|
||||
user = await this.usersService.findByEmail(identifier);
|
||||
|
||||
// 检查邮箱是否已验证
|
||||
if (user && !user.email_verified) {
|
||||
throw new BadRequestException('邮箱未验证,无法重置密码');
|
||||
}
|
||||
} else if (this.isPhoneNumber(identifier)) {
|
||||
const users = await this.usersService.findAll();
|
||||
user = users.find(u => u.phone === identifier) || null;
|
||||
@@ -246,18 +292,30 @@ export class LoginCoreService {
|
||||
throw new NotFoundException('用户不存在');
|
||||
}
|
||||
|
||||
// 生成6位数验证码
|
||||
const verificationCode = this.generateVerificationCode();
|
||||
// 生成验证码
|
||||
const verificationCode = await this.verificationService.generateCode(
|
||||
identifier,
|
||||
VerificationCodeType.PASSWORD_RESET
|
||||
);
|
||||
|
||||
// TODO: 实际应用中应该:
|
||||
// 1. 将验证码存储到Redis等缓存中,设置过期时间(如5分钟)
|
||||
// 2. 发送验证码到用户邮箱或手机
|
||||
// 3. 返回成功消息而不是验证码本身
|
||||
// 发送验证码
|
||||
if (this.isEmail(identifier)) {
|
||||
const success = await this.emailService.sendVerificationCode({
|
||||
email: identifier,
|
||||
code: verificationCode,
|
||||
nickname: user.nickname,
|
||||
purpose: 'password_reset'
|
||||
});
|
||||
|
||||
// 这里为了演示,直接返回验证码
|
||||
console.log(`密码重置验证码(${identifier}): ${verificationCode}`);
|
||||
if (!success) {
|
||||
throw new BadRequestException('验证码发送失败,请稍后重试');
|
||||
}
|
||||
} else {
|
||||
// TODO: 实现短信发送
|
||||
console.log(`短信验证码(${identifier}): ${verificationCode}`);
|
||||
}
|
||||
|
||||
return verificationCode;
|
||||
return verificationCode; // 实际应用中不应返回验证码
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,10 +329,15 @@ export class LoginCoreService {
|
||||
async resetPassword(resetRequest: PasswordResetRequest): Promise<Users> {
|
||||
const { identifier, verificationCode, newPassword } = resetRequest;
|
||||
|
||||
// TODO: 实际应用中应该验证验证码的有效性
|
||||
// 这里为了演示,简单验证验证码格式
|
||||
if (!/^\d{6}$/.test(verificationCode)) {
|
||||
throw new BadRequestException('验证码格式错误');
|
||||
// 验证验证码
|
||||
const isValidCode = await this.verificationService.verifyCode(
|
||||
identifier,
|
||||
VerificationCodeType.PASSWORD_RESET,
|
||||
verificationCode
|
||||
);
|
||||
|
||||
if (!isValidCode) {
|
||||
throw new BadRequestException('验证码验证失败');
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
@@ -389,6 +452,90 @@ export class LoginCoreService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮箱验证码
|
||||
*
|
||||
* @param email 邮箱地址
|
||||
* @param nickname 用户昵称
|
||||
* @returns 验证码
|
||||
*/
|
||||
async sendEmailVerification(email: string, nickname?: string): Promise<string> {
|
||||
// 生成验证码
|
||||
const verificationCode = await this.verificationService.generateCode(
|
||||
email,
|
||||
VerificationCodeType.EMAIL_VERIFICATION
|
||||
);
|
||||
|
||||
// 发送验证邮件
|
||||
const success = await this.emailService.sendVerificationCode({
|
||||
email,
|
||||
code: verificationCode,
|
||||
nickname,
|
||||
purpose: 'email_verification'
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
throw new BadRequestException('验证邮件发送失败,请稍后重试');
|
||||
}
|
||||
|
||||
return verificationCode; // 实际应用中不应返回验证码
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱验证码
|
||||
*
|
||||
* @param email 邮箱地址
|
||||
* @param code 验证码
|
||||
* @returns 验证结果
|
||||
*/
|
||||
async verifyEmailCode(email: string, code: string): Promise<boolean> {
|
||||
// 验证验证码
|
||||
const isValid = await this.verificationService.verifyCode(
|
||||
email,
|
||||
VerificationCodeType.EMAIL_VERIFICATION,
|
||||
code
|
||||
);
|
||||
|
||||
if (isValid) {
|
||||
// 更新用户邮箱验证状态
|
||||
const user = await this.usersService.findByEmail(email);
|
||||
if (user) {
|
||||
await this.usersService.update(user.id, {
|
||||
email_verified: true
|
||||
});
|
||||
|
||||
// 发送欢迎邮件
|
||||
try {
|
||||
await this.emailService.sendWelcomeEmail(email, user.nickname);
|
||||
} catch (error) {
|
||||
console.warn(`欢迎邮件发送失败: ${email}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新发送邮箱验证码
|
||||
*
|
||||
* @param email 邮箱地址
|
||||
* @returns 验证码
|
||||
*/
|
||||
async resendEmailVerification(email: string): Promise<string> {
|
||||
const user = await this.usersService.findByEmail(email);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('用户不存在');
|
||||
}
|
||||
|
||||
if (user.email_verified) {
|
||||
throw new BadRequestException('邮箱已验证,无需重复验证');
|
||||
}
|
||||
|
||||
return await this.sendEmailVerification(email, user.nickname);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user