@nest-omni/core 4.1.3-11 → 4.1.3-12
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/audit/audit.module.js +17 -0
- package/audit/controllers/audit.controller.d.ts +64 -0
- package/audit/controllers/audit.controller.js +50 -0
- package/audit/decorators/audit-action.decorator.d.ts +74 -0
- package/audit/decorators/audit-action.decorator.js +42 -0
- package/audit/decorators/entity-audit.decorator.d.ts +10 -1
- package/audit/decorators/entity-audit.decorator.js +34 -16
- package/audit/decorators/index.d.ts +1 -0
- package/audit/decorators/index.js +1 -0
- package/audit/entities/audit-action-summary.entity.d.ts +23 -0
- package/audit/entities/audit-action-summary.entity.js +101 -0
- package/audit/entities/entity-audit-log.entity.d.ts +3 -0
- package/audit/entities/entity-audit-log.entity.js +25 -2
- package/audit/entities/entity-transaction.entity.d.ts +3 -4
- package/audit/entities/entity-transaction.entity.js +10 -3
- package/audit/entities/index.d.ts +1 -0
- package/audit/entities/index.js +1 -0
- package/audit/entities/manual-operation-log.entity.js +8 -1
- package/audit/enums/audit.enums.d.ts +1 -10
- package/audit/enums/audit.enums.js +7 -17
- package/audit/index.d.ts +2 -1
- package/audit/index.js +5 -1
- package/audit/interceptors/audit-action.interceptor.d.ts +38 -0
- package/audit/interceptors/audit-action.interceptor.js +215 -0
- package/audit/interceptors/index.d.ts +1 -0
- package/audit/interceptors/index.js +1 -0
- package/audit/interfaces/audit.interfaces.d.ts +10 -5
- package/audit/services/audit-action.service.d.ts +141 -0
- package/audit/services/audit-action.service.js +244 -0
- package/audit/services/audit-context.service.d.ts +82 -0
- package/audit/services/audit-context.service.js +170 -0
- package/audit/services/entity-audit.service.d.ts +104 -3
- package/audit/services/entity-audit.service.js +306 -9
- package/audit/services/index.d.ts +1 -0
- package/audit/services/index.js +1 -0
- package/audit/services/manual-audit-log.service.d.ts +24 -23
- package/audit/services/manual-audit-log.service.js +32 -53
- package/audit/services/operation-description.service.d.ts +13 -3
- package/audit/services/operation-description.service.js +161 -24
- package/audit/services/transaction-audit.service.js +3 -3
- package/audit/subscribers/entity-audit.subscriber.d.ts +4 -0
- package/audit/subscribers/entity-audit.subscriber.js +47 -0
- package/file-upload/controllers/file-access.controller.d.ts +23 -0
- package/file-upload/controllers/file-access.controller.js +128 -0
- package/file-upload/decorators/csv-data.decorator.d.ts +44 -0
- package/file-upload/decorators/csv-data.decorator.js +131 -0
- package/file-upload/decorators/excel-data.decorator.d.ts +44 -0
- package/file-upload/decorators/excel-data.decorator.js +125 -0
- package/file-upload/decorators/file-upload.decorator.d.ts +83 -0
- package/file-upload/decorators/file-upload.decorator.js +172 -0
- package/file-upload/decorators/index.d.ts +4 -0
- package/file-upload/decorators/index.js +20 -0
- package/file-upload/decorators/process.decorator.d.ts +40 -0
- package/file-upload/decorators/process.decorator.js +52 -0
- package/file-upload/dto/create-file.dto.d.ts +24 -0
- package/file-upload/dto/create-file.dto.js +112 -0
- package/file-upload/dto/find-files.dto.d.ts +15 -0
- package/file-upload/dto/find-files.dto.js +76 -0
- package/file-upload/dto/index.d.ts +4 -0
- package/file-upload/dto/index.js +20 -0
- package/file-upload/dto/pagination.dto.d.ts +7 -0
- package/file-upload/dto/pagination.dto.js +39 -0
- package/file-upload/dto/update-file.dto.d.ts +16 -0
- package/file-upload/dto/update-file.dto.js +71 -0
- package/file-upload/entities/file-metadata.entity.d.ts +22 -0
- package/file-upload/entities/file-metadata.entity.js +84 -0
- package/file-upload/entities/file.entity.d.ts +129 -0
- package/file-upload/entities/file.entity.js +384 -0
- package/file-upload/entities/index.d.ts +2 -0
- package/file-upload/entities/index.js +18 -0
- package/file-upload/enums/file-type.enum.d.ts +72 -0
- package/file-upload/enums/file-type.enum.js +212 -0
- package/file-upload/exceptions/file-upload.exception.d.ts +57 -0
- package/file-upload/exceptions/file-upload.exception.js +120 -0
- package/file-upload/exceptions/index.d.ts +1 -0
- package/file-upload/exceptions/index.js +17 -0
- package/file-upload/file-upload.module.d.ts +89 -0
- package/file-upload/file-upload.module.js +264 -0
- package/file-upload/index.d.ts +26 -0
- package/file-upload/index.js +59 -0
- package/file-upload/interceptors/file-upload.interceptor.d.ts +48 -0
- package/file-upload/interceptors/file-upload.interceptor.js +434 -0
- package/file-upload/interceptors/index.d.ts +1 -0
- package/file-upload/interceptors/index.js +17 -0
- package/file-upload/interfaces/custom-file-type.interface.d.ts +72 -0
- package/file-upload/interfaces/custom-file-type.interface.js +2 -0
- package/file-upload/interfaces/file-buffer.interface.d.ts +72 -0
- package/file-upload/interfaces/file-buffer.interface.js +2 -0
- package/file-upload/interfaces/file-entity.interface.d.ts +142 -0
- package/file-upload/interfaces/file-entity.interface.js +28 -0
- package/file-upload/interfaces/file-metadata.interface.d.ts +21 -0
- package/file-upload/interfaces/file-metadata.interface.js +2 -0
- package/file-upload/interfaces/file-upload-options.interface.d.ts +117 -0
- package/file-upload/interfaces/file-upload-options.interface.js +2 -0
- package/file-upload/interfaces/index.d.ts +7 -0
- package/file-upload/interfaces/index.js +24 -0
- package/file-upload/interfaces/storage-provider.interface.d.ts +239 -0
- package/file-upload/interfaces/storage-provider.interface.js +2 -0
- package/file-upload/interfaces/upload-options.interface.d.ts +19 -0
- package/file-upload/interfaces/upload-options.interface.js +2 -0
- package/file-upload/providers/index.d.ts +2 -0
- package/file-upload/providers/index.js +18 -0
- package/file-upload/providers/local-storage.provider.d.ts +98 -0
- package/file-upload/providers/local-storage.provider.js +484 -0
- package/file-upload/providers/s3-storage.provider.d.ts +87 -0
- package/file-upload/providers/s3-storage.provider.js +455 -0
- package/file-upload/services/file-signature-validator.service.d.ts +118 -0
- package/file-upload/services/file-signature-validator.service.js +376 -0
- package/file-upload/services/file.service.d.ts +190 -0
- package/file-upload/services/file.service.js +609 -0
- package/file-upload/services/index.d.ts +4 -0
- package/file-upload/services/index.js +20 -0
- package/file-upload/services/malicious-file-detector.service.d.ts +274 -0
- package/file-upload/services/malicious-file-detector.service.js +1035 -0
- package/file-upload/services/mime-registry.service.d.ts +47 -0
- package/file-upload/services/mime-registry.service.js +167 -0
- package/file-upload/utils/checksum.util.d.ts +28 -0
- package/file-upload/utils/checksum.util.js +65 -0
- package/file-upload/utils/dynamic-import.util.d.ts +50 -0
- package/file-upload/utils/dynamic-import.util.js +144 -0
- package/file-upload/utils/filename.util.d.ts +59 -0
- package/file-upload/utils/filename.util.js +184 -0
- package/file-upload/utils/filepath.util.d.ts +70 -0
- package/file-upload/utils/filepath.util.js +152 -0
- package/file-upload/utils/index.d.ts +4 -0
- package/file-upload/utils/index.js +20 -0
- package/index.d.ts +3 -1
- package/index.js +4 -1
- package/package.json +4 -5
- package/setup/bootstrap.setup.d.ts +1 -0
- package/setup/bootstrap.setup.js +1 -0
- package/shared/index.d.ts +1 -1
- package/shared/index.js +1 -1
- package/shared/{serviceRegistryModule.js → service-registry.module.js} +0 -12
- package/shared/services/index.d.ts +0 -1
- package/shared/services/index.js +0 -1
- package/transaction/__tests__/mocks.d.ts +9 -0
- package/transaction/__tests__/mocks.js +33 -0
- package/transaction/base-service-transaction.d.ts +99 -0
- package/transaction/base-service-transaction.js +286 -0
- package/transaction/cls-compatibility.service.d.ts +55 -0
- package/transaction/cls-compatibility.service.js +127 -0
- package/transaction/data-source-registry.d.ts +91 -0
- package/transaction/data-source-registry.js +349 -0
- package/transaction/database-adapter.d.ts +44 -0
- package/transaction/database-adapter.js +240 -0
- package/transaction/decorators/entity-datasource.decorator.d.ts +62 -0
- package/transaction/decorators/entity-datasource.decorator.js +105 -0
- package/transaction/index.d.ts +14 -0
- package/transaction/index.js +57 -0
- package/transaction/logging-transactional.interceptor.d.ts +18 -0
- package/transaction/logging-transactional.interceptor.js +163 -0
- package/transaction/transaction-context.service.d.ts +137 -0
- package/transaction/transaction-context.service.js +411 -0
- package/transaction/transaction-manager.d.ts +230 -0
- package/transaction/transaction-manager.js +1001 -0
- package/transaction/transaction-synchronization.d.ts +171 -0
- package/transaction/transaction-synchronization.js +380 -0
- package/transaction/transaction.errors.d.ts +91 -0
- package/transaction/transaction.errors.js +206 -0
- package/transaction/transaction.module.d.ts +30 -0
- package/transaction/transaction.module.js +98 -0
- package/transaction/transactional.decorator.d.ts +82 -0
- package/transaction/transactional.decorator.js +319 -0
- package/transaction/typeorm-module-wrapper.d.ts +96 -0
- package/transaction/typeorm-module-wrapper.js +197 -0
- package/validators/file-mimetype.validator.d.ts +0 -2
- package/validators/file-mimetype.validator.js +4 -6
- package/validators/is-exists.validator.d.ts +2 -5
- package/validators/is-exists.validator.js +4 -6
- package/validators/is-unique.validator.d.ts +2 -5
- package/validators/is-unique.validator.js +6 -11
- package/shared/services/validator.service.d.ts +0 -3
- package/shared/services/validator.service.js +0 -20
- /package/shared/{serviceRegistryModule.d.ts → service-registry.module.d.ts} +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
15
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
16
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
17
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
18
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
19
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
20
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
var FileAccessController_1;
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.FileAccessController = void 0;
|
|
26
|
+
const common_1 = require("@nestjs/common");
|
|
27
|
+
const file_service_1 = require("../services/file.service");
|
|
28
|
+
/**
|
|
29
|
+
* 文件访问控制器
|
|
30
|
+
* 提供文件下载和预览功能
|
|
31
|
+
* 注意:Controller 仅负责 HTTP 层逻辑,业务逻辑在 FileService
|
|
32
|
+
*/
|
|
33
|
+
let FileAccessController = FileAccessController_1 = class FileAccessController {
|
|
34
|
+
constructor(fileService) {
|
|
35
|
+
this.fileService = fileService;
|
|
36
|
+
this.logger = new common_1.Logger(FileAccessController_1.name);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 通过文件 ID 下载/访问文件
|
|
40
|
+
* GET /files/:id
|
|
41
|
+
*/
|
|
42
|
+
getFile(id, res) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
// 从数据库获取文件记录
|
|
45
|
+
const fileEntity = yield this.fileService.findById(id);
|
|
46
|
+
if (!fileEntity) {
|
|
47
|
+
throw new common_1.NotFoundException(`文件不存在: ${id}`);
|
|
48
|
+
}
|
|
49
|
+
// 检查文件是否可访问
|
|
50
|
+
if (fileEntity.status !== 'completed') {
|
|
51
|
+
throw new common_1.NotFoundException(`文件尚未准备就绪: ${id}`);
|
|
52
|
+
}
|
|
53
|
+
// 对象存储:重定向到预签名 URL
|
|
54
|
+
if (['s3', 'oss', 'minio', 'cdn'].includes(fileEntity.storageProvider)) {
|
|
55
|
+
try {
|
|
56
|
+
const signedUrl = yield this.fileService.getSignedUrl(id);
|
|
57
|
+
res.redirect(302, signedUrl);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
this.logger.error(`Failed to get signed URL for file ${id}: ${error.message}`);
|
|
62
|
+
throw new common_1.NotFoundException(`无法访问文件: ${id}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 本地存储:流式返回
|
|
66
|
+
if (fileEntity.storageProvider === 'local') {
|
|
67
|
+
try {
|
|
68
|
+
const stream = yield this.fileService.getFileStream(id);
|
|
69
|
+
res.set({
|
|
70
|
+
'Content-Type': fileEntity.mimeType,
|
|
71
|
+
'Content-Length': fileEntity.size.toString(),
|
|
72
|
+
});
|
|
73
|
+
return new common_1.StreamableFile(stream);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.logger.error(`Failed to stream file ${id}: ${error.message}`);
|
|
77
|
+
throw new common_1.NotFoundException(`无法访问文件: ${id}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 不支持的存储类型
|
|
81
|
+
throw new common_1.NotFoundException(`不支持的存储类型: ${fileEntity.storageProvider}`);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 通过文件 ID 下载文件(强制下载)
|
|
86
|
+
* GET /files/:id/download
|
|
87
|
+
*/
|
|
88
|
+
downloadFile(id, res) {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
const fileEntity = yield this.fileService.findById(id);
|
|
91
|
+
if (!fileEntity) {
|
|
92
|
+
throw new common_1.NotFoundException(`文件不存在: ${id}`);
|
|
93
|
+
}
|
|
94
|
+
if (fileEntity.status !== 'completed') {
|
|
95
|
+
throw new common_1.NotFoundException(`文件尚未准备就绪: ${id}`);
|
|
96
|
+
}
|
|
97
|
+
// 设置下载响应头
|
|
98
|
+
res.set({
|
|
99
|
+
'Content-Disposition': `attachment; filename="${encodeURIComponent(fileEntity.originalName)}"`,
|
|
100
|
+
});
|
|
101
|
+
// 复用 getFile 逻辑
|
|
102
|
+
return this.getFile(id, res);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
exports.FileAccessController = FileAccessController;
|
|
107
|
+
__decorate([
|
|
108
|
+
(0, common_1.Get)(':id'),
|
|
109
|
+
(0, common_1.Header)('Cache-Control', 'public, max-age=31536000') // 缓存1年
|
|
110
|
+
,
|
|
111
|
+
__param(0, (0, common_1.Param)('id')),
|
|
112
|
+
__param(1, (0, common_1.Res)({ passthrough: true })),
|
|
113
|
+
__metadata("design:type", Function),
|
|
114
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
115
|
+
__metadata("design:returntype", Promise)
|
|
116
|
+
], FileAccessController.prototype, "getFile", null);
|
|
117
|
+
__decorate([
|
|
118
|
+
(0, common_1.Get)(':id/download'),
|
|
119
|
+
__param(0, (0, common_1.Param)('id')),
|
|
120
|
+
__param(1, (0, common_1.Res)({ passthrough: true })),
|
|
121
|
+
__metadata("design:type", Function),
|
|
122
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
123
|
+
__metadata("design:returntype", Promise)
|
|
124
|
+
], FileAccessController.prototype, "downloadFile", null);
|
|
125
|
+
exports.FileAccessController = FileAccessController = FileAccessController_1 = __decorate([
|
|
126
|
+
(0, common_1.Controller)('files'),
|
|
127
|
+
__metadata("design:paramtypes", [file_service_1.FileService])
|
|
128
|
+
], FileAccessController);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CsvDataOptions } from '../interfaces/file-upload-options.interface';
|
|
2
|
+
/**
|
|
3
|
+
* CSV 列映射元数据键
|
|
4
|
+
*/
|
|
5
|
+
export declare const CSV_COLUMN_METADATA = "csv:column";
|
|
6
|
+
/**
|
|
7
|
+
* CSV 列名映射装饰器
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* export class OrderImportDto {
|
|
11
|
+
* @CsvColumn('订单号')
|
|
12
|
+
* @IsString()
|
|
13
|
+
* orderNo: string;
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function CsvColumn(columnName: string): PropertyDecorator;
|
|
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 列映射
|
|
36
|
+
*/
|
|
37
|
+
export declare function getCsvColumnMapping(dto: any): Map<string, string>;
|
|
38
|
+
/**
|
|
39
|
+
* 辅助函数:解析和验证 CSV 数据
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseAndValidateCsvData<T extends object>(rawData: any[], dtoClass: new () => T, options?: CsvDataOptions): Promise<{
|
|
42
|
+
data: T[];
|
|
43
|
+
errors: any[];
|
|
44
|
+
}>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CsvData = exports.CSV_COLUMN_METADATA = void 0;
|
|
13
|
+
exports.CsvColumn = CsvColumn;
|
|
14
|
+
exports.getCsvColumnMapping = getCsvColumnMapping;
|
|
15
|
+
exports.parseAndValidateCsvData = parseAndValidateCsvData;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const class_transformer_1 = require("class-transformer");
|
|
18
|
+
const class_validator_1 = require("class-validator");
|
|
19
|
+
/**
|
|
20
|
+
* CSV 列映射元数据键
|
|
21
|
+
*/
|
|
22
|
+
exports.CSV_COLUMN_METADATA = 'csv:column';
|
|
23
|
+
/**
|
|
24
|
+
* CSV 列名映射装饰器
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* export class OrderImportDto {
|
|
28
|
+
* @CsvColumn('订单号')
|
|
29
|
+
* @IsString()
|
|
30
|
+
* orderNo: string;
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function CsvColumn(columnName) {
|
|
35
|
+
return (target, propertyKey) => {
|
|
36
|
+
Reflect.defineMetadata(exports.CSV_COLUMN_METADATA, columnName, target, propertyKey);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
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 列映射
|
|
68
|
+
*/
|
|
69
|
+
function getCsvColumnMapping(dto) {
|
|
70
|
+
const mapping = new Map();
|
|
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;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 辅助函数:解析和验证 CSV 数据
|
|
86
|
+
*/
|
|
87
|
+
function parseAndValidateCsvData(rawData_1, dtoClass_1) {
|
|
88
|
+
return __awaiter(this, arguments, void 0, function* (rawData, dtoClass, options = {}) {
|
|
89
|
+
const { skipEmptyLines = true, skipHeader = false } = options;
|
|
90
|
+
const data = [];
|
|
91
|
+
const errors = [];
|
|
92
|
+
const mapping = getCsvColumnMapping(dtoClass);
|
|
93
|
+
const startIndex = skipHeader ? 1 : 0;
|
|
94
|
+
for (let i = startIndex; i < rawData.length; i++) {
|
|
95
|
+
const row = rawData[i];
|
|
96
|
+
// 跳过空行
|
|
97
|
+
if (skipEmptyLines && isEmptyRow(row)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const obj = {};
|
|
101
|
+
// 映射列名到属性名
|
|
102
|
+
for (const [columnName, propertyKey] of mapping.entries()) {
|
|
103
|
+
obj[propertyKey] = row[columnName];
|
|
104
|
+
}
|
|
105
|
+
// 转换为 DTO 实例
|
|
106
|
+
const instance = (0, class_transformer_1.plainToInstance)(dtoClass, obj);
|
|
107
|
+
// 验证
|
|
108
|
+
const validationErrors = yield (0, class_validator_1.validate)(instance);
|
|
109
|
+
if (validationErrors.length > 0) {
|
|
110
|
+
errors.push({
|
|
111
|
+
row: i + 1,
|
|
112
|
+
data: obj,
|
|
113
|
+
errors: validationErrors,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
data.push(instance);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { data, errors };
|
|
121
|
+
});
|
|
122
|
+
}
|
|
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,44 @@
|
|
|
1
|
+
import { ExcelDataOptions } from '../interfaces/file-upload-options.interface';
|
|
2
|
+
/**
|
|
3
|
+
* Excel 列映射元数据键
|
|
4
|
+
*/
|
|
5
|
+
export declare const EXCEL_COLUMN_METADATA = "excel:column";
|
|
6
|
+
/**
|
|
7
|
+
* Excel 列名映射装饰器
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* export class UserImportDto {
|
|
11
|
+
* @ExcelColumn('姓名')
|
|
12
|
+
* @IsString()
|
|
13
|
+
* name: string;
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function ExcelColumn(columnName: string): PropertyDecorator;
|
|
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 列映射
|
|
36
|
+
*/
|
|
37
|
+
export declare function getExcelColumnMapping(dto: any): Map<string, string>;
|
|
38
|
+
/**
|
|
39
|
+
* 辅助函数:解析和验证 Excel 数据
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseAndValidateExcelData<T extends object>(rawData: any[], dtoClass: new () => T, options?: ExcelDataOptions): Promise<{
|
|
42
|
+
data: T[];
|
|
43
|
+
errors: any[];
|
|
44
|
+
}>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ExcelData = exports.EXCEL_COLUMN_METADATA = void 0;
|
|
13
|
+
exports.ExcelColumn = ExcelColumn;
|
|
14
|
+
exports.getExcelColumnMapping = getExcelColumnMapping;
|
|
15
|
+
exports.parseAndValidateExcelData = parseAndValidateExcelData;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const class_transformer_1 = require("class-transformer");
|
|
18
|
+
const class_validator_1 = require("class-validator");
|
|
19
|
+
/**
|
|
20
|
+
* Excel 列映射元数据键
|
|
21
|
+
*/
|
|
22
|
+
exports.EXCEL_COLUMN_METADATA = 'excel:column';
|
|
23
|
+
/**
|
|
24
|
+
* Excel 列名映射装饰器
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* export class UserImportDto {
|
|
28
|
+
* @ExcelColumn('姓名')
|
|
29
|
+
* @IsString()
|
|
30
|
+
* name: string;
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function ExcelColumn(columnName) {
|
|
35
|
+
return (target, propertyKey) => {
|
|
36
|
+
Reflect.defineMetadata(exports.EXCEL_COLUMN_METADATA, columnName, target, propertyKey);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Excel 数据参数装饰器
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* @Post('import/excel')
|
|
44
|
+
* @FileUpload({ types: [FileType.Excel] })
|
|
45
|
+
* async importExcel(@ExcelData(UserImportDto) users: UserImportDto[]) {
|
|
46
|
+
* await this.userService.batchCreate(users);
|
|
47
|
+
* return { count: users.length };
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
exports.ExcelData = (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 中处理 Excel 解析
|
|
58
|
+
// 当前只返回文件信息,实际解析逻辑在 FileUploadInterceptor 中
|
|
59
|
+
return {
|
|
60
|
+
_excelData: true,
|
|
61
|
+
dto: data,
|
|
62
|
+
file,
|
|
63
|
+
options: data === null || data === void 0 ? void 0 : data['options'],
|
|
64
|
+
};
|
|
65
|
+
}));
|
|
66
|
+
/**
|
|
67
|
+
* 辅助函数:获取 DTO 的 Excel 列映射
|
|
68
|
+
*/
|
|
69
|
+
function getExcelColumnMapping(dto) {
|
|
70
|
+
const mapping = new Map();
|
|
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.EXCEL_COLUMN_METADATA, prototype, propertyKey);
|
|
78
|
+
if (columnName) {
|
|
79
|
+
mapping.set(columnName, propertyKey);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return mapping;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 辅助函数:解析和验证 Excel 数据
|
|
86
|
+
*/
|
|
87
|
+
function parseAndValidateExcelData(rawData_1, dtoClass_1) {
|
|
88
|
+
return __awaiter(this, arguments, void 0, function* (rawData, dtoClass, options = {}) {
|
|
89
|
+
const { startRow = 1, maxRows, stopOnError = false } = options;
|
|
90
|
+
const data = [];
|
|
91
|
+
const errors = [];
|
|
92
|
+
const mapping = getExcelColumnMapping(dtoClass);
|
|
93
|
+
let rowsProcessed = 0;
|
|
94
|
+
for (let i = startRow; i < rawData.length; i++) {
|
|
95
|
+
if (maxRows && rowsProcessed >= maxRows) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
const row = rawData[i];
|
|
99
|
+
const obj = {};
|
|
100
|
+
// 映射列名到属性名
|
|
101
|
+
for (const [columnName, propertyKey] of mapping.entries()) {
|
|
102
|
+
obj[propertyKey] = row[columnName];
|
|
103
|
+
}
|
|
104
|
+
// 转换为 DTO 实例
|
|
105
|
+
const instance = (0, class_transformer_1.plainToInstance)(dtoClass, obj);
|
|
106
|
+
// 验证
|
|
107
|
+
const validationErrors = yield (0, class_validator_1.validate)(instance);
|
|
108
|
+
if (validationErrors.length > 0) {
|
|
109
|
+
errors.push({
|
|
110
|
+
row: i + 1,
|
|
111
|
+
data: obj,
|
|
112
|
+
errors: validationErrors,
|
|
113
|
+
});
|
|
114
|
+
if (stopOnError) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
data.push(instance);
|
|
120
|
+
}
|
|
121
|
+
rowsProcessed++;
|
|
122
|
+
}
|
|
123
|
+
return { data, errors };
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { FileUploadOptions } from '../interfaces/file-upload-options.interface';
|
|
2
|
+
/**
|
|
3
|
+
* 文件上传装饰器元数据键
|
|
4
|
+
*/
|
|
5
|
+
export declare const FILE_UPLOAD_OPTIONS = "file_upload_options";
|
|
6
|
+
/**
|
|
7
|
+
* 统一的文件上传装饰器
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* @Post('upload')
|
|
12
|
+
* @FileUpload({ types: [FileType.Image, FileType.PDF] })
|
|
13
|
+
* async upload(@UploadedFile() file: Express.Multer.File) {
|
|
14
|
+
* return { url: file.path };
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function FileUpload(options?: FileUploadOptions): MethodDecorator;
|
|
19
|
+
/**
|
|
20
|
+
* 静态快捷方法命名空间
|
|
21
|
+
*/
|
|
22
|
+
export declare namespace FileUpload {
|
|
23
|
+
/**
|
|
24
|
+
* 图片上传
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* @Post('photos')
|
|
28
|
+
* @FileUpload.Image()
|
|
29
|
+
* async uploadPhoto(@UploadedFile() file: Express.Multer.File) {
|
|
30
|
+
* return { url: file.path };
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function Image(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
35
|
+
/**
|
|
36
|
+
* 头像上传(带默认图片处理)
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* @Post('users/:id/avatar')
|
|
40
|
+
* @FileUpload.Avatar()
|
|
41
|
+
* async uploadAvatar(@UploadedFile() file: Express.Multer.File) {
|
|
42
|
+
* return { url: file.path };
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function Avatar(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
47
|
+
/**
|
|
48
|
+
* 文档上传
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* @Post('documents')
|
|
52
|
+
* @FileUpload.Document()
|
|
53
|
+
* async uploadDocument(@UploadedFile() file: Express.Multer.File) {
|
|
54
|
+
* return { url: file.path };
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
function Document(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
59
|
+
/**
|
|
60
|
+
* PDF 上传
|
|
61
|
+
*/
|
|
62
|
+
function PDF(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
63
|
+
/**
|
|
64
|
+
* Excel 上传
|
|
65
|
+
*/
|
|
66
|
+
function Excel(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
67
|
+
/**
|
|
68
|
+
* CSV 上传
|
|
69
|
+
*/
|
|
70
|
+
function CSV(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
71
|
+
/**
|
|
72
|
+
* 视频上传
|
|
73
|
+
*/
|
|
74
|
+
function Video(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
75
|
+
/**
|
|
76
|
+
* 音频上传
|
|
77
|
+
*/
|
|
78
|
+
function Audio(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
79
|
+
/**
|
|
80
|
+
* 公开访问的文件上传
|
|
81
|
+
*/
|
|
82
|
+
function Public(options?: Partial<FileUploadOptions>): MethodDecorator;
|
|
83
|
+
}
|