@nest-omni/core 4.1.3-10 → 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.
Files changed (183) hide show
  1. package/audit/audit.module.js +42 -2
  2. package/audit/controllers/audit.controller.d.ts +64 -0
  3. package/audit/controllers/audit.controller.js +50 -0
  4. package/audit/decorators/audit-action.decorator.d.ts +74 -0
  5. package/audit/decorators/audit-action.decorator.js +42 -0
  6. package/audit/decorators/audit-controller.decorator.d.ts +1 -1
  7. package/audit/decorators/audit-controller.decorator.js +2 -2
  8. package/audit/decorators/entity-audit.decorator.d.ts +78 -2
  9. package/audit/decorators/entity-audit.decorator.js +145 -4
  10. package/audit/decorators/index.d.ts +2 -0
  11. package/audit/decorators/index.js +2 -0
  12. package/audit/entities/audit-action-summary.entity.d.ts +23 -0
  13. package/audit/entities/audit-action-summary.entity.js +101 -0
  14. package/audit/entities/entity-audit-log.entity.d.ts +8 -0
  15. package/audit/entities/entity-audit-log.entity.js +54 -2
  16. package/audit/entities/entity-transaction.entity.d.ts +8 -2
  17. package/audit/entities/entity-transaction.entity.js +39 -3
  18. package/audit/entities/index.d.ts +3 -0
  19. package/audit/entities/index.js +3 -0
  20. package/audit/entities/manual-operation-log.entity.js +8 -1
  21. package/audit/enums/audit.enums.d.ts +22 -6
  22. package/audit/enums/audit.enums.js +27 -9
  23. package/audit/index.d.ts +4 -1
  24. package/audit/index.js +25 -2
  25. package/audit/interceptors/audit-action.interceptor.d.ts +38 -0
  26. package/audit/interceptors/audit-action.interceptor.js +215 -0
  27. package/audit/interceptors/index.d.ts +1 -0
  28. package/audit/interceptors/index.js +1 -0
  29. package/audit/interfaces/audit.interfaces.d.ts +145 -2
  30. package/audit/services/audit-action.service.d.ts +141 -0
  31. package/audit/services/audit-action.service.js +244 -0
  32. package/audit/services/audit-context.service.d.ts +82 -0
  33. package/audit/services/audit-context.service.js +170 -0
  34. package/audit/services/entity-audit.service.d.ts +174 -4
  35. package/audit/services/entity-audit.service.js +515 -14
  36. package/audit/services/index.d.ts +3 -0
  37. package/audit/services/index.js +3 -0
  38. package/audit/services/manual-audit-log.service.d.ts +24 -23
  39. package/audit/services/manual-audit-log.service.js +32 -53
  40. package/audit/services/operation-description.service.d.ts +13 -3
  41. package/audit/services/operation-description.service.js +161 -24
  42. package/audit/services/transaction-audit.service.js +3 -3
  43. package/audit/subscribers/entity-audit.subscriber.d.ts +4 -0
  44. package/audit/subscribers/entity-audit.subscriber.js +47 -0
  45. package/file-upload/controllers/file-access.controller.d.ts +23 -0
  46. package/file-upload/controllers/file-access.controller.js +128 -0
  47. package/file-upload/decorators/csv-data.decorator.d.ts +44 -0
  48. package/file-upload/decorators/csv-data.decorator.js +131 -0
  49. package/file-upload/decorators/excel-data.decorator.d.ts +44 -0
  50. package/file-upload/decorators/excel-data.decorator.js +125 -0
  51. package/file-upload/decorators/file-upload.decorator.d.ts +83 -0
  52. package/file-upload/decorators/file-upload.decorator.js +172 -0
  53. package/file-upload/decorators/index.d.ts +4 -0
  54. package/file-upload/decorators/index.js +20 -0
  55. package/file-upload/decorators/process.decorator.d.ts +40 -0
  56. package/file-upload/decorators/process.decorator.js +52 -0
  57. package/file-upload/dto/create-file.dto.d.ts +24 -0
  58. package/file-upload/dto/create-file.dto.js +112 -0
  59. package/file-upload/dto/find-files.dto.d.ts +15 -0
  60. package/file-upload/dto/find-files.dto.js +76 -0
  61. package/file-upload/dto/index.d.ts +4 -0
  62. package/file-upload/dto/index.js +20 -0
  63. package/file-upload/dto/pagination.dto.d.ts +7 -0
  64. package/file-upload/dto/pagination.dto.js +39 -0
  65. package/file-upload/dto/update-file.dto.d.ts +16 -0
  66. package/file-upload/dto/update-file.dto.js +71 -0
  67. package/file-upload/entities/file-metadata.entity.d.ts +22 -0
  68. package/file-upload/entities/file-metadata.entity.js +84 -0
  69. package/file-upload/entities/file.entity.d.ts +129 -0
  70. package/file-upload/entities/file.entity.js +384 -0
  71. package/file-upload/entities/index.d.ts +2 -0
  72. package/file-upload/entities/index.js +18 -0
  73. package/file-upload/enums/file-type.enum.d.ts +72 -0
  74. package/file-upload/enums/file-type.enum.js +212 -0
  75. package/file-upload/exceptions/file-upload.exception.d.ts +57 -0
  76. package/file-upload/exceptions/file-upload.exception.js +120 -0
  77. package/file-upload/exceptions/index.d.ts +1 -0
  78. package/file-upload/exceptions/index.js +17 -0
  79. package/file-upload/file-upload.module.d.ts +89 -0
  80. package/file-upload/file-upload.module.js +264 -0
  81. package/file-upload/index.d.ts +26 -0
  82. package/file-upload/index.js +59 -0
  83. package/file-upload/interceptors/file-upload.interceptor.d.ts +48 -0
  84. package/file-upload/interceptors/file-upload.interceptor.js +434 -0
  85. package/file-upload/interceptors/index.d.ts +1 -0
  86. package/file-upload/interceptors/index.js +17 -0
  87. package/file-upload/interfaces/custom-file-type.interface.d.ts +72 -0
  88. package/file-upload/interfaces/custom-file-type.interface.js +2 -0
  89. package/file-upload/interfaces/file-buffer.interface.d.ts +72 -0
  90. package/file-upload/interfaces/file-buffer.interface.js +2 -0
  91. package/file-upload/interfaces/file-entity.interface.d.ts +142 -0
  92. package/file-upload/interfaces/file-entity.interface.js +28 -0
  93. package/file-upload/interfaces/file-metadata.interface.d.ts +21 -0
  94. package/file-upload/interfaces/file-metadata.interface.js +2 -0
  95. package/file-upload/interfaces/file-upload-options.interface.d.ts +117 -0
  96. package/file-upload/interfaces/file-upload-options.interface.js +2 -0
  97. package/file-upload/interfaces/index.d.ts +7 -0
  98. package/file-upload/interfaces/index.js +24 -0
  99. package/file-upload/interfaces/storage-provider.interface.d.ts +239 -0
  100. package/file-upload/interfaces/storage-provider.interface.js +2 -0
  101. package/file-upload/interfaces/upload-options.interface.d.ts +19 -0
  102. package/file-upload/interfaces/upload-options.interface.js +2 -0
  103. package/file-upload/providers/index.d.ts +2 -0
  104. package/file-upload/providers/index.js +18 -0
  105. package/file-upload/providers/local-storage.provider.d.ts +98 -0
  106. package/file-upload/providers/local-storage.provider.js +484 -0
  107. package/file-upload/providers/s3-storage.provider.d.ts +87 -0
  108. package/file-upload/providers/s3-storage.provider.js +455 -0
  109. package/file-upload/services/file-signature-validator.service.d.ts +118 -0
  110. package/file-upload/services/file-signature-validator.service.js +376 -0
  111. package/file-upload/services/file.service.d.ts +190 -0
  112. package/file-upload/services/file.service.js +609 -0
  113. package/file-upload/services/index.d.ts +4 -0
  114. package/file-upload/services/index.js +20 -0
  115. package/file-upload/services/malicious-file-detector.service.d.ts +274 -0
  116. package/file-upload/services/malicious-file-detector.service.js +1035 -0
  117. package/file-upload/services/mime-registry.service.d.ts +47 -0
  118. package/file-upload/services/mime-registry.service.js +167 -0
  119. package/file-upload/utils/checksum.util.d.ts +28 -0
  120. package/file-upload/utils/checksum.util.js +65 -0
  121. package/file-upload/utils/dynamic-import.util.d.ts +50 -0
  122. package/file-upload/utils/dynamic-import.util.js +144 -0
  123. package/file-upload/utils/filename.util.d.ts +59 -0
  124. package/file-upload/utils/filename.util.js +184 -0
  125. package/file-upload/utils/filepath.util.d.ts +70 -0
  126. package/file-upload/utils/filepath.util.js +152 -0
  127. package/file-upload/utils/index.d.ts +4 -0
  128. package/file-upload/utils/index.js +20 -0
  129. package/http-client/http-client.module.js +1 -5
  130. package/index.d.ts +3 -1
  131. package/index.js +4 -1
  132. package/package.json +4 -5
  133. package/redis-lock/lock-heartbeat.service.d.ts +2 -2
  134. package/redis-lock/lock-heartbeat.service.js +4 -4
  135. package/redis-lock/redis-lock.service.d.ts +18 -0
  136. package/redis-lock/redis-lock.service.js +38 -8
  137. package/setup/bootstrap.setup.d.ts +1 -0
  138. package/setup/bootstrap.setup.js +1 -0
  139. package/setup/schedule.decorator.js +18 -8
  140. package/shared/index.d.ts +1 -1
  141. package/shared/index.js +1 -1
  142. package/shared/{serviceRegistryModule.js → service-registry.module.js} +9 -16
  143. package/shared/services/index.d.ts +0 -1
  144. package/shared/services/index.js +0 -1
  145. package/transaction/__tests__/mocks.d.ts +9 -0
  146. package/transaction/__tests__/mocks.js +33 -0
  147. package/transaction/base-service-transaction.d.ts +99 -0
  148. package/transaction/base-service-transaction.js +286 -0
  149. package/transaction/cls-compatibility.service.d.ts +55 -0
  150. package/transaction/cls-compatibility.service.js +127 -0
  151. package/transaction/data-source-registry.d.ts +91 -0
  152. package/transaction/data-source-registry.js +349 -0
  153. package/transaction/database-adapter.d.ts +44 -0
  154. package/transaction/database-adapter.js +240 -0
  155. package/transaction/decorators/entity-datasource.decorator.d.ts +62 -0
  156. package/transaction/decorators/entity-datasource.decorator.js +105 -0
  157. package/transaction/index.d.ts +14 -0
  158. package/transaction/index.js +57 -0
  159. package/transaction/logging-transactional.interceptor.d.ts +18 -0
  160. package/transaction/logging-transactional.interceptor.js +163 -0
  161. package/transaction/transaction-context.service.d.ts +137 -0
  162. package/transaction/transaction-context.service.js +411 -0
  163. package/transaction/transaction-manager.d.ts +230 -0
  164. package/transaction/transaction-manager.js +1001 -0
  165. package/transaction/transaction-synchronization.d.ts +171 -0
  166. package/transaction/transaction-synchronization.js +380 -0
  167. package/transaction/transaction.errors.d.ts +91 -0
  168. package/transaction/transaction.errors.js +206 -0
  169. package/transaction/transaction.module.d.ts +30 -0
  170. package/transaction/transaction.module.js +98 -0
  171. package/transaction/transactional.decorator.d.ts +82 -0
  172. package/transaction/transactional.decorator.js +319 -0
  173. package/transaction/typeorm-module-wrapper.d.ts +96 -0
  174. package/transaction/typeorm-module-wrapper.js +197 -0
  175. package/validators/file-mimetype.validator.d.ts +0 -2
  176. package/validators/file-mimetype.validator.js +4 -6
  177. package/validators/is-exists.validator.d.ts +2 -5
  178. package/validators/is-exists.validator.js +4 -6
  179. package/validators/is-unique.validator.d.ts +2 -5
  180. package/validators/is-unique.validator.js +6 -11
  181. package/shared/services/validator.service.d.ts +0 -3
  182. package/shared/services/validator.service.js +0 -20
  183. /package/shared/{serviceRegistryModule.d.ts → service-registry.module.d.ts} +0 -0
@@ -48,7 +48,7 @@ let TransactionAuditService = class TransactionAuditService {
48
48
  const context = yield this.contextService.getCurrentContext();
49
49
  const transaction = this.transactionRepository.create({
50
50
  description,
51
- status: enums_1.TransactionStatus.PENDING,
51
+ status: enums_1.AuditTransactionStatus.PENDING,
52
52
  entities: [],
53
53
  userId: context.userId,
54
54
  username: context.username,
@@ -72,7 +72,7 @@ let TransactionAuditService = class TransactionAuditService {
72
72
  if (!transaction) {
73
73
  throw new Error(`Transaction not found: ${transactionId}`);
74
74
  }
75
- transaction.status = enums_1.TransactionStatus.COMMITTED;
75
+ transaction.status = enums_1.AuditTransactionStatus.COMMITTED;
76
76
  yield this.transactionRepository.save(transaction);
77
77
  });
78
78
  }
@@ -89,7 +89,7 @@ let TransactionAuditService = class TransactionAuditService {
89
89
  }
90
90
  // 执行回滚操作
91
91
  yield this.performRollback(transaction);
92
- transaction.status = enums_1.TransactionStatus.ROLLED_BACK;
92
+ transaction.status = enums_1.AuditTransactionStatus.ROLLED_BACK;
93
93
  yield this.transactionRepository.save(transaction);
94
94
  });
95
95
  }
@@ -22,6 +22,10 @@ export declare class EntityAuditSubscriber implements EntitySubscriberInterface<
22
22
  * 在实体删除前记录审计日志
23
23
  */
24
24
  beforeRemove(event: RemoveEvent<any>): Promise<void>;
25
+ /**
26
+ * 计算变更字段
27
+ */
28
+ private calculateChangedFields;
25
29
  /**
26
30
  * 检查是否启用了审计
27
31
  */
@@ -57,11 +57,21 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
57
57
  try {
58
58
  // 获取上下文信息
59
59
  const context = yield this.contextService.getCurrentContext();
60
+ const actionContext = this.contextService.getCurrentActionContext();
61
+ // 记录到审计动作上下文
62
+ if (actionContext) {
63
+ this.contextService.recordEntityChangeInAction(entityType, entityId, enums_1.AuditOperation.CREATE);
64
+ // 记录详细的实体变更数据
65
+ this.contextService.recordDetailedEntityChange(entityType, entityId, enums_1.AuditOperation.CREATE, {}, entity, Object.keys(entity));
66
+ }
60
67
  // 记录审计日志
61
68
  yield this.entityAuditService.logEntityChange(entityType, entityId, enums_1.AuditOperation.CREATE, {}, entity, {
62
69
  requestId: context.requestId,
63
70
  requestIp: context.requestIp,
64
71
  userAgent: context.userAgent,
72
+ auditActionId: actionContext === null || actionContext === void 0 ? void 0 : actionContext.actionId,
73
+ auditActionName: actionContext === null || actionContext === void 0 ? void 0 : actionContext.actionName,
74
+ sequenceInAction: (actionContext === null || actionContext === void 0 ? void 0 : actionContext.entityChanges.length) || 0,
65
75
  });
66
76
  }
67
77
  catch (error) {
@@ -91,11 +101,22 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
91
101
  try {
92
102
  // 获取上下文信息
93
103
  const context = yield this.contextService.getCurrentContext();
104
+ const actionContext = this.contextService.getCurrentActionContext();
105
+ // 记录到审计动作上下文
106
+ if (actionContext) {
107
+ this.contextService.recordEntityChangeInAction(entityType, entityId, enums_1.AuditOperation.UPDATE);
108
+ // 记录详细的实体变更数据
109
+ const changedFields = this.calculateChangedFields(databaseEntity || {}, entity);
110
+ this.contextService.recordDetailedEntityChange(entityType, entityId, enums_1.AuditOperation.UPDATE, databaseEntity || {}, entity, changedFields);
111
+ }
94
112
  // 记录审计日志
95
113
  yield this.entityAuditService.logEntityChange(entityType, entityId, enums_1.AuditOperation.UPDATE, databaseEntity || {}, entity, {
96
114
  requestId: context.requestId,
97
115
  requestIp: context.requestIp,
98
116
  userAgent: context.userAgent,
117
+ auditActionId: actionContext === null || actionContext === void 0 ? void 0 : actionContext.actionId,
118
+ auditActionName: actionContext === null || actionContext === void 0 ? void 0 : actionContext.actionName,
119
+ sequenceInAction: (actionContext === null || actionContext === void 0 ? void 0 : actionContext.entityChanges.length) || 0,
99
120
  });
100
121
  }
101
122
  catch (error) {
@@ -124,11 +145,21 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
124
145
  try {
125
146
  // 获取上下文信息
126
147
  const context = yield this.contextService.getCurrentContext();
148
+ const actionContext = this.contextService.getCurrentActionContext();
149
+ // 记录到审计动作上下文
150
+ if (actionContext) {
151
+ this.contextService.recordEntityChangeInAction(entityType, entityId, enums_1.AuditOperation.DELETE);
152
+ // 记录详细的实体变更数据
153
+ this.contextService.recordDetailedEntityChange(entityType, entityId, enums_1.AuditOperation.DELETE, entity, {}, Object.keys(entity));
154
+ }
127
155
  // 记录审计日志
128
156
  yield this.entityAuditService.logEntityChange(entityType, entityId, enums_1.AuditOperation.DELETE, entity, {}, {
129
157
  requestId: context.requestId,
130
158
  requestIp: context.requestIp,
131
159
  userAgent: context.userAgent,
160
+ auditActionId: actionContext === null || actionContext === void 0 ? void 0 : actionContext.actionId,
161
+ auditActionName: actionContext === null || actionContext === void 0 ? void 0 : actionContext.actionName,
162
+ sequenceInAction: (actionContext === null || actionContext === void 0 ? void 0 : actionContext.entityChanges.length) || 0,
132
163
  });
133
164
  }
134
165
  catch (error) {
@@ -136,6 +167,22 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
136
167
  }
137
168
  });
138
169
  }
170
+ /**
171
+ * 计算变更字段
172
+ */
173
+ calculateChangedFields(oldValue, newValue) {
174
+ const changedFields = [];
175
+ const allKeys = new Set([
176
+ ...Object.keys(oldValue || {}),
177
+ ...Object.keys(newValue || {}),
178
+ ]);
179
+ for (const key of allKeys) {
180
+ if (JSON.stringify(oldValue[key]) !== JSON.stringify(newValue[key])) {
181
+ changedFields.push(key);
182
+ }
183
+ }
184
+ return changedFields;
185
+ }
139
186
  /**
140
187
  * 检查是否启用了审计
141
188
  */
@@ -0,0 +1,23 @@
1
+ import { StreamableFile } from '@nestjs/common';
2
+ import { Response } from 'express';
3
+ import { FileService } from '../services/file.service';
4
+ /**
5
+ * 文件访问控制器
6
+ * 提供文件下载和预览功能
7
+ * 注意:Controller 仅负责 HTTP 层逻辑,业务逻辑在 FileService
8
+ */
9
+ export declare class FileAccessController {
10
+ private readonly fileService;
11
+ private readonly logger;
12
+ constructor(fileService: FileService);
13
+ /**
14
+ * 通过文件 ID 下载/访问文件
15
+ * GET /files/:id
16
+ */
17
+ getFile(id: string, res: Response): Promise<StreamableFile>;
18
+ /**
19
+ * 通过文件 ID 下载文件(强制下载)
20
+ * GET /files/:id/download
21
+ */
22
+ downloadFile(id: string, res: Response): Promise<StreamableFile>;
23
+ }
@@ -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
+ }