Package Exports
- @maxtan/nest-core
- @maxtan/nest-core/decorators/auth.decorator.d.ts
- @maxtan/nest-core/decorators/auth.decorator.d.ts.map
- @maxtan/nest-core/decorators/auth.decorator.js
- @maxtan/nest-core/decorators/auth.decorator.js.map
- @maxtan/nest-core/decorators/index.d.ts
- @maxtan/nest-core/decorators/index.d.ts.map
- @maxtan/nest-core/decorators/index.js
- @maxtan/nest-core/decorators/index.js.map
- @maxtan/nest-core/decorators/operation-log.decorator.d.ts
- @maxtan/nest-core/decorators/operation-log.decorator.d.ts.map
- @maxtan/nest-core/decorators/operation-log.decorator.js
- @maxtan/nest-core/decorators/operation-log.decorator.js.map
- @maxtan/nest-core/decorators/xml.decorator.d.ts
- @maxtan/nest-core/decorators/xml.decorator.d.ts.map
- @maxtan/nest-core/decorators/xml.decorator.js
- @maxtan/nest-core/decorators/xml.decorator.js.map
- @maxtan/nest-core/filters/exceptions.filter.d.ts
- @maxtan/nest-core/filters/exceptions.filter.d.ts.map
- @maxtan/nest-core/filters/exceptions.filter.js
- @maxtan/nest-core/filters/exceptions.filter.js.map
- @maxtan/nest-core/filters/index.d.ts
- @maxtan/nest-core/filters/index.d.ts.map
- @maxtan/nest-core/filters/index.js
- @maxtan/nest-core/filters/index.js.map
- @maxtan/nest-core/index.d.ts
- @maxtan/nest-core/index.d.ts.map
- @maxtan/nest-core/index.js
- @maxtan/nest-core/index.js.map
- @maxtan/nest-core/interceptors/index.d.ts
- @maxtan/nest-core/interceptors/index.d.ts.map
- @maxtan/nest-core/interceptors/index.js
- @maxtan/nest-core/interceptors/index.js.map
- @maxtan/nest-core/interceptors/operation-log.interceptor.d.ts
- @maxtan/nest-core/interceptors/operation-log.interceptor.d.ts.map
- @maxtan/nest-core/interceptors/operation-log.interceptor.js
- @maxtan/nest-core/interceptors/operation-log.interceptor.js.map
- @maxtan/nest-core/modules/auth/auth.guard.d.ts
- @maxtan/nest-core/modules/auth/auth.guard.d.ts.map
- @maxtan/nest-core/modules/auth/auth.guard.js
- @maxtan/nest-core/modules/auth/auth.guard.js.map
- @maxtan/nest-core/modules/auth/auth.module.d.ts
- @maxtan/nest-core/modules/auth/auth.module.d.ts.map
- @maxtan/nest-core/modules/auth/auth.module.js
- @maxtan/nest-core/modules/auth/auth.module.js.map
- @maxtan/nest-core/modules/auth/auth.strategy.d.ts
- @maxtan/nest-core/modules/auth/auth.strategy.d.ts.map
- @maxtan/nest-core/modules/auth/auth.strategy.js
- @maxtan/nest-core/modules/auth/auth.strategy.js.map
- @maxtan/nest-core/modules/cache/cache.module.d.ts
- @maxtan/nest-core/modules/cache/cache.module.d.ts.map
- @maxtan/nest-core/modules/cache/cache.module.js
- @maxtan/nest-core/modules/cache/cache.module.js.map
- @maxtan/nest-core/modules/cache/cache.service.d.ts
- @maxtan/nest-core/modules/cache/cache.service.d.ts.map
- @maxtan/nest-core/modules/cache/cache.service.js
- @maxtan/nest-core/modules/cache/cache.service.js.map
- @maxtan/nest-core/modules/common/common.module.d.ts
- @maxtan/nest-core/modules/common/common.module.d.ts.map
- @maxtan/nest-core/modules/common/common.module.js
- @maxtan/nest-core/modules/common/common.module.js.map
- @maxtan/nest-core/modules/common/common.service.d.ts
- @maxtan/nest-core/modules/common/common.service.d.ts.map
- @maxtan/nest-core/modules/common/common.service.js
- @maxtan/nest-core/modules/common/common.service.js.map
- @maxtan/nest-core/modules/index.d.ts
- @maxtan/nest-core/modules/index.d.ts.map
- @maxtan/nest-core/modules/index.js
- @maxtan/nest-core/modules/index.js.map
- @maxtan/nest-core/modules/operation-log/operation-log.interceptor.d.ts
- @maxtan/nest-core/modules/operation-log/operation-log.interceptor.d.ts.map
- @maxtan/nest-core/modules/operation-log/operation-log.interceptor.js
- @maxtan/nest-core/modules/operation-log/operation-log.interceptor.js.map
- @maxtan/nest-core/modules/operation/index.d.ts
- @maxtan/nest-core/modules/operation/index.d.ts.map
- @maxtan/nest-core/modules/operation/index.js
- @maxtan/nest-core/modules/operation/index.js.map
- @maxtan/nest-core/modules/operation/operation.decorator.d.ts
- @maxtan/nest-core/modules/operation/operation.decorator.d.ts.map
- @maxtan/nest-core/modules/operation/operation.decorator.js
- @maxtan/nest-core/modules/operation/operation.decorator.js.map
- @maxtan/nest-core/modules/operation/operation.interceptor.d.ts
- @maxtan/nest-core/modules/operation/operation.interceptor.d.ts.map
- @maxtan/nest-core/modules/operation/operation.interceptor.js
- @maxtan/nest-core/modules/operation/operation.interceptor.js.map
- @maxtan/nest-core/modules/operation/operation.module.d.ts
- @maxtan/nest-core/modules/operation/operation.module.d.ts.map
- @maxtan/nest-core/modules/operation/operation.module.js
- @maxtan/nest-core/modules/operation/operation.module.js.map
- @maxtan/nest-core/pipes/index.d.ts
- @maxtan/nest-core/pipes/index.d.ts.map
- @maxtan/nest-core/pipes/index.js
- @maxtan/nest-core/pipes/index.js.map
- @maxtan/nest-core/pipes/validation.pipe.d.ts
- @maxtan/nest-core/pipes/validation.pipe.d.ts.map
- @maxtan/nest-core/pipes/validation.pipe.js
- @maxtan/nest-core/pipes/validation.pipe.js.map
- @maxtan/nest-core/tsconfig.tsbuildinfo
- @maxtan/nest-core/types/auth.d.ts
- @maxtan/nest-core/types/auth.d.ts.map
- @maxtan/nest-core/types/auth.js
- @maxtan/nest-core/types/auth.js.map
- @maxtan/nest-core/types/boot.d.ts
- @maxtan/nest-core/types/boot.d.ts.map
- @maxtan/nest-core/types/boot.js
- @maxtan/nest-core/types/boot.js.map
- @maxtan/nest-core/types/index.d.ts
- @maxtan/nest-core/types/index.d.ts.map
- @maxtan/nest-core/types/index.js
- @maxtan/nest-core/types/index.js.map
- @maxtan/nest-core/types/logger.d.ts
- @maxtan/nest-core/types/logger.d.ts.map
- @maxtan/nest-core/types/logger.js
- @maxtan/nest-core/types/logger.js.map
- @maxtan/nest-core/types/operation-log.d.ts
- @maxtan/nest-core/types/operation-log.d.ts.map
- @maxtan/nest-core/types/operation-log.js
- @maxtan/nest-core/types/operation-log.js.map
- @maxtan/nest-core/utils/common.d.ts
- @maxtan/nest-core/utils/common.d.ts.map
- @maxtan/nest-core/utils/common.js
- @maxtan/nest-core/utils/common.js.map
- @maxtan/nest-core/utils/dto.d.ts
- @maxtan/nest-core/utils/dto.d.ts.map
- @maxtan/nest-core/utils/dto.js
- @maxtan/nest-core/utils/dto.js.map
- @maxtan/nest-core/utils/entries.d.ts
- @maxtan/nest-core/utils/entries.d.ts.map
- @maxtan/nest-core/utils/entries.js
- @maxtan/nest-core/utils/entries.js.map
- @maxtan/nest-core/utils/index.d.ts
- @maxtan/nest-core/utils/index.d.ts.map
- @maxtan/nest-core/utils/index.js
- @maxtan/nest-core/utils/index.js.map
- @maxtan/nest-core/utils/logger.d.ts
- @maxtan/nest-core/utils/logger.d.ts.map
- @maxtan/nest-core/utils/logger.js
- @maxtan/nest-core/utils/logger.js.map
- @maxtan/nest-core/utils/snowflake.d.ts
- @maxtan/nest-core/utils/snowflake.d.ts.map
- @maxtan/nest-core/utils/snowflake.js
- @maxtan/nest-core/utils/snowflake.js.map
Readme
@maxtan/nest-core
NestJS 增强工具包,提供了一系列开箱即用的模块和工具,帮助您快速构建高效、规范的 NestJS 应用。
功能特性
- 日志系统: 基于 winston 的灵活日志系统,支持控制台、文件、阿里云 SLS 等多种输出方式
- 操作日志: 基于装饰器的操作日志记录,自动捕获请求信息、执行结果、耗时等
- 缓存模块: 基于 Redis 的高性能缓存解决方案,支持生命周期管理和批量操作
- 异常过滤器: 统一的异常处理机制,自动记录异常日志和错误追踪
- 验证管道: 增强的数据验证管道,支持嵌套验证和配置化
- 工具函数库: 常用辅助函数集合,使用加密安全的随机数生成
- 授权模块: 基于 JWT 的认证和授权系统,支持多种 Token 提取策略
- 雪花算法: 基于 @sapphire/snowflake 的分布式唯一 ID 生成器
- XML 解析: 高性能 XML 解析装饰器,支持自定义配置和错误处理
- TypeORM 实体基类: 提供包含通用字段(创建时间、更新时间、创建者、更新者、软删除)的基础实体类
安装
npm install @maxtan/nest-core
# 或使用 pnpm
pnpm add @maxtan/nest-core使用指南
日志模块
基础配置
import { createLogger } from '@maxtan/nest-core'
// 在应用启动时配置日志
createLogger() // 默认配置,日志输出到控制台和文件使用日志工具
import { logger } from '@maxtan/nest-core'
// 使用默认logger记录不同级别的日志
logger.info('普通信息')
logger.debug('调试信息')
logger.warn('警告信息')
logger.error('错误信息')
// 或使用 log 方法(需要指定级别)
logger.log('info', '普通信息')
logger.log('error', '错误信息', errorObject)自定义配置选项
import { createLogger } from '@maxtan/nest-core'
// 自定义配置
createLogger({
useConsole: true, // 是否输出到控制台,默认 true
maxDays: 30, // 日志文件保留天数,默认 30 天
maxSize: '20m', // 单个日志文件最大大小,默认 20MB
sls: {
// 阿里云 SLS 配置(可选)
accessKeyId: 'your-ak',
accessKeySecret: 'your-sk',
projectName: 'your-project',
logStoreName: 'your-logstore',
// 可选配置
endpoint: 'cn-hangzhou.log.aliyuncs.com',
topic: 'nest-boot',
source: 'source',
env: 'production'
}
})日志文件轮转机制
日志系统使用 winston-daily-rotate-file 实现自动轮转:
按大小轮转:当单个文件达到
maxSize(20MB)时,自动创建新文件logs/default-2024-01-01.log // 当前文件 logs/default-2024-01-01.1.log.gz // 第1个文件(已压缩) logs/default-2024-01-01.2.log.gz // 第2个文件(已压缩)按日期轮转:每天午夜自动创建新文件
logs/default-2024-01-01.log.gz logs/default-2024-01-02.log.gz logs/default-2024-01-03.log // 当前日期自动压缩:旧文件自动压缩为
.gz格式,节省磁盘空间自动清理:超过
maxDays(30天)的文件自动删除
在 NestJS 应用中使用
import { NestFactory } from '@nestjs/core'
import { createLogger, WinstonLoggerService } from '@maxtan/nest-core'
import { AppModule } from './app.module'
async function bootstrap() {
// 配置日志系统
createLogger({
useConsole: true,
maxDays: 30,
sls: {
// SLS 配置...
}
})
const app = await NestFactory.create(AppModule, {
logger: new WinstonLoggerService() // 使用 Winston 作为 NestJS 日志服务
})
await app.listen(3000)
}
bootstrap()缓存模块
缓存模块提供了基于 Redis 的高性能缓存解决方案,内置生命周期管理,支持批量操作、对象序列化等功能。
import { CacheModule } from '@maxtan/nest-core'
@Module({
imports: [
CacheModule.forRoot(
{
url: 'redis://localhost:6379'
// 其他 Redis 客户端选项
},
true // 第二个参数表示是否全局注册模块
)
]
})
export class AppModule {}在服务中使用:
import { Injectable } from '@nestjs/common'
import { CacheService } from '@maxtan/nest-core'
@Injectable()
export class YourService {
constructor(private readonly cacheService: CacheService) {}
async getData(key: string) {
// 从缓存获取数据
const cached = await this.cacheService.get(key)
if (cached) return cached
// 获取数据并缓存
const data = await this.fetchData()
await this.cacheService.set(key, JSON.stringify(data), 3600) // 缓存1小时
return data
}
// 使用对象序列化
async getObjectData(key: string) {
// 自动 JSON 解析
const cached = await this.cacheService.getObject<UserData>(key)
if (cached) return cached
const data = await this.fetchData()
await this.cacheService.setObject(key, data, 3600) // 自动 JSON 序列化
return data
}
// 批量操作
async batchGet(keys: string[]) {
return await this.cacheService.mget(...keys)
}
async batchSet(data: Record<string, any>) {
await this.cacheService.mset(data)
}
// 计数器
async incrementCounter(key: string) {
return await this.cacheService.incr(key)
}
// 检查连接状态
async checkHealth() {
const isReady = await this.cacheService.isReady()
return { redis: isReady ? 'healthy' : 'unhealthy' }
}
}操作日志模块
操作日志模块提供了全局自动记录所有控制器方法执行情况的功能,自动捕获请求信息、执行结果、耗时等。
模块注册
在应用模块中导入 OperationModule:
import { Module } from '@nestjs/common'
import { OperationModule } from '@maxtan/nest-core'
@Module({
imports: [
// 基础使用 - 默认全局拦截所有控制器方法
OperationModule.forRoot()
]
})
export class AppModule {}自定义配置
import { Module } from '@nestjs/common'
import { OperationModule } from '@maxtan/nest-core'
@Module({
imports: [
OperationModule.forRoot({
enabled: true, // 是否启用,默认 true
excludePaths: ['/health', '/metrics'], // 排除不需要记录日志的路径
global: true, // 是否全局注册,默认 true
request: false, // 是否默认记录请求参数,默认 false
response: false, // 是否默认记录响应结果,默认 false
idField: 'id', // 用户ID字段名,默认 'id'
nameField: 'name' // 用户名字段名,默认 'name'
})
]
})
export class AppModule {}使用 @Operation 装饰器
@Operation 装饰器是可选的,即使不使用装饰器,拦截器也会自动记录所有控制器方法的日志。装饰器主要用于:
- 添加操作描述
- 禁用特定方法的日志记录
- 为特定方法配置是否记录请求和响应
import { Controller, Post, Put, Get, Body, Param } from '@nestjs/common'
import { Operation } from '@maxtan/nest-core'
@Controller('users')
export class UserController {
// 不使用装饰器 - 自动记录日志(根据全局配置)
@Post()
async createUser(@Body() dto: CreateUserDto) {
return this.userService.create(dto)
}
// 添加描述并记录请求和响应
@Put(':id')
@Operation({
description: '更新用户信息',
request: true, // 记录请求参数
response: true // 记录响应结果
})
async updateUser(@Param('id') id: string, @Body() dto: UpdateUserDto) {
return this.userService.update(id, dto)
}
// 仅记录请求,不记录响应
@Post('login')
@Operation({
description: '用户登录',
request: true
})
async login(@Body() dto: LoginDto) {
return this.authService.login(dto)
}
// 禁用日志记录(如健康检查接口)
@Get('health')
@Operation({ disabled: true })
async healthCheck() {
return { status: 'ok' }
}
}配置选项
OperationModuleOptions (模块配置)
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled |
boolean | true |
是否启用操作日志 |
excludePaths |
string[] | [] |
排除的路径(支持通配符),不记录日志 |
global |
boolean | true |
是否全局注册 |
request |
boolean | false |
是否默认记录请求参数 |
response |
boolean | false |
是否默认记录响应结果 |
idField |
string | 'id' |
用户ID字段名。系统会优先使用标准字段 sub 和 userId,如果不存在则使用此字段从 JWT payload 中获取 |
nameField |
string | 'name' |
用户名字段名。系统会优先使用 username,如果不存在则使用此字段从 JWT payload 中获取 |
OperationOptions (装饰器配置)
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
description |
string | - | 操作描述,添加到日志中 |
disabled |
boolean | false |
是否禁用此方法的日志记录 |
request |
boolean | - | 是否记录请求参数(优先于全局配置) |
response |
boolean | - | 是否记录响应结果(优先于全局配置) |
注意:装饰器中的
request和response配置优先级高于模块全局配置。
用户信息提取规则
操作日志会自动从 JWT token 中提取用户信息(如果存在认证)。为了兼容不同的 JWT payload 结构,系统使用以下优先级规则:
用户 ID 提取优先级(从高到低):
payload.sub- JWT 标准字段payload.userId- 常用的用户ID字段payload[idField]- 自定义字段(由idField配置指定,默认'id')
用户名提取优先级(从高到低):
payload.username- 常用的用户名字段payload[nameField]- 自定义字段(由nameField配置指定,默认'name')
这种灵活的配置方式使得操作日志模块可以适配各种 JWT payload 结构,无需修改现有的认证逻辑。
示例:
// JWT payload 示例 1:使用标准字段
{
"sub": "user_12345",
"username": "john_doe"
}
// 提取结果:userId = "user_12345", username = "john_doe"
// JWT payload 示例 2:使用自定义字段
{
"id": "12345",
"name": "John Doe"
}
// 使用默认配置,提取结果:userId = "12345", username = "John Doe"
// JWT payload 示例 3:自定义配置
{
"uid": "12345",
"nickname": "JohnD"
}
// 使用配置 { idField: 'uid', nameField: 'nickname' }
// 提取结果:userId = "12345", username = "JohnD"日志输出示例
基础日志(不记录参数和结果):
{
"operationId": "1234567890123456789",
"path": "/users",
"httpMethod": "POST",
"userId": "123",
"username": "admin",
"status": "success",
"statusCode": 200,
"duration": "45ms"
}带描述和参数的日志:
{
"operationId": "1234567890123456789",
"description": "更新用户信息",
"path": "/users/123",
"httpMethod": "PUT",
"userId": "123",
"username": "admin",
"params": {
"username": "newuser",
"password": "***", // 自动脱敏
"email": "user@example.com"
},
"status": "success",
"statusCode": 200,
"duration": "45ms"
}带结果的日志:
{
"operationId": "1234567890123456789",
"description": "更新用户信息",
"path": "/users/123",
"httpMethod": "PUT",
"result": {
"id": "123",
"username": "newuser",
"email": "user@example.com"
},
"status": "success",
"statusCode": 200,
"duration": "45ms"
}失败请求:
{
"operationId": "1234567890123456790",
"path": "/users/123",
"httpMethod": "DELETE",
"status": "failure",
"statusCode": 500,
"duration": "12ms",
"error": {
"message": "用户不存在",
"name": "Error"
}
}失败请求时,异常过滤器会单独输出堆栈信息(仅 500+ 错误或需要调试的异常):
[Stack Trace] operationId: 1234567890123456790 | errorId: 9876543210
Error: 用户不存在
at UserService.deleteUser (D:\app\user.service.ts:45:11)
at UserController.deleteUser (D:\app\user.controller.ts:30:28)
at @nestjs/core/router/router-execution-context.js:38:29
...字段说明
| 字段 | 说明 |
|---|---|
operationId |
操作唯一 ID,用于日志追踪 |
description |
操作描述(可选) |
path |
请求路径(包含路径参数) |
httpMethod |
HTTP 方法 |
userId |
用户 ID(从 JWT token 中获取) |
username |
用户名(从 JWT token 中获取) |
params |
请求参数(可选,自动脱敏) |
result |
响应结果(可选) |
status |
操作状态(success/failure) |
statusCode |
HTTP 状态码 |
duration |
执行耗时 |
error |
错误信息(失败时,不含 stack) |
错误日志关联
当操作失败时,会输出两条日志:
- 操作日志:包含业务上下文和错误信息(不含堆栈)
- 堆栈信息:由异常过滤器输出(仅 500+ 错误或需要调试的异常)
两条日志通过 operationId 关联,可以快速定位问题:
# 查看某个操作的所有相关日志
grep "1234567890" app.log安全特性
- 自动脱敏 - 自动隐藏敏感字段:
password、token、secret、accessToken、refreshToken、apiKey - 异步记录 - 异步记录日志,不影响请求性能
- 唯一追踪 - 每个操作生成唯一
operationId,便于日志追踪和关联 - 堆栈分离 - 错误堆栈与操作日志分离,通过
operationId关联,避免日志冗余 - 灵活配置 - 支持全局配置和方法级配置,装饰器配置优先级更高
授权模块
授权模块提供了基于 JWT 的认证和授权功能,可以轻松集成到您的 NestJS 应用中。
基本使用
import { AuthModule } from '@maxtan/nest-core'
@Module({
imports: [
AuthModule.register({
secret: 'your-jwt-secret-key'
})
]
})
export class AppModule {}自定义配置
您可以自定义 JWT 签名选项:
import { AuthModule } from '@maxtan/nest-core'
@Module({
imports: [
AuthModule.register({
secret: 'your-jwt-secret-key',
signOptions: {
expiresIn: '7d', // Token有效期7天
issuer: 'your-app', // 发行方
audience: 'your-users' // 目标用户
}
})
]
})
export class AppModule {}保护路由
使用 AuthGuard 保护您的路由:
import { Controller, Get, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@maxtan/nest-core'
@Controller('protected')
@UseGuards(AuthGuard)
export class ProtectedController {
@Get()
getProtectedData() {
return { message: '这是受保护的数据' }
}
}全局应用 AuthGuard
您可以在应用级别全局应用 AuthGuard,这样所有路由默认都会受到保护:
import { Module } from '@nestjs/common'
import { APP_GUARD } from '@nestjs/core'
import { AuthGuard, AuthModule } from '@maxtan/nest-core'
@Module({
imports: [
AuthModule.register({
secret: 'your-jwt-secret-key'
})
// 其他模块...
],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard
}
]
})
export class AppModule {}当启用全局 AuthGuard 后,可以使用 @AuthPublic() 装饰器来标记不需要认证的公开路由:
import { Controller, Get } from '@nestjs/common'
import { AuthPublic } from '@maxtan/nest-core'
@Controller('public')
export class PublicController {
@Get()
@AuthPublic()
getPublicData() {
return { message: '这是公开数据,无需认证' }
}
}获取当前用户
从请求中获取当前认证用户:
import { Controller, Get, Request, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@maxtan/nest-core'
@Controller('profile')
@UseGuards(AuthGuard)
export class ProfileController {
@Get()
getProfile(@Request() req) {
// req.user 包含了 JWT payload 中的信息
return req.user
}
}使用 AuthDecorator 获取用户信息
更简洁的方式是使用 AuthDecorator 装饰器来直接获取认证用户信息:
import { Controller, Get, UseGuards } from '@nestjs/common'
import { AuthGuard, AuthDecorator } from '@maxtan/nest-core'
@Controller('profile')
@UseGuards(AuthGuard)
export class ProfileController {
@Get()
getProfile(@AuthDecorator() auth) {
// auth 包含完整的认证用户信息
return auth
}
@Get('username')
getUsername(@AuthDecorator('username') username: string) {
// 直接获取指定字段
return { username }
}
@Get('data')
getData(@AuthDecorator('data') data: any) {
// 获取自定义数据字段
return data
}
}AuthDecorator 可以接受一个可选的参数,用于指定要获取的 JWT payload 中的字段名。如果不提供参数,将返回整个认证对象。
JWT Token 自定义扩展
@maxtan/nest-core 的认证模块只负责 验证和解析 JWT token,不干涉 token 的生成和加密逻辑。
你可以在外部应用中自由定义 JWT payload 结构和加密方式:
import { JwtService } from '@nestjs/jwt'
import { Injectable } from '@nestjs/common'
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
// 自定义 token 生成逻辑
async generateToken(user: any) {
// 自定义 payload 结构
const payload = {
sub: user.id, // 标准字段
username: user.username, // 业务字段
roles: user.roles, // 自定义字段
permissions: user.perms, // 自定义字段
customData: {
// 任意嵌套结构
department: user.dept,
level: user.level
}
}
// 使用自己的加密方式
return this.jwtService.sign(payload, {
algorithm: 'RS256', // 支持 RSA 非对称加密
expiresIn: '7d'
// 更多自定义配置...
})
}
}支持的自定义选项:
- Payload 结构:任意自定义字段,
AuthPayload类型支持索引签名 - 加密算法:支持 HS256、HS384、HS512、RS256、RS384、RS512 等
- 签名密钥:支持对称密钥和 RSA 非对称密钥
- 过期策略:自定义
expiresIn、notBefore等 - 多租户支持:通过
iss、aud字段实现
AuthGuard 会自动解析你的自定义 payload,并注入到 request.user 中:
@Get('profile')
getProfile(@AuthDecorator() auth) {
// 可以访问所有自定义字段
console.log(auth.roles) // 自定义字段
console.log(auth.permissions) // 自定义字段
console.log(auth.customData) // 嵌套对象
return auth
}TypeORM 实体基类
TypeOrmDocument 是一个基础实体类,提供了常用的审计字段和自动时间戳管理功能。
基础使用
import { Entity, PrimaryColumn } from 'typeorm'
import { TypeOrmDocument } from '@maxtan/nest-core'
@Entity('users')
export class User extends TypeOrmDocument {
@PrimaryColumn({ type: 'bigint' })
id: string
@Column()
username: string
@Column()
email: string
}继承 TypeOrmDocument 后,实体会自动拥有以下字段:
| 字段名 | 数据库列名 | 数据库类型 | 应用层类型 | 说明 |
|---|---|---|---|---|
createdAt |
create_at |
bigint | number | 创建时间(毫秒时间戳),插入时自动设置 |
createBy |
create_by |
varchar | string | 创建者(可为空),需手动设置 |
updateAt |
update_at |
bigint | number | 更新时间(毫秒时间戳),插入或更新时自动设置 |
updateBy |
update_by |
varchar | string | 更新者(可为空),需手动设置 |
deletedAt |
deleted_at |
bigint | string | 删除时间(软删除标记),默认不返回(需显式查询) |
自动时间戳
TypeOrmDocument 使用 TypeORM 的生命周期钩子自动管理时间戳:
// 插入时
const user = new User()
user.username = 'john'
// createdAt 和 updateAt 会自动设置为当前时间
await repository.save(user)
// 更新时
user.email = 'john@example.com'
// updateAt 会自动更新为当前时间,createdAt 保持不变
await repository.save(user)软删除支持
deletedAt 字段用于软删除功能,默认设置了 select: false 和索引,不会在查询时自动返回:
import { IsNull, Not } from 'typeorm'
// 查询未删除的记录(推荐)
// 由于 deletedAt 默认 select: false,不需要额外过滤
const activeUsers = await repository.find()
// 如需显式过滤未删除记录
const activeUsers = await repository.find({
where: { deletedAt: IsNull() }
})
// 查询已删除的记录(需显式选择 deletedAt)
const deletedUsers = await repository.find({
where: { deletedAt: Not(IsNull()) },
select: ['id', 'username', 'deletedAt'], // 必须显式指定
withDeleted: true // TypeORM 选项
})
// 软删除(手动设置 deletedAt)
user.deletedAt = String(Date.now()) // 注意:deletedAt 是 string 类型
await repository.save(user)
// 恢复软删除的记录
user.deletedAt = null
await repository.save(user)注意事项:
deletedAt字段类型为string(不是number),设置时需转换- 默认
select: false,查询已删除记录时必须显式指定 - 该字段已建立索引,查询性能良好
时间戳转换
时间戳字段在数据库中存储为 bigint 类型,在应用层通过 TypeOrmTimeTransformer 转换器自动转换为 number 类型:
import { TypeOrmTimeTransformer } from '@maxtan/nest-core'
// TypeOrmTimeTransformer 自动处理 bigint 和 number 之间的转换
// - 存入数据库:number -> bigint
// - 从数据库读取:bigint -> number
const user = await repository.findOne({ where: { id: '123' } })
console.log(user.createdAt) // 1700000000000 (number)
console.log(typeof user.createdAt) // "number"
// 可以直接用于 JSON 响应和日期转换
return user // 自动序列化为数字
const date = new Date(user.createdAt) // 直接转换,无需 Number()自定义创建者和更新者
你可以在业务逻辑中手动设置创建者和更新者:
// 创建时设置创建者
const user = new User()
user.username = 'john'
user.createBy = currentUserId // 手动设置
await repository.save(user)
// 更新时设置更新者
user.email = 'john@example.com'
user.updateBy = currentUserId // 手动设置
await repository.save(user)或者结合拦截器自动设置:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'
import { Observable } from 'rxjs'
import { tap } from 'rxjs/operators'
@Injectable()
export class AuditInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest()
const userId = request.user?.id // 从认证信息中获取用户ID
return next.handle().pipe(
tap((data) => {
// 在保存实体前设置审计字段
if (data && userId) {
if (!data.createBy) data.createBy = userId
data.updateBy = userId
}
})
)
}
}雪花算法
雪花算法用于生成全局唯一的分布式 ID,基于 @sapphire/snowflake 实现:
基础使用
import { createSnowflake } from '@maxtan/nest-core'
// 创建默认雪花算法实例
const snowflake = createSnowflake()
// 生成唯一ID
const id = snowflake.generateSnowflakeId()
console.log(id) // 输出类似: "1234567890123456789"自定义配置
import { createSnowflake } from '@maxtan/nest-core'
// 自定义工作节点ID和起始时间
const snowflake = createSnowflake({
workerId: 1, // 工作节点ID
epoch: new Date('2024-01-01T00:00:00.000Z') // 自定义起始时间
})
const id = snowflake.generateSnowflakeId()支持的配置选项
interface SnowflakeOptions {
workerId?: number // 工作节点ID,默认为1
epoch?: number | string | Date // 起始时间,默认为2024-01-01 00:00:00 UTC
}实例缓存
相同配置参数的实例会被自动缓存,避免重复创建:
// 这两个调用会返回同一个实例
const snowflake1 = createSnowflake({ workerId: 1 })
const snowflake2 = createSnowflake({ workerId: 1 })
console.log(snowflake1 === snowflake2) // true异常过滤器
异常过滤器会捕获应用中的所有异常,并以统一的格式返回响应,同时记录详细的错误日志:
import { APP_FILTER } from '@nestjs/core'
import { AllExceptionsFilter } from '@maxtan/nest-core'
@Module({
providers: [
{
provide: APP_FILTER,
useClass: AllExceptionsFilter
}
]
})
export class AppModule {}配置参考
AllLoggerOptions
| 参数 | 类型 | 描述 |
|---|---|---|
useConsole |
boolean | 是否输出到控制台,默认 true |
maxDays |
number | 日志文件保留天数,默认 30 天 |
maxSize |
string | 单个日志文件最大大小,默认 '20m' |
sls |
SlsAppenderOptions | 阿里云 SLS 配置(可选) |
SlsAppenderOptions
| 参数 | 类型 | 描述 |
|---|---|---|
accessKeyId |
string | 阿里云访问密钥ID |
accessKeySecret |
string | 阿里云访问密钥密码 |
endpoint |
string | SLS服务端点,默认'cn-hangzhou.log.aliyuncs.com' |
projectName |
string | SLS项目名称 |
logStoreName |
string | SLS日志存储名称 |
topic |
string | 日志主题,默认'nest-boot' |
source |
string | 日志来源,默认'source' |
env |
string | 环境标识,默认'production' |
AuthModuleOptions
| 参数 | 类型 | 描述 |
|---|---|---|
secret |
string | 用于签名 JWT 的密钥 |
signOptions |
JwtSignOptions | JWT 签名选项,包含过期时间、发行方等配置 |
SnowflakeOptions
| 参数 | 类型 | 描述 |
|---|---|---|
workerId |
number | 工作节点ID,用于分布式环境中区分不同节点 |
epoch |
number | string | Date | 起始时间,默认为2025-01-01 00:00:00 UTC |
最佳实践
在启动时配置全局日志:使用
createLogger函数在应用初始化时配置日志系统,确保所有日志都能被正确捕获。使用不同的日志级别:根据信息的重要程度选择合适的日志级别,如调试信息使用 debug,错误信息使用 error 等。
区分环境配置:
- 开发环境:启用控制台日志,使用较低的日志级别(如 debug)
- 生产环境:可关闭控制台日志,使用较高的日志级别(如 info 或 warn),启用 SLS 日志
合理设置日志保留期:根据存储空间和审计要求设置合适的
maxDays值,默认 30 天,并启用日志压缩。错误处理结合异常过滤器:使用 AllExceptionsFilter 统一处理异常,确保所有异常都被正确记录,并带有错误追踪 ID。
利用阿里云 SLS 进行日志分析:在生产环境中使用阿里云 SLS 进行集中式日志管理和分析,方便问题排查。
合理使用缓存:
- 对频繁访问但不常变化的数据使用缓存
- 设置合理的过期时间
- 使用
setObject/getObject自动处理 JSON 序列化 - 利用批量操作(
mget/mset)提升性能
操作日志记录最佳实践:
- 通过
OperationModule.forRoot()全局启用自动日志记录 - 使用
excludePaths排除健康检查、监控等不需要记录的路径 - 全局不启用
request和response,避免日志过大 - 对重要操作使用
@Operation装饰器按需启用请求和响应记录 - 利用自动脱敏保护敏感信息
- 利用
operationId关联业务日志和错误堆栈
- 通过
授权与认证安全:
- 使用足够长且复杂的 JWT 密钥
- 设置合理的 Token 过期时间
- 考虑在生产环境中使用非对称加密算法(RS256)
- 实现 Token 刷新机制以提高安全性
- 利用多种 Token 提取策略支持不同场景
雪花算法使用建议:
- 在分布式环境中为不同节点设置不同的 workerId
- 确保各节点的系统时间同步,避免时钟回拨问题
- 根据业务需求选择合适的起始时间(epoch)
- 利用实例缓存特性,避免重复创建相同配置的实例
- 使用加密安全的随机数生成器
数据验证优化:
- 利用增强的 ValidationPipe 处理嵌套对象验证
- 根据需要设置
showAllErrors显示所有错误 - 使用自定义错误分隔符优化错误显示
事务管理:
- 使用
runInTransaction自动管理事务生命周期 - 避免手动管理事务的提交、回滚和释放
- 使用
TypeORM 实体设计:
- 继承
TypeOrmDocument获得标准审计字段(创建时间、更新时间、创建者、更新者、软删除标记) - 利用
@BeforeInsert和@BeforeUpdate钩子自动管理时间戳,无需手动设置 - 时间戳字段在数据库中存储为
bigint,在应用层自动转换为number,便于计算和 JSON 序列化 - 使用
deletedAt实现软删除,保留历史数据(注意:deletedAt为string类型) deletedAt默认不返回(select: false),查询已删除记录时需显式指定- 结合拦截器或装饰器自动设置
createBy和updateBy字段 - 所有审计字段均已建立索引,支持高效查询
- 继承
许可证
ISC