好的,用户之前已经询问过后端选择Java还是Node.js的问题,现在他们决定使用Node.js,特别是考虑到前端是TypeScript实现的小程序,可能希望保持技术栈的一致性。用户需要更详细的服务框架和公共能力的实现。
首先,我需要回顾之前的对话,确保理解用户的需求。用户之前得到过Java和Node.js的对比,现在选择了Node.js,所以需要深入Node.js的后端设计,特别是NestJS框架的应用。用户可能希望后端能够与前端TypeScript无缝集成,利用相同的语言特性,提高开发效率。
接下来,用户需要详细的服务框架和公共能力。公共能力可能包括日志记录、错误处理、配置管理、数据库集成、缓存、鉴权、接口文档、任务队列等。需要确保这些模块是可扩展、易于维护,并且符合最佳实践。
考虑到用户使用的是TypeScript,NestJS是一个很好的选择,因为它支持TypeScript,并且提供了模块化的结构,类似于Angular,这对前端开发者来说可能更熟悉。需要展示如何组织项目结构,每个模块的职责,以及如何集成各种公共能力。
用户可能还需要监控、性能优化和安全性方面的考虑,比如请求追踪、速率限制、数据验证等。此外,部署和持续集成/持续部署(CI/CD)的配置也是重要部分,尤其是Docker和Kubernetes的使用,这有助于项目的可扩展性和维护性。
需要提供具体的代码示例,比如使用NestJS的模块、控制器、服务、DTO和实体,以及如何实现全局的拦截器、过滤器、守卫等。同时,集成TypeORM进行数据库操作,使用Swagger生成API文档,使用BullMQ处理队列任务,都是关键点。
另外,用户可能关注如何保证代码质量和规范,所以需要包括ESLint和Prettier的配置,以及单元测试和集成测试的设置。日志系统需要支持多种传输方式,并结构化日志数据,方便后续分析。
最后,确保整个架构是可扩展的,能够处理高并发,并且有良好的错误处理机制,比如全局异常过滤器和自定义业务异常。安全方面,JWT鉴权和数据加密也是必须考虑的部分。
总结来说,需要构建一个结构清晰、模块化、具备完整公共能力的NestJS后端服务,与前端TypeScript小程序良好集成,同时注重性能、安全和可维护性。
以下是基于NestJS框架的详细后端服务实现方案,深度整合TypeScript工程化实践:
全量服务架构设计
├── src
│ ├── core # 核心框架
│ │ ├── decorators # 自定义装饰器
│ │ ├── guards # 鉴权守卫
│ │ ├── interceptors # 全局拦截器
│ │ └── filters # 异常过滤器
│ ├── modules # 业务模块
│ │ └── user
│ │ ├── user.module.ts
│ │ ├── user.controller.ts
│ │ ├── user.service.ts
│ │ ├── user.entity.ts
│ │ └── dto # 数据传输对象
│ ├── common # 公共能力
│ │ ├── cache # 缓存模块
│ │ ├── logger # 日志系统
│ │ ├── queue # 任务队列
│ │ └── utils # 工具库
│ ├── config # 配置中心
│ ├── middleware # 全局中间件
│ └── main.ts # 入口文件
├── test # 测试用例
├── docker-compose.yml # 容器编排
├── package.json
└── nest-cli.json
核心公共能力实现
1. 增强型日志系统
// common/logger/log.service.ts
@Injectable()
export class LogService {
private readonly logger: Logger;
constructor(config: ConfigService) {
const transports: Transport[] = [
new transports.Console({ format: format.combine(format.timestamp(), format.json()) }),
new transports.File({ filename: 'logs/combined.log' })
];
if (config.get('ENV') === 'production') {
transports.push(
new transports.Http({
host: 'log-collector.domain.com',
path: '/ingest',
ssl: true
})
);
}
this.logger = createLogger({
level: config.get('LOG_LEVEL'),
transports,
defaultMeta: { service: 'backend-service' }
});
}
@LogTrace() // 自定义方法装饰器
log(level: LogLevel, message: string, meta?: Record<string, any>) {
this.logger.log(level, message, {
...meta,
traceId: RequestContext.currentRequestId()
});
}
}
2. 统一响应拦截器
// core/interceptors/response.interceptor.ts
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
return next.handle().pipe(
map(data => ({
code: 0,
data,
requestId: request.requestId,
timestamp: Date.now()
})),
tap(() => this.logAccess(request))
);
}
private logAccess(request: Request) {
const { method, originalUrl, ip, headers } = request;
const userAgent = headers['user-agent'] || '';
const responseTime = Date.now() - request.startTime;
this.logService.info('API_ACCESS', {
method,
path: originalUrl,
clientIp: ip,
userAgent,
responseTime
});
}
}
3. 分布式追踪实现
// core/middleware/tracing.middleware.ts
@Injectable()
export class TracingMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const requestId = req.headers['x-request-id'] || uuidv4();
// 注入追踪上下文
RequestContext.create({
requestId,
startTime: Date.now(),
clientIp: req.ip
}, next);
// 设置响应头
res.setHeader('X-Request-ID', requestId);
res.setHeader('X-Server-Name', os.hostname());
}
}
业务模块深度实现
用户模块示例
// modules/user/user.controller.ts
@Controller('users')
@UseGuards(JwtAuthGuard, RateLimitGuard)
@ApiTags('用户管理')
export class UserController {
constructor(
private readonly userService: UserService,
private readonly cacheManager: Cache
) {}
@Post()
@ApiOperation({ summary: '创建用户' })
async createUser(@Body() createUserDto: CreateUserDto) {
const cachedUser = await this.cacheManager.get(`user:${createUserDto.mobile}`);
if (cachedUser) throw new ConflictException('用户已存在');
return this.userService.create(createUserDto);
}
@Get(':id')
@CacheTTL(60) // 自定义缓存装饰器
async getUserProfile(@Param('id') userId: string) {
return this.userService.findById(userId);
}
}
// modules/user/user.service.ts
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly smsService: SmsService
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const existingUser = await this.userRepository.findOne({
where: { mobile: createUserDto.mobile }
});
if (existingUser) {
throw new BusinessException(ErrorCode.USER_ALREADY_EXISTS);
}
const user = this.userRepository.create({
...createUserDto,
password: await bcrypt.hash(createUserDto.password, 10)
});
await this.smsService.sendVerification(user.mobile);
return this.userRepository.save(user);
}
}
基础设施集成
数据库配置(TypeORM)
// config/database.config.ts
export default TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
type: 'mysql',
host: config.get('DB_HOST'),
port: config.get('DB_PORT'),
username: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_NAME'),
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: config.get('NODE_ENV') !== 'production',
logging: config.get('DB_LOGGING'),
extra: {
connectionLimit: 10,
waitForConnections: true
}
}),
inject: [ConfigService]
});
队列系统完整实现
// common/queue/queue.module.ts
@Module({
imports: [ConfigModule, BullModule.registerQueue({ name: 'email' })],
providers: [
{
provide: 'QUEUE_CONNECTION',
useFactory: (config: ConfigService) => new IORedis({
host: config.get('REDIS_HOST'),
port: config.get('REDIS_PORT'),
maxRetriesPerRequest: null,
enableReadyCheck: false
}),
inject: [ConfigService]
},
{
provide: 'EMAIL_QUEUE',
useFactory: (conn: IORedis) => new Queue('email', {
connection: conn,
defaultJobOptions: {
attempts: 3,
backoff: { type: 'exponential', delay: 1000 }
}
}),
inject: ['QUEUE_CONNECTION']
}
],
exports: ['EMAIL_QUEUE']
})
export class QueueModule {}
// 队列处理器(独立进程)
// src/consumers/email.consumer.ts
@Processor('email')
export class EmailConsumer {
constructor(
private readonly mailService: MailService,
private readonly logger: Logger
) {}
@Process('welcome')
async handleWelcomeEmail(job: Job<{ userId: string }>) {
try {
const user = await this.userService.findById(job.data.userId);
await this.mailService.sendWelcomeEmail(user);
this.logger.log(`Welcome email sent to ${user.email}`);
} catch (e) {
this.logger.error('邮件发送失败', e.stack);
throw new Error(e);
}
}
@OnQueueFailed()
handleFailedJob(job: Job, err: Error) {
this.logger.error(`任务失败: ${job.id}`, {
jobData: job.data,
error: err.message
});
}
}
安全防护完整实现
// core/guards/jwt.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
handleRequest(err: any, user: any, info: any, context: Context) {
if (err || !user) {
throw err || new UnauthorizedException('身份验证失败');
}
// 接口权限检查
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (roles && !roles.some(role => user.roles.includes(role))) {
throw new ForbiddenException('权限不足');
}
return user;
}
}
// 配套策略配置
// config/auth.config.ts
export const JwtStrategyConfig = {
secretOrKeyProvider: (request: Request, rawJwtToken: string) => {
return process.env.JWT_SECRET;
},
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => request?.cookies?.Authorization,
ExtractJwt.fromAuthHeaderAsBearerToken()
]),
ignoreExpiration: false
};
增强缓存模块
// common/cache/cache.decorator.ts
export const CacheResponse = (ttl: number = 60) => applyDecorators(
UseInterceptors(CacheInterceptor),
SetMetadata('cache-ttl', ttl),
ApiHeader({ name: 'X-Cache-Status', description: '缓存状态' })
);
// 缓存配置
// config/cache.config.ts
export const CacheConfig: CacheModuleOptions = {
ttl: 5, // 默认秒数
max: 100, // 最大缓存条目
store: redisStore({
socket: {
host: process.env.REDIS_HOST,
port: +process.env.REDIS_PORT
},
ttl: 60 * 60 // 单位秒
})
};
Docker 生产环境配置
# docker-compose.prod.yml
version: '3.8'
services:
app:
build:
context: .
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- REDIS_HOST=redis
- DB_HOST=mysql
depends_on:
- redis
- mysql
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
volumes:
redis_data:
mysql_data:
事务管理示例
// modules/payment/payment.service.ts
@Injectable()
export class PaymentService {
constructor(
@InjectEntityManager()
private readonly entityManager: EntityManager,
private readonly ledgerService: LedgerService
) {}
async createTransaction(paymentData: PaymentDto) {
return this.entityManager.transaction(async transactionalManager => {
// 1. 创建支付记录
const payment = transactionalManager.create(Payment, paymentData);
await transactionalManager.save(payment);
// 2. 更新账户余额
await transactionalManager.update(
Account,
{ userId: payment.userId },
{ balance: () => `balance - ${payment.amount}` }
);
// 3. 写入资金流水
await this.ledgerService.createFlow(transactionalManager, {
userId: payment.userId,
amount: payment.amount,
type: 'PAYMENT'
});
return payment;
});
}
}
Swagger API 文档集成
// main.ts
function buildSwagger(app: INestApplication) {
const config = new DocumentBuilder()
.setTitle('电商平台API')
.setDescription('## 全局响应格式\n```json\n{\n "code": 0,\n "data": {},\n "message": "success"\n}\n```')
.setVersion('1.2.0')
.addBearerAuth({ type: 'http', scheme: 'bearer' }, 'JWT')
.addCookieAuth('sessionId', { type: 'apiKey' })
.addServer('https://api.example.com', '生产环境')
.addApiKey({ type: 'apiKey', name: 'X-API-KEY' }, 'PartnerKey')
.build();
const document = SwaggerModule.createDocument(app, config, {
extraModels: [PaginatedResponse],