范围:src/gateway/auth/, src/business/auth/, src/app.module.ts 涉及文件: - 新增:src/gateway/auth/ 目录及所有文件 - 移动:Controller、Guard、Decorator、DTO从business层移至gateway层 - 修改:src/business/auth/index.ts(移除Gateway层组件导出) - 修改:src/app.module.ts(使用AuthGatewayModule替代AuthModule) 主要改进: - 明确Gateway层和Business层的职责边界 - Controller、Guard、Decorator属于Gateway层职责 - Business层专注于业务逻辑和服务 - 符合分层架构设计原则
286 lines
8.1 KiB
TypeScript
286 lines
8.1 KiB
TypeScript
/**
|
||
* 注册网关控制器
|
||
*
|
||
* 架构层级:Gateway Layer(网关层)
|
||
*
|
||
* 功能描述:
|
||
* - 处理用户注册相关的HTTP请求和响应
|
||
* - 提供RESTful API接口
|
||
* - 数据验证和格式化
|
||
* - 邮箱验证功能
|
||
*
|
||
* 职责分离:
|
||
* - 专注于HTTP协议处理和请求响应
|
||
* - 调用业务层服务完成具体功能
|
||
* - 处理API文档和参数验证
|
||
* - 不包含业务逻辑,只做数据转换和路由
|
||
*
|
||
* 依赖关系:
|
||
* - 依赖 Business Layer 的 RegisterService
|
||
* - 使用 DTO 进行数据验证
|
||
*
|
||
* API端点:
|
||
* - POST /auth/register - 用户注册
|
||
* - POST /auth/send-email-verification - 发送邮箱验证码
|
||
* - POST /auth/verify-email - 验证邮箱验证码
|
||
* - POST /auth/resend-email-verification - 重新发送邮箱验证码
|
||
*
|
||
* @author moyin
|
||
* @version 2.0.0
|
||
* @since 2026-01-14
|
||
* @lastModified 2026-01-14
|
||
*/
|
||
|
||
import {
|
||
Controller,
|
||
Post,
|
||
Body,
|
||
HttpStatus,
|
||
ValidationPipe,
|
||
UsePipes,
|
||
Logger,
|
||
Res
|
||
} from '@nestjs/common';
|
||
import {
|
||
ApiTags,
|
||
ApiOperation,
|
||
ApiResponse as SwaggerApiResponse,
|
||
ApiBody
|
||
} from '@nestjs/swagger';
|
||
import { Response } from 'express';
|
||
import { RegisterService } from '../../business/auth/register.service';
|
||
import {
|
||
RegisterDto,
|
||
EmailVerificationDto,
|
||
SendEmailVerificationDto
|
||
} from './dto/login.dto';
|
||
import {
|
||
RegisterResponseDto,
|
||
CommonResponseDto,
|
||
TestModeEmailVerificationResponseDto,
|
||
SuccessEmailVerificationResponseDto
|
||
} from './dto/login_response.dto';
|
||
import { Throttle, ThrottlePresets } from '../../core/security_core/throttle.decorator';
|
||
import { Timeout, TimeoutPresets } from '../../core/security_core/timeout.decorator';
|
||
|
||
// 错误代码到HTTP状态码的映射
|
||
const ERROR_STATUS_MAP = {
|
||
REGISTER_FAILED: HttpStatus.BAD_REQUEST,
|
||
TEST_MODE_ONLY: HttpStatus.PARTIAL_CONTENT,
|
||
SEND_EMAIL_VERIFICATION_FAILED: HttpStatus.BAD_REQUEST,
|
||
EMAIL_VERIFICATION_FAILED: HttpStatus.BAD_REQUEST,
|
||
RESEND_EMAIL_VERIFICATION_FAILED: HttpStatus.BAD_REQUEST,
|
||
INVALID_VERIFICATION_CODE: HttpStatus.BAD_REQUEST,
|
||
} as const;
|
||
|
||
@ApiTags('auth')
|
||
@Controller('auth')
|
||
export class RegisterController {
|
||
private readonly logger = new Logger(RegisterController.name);
|
||
|
||
constructor(private readonly registerService: RegisterService) {}
|
||
|
||
/**
|
||
* 通用响应处理方法
|
||
*
|
||
* 职责:
|
||
* - 根据业务结果设置HTTP状态码
|
||
* - 处理不同类型的错误响应
|
||
* - 统一响应格式和错误处理
|
||
*
|
||
* @param result 业务服务返回的结果
|
||
* @param res Express响应对象
|
||
* @param successStatus 成功时的HTTP状态码,默认为200
|
||
* @private
|
||
*/
|
||
private handleResponse(result: any, res: Response, successStatus: HttpStatus = HttpStatus.OK): void {
|
||
if (result.success) {
|
||
res.status(successStatus).json(result);
|
||
return;
|
||
}
|
||
|
||
const statusCode = this.getErrorStatusCode(result);
|
||
res.status(statusCode).json(result);
|
||
}
|
||
|
||
/**
|
||
* 根据错误代码和消息获取HTTP状态码
|
||
*
|
||
* @param result 业务服务返回的结果
|
||
* @returns HTTP状态码
|
||
* @private
|
||
*/
|
||
private getErrorStatusCode(result: any): HttpStatus {
|
||
if (result.error_code && ERROR_STATUS_MAP[result.error_code as keyof typeof ERROR_STATUS_MAP]) {
|
||
return ERROR_STATUS_MAP[result.error_code as keyof typeof ERROR_STATUS_MAP];
|
||
}
|
||
|
||
if (result.message?.includes('已存在') || result.message?.includes('已被注册')) {
|
||
return HttpStatus.CONFLICT;
|
||
}
|
||
|
||
if (result.message?.includes('用户不存在')) {
|
||
return HttpStatus.NOT_FOUND;
|
||
}
|
||
|
||
return HttpStatus.BAD_REQUEST;
|
||
}
|
||
|
||
/**
|
||
* 用户注册
|
||
*
|
||
* @param registerDto 注册数据
|
||
* @param res Express响应对象
|
||
*/
|
||
@ApiOperation({
|
||
summary: '用户注册',
|
||
description: '创建新用户账户'
|
||
})
|
||
@ApiBody({ type: RegisterDto })
|
||
@SwaggerApiResponse({
|
||
status: 201,
|
||
description: '注册成功',
|
||
type: RegisterResponseDto
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 400,
|
||
description: '请求参数错误'
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 409,
|
||
description: '用户名或邮箱已存在'
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 429,
|
||
description: '注册请求过于频繁'
|
||
})
|
||
@Throttle(ThrottlePresets.REGISTER)
|
||
@Timeout(TimeoutPresets.NORMAL)
|
||
@Post('register')
|
||
@UsePipes(new ValidationPipe({ transform: true }))
|
||
async register(@Body() registerDto: RegisterDto, @Res() res: Response): Promise<void> {
|
||
const result = await this.registerService.register({
|
||
username: registerDto.username,
|
||
password: registerDto.password,
|
||
nickname: registerDto.nickname,
|
||
email: registerDto.email,
|
||
phone: registerDto.phone,
|
||
email_verification_code: registerDto.email_verification_code
|
||
});
|
||
|
||
this.handleResponse(result, res, HttpStatus.CREATED);
|
||
}
|
||
|
||
/**
|
||
* 发送邮箱验证码
|
||
*
|
||
* @param sendEmailVerificationDto 发送验证码数据
|
||
* @param res Express响应对象
|
||
*/
|
||
@ApiOperation({
|
||
summary: '发送邮箱验证码',
|
||
description: '向指定邮箱发送验证码'
|
||
})
|
||
@ApiBody({ type: SendEmailVerificationDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '验证码发送成功(真实发送模式)',
|
||
type: SuccessEmailVerificationResponseDto
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 206,
|
||
description: '测试模式:验证码已生成但未真实发送',
|
||
type: TestModeEmailVerificationResponseDto
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 400,
|
||
description: '请求参数错误'
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 429,
|
||
description: '发送频率过高'
|
||
})
|
||
@Throttle(ThrottlePresets.SEND_CODE_PER_EMAIL)
|
||
@Timeout(TimeoutPresets.EMAIL_SEND)
|
||
@Post('send-email-verification')
|
||
@UsePipes(new ValidationPipe({ transform: true }))
|
||
async sendEmailVerification(
|
||
@Body() sendEmailVerificationDto: SendEmailVerificationDto,
|
||
@Res() res: Response
|
||
): Promise<void> {
|
||
const result = await this.registerService.sendEmailVerification(sendEmailVerificationDto.email);
|
||
this.handleResponse(result, res);
|
||
}
|
||
|
||
/**
|
||
* 验证邮箱验证码
|
||
*
|
||
* @param emailVerificationDto 邮箱验证数据
|
||
* @param res Express响应对象
|
||
*/
|
||
@ApiOperation({
|
||
summary: '验证邮箱验证码',
|
||
description: '使用验证码验证邮箱'
|
||
})
|
||
@ApiBody({ type: EmailVerificationDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '邮箱验证成功',
|
||
type: CommonResponseDto
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 400,
|
||
description: '验证码错误或已过期'
|
||
})
|
||
@Post('verify-email')
|
||
@UsePipes(new ValidationPipe({ transform: true }))
|
||
async verifyEmail(@Body() emailVerificationDto: EmailVerificationDto, @Res() res: Response): Promise<void> {
|
||
const result = await this.registerService.verifyEmailCode(
|
||
emailVerificationDto.email,
|
||
emailVerificationDto.verification_code
|
||
);
|
||
|
||
this.handleResponse(result, res);
|
||
}
|
||
|
||
/**
|
||
* 重新发送邮箱验证码
|
||
*
|
||
* @param sendEmailVerificationDto 发送验证码数据
|
||
* @param res Express响应对象
|
||
*/
|
||
@ApiOperation({
|
||
summary: '重新发送邮箱验证码',
|
||
description: '重新向指定邮箱发送验证码'
|
||
})
|
||
@ApiBody({ type: SendEmailVerificationDto })
|
||
@SwaggerApiResponse({
|
||
status: 200,
|
||
description: '验证码重新发送成功',
|
||
type: SuccessEmailVerificationResponseDto
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 206,
|
||
description: '测试模式:验证码已生成但未真实发送',
|
||
type: TestModeEmailVerificationResponseDto
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 400,
|
||
description: '邮箱已验证或用户不存在'
|
||
})
|
||
@SwaggerApiResponse({
|
||
status: 429,
|
||
description: '发送频率过高'
|
||
})
|
||
@Throttle(ThrottlePresets.SEND_CODE)
|
||
@Post('resend-email-verification')
|
||
@UsePipes(new ValidationPipe({ transform: true }))
|
||
async resendEmailVerification(
|
||
@Body() sendEmailVerificationDto: SendEmailVerificationDto,
|
||
@Res() res: Response
|
||
): Promise<void> {
|
||
const result = await this.registerService.resendEmailVerification(sendEmailVerificationDto.email);
|
||
this.handleResponse(result, res);
|
||
}
|
||
}
|