如何基于内容优化TypeORM权限定义?NestJS+SQL Server场景
优化用户内容过滤的实现方案
1. 用TypeORM查询构建器关联权限表(推荐)
直接通过JOIN关联权限表,把两次数据库请求合并成一次,避免额外的权限ID查询步骤。以查询用户可见Asset为例:
public async findAllAssetsByUser(queryParams: QueryParamsDto, userId?: string): Promise<Asset[]> { const queryBuilder = this.assetsRepo.createQueryBuilder('asset'); // 关联BuildingUserPermission表,过滤用户有权限的建筑 queryBuilder .innerJoin('building_user_permission', 'bup', 'asset.buildingId = bup.buildingId') .where('bup.userId = :userId', { userId }) // 追加业务查询条件 .andWhere('asset.status = :status', { status: queryParams.status }) // 分页与排序 .skip(queryParams.skip) .take(queryParams.take) .orderBy('asset.createdAt', 'DESC'); return queryBuilder.getMany(); }
如果需要同时校验Client层级的权限(比如用户需先拥有Client权限才能访问下属建筑),可以多层JOIN:
queryBuilder .innerJoin('building', 'b', 'asset.buildingId = b.id') .innerJoin('client_user_permission', 'cup', 'b.clientId = cup.clientId') .where('cup.userId = :userId', { userId });
2. 利用TypeORM实体关联映射
如果你的实体已经定义了多对多关系(比如User和Building通过BuildingUserPermission关联),可以直接通过实体关系过滤,代码更简洁:
首先确保User实体的关系定义:
@Entity() export class User { @PrimaryGeneratedColumn() id: string; @ManyToMany(() => Building) @JoinTable({ name: 'building_user_permission', joinColumn: { name: 'userId', referencedColumnName: 'id' }, inverseJoinColumn: { name: 'buildingId', referencedColumnName: 'id' }, }) allowedBuildings: Building[]; }
然后查询时直接通过关联关系过滤:
public async findAllAssetsByUser(queryParams: QueryParamsDto, userId?: string): Promise<Asset[]> { return this.assetsRepo.find({ where: { building: { users: { id: userId } }, status: queryParams.status }, relations: ['building', 'building.users'], skip: queryParams.skip, take: queryParams.take, order: { createdAt: 'DESC' } }); }
底层会自动生成JOIN语句,同样是一次查询完成权限过滤。
3. 自定义数据库视图(适合复杂权限场景)
如果权限逻辑涉及多层级关联(比如Country→Client→Building→Asset),可以把权限过滤逻辑封装成数据库视图,再映射为TypeORM实体直接查询:
创建视图示例:
CREATE VIEW v_user_allowed_assets AS SELECT a.* FROM Asset a JOIN Building b ON a.buildingId = b.id JOIN Client c ON b.clientId = c.id JOIN BuildingUserPermission bup ON b.id = bup.buildingId WHERE bup.userId = @userId;
映射视图为实体:
@ViewEntity({ name: 'v_user_allowed_assets' }) export class UserAllowedAsset { @PrimaryColumn() id: string; @Column() name: string; // 其他Asset字段... }
查询时直接操作视图实体:
public async findAllAssetsByUser(queryParams: QueryParamsDto, userId?: string): Promise<UserAllowedAsset[]> { return this.userAllowedAssetRepo.find({ where: { userId }, skip: queryParams.skip, take: queryParams.take }); }
这种方式适合权限规则固定且复杂的场景,能大幅简化应用层代码。
4. 使用TypeORM子查询替代两次查询
如果不想用JOIN,也可以把权限ID查询改成子查询,合并成一次数据库请求:
public async findAllAssetsByUser(queryParams: QueryParamsDto, userId?: string): Promise<Asset[]> { const subQuery = this.buildingUserPermissionRepo .createQueryBuilder('bup') .select('bup.buildingId') .where('bup.userId = :userId', { userId }); return this.assetsRepo.find({ where: { buildingId: In(subQuery.getQuery()), status: queryParams.status }, parameters: { userId }, skip: queryParams.skip, take: queryParams.take }); }
这种方式逻辑和你原来的IN查询类似,但避免了两次数据库请求。
内容的提问来源于stack exchange,提问作者Jorge
相关产品推荐
相关产品推荐

