feat: integrate invitation access, world NPCs, and deployment
This commit is contained in:
@@ -71,6 +71,12 @@ export class LoginDto {
|
||||
* 注册请求DTO
|
||||
*/
|
||||
export class RegisterDto {
|
||||
@ApiProperty({ description: '邀请码', example: 'WT-ABCD-EFGH-IJKL' })
|
||||
@IsString({ message: '邀请码必须是字符串' })
|
||||
@IsNotEmpty({ message: '邀请码不能为空' })
|
||||
@Length(8, 30, { message: '邀请码格式不正确' })
|
||||
invitation_code: string;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@@ -383,12 +389,9 @@ export class EmailVerificationDto {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮箱验证码请求DTO
|
||||
* 邮箱地址请求DTO
|
||||
*/
|
||||
export class SendEmailVerificationDto {
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
export class EmailAddressDto {
|
||||
@ApiProperty({
|
||||
description: '邮箱地址',
|
||||
example: 'test@example.com'
|
||||
@@ -398,6 +401,17 @@ export class SendEmailVerificationDto {
|
||||
email: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮箱验证码请求DTO
|
||||
*/
|
||||
export class SendEmailVerificationDto extends EmailAddressDto {
|
||||
@ApiProperty({ description: '邀请码', example: 'WT-ABCD-EFGH-IJKL' })
|
||||
@IsString({ message: '邀请码必须是字符串' })
|
||||
@IsNotEmpty({ message: '邀请码不能为空' })
|
||||
@Length(8, 30, { message: '邀请码格式不正确' })
|
||||
invitation_code: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码登录请求DTO
|
||||
*/
|
||||
|
||||
@@ -65,7 +65,7 @@ import {
|
||||
VerificationCodeLoginDto,
|
||||
SendLoginVerificationCodeDto,
|
||||
RefreshTokenDto,
|
||||
SendEmailVerificationDto
|
||||
EmailAddressDto,
|
||||
} from './dto/login.dto';
|
||||
import {
|
||||
LoginResponseDto,
|
||||
@@ -490,11 +490,11 @@ export class LoginController {
|
||||
summary: '调试验证码信息',
|
||||
description: '获取验证码的详细调试信息(仅开发环境)'
|
||||
})
|
||||
@ApiBody({ type: SendEmailVerificationDto })
|
||||
@ApiBody({ type: EmailAddressDto })
|
||||
@Post('debug-verification-code')
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
async debugVerificationCode(
|
||||
@Body() sendEmailVerificationDto: SendEmailVerificationDto,
|
||||
@Body() sendEmailVerificationDto: EmailAddressDto,
|
||||
@Res() res: Response
|
||||
): Promise<void> {
|
||||
const result = await this.loginService.debugVerificationCode(sendEmailVerificationDto.email);
|
||||
|
||||
240
src/gateway/auth/register.controller.spec.ts
Normal file
240
src/gateway/auth/register.controller.spec.ts
Normal file
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* RegisterController 单元测试
|
||||
*
|
||||
* 功能描述:
|
||||
* - 测试注册控制器的HTTP请求处理
|
||||
* - 验证API响应格式和状态码
|
||||
* - 测试邮箱验证流程
|
||||
*
|
||||
* 最近修改:
|
||||
* - 2026-01-14: 架构重构 - 从business层移动到gateway层 (修改者: moyin)
|
||||
* - 2026-01-12: 代码规范优化 - 创建缺失的控制器测试文件 (修改者: moyin)
|
||||
*
|
||||
* @author moyin
|
||||
* @version 1.1.0
|
||||
* @since 2026-01-12
|
||||
* @lastModified 2026-01-14
|
||||
*/
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Response } from 'express';
|
||||
import { HttpStatus } from '@nestjs/common';
|
||||
import { RegisterController } from './register.controller';
|
||||
import { RegisterService } from '../../business/auth/register.service';
|
||||
|
||||
describe('RegisterController', () => {
|
||||
let controller: RegisterController;
|
||||
let registerService: jest.Mocked<RegisterService>;
|
||||
let mockResponse: jest.Mocked<Response>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const mockRegisterService = {
|
||||
register: jest.fn(),
|
||||
sendEmailVerification: jest.fn(),
|
||||
verifyEmailCode: jest.fn(),
|
||||
resendEmailVerification: jest.fn(),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [RegisterController],
|
||||
providers: [
|
||||
{
|
||||
provide: RegisterService,
|
||||
useValue: mockRegisterService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<RegisterController>(RegisterController);
|
||||
registerService = module.get(RegisterService);
|
||||
|
||||
// Mock Response object
|
||||
mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
json: jest.fn().mockReturnThis(),
|
||||
} as any;
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
|
||||
describe('register', () => {
|
||||
it('should handle successful registration', async () => {
|
||||
const registerDto = {
|
||||
invitation_code: 'WT-TEST-CODE-0001',
|
||||
username: 'newuser',
|
||||
password: 'password123',
|
||||
nickname: '新用户',
|
||||
email: 'newuser@example.com',
|
||||
email_verification_code: '123456',
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: true,
|
||||
data: {
|
||||
user: {
|
||||
id: '1',
|
||||
username: 'newuser',
|
||||
nickname: '新用户',
|
||||
role: 1,
|
||||
created_at: new Date()
|
||||
},
|
||||
access_token: 'token',
|
||||
refresh_token: 'refresh_token',
|
||||
expires_in: 3600,
|
||||
token_type: 'Bearer',
|
||||
is_new_user: true,
|
||||
message: '注册成功'
|
||||
},
|
||||
message: '注册成功'
|
||||
};
|
||||
|
||||
registerService.register.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.register(registerDto, mockResponse);
|
||||
|
||||
expect(registerService.register).toHaveBeenCalledWith(registerDto);
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.CREATED);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
|
||||
it('should handle registration failure', async () => {
|
||||
const registerDto = {
|
||||
invitation_code: 'WT-TEST-CODE-0001',
|
||||
username: 'existinguser',
|
||||
password: 'password123',
|
||||
nickname: '用户',
|
||||
email: 'existing@example.com',
|
||||
email_verification_code: '123456',
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: false,
|
||||
message: '用户名已存在',
|
||||
error_code: 'REGISTER_FAILED'
|
||||
};
|
||||
|
||||
registerService.register.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.register(registerDto, mockResponse);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.BAD_REQUEST);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendEmailVerification', () => {
|
||||
it('should handle email verification in production mode', async () => {
|
||||
const sendEmailDto = {
|
||||
email: 'test@example.com',
|
||||
invitation_code: 'WT-TEST-CODE-0001',
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: true,
|
||||
data: { is_test_mode: false },
|
||||
message: '验证码已发送,请查收邮件'
|
||||
};
|
||||
|
||||
registerService.sendEmailVerification.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.sendEmailVerification(sendEmailDto, mockResponse);
|
||||
|
||||
expect(registerService.sendEmailVerification).toHaveBeenCalledWith(
|
||||
'test@example.com',
|
||||
'WT-TEST-CODE-0001',
|
||||
);
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.OK);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
|
||||
it('should handle email verification in test mode', async () => {
|
||||
const sendEmailDto = {
|
||||
email: 'test@example.com',
|
||||
invitation_code: 'WT-TEST-CODE-0001',
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: false,
|
||||
data: {
|
||||
verification_code: '123456',
|
||||
is_test_mode: true
|
||||
},
|
||||
message: '⚠️ 测试模式:验证码已生成但未真实发送。请在控制台查看验证码,或配置邮件服务以启用真实发送。',
|
||||
error_code: 'TEST_MODE_ONLY'
|
||||
};
|
||||
|
||||
registerService.sendEmailVerification.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.sendEmailVerification(sendEmailDto, mockResponse);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.PARTIAL_CONTENT);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyEmail', () => {
|
||||
it('should handle email verification successfully', async () => {
|
||||
const verifyEmailDto = {
|
||||
email: 'test@example.com',
|
||||
verification_code: '123456'
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: true,
|
||||
message: '邮箱验证成功'
|
||||
};
|
||||
|
||||
registerService.verifyEmailCode.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.verifyEmail(verifyEmailDto, mockResponse);
|
||||
|
||||
expect(registerService.verifyEmailCode).toHaveBeenCalledWith('test@example.com', '123456');
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.OK);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
|
||||
it('should handle invalid verification code', async () => {
|
||||
const verifyEmailDto = {
|
||||
email: 'test@example.com',
|
||||
verification_code: '000000'
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: false,
|
||||
message: '验证码错误',
|
||||
error_code: 'INVALID_VERIFICATION_CODE'
|
||||
};
|
||||
|
||||
registerService.verifyEmailCode.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.verifyEmail(verifyEmailDto, mockResponse);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.BAD_REQUEST);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resendEmailVerification', () => {
|
||||
it('should handle resend email verification successfully', async () => {
|
||||
const sendEmailDto = {
|
||||
email: 'test@example.com'
|
||||
};
|
||||
|
||||
const mockResult = {
|
||||
success: true,
|
||||
data: { is_test_mode: false },
|
||||
message: '验证码已重新发送,请查收邮件'
|
||||
};
|
||||
|
||||
registerService.resendEmailVerification.mockResolvedValue(mockResult);
|
||||
|
||||
await controller.resendEmailVerification(sendEmailDto, mockResponse);
|
||||
|
||||
expect(registerService.resendEmailVerification).toHaveBeenCalledWith('test@example.com');
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.OK);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -52,7 +52,8 @@ import { RegisterService } from '../../business/auth/register.service';
|
||||
import {
|
||||
RegisterDto,
|
||||
EmailVerificationDto,
|
||||
SendEmailVerificationDto
|
||||
SendEmailVerificationDto,
|
||||
EmailAddressDto,
|
||||
} from './dto/login.dto';
|
||||
import {
|
||||
RegisterResponseDto,
|
||||
@@ -166,7 +167,8 @@ export class RegisterController {
|
||||
email: registerDto.email,
|
||||
phone: registerDto.phone,
|
||||
skin_id: registerDto.skin_id,
|
||||
email_verification_code: registerDto.email_verification_code
|
||||
email_verification_code: registerDto.email_verification_code,
|
||||
invitation_code: registerDto.invitation_code,
|
||||
});
|
||||
|
||||
this.handleResponse(result, res, HttpStatus.CREATED);
|
||||
@@ -209,7 +211,7 @@ export class RegisterController {
|
||||
@Body() sendEmailVerificationDto: SendEmailVerificationDto,
|
||||
@Res() res: Response
|
||||
): Promise<void> {
|
||||
const result = await this.registerService.sendEmailVerification(sendEmailVerificationDto.email);
|
||||
const result = await this.registerService.sendEmailVerification(sendEmailVerificationDto.email, sendEmailVerificationDto.invitation_code);
|
||||
this.handleResponse(result, res);
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ export class RegisterController {
|
||||
summary: '重新发送邮箱验证码',
|
||||
description: '重新向指定邮箱发送验证码'
|
||||
})
|
||||
@ApiBody({ type: SendEmailVerificationDto })
|
||||
@ApiBody({ type: EmailAddressDto })
|
||||
@SwaggerApiResponse({
|
||||
status: 200,
|
||||
description: '验证码重新发送成功',
|
||||
@@ -277,7 +279,7 @@ export class RegisterController {
|
||||
@Post('resend-email-verification')
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
async resendEmailVerification(
|
||||
@Body() sendEmailVerificationDto: SendEmailVerificationDto,
|
||||
@Body() sendEmailVerificationDto: EmailAddressDto,
|
||||
@Res() res: Response
|
||||
): Promise<void> {
|
||||
const result = await this.registerService.resendEmailVerification(sendEmailVerificationDto.email);
|
||||
|
||||
Reference in New Issue
Block a user