@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.
- package/cache/dependencies/db.dependency.d.ts +55 -6
- package/cache/dependencies/db.dependency.js +64 -13
- package/common/boilerplate.polyfill.js +1 -1
- package/file-upload/decorators/column.decorator.d.ts +151 -0
- package/file-upload/decorators/column.decorator.js +273 -0
- package/file-upload/decorators/csv-data.decorator.d.ts +17 -31
- package/file-upload/decorators/csv-data.decorator.js +45 -91
- package/file-upload/decorators/csv-import.decorator.d.ts +34 -0
- package/file-upload/decorators/csv-import.decorator.js +24 -0
- package/file-upload/decorators/examples/column-mapping.example.d.ts +76 -0
- package/file-upload/decorators/examples/column-mapping.example.js +122 -0
- package/file-upload/decorators/excel-data.decorator.d.ts +15 -29
- package/file-upload/decorators/excel-data.decorator.js +42 -82
- package/file-upload/decorators/index.d.ts +3 -2
- package/file-upload/decorators/index.js +20 -2
- package/file-upload/decorators/validate-data.decorator.d.ts +91 -0
- package/file-upload/decorators/validate-data.decorator.js +39 -0
- package/file-upload/dto/update-file.dto.d.ts +0 -1
- package/file-upload/dto/update-file.dto.js +0 -4
- package/file-upload/entities/file-metadata.entity.d.ts +6 -3
- package/file-upload/entities/file-metadata.entity.js +2 -10
- package/file-upload/entities/file.entity.d.ts +3 -18
- package/file-upload/entities/file.entity.js +0 -34
- package/file-upload/file-upload.module.d.ts +1 -1
- package/file-upload/file-upload.module.js +44 -16
- package/file-upload/index.d.ts +13 -2
- package/file-upload/index.js +21 -3
- package/file-upload/interceptors/file-upload.interceptor.d.ts +61 -8
- package/file-upload/interceptors/file-upload.interceptor.js +417 -257
- package/file-upload/interfaces/file-processor.interface.d.ts +93 -0
- package/file-upload/interfaces/file-processor.interface.js +2 -0
- package/file-upload/interfaces/file-upload-options.interface.d.ts +3 -46
- package/file-upload/interfaces/file-upload-options.interface.js +3 -0
- package/file-upload/interfaces/processor-options.interface.d.ts +102 -0
- package/file-upload/interfaces/processor-options.interface.js +2 -0
- package/file-upload/processors/csv.processor.d.ts +98 -0
- package/file-upload/processors/csv.processor.js +391 -0
- package/file-upload/processors/excel.processor.d.ts +130 -0
- package/file-upload/processors/excel.processor.js +547 -0
- package/file-upload/processors/image.processor.d.ts +199 -0
- package/file-upload/processors/image.processor.js +377 -0
- package/file-upload/services/file.service.d.ts +3 -0
- package/file-upload/services/file.service.js +39 -10
- package/file-upload/services/malicious-file-detector.service.d.ts +29 -3
- package/file-upload/services/malicious-file-detector.service.js +256 -57
- package/file-upload/utils/dynamic-import.util.d.ts +6 -2
- package/file-upload/utils/dynamic-import.util.js +17 -5
- package/http-client/decorators/http-client.decorators.d.ts +4 -2
- package/http-client/decorators/http-client.decorators.js +2 -1
- package/http-client/entities/http-log.entity.js +1 -9
- package/http-client/examples/proxy-from-environment.example.d.ts +133 -0
- package/http-client/examples/proxy-from-environment.example.js +410 -0
- package/http-client/http-client.module.js +65 -6
- package/http-client/interfaces/http-client-config.interface.d.ts +6 -0
- package/http-client/services/http-client.service.d.ts +8 -0
- package/http-client/services/http-client.service.js +61 -17
- package/http-client/services/logging.service.d.ts +1 -1
- package/http-client/services/logging.service.js +74 -58
- package/http-client/utils/index.d.ts +1 -0
- package/http-client/utils/index.js +1 -0
- package/http-client/utils/proxy-environment.util.d.ts +42 -0
- package/http-client/utils/proxy-environment.util.js +148 -0
- package/package.json +9 -5
- package/shared/service-registry.module.js +18 -0
- package/transaction/data-source.util.d.ts +142 -0
- package/transaction/data-source.util.js +330 -0
- package/transaction/index.d.ts +1 -0
- package/transaction/index.js +12 -1
- package/validators/is-exists.validator.d.ts +19 -2
- package/validators/is-exists.validator.js +27 -2
- package/validators/is-unique.validator.d.ts +12 -1
- package/validators/is-unique.validator.js +26 -1
|
@@ -1,44 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import 'reflect-metadata';
|
|
2
2
|
/**
|
|
3
|
-
* CSV
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* CSV 列名映射装饰器
|
|
3
|
+
* CSV 列映射装饰器
|
|
4
|
+
* 用于标记 DTO 类的属性对应的 CSV 列名
|
|
5
|
+
*
|
|
8
6
|
* @example
|
|
9
7
|
* ```typescript
|
|
10
|
-
*
|
|
11
|
-
* @CsvColumn('
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* class UserDto {
|
|
9
|
+
* @CsvColumn('姓名')
|
|
10
|
+
* name: string;
|
|
11
|
+
*
|
|
12
|
+
* @CsvColumn('邮箱')
|
|
13
|
+
* email: string;
|
|
14
14
|
* }
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
export declare function CsvColumn(columnName: string): PropertyDecorator;
|
|
18
18
|
/**
|
|
19
|
-
* CSV
|
|
20
|
-
* @example
|
|
21
|
-
* ```typescript
|
|
22
|
-
* @Post('import/csv')
|
|
23
|
-
* @FileUpload({ types: [FileType.CSV] })
|
|
24
|
-
* async importCsv(@CsvData(OrderImportDto) orders: OrderImportDto[]) {
|
|
25
|
-
* await this.orderService.batchCreate(orders);
|
|
26
|
-
* return { count: orders.length };
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export declare const CsvData: (...dataOrPipes: (import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | {
|
|
31
|
-
dto: any;
|
|
32
|
-
options?: CsvDataOptions;
|
|
33
|
-
})[]) => ParameterDecorator;
|
|
34
|
-
/**
|
|
35
|
-
* 辅助函数:获取 DTO 的 CSV 列映射
|
|
19
|
+
* 获取 CSV 列映射
|
|
36
20
|
*/
|
|
37
|
-
export declare function getCsvColumnMapping(dto: any):
|
|
21
|
+
export declare function getCsvColumnMapping(dto: any): Record<string, string>;
|
|
38
22
|
/**
|
|
39
|
-
*
|
|
23
|
+
* 解析并验证 CSV 数据
|
|
40
24
|
*/
|
|
41
|
-
export declare function parseAndValidateCsvData
|
|
42
|
-
|
|
25
|
+
export declare function parseAndValidateCsvData(data: any[], dto: any, options?: {
|
|
26
|
+
skipValidation?: boolean;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
data: any[];
|
|
43
29
|
errors: any[];
|
|
44
30
|
}>;
|
|
@@ -9,123 +9,77 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.CsvData = exports.CSV_COLUMN_METADATA = void 0;
|
|
13
12
|
exports.CsvColumn = CsvColumn;
|
|
14
13
|
exports.getCsvColumnMapping = getCsvColumnMapping;
|
|
15
14
|
exports.parseAndValidateCsvData = parseAndValidateCsvData;
|
|
16
|
-
|
|
15
|
+
require("reflect-metadata");
|
|
17
16
|
const class_transformer_1 = require("class-transformer");
|
|
18
17
|
const class_validator_1 = require("class-validator");
|
|
18
|
+
const CSV_COLUMN_METADATA_KEY = Symbol('csv:column');
|
|
19
19
|
/**
|
|
20
|
-
* CSV
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* CSV 列名映射装饰器
|
|
20
|
+
* CSV 列映射装饰器
|
|
21
|
+
* 用于标记 DTO 类的属性对应的 CSV 列名
|
|
22
|
+
*
|
|
25
23
|
* @example
|
|
26
24
|
* ```typescript
|
|
27
|
-
*
|
|
28
|
-
* @CsvColumn('
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* class UserDto {
|
|
26
|
+
* @CsvColumn('姓名')
|
|
27
|
+
* name: string;
|
|
28
|
+
*
|
|
29
|
+
* @CsvColumn('邮箱')
|
|
30
|
+
* email: string;
|
|
31
31
|
* }
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
function CsvColumn(columnName) {
|
|
35
35
|
return (target, propertyKey) => {
|
|
36
|
-
Reflect.
|
|
36
|
+
const existingColumns = Reflect.getMetadata(CSV_COLUMN_METADATA_KEY, target.constructor) || {};
|
|
37
|
+
existingColumns[columnName] = propertyKey;
|
|
38
|
+
Reflect.defineMetadata(CSV_COLUMN_METADATA_KEY, existingColumns, target.constructor);
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
|
-
* CSV
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* @Post('import/csv')
|
|
44
|
-
* @FileUpload({ types: [FileType.CSV] })
|
|
45
|
-
* async importCsv(@CsvData(OrderImportDto) orders: OrderImportDto[]) {
|
|
46
|
-
* await this.orderService.batchCreate(orders);
|
|
47
|
-
* return { count: orders.length };
|
|
48
|
-
* }
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
exports.CsvData = (0, common_1.createParamDecorator)((data, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
|
-
const request = ctx.switchToHttp().getRequest();
|
|
53
|
-
const file = request.file;
|
|
54
|
-
if (!file) {
|
|
55
|
-
throw new Error('No file uploaded');
|
|
56
|
-
}
|
|
57
|
-
// 这里将在 interceptor 中处理 CSV 解析
|
|
58
|
-
// 当前只返回文件信息,实际解析逻辑在 FileUploadInterceptor 中
|
|
59
|
-
return {
|
|
60
|
-
_csvData: true,
|
|
61
|
-
dto: data,
|
|
62
|
-
file,
|
|
63
|
-
options: data === null || data === void 0 ? void 0 : data['options'],
|
|
64
|
-
};
|
|
65
|
-
}));
|
|
66
|
-
/**
|
|
67
|
-
* 辅助函数:获取 DTO 的 CSV 列映射
|
|
42
|
+
* 获取 CSV 列映射
|
|
68
43
|
*/
|
|
69
44
|
function getCsvColumnMapping(dto) {
|
|
70
|
-
|
|
71
|
-
const prototype = dto.prototype;
|
|
72
|
-
if (!prototype) {
|
|
73
|
-
return mapping;
|
|
74
|
-
}
|
|
75
|
-
const propertyKeys = Object.getOwnPropertyNames(prototype);
|
|
76
|
-
for (const propertyKey of propertyKeys) {
|
|
77
|
-
const columnName = Reflect.getMetadata(exports.CSV_COLUMN_METADATA, prototype, propertyKey);
|
|
78
|
-
if (columnName) {
|
|
79
|
-
mapping.set(columnName, propertyKey);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return mapping;
|
|
45
|
+
return Reflect.getMetadata(CSV_COLUMN_METADATA_KEY, dto) || {};
|
|
83
46
|
}
|
|
84
47
|
/**
|
|
85
|
-
*
|
|
48
|
+
* 解析并验证 CSV 数据
|
|
86
49
|
*/
|
|
87
|
-
function parseAndValidateCsvData(
|
|
88
|
-
return __awaiter(this,
|
|
89
|
-
const
|
|
90
|
-
const
|
|
50
|
+
function parseAndValidateCsvData(data, dto, options) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const columnMapping = getCsvColumnMapping(dto);
|
|
53
|
+
const results = [];
|
|
91
54
|
const errors = [];
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const obj = {};
|
|
101
|
-
// 映射列名到属性名
|
|
102
|
-
for (const [columnName, propertyKey] of mapping.entries()) {
|
|
103
|
-
obj[propertyKey] = row[columnName];
|
|
55
|
+
for (let i = 0; i < data.length; i++) {
|
|
56
|
+
const row = data[i];
|
|
57
|
+
const mappedData = {};
|
|
58
|
+
// 映射列名
|
|
59
|
+
for (const [csvColumn, dtoProperty] of Object.entries(columnMapping)) {
|
|
60
|
+
if (row.hasOwnProperty(csvColumn)) {
|
|
61
|
+
mappedData[dtoProperty] = row[csvColumn];
|
|
62
|
+
}
|
|
104
63
|
}
|
|
105
64
|
// 转换为 DTO 实例
|
|
106
|
-
const instance = (0, class_transformer_1.plainToInstance)(
|
|
65
|
+
const instance = (0, class_transformer_1.plainToInstance)(dto, mappedData);
|
|
107
66
|
// 验证
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
67
|
+
if (!(options === null || options === void 0 ? void 0 : options.skipValidation)) {
|
|
68
|
+
const validationErrors = yield (0, class_validator_1.validate)(instance);
|
|
69
|
+
if (validationErrors.length > 0) {
|
|
70
|
+
errors.push({
|
|
71
|
+
row: i + 1,
|
|
72
|
+
data: row,
|
|
73
|
+
errors: validationErrors.map((e) => ({
|
|
74
|
+
property: e.property,
|
|
75
|
+
constraints: e.constraints,
|
|
76
|
+
})),
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
118
80
|
}
|
|
81
|
+
results.push(instance);
|
|
119
82
|
}
|
|
120
|
-
return { data, errors };
|
|
83
|
+
return { data: results, errors };
|
|
121
84
|
});
|
|
122
85
|
}
|
|
123
|
-
/**
|
|
124
|
-
* 检查是否为空行
|
|
125
|
-
*/
|
|
126
|
-
function isEmptyRow(row) {
|
|
127
|
-
if (!row)
|
|
128
|
-
return true;
|
|
129
|
-
const values = Object.values(row);
|
|
130
|
-
return values.every((val) => !val || String(val).trim() === '');
|
|
131
|
-
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CsvProcessorOptions } from '../processors/csv.processor';
|
|
2
|
+
import { ValidateDataOptions } from './validate-data.decorator';
|
|
3
|
+
/**
|
|
4
|
+
* CSV 处理装饰器元数据键
|
|
5
|
+
*/
|
|
6
|
+
export declare const CSV_PROCESS_KEY = "csv:process";
|
|
7
|
+
/**
|
|
8
|
+
* CSV 数据处理选项
|
|
9
|
+
*/
|
|
10
|
+
export interface CsvDataOptions extends CsvProcessorOptions {
|
|
11
|
+
/** 验证类(使用 class-validator) */
|
|
12
|
+
validateClass?: any;
|
|
13
|
+
/** 验证选项 */
|
|
14
|
+
validation?: ValidateDataOptions;
|
|
15
|
+
/** 批处理大小 */
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
/** 是否异步处理 */
|
|
18
|
+
async?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* CSV 数据处理装饰器
|
|
22
|
+
* 用于标记方法处理 CSV 文件数据
|
|
23
|
+
*/
|
|
24
|
+
export declare const CsvData: (options?: CsvDataOptions) => import("@nestjs/common").CustomDecorator<string>;
|
|
25
|
+
/**
|
|
26
|
+
* CSV 批量导入装饰器
|
|
27
|
+
* 专门用于批量导入场景
|
|
28
|
+
*/
|
|
29
|
+
export declare const CsvImport: (validateClass: any, options?: Omit<CsvDataOptions, "validateClass"> & {
|
|
30
|
+
/** 目标服务名 */
|
|
31
|
+
service?: string;
|
|
32
|
+
/** 目标方法名 */
|
|
33
|
+
method?: string;
|
|
34
|
+
}) => import("@nestjs/common").CustomDecorator<string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CsvImport = exports.CsvData = exports.CSV_PROCESS_KEY = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
/**
|
|
6
|
+
* CSV 处理装饰器元数据键
|
|
7
|
+
*/
|
|
8
|
+
exports.CSV_PROCESS_KEY = 'csv:process';
|
|
9
|
+
/**
|
|
10
|
+
* CSV 数据处理装饰器
|
|
11
|
+
* 用于标记方法处理 CSV 文件数据
|
|
12
|
+
*/
|
|
13
|
+
const CsvData = (options = {}) => {
|
|
14
|
+
return (0, common_1.SetMetadata)(exports.CSV_PROCESS_KEY, options);
|
|
15
|
+
};
|
|
16
|
+
exports.CsvData = CsvData;
|
|
17
|
+
/**
|
|
18
|
+
* CSV 批量导入装饰器
|
|
19
|
+
* 专门用于批量导入场景
|
|
20
|
+
*/
|
|
21
|
+
const CsvImport = (validateClass, options = {}) => {
|
|
22
|
+
return (0, common_1.SetMetadata)(exports.CSV_PROCESS_KEY, Object.assign(Object.assign({}, options), { validateClass, type: 'import' }));
|
|
23
|
+
};
|
|
24
|
+
exports.CsvImport = CsvImport;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 用户导入DTO示例
|
|
3
|
+
* 展示如何使用列序号、列名和Excel列名进行映射
|
|
4
|
+
*/
|
|
5
|
+
export declare class UserImportDto {
|
|
6
|
+
/**
|
|
7
|
+
* 使用列名映射(推荐,更易读)
|
|
8
|
+
* CSV: 通过表头"姓名"找到列
|
|
9
|
+
* Excel: 通过表头"姓名"或列名"A"找到列
|
|
10
|
+
*/
|
|
11
|
+
name: string;
|
|
12
|
+
/**
|
|
13
|
+
* 使用列序号映射(从0开始)
|
|
14
|
+
* CSV: 第2列(索引为1)
|
|
15
|
+
* Excel: 第2列(B列)
|
|
16
|
+
*/
|
|
17
|
+
age: number;
|
|
18
|
+
/**
|
|
19
|
+
* 使用Excel列名映射
|
|
20
|
+
* CSV: 如果没有表头,需要使用数字索引
|
|
21
|
+
* Excel: 可以直接使用列名"C"
|
|
22
|
+
*/
|
|
23
|
+
email: string;
|
|
24
|
+
/**
|
|
25
|
+
* 混合使用不同的映射方式
|
|
26
|
+
*/
|
|
27
|
+
phone?: string;
|
|
28
|
+
/**
|
|
29
|
+
* 使用较大的列索引
|
|
30
|
+
*/
|
|
31
|
+
address?: string;
|
|
32
|
+
/**
|
|
33
|
+
* 使用更大的Excel列名(如AA, AB等)
|
|
34
|
+
*/
|
|
35
|
+
notes?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 产品导入DTO示例
|
|
39
|
+
*/
|
|
40
|
+
export declare class ProductImportDto {
|
|
41
|
+
/**
|
|
42
|
+
* 产品名称 - 第1列
|
|
43
|
+
*/
|
|
44
|
+
name: string;
|
|
45
|
+
/**
|
|
46
|
+
* 价格 - 第2列
|
|
47
|
+
*/
|
|
48
|
+
price: number;
|
|
49
|
+
/**
|
|
50
|
+
* 库存 - 第3列
|
|
51
|
+
*/
|
|
52
|
+
stock: number;
|
|
53
|
+
/**
|
|
54
|
+
* 描述 - 第4列(可选)
|
|
55
|
+
*/
|
|
56
|
+
description?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 使用说明:
|
|
60
|
+
*
|
|
61
|
+
* 1. CSV文件:
|
|
62
|
+
* - 如果CSV有表头,建议使用列名(如 @CSVColumn('姓名'))
|
|
63
|
+
* - 如果CSV没有表头,使用数字索引(如 @CSVColumn(0) 表示第1列)
|
|
64
|
+
*
|
|
65
|
+
* 2. Excel文件:
|
|
66
|
+
* - 可以使用列名(如 @ExcelColumn('姓名'))
|
|
67
|
+
* - 可以使用数字索引(如 @ExcelColumn(0) 表示第1列)
|
|
68
|
+
* - 可以使用Excel列名(如 @ExcelColumn('A') 表示A列)
|
|
69
|
+
* - 数字索引会自动转换为对应的Excel列名(0 -> A, 1 -> B, 26 -> AA)
|
|
70
|
+
*
|
|
71
|
+
* 3. 最佳实践:
|
|
72
|
+
* - 优先使用列名,代码更易读
|
|
73
|
+
* - 对于无表头的CSV文件,使用数字索引
|
|
74
|
+
* - 对于Excel文件,推荐使用Excel列名(A, B, C)或数字索引
|
|
75
|
+
* - 可以混合使用不同的映射方式
|
|
76
|
+
*/
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ProductImportDto = exports.UserImportDto = void 0;
|
|
13
|
+
const column_decorator_1 = require("../column.decorator");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
/**
|
|
16
|
+
* 用户导入DTO示例
|
|
17
|
+
* 展示如何使用列序号、列名和Excel列名进行映射
|
|
18
|
+
*/
|
|
19
|
+
class UserImportDto {
|
|
20
|
+
}
|
|
21
|
+
exports.UserImportDto = UserImportDto;
|
|
22
|
+
__decorate([
|
|
23
|
+
(0, column_decorator_1.CSVColumn)('姓名'),
|
|
24
|
+
(0, column_decorator_1.ExcelColumn)('姓名'),
|
|
25
|
+
(0, class_validator_1.IsString)(),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], UserImportDto.prototype, "name", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, column_decorator_1.CSVColumn)(1),
|
|
30
|
+
(0, column_decorator_1.ExcelColumn)(1),
|
|
31
|
+
(0, class_validator_1.IsNumber)(),
|
|
32
|
+
(0, class_validator_1.Min)(0),
|
|
33
|
+
(0, class_validator_1.Max)(150),
|
|
34
|
+
__metadata("design:type", Number)
|
|
35
|
+
], UserImportDto.prototype, "age", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, column_decorator_1.CSVColumn)(2) // CSV使用数字索引
|
|
38
|
+
,
|
|
39
|
+
(0, column_decorator_1.ExcelColumn)('C') // Excel使用列名
|
|
40
|
+
,
|
|
41
|
+
(0, class_validator_1.IsEmail)(),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], UserImportDto.prototype, "email", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, column_decorator_1.CSVColumn)('电话') // CSV使用列名
|
|
46
|
+
,
|
|
47
|
+
(0, column_decorator_1.ExcelColumn)('D') // Excel使用列名
|
|
48
|
+
,
|
|
49
|
+
(0, class_validator_1.IsOptional)(),
|
|
50
|
+
(0, class_validator_1.IsString)(),
|
|
51
|
+
__metadata("design:type", String)
|
|
52
|
+
], UserImportDto.prototype, "phone", void 0);
|
|
53
|
+
__decorate([
|
|
54
|
+
(0, column_decorator_1.CSVColumn)(4) // 第5列
|
|
55
|
+
,
|
|
56
|
+
(0, column_decorator_1.ExcelColumn)('E') // 或使用Excel列名
|
|
57
|
+
,
|
|
58
|
+
(0, class_validator_1.IsOptional)(),
|
|
59
|
+
(0, class_validator_1.IsString)(),
|
|
60
|
+
__metadata("design:type", String)
|
|
61
|
+
], UserImportDto.prototype, "address", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
(0, column_decorator_1.CSVColumn)(26) // 第27列
|
|
64
|
+
,
|
|
65
|
+
(0, column_decorator_1.ExcelColumn)('AA') // Excel列AA
|
|
66
|
+
,
|
|
67
|
+
(0, class_validator_1.IsOptional)(),
|
|
68
|
+
(0, class_validator_1.IsString)(),
|
|
69
|
+
__metadata("design:type", String)
|
|
70
|
+
], UserImportDto.prototype, "notes", void 0);
|
|
71
|
+
/**
|
|
72
|
+
* 产品导入DTO示例
|
|
73
|
+
*/
|
|
74
|
+
class ProductImportDto {
|
|
75
|
+
}
|
|
76
|
+
exports.ProductImportDto = ProductImportDto;
|
|
77
|
+
__decorate([
|
|
78
|
+
(0, column_decorator_1.CSVColumn)('产品名称'),
|
|
79
|
+
(0, column_decorator_1.ExcelColumn)(0),
|
|
80
|
+
(0, class_validator_1.IsString)(),
|
|
81
|
+
__metadata("design:type", String)
|
|
82
|
+
], ProductImportDto.prototype, "name", void 0);
|
|
83
|
+
__decorate([
|
|
84
|
+
(0, column_decorator_1.CSVColumn)('价格'),
|
|
85
|
+
(0, column_decorator_1.ExcelColumn)('B'),
|
|
86
|
+
(0, class_validator_1.IsNumber)(),
|
|
87
|
+
(0, class_validator_1.Min)(0),
|
|
88
|
+
__metadata("design:type", Number)
|
|
89
|
+
], ProductImportDto.prototype, "price", void 0);
|
|
90
|
+
__decorate([
|
|
91
|
+
(0, column_decorator_1.CSVColumn)('库存数量'),
|
|
92
|
+
(0, column_decorator_1.ExcelColumn)(2),
|
|
93
|
+
(0, class_validator_1.IsNumber)(),
|
|
94
|
+
(0, class_validator_1.Min)(0),
|
|
95
|
+
__metadata("design:type", Number)
|
|
96
|
+
], ProductImportDto.prototype, "stock", void 0);
|
|
97
|
+
__decorate([
|
|
98
|
+
(0, column_decorator_1.CSVColumn)(3),
|
|
99
|
+
(0, column_decorator_1.ExcelColumn)('D'),
|
|
100
|
+
(0, class_validator_1.IsOptional)(),
|
|
101
|
+
(0, class_validator_1.IsString)(),
|
|
102
|
+
__metadata("design:type", String)
|
|
103
|
+
], ProductImportDto.prototype, "description", void 0);
|
|
104
|
+
/**
|
|
105
|
+
* 使用说明:
|
|
106
|
+
*
|
|
107
|
+
* 1. CSV文件:
|
|
108
|
+
* - 如果CSV有表头,建议使用列名(如 @CSVColumn('姓名'))
|
|
109
|
+
* - 如果CSV没有表头,使用数字索引(如 @CSVColumn(0) 表示第1列)
|
|
110
|
+
*
|
|
111
|
+
* 2. Excel文件:
|
|
112
|
+
* - 可以使用列名(如 @ExcelColumn('姓名'))
|
|
113
|
+
* - 可以使用数字索引(如 @ExcelColumn(0) 表示第1列)
|
|
114
|
+
* - 可以使用Excel列名(如 @ExcelColumn('A') 表示A列)
|
|
115
|
+
* - 数字索引会自动转换为对应的Excel列名(0 -> A, 1 -> B, 26 -> AA)
|
|
116
|
+
*
|
|
117
|
+
* 3. 最佳实践:
|
|
118
|
+
* - 优先使用列名,代码更易读
|
|
119
|
+
* - 对于无表头的CSV文件,使用数字索引
|
|
120
|
+
* - 对于Excel文件,推荐使用Excel列名(A, B, C)或数字索引
|
|
121
|
+
* - 可以混合使用不同的映射方式
|
|
122
|
+
*/
|
|
@@ -1,44 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import 'reflect-metadata';
|
|
2
2
|
/**
|
|
3
|
-
* Excel
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Excel 列名映射装饰器
|
|
3
|
+
* Excel 列映射装饰器
|
|
4
|
+
* 用于标记 DTO 类的属性对应的 Excel 列名
|
|
5
|
+
*
|
|
8
6
|
* @example
|
|
9
7
|
* ```typescript
|
|
10
|
-
*
|
|
8
|
+
* class UserDto {
|
|
11
9
|
* @ExcelColumn('姓名')
|
|
12
|
-
* @IsString()
|
|
13
10
|
* name: string;
|
|
11
|
+
*
|
|
12
|
+
* @ExcelColumn('邮箱')
|
|
13
|
+
* email: string;
|
|
14
14
|
* }
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
export declare function ExcelColumn(columnName: string): PropertyDecorator;
|
|
18
18
|
/**
|
|
19
|
-
* Excel
|
|
20
|
-
* @example
|
|
21
|
-
* ```typescript
|
|
22
|
-
* @Post('import/excel')
|
|
23
|
-
* @FileUpload({ types: [FileType.Excel] })
|
|
24
|
-
* async importExcel(@ExcelData(UserImportDto) users: UserImportDto[]) {
|
|
25
|
-
* await this.userService.batchCreate(users);
|
|
26
|
-
* return { count: users.length };
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export declare const ExcelData: (...dataOrPipes: (import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | {
|
|
31
|
-
dto: any;
|
|
32
|
-
options?: ExcelDataOptions;
|
|
33
|
-
})[]) => ParameterDecorator;
|
|
34
|
-
/**
|
|
35
|
-
* 辅助函数:获取 DTO 的 Excel 列映射
|
|
19
|
+
* 获取 Excel 列映射
|
|
36
20
|
*/
|
|
37
|
-
export declare function getExcelColumnMapping(dto: any):
|
|
21
|
+
export declare function getExcelColumnMapping(dto: any): Record<string, string>;
|
|
38
22
|
/**
|
|
39
|
-
*
|
|
23
|
+
* 解析并验证 Excel 数据
|
|
40
24
|
*/
|
|
41
|
-
export declare function parseAndValidateExcelData
|
|
42
|
-
|
|
25
|
+
export declare function parseAndValidateExcelData(data: any[], dto: any, options?: {
|
|
26
|
+
skipValidation?: boolean;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
data: any[];
|
|
43
29
|
errors: any[];
|
|
44
30
|
}>;
|