sa2kit 1.0.0 → 1.0.2

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 (99) hide show
  1. package/dist/UniversalFileService-CEZRJ87g.d.mts +727 -0
  2. package/dist/UniversalFileService-CEZRJ87g.d.ts +727 -0
  3. package/dist/api/index.d.mts +248 -0
  4. package/dist/api/index.d.ts +248 -0
  5. package/dist/api/index.js +294 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/api/index.mjs +290 -0
  8. package/dist/api/index.mjs.map +1 -0
  9. package/dist/auth/client/index.d.mts +52 -3
  10. package/dist/auth/client/index.d.ts +52 -3
  11. package/dist/auth/components/index.d.mts +149 -4
  12. package/dist/auth/components/index.d.ts +149 -4
  13. package/dist/auth/components/index.js +243 -9
  14. package/dist/auth/components/index.js.map +1 -1
  15. package/dist/auth/components/index.mjs +237 -4
  16. package/dist/auth/components/index.mjs.map +1 -1
  17. package/dist/auth/hooks/index.d.mts +31 -2
  18. package/dist/auth/hooks/index.d.ts +31 -2
  19. package/dist/auth/index.d.mts +5 -5
  20. package/dist/auth/index.d.ts +5 -5
  21. package/dist/auth/index.js +49 -17
  22. package/dist/auth/index.mjs +1 -1
  23. package/dist/auth/routes/index.d.mts +103 -5
  24. package/dist/auth/routes/index.d.ts +103 -5
  25. package/dist/auth/routes/index.js +37 -5
  26. package/dist/auth/routes/index.mjs +1 -1
  27. package/dist/chunk-42IJ7HEI.js +573 -0
  28. package/dist/chunk-42IJ7HEI.js.map +1 -0
  29. package/dist/chunk-7XLFSPDG.mjs +31 -0
  30. package/dist/chunk-7XLFSPDG.mjs.map +1 -0
  31. package/dist/chunk-GCVOKQZP.js +36 -0
  32. package/dist/chunk-GCVOKQZP.js.map +1 -0
  33. package/dist/chunk-IBLB7ARJ.mjs +560 -0
  34. package/dist/chunk-IBLB7ARJ.mjs.map +1 -0
  35. package/dist/{chunk-6FNUWAIV.js → chunk-LX4XX6W7.js} +54 -8
  36. package/dist/chunk-LX4XX6W7.js.map +1 -0
  37. package/dist/{chunk-HXFFYNIF.mjs → chunk-T5OZHYVM.mjs} +54 -8
  38. package/dist/chunk-T5OZHYVM.mjs.map +1 -0
  39. package/dist/config/server/index.d.mts +1533 -0
  40. package/dist/config/server/index.d.ts +1533 -0
  41. package/dist/config/server/index.js +1177 -0
  42. package/dist/config/server/index.js.map +1 -0
  43. package/dist/config/server/index.mjs +1138 -0
  44. package/dist/config/server/index.mjs.map +1 -0
  45. package/dist/i18n/index.d.mts +2 -1
  46. package/dist/i18n/index.d.ts +2 -1
  47. package/dist/i18n/index.js +125 -61
  48. package/dist/i18n/index.js.map +1 -1
  49. package/dist/i18n/index.mjs +126 -62
  50. package/dist/i18n/index.mjs.map +1 -1
  51. package/dist/index.js +6 -6
  52. package/dist/index.mjs +1 -1
  53. package/dist/mmd/index.d.mts +346 -0
  54. package/dist/mmd/index.d.ts +346 -0
  55. package/dist/mmd/index.js +1535 -0
  56. package/dist/mmd/index.js.map +1 -0
  57. package/dist/mmd/index.mjs +1503 -0
  58. package/dist/mmd/index.mjs.map +1 -0
  59. package/dist/storage/index.d.mts +1 -0
  60. package/dist/storage/index.d.ts +1 -0
  61. package/dist/storage/index.js +9 -9
  62. package/dist/storage/index.mjs +1 -1
  63. package/dist/{index-8VoHap_4.d.mts → types-CroexXnI.d.ts} +38 -44
  64. package/dist/{index-8VoHap_4.d.ts → types-DmsXCWvm.d.mts} +38 -44
  65. package/dist/{types-DAxQ1MeY.d.ts → types-Dt0oqeFM.d.mts} +1 -1
  66. package/dist/{types-DT8LVCvE.d.mts → types-zK6kDzDQ.d.ts} +1 -1
  67. package/dist/universalExport/index.js +17 -32
  68. package/dist/universalExport/index.js.map +1 -1
  69. package/dist/universalExport/index.mjs +2 -29
  70. package/dist/universalExport/index.mjs.map +1 -1
  71. package/dist/universalExport/server/index.d.mts +849 -8
  72. package/dist/universalExport/server/index.d.ts +849 -8
  73. package/dist/universalExport/server/index.js +1382 -2
  74. package/dist/universalExport/server/index.js.map +1 -1
  75. package/dist/universalExport/server/index.mjs +1355 -3
  76. package/dist/universalExport/server/index.mjs.map +1 -1
  77. package/dist/universalFile/index.d.mts +54 -3
  78. package/dist/universalFile/index.d.ts +54 -3
  79. package/dist/universalFile/index.js +272 -0
  80. package/dist/universalFile/index.js.map +1 -1
  81. package/dist/universalFile/index.mjs +267 -1
  82. package/dist/universalFile/index.mjs.map +1 -1
  83. package/dist/universalFile/server/index.d.mts +2541 -469
  84. package/dist/universalFile/server/index.d.ts +2541 -469
  85. package/dist/universalFile/server/index.js +830 -64
  86. package/dist/universalFile/server/index.js.map +1 -1
  87. package/dist/universalFile/server/index.mjs +803 -66
  88. package/dist/universalFile/server/index.mjs.map +1 -1
  89. package/package.json +47 -23
  90. package/dist/chunk-6FNUWAIV.js.map +0 -1
  91. package/dist/chunk-APY57REU.js +0 -300
  92. package/dist/chunk-APY57REU.js.map +0 -1
  93. package/dist/chunk-C64RY2OW.mjs +0 -295
  94. package/dist/chunk-C64RY2OW.mjs.map +0 -1
  95. package/dist/chunk-HXFFYNIF.mjs.map +0 -1
  96. package/dist/types-CoGG1rNV.d.mts +0 -258
  97. package/dist/types-CoGG1rNV.d.ts +0 -258
  98. package/dist/types-DW9qar-w.d.mts +0 -52
  99. package/dist/types-DW9qar-w.d.ts +0 -52
@@ -1,6 +1,1207 @@
1
1
  'use strict';
2
2
 
3
+ var chunkGCVOKQZP_js = require('../../chunk-GCVOKQZP.js');
4
+ var chunk6PRFP5EG_js = require('../../chunk-6PRFP5EG.js');
3
5
  require('../../chunk-DGUM43GV.js');
6
+ var XLSX = require('xlsx');
7
+ var pgCore = require('drizzle-orm/pg-core');
8
+ var drizzleOrm = require('drizzle-orm');
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
29
+
30
+ var logger = chunk6PRFP5EG_js.createLogger("UniversalExportService");
31
+ var DEFAULT_CONFIG = {
32
+ defaultFormat: "csv",
33
+ defaultDelimiter: ",",
34
+ defaultEncoding: "utf-8",
35
+ defaultAddBOM: true,
36
+ maxFileSize: 100 * 1024 * 1024,
37
+ // 100MB
38
+ maxRowsLimit: 1e5,
39
+ maxConcurrentExports: 5,
40
+ exportTimeout: 3e5,
41
+ // 5分钟
42
+ cache: {
43
+ configTTL: 3600,
44
+ // 1小时
45
+ resultTTL: 1800
46
+ // 30分钟
47
+ }
48
+ };
49
+ var DEFAULT_FORMATTERS = {
50
+ // 日期格式化
51
+ date: (value) => {
52
+ if (!value) return "";
53
+ const date = new Date(value);
54
+ return date.toISOString().split("T")[0];
55
+ },
56
+ // 时间格式化
57
+ datetime: (value) => {
58
+ if (!value) return "";
59
+ const date = new Date(value);
60
+ return date.toLocaleString("zh-CN");
61
+ },
62
+ // 数字格式化
63
+ number: (value) => {
64
+ if (value === null || value === void 0) return "";
65
+ return String(value);
66
+ },
67
+ // 货币格式化
68
+ currency: (value) => {
69
+ if (value === null || value === void 0) return "";
70
+ return `\xA5${Number(value).toFixed(2)}`;
71
+ },
72
+ // 百分比格式化
73
+ percentage: (value) => {
74
+ if (value === null || value === void 0) return "";
75
+ return `${(Number(value) * 100).toFixed(2)}%`;
76
+ },
77
+ // 布尔值格式化
78
+ boolean: (value) => {
79
+ if (value === null || value === void 0) return "";
80
+ return value ? "\u662F" : "\u5426";
81
+ },
82
+ // 数组格式化
83
+ array: (value) => {
84
+ if (!Array.isArray(value)) return "";
85
+ return value.join(", ");
86
+ },
87
+ // 对象格式化
88
+ object: (value) => {
89
+ if (!value || typeof value !== "object") return "";
90
+ return JSON.stringify(value);
91
+ }
92
+ };
93
+ var UniversalExportService = class {
94
+ constructor(config, client) {
95
+ this.eventListeners = /* @__PURE__ */ new Map();
96
+ this.activeExports = /* @__PURE__ */ new Map();
97
+ this.configCache = /* @__PURE__ */ new Map();
98
+ this.resultCache = /* @__PURE__ */ new Map();
99
+ this.config = { ...DEFAULT_CONFIG, ...config };
100
+ this.client = client;
101
+ }
102
+ // ============= 配置管理 =============
103
+ /**
104
+ * 创建导出配置
105
+ */
106
+ async createConfig(config) {
107
+ try {
108
+ this.validateConfig({
109
+ ...config,
110
+ id: "temp",
111
+ createdAt: /* @__PURE__ */ new Date(),
112
+ updatedAt: /* @__PURE__ */ new Date()
113
+ });
114
+ if (!this.client) {
115
+ throw new chunkGCVOKQZP_js.ExportConfigError("\u672A\u63D0\u4F9B\u5BFC\u51FA\u5BA2\u6237\u7AEF\u670D\u52A1", config);
116
+ }
117
+ const newConfig = await this.client.createConfig(config);
118
+ this.configCache.set(newConfig.id, {
119
+ config: newConfig,
120
+ timestamp: Date.now()
121
+ });
122
+ this.emitEvent({
123
+ type: "config:save",
124
+ exportId: newConfig.id,
125
+ timestamp: /* @__PURE__ */ new Date(),
126
+ data: { config: newConfig }
127
+ });
128
+ return newConfig;
129
+ } catch (error) {
130
+ throw new chunkGCVOKQZP_js.ExportConfigError(
131
+ `\u521B\u5EFA\u5BFC\u51FA\u914D\u7F6E\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
132
+ { originalError: error }
133
+ );
134
+ }
135
+ }
136
+ /**
137
+ * 获取导出配置
138
+ */
139
+ async getConfig(configId) {
140
+ const cached = this.configCache.get(configId);
141
+ if (cached && Date.now() - cached.timestamp < this.config.cache.configTTL * 1e3) {
142
+ return cached.config;
143
+ }
144
+ return null;
145
+ }
146
+ /**
147
+ * 更新导出配置
148
+ */
149
+ async updateConfig(configId, updates) {
150
+ const existing = await this.getConfig(configId);
151
+ if (!existing) {
152
+ throw new chunkGCVOKQZP_js.ExportConfigError(`\u914D\u7F6E\u4E0D\u5B58\u5728: ${configId}`);
153
+ }
154
+ const updatedConfig = {
155
+ ...existing,
156
+ ...updates,
157
+ updatedAt: /* @__PURE__ */ new Date()
158
+ };
159
+ this.validateConfig(updatedConfig);
160
+ this.configCache.set(configId, {
161
+ config: updatedConfig,
162
+ timestamp: Date.now()
163
+ });
164
+ this.emitEvent({
165
+ type: "config:save",
166
+ exportId: configId,
167
+ timestamp: /* @__PURE__ */ new Date(),
168
+ data: { config: updatedConfig }
169
+ });
170
+ return updatedConfig;
171
+ }
172
+ /**
173
+ * 删除导出配置
174
+ */
175
+ async deleteConfig(configId) {
176
+ const existing = await this.getConfig(configId);
177
+ if (!existing) {
178
+ throw new chunkGCVOKQZP_js.ExportConfigError(`\u914D\u7F6E\u4E0D\u5B58\u5728: ${configId}`);
179
+ }
180
+ this.configCache.delete(configId);
181
+ this.emitEvent({
182
+ type: "config:delete",
183
+ exportId: configId,
184
+ timestamp: /* @__PURE__ */ new Date(),
185
+ data: { configId }
186
+ });
187
+ }
188
+ /**
189
+ * 获取模块的配置列表
190
+ */
191
+ async getConfigsByModule(moduleId, businessId) {
192
+ if (!this.client) {
193
+ throw new chunkGCVOKQZP_js.ExportConfigError("\u672A\u63D0\u4F9B\u5BFC\u51FA\u5BA2\u6237\u7AEF\u670D\u52A1", { moduleId, businessId });
194
+ }
195
+ return await this.client.getConfigsByModule(moduleId, businessId);
196
+ }
197
+ // ============= 导出执行 =============
198
+ /**
199
+ * 执行导出
200
+ */
201
+ async export(request) {
202
+ const exportId = this.generateId();
203
+ const startTime = /* @__PURE__ */ new Date();
204
+ logger.info("\u{1F680} [UniversalExportService] \u5F00\u59CB\u5BFC\u51FA:", {
205
+ exportId,
206
+ configId: request.configId,
207
+ hasDataSource: !!request.dataSource,
208
+ hasCallbacks: !!request.callbacks
209
+ });
210
+ try {
211
+ let config;
212
+ if (typeof request.configId === "object" && request.configId !== null) {
213
+ config = request.configId;
214
+ logger.info("\u{1F4CB} [UniversalExportService] \u4F7F\u7528\u76F4\u63A5\u4F20\u5165\u7684\u914D\u7F6E:", {
215
+ configId: config.id,
216
+ configName: config.name,
217
+ format: config.format,
218
+ fieldsCount: config.fields.length,
219
+ hasGrouping: !!config.grouping,
220
+ groupingEnabled: config.grouping?.enabled,
221
+ groupingFieldsCount: config.grouping?.fields?.length || 0,
222
+ groupingFields: config.grouping?.fields?.map((f) => ({ key: f.key, mergeCells: f.mergeCells })) || []
223
+ });
224
+ } else {
225
+ logger.info("\u{1F50D} [UniversalExportService] \u4ECE\u7F13\u5B58\u83B7\u53D6\u914D\u7F6E:", request.configId);
226
+ const cachedConfig = await this.getConfig(request.configId);
227
+ if (!cachedConfig) {
228
+ throw new chunkGCVOKQZP_js.ExportConfigError(`\u5BFC\u51FA\u914D\u7F6E\u4E0D\u5B58\u5728: ${request.configId}`);
229
+ }
230
+ config = cachedConfig;
231
+ logger.info("\u2705 [UniversalExportService] \u6210\u529F\u83B7\u53D6\u7F13\u5B58\u914D\u7F6E:", {
232
+ configId: config.id,
233
+ configName: config.name
234
+ });
235
+ }
236
+ const progress = {
237
+ exportId,
238
+ status: "pending",
239
+ progress: 0,
240
+ processedRows: 0,
241
+ totalRows: 0,
242
+ startTime
243
+ };
244
+ this.activeExports.set(exportId, progress);
245
+ this.emitEvent({
246
+ type: "export:start",
247
+ exportId,
248
+ timestamp: startTime,
249
+ data: { config, request }
250
+ });
251
+ if (request.callbacks?.onProgress) {
252
+ logger.info("\u{1F4DE} [UniversalExportService] \u8C03\u7528 onProgress \u56DE\u8C03 - \u5F00\u59CB");
253
+ request.callbacks.onProgress(progress);
254
+ }
255
+ logger.info("\u{1F4CA} [UniversalExportService] \u5F00\u59CB\u83B7\u53D6\u6570\u636E...");
256
+ const data = await this.getData(request);
257
+ logger.info("\u2705 [UniversalExportService] \u6570\u636E\u83B7\u53D6\u6210\u529F:", {
258
+ dataLength: data.length,
259
+ firstItem: data[0] ? Object.keys(data[0]) : [],
260
+ sampleData: data.slice(0, 2)
261
+ });
262
+ progress.totalRows = data.length;
263
+ progress.status = "processing";
264
+ if (request.callbacks?.onProgress) {
265
+ logger.info("\u{1F4DE} [UniversalExportService] \u8C03\u7528 onProgress \u56DE\u8C03 - \u6570\u636E\u5904\u7406");
266
+ progress.progress = 30;
267
+ request.callbacks.onProgress(progress);
268
+ }
269
+ logger.info("\u{1F504} [UniversalExportService] \u5F00\u59CB\u5904\u7406\u6570\u636E...");
270
+ const processedData = await this.processData(data, request, config);
271
+ logger.info("\u2705 [UniversalExportService] \u6570\u636E\u5904\u7406\u5B8C\u6210:", {
272
+ originalLength: data.length,
273
+ processedLength: processedData.length
274
+ });
275
+ if (request.callbacks?.onProgress) {
276
+ logger.info("\u{1F4DE} [UniversalExportService] \u8C03\u7528 onProgress \u56DE\u8C03 - \u6570\u636E\u5B8C\u6210");
277
+ progress.progress = 60;
278
+ request.callbacks.onProgress(progress);
279
+ }
280
+ logger.info("\u{1F4C4} [UniversalExportService] \u5F00\u59CB\u751F\u6210\u6587\u4EF6...");
281
+ const result = await this.generateFile(processedData, config, request, exportId);
282
+ logger.info("\u2705 [UniversalExportService] \u6587\u4EF6\u751F\u6210\u6210\u529F:", {
283
+ fileName: result.fileName,
284
+ fileSize: result.fileSize,
285
+ exportedRows: result.exportedRows
286
+ });
287
+ progress.status = "completed";
288
+ progress.progress = 100;
289
+ progress.processedRows = data.length;
290
+ if (request.callbacks?.onSuccess) {
291
+ logger.info("\u{1F4DE} [UniversalExportService] \u8C03\u7528 onSuccess \u56DE\u8C03");
292
+ request.callbacks.onSuccess(result);
293
+ }
294
+ this.emitEvent({
295
+ type: "export:complete",
296
+ exportId,
297
+ timestamp: /* @__PURE__ */ new Date(),
298
+ data: { result }
299
+ });
300
+ this.resultCache.set(exportId, {
301
+ result,
302
+ timestamp: Date.now()
303
+ });
304
+ this.activeExports.delete(exportId);
305
+ return result;
306
+ } catch (error) {
307
+ const errorObj = {
308
+ code: "EXPORT_FAILED",
309
+ message: error instanceof Error ? error.message : "\u5BFC\u51FA\u5931\u8D25",
310
+ details: { originalError: error },
311
+ timestamp: /* @__PURE__ */ new Date()
312
+ };
313
+ const progress = this.activeExports.get(exportId);
314
+ if (progress) {
315
+ progress.status = "failed";
316
+ progress.error = errorObj.message;
317
+ this.activeExports.delete(exportId);
318
+ }
319
+ if (request.callbacks?.onError) {
320
+ logger.info("\u{1F4DE} [UniversalExportService] \u8C03\u7528 onError \u56DE\u8C03");
321
+ request.callbacks.onError(errorObj);
322
+ }
323
+ this.emitEvent({
324
+ type: "export:error",
325
+ exportId,
326
+ timestamp: /* @__PURE__ */ new Date(),
327
+ error: errorObj.message,
328
+ data: { error: errorObj }
329
+ });
330
+ throw error;
331
+ }
332
+ }
333
+ /**
334
+ * 获取导出进度
335
+ */
336
+ getExportProgress(exportId) {
337
+ return this.activeExports.get(exportId) || null;
338
+ }
339
+ /**
340
+ * 取消导出
341
+ */
342
+ cancelExport(exportId) {
343
+ const progress = this.activeExports.get(exportId);
344
+ if (!progress) {
345
+ return false;
346
+ }
347
+ progress.status = "cancelled";
348
+ this.activeExports.delete(exportId);
349
+ this.emitEvent({
350
+ type: "export:cancel",
351
+ exportId,
352
+ timestamp: /* @__PURE__ */ new Date(),
353
+ data: { progress }
354
+ });
355
+ return true;
356
+ }
357
+ // ============= 事件管理 =============
358
+ /**
359
+ * 添加事件监听器
360
+ */
361
+ addEventListener(type, listener) {
362
+ if (!this.eventListeners.has(type)) {
363
+ this.eventListeners.set(type, []);
364
+ }
365
+ this.eventListeners.get(type).push(listener);
366
+ }
367
+ /**
368
+ * 移除事件监听器
369
+ */
370
+ removeEventListener(type, listener) {
371
+ const listeners = this.eventListeners.get(type);
372
+ if (listeners) {
373
+ const index = listeners.indexOf(listener);
374
+ if (index > -1) {
375
+ listeners.splice(index, 1);
376
+ }
377
+ }
378
+ }
379
+ // ============= 私有方法 =============
380
+ /**
381
+ * 生成唯一ID
382
+ */
383
+ generateId() {
384
+ return `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
385
+ }
386
+ /**
387
+ * 验证配置
388
+ */
389
+ validateConfig(config) {
390
+ if (!config.name || config.name.trim() === "") {
391
+ throw new chunkGCVOKQZP_js.ExportConfigError("\u914D\u7F6E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
392
+ }
393
+ if (!config.fields || config.fields.length === 0) {
394
+ throw new chunkGCVOKQZP_js.ExportConfigError("\u81F3\u5C11\u9700\u8981\u5B9A\u4E49\u4E00\u4E2A\u5B57\u6BB5");
395
+ }
396
+ const enabledFields = config.fields.filter((f) => f.enabled);
397
+ if (enabledFields.length === 0) {
398
+ throw new chunkGCVOKQZP_js.ExportConfigError("\u81F3\u5C11\u9700\u8981\u542F\u7528\u4E00\u4E2A\u5B57\u6BB5");
399
+ }
400
+ const keys = config.fields.map((f) => f.key);
401
+ const uniqueKeys = new Set(keys);
402
+ if (keys.length !== uniqueKeys.size) {
403
+ throw new chunkGCVOKQZP_js.ExportConfigError("\u5B57\u6BB5\u952E\u540D\u5FC5\u987B\u552F\u4E00");
404
+ }
405
+ }
406
+ /**
407
+ * 获取数据
408
+ */
409
+ async getData(request) {
410
+ logger.info("\u{1F50D} [UniversalExportService] getData \u5F00\u59CB\u6267\u884C...");
411
+ try {
412
+ if (typeof request.dataSource === "function") {
413
+ logger.info("\u{1F4DE} [UniversalExportService] \u8C03\u7528\u6570\u636E\u6E90\u51FD\u6570...");
414
+ const data = await request.dataSource();
415
+ logger.info("\u2705 [UniversalExportService] \u6570\u636E\u6E90\u51FD\u6570\u6267\u884C\u6210\u529F:", {
416
+ dataType: typeof data,
417
+ isArray: Array.isArray(data),
418
+ length: Array.isArray(data) ? data.length : "N/A"
419
+ });
420
+ return data;
421
+ } else {
422
+ console.error("\u274C [UniversalExportService] \u6570\u636E\u6E90\u4E0D\u662F\u51FD\u6570:", typeof request.dataSource);
423
+ throw new chunkGCVOKQZP_js.ExportDataError("\u6682\u4E0D\u652F\u6301\u5B57\u7B26\u4E32\u6570\u636E\u6E90");
424
+ }
425
+ } catch (error) {
426
+ console.error("\u274C [UniversalExportService] \u83B7\u53D6\u6570\u636E\u5931\u8D25:", error);
427
+ throw new chunkGCVOKQZP_js.ExportDataError(
428
+ `\u83B7\u53D6\u6570\u636E\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
429
+ { originalError: error }
430
+ );
431
+ }
432
+ }
433
+ /**
434
+ * 处理数据
435
+ */
436
+ async processData(data, request, config) {
437
+ logger.info("\u{1F504} [UniversalExportService] processData \u5F00\u59CB\u6267\u884C:", {
438
+ dataLength: data.length,
439
+ hasFilters: !!(request.filters && request.filters.length > 0),
440
+ hasSortBy: !!(request.sortBy && request.sortBy.length > 0),
441
+ hasPagination: !!request.pagination,
442
+ hasGrouping: !!(config.grouping && config.grouping.enabled),
443
+ maxRows: config.maxRows
444
+ });
445
+ logger.info("\u{1F50D} [UniversalExportService] \u8BE6\u7EC6\u5206\u7EC4\u914D\u7F6E\u68C0\u67E5:", {
446
+ configGrouping: config.grouping,
447
+ groupingExists: !!config.grouping,
448
+ groupingEnabled: config.grouping?.enabled,
449
+ groupingFields: config.grouping?.fields,
450
+ groupingFieldsLength: config.grouping?.fields?.length
451
+ });
452
+ let processedData = [...data];
453
+ if (request.filters && request.filters.length > 0) {
454
+ logger.info("\u{1F50D} [UniversalExportService] \u5E94\u7528\u8FC7\u6EE4\u5668...");
455
+ processedData = this.applyFilters(processedData, request.filters);
456
+ logger.info("\u2705 [UniversalExportService] \u8FC7\u6EE4\u5668\u5E94\u7528\u5B8C\u6210:", {
457
+ beforeLength: data.length,
458
+ afterLength: processedData.length
459
+ });
460
+ }
461
+ if (request.sortBy && request.sortBy.length > 0) {
462
+ logger.info("\u{1F4CA} [UniversalExportService] \u5E94\u7528\u6392\u5E8F...");
463
+ processedData = this.applySorting(processedData, request.sortBy);
464
+ logger.info("\u2705 [UniversalExportService] \u6392\u5E8F\u5E94\u7528\u5B8C\u6210");
465
+ }
466
+ if (config.grouping && config.grouping.enabled) {
467
+ logger.info("\u{1F4CA} [UniversalExportService] \u5E94\u7528\u5206\u7EC4...");
468
+ processedData = this.applyGrouping(processedData, config.grouping);
469
+ logger.info("\u2705 [UniversalExportService] \u5206\u7EC4\u5E94\u7528\u5B8C\u6210:", {
470
+ groupsCount: this.countGroups(processedData),
471
+ resultLength: processedData.length
472
+ });
473
+ }
474
+ if (request.pagination) {
475
+ logger.info("\u{1F4C4} [UniversalExportService] \u5E94\u7528\u5206\u9875...");
476
+ const { page, pageSize } = request.pagination;
477
+ const start = (page - 1) * pageSize;
478
+ const end = start + pageSize;
479
+ processedData = processedData.slice(start, end);
480
+ logger.info("\u2705 [UniversalExportService] \u5206\u9875\u5E94\u7528\u5B8C\u6210:", {
481
+ page,
482
+ pageSize,
483
+ start,
484
+ end,
485
+ resultLength: processedData.length
486
+ });
487
+ }
488
+ if (config.maxRows && processedData.length > config.maxRows) {
489
+ logger.info("\u{1F4CF} [UniversalExportService] \u5E94\u7528\u884C\u6570\u9650\u5236...");
490
+ processedData = processedData.slice(0, config.maxRows);
491
+ logger.info("\u2705 [UniversalExportService] \u884C\u6570\u9650\u5236\u5E94\u7528\u5B8C\u6210:", {
492
+ maxRows: config.maxRows,
493
+ resultLength: processedData.length
494
+ });
495
+ }
496
+ logger.info("\u2705 [UniversalExportService] processData \u6267\u884C\u5B8C\u6210:", {
497
+ originalLength: data.length,
498
+ finalLength: processedData.length
499
+ });
500
+ return processedData;
501
+ }
502
+ /**
503
+ * 应用过滤器
504
+ */
505
+ applyFilters(data, filters) {
506
+ return data.filter((item) => {
507
+ return filters.every((filter) => {
508
+ const value = this.getNestedValue(item, filter.field);
509
+ switch (filter.operator) {
510
+ case "eq":
511
+ return value === filter.value;
512
+ case "ne":
513
+ return value !== filter.value;
514
+ case "gt":
515
+ return value > filter.value;
516
+ case "gte":
517
+ return value >= filter.value;
518
+ case "lt":
519
+ return value < filter.value;
520
+ case "lte":
521
+ return value <= filter.value;
522
+ case "contains":
523
+ return String(value).includes(String(filter.value));
524
+ case "startsWith":
525
+ return String(value).startsWith(String(filter.value));
526
+ case "endsWith":
527
+ return String(value).endsWith(String(filter.value));
528
+ case "in":
529
+ return Array.isArray(filter.value) && filter.value.includes(value);
530
+ case "notIn":
531
+ return Array.isArray(filter.value) && !filter.value.includes(value);
532
+ default:
533
+ return true;
534
+ }
535
+ });
536
+ });
537
+ }
538
+ /**
539
+ * 应用排序
540
+ */
541
+ applySorting(data, sortBy) {
542
+ return data.sort((a, b) => {
543
+ for (const sort of sortBy) {
544
+ const aValue = this.getNestedValue(a, sort.field);
545
+ const bValue = this.getNestedValue(b, sort.field);
546
+ if (aValue < bValue) {
547
+ return sort.direction === "asc" ? -1 : 1;
548
+ }
549
+ if (aValue > bValue) {
550
+ return sort.direction === "asc" ? 1 : -1;
551
+ }
552
+ }
553
+ return 0;
554
+ });
555
+ }
556
+ /**
557
+ * 获取嵌套值
558
+ */
559
+ getNestedValue(obj, path) {
560
+ return path.split(".").reduce((current, key) => {
561
+ return current && current[key] !== void 0 ? current[key] : null;
562
+ }, obj);
563
+ }
564
+ /**
565
+ * 过滤掉所有行都为空值的字段
566
+ */
567
+ filterEmptyFields(data, fields) {
568
+ const filteredFields = fields.filter((field) => {
569
+ const forceKeepFields = ["pickupMethod", "notes", "adminNotes"];
570
+ if (forceKeepFields.includes(field.key)) {
571
+ logger.info(`\u{1F527} [UniversalExportService] \u5F3A\u5236\u4FDD\u7559\u5B57\u6BB5 "${field.key}" (${field.label})`);
572
+ return true;
573
+ }
574
+ const hasValue = data.some((item) => {
575
+ const value = this.getNestedValue(item, field.key);
576
+ return value !== null && value !== void 0 && value !== "";
577
+ });
578
+ if (!hasValue) {
579
+ logger.info(
580
+ `\u{1F50D} [UniversalExportService] \u5B57\u6BB5 "${field.key}" (${field.label}) \u88AB\u8FC7\u6EE4\u6389 - \u6240\u6709\u884C\u90FD\u4E3A\u7A7A\u503C`
581
+ );
582
+ }
583
+ return hasValue;
584
+ });
585
+ logger.info("\u{1F4CA} [UniversalExportService] \u5B57\u6BB5\u8FC7\u6EE4\u7ED3\u679C:", {
586
+ \u539F\u59CB\u5B57\u6BB5\u6570: fields.length,
587
+ \u8FC7\u6EE4\u540E\u5B57\u6BB5\u6570: filteredFields.length,
588
+ \u88AB\u8FC7\u6EE4\u7684\u5B57\u6BB5: fields.filter((f) => !filteredFields.includes(f)).map((f) => f.key),
589
+ \u4FDD\u7559\u7684\u5B57\u6BB5: filteredFields.map((f) => f.key)
590
+ });
591
+ return filteredFields;
592
+ }
593
+ /**
594
+ * 生成文件
595
+ */
596
+ async generateFile(data, config, request, exportId) {
597
+ const startTime = /* @__PURE__ */ new Date();
598
+ const enabledFields = config.fields.filter((f) => f.enabled);
599
+ logger.info("\u{1F4C4} [UniversalExportService] generateFile \u5F00\u59CB\u6267\u884C:", {
600
+ dataLength: data.length,
601
+ enabledFieldsCount: enabledFields.length,
602
+ format: config.format,
603
+ enabledFields: enabledFields.map((f) => ({ key: f.key, label: f.label }))
604
+ });
605
+ try {
606
+ let content;
607
+ let fileName;
608
+ switch (config.format) {
609
+ case "csv":
610
+ logger.info("\u{1F4CA} [UniversalExportService] \u751F\u6210CSV\u683C\u5F0F...");
611
+ content = this.generateCSV(data, enabledFields, config);
612
+ fileName = this.generateFileName(
613
+ request.customFileName || config.fileNameTemplate,
614
+ "csv"
615
+ );
616
+ logger.info("\u2705 [UniversalExportService] CSV\u751F\u6210\u5B8C\u6210:", {
617
+ contentLength: content.length,
618
+ fileName
619
+ });
620
+ break;
621
+ case "excel":
622
+ logger.info("\u{1F4CA} [UniversalExportService] \u751F\u6210Excel\u683C\u5F0F...");
623
+ const excelBuffer = this.generateExcel(data, enabledFields, config);
624
+ fileName = this.generateFileName(
625
+ request.customFileName || config.fileNameTemplate,
626
+ "xlsx"
627
+ );
628
+ logger.info("\u2705 [UniversalExportService] Excel\u751F\u6210\u5B8C\u6210:", {
629
+ bufferLength: excelBuffer.byteLength,
630
+ fileName
631
+ });
632
+ const excelBlob = new Blob([excelBuffer], { type: this.getMimeType(config.format) });
633
+ const endTime2 = /* @__PURE__ */ new Date();
634
+ const duration2 = endTime2.getTime() - startTime.getTime();
635
+ return {
636
+ exportId,
637
+ fileName,
638
+ fileSize: excelBlob.size,
639
+ fileBlob: excelBlob,
640
+ exportedRows: data.length,
641
+ startTime,
642
+ endTime: endTime2,
643
+ duration: duration2,
644
+ statistics: {
645
+ totalRows: data.length,
646
+ filteredRows: data.length,
647
+ exportedRows: data.length,
648
+ skippedRows: 0
649
+ }
650
+ };
651
+ case "json":
652
+ logger.info("\u{1F4C4} [UniversalExportService] \u751F\u6210JSON\u683C\u5F0F...");
653
+ content = this.generateJSON(data, enabledFields);
654
+ fileName = this.generateFileName(
655
+ request.customFileName || config.fileNameTemplate,
656
+ "json"
657
+ );
658
+ logger.info("\u2705 [UniversalExportService] JSON\u751F\u6210\u5B8C\u6210:", {
659
+ contentLength: content.length,
660
+ fileName
661
+ });
662
+ break;
663
+ default:
664
+ console.error("\u274C [UniversalExportService] \u4E0D\u652F\u6301\u7684\u683C\u5F0F:", config.format);
665
+ throw new chunkGCVOKQZP_js.ExportFileError(`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u683C\u5F0F: ${config.format}`);
666
+ }
667
+ const blob = new Blob([content], { type: this.getMimeType(config.format) });
668
+ if (blob.size > this.config.maxFileSize) {
669
+ throw new chunkGCVOKQZP_js.ExportFileError(`\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC7\u9650\u5236: ${blob.size} > ${this.config.maxFileSize}`);
670
+ }
671
+ const endTime = /* @__PURE__ */ new Date();
672
+ const duration = endTime.getTime() - startTime.getTime();
673
+ return {
674
+ exportId,
675
+ fileName,
676
+ fileSize: blob.size,
677
+ fileBlob: blob,
678
+ exportedRows: data.length,
679
+ startTime,
680
+ endTime,
681
+ duration,
682
+ statistics: {
683
+ totalRows: data.length,
684
+ filteredRows: data.length,
685
+ exportedRows: data.length,
686
+ skippedRows: 0
687
+ }
688
+ };
689
+ } catch (error) {
690
+ throw new chunkGCVOKQZP_js.ExportFileError(
691
+ `\u751F\u6210\u6587\u4EF6\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
692
+ { originalError: error }
693
+ );
694
+ }
695
+ }
696
+ /**
697
+ * 生成CSV内容
698
+ */
699
+ generateCSV(data, fields, config) {
700
+ logger.info("\u{1F4CA} [UniversalExportService] generateCSV \u5F00\u59CB\u6267\u884C:", {
701
+ dataLength: data.length,
702
+ fieldsCount: fields.length,
703
+ includeHeader: config.includeHeader,
704
+ delimiter: config.delimiter,
705
+ addBOM: config.addBOM
706
+ });
707
+ const lines = [];
708
+ if (config.addBOM) {
709
+ lines.push("\uFEFF");
710
+ logger.info("\u{1F4DD} [UniversalExportService] \u6DFB\u52A0BOM");
711
+ }
712
+ const nonEmptyFields = this.filterEmptyFields(data, fields);
713
+ logger.info("\u{1F4CA} [UniversalExportService] \u8FC7\u6EE4\u7A7A\u5B57\u6BB5:", {
714
+ originalFieldsCount: fields.length,
715
+ nonEmptyFieldsCount: nonEmptyFields.length,
716
+ removedFields: fields.filter((f) => !nonEmptyFields.includes(f)).map((f) => f.key)
717
+ });
718
+ if (config.includeHeader) {
719
+ const headers = nonEmptyFields.map((f) => this.escapeCSVField(f.label));
720
+ lines.push(headers.join(config.delimiter));
721
+ logger.info("\u{1F4CB} [UniversalExportService] \u6DFB\u52A0\u8868\u5934:", headers);
722
+ }
723
+ logger.info("\u{1F4CA} [UniversalExportService] \u5F00\u59CB\u5904\u7406\u6570\u636E\u884C...");
724
+ for (let i = 0; i < data.length; i++) {
725
+ const item = data[i];
726
+ if (i === 0) {
727
+ logger.info("\u{1F4CA} [UniversalExportService] \u7B2C\u4E00\u884C\u6570\u636E\u793A\u4F8B:", item);
728
+ }
729
+ const row = nonEmptyFields.map((field) => {
730
+ if (item.__isGroupHeader) {
731
+ return this.escapeCSVField(item[field.key] || "");
732
+ }
733
+ let value = this.getNestedValue(item, field.key);
734
+ if (field.formatter) {
735
+ value = field.formatter(value);
736
+ } else if (DEFAULT_FORMATTERS[field.type]) {
737
+ value = DEFAULT_FORMATTERS[field.type](value);
738
+ } else {
739
+ value = String(value || "");
740
+ }
741
+ return this.escapeCSVField(value);
742
+ });
743
+ lines.push(row.join(config.delimiter));
744
+ if (i === 0) {
745
+ logger.info("\u{1F4CA} [UniversalExportService] \u7B2C\u4E00\u884C\u5904\u7406\u7ED3\u679C:", row);
746
+ }
747
+ }
748
+ const result = lines.join("\n");
749
+ logger.info("\u2705 [UniversalExportService] CSV\u751F\u6210\u5B8C\u6210:", {
750
+ totalLines: lines.length,
751
+ resultLength: result.length
752
+ });
753
+ return result;
754
+ }
755
+ /**
756
+ * 生成JSON内容
757
+ */
758
+ generateJSON(data, fields) {
759
+ const processedData = data.map((item) => {
760
+ const processed = {};
761
+ for (const field of fields) {
762
+ let value = this.getNestedValue(item, field.key);
763
+ if (field.formatter) {
764
+ value = field.formatter(value);
765
+ } else if (DEFAULT_FORMATTERS[field.type]) {
766
+ value = DEFAULT_FORMATTERS[field.type](value);
767
+ }
768
+ processed[field.key] = value;
769
+ }
770
+ return processed;
771
+ });
772
+ return JSON.stringify(processedData, null, 2);
773
+ }
774
+ /**
775
+ * 转义CSV字段
776
+ */
777
+ escapeCSVField(value) {
778
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
779
+ return `"${value.replace(/"/g, '""')}"`;
780
+ }
781
+ return value;
782
+ }
783
+ /**
784
+ * 生成文件名
785
+ */
786
+ generateFileName(template, extension) {
787
+ const now = /* @__PURE__ */ new Date();
788
+ const dateStr = now.toISOString().split("T")[0];
789
+ const timeStr = now.toTimeString().split(" ")[0].replace(/:/g, "-");
790
+ return template.replace("{date}", dateStr).replace("{time}", timeStr).replace("{timestamp}", now.getTime().toString()) + `.${extension}`;
791
+ }
792
+ /**
793
+ * 获取MIME类型
794
+ */
795
+ getMimeType(format) {
796
+ switch (format) {
797
+ case "csv":
798
+ return "text/csv; charset=utf-8";
799
+ case "json":
800
+ return "application/json; charset=utf-8";
801
+ case "excel":
802
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
803
+ default:
804
+ return "application/octet-stream";
805
+ }
806
+ }
807
+ /**
808
+ * 触发事件
809
+ */
810
+ emitEvent(event) {
811
+ const listeners = this.eventListeners.get(event.type);
812
+ if (listeners) {
813
+ listeners.forEach((listener) => {
814
+ try {
815
+ listener(event);
816
+ } catch (error) {
817
+ console.error("\u4E8B\u4EF6\u76D1\u542C\u5668\u6267\u884C\u5931\u8D25:", error);
818
+ }
819
+ });
820
+ }
821
+ }
822
+ // ============= 分组相关方法 =============
823
+ /**
824
+ * 应用分组
825
+ */
826
+ applyGrouping(data, groupingConfig) {
827
+ logger.info("\u{1F4CA} [UniversalExportService] applyGrouping \u5F00\u59CB\u6267\u884C:", {
828
+ dataLength: data.length,
829
+ groupingFields: groupingConfig.fields.map((f) => f.key),
830
+ preserveOrder: groupingConfig.preserveOrder
831
+ });
832
+ if (!groupingConfig.fields || groupingConfig.fields.length === 0) {
833
+ return data;
834
+ }
835
+ const grouped = this.groupDataByFields(data, groupingConfig);
836
+ const result = this.processGroupedData(grouped, groupingConfig);
837
+ logger.info("\u2705 [UniversalExportService] applyGrouping \u6267\u884C\u5B8C\u6210:", {
838
+ originalLength: data.length,
839
+ groupedLength: result.length
840
+ });
841
+ return result;
842
+ }
843
+ /**
844
+ * 按字段分组数据
845
+ */
846
+ groupDataByFields(data, groupingConfig) {
847
+ const groups = /* @__PURE__ */ new Map();
848
+ for (const item of data) {
849
+ const groupKey = this.generateGroupKey(item, groupingConfig.fields);
850
+ if (!groups.has(groupKey)) {
851
+ groups.set(groupKey, []);
852
+ }
853
+ groups.get(groupKey).push(item);
854
+ }
855
+ return groups;
856
+ }
857
+ /**
858
+ * 生成分组键
859
+ */
860
+ generateGroupKey(item, groupingFields) {
861
+ const keyParts = groupingFields.map((field) => {
862
+ const value = this.getNestedValue(item, field.key);
863
+ if (value === null || value === void 0 || value === "") {
864
+ return "__NULL__";
865
+ }
866
+ return String(value);
867
+ });
868
+ return keyParts.join("|");
869
+ }
870
+ /**
871
+ * 处理分组后的数据
872
+ */
873
+ processGroupedData(groups, groupingConfig) {
874
+ const result = [];
875
+ for (const [groupKey, groupItems] of groups) {
876
+ if (groupItems.length === 0) continue;
877
+ const groupValues = groupKey.split("|");
878
+ const processedGroup = this.processGroup(groupItems, groupingConfig, groupValues);
879
+ result.push(...processedGroup);
880
+ }
881
+ return result;
882
+ }
883
+ /**
884
+ * 处理单个分组
885
+ */
886
+ processGroup(groupItems, groupingConfig, groupValues) {
887
+ const result = [];
888
+ const showGroupHeader = groupingConfig.fields.some((f) => f.showGroupHeader);
889
+ if (showGroupHeader) {
890
+ const groupHeader = this.createGroupHeader(groupValues, groupingConfig.fields);
891
+ result.push(groupHeader);
892
+ }
893
+ if (groupingConfig.fields.length > 1) {
894
+ result.push(...this.processMultiFieldMergeMode(groupItems, groupingConfig.fields));
895
+ } else {
896
+ const primaryGroupField = groupingConfig.fields[0];
897
+ switch (primaryGroupField.mode) {
898
+ case "merge":
899
+ result.push(...this.processMergeMode(groupItems, primaryGroupField));
900
+ break;
901
+ case "separate":
902
+ result.push(...groupItems);
903
+ break;
904
+ case "nested":
905
+ result.push(...this.processNestedMode(groupItems, groupingConfig));
906
+ break;
907
+ default:
908
+ result.push(...groupItems);
909
+ }
910
+ }
911
+ return result;
912
+ }
913
+ /**
914
+ * 创建分组头行
915
+ */
916
+ createGroupHeader(groupValues, groupingFields) {
917
+ const header = { __isGroupHeader: true };
918
+ groupingFields.forEach((field, index) => {
919
+ const value = groupValues[index] === "__NULL__" ? "" : groupValues[index];
920
+ const template = field.groupHeaderTemplate || `${field.label}: {value}`;
921
+ header[field.key] = template.replace("{value}", value);
922
+ });
923
+ return header;
924
+ }
925
+ /**
926
+ * 处理合并模式
927
+ */
928
+ processMergeMode(groupItems, groupField) {
929
+ if (groupItems.length === 0) return [];
930
+ const result = [];
931
+ const firstItem = { ...groupItems[0] };
932
+ firstItem.__groupSize = groupItems.length;
933
+ firstItem.__isGroupFirst = true;
934
+ result.push(firstItem);
935
+ for (let i = 1; i < groupItems.length; i++) {
936
+ const item = { ...groupItems[i] };
937
+ item[groupField.key] = "";
938
+ item.__isGroupChild = true;
939
+ item.__groupIndex = i;
940
+ result.push(item);
941
+ }
942
+ return result;
943
+ }
944
+ /**
945
+ * 处理多字段合并模式
946
+ */
947
+ processMultiFieldMergeMode(groupItems, groupFields) {
948
+ if (groupItems.length === 0) return [];
949
+ const result = [];
950
+ const firstItem = { ...groupItems[0] };
951
+ firstItem.__groupSize = groupItems.length;
952
+ firstItem.__isGroupFirst = true;
953
+ groupFields.forEach((field) => {
954
+ firstItem[`__${field.key}_groupSize`] = groupItems.length;
955
+ firstItem[`__${field.key}_isGroupFirst`] = true;
956
+ });
957
+ result.push(firstItem);
958
+ logger.info("\u{1F517} [UniversalExportService] \u5904\u7406\u591A\u5B57\u6BB5\u5408\u5E76\u6A21\u5F0F:", {
959
+ groupItemsLength: groupItems.length,
960
+ groupFields: groupFields.map((f) => f.key),
961
+ firstItem
962
+ });
963
+ for (let i = 1; i < groupItems.length; i++) {
964
+ const item = { ...groupItems[i] };
965
+ groupFields.forEach((field) => {
966
+ item[field.key] = "";
967
+ });
968
+ item.__isGroupChild = true;
969
+ item.__groupIndex = i;
970
+ result.push(item);
971
+ }
972
+ return result;
973
+ }
974
+ /**
975
+ * 处理嵌套模式
976
+ */
977
+ processNestedMode(groupItems, groupingConfig) {
978
+ if (groupingConfig.fields.length === 1) {
979
+ return this.processMergeMode(groupItems, groupingConfig.fields[0]);
980
+ }
981
+ const subGroupingConfig = {
982
+ ...groupingConfig,
983
+ fields: groupingConfig.fields.slice(1)
984
+ };
985
+ return this.applyGrouping(groupItems, subGroupingConfig);
986
+ }
987
+ /**
988
+ * 统计分组数量
989
+ */
990
+ countGroups(data) {
991
+ const groupHeaders = data.filter((item) => item.__isGroupHeader);
992
+ const groupFirsts = data.filter((item) => item.__isGroupFirst);
993
+ return Math.max(groupHeaders.length, groupFirsts.length);
994
+ }
995
+ /**
996
+ * 生成Excel文件
997
+ */
998
+ generateExcel(data, fields, config) {
999
+ logger.info("\u{1F4CA} [UniversalExportService] generateExcel \u5F00\u59CB\u6267\u884C:", {
1000
+ dataLength: data.length,
1001
+ fieldsCount: fields.length,
1002
+ hasGrouping: !!(config.grouping && config.grouping.enabled)
1003
+ });
1004
+ const workbook = XLSX__namespace.utils.book_new();
1005
+ const nonEmptyFields = this.filterEmptyFields(data, fields);
1006
+ const worksheetData = this.prepareExcelData(data, nonEmptyFields, config);
1007
+ const worksheet = XLSX__namespace.utils.aoa_to_sheet(worksheetData);
1008
+ if (config.grouping && config.grouping.enabled) {
1009
+ this.applyExcelGrouping(
1010
+ worksheet,
1011
+ data,
1012
+ nonEmptyFields,
1013
+ config.grouping,
1014
+ config.includeHeader
1015
+ );
1016
+ }
1017
+ this.setExcelColumnWidths(worksheet, nonEmptyFields);
1018
+ this.applyExcelDataStyles(worksheet, config.includeHeader);
1019
+ XLSX__namespace.utils.book_append_sheet(workbook, worksheet, "Sheet1");
1020
+ const excelBuffer = XLSX__namespace.write(workbook, {
1021
+ bookType: "xlsx",
1022
+ type: "array",
1023
+ cellStyles: true
1024
+ });
1025
+ logger.info("\u2705 [UniversalExportService] generateExcel \u6267\u884C\u5B8C\u6210");
1026
+ return excelBuffer;
1027
+ }
1028
+ /**
1029
+ * 准备Excel数据
1030
+ */
1031
+ prepareExcelData(data, fields, config) {
1032
+ const result = [];
1033
+ logger.info("\u{1F4CA} [UniversalExportService] \u51C6\u5907Excel\u6570\u636E:", {
1034
+ dataLength: data.length,
1035
+ fieldsCount: fields.length,
1036
+ includeHeader: config.includeHeader,
1037
+ hasGrouping: !!(config.grouping && config.grouping.enabled)
1038
+ });
1039
+ if (config.includeHeader) {
1040
+ const headers = fields.map((field) => field.label);
1041
+ result.push(headers);
1042
+ logger.info("\u{1F4CB} [UniversalExportService] \u6DFB\u52A0\u8868\u5934:", headers);
1043
+ }
1044
+ for (let i = 0; i < data.length; i++) {
1045
+ const item = data[i];
1046
+ const row = fields.map((field) => {
1047
+ if (item.__isGroupHeader) {
1048
+ return item[field.key] || "";
1049
+ }
1050
+ let value = this.getNestedValue(item, field.key);
1051
+ if (field.formatter) {
1052
+ value = field.formatter(value);
1053
+ } else if (DEFAULT_FORMATTERS[field.type]) {
1054
+ value = DEFAULT_FORMATTERS[field.type](value);
1055
+ } else {
1056
+ value = String(value || "");
1057
+ }
1058
+ return value;
1059
+ });
1060
+ result.push(row);
1061
+ if (i === 0) {
1062
+ logger.info("\u{1F4CA} [UniversalExportService] \u7B2C\u4E00\u884C\u6570\u636E\u793A\u4F8B:", row);
1063
+ }
1064
+ }
1065
+ logger.info("\u2705 [UniversalExportService] Excel\u6570\u636E\u51C6\u5907\u5B8C\u6210:", {
1066
+ totalRows: result.length,
1067
+ headerRows: config.includeHeader ? 1 : 0,
1068
+ dataRows: result.length - (config.includeHeader ? 1 : 0)
1069
+ });
1070
+ return result;
1071
+ }
1072
+ /**
1073
+ * 应用Excel分组和合并单元格
1074
+ */
1075
+ applyExcelGrouping(worksheet, data, fields, groupingConfig, includeHeader = true) {
1076
+ if (!worksheet["!merges"]) {
1077
+ worksheet["!merges"] = [];
1078
+ }
1079
+ const headerOffset = includeHeader ? 1 : 0;
1080
+ let currentRow = headerOffset;
1081
+ logger.info("\u{1F4CA} [UniversalExportService] \u5F00\u59CB\u5904\u7406Excel\u5206\u7EC4\u548C\u5408\u5E76\u5355\u5143\u683C:", {
1082
+ dataLength: data.length,
1083
+ headerOffset,
1084
+ groupingFields: groupingConfig.fields.map((f) => ({ key: f.key, mergeCells: f.mergeCells }))
1085
+ });
1086
+ for (let i = 0; i < data.length; i++) {
1087
+ const item = data[i];
1088
+ if (item.__isGroupFirst && item.__groupSize > 1) {
1089
+ logger.info("\u{1F517} [UniversalExportService] \u5904\u7406\u5206\u7EC4\u5408\u5E76:", {
1090
+ row: currentRow,
1091
+ groupSize: item.__groupSize,
1092
+ item
1093
+ });
1094
+ groupingConfig.fields.forEach((groupField) => {
1095
+ if (groupField.mergeCells) {
1096
+ const fieldIndex = fields.findIndex((f) => f.key === groupField.key);
1097
+ if (fieldIndex >= 0) {
1098
+ const groupSize = item[`__${groupField.key}_groupSize`] || item.__groupSize;
1099
+ const mergeRange = {
1100
+ s: { r: currentRow, c: fieldIndex },
1101
+ // 开始行列
1102
+ e: { r: currentRow + groupSize - 1, c: fieldIndex }
1103
+ // 结束行列
1104
+ };
1105
+ logger.info("\u{1F4CA} [UniversalExportService] \u6DFB\u52A0\u5408\u5E76\u533A\u57DF:", {
1106
+ field: groupField.key,
1107
+ fieldIndex,
1108
+ groupSize,
1109
+ mergeRange
1110
+ });
1111
+ worksheet["!merges"].push(mergeRange);
1112
+ const startCellAddress = XLSX__namespace.utils.encode_cell(mergeRange.s);
1113
+ if (worksheet[startCellAddress]) {
1114
+ worksheet[startCellAddress].s = {
1115
+ ...worksheet[startCellAddress].s,
1116
+ alignment: { horizontal: "center", vertical: "middle" },
1117
+ fill: { fgColor: { rgb: "F2F2F2" } },
1118
+ border: {
1119
+ top: { style: "thin", color: { rgb: "000000" } },
1120
+ bottom: { style: "thin", color: { rgb: "000000" } },
1121
+ left: { style: "thin", color: { rgb: "000000" } },
1122
+ right: { style: "thin", color: { rgb: "000000" } }
1123
+ }
1124
+ };
1125
+ }
1126
+ }
1127
+ }
1128
+ });
1129
+ }
1130
+ currentRow++;
1131
+ }
1132
+ logger.info("\u2705 [UniversalExportService] Excel\u5206\u7EC4\u548C\u5408\u5E76\u5355\u5143\u683C\u5904\u7406\u5B8C\u6210:", {
1133
+ totalMerges: worksheet["!merges"]?.length || 0
1134
+ });
1135
+ }
1136
+ /**
1137
+ * 设置Excel列宽和样式
1138
+ */
1139
+ setExcelColumnWidths(worksheet, fields) {
1140
+ const colWidths = fields.map((field) => ({
1141
+ wch: field.width || 15
1142
+ // 默认宽度15字符
1143
+ }));
1144
+ worksheet["!cols"] = colWidths;
1145
+ if (worksheet["!ref"]) {
1146
+ const range = XLSX__namespace.utils.decode_range(worksheet["!ref"]);
1147
+ for (let col = range.s.c; col <= range.e.c; col++) {
1148
+ const cellAddress = XLSX__namespace.utils.encode_cell({ r: 0, c: col });
1149
+ if (worksheet[cellAddress]) {
1150
+ worksheet[cellAddress].s = {
1151
+ font: { bold: true, color: { rgb: "FFFFFF" } },
1152
+ fill: { fgColor: { rgb: "4472C4" } },
1153
+ alignment: { horizontal: "center", vertical: "center" },
1154
+ border: {
1155
+ top: { style: "thin", color: { rgb: "000000" } },
1156
+ bottom: { style: "thin", color: { rgb: "000000" } },
1157
+ left: { style: "thin", color: { rgb: "000000" } },
1158
+ right: { style: "thin", color: { rgb: "000000" } }
1159
+ }
1160
+ };
1161
+ }
1162
+ }
1163
+ }
1164
+ logger.info("\u2705 [UniversalExportService] Excel\u5217\u5BBD\u548C\u6837\u5F0F\u8BBE\u7F6E\u5B8C\u6210:", {
1165
+ columnsCount: colWidths.length,
1166
+ columnWidths: colWidths.map((col, index) => ({ field: fields[index]?.key, width: col.wch }))
1167
+ });
1168
+ }
1169
+ /**
1170
+ * 为Excel数据单元格应用样式
1171
+ */
1172
+ applyExcelDataStyles(worksheet, includeHeader = true) {
1173
+ if (!worksheet["!ref"]) return;
1174
+ const range = XLSX__namespace.utils.decode_range(worksheet["!ref"]);
1175
+ const startRow = includeHeader ? 1 : 0;
1176
+ logger.info("\u{1F3A8} [UniversalExportService] \u5F00\u59CB\u5E94\u7528Excel\u6570\u636E\u6837\u5F0F:", {
1177
+ totalRows: range.e.r + 1,
1178
+ totalCols: range.e.c + 1,
1179
+ startRow
1180
+ });
1181
+ for (let row = startRow; row <= range.e.r; row++) {
1182
+ for (let col = range.s.c; col <= range.e.c; col++) {
1183
+ const cellAddress = XLSX__namespace.utils.encode_cell({ r: row, c: col });
1184
+ if (worksheet[cellAddress]) {
1185
+ const existingStyle = worksheet[cellAddress].s || {};
1186
+ worksheet[cellAddress].s = {
1187
+ ...existingStyle,
1188
+ border: {
1189
+ top: { style: "thin", color: { rgb: "CCCCCC" } },
1190
+ bottom: { style: "thin", color: { rgb: "CCCCCC" } },
1191
+ left: { style: "thin", color: { rgb: "CCCCCC" } },
1192
+ right: { style: "thin", color: { rgb: "CCCCCC" } }
1193
+ },
1194
+ alignment: {
1195
+ ...existingStyle.alignment,
1196
+ vertical: "center"
1197
+ }
1198
+ };
1199
+ }
1200
+ }
1201
+ }
1202
+ logger.info("\u2705 [UniversalExportService] Excel\u6570\u636E\u6837\u5F0F\u5E94\u7528\u5B8C\u6210");
1203
+ }
1204
+ };
4
1205
 
5
1206
  // src/universalExport/server/factory.ts
6
1207
  function createExportServiceConfig(options = {}) {
@@ -224,9 +1425,9 @@ function escapeCsvValue(value) {
224
1425
  return value;
225
1426
  }
226
1427
  function generateUniqueFilename(prefix, extension) {
227
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5);
1428
+ const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5);
228
1429
  const random = Math.random().toString(36).substring(2, 8);
229
- return `${prefix}_${timestamp}_${random}.${extension}`;
1430
+ return `${prefix}_${timestamp2}_${random}.${extension}`;
230
1431
  }
231
1432
  function calculateProgress(processed, total) {
232
1433
  if (total === 0) return 0;
@@ -238,11 +1439,186 @@ function estimateRemainingTime(processed, total, elapsedMs) {
238
1439
  const remainingRows = total - processed;
239
1440
  return Math.round(remainingRows * avgTimePerRow / 1e3);
240
1441
  }
1442
+ var exportConfigs = pgCore.pgTable("ExportConfig", {
1443
+ /** 主键ID */
1444
+ id: pgCore.text("id").primaryKey().$defaultFn(() => `export_config_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`),
1445
+ /** 配置名称 */
1446
+ name: pgCore.text("name").notNull(),
1447
+ /** 配置描述 */
1448
+ description: pgCore.text("description"),
1449
+ /** 导出格式:'csv' | 'excel' | 'json' */
1450
+ format: pgCore.text("format").notNull(),
1451
+ /** 导出字段配置(JSON数组) */
1452
+ fields: pgCore.jsonb("fields").notNull(),
1453
+ // ExportField[]
1454
+ /** 分组配置 */
1455
+ grouping: pgCore.jsonb("grouping"),
1456
+ // GroupingConfig
1457
+ /** 文件名模板 */
1458
+ fileNameTemplate: pgCore.text("fileNameTemplate").notNull(),
1459
+ /** 是否包含表头 */
1460
+ includeHeader: pgCore.boolean("includeHeader").default(true).notNull(),
1461
+ /** 分隔符(用于CSV格式) */
1462
+ delimiter: pgCore.text("delimiter").default(",").notNull(),
1463
+ /** 编码格式 */
1464
+ encoding: pgCore.text("encoding").default("utf-8").notNull(),
1465
+ /** 是否添加BOM(用于Excel打开UTF-8 CSV) */
1466
+ addBOM: pgCore.boolean("addBOM").default(true).notNull(),
1467
+ /** 最大导出行数 */
1468
+ maxRows: pgCore.integer("maxRows"),
1469
+ /** 模块ID */
1470
+ moduleId: pgCore.text("moduleId").notNull(),
1471
+ /** 业务ID */
1472
+ businessId: pgCore.text("businessId"),
1473
+ /** 创建者ID */
1474
+ createdBy: pgCore.text("createdBy"),
1475
+ /** 创建时间 */
1476
+ createdAt: pgCore.timestamp("createdAt", { precision: 3, mode: "date" }).default(drizzleOrm.sql`CURRENT_TIMESTAMP`).notNull(),
1477
+ /** 更新时间 */
1478
+ updatedAt: pgCore.timestamp("updatedAt", { precision: 3, mode: "date" }).default(drizzleOrm.sql`CURRENT_TIMESTAMP`).notNull()
1479
+ });
1480
+ var exportHistory = pgCore.pgTable("ExportHistory", {
1481
+ /** 主键ID */
1482
+ id: pgCore.text("id").primaryKey().$defaultFn(() => `export_history_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`),
1483
+ /** 关联的配置ID */
1484
+ configId: pgCore.text("configId").notNull(),
1485
+ /** 导出的文件名 */
1486
+ fileName: pgCore.text("fileName").notNull(),
1487
+ /** 文件大小(字节) */
1488
+ fileSize: pgCore.integer("fileSize").notNull(),
1489
+ /** 导出的行数 */
1490
+ exportedRows: pgCore.integer("exportedRows").notNull(),
1491
+ /** 导出状态:'pending' | 'processing' | 'completed' | 'failed' */
1492
+ status: pgCore.text("status").notNull(),
1493
+ /** 错误信息(如果失败) */
1494
+ error: pgCore.text("error"),
1495
+ /** 导出耗时(毫秒) */
1496
+ duration: pgCore.integer("duration"),
1497
+ /** 开始时间 */
1498
+ startTime: pgCore.timestamp("startTime", { precision: 3, mode: "date" }).notNull(),
1499
+ /** 结束时间 */
1500
+ endTime: pgCore.timestamp("endTime", { precision: 3, mode: "date" }),
1501
+ /** 创建者ID */
1502
+ createdBy: pgCore.text("createdBy"),
1503
+ /** 创建时间 */
1504
+ createdAt: pgCore.timestamp("createdAt", { precision: 3, mode: "date" }).default(drizzleOrm.sql`CURRENT_TIMESTAMP`).notNull()
1505
+ });
1506
+ var exportConfigsRelations = drizzleOrm.relations(exportConfigs, ({ many }) => ({
1507
+ history: many(exportHistory)
1508
+ }));
1509
+ var exportHistoryRelations = drizzleOrm.relations(exportHistory, ({ one }) => ({
1510
+ config: one(exportConfigs, {
1511
+ fields: [exportHistory.configId],
1512
+ references: [exportConfigs.id]
1513
+ })
1514
+ }));
1515
+ var ExportConfigDatabaseService = class {
1516
+ constructor(db) {
1517
+ this.db = db;
1518
+ }
1519
+ /**
1520
+ * 创建导出配置
1521
+ */
1522
+ async createConfig(config) {
1523
+ const id = `export_config_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1524
+ const now = /* @__PURE__ */ new Date();
1525
+ const [newConfig] = await this.db.insert(exportConfigs).values({
1526
+ id,
1527
+ ...config,
1528
+ createdAt: now,
1529
+ updatedAt: now
1530
+ }).returning();
1531
+ return newConfig;
1532
+ }
1533
+ /**
1534
+ * 根据ID获取配置
1535
+ */
1536
+ async getConfigById(id) {
1537
+ const [config] = await this.db.select().from(exportConfigs).where(drizzleOrm.eq(exportConfigs.id, id)).limit(1);
1538
+ return config || null;
1539
+ }
1540
+ /**
1541
+ * 根据模块和业务ID获取配置列表
1542
+ */
1543
+ async getConfigsByModule(moduleId, businessId) {
1544
+ const whereConditions = businessId ? drizzleOrm.and(drizzleOrm.eq(exportConfigs.moduleId, moduleId), drizzleOrm.eq(exportConfigs.businessId, businessId)) : drizzleOrm.eq(exportConfigs.moduleId, moduleId);
1545
+ return await this.db.select().from(exportConfigs).where(whereConditions).orderBy(drizzleOrm.desc(exportConfigs.updatedAt));
1546
+ }
1547
+ /**
1548
+ * 更新配置
1549
+ */
1550
+ async updateConfig(id, updates) {
1551
+ const [updatedConfig] = await this.db.update(exportConfigs).set({
1552
+ ...updates,
1553
+ updatedAt: /* @__PURE__ */ new Date()
1554
+ }).where(drizzleOrm.eq(exportConfigs.id, id)).returning();
1555
+ return updatedConfig || null;
1556
+ }
1557
+ /**
1558
+ * 删除配置
1559
+ */
1560
+ async deleteConfig(id) {
1561
+ await this.db.delete(exportConfigs).where(drizzleOrm.eq(exportConfigs.id, id));
1562
+ return true;
1563
+ }
1564
+ /**
1565
+ * 根据用户ID获取配置列表
1566
+ */
1567
+ async getConfigsByUser(userId) {
1568
+ return await this.db.select().from(exportConfigs).where(drizzleOrm.eq(exportConfigs.createdBy, userId)).orderBy(drizzleOrm.desc(exportConfigs.updatedAt));
1569
+ }
1570
+ };
1571
+ var ExportHistoryDatabaseService = class {
1572
+ constructor(db) {
1573
+ this.db = db;
1574
+ }
1575
+ /**
1576
+ * 创建导出历史记录
1577
+ */
1578
+ async createHistory(history) {
1579
+ const id = `export_history_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1580
+ const [newHistory] = await this.db.insert(exportHistory).values({
1581
+ id,
1582
+ ...history,
1583
+ createdAt: /* @__PURE__ */ new Date()
1584
+ }).returning();
1585
+ return newHistory;
1586
+ }
1587
+ /**
1588
+ * 根据配置ID获取历史记录
1589
+ */
1590
+ async getHistoryByConfigId(configId) {
1591
+ return await this.db.select().from(exportHistory).where(drizzleOrm.eq(exportHistory.configId, configId)).orderBy(drizzleOrm.desc(exportHistory.createdAt));
1592
+ }
1593
+ /**
1594
+ * 根据用户ID获取历史记录
1595
+ */
1596
+ async getHistoryByUser(userId) {
1597
+ return await this.db.select().from(exportHistory).where(drizzleOrm.eq(exportHistory.createdBy, userId)).orderBy(drizzleOrm.desc(exportHistory.createdAt));
1598
+ }
1599
+ /**
1600
+ * 获取最近的导出历史记录
1601
+ */
1602
+ async getRecentHistory(limit = 10) {
1603
+ return await this.db.select().from(exportHistory).orderBy(drizzleOrm.desc(exportHistory.createdAt)).limit(limit);
1604
+ }
1605
+ };
1606
+ function createExportDatabaseServices(options) {
1607
+ const { db } = options;
1608
+ return {
1609
+ configDB: new ExportConfigDatabaseService(db),
1610
+ historyDB: new ExportHistoryDatabaseService(db)
1611
+ };
1612
+ }
241
1613
 
1614
+ exports.ExportConfigDatabaseService = ExportConfigDatabaseService;
242
1615
  exports.ExportConfigValidationError = ConfigValidationError;
1616
+ exports.ExportHistoryDatabaseService = ExportHistoryDatabaseService;
1617
+ exports.UniversalExportService = UniversalExportService;
243
1618
  exports.calculateProgress = calculateProgress;
244
1619
  exports.convertFieldType = convertFieldType;
245
1620
  exports.createBatchExportPreset = createBatchExportPreset;
1621
+ exports.createExportDatabaseServices = createExportDatabaseServices;
246
1622
  exports.createExportServiceConfig = createExportServiceConfig;
247
1623
  exports.createExportServiceFromEnv = createExportServiceFromEnv;
248
1624
  exports.createLargeAppPreset = createLargeAppPreset;
@@ -253,6 +1629,10 @@ exports.createSmartExportPreset = createSmartExportPreset;
253
1629
  exports.createUniversalExportService = createUniversalExportService;
254
1630
  exports.escapeCsvValue = escapeCsvValue;
255
1631
  exports.estimateRemainingTime = estimateRemainingTime;
1632
+ exports.exportConfigs = exportConfigs;
1633
+ exports.exportConfigsRelations = exportConfigsRelations;
1634
+ exports.exportHistory = exportHistory;
1635
+ exports.exportHistoryRelations = exportHistoryRelations;
256
1636
  exports.formatFieldValue = formatFieldValue;
257
1637
  exports.generateUniqueFilename = generateUniqueFilename;
258
1638
  exports.getNestedValue = getNestedValue;