refactor(auth): 重构认证模块架构 - 将Gateway层组件从Business层分离

范围: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层专注于业务逻辑和服务
- 符合分层架构设计原则
This commit is contained in:
moyin
2026-01-14 13:07:11 +08:00
parent f7c3983cc1
commit 73e3e0153c
21 changed files with 565 additions and 220 deletions

View File

@@ -7,7 +7,7 @@ import { AppService } from './app.service';
import { LoggerModule } from './core/utils/logger/logger.module';
import { UsersModule } from './core/db/users/users.module';
import { LoginCoreModule } from './core/login_core/login_core.module';
import { AuthModule } from './business/auth/auth.module';
import { AuthGatewayModule } from './gateway/auth/auth.gateway.module';
import { ZulipModule } from './business/zulip/zulip.module';
import { RedisModule } from './core/redis/redis.module';
import { AdminModule } from './business/admin/admin.module';
@@ -69,7 +69,7 @@ function isDatabaseConfigured(): boolean {
// 根据数据库配置选择用户模块模式
isDatabaseConfigured() ? UsersModule.forDatabase() : UsersModule.forMemory(),
LoginCoreModule,
AuthModule,
AuthGatewayModule, // 使用网关层模块替代业务层模块
ZulipModule,
UserMgmtModule,
AdminModule,

View File

@@ -2,38 +2,30 @@
* 用户认证业务模块导出
*
* 功能概述:
* - 用户登录和注册
* - 用户登录和注册业务逻辑
* - GitHub OAuth集成
* - 密码管理(忘记密码、重置密码、修改密码)
* - 邮箱验证功能
* - JWT Token管理
*
* 职责分离:
* - 专注于模块导出和接口暴露
* - 提供统一的模块入口点
* - 专注于业务层模块导出
* - 提供统一的业务服务入口点
* - 简化外部模块的引用方式
*
* 最近修改:
* - 2026-01-14: 架构重构 - 移除Controller和DTO导出已移至Gateway层(修改者: moyin)
* - 2026-01-07: 代码规范优化 - 文件夹扁平化,移除单文件文件夹结构
* - 2026-01-07: 代码规范优化 - 更新注释规范
*
* @author moyin
* @version 1.0.2
* @version 2.0.0
* @since 2025-12-17
* @lastModified 2026-01-07
* @lastModified 2026-01-14
*/
// 模块
export * from './auth.module';
// 控制器
export * from './login.controller';
export * from './register.controller';
// 服务
// 服务(业务层)
export { LoginService } from './login.service';
export { RegisterService } from './register.service';
// DTO
export * from './login.dto';
export * from './login_response.dto';
export { RegisterService } from './register.service';

View File

@@ -51,8 +51,8 @@ import {
ApiBearerAuth,
ApiBody,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/jwt_auth.guard';
import { CurrentUser } from '../../auth/current_user.decorator';
import { JwtAuthGuard } from '../../../gateway/auth/jwt_auth.guard';
import { CurrentUser } from '../../../gateway/auth/current_user.decorator';
import { JwtPayload } from '../../../core/login_core/login_core.service';
// 导入业务服务

View File

@@ -51,8 +51,8 @@ import {
ApiBearerAuth,
ApiBody,
} from '@nestjs/swagger';
import { JwtAuthGuard, AuthenticatedRequest } from '../auth/jwt_auth.guard';
import { CurrentUser } from '../auth/current_user.decorator';
import { JwtAuthGuard, AuthenticatedRequest } from '../../gateway/auth/jwt_auth.guard';
import { CurrentUser } from '../../gateway/auth/current_user.decorator';
import { JwtPayload } from '../../core/login_core/login_core.service';
// 导入业务服务

View File

@@ -13,8 +13,8 @@ import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagg
import { NoticeService } from './notice.service';
import { CreateNoticeDto } from './dto/create-notice.dto';
import { NoticeResponseDto } from './dto/notice-response.dto';
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
import { CurrentUser } from '../auth/current_user.decorator';
import { JwtAuthGuard } from '../../gateway/auth/jwt_auth.guard';
import { CurrentUser } from '../../gateway/auth/current_user.decorator';
@ApiTags('通知管理')
@Controller('api/notices')

View File

@@ -29,7 +29,7 @@ import { ChatController } from './chat.controller';
import { ZulipService } from './zulip.service';
import { MessageFilterService } from './services/message_filter.service';
import { CleanWebSocketGateway } from './clean_websocket.gateway';
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
import { JwtAuthGuard } from '../../gateway/auth/jwt_auth.guard';
// Mock JwtAuthGuard
const mockJwtAuthGuard = {

View File

@@ -40,7 +40,7 @@ import {
ApiBearerAuth,
ApiQuery,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
import { JwtAuthGuard } from '../../gateway/auth/jwt_auth.guard';
import { ZulipService } from './zulip.service';
import { CleanWebSocketGateway } from './clean_websocket.gateway';
import {

View File

@@ -26,7 +26,7 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HttpException, HttpStatus } from '@nestjs/common';
import { ZulipAccountsController } from './zulip_accounts.controller';
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
import { JwtAuthGuard } from '../../gateway/auth/jwt_auth.guard';
import { AppLoggerService } from '../../core/utils/logger/logger.service';
import { ZulipAccountsBusinessService } from './services/zulip_accounts_business.service';

View File

@@ -50,7 +50,7 @@ import {
ApiQuery,
} from '@nestjs/swagger';
import { Request } from 'express';
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
import { JwtAuthGuard } from '../../gateway/auth/jwt_auth.guard';
import { ZulipAccountsService } from '../../core/db/zulip_accounts/zulip_accounts.service';
import { ZulipAccountsMemoryService } from '../../core/db/zulip_accounts/zulip_accounts_memory.service';
import { AppLoggerService } from '../../core/utils/logger/logger.service';

338
src/gateway/auth/README.md Normal file
View File

@@ -0,0 +1,338 @@
# 认证网关模块 (Auth Gateway Module)
认证网关模块是系统的HTTP API入口负责处理所有认证相关的HTTP请求包括用户登录、注册、密码管理、邮箱验证等功能提供统一的API接口和完善的安全保护机制。
## 架构层级
**Gateway Layer网关层**
## 职责定位
网关层是系统的HTTP API入口负责
1. **协议处理**处理HTTP请求和响应
2. **数据验证**使用DTO进行请求参数验证
3. **路由管理**定义API端点和路由规则
4. **认证守卫**JWT令牌验证和权限检查
5. **错误转换**将业务错误转换为HTTP状态码
6. **API文档**提供Swagger API文档
## 模块组成
```
src/gateway/auth/
├── login.controller.ts # 登录API控制器
├── login.controller.spec.ts # 登录控制器测试
├── register.controller.ts # 注册API控制器
├── register.controller.spec.ts # 注册控制器测试
├── jwt_auth.guard.ts # JWT认证守卫
├── jwt_auth.guard.spec.ts # JWT认证守卫测试
├── current_user.decorator.ts # 当前用户装饰器
├── jwt_usage_example.ts # JWT使用示例开发参考
├── dto/ # 数据传输对象
│ ├── login.dto.ts # 登录相关DTO
│ └── login_response.dto.ts # 响应DTO
├── auth.gateway.module.ts # 网关模块配置
└── README.md # 模块文档
```
## 依赖关系
```
Gateway Layer (auth.gateway.module)
↓ 依赖
Business Layer (auth.module)
↓ 依赖
Core Layer (login_core.module)
```
## 核心原则
### 1. 只做协议转换,不做业务逻辑
```typescript
// ✅ 正确只做HTTP协议处理
@Post('login')
async login(@Body() loginDto: LoginDto, @Res() res: Response): Promise<void> {
const result = await this.loginService.login({
identifier: loginDto.identifier,
password: loginDto.password
});
this.handleResponse(result, res);
}
// ❌ 错误在Controller中包含业务逻辑
@Post('login')
async login(@Body() loginDto: LoginDto): Promise<any> {
// 验证用户
const user = await this.userService.findByIdentifier(loginDto.identifier);
// 检查密码
const isValid = await this.comparePassword(loginDto.password, user.password);
// ... 更多业务逻辑
}
```
### 2. 统一的错误处理
```typescript
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);
}
```
### 3. 使用DTO进行数据验证
```typescript
export class LoginDto {
@IsString()
@IsNotEmpty()
identifier: string;
@IsString()
@IsNotEmpty()
password: string;
}
```
## 对外提供的接口
### LoginController
#### login(loginDto: LoginDto, res: Response): Promise<void>
处理用户登录请求,支持用户名、邮箱或手机号多种方式登录。
#### githubOAuth(githubDto: GitHubOAuthDto, res: Response): Promise<void>
处理GitHub OAuth登录请求支持使用GitHub账户登录或注册。
#### refreshToken(refreshTokenDto: RefreshTokenDto, res: Response): Promise<void>
刷新访问令牌,使用有效的刷新令牌生成新的访问令牌。
#### forgotPassword(forgotPasswordDto: ForgotPasswordDto, res: Response): Promise<void>
发送密码重置验证码到用户邮箱或手机。
#### resetPassword(resetPasswordDto: ResetPasswordDto, res: Response): Promise<void>
使用验证码重置用户密码。
#### changePassword(changePasswordDto: ChangePasswordDto, res: Response): Promise<void>
修改用户密码,需要提供旧密码验证。
#### verificationCodeLogin(verificationCodeLoginDto: VerificationCodeLoginDto, res: Response): Promise<void>
使用邮箱或手机号和验证码进行登录,无需密码。
#### sendLoginVerificationCode(sendLoginVerificationCodeDto: SendLoginVerificationCodeDto, res: Response): Promise<void>
向用户邮箱或手机发送登录验证码。
#### debugVerificationCode(sendEmailVerificationDto: SendEmailVerificationDto, res: Response): Promise<void>
获取验证码的详细调试信息,仅用于开发环境。
### RegisterController
#### register(registerDto: RegisterDto, res: Response): Promise<void>
处理用户注册请求,创建新用户账户。
#### sendEmailVerification(sendEmailVerificationDto: SendEmailVerificationDto, res: Response): Promise<void>
向指定邮箱发送验证码。
#### verifyEmail(emailVerificationDto: EmailVerificationDto, res: Response): Promise<void>
使用验证码验证邮箱。
#### resendEmailVerification(sendEmailVerificationDto: SendEmailVerificationDto, res: Response): Promise<void>
重新向指定邮箱发送验证码。
### JwtAuthGuard
#### canActivate(context: ExecutionContext): Promise<boolean>
验证请求中的JWT令牌提取用户信息并添加到请求上下文。
### CurrentUser Decorator
#### CurrentUser(data?: keyof JwtPayload): ParameterDecorator
从请求上下文中提取当前认证用户信息的参数装饰器。
## 对外API接口
### POST /auth/login
用户登录接口支持用户名、邮箱或手机号多种方式登录返回JWT令牌。
### POST /auth/github
GitHub OAuth登录接口使用GitHub账户登录或注册。
### POST /auth/verification-code-login
验证码登录接口,使用邮箱或手机号和验证码进行登录,无需密码。
### POST /auth/refresh-token
刷新访问令牌接口,使用有效的刷新令牌生成新的访问令牌。
### POST /auth/forgot-password
发送密码重置验证码接口,向用户邮箱或手机发送密码重置验证码。
### POST /auth/reset-password
重置密码接口,使用验证码重置用户密码。
### PUT /auth/change-password
修改密码接口,用户修改自己的密码,需要提供旧密码验证。
### POST /auth/send-login-verification-code
发送登录验证码接口,向用户邮箱或手机发送登录验证码。
### POST /auth/debug-verification-code
调试验证码信息接口,获取验证码的详细调试信息,仅用于开发环境。
### POST /auth/register
用户注册接口,创建新用户账户。
### POST /auth/send-email-verification
发送邮箱验证码接口,向指定邮箱发送验证码。
### POST /auth/verify-email
验证邮箱接口,使用验证码验证邮箱。
### POST /auth/resend-email-verification
重新发送邮箱验证码接口,重新向指定邮箱发送验证码。
## 使用的项目内部依赖
### LoginService (来自 business/auth/login.service)
登录业务服务,提供用户登录、密码管理、令牌刷新等业务逻辑。
### RegisterService (来自 business/auth/register.service)
注册业务服务,提供用户注册、邮箱验证等业务逻辑。
### LoginCoreService (来自 core/login_core/login_core.service)
登录核心服务提供JWT令牌验证和生成等技术实现。
### JwtPayload (来自 core/login_core/login_core.service)
JWT令牌载荷类型定义包含用户ID、用户名、角色等信息。
### ThrottlePresets (来自 core/security_core/throttle.decorator)
限流预设配置,提供登录、注册、发送验证码等场景的限流规则。
### TimeoutPresets (来自 core/security_core/timeout.decorator)
超时预设配置,提供不同场景的超时时间设置。
## 核心特性
### 统一的响应处理机制
- 智能错误状态码映射根据错误代码和消息自动选择合适的HTTP状态码
- 统一响应格式所有API返回统一的JSON格式
- 错误信息标准化:提供清晰的错误代码和消息
### 完善的安全保护
- JWT令牌认证使用JWT进行用户身份验证
- 限流保护防止API滥用和暴力破解
- 超时控制:防止长时间阻塞和资源占用
- 请求验证使用DTO和class-validator进行严格的数据验证
### 完整的API文档
- Swagger集成自动生成交互式API文档
- 详细的接口说明每个API都有完整的描述和示例
- 请求响应示例:提供清晰的数据格式说明
### 灵活的认证方式
- 多种登录方式:支持用户名、邮箱、手机号登录
- 验证码登录:支持无密码的验证码登录
- OAuth集成支持GitHub OAuth登录
- 令牌刷新:支持无感知的令牌续期
## 使用示例
### JWT认证完整示例
本模块提供了完整的JWT认证使用示例文件 `jwt_usage_example.ts`,展示了以下场景:
1. **公开接口**:无需认证的接口
2. **受保护接口**需要JWT令牌的接口
3. **用户信息获取**:使用 `@CurrentUser()` 装饰器
4. **特定属性提取**:使用 `@CurrentUser('username')` 获取特定字段
5. **角色权限检查**:基于用户角色的访问控制
详细代码请参考 `src/gateway/auth/jwt_usage_example.ts` 文件。
### 在其他模块中使用认证守卫
```typescript
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../gateway/auth/jwt_auth.guard';
import { CurrentUser } from '../gateway/auth/current_user.decorator';
@Controller('profile')
export class ProfileController {
@Get()
@UseGuards(JwtAuthGuard)
getProfile(@CurrentUser() user: any) {
return { user };
}
}
```
## 与业务层的交互
网关层通过依赖注入使用业务层服务:
```typescript
constructor(
private readonly loginService: LoginService,
private readonly registerService: RegisterService
) {}
```
业务层返回统一的响应格式:
```typescript
interface ApiResponse<T = any> {
success: boolean;
data?: T;
message: string;
error_code?: string;
}
```
## 最佳实践
1. **保持Controller轻量**:只做请求响应处理
2. **使用DTO验证**所有输入都要经过DTO验证
3. **统一错误处理**:使用统一的错误处理方法
4. **完善API文档**使用Swagger装饰器
5. **限流保护**使用Throttle装饰器防止滥用
6. **超时控制**使用Timeout装饰器防止长时间阻塞
## 潜在风险
### 限流配置不当风险
- 限流阈值过低可能影响正常用户使用
- 限流阈值过高无法有效防止攻击
- 缓解措施:根据实际业务场景调整限流参数,监控限流触发情况
### JWT令牌安全风险
- 令牌泄露可能导致账户被盗用
- 令牌过期时间设置不当影响用户体验
- 缓解措施使用HTTPS传输合理设置令牌过期时间实现令牌刷新机制
### 验证码安全风险
- 验证码被暴力破解
- 验证码发送频率过高导致资源浪费
- 缓解措施:限制验证码发送频率,增加验证码复杂度,设置验证码有效期
### API滥用风险
- 恶意用户频繁调用API消耗服务器资源
- 自动化工具批量注册账户
- 缓解措施:实施限流策略,添加人机验证,监控异常请求模式
### 错误信息泄露风险
- 详细的错误信息可能泄露系统实现细节
- 帮助攻击者了解系统弱点
- 缓解措施:生产环境使用通用错误消息,详细日志仅记录在服务器端
## 注意事项
- 网关层不应该直接访问数据库
- 网关层不应该包含复杂的业务逻辑
- 网关层不应该直接调用Core层服务Guard除外
- 所有业务逻辑都应该在Business层实现

View File

@@ -0,0 +1,52 @@
/**
* 认证网关模块
*
* 架构层级Gateway Layer网关层
*
* 功能描述:
* - 整合所有认证相关的网关组件
* - 提供HTTP API接口
* - 配置认证守卫和中间件
* - 处理请求验证和响应格式化
*
* 职责分离:
* - 专注于HTTP协议处理和API网关功能
* - 依赖业务层服务,不包含业务逻辑
* - 提供统一的API入口和文档
*
* 依赖关系:
* - 依赖 Business Layer 的 AuthModule
* - 提供 Controller 和 Guard
*
* @author moyin
* @version 2.0.0
* @since 2026-01-14
* @lastModified 2026-01-14
*/
import { Module } from '@nestjs/common';
import { LoginController } from './login.controller';
import { RegisterController } from './register.controller';
import { JwtAuthGuard } from './jwt_auth.guard';
import { AuthModule } from '../../business/auth/auth.module';
@Module({
imports: [
// 导入业务层模块
AuthModule,
],
controllers: [
// 网关层控制器
LoginController,
RegisterController,
],
providers: [
// 认证守卫
JwtAuthGuard,
],
exports: [
// 导出守卫供其他模块使用
JwtAuthGuard,
],
})
export class AuthGatewayModule {}

View File

@@ -7,12 +7,13 @@
* -
*
*
* - 2026-01-14: 架构重构 - business层移动到gateway层 (修改者: moyin)
* - 2026-01-12: 代码规范优化 - (修改者: moyin)
*
* @author moyin
* @version 1.0.0
* @version 1.1.0
* @since 2026-01-12
* @lastModified 2026-01-12
* @lastModified 2026-01-14
*/
import { Test, TestingModule } from '@nestjs/testing';
@@ -160,4 +161,4 @@ describe('JwtAuthGuard', () => {
expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
});
});
});
});

View File

@@ -1,6 +1,8 @@
/**
* JWT 使
*
* Gateway Layer
*
*
* - 使 JWT
* - JWT认证使用示例和最佳实践
@@ -11,14 +13,20 @@
* -
* -
*
*
* - Gateway层Controller的架构定位
* - JWT Guard和装饰器位于同层src/gateway/auth
* - 使
*
*
* - 2026-01-14: 架构优化 - Business层移至Gateway层 (Modified by: moyin)
* - 2026-01-14: 代码规范优化 - Gateway层 (Modified by: moyin)
* - 2026-01-07: 代码规范优化 -
* - 2026-01-07: 代码规范优化 - snake_case格式
*
* @author moyin
* @version 1.0.2
* @version 1.2.0
* @since 2025-01-05
* @lastModified 2026-01-07
* @lastModified 2026-01-14
*/
import { Controller, Get, UseGuards, Post, Body } from '@nestjs/common';

View File

@@ -7,19 +7,20 @@
* -
*
*
* - 2026-01-14: 架构重构 - business层移动到gateway层 (修改者: moyin)
* - 2026-01-12: 代码规范优化 - (修改者: moyin)
*
* @author moyin
* @version 1.0.0
* @version 1.1.0
* @since 2026-01-12
* @lastModified 2026-01-12
* @lastModified 2026-01-14
*/
import { Test, TestingModule } from '@nestjs/testing';
import { Response } from 'express';
import { HttpStatus } from '@nestjs/common';
import { LoginController } from './login.controller';
import { LoginService } from './login.service';
import { LoginService } from '../../business/auth/login.service';
describe('LoginController', () => {
let controller: LoginController;
@@ -205,4 +206,4 @@ describe('LoginController', () => {
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
});
});
});
});

View File

@@ -1,47 +1,79 @@
/**
*
*
*
* Gateway Layer
*
*
* - HTTP请求和响应
* - RESTful API接口
* -
* -
*
*
* - HTTP请求处理和响应格式化
* -
* - HTTP协议处理和请求响应
* -
* - API文档和参数验证
* -
*
*
* - Business Layer LoginService
* - 使 DTO
* - 使 Guard
*
* API端点
* - POST /auth/login -
* - POST /auth/register -
* - POST /auth/github - GitHub OAuth登录
* - POST /auth/forgot-password -
* - POST /auth/reset-password -
* - PUT /auth/change-password -
* - POST /auth/refresh-token - 访
*
*
* - 2026-01-07: 代码规范优化 -
* - 2026-01-07: 代码规范优化 -
* - POST /auth/verification-code-login -
* - POST /auth/send-login-verification-code -
*
* @author moyin
* @version 1.0.2
* @since 2025-12-17
* @lastModified 2026-01-07
* @version 2.0.0
* @since 2026-01-14
* @lastModified 2026-01-14
*/
import { Controller, Post, Put, Body, HttpCode, HttpStatus, ValidationPipe, UsePipes, Logger, Res } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse as SwaggerApiResponse, ApiBody } from '@nestjs/swagger';
import {
Controller,
Post,
Put,
Body,
HttpCode,
HttpStatus,
ValidationPipe,
UsePipes,
Logger,
Res
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse as SwaggerApiResponse,
ApiBody
} from '@nestjs/swagger';
import { Response } from 'express';
import { LoginService, ApiResponse, LoginResponse } from './login.service';
import { LoginDto, GitHubOAuthDto, ForgotPasswordDto, ResetPasswordDto, ChangePasswordDto, VerificationCodeLoginDto, SendLoginVerificationCodeDto, RefreshTokenDto, SendEmailVerificationDto } from './login.dto';
import { LoginService } from '../../business/auth/login.service';
import {
LoginDto,
GitHubOAuthDto,
ForgotPasswordDto,
ResetPasswordDto,
ChangePasswordDto,
VerificationCodeLoginDto,
SendLoginVerificationCodeDto,
RefreshTokenDto,
SendEmailVerificationDto
} from './dto/login.dto';
import {
LoginResponseDto,
GitHubOAuthResponseDto,
ForgotPasswordResponseDto,
CommonResponseDto,
RefreshTokenResponseDto
} from './login_response.dto';
} from './dto/login_response.dto';
import { Throttle, ThrottlePresets } from '../../core/security_core/throttle.decorator';
import { Timeout, TimeoutPresets } from '../../core/security_core/timeout.decorator';
@@ -68,10 +100,10 @@ export class LoginController {
/**
*
*
*
* 1. HTTP状态码
* 2.
* 3.
*
* - HTTP状态码
* -
* -
*
* @param result
* @param res Express响应对象
@@ -84,7 +116,6 @@ export class LoginController {
return;
}
// 根据错误代码获取状态码
const statusCode = this.getErrorStatusCode(result);
res.status(statusCode).json(result);
}
@@ -97,12 +128,10 @@ export class LoginController {
* @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;
}
@@ -115,7 +144,6 @@ export class LoginController {
return HttpStatus.NOT_FOUND;
}
// 默认返回400
return HttpStatus.BAD_REQUEST;
}
@@ -123,7 +151,7 @@ export class LoginController {
*
*
* @param loginDto
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: '用户登录',
@@ -168,7 +196,7 @@ export class LoginController {
* GitHub OAuth登录
*
* @param githubDto GitHub OAuth数据
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: 'GitHub OAuth登录',
@@ -207,7 +235,6 @@ export class LoginController {
*
* @param forgotPasswordDto
* @param res Express响应对象
* @returns
*/
@ApiOperation({
summary: '发送密码重置验证码',
@@ -251,7 +278,7 @@ export class LoginController {
*
*
* @param resetPasswordDto
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: '重置密码',
@@ -292,7 +319,7 @@ export class LoginController {
*
*
* @param changePasswordDto
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: '修改密码',
@@ -315,8 +342,6 @@ export class LoginController {
@Put('change-password')
@UsePipes(new ValidationPipe({ transform: true }))
async changePassword(@Body() changePasswordDto: ChangePasswordDto, @Res() res: Response): Promise<void> {
// 实际应用中应从JWT令牌中获取用户ID
// 这里为了演示使用请求体中的用户ID
const userId = BigInt(changePasswordDto.user_id);
const result = await this.loginService.changePassword(
@@ -332,7 +357,7 @@ export class LoginController {
*
*
* @param verificationCodeLoginDto
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: '验证码登录',
@@ -359,11 +384,16 @@ export class LoginController {
@Post('verification-code-login')
@HttpCode(HttpStatus.OK)
@UsePipes(new ValidationPipe({ transform: true }))
async verificationCodeLogin(@Body() verificationCodeLoginDto: VerificationCodeLoginDto): Promise<ApiResponse<LoginResponse>> {
return await this.loginService.verificationCodeLogin({
async verificationCodeLogin(
@Body() verificationCodeLoginDto: VerificationCodeLoginDto,
@Res() res: Response
): Promise<void> {
const result = await this.loginService.verificationCodeLogin({
identifier: verificationCodeLoginDto.identifier,
verificationCode: verificationCodeLoginDto.verification_code
});
this.handleResponse(result, res);
}
/**
@@ -371,11 +401,10 @@ export class LoginController {
*
* @param sendLoginVerificationCodeDto
* @param res Express响应对象
* @returns
*/
@ApiOperation({
summary: '发送登录验证码',
description: '向用户邮箱或手机发送登录验证码。邮件使用专门的登录验证码模板,内容明确标识为登录验证而非密码重置。'
description: '向用户邮箱或手机发送登录验证码'
})
@ApiBody({ type: SendLoginVerificationCodeDto })
@SwaggerApiResponse({
@@ -410,63 +439,15 @@ export class LoginController {
this.handleResponse(result, res);
}
/**
*
*
*
* @param sendEmailVerificationDto
* @returns
*/
@ApiOperation({
summary: '调试验证码信息',
description: '获取验证码的详细调试信息(仅开发环境)'
})
@ApiBody({ type: SendEmailVerificationDto })
@Post('debug-verification-code')
@UsePipes(new ValidationPipe({ transform: true }))
async debugVerificationCode(@Body() sendEmailVerificationDto: SendEmailVerificationDto, @Res() res: Response): Promise<void> {
const result = await this.loginService.debugVerificationCode(sendEmailVerificationDto.email);
// 调试接口总是返回200
res.status(HttpStatus.OK).json(result);
}
/**
*
*/
@ApiOperation({
summary: '清除限流记录',
description: '清除所有限流记录(仅开发环境使用)'
})
@Post('debug-clear-throttle')
async clearThrottle(@Res() res: Response): Promise<void> {
// 注入ThrottleGuard并清除记录
// 这里需要通过依赖注入获取ThrottleGuard实例
res.status(HttpStatus.OK).json({
success: true,
message: '限流记录已清除'
});
}
/**
* 访
*
*
* 使访
*
*
* 1.
* 2.
* 3. JWT令牌对
* 4. 访
*
* @param refreshTokenDto
* @param res Express响应对象
* @returns
*/
@ApiOperation({
summary: '刷新访问令牌',
description: '使用有效的刷新令牌生成新的访问令牌,实现无感知的令牌续期。建议在访问令牌即将过期时调用此接口。'
description: '使用有效的刷新令牌生成新的访问令牌'
})
@ApiBody({ type: RefreshTokenDto })
@SwaggerApiResponse({
@@ -495,73 +476,28 @@ export class LoginController {
@Post('refresh-token')
@UsePipes(new ValidationPipe({ transform: true }))
async refreshToken(@Body() refreshTokenDto: RefreshTokenDto, @Res() res: Response): Promise<void> {
const startTime = Date.now();
try {
this.logRefreshTokenStart();
const result = await this.loginService.refreshAccessToken(refreshTokenDto.refresh_token);
this.handleRefreshTokenResponse(result, res, startTime);
} catch (error) {
this.handleRefreshTokenError(error, res, startTime);
}
const result = await this.loginService.refreshAccessToken(refreshTokenDto.refresh_token);
this.handleResponse(result, res);
}
/**
*
* @private
*
*
* @param sendEmailVerificationDto
* @param res Express响应对象
*/
private logRefreshTokenStart(): void {
this.logger.log('令牌刷新请求', {
operation: 'refreshToken',
timestamp: new Date().toISOString(),
});
@ApiOperation({
summary: '调试验证码信息',
description: '获取验证码的详细调试信息(仅开发环境)'
})
@ApiBody({ type: SendEmailVerificationDto })
@Post('debug-verification-code')
@UsePipes(new ValidationPipe({ transform: true }))
async debugVerificationCode(
@Body() sendEmailVerificationDto: SendEmailVerificationDto,
@Res() res: Response
): Promise<void> {
const result = await this.loginService.debugVerificationCode(sendEmailVerificationDto.email);
res.status(HttpStatus.OK).json(result);
}
/**
*
* @private
*/
private handleRefreshTokenResponse(result: any, res: Response, startTime: number): void {
const duration = Date.now() - startTime;
if (result.success) {
this.logger.log('令牌刷新成功', {
operation: 'refreshToken',
duration,
timestamp: new Date().toISOString(),
});
res.status(HttpStatus.OK).json(result);
} else {
this.logger.warn('令牌刷新失败', {
operation: 'refreshToken',
error: result.message,
errorCode: result.error_code,
duration,
timestamp: new Date().toISOString(),
});
this.handleResponse(result, res);
}
}
/**
*
* @private
*/
private handleRefreshTokenError(error: unknown, res: Response, startTime: number): void {
const duration = Date.now() - startTime;
const err = error as Error;
this.logger.error('令牌刷新异常', {
operation: 'refreshToken',
error: err.message,
duration,
timestamp: new Date().toISOString(),
}, err.stack);
res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
success: false,
message: '服务器内部错误',
error_code: 'INTERNAL_SERVER_ERROR'
});
}
}
}

View File

@@ -7,19 +7,20 @@
* -
*
*
* - 2026-01-14: 架构重构 - business层移动到gateway层 (修改者: moyin)
* - 2026-01-12: 代码规范优化 - (修改者: moyin)
*
* @author moyin
* @version 1.0.0
* @version 1.1.0
* @since 2026-01-12
* @lastModified 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 './register.service';
import { RegisterService } from '../../business/auth/register.service';
describe('RegisterController', () => {
let controller: RegisterController;
@@ -227,4 +228,4 @@ describe('RegisterController', () => {
expect(mockResponse.json).toHaveBeenCalledWith(mockResult);
});
});
});
});

View File

@@ -1,5 +1,7 @@
/**
*
*
*
* Gateway Layer
*
*
* - HTTP请求和响应
@@ -8,9 +10,14 @@
* -
*
*
* - HTTP请求处理和响应格式化
* -
* - HTTP协议处理和请求响应
* -
* - API文档和参数验证
* -
*
*
* - Business Layer RegisterService
* - 使 DTO
*
* API端点
* - POST /auth/register -
@@ -18,26 +25,41 @@
* - POST /auth/verify-email -
* - POST /auth/resend-email-verification -
*
*
* - 2026-01-12: 代码分离 - login.controller.ts中分离注册相关功能
*
* @author moyin
* @version 1.0.0
* @since 2026-01-12
* @lastModified 2026-01-12
* @version 2.0.0
* @since 2026-01-14
* @lastModified 2026-01-14
*/
import { Controller, Post, Body, HttpCode, HttpStatus, ValidationPipe, UsePipes, Logger, Res } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse as SwaggerApiResponse, ApiBody } from '@nestjs/swagger';
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, ApiResponse, RegisterResponse } from './register.service';
import { RegisterDto, EmailVerificationDto, SendEmailVerificationDto } from './login.dto';
import { RegisterService } from '../../business/auth/register.service';
import {
RegisterDto,
EmailVerificationDto,
SendEmailVerificationDto
} from './dto/login.dto';
import {
RegisterResponseDto,
CommonResponseDto,
TestModeEmailVerificationResponseDto,
SuccessEmailVerificationResponseDto
} from './login_response.dto';
} from './dto/login_response.dto';
import { Throttle, ThrottlePresets } from '../../core/security_core/throttle.decorator';
import { Timeout, TimeoutPresets } from '../../core/security_core/timeout.decorator';
@@ -61,10 +83,10 @@ export class RegisterController {
/**
*
*
*
* 1. HTTP状态码
* 2.
* 3.
*
* - HTTP状态码
* -
* -
*
* @param result
* @param res Express响应对象
@@ -77,7 +99,6 @@ export class RegisterController {
return;
}
// 根据错误代码获取状态码
const statusCode = this.getErrorStatusCode(result);
res.status(statusCode).json(result);
}
@@ -90,12 +111,10 @@ export class RegisterController {
* @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;
}
@@ -104,7 +123,6 @@ export class RegisterController {
return HttpStatus.NOT_FOUND;
}
// 默认返回400
return HttpStatus.BAD_REQUEST;
}
@@ -112,11 +130,11 @@ export class RegisterController {
*
*
* @param registerDto
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: '用户注册',
description: '创建新用户账户。如果提供邮箱,需要先调用发送验证码接口获取验证码,然后在注册时提供验证码进行验证。'
description: '创建新用户账户'
})
@ApiBody({ type: RegisterDto })
@SwaggerApiResponse({
@@ -158,7 +176,6 @@ export class RegisterController {
*
* @param sendEmailVerificationDto
* @param res Express响应对象
* @returns
*/
@ApiOperation({
summary: '发送邮箱验证码',
@@ -199,7 +216,7 @@ export class RegisterController {
*
*
* @param emailVerificationDto
* @returns
* @param res Express响应对象
*/
@ApiOperation({
summary: '验证邮箱验证码',
@@ -231,7 +248,6 @@ export class RegisterController {
*
* @param sendEmailVerificationDto
* @param res Express响应对象
* @returns
*/
@ApiOperation({
summary: '重新发送邮箱验证码',
@@ -266,4 +282,4 @@ export class RegisterController {
const result = await this.registerService.resendEmailVerification(sendEmailVerificationDto.email);
this.handleResponse(result, res);
}
}
}