2
0
Просмотр исходного кода

✨ feat(crud): 增强通用CRUD服务的关联查询能力

- 改进关联别名生成策略,使用下划线连接嵌套路径确保一致性
- 支持关联字段搜索,格式为relation.field或relation.nestedRelation.field
- 实现关联字段条件查询,支持IN、LIKE和范围查询等多种条件类型
- 优化别名生成逻辑,解决嵌套关联查询时的别名冲突问题
yourname 6 месяцев назад
Родитель
Сommit
907b5d08c7
1 измененных файлов с 42 добавлено и 13 удалено
  1. 42 13
      src/server/utils/generic-crud.service.ts

+ 42 - 13
src/server/utils/generic-crud.service.ts

@@ -39,24 +39,43 @@ export abstract class GenericCrudService<T extends ObjectLiteral> {
     const query = this.repository.createQueryBuilder('entity');
 
     // 添加关联关系(支持嵌套关联,如 ['contract.client'])
+    // 使用一致的别名生成策略,确保搜索时能正确引用关联字段
     if (relations.length > 0) {
-      relations.forEach((relation, relationIndex) => {
+      relations.forEach((relation) => {
         const parts = relation.split('.');
         let currentAlias = 'entity';
         
         parts.forEach((part, index) => {
-          const newAlias = index === 0 ? part : `${currentAlias}_${relationIndex}`;
+          // 生成一致的别名:对于嵌套关联,使用下划线连接路径
+          const newAlias = index === 0 ? part : parts.slice(0, index + 1).join('_');
           query.leftJoinAndSelect(`${currentAlias}.${part}`, newAlias);
           currentAlias = newAlias;
         });
       });
     }
 
-    // 关键词搜索
+    // 关键词搜索 - 支持关联字段搜索(格式:relation.field 或 relation.nestedRelation.field)
     if (keyword && searchFields && searchFields.length > 0) {
-      query.andWhere(`(${searchFields.map(field => `entity.${field} LIKE :keyword`).join(' OR ')})`, {
-        keyword: `%${keyword}%`
+      const searchConditions: string[] = [];
+      const searchParams: Record<string, string> = { keyword: `%${keyword}%` };
+
+      searchFields.forEach((field) => {
+        // 检查是否为关联字段(包含点号)
+        if (field.includes('.')) {
+          const parts = field.split('.');
+          const alias = parts.slice(0, -1).join('_'); // 使用下划线连接关系路径作为别名
+          const fieldName = parts[parts.length - 1];
+          
+          searchConditions.push(`${alias}.${fieldName} LIKE :keyword`);
+        } else {
+          // 普通字段搜索
+          searchConditions.push(`entity.${field} LIKE :keyword`);
+        }
       });
+
+      if (searchConditions.length > 0) {
+        query.andWhere(`(${searchConditions.join(' OR ')})`, searchParams);
+      }
     }
 
     // 条件查询
@@ -74,38 +93,48 @@ export abstract class GenericCrudService<T extends ObjectLiteral> {
         if (value !== undefined && value !== null && value !== '') {
           const fieldName = key.startsWith('_') ? key.substring(1) : key;
           
+          // 检查是否为关联字段(包含点号)
+          let tableAlias = 'entity';
+          let actualFieldName = fieldName;
+          
+          if (fieldName.includes('.')) {
+            const parts = fieldName.split('.');
+            tableAlias = parts.slice(0, -1).join('_'); // 使用下划线连接关系路径作为别名
+            actualFieldName = parts[parts.length - 1];
+          }
+          
           // 支持不同类型的筛选
           if (Array.isArray(value)) {
             // 数组类型:IN查询
             if (value.length > 0) {
-              query.andWhere(`entity.${fieldName} IN (:...${key})`, { [key]: value });
+              query.andWhere(`${tableAlias}.${actualFieldName} IN (:...${key})`, { [key]: value });
             }
           } else if (typeof value === 'string' && value.includes('%')) {
             // 模糊匹配
-            query.andWhere(`entity.${fieldName} LIKE :${key}`, { [key]: value });
+            query.andWhere(`${tableAlias}.${actualFieldName} LIKE :${key}`, { [key]: value });
           } else if (typeof value === 'object' && value !== null) {
             // 范围查询
             if ('gte' in value) {
-              query.andWhere(`entity.${fieldName} >= :${key}_gte`, { [`${key}_gte`]: value.gte });
+              query.andWhere(`${tableAlias}.${actualFieldName} >= :${key}_gte`, { [`${key}_gte`]: value.gte });
             }
             if ('gt' in value) {
-              query.andWhere(`entity.${fieldName} > :${key}_gt`, { [`${key}_gt`]: value.gt });
+              query.andWhere(`${tableAlias}.${actualFieldName} > :${key}_gt`, { [`${key}_gt`]: value.gt });
             }
             if ('lte' in value) {
-              query.andWhere(`entity.${fieldName} <= :${key}_lte`, { [`${key}_lte`]: value.lte });
+              query.andWhere(`${tableAlias}.${actualFieldName} <= :${key}_lte`, { [`${key}_lte`]: value.lte });
             }
             if ('lt' in value) {
-              query.andWhere(`entity.${fieldName} < :${key}_lt`, { [`${key}_lt`]: value.lt });
+              query.andWhere(`${tableAlias}.${actualFieldName} < :${key}_lt`, { [`${key}_lt`]: value.lt });
             }
             if ('between' in value && Array.isArray(value.between) && value.between.length === 2) {
-              query.andWhere(`entity.${fieldName} BETWEEN :${key}_start AND :${key}_end`, {
+              query.andWhere(`${tableAlias}.${actualFieldName} BETWEEN :${key}_start AND :${key}_end`, {
                 [`${key}_start`]: value.between[0],
                 [`${key}_end`]: value.between[1]
               });
             }
           } else {
             // 精确匹配
-            query.andWhere(`entity.${fieldName} = :${key}`, { [key]: value });
+            query.andWhere(`${tableAlias}.${actualFieldName} = :${key}`, { [key]: value });
           }
         }
       });