NestJS关注/取关功能实现报错排查与优化方案求助
NestJS 关注/取关功能问题排查与最优实现
核心错误分析与排查
1. TypeScript类型错误:number无法赋值给User类型
这种错误几乎都是因为直接将用户ID(number)赋值给了User类型的关联数组。比如在User实体中定义了followers: User[]或followings: User[],但代码里直接执行了user.followings.push(targetUserId),这里targetUserId是number类型,和数组的User类型不匹配。
排查步骤:
- 检查User实体的关联字段定义,确认是
User[]类型而非number[] - 查看service层代码,避免直接给关联数组push数字ID,必须传入User实例,或用TypeORM的关联API操作
2. SQL查询id为NaN的报错
报错提示“列nan不存在”,说明代码中把NaN当作用户ID传入了SQL语句,常见原因:
- 参数解析错误:比如
@Param('id')获取的是字符串,未做有效转换(如空字符串、非数字字符串转成NaN) - 当前用户ID获取失败:自定义的
GetCurrentUserId装饰器返回了undefined,后续转成NaN - 缺少参数验证:未对传入的ID做数字校验
排查步骤:
- 在service方法开头添加日志,打印传入的
userId和targetId,确认是否存在NaN - 控制器参数使用
ParseIntPipe强制转换为数字,比如@Param('id', ParseIntPipe) targetId: number - 检查
GetCurrentUserId装饰器的实现,确保能正确获取到登录用户的有效ID
最优关注/取关实现方案(基于TypeORM)
1. User实体定义(User.entity.ts)
正确配置多对多关联,指定中间表结构:
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) username: string; // 我的粉丝(关注我的人) @ManyToMany(() => User, user => user.followings) @JoinTable({ name: 'user_follow_relation', joinColumn: { name: 'followed_id', referencedColumnName: 'id' }, inverseJoinColumn: { name: 'follower_id', referencedColumnName: 'id' }, }) followers: User[]; // 我关注的人 @ManyToMany(() => User, user => user.followers) followings: User[]; }
2. Service层实现(users.service.ts)
使用TypeORM的关联API或QueryBuilder操作,避免类型错误,同时增加重复关注/取关的校验:
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './user.entity'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User>, ) {} // 关注用户 async followUser(followerId: number, targetId: number) { // 禁止关注自己 if (followerId === targetId) { throw new BadRequestException('无法关注自己'); } // 检查用户是否存在 const [follower, targetUser] = await Promise.all([ this.usersRepository.findOneBy({ id: followerId }), this.usersRepository.findOneBy({ id: targetId }), ]); if (!follower || !targetUser) { throw new NotFoundException('目标用户不存在'); } // 检查是否已关注 const isFollowed = await this.usersRepository .createQueryBuilder('user') .innerJoin('user.followings', 'following') .where('user.id = :followerId', { followerId }) .andWhere('following.id = :targetId', { targetId }) .getExists(); if (isFollowed) { return { message: '已关注该用户' }; } // 建立关联 await this.usersRepository .createQueryBuilder() .relation(User, 'followings') .of(followerId) .add(targetId); return { message: '关注成功' }; } // 取关用户 async unfollowUser(followerId: number, targetId: number) { const [follower, targetUser] = await Promise.all([ this.usersRepository.findOneBy({ id: followerId }), this.usersRepository.findOneBy({ id: targetId }), ]); if (!follower || !targetUser) { throw new NotFoundException('目标用户不存在'); } // 检查是否已关注 const isFollowed = await this.usersRepository .createQueryBuilder('user') .innerJoin('user.followings', 'following') .where('user.id = :followerId', { followerId }) .andWhere('following.id = :targetId', { targetId }) .getExists(); if (!isFollowed) { return { message: '未关注该用户' }; } // 移除关联 await this.usersRepository .createQueryBuilder() .relation(User, 'followings') .of(followerId) .remove(targetId); return { message: '取关成功' }; } // 获取用户关注列表 async getFollowings(userId: number) { return this.usersRepository.findOne({ where: { id: userId }, relations: ['followings'], select: ['id', 'username'], }); } // 获取用户粉丝列表 async getFollowers(userId: number) { return this.usersRepository.findOne({ where: { id: userId }, relations: ['followers'], select: ['id', 'username'], }); } }
3. Controller层实现(users.controller.ts)
使用管道确保参数合法性,简化逻辑:
import { Controller, Post, Delete, Param, UsePipes, ValidationPipe, Get } from '@nestjs/common'; import { UsersService } from './users.service'; import { ParseIntPipe } from '@nestjs/common'; import { GetCurrentUserId } from '../auth/decorators/get-current-user-id.decorator'; @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Post(':targetId/follow') @UsePipes(ValidationPipe) followUser( @GetCurrentUserId() userId: number, @Param('targetId', ParseIntPipe) targetId: number, ) { return this.usersService.followUser(userId, targetId); } @Delete(':targetId/unfollow') @UsePipes(ValidationPipe) unfollowUser( @GetCurrentUserId() userId: number, @Param('targetId', ParseIntPipe) targetId: number, ) { return this.usersService.unfollowUser(userId, targetId); } @Get(':userId/followings') getFollowings(@Param('userId', ParseIntPipe) userId: number) { return this.usersService.getFollowings(userId); } @Get(':userId/followers') getFollowers(@Param('userId', ParseIntPipe) userId: number) { return this.usersService.getFollowers(userId); } }
内容的提问来源于stack exchange,提问作者Vladon305
相关产品推荐
相关产品推荐

