@nest-omni/core 4.1.3-12 → 4.1.3-14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/cache/dependencies/db.dependency.d.ts +55 -6
  2. package/cache/dependencies/db.dependency.js +64 -13
  3. package/common/boilerplate.polyfill.js +1 -1
  4. package/file-upload/decorators/column.decorator.d.ts +151 -0
  5. package/file-upload/decorators/column.decorator.js +273 -0
  6. package/file-upload/decorators/csv-data.decorator.d.ts +17 -31
  7. package/file-upload/decorators/csv-data.decorator.js +45 -91
  8. package/file-upload/decorators/csv-import.decorator.d.ts +34 -0
  9. package/file-upload/decorators/csv-import.decorator.js +24 -0
  10. package/file-upload/decorators/examples/column-mapping.example.d.ts +76 -0
  11. package/file-upload/decorators/examples/column-mapping.example.js +122 -0
  12. package/file-upload/decorators/excel-data.decorator.d.ts +15 -29
  13. package/file-upload/decorators/excel-data.decorator.js +42 -82
  14. package/file-upload/decorators/index.d.ts +3 -2
  15. package/file-upload/decorators/index.js +20 -2
  16. package/file-upload/decorators/validate-data.decorator.d.ts +91 -0
  17. package/file-upload/decorators/validate-data.decorator.js +39 -0
  18. package/file-upload/dto/update-file.dto.d.ts +0 -1
  19. package/file-upload/dto/update-file.dto.js +0 -4
  20. package/file-upload/entities/file-metadata.entity.d.ts +6 -3
  21. package/file-upload/entities/file-metadata.entity.js +2 -10
  22. package/file-upload/entities/file.entity.d.ts +3 -18
  23. package/file-upload/entities/file.entity.js +0 -34
  24. package/file-upload/file-upload.module.d.ts +1 -1
  25. package/file-upload/file-upload.module.js +44 -16
  26. package/file-upload/index.d.ts +13 -2
  27. package/file-upload/index.js +21 -3
  28. package/file-upload/interceptors/file-upload.interceptor.d.ts +61 -8
  29. package/file-upload/interceptors/file-upload.interceptor.js +417 -257
  30. package/file-upload/interfaces/file-processor.interface.d.ts +93 -0
  31. package/file-upload/interfaces/file-processor.interface.js +2 -0
  32. package/file-upload/interfaces/file-upload-options.interface.d.ts +3 -46
  33. package/file-upload/interfaces/file-upload-options.interface.js +3 -0
  34. package/file-upload/interfaces/processor-options.interface.d.ts +102 -0
  35. package/file-upload/interfaces/processor-options.interface.js +2 -0
  36. package/file-upload/processors/csv.processor.d.ts +98 -0
  37. package/file-upload/processors/csv.processor.js +391 -0
  38. package/file-upload/processors/excel.processor.d.ts +130 -0
  39. package/file-upload/processors/excel.processor.js +547 -0
  40. package/file-upload/processors/image.processor.d.ts +199 -0
  41. package/file-upload/processors/image.processor.js +377 -0
  42. package/file-upload/services/file.service.d.ts +3 -0
  43. package/file-upload/services/file.service.js +39 -10
  44. package/file-upload/services/malicious-file-detector.service.d.ts +29 -3
  45. package/file-upload/services/malicious-file-detector.service.js +256 -57
  46. package/file-upload/utils/dynamic-import.util.d.ts +6 -2
  47. package/file-upload/utils/dynamic-import.util.js +17 -5
  48. package/http-client/decorators/http-client.decorators.d.ts +4 -2
  49. package/http-client/decorators/http-client.decorators.js +2 -1
  50. package/http-client/entities/http-log.entity.js +1 -9
  51. package/http-client/examples/proxy-from-environment.example.d.ts +133 -0
  52. package/http-client/examples/proxy-from-environment.example.js +410 -0
  53. package/http-client/http-client.module.js +65 -6
  54. package/http-client/interfaces/http-client-config.interface.d.ts +6 -0
  55. package/http-client/services/http-client.service.d.ts +8 -0
  56. package/http-client/services/http-client.service.js +61 -17
  57. package/http-client/services/logging.service.d.ts +1 -1
  58. package/http-client/services/logging.service.js +74 -58
  59. package/http-client/utils/index.d.ts +1 -0
  60. package/http-client/utils/index.js +1 -0
  61. package/http-client/utils/proxy-environment.util.d.ts +42 -0
  62. package/http-client/utils/proxy-environment.util.js +148 -0
  63. package/package.json +9 -5
  64. package/shared/service-registry.module.js +18 -0
  65. package/transaction/data-source.util.d.ts +142 -0
  66. package/transaction/data-source.util.js +330 -0
  67. package/transaction/index.d.ts +1 -0
  68. package/transaction/index.js +12 -1
  69. package/validators/is-exists.validator.d.ts +19 -2
  70. package/validators/is-exists.validator.js +27 -2
  71. package/validators/is-unique.validator.d.ts +12 -1
  72. package/validators/is-unique.validator.js +26 -1
@@ -1,5 +1,20 @@
1
1
  import { DataSource } from 'typeorm';
2
2
  import type { CacheDependency } from '../interfaces/cache-dependency.interface';
3
+ /**
4
+ * Options for DbDependency
5
+ */
6
+ export interface DbDependencyOptions {
7
+ /**
8
+ * Name for this dependency (used in cache key generation)
9
+ * Default: 'db'
10
+ */
11
+ name?: string;
12
+ /**
13
+ * Data source name to use for querying
14
+ * If not specified, uses the default data source
15
+ */
16
+ dataSourceName?: string;
17
+ }
3
18
  /**
4
19
  * Database-based cache dependency
5
20
  *
@@ -33,23 +48,57 @@ import type { CacheDependency } from '../interfaces/cache-dependency.interface';
33
48
  * ]
34
49
  * })
35
50
  * async getUserList(tenantId: string) { }
51
+ *
52
+ * // Use specific data source
53
+ * @Cacheable({
54
+ * key: (id) => `product:${id}:details`,
55
+ * dependencies: [
56
+ * new DbDependency(
57
+ * 'SELECT updated_at FROM products WHERE id = ?',
58
+ * (id) => [id],
59
+ * { dataSourceName: 'readReplicas' }
60
+ * )
61
+ * ]
62
+ * })
63
+ * async getProductDetails(id: string) { }
64
+ *
65
+ * // With custom name and data source
66
+ * new DbDependency(
67
+ * 'SELECT COUNT(*) FROM events',
68
+ * [],
69
+ * { name: 'eventCount', dataSourceName: 'analytics' }
70
+ * )
36
71
  * ```
37
72
  */
38
73
  export declare class DbDependency implements CacheDependency {
39
74
  private readonly sql;
40
75
  private readonly paramsFactory?;
41
- private readonly name?;
42
- private static dataSource;
43
- constructor(sql: string, paramsFactory?: (...args: any[]) => any[], name?: string);
76
+ private readonly options?;
77
+ private static fallbackDataSource;
78
+ private static defaultDataSourceName;
79
+ constructor(sql: string, paramsFactory?: (...args: any[]) => any[], options?: DbDependencyOptions);
44
80
  /**
45
- * Set the TypeORM DataSource for database queries
46
- * This should be called once during application initialization
81
+ * Set a fallback DataSource for database queries
82
+ * This is used when DataSourceUtil cannot provide a DataSource
83
+ * This method is deprecated - use DataSourceUtil.registerDataSource() instead
84
+ * @deprecated Use DataSourceUtil.registerDataSource() to register DataSources
47
85
  */
48
86
  static setDataSource(dataSource: DataSource): void;
49
87
  /**
50
- * Get the current DataSource
88
+ * Set the default data source name for DbDependency
89
+ * @param name The data source name (default: 'default')
90
+ */
91
+ static setDefaultDataSourceName(name: string): void;
92
+ /**
93
+ * Get the current fallback DataSource
94
+ * @deprecated Use DataSourceUtil.getDataSource() instead
51
95
  */
52
96
  static getDataSource(): DataSource | null;
97
+ /**
98
+ * Get DataSource for executing queries
99
+ * Priority: 1. Specific dataSourceName from options, 2. Default from DataSourceUtil, 3. Fallback
100
+ */
101
+ private getDataSource;
53
102
  getKey(): string;
54
103
  getData(): Promise<any>;
55
104
  isChanged(oldData: any): Promise<boolean>;
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DbDependency = void 0;
13
+ const data_source_util_1 = require("../../transaction/data-source.util");
13
14
  /**
14
15
  * Database-based cache dependency
15
16
  *
@@ -43,44 +44,93 @@ exports.DbDependency = void 0;
43
44
  * ]
44
45
  * })
45
46
  * async getUserList(tenantId: string) { }
47
+ *
48
+ * // Use specific data source
49
+ * @Cacheable({
50
+ * key: (id) => `product:${id}:details`,
51
+ * dependencies: [
52
+ * new DbDependency(
53
+ * 'SELECT updated_at FROM products WHERE id = ?',
54
+ * (id) => [id],
55
+ * { dataSourceName: 'readReplicas' }
56
+ * )
57
+ * ]
58
+ * })
59
+ * async getProductDetails(id: string) { }
60
+ *
61
+ * // With custom name and data source
62
+ * new DbDependency(
63
+ * 'SELECT COUNT(*) FROM events',
64
+ * [],
65
+ * { name: 'eventCount', dataSourceName: 'analytics' }
66
+ * )
46
67
  * ```
47
68
  */
48
69
  class DbDependency {
49
- constructor(sql, paramsFactory, name) {
70
+ constructor(sql, paramsFactory, options) {
50
71
  this.sql = sql;
51
72
  this.paramsFactory = paramsFactory;
52
- this.name = name;
73
+ this.options = options;
53
74
  if (!sql || typeof sql !== 'string') {
54
75
  throw new Error('DbDependency requires a valid SQL query');
55
76
  }
56
77
  }
57
78
  /**
58
- * Set the TypeORM DataSource for database queries
59
- * This should be called once during application initialization
79
+ * Set a fallback DataSource for database queries
80
+ * This is used when DataSourceUtil cannot provide a DataSource
81
+ * This method is deprecated - use DataSourceUtil.registerDataSource() instead
82
+ * @deprecated Use DataSourceUtil.registerDataSource() to register DataSources
60
83
  */
61
84
  static setDataSource(dataSource) {
62
- this.dataSource = dataSource;
85
+ this.fallbackDataSource = dataSource;
63
86
  }
64
87
  /**
65
- * Get the current DataSource
88
+ * Set the default data source name for DbDependency
89
+ * @param name The data source name (default: 'default')
90
+ */
91
+ static setDefaultDataSourceName(name) {
92
+ this.defaultDataSourceName = name;
93
+ }
94
+ /**
95
+ * Get the current fallback DataSource
96
+ * @deprecated Use DataSourceUtil.getDataSource() instead
66
97
  */
67
98
  static getDataSource() {
68
- return this.dataSource;
99
+ return this.fallbackDataSource;
100
+ }
101
+ /**
102
+ * Get DataSource for executing queries
103
+ * Priority: 1. Specific dataSourceName from options, 2. Default from DataSourceUtil, 3. Fallback
104
+ */
105
+ getDataSource() {
106
+ var _a;
107
+ // Try to get from DataSourceUtil first (supports dynamic data sources from transactions)
108
+ const dsName = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.dataSourceName) || DbDependency.defaultDataSourceName;
109
+ const dataSource = data_source_util_1.DataSourceUtil.getDataSourceSafe(dsName);
110
+ if (dataSource) {
111
+ return dataSource;
112
+ }
113
+ // Fallback to static dataSource for backward compatibility
114
+ if (DbDependency.fallbackDataSource) {
115
+ return DbDependency.fallbackDataSource;
116
+ }
117
+ throw new Error(`DbDependency requires DataSource '${dsName}' to be registered. ` +
118
+ `Use DataSourceUtil.registerDataSource('${dsName}', dataSource) ` +
119
+ `or DbDependency.setDataSource(dataSource) for fallback.`);
69
120
  }
70
121
  getKey() {
122
+ var _a;
71
123
  const queryHash = this.hashCode(this.sql);
72
- const name = this.name || 'db';
124
+ const name = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.name) || 'db';
73
125
  return `db:${name}:${queryHash}`;
74
126
  }
75
127
  getData() {
76
128
  return __awaiter(this, void 0, void 0, function* () {
77
129
  var _a;
78
- if (!DbDependency.dataSource) {
79
- throw new Error('DbDependency requires DataSource to be set. Call DbDependency.setDataSource(dataSource) during app initialization.');
80
- }
81
130
  try {
131
+ const dataSource = this.getDataSource();
82
132
  const params = ((_a = this.paramsFactory) === null || _a === void 0 ? void 0 : _a.call(this)) || [];
83
- const result = yield DbDependency.dataSource.query(this.sql, params);
133
+ const result = yield dataSource.query(this.sql, params);
84
134
  // Serialize result for comparison
85
135
  return JSON.stringify(result);
86
136
  }
@@ -115,4 +165,5 @@ class DbDependency {
115
165
  }
116
166
  }
117
167
  exports.DbDependency = DbDependency;
118
- DbDependency.dataSource = null;
168
+ DbDependency.fallbackDataSource = null;
169
+ DbDependency.defaultDataSourceName = 'default';
@@ -52,7 +52,7 @@ typeorm_1.SelectQueryBuilder.prototype.searchByString = function (q, columnNames
52
52
  }
53
53
  this.andWhere(new typeorm_1.Brackets((qb) => {
54
54
  for (const item of columnNames) {
55
- qb.orWhere(`${item} ILIKE :q`);
55
+ qb.orWhere(`${item} LIKE :q`);
56
56
  }
57
57
  }));
58
58
  if (options === null || options === void 0 ? void 0 : options.formStart) {
@@ -0,0 +1,151 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * CSV列名映射元数据键
4
+ */
5
+ export declare const CSV_COLUMN_METADATA = "csv:column";
6
+ /**
7
+ * Excel列名映射元数据键
8
+ */
9
+ export declare const EXCEL_COLUMN_METADATA = "excel:column";
10
+ /**
11
+ * 列标识类型
12
+ * 可以是列名(字符串)、列序号(数字)或Excel列名(A, B, C)
13
+ */
14
+ export type ColumnIdentifier = string | number;
15
+ /**
16
+ * CSV列装饰器
17
+ * 用于指定CSV文件中列与DTO属性的映射关系
18
+ * 支持列名、列序号(从0开始)或Excel列名(A, B, C)
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * export class UserImportDto {
23
+ * @CSVColumn('姓名') // 使用列名
24
+ * @CSVColumn(0) // 使用列序号(第一列)
25
+ * @CSVColumn('A') // 使用Excel列名
26
+ * @IsString()
27
+ * @Length(2, 50)
28
+ * name: string;
29
+ *
30
+ * @CSVColumn('年龄') // 使用列名
31
+ * @CSVColumn(1) // 使用列序号(第二列)
32
+ * @CSVColumn('B') // 使用Excel列名
33
+ * @IsNumber()
34
+ * @Min(0)
35
+ * @Max(150)
36
+ * age: number;
37
+ * }
38
+ * ```
39
+ */
40
+ export declare function CSVColumn(columnIdentifier: ColumnIdentifier): PropertyDecorator;
41
+ /**
42
+ * Excel列装饰器
43
+ * 用于指定Excel文件中列与DTO属性的映射关系
44
+ * 支持列名、列序号(从0开始)或Excel列名(A, B, C)
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * export class ProductImportDto {
49
+ * @ExcelColumn('产品名称') // 使用列名
50
+ * @ExcelColumn(0) // 使用列序号(第一列)
51
+ * @ExcelColumn('A') // 使用Excel列名
52
+ * @IsString()
53
+ * @Length(2, 200)
54
+ * name: string;
55
+ *
56
+ * @ExcelColumn('价格') // 使用列名
57
+ * @ExcelColumn(1) // 使用列序号(第二列)
58
+ * @ExcelColumn('B') // 使用Excel列名
59
+ * @IsNumber()
60
+ * @Min(0)
61
+ * price: number;
62
+ * }
63
+ * ```
64
+ */
65
+ export declare function ExcelColumn(columnIdentifier: ColumnIdentifier): PropertyDecorator;
66
+ /**
67
+ * 获取CSV列映射信息
68
+ * 返回列标识到属性名的映射Map
69
+ *
70
+ * @param dtoClass DTO类
71
+ * @returns Map<列标识, 属性名>
72
+ */
73
+ export declare function getCsvColumnMapping(dtoClass: any): Map<ColumnIdentifier, string>;
74
+ /**
75
+ * 获取Excel列映射信息
76
+ * 返回列标识到属性名的映射Map
77
+ *
78
+ * @param dtoClass DTO类
79
+ * @returns Map<列标识, 属性名>
80
+ */
81
+ export declare function getExcelColumnMapping(dtoClass: any): Map<ColumnIdentifier, string>;
82
+ /**
83
+ * Excel列名转数字(A=0, B=1, ..., AA=26, AB=27)
84
+ */
85
+ export declare function excelColumnToNumber(columnLetter: string): number;
86
+ /**
87
+ * 处理行数据
88
+ * 将原始数据行转换为DTO实例,支持多语言错误信息
89
+ * 支持列名、列序号和Excel列名(A, B, C)
90
+ *
91
+ * @param rowData 原始行数据
92
+ * @param dtoClass DTO类
93
+ * @param columnMapping 列映射
94
+ * @param type 数据类型(csv/excel)
95
+ * @returns 转换结果
96
+ */
97
+ export declare function processRowWithMapping<T>(rowData: any, dtoClass: new () => T, columnMapping: Map<ColumnIdentifier, string>, type: 'csv' | 'excel'): {
98
+ instance: T;
99
+ errors: ValidationError[];
100
+ };
101
+ /**
102
+ * 数字转Excel列名(0=A, 1=B, ..., 26=AA, 27=AB)
103
+ */
104
+ export declare function numberToExcelColumn(num: number): string;
105
+ /**
106
+ * 验证错误接口
107
+ */
108
+ interface ValidationError {
109
+ property: string;
110
+ value: any;
111
+ constraints: {
112
+ [key: string]: {
113
+ message: string;
114
+ values?: any[];
115
+ };
116
+ };
117
+ }
118
+ /**
119
+ * 格式化验证错误为多语言
120
+ * 支持中英文错误信息
121
+ *
122
+ * @param errors class-validator错误数组
123
+ * @returns 格式化后的错误信息
124
+ */
125
+ export declare function formatValidationErrors(errors: ValidationError[]): string[];
126
+ /**
127
+ * 创建CSV数据导入结果
128
+ * @param data 成功导入的数据
129
+ * @param errors 错误信息
130
+ * @param totalRows 总行数
131
+ * @param headers 表头信息
132
+ * @returns 导入结果对象
133
+ */
134
+ export declare function createImportResult<T>(data: T[], errors: {
135
+ row: number;
136
+ errors: string[];
137
+ data: any;
138
+ }[], totalRows: number, headers?: string[]): {
139
+ success: boolean;
140
+ data: T[];
141
+ errors: {
142
+ row: number;
143
+ errors: string[];
144
+ data: any;
145
+ }[];
146
+ totalCount: number;
147
+ successCount: number;
148
+ errorCount: number;
149
+ headers?: string[];
150
+ };
151
+ export {};
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXCEL_COLUMN_METADATA = exports.CSV_COLUMN_METADATA = void 0;
4
+ exports.CSVColumn = CSVColumn;
5
+ exports.ExcelColumn = ExcelColumn;
6
+ exports.getCsvColumnMapping = getCsvColumnMapping;
7
+ exports.getExcelColumnMapping = getExcelColumnMapping;
8
+ exports.excelColumnToNumber = excelColumnToNumber;
9
+ exports.processRowWithMapping = processRowWithMapping;
10
+ exports.numberToExcelColumn = numberToExcelColumn;
11
+ exports.formatValidationErrors = formatValidationErrors;
12
+ exports.createImportResult = createImportResult;
13
+ require("reflect-metadata");
14
+ /**
15
+ * CSV列名映射元数据键
16
+ */
17
+ exports.CSV_COLUMN_METADATA = 'csv:column';
18
+ /**
19
+ * Excel列名映射元数据键
20
+ */
21
+ exports.EXCEL_COLUMN_METADATA = 'excel:column';
22
+ /**
23
+ * CSV列装饰器
24
+ * 用于指定CSV文件中列与DTO属性的映射关系
25
+ * 支持列名、列序号(从0开始)或Excel列名(A, B, C)
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * export class UserImportDto {
30
+ * @CSVColumn('姓名') // 使用列名
31
+ * @CSVColumn(0) // 使用列序号(第一列)
32
+ * @CSVColumn('A') // 使用Excel列名
33
+ * @IsString()
34
+ * @Length(2, 50)
35
+ * name: string;
36
+ *
37
+ * @CSVColumn('年龄') // 使用列名
38
+ * @CSVColumn(1) // 使用列序号(第二列)
39
+ * @CSVColumn('B') // 使用Excel列名
40
+ * @IsNumber()
41
+ * @Min(0)
42
+ * @Max(150)
43
+ * age: number;
44
+ * }
45
+ * ```
46
+ */
47
+ function CSVColumn(columnIdentifier) {
48
+ return (target, propertyKey) => {
49
+ // target 是类的原型,我们需要将元数据存储在构造函数上
50
+ const constructor = target.constructor;
51
+ const columns = Reflect.getMetadata(exports.CSV_COLUMN_METADATA, constructor) || {};
52
+ columns[propertyKey] = columnIdentifier;
53
+ Reflect.defineMetadata(exports.CSV_COLUMN_METADATA, columns, constructor);
54
+ };
55
+ }
56
+ /**
57
+ * Excel列装饰器
58
+ * 用于指定Excel文件中列与DTO属性的映射关系
59
+ * 支持列名、列序号(从0开始)或Excel列名(A, B, C)
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * export class ProductImportDto {
64
+ * @ExcelColumn('产品名称') // 使用列名
65
+ * @ExcelColumn(0) // 使用列序号(第一列)
66
+ * @ExcelColumn('A') // 使用Excel列名
67
+ * @IsString()
68
+ * @Length(2, 200)
69
+ * name: string;
70
+ *
71
+ * @ExcelColumn('价格') // 使用列名
72
+ * @ExcelColumn(1) // 使用列序号(第二列)
73
+ * @ExcelColumn('B') // 使用Excel列名
74
+ * @IsNumber()
75
+ * @Min(0)
76
+ * price: number;
77
+ * }
78
+ * ```
79
+ */
80
+ function ExcelColumn(columnIdentifier) {
81
+ return (target, propertyKey) => {
82
+ // target 是类的原型,我们需要将元数据存储在构造函数上
83
+ const constructor = target.constructor;
84
+ const columns = Reflect.getMetadata(exports.EXCEL_COLUMN_METADATA, constructor) || {};
85
+ columns[propertyKey] = columnIdentifier;
86
+ Reflect.defineMetadata(exports.EXCEL_COLUMN_METADATA, columns, constructor);
87
+ };
88
+ }
89
+ /**
90
+ * 获取CSV列映射信息
91
+ * 返回列标识到属性名的映射Map
92
+ *
93
+ * @param dtoClass DTO类
94
+ * @returns Map<列标识, 属性名>
95
+ */
96
+ function getCsvColumnMapping(dtoClass) {
97
+ const constructor = dtoClass;
98
+ const mapping = new Map();
99
+ if (!constructor) {
100
+ return mapping;
101
+ }
102
+ // 从构造函数获取列映射元数据
103
+ const columns = Reflect.getMetadata(exports.CSV_COLUMN_METADATA, constructor) || {};
104
+ for (const [propertyKey, columnIdentifier] of Object.entries(columns)) {
105
+ mapping.set(columnIdentifier, propertyKey);
106
+ }
107
+ return mapping;
108
+ }
109
+ /**
110
+ * 获取Excel列映射信息
111
+ * 返回列标识到属性名的映射Map
112
+ *
113
+ * @param dtoClass DTO类
114
+ * @returns Map<列标识, 属性名>
115
+ */
116
+ function getExcelColumnMapping(dtoClass) {
117
+ const constructor = dtoClass;
118
+ const mapping = new Map();
119
+ if (!constructor) {
120
+ return mapping;
121
+ }
122
+ // 从构造函数获取列映射元数据
123
+ const columns = Reflect.getMetadata(exports.EXCEL_COLUMN_METADATA, constructor) || {};
124
+ for (const [propertyKey, columnIdentifier] of Object.entries(columns)) {
125
+ mapping.set(columnIdentifier, propertyKey);
126
+ }
127
+ return mapping;
128
+ }
129
+ /**
130
+ * Excel列名转数字(A=0, B=1, ..., AA=26, AB=27)
131
+ */
132
+ function excelColumnToNumber(columnLetter) {
133
+ let result = 0;
134
+ for (let i = 0; i < columnLetter.length; i++) {
135
+ const char = columnLetter.toUpperCase().charCodeAt(i) - 64; // A=65, so A-64=1
136
+ result = result * 26 + char;
137
+ }
138
+ return result - 1; // 转换为0基索引
139
+ }
140
+ /**
141
+ * 处理行数据
142
+ * 将原始数据行转换为DTO实例,支持多语言错误信息
143
+ * 支持列名、列序号和Excel列名(A, B, C)
144
+ *
145
+ * @param rowData 原始行数据
146
+ * @param dtoClass DTO类
147
+ * @param columnMapping 列映射
148
+ * @param type 数据类型(csv/excel)
149
+ * @returns 转换结果
150
+ */
151
+ function processRowWithMapping(rowData, dtoClass, columnMapping, type) {
152
+ const instance = new dtoClass();
153
+ const errors = [];
154
+ // 遍历所有映射的列
155
+ for (const [columnIdentifier, propertyKey] of columnMapping.entries()) {
156
+ let value;
157
+ if (typeof columnIdentifier === 'number') {
158
+ // 列序号(0基索引)
159
+ if (Array.isArray(rowData)) {
160
+ // 如果rowData是数组,直接使用索引
161
+ value = rowData[columnIdentifier];
162
+ }
163
+ else if (type === 'excel') {
164
+ // Excel数据:尝试将列序号转换为Excel列名
165
+ const columnLetter = numberToExcelColumn(columnIdentifier);
166
+ value = rowData[columnLetter];
167
+ }
168
+ else {
169
+ // CSV数据:如果无法通过索引获取,则跳过
170
+ value = undefined;
171
+ }
172
+ }
173
+ else if (typeof columnIdentifier === 'string') {
174
+ // 列名或Excel列名
175
+ // 优先通过列名获取值
176
+ if (rowData.hasOwnProperty(columnIdentifier)) {
177
+ value = rowData[columnIdentifier];
178
+ }
179
+ else if (type === 'excel' && /^[A-Za-z]+$/.test(columnIdentifier)) {
180
+ // 如果是Excel列名格式(如A, B, C),但数据中没有,尝试其他方式
181
+ value = undefined;
182
+ }
183
+ // 如果是数组且列标识是特殊的索引格式
184
+ else if (Array.isArray(rowData) && columnIdentifier.startsWith('__index_')) {
185
+ const index = parseInt(columnIdentifier.replace('__index_', ''), 10);
186
+ if (!isNaN(index) && index < rowData.length) {
187
+ value = rowData[index];
188
+ }
189
+ }
190
+ }
191
+ // 设置值到实例
192
+ instance[propertyKey] = value;
193
+ }
194
+ return { instance, errors };
195
+ }
196
+ /**
197
+ * 数字转Excel列名(0=A, 1=B, ..., 26=AA, 27=AB)
198
+ */
199
+ function numberToExcelColumn(num) {
200
+ let result = '';
201
+ let n = num + 1; // 转换为1基索引
202
+ while (n > 0) {
203
+ n--;
204
+ result = String.fromCharCode(65 + (n % 26)) + result;
205
+ n = Math.floor(n / 26);
206
+ }
207
+ return result;
208
+ }
209
+ /**
210
+ * 格式化验证错误为多语言
211
+ * 支持中英文错误信息
212
+ *
213
+ * @param errors class-validator错误数组
214
+ * @returns 格式化后的错误信息
215
+ */
216
+ function formatValidationErrors(errors) {
217
+ return errors.map(error => {
218
+ var _a, _b, _c, _d, _e;
219
+ const constraint = Object.values(error.constraints)[0];
220
+ // 英文错误信息直接使用
221
+ if (/^[a-zA-Z\s]+$/.test(constraint.message)) {
222
+ return `${error.property}: ${constraint.message}`;
223
+ }
224
+ // 中文错误信息特殊处理
225
+ const chineseMessages = {
226
+ 'isString': '必须是字符串',
227
+ 'isNumber': '必须是数字',
228
+ 'isBoolean': '必须是布尔值',
229
+ 'isDate': '必须是日期格式',
230
+ 'min': `不能小于 ${(_a = constraint.values) === null || _a === void 0 ? void 0 : _a[0]}`,
231
+ 'max': `不能大于 ${(_b = constraint.values) === null || _b === void 0 ? void 0 : _b[0]}`,
232
+ 'minLength': `长度不能少于 ${(_c = constraint.values) === null || _c === void 0 ? void 0 : _c[0]}`,
233
+ 'maxLength': `长度不能超过 ${(_d = constraint.values) === null || _d === void 0 ? void 0 : _d[0]}`,
234
+ 'isEmail': '邮箱格式不正确',
235
+ 'isEnum': `必须是以下值之一: ${(_e = constraint.values) === null || _e === void 0 ? void 0 : _e.join(', ')}`,
236
+ 'matches': '格式不正确',
237
+ };
238
+ const ruleName = constraint.message.split(' ')[0];
239
+ const chineseMessage = chineseMessages[ruleName];
240
+ if (chineseMessage) {
241
+ const args = constraint.values || [];
242
+ let message = chineseMessage;
243
+ args.forEach(arg => {
244
+ message = message.replace(/\$\{(\w+)\}/g, (_, key) => {
245
+ const index = parseInt(key.replace(/[^\d]/g, '')) - 1;
246
+ return args[index] !== undefined ? args[index] : `$${key}`;
247
+ });
248
+ });
249
+ return `${error.property}: ${message}`;
250
+ }
251
+ // 默认使用原错误信息
252
+ return `${error.property}: ${constraint.message}`;
253
+ });
254
+ }
255
+ /**
256
+ * 创建CSV数据导入结果
257
+ * @param data 成功导入的数据
258
+ * @param errors 错误信息
259
+ * @param totalRows 总行数
260
+ * @param headers 表头信息
261
+ * @returns 导入结果对象
262
+ */
263
+ function createImportResult(data, errors, totalRows, headers) {
264
+ return {
265
+ success: errors.length === 0,
266
+ data,
267
+ errors,
268
+ totalCount: totalRows,
269
+ successCount: data.length,
270
+ errorCount: errors.length,
271
+ headers
272
+ };
273
+ }