Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | 4x 7x 7x 4x 4x 4x 4x 4x 4x 1x 1x 3x 3x 3x 3x 3x 3x 1x 1x 2x 2x | /**
* TypeORM 分页适配器
*
* 注意:此模块需要安装 typeorm 作为 peerDependency
*/
import type {
Repository,
SelectQueryBuilder,
ObjectLiteral,
FindManyOptions,
FindOptionsOrder,
} from 'typeorm';
import type { PaginationParams, PaginatedResult } from '../types';
import {
normalizePagination,
calculateOffset,
calculateTotalPages,
hasNextPage,
hasPreviousPage,
} from '../utils';
/**
* 实体映射器函数类型
*
* 将实体类型转换为 DTO 类型的映射函数
*
* @typeParam TEntity - 实体类型
* @typeParam TDto - DTO 类型
*/
export type EntityMapper<TEntity, TDto> = (entity: TEntity) => TDto | Promise<TDto>;
/**
* TypeORM 分页查询选项(带映射器)
*
* 配置 TypeORM 分页查询的选项,必须提供映射函数
*
* @typeParam TEntity - 实体类型
* @typeParam TDto - DTO 类型
*/
export type TypeOrmPaginateOptionsWithMapper<TEntity extends ObjectLiteral, TDto> = {
/**
* 实体到 DTO 的映射函数
*
* 必须提供映射函数以确保类型安全
*/
mapper: EntityMapper<TEntity, TDto>;
/**
* 排序规则
*/
order?: FindOptionsOrder<TEntity>;
/**
* 查询条件
*/
where?: FindManyOptions<TEntity>['where'];
/**
* 关联关系
*/
relations?: FindManyOptions<TEntity>['relations'];
/**
* 选择字段
*/
select?: FindManyOptions<TEntity>['select'];
};
/**
* TypeORM 分页查询选项(无映射器)
*
* 配置 TypeORM 分页查询的选项,返回原始实体
*
* @typeParam TEntity - 实体类型
*/
export type TypeOrmPaginateOptionsNoMapper<TEntity extends ObjectLiteral> = {
/**
* 排序规则
*/
order?: FindOptionsOrder<TEntity>;
/**
* 查询条件
*/
where?: FindManyOptions<TEntity>['where'];
/**
* 关联关系
*/
relations?: FindManyOptions<TEntity>['relations'];
/**
* 选择字段
*/
select?: FindManyOptions<TEntity>['select'];
};
/**
* TypeORM 分页查询选项
*
* 配置 TypeORM 分页查询的选项
*
* @typeParam TEntity - 实体类型
* @typeParam TDto - DTO 类型
*/
export type TypeOrmPaginateOptions<TEntity extends ObjectLiteral, TDto = TEntity> =
| TypeOrmPaginateOptionsWithMapper<TEntity, TDto>
| TypeOrmPaginateOptionsNoMapper<TEntity>;
/**
* 类型守卫:检查选项是否包含映射器
*
* @typeParam TEntity - 实体类型
* @typeParam TDto - DTO 类型
* @param options - 查询选项
* @returns 是否包含映射器
*/
function hasMapper<TEntity extends ObjectLiteral, TDto>(
options:
| TypeOrmPaginateOptionsWithMapper<TEntity, TDto>
| TypeOrmPaginateOptionsNoMapper<TEntity>
| undefined,
): options is TypeOrmPaginateOptionsWithMapper<TEntity, TDto> {
return options !== undefined && 'mapper' in options;
}
/**
* 构建分页结果
*
* 内部辅助函数,构建分页结果对象
*
* @typeParam TDto - DTO 类型
* @param items - 数据项数组
* @param total - 总数
* @param page - 当前页
* @param pageSize - 每页大小
* @returns 分页结果
*/
function buildPaginatedResult<TDto>(
items: TDto[],
total: number,
page: number,
pageSize: number,
): PaginatedResult<TDto> {
/**
* 计算得到的总页数
*/
const totalPages = calculateTotalPages(total, pageSize);
return {
items,
total,
page,
pageSize,
totalPages,
hasNextPage: hasNextPage(page, totalPages),
hasPreviousPage: hasPreviousPage(page),
};
}
/**
* 使用 Repository 进行分页查询
*
* 通过 TypeORM Repository 进行分页查询,支持实体到 DTO 的映射
*
* 当提供 mapper 时返回映射后的 DTO,否则返回原始实体
*
* @typeParam TEntity - 实体类型
* @typeParam TDto - DTO 类型
* @param repository - TypeORM 仓库
* @param params - 分页参数
* @param options - 查询选项
* @returns 分页结果
*/
export async function paginateWithRepository<TEntity extends ObjectLiteral, TDto = TEntity>(
repository: Repository<TEntity>,
params: PaginationParams,
options?: TypeOrmPaginateOptions<TEntity, TDto>,
): Promise<PaginatedResult<TEntity | TDto>> {
/**
* 规范化后的分页参数
*/
const { page, pageSize } = normalizePagination(params);
/**
* 计算得到的偏移量
*/
const offset = calculateOffset(page, pageSize);
/**
* 默认排序规则
*/
const defaultOrder: FindOptionsOrder<TEntity> = { createdAt: 'DESC' };
/**
* TypeORM 查询选项配置
*/
const findOptions: FindManyOptions<TEntity> = {
skip: offset,
take: pageSize,
where: options?.where,
relations: options?.relations,
select: options?.select,
order: options?.order ?? defaultOrder,
};
/**
* 查询结果:实体数组和总数
*/
const [entities, total] = await repository.findAndCount(findOptions);
if (hasMapper(options)) {
/**
* 映射后的 DTO 数组
*/
const items = await Promise.all(entities.map(options.mapper));
return buildPaginatedResult(items, total, page, pageSize);
}
return buildPaginatedResult(entities, total, page, pageSize);
}
/**
* 使用 QueryBuilder 进行分页查询
*
* 通过 TypeORM QueryBuilder 进行分页查询,支持实体到 DTO 的映射
*
* 当提供 mapper 时返回映射后的 DTO,否则返回原始实体
*
* @typeParam TEntity - 实体类型
* @typeParam TDto - DTO 类型
* @param queryBuilder - TypeORM QueryBuilder
* @param params - 分页参数
* @param mapper - 实体到 DTO 的映射函数(可选)
* @returns 分页结果
*/
export async function paginateWithQueryBuilder<TEntity extends ObjectLiteral, TDto = TEntity>(
queryBuilder: SelectQueryBuilder<TEntity>,
params: PaginationParams,
mapper?: EntityMapper<TEntity, TDto>,
): Promise<PaginatedResult<TEntity | TDto>> {
/**
* 规范化后的分页参数
*/
const { page, pageSize } = normalizePagination(params);
/**
* 计算得到的偏移量
*/
const offset = calculateOffset(page, pageSize);
queryBuilder.skip(offset).take(pageSize);
/**
* 查询结果:实体数组和总数
*/
const [entities, total] = await queryBuilder.getManyAndCount();
if (mapper) {
/**
* 映射后的 DTO 数组
*/
const items = await Promise.all(entities.map(mapper));
return buildPaginatedResult(items, total, page, pageSize);
}
return buildPaginatedResult(entities, total, page, pageSize);
}
/**
* 创建分页查询构建器
*
* 创建一个用于分页查询的 TypeORM QueryBuilder
*
* @typeParam TEntity - 实体类型
* @param repository - TypeORM 仓库
* @param alias - 查询别名
* @returns QueryBuilder
*/
export function createPaginationQueryBuilder<TEntity extends ObjectLiteral>(
repository: Repository<TEntity>,
alias?: string,
): SelectQueryBuilder<TEntity> {
return repository.createQueryBuilder(alias ?? repository.metadata.name.toLowerCase());
}
|