@qwe8652591/abap-recursive-query 1.1.0

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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/dist/cli/consumer-cli.d.ts +8 -0
  4. package/dist/cli/consumer-cli.d.ts.map +1 -0
  5. package/dist/cli/consumer-cli.js +180 -0
  6. package/dist/cli/producer-cli.d.ts +8 -0
  7. package/dist/cli/producer-cli.d.ts.map +1 -0
  8. package/dist/cli/producer-cli.js +249 -0
  9. package/dist/cli/query-object.d.ts +3 -0
  10. package/dist/cli/query-object.d.ts.map +1 -0
  11. package/dist/cli/query-object.js +486 -0
  12. package/dist/common/FilterCache.d.ts +62 -0
  13. package/dist/common/FilterCache.d.ts.map +1 -0
  14. package/dist/common/FilterCache.js +119 -0
  15. package/dist/common/RedisQueueManager.d.ts +170 -0
  16. package/dist/common/RedisQueueManager.d.ts.map +1 -0
  17. package/dist/common/RedisQueueManager.js +663 -0
  18. package/dist/common/abapStructures.d.ts +391 -0
  19. package/dist/common/abapStructures.d.ts.map +1 -0
  20. package/dist/common/abapStructures.js +2 -0
  21. package/dist/common/config.d.ts +66 -0
  22. package/dist/common/config.d.ts.map +1 -0
  23. package/dist/common/config.js +56 -0
  24. package/dist/common/index.d.ts +13 -0
  25. package/dist/common/index.d.ts.map +1 -0
  26. package/dist/common/index.js +38 -0
  27. package/dist/common/recursiveQueryConfig.d.ts +77 -0
  28. package/dist/common/recursiveQueryConfig.d.ts.map +1 -0
  29. package/dist/common/recursiveQueryConfig.js +129 -0
  30. package/dist/common/tableStructures.d.ts +176 -0
  31. package/dist/common/tableStructures.d.ts.map +1 -0
  32. package/dist/common/tableStructures.js +10 -0
  33. package/dist/common/types.d.ts +104 -0
  34. package/dist/common/types.d.ts.map +1 -0
  35. package/dist/common/types.js +31 -0
  36. package/dist/common/utils.d.ts +57 -0
  37. package/dist/common/utils.d.ts.map +1 -0
  38. package/dist/common/utils.js +300 -0
  39. package/dist/consumer/FileDownloadConsumer.d.ts +127 -0
  40. package/dist/consumer/FileDownloadConsumer.d.ts.map +1 -0
  41. package/dist/consumer/FileDownloadConsumer.js +1003 -0
  42. package/dist/consumer/generators/baseGenerator.d.ts +38 -0
  43. package/dist/consumer/generators/baseGenerator.d.ts.map +1 -0
  44. package/dist/consumer/generators/baseGenerator.js +103 -0
  45. package/dist/consumer/generators/domainGenerator.d.ts +78 -0
  46. package/dist/consumer/generators/domainGenerator.d.ts.map +1 -0
  47. package/dist/consumer/generators/domainGenerator.js +241 -0
  48. package/dist/consumer/generators/guiStatusGenerator.d.ts +16 -0
  49. package/dist/consumer/generators/guiStatusGenerator.d.ts.map +1 -0
  50. package/dist/consumer/generators/guiStatusGenerator.js +48 -0
  51. package/dist/consumer/generators/guiTitleGenerator.d.ts +16 -0
  52. package/dist/consumer/generators/guiTitleGenerator.d.ts.map +1 -0
  53. package/dist/consumer/generators/guiTitleGenerator.js +48 -0
  54. package/dist/consumer/generators/index.d.ts +14 -0
  55. package/dist/consumer/generators/index.d.ts.map +1 -0
  56. package/dist/consumer/generators/index.js +38 -0
  57. package/dist/consumer/generators/messageGenerator.d.ts +16 -0
  58. package/dist/consumer/generators/messageGenerator.d.ts.map +1 -0
  59. package/dist/consumer/generators/messageGenerator.js +73 -0
  60. package/dist/consumer/generators/metadataGenerator.d.ts +29 -0
  61. package/dist/consumer/generators/metadataGenerator.d.ts.map +1 -0
  62. package/dist/consumer/generators/metadataGenerator.js +135 -0
  63. package/dist/consumer/generators/screenGenerator.d.ts +173 -0
  64. package/dist/consumer/generators/screenGenerator.d.ts.map +1 -0
  65. package/dist/consumer/generators/screenGenerator.js +859 -0
  66. package/dist/consumer/generators/structureGenerator.d.ts +36 -0
  67. package/dist/consumer/generators/structureGenerator.d.ts.map +1 -0
  68. package/dist/consumer/generators/structureGenerator.js +131 -0
  69. package/dist/consumer/generators/textElementGenerator.d.ts +16 -0
  70. package/dist/consumer/generators/textElementGenerator.d.ts.map +1 -0
  71. package/dist/consumer/generators/textElementGenerator.js +70 -0
  72. package/dist/consumer/handlers/handleGetClass.d.ts +8 -0
  73. package/dist/consumer/handlers/handleGetClass.d.ts.map +1 -0
  74. package/dist/consumer/handlers/handleGetClass.js +18 -0
  75. package/dist/consumer/handlers/handleGetFunction.d.ts +8 -0
  76. package/dist/consumer/handlers/handleGetFunction.d.ts.map +1 -0
  77. package/dist/consumer/handlers/handleGetFunction.js +19 -0
  78. package/dist/consumer/handlers/handleGetFunctionGroup.d.ts +8 -0
  79. package/dist/consumer/handlers/handleGetFunctionGroup.d.ts.map +1 -0
  80. package/dist/consumer/handlers/handleGetFunctionGroup.js +19 -0
  81. package/dist/consumer/handlers/handleGetInclude.d.ts +8 -0
  82. package/dist/consumer/handlers/handleGetInclude.d.ts.map +1 -0
  83. package/dist/consumer/handlers/handleGetInclude.js +18 -0
  84. package/dist/consumer/handlers/handleGetProgram.d.ts +8 -0
  85. package/dist/consumer/handlers/handleGetProgram.d.ts.map +1 -0
  86. package/dist/consumer/handlers/handleGetProgram.js +27 -0
  87. package/dist/consumer/handlers/handleZTableQuery.d.ts +8 -0
  88. package/dist/consumer/handlers/handleZTableQuery.d.ts.map +1 -0
  89. package/dist/consumer/handlers/handleZTableQuery.js +20 -0
  90. package/dist/consumer/handlers/index.d.ts +10 -0
  91. package/dist/consumer/handlers/index.d.ts.map +1 -0
  92. package/dist/consumer/handlers/index.js +27 -0
  93. package/dist/consumer/index.d.ts +9 -0
  94. package/dist/consumer/index.d.ts.map +1 -0
  95. package/dist/consumer/index.js +24 -0
  96. package/dist/consumer/utils/download.d.ts +13 -0
  97. package/dist/consumer/utils/download.d.ts.map +1 -0
  98. package/dist/consumer/utils/download.js +38 -0
  99. package/dist/consumer/utils/index.d.ts +5 -0
  100. package/dist/consumer/utils/index.d.ts.map +1 -0
  101. package/dist/consumer/utils/index.js +20 -0
  102. package/dist/handlers/handleGetClass.d.ts +8 -0
  103. package/dist/handlers/handleGetClass.d.ts.map +1 -0
  104. package/dist/handlers/handleGetClass.js +19 -0
  105. package/dist/handlers/handleGetFunction.d.ts +8 -0
  106. package/dist/handlers/handleGetFunction.d.ts.map +1 -0
  107. package/dist/handlers/handleGetFunction.js +20 -0
  108. package/dist/handlers/handleGetFunctionGroup.d.ts +8 -0
  109. package/dist/handlers/handleGetFunctionGroup.d.ts.map +1 -0
  110. package/dist/handlers/handleGetFunctionGroup.js +19 -0
  111. package/dist/handlers/handleGetInclude.d.ts +8 -0
  112. package/dist/handlers/handleGetInclude.d.ts.map +1 -0
  113. package/dist/handlers/handleGetInclude.js +19 -0
  114. package/dist/handlers/handleGetInterface.d.ts +8 -0
  115. package/dist/handlers/handleGetInterface.d.ts.map +1 -0
  116. package/dist/handlers/handleGetInterface.js +19 -0
  117. package/dist/handlers/handleGetPackage.d.ts +8 -0
  118. package/dist/handlers/handleGetPackage.d.ts.map +1 -0
  119. package/dist/handlers/handleGetPackage.js +42 -0
  120. package/dist/handlers/handleGetProgram.d.ts +8 -0
  121. package/dist/handlers/handleGetProgram.d.ts.map +1 -0
  122. package/dist/handlers/handleGetProgram.js +19 -0
  123. package/dist/handlers/handleGetStructure.d.ts +8 -0
  124. package/dist/handlers/handleGetStructure.d.ts.map +1 -0
  125. package/dist/handlers/handleGetStructure.js +19 -0
  126. package/dist/handlers/handleGetTable.d.ts +8 -0
  127. package/dist/handlers/handleGetTable.d.ts.map +1 -0
  128. package/dist/handlers/handleGetTable.js +19 -0
  129. package/dist/handlers/handleGetTableContents.d.ts +8 -0
  130. package/dist/handlers/handleGetTableContents.d.ts.map +1 -0
  131. package/dist/handlers/handleGetTableContents.js +22 -0
  132. package/dist/handlers/handleGetTransaction.d.ts +8 -0
  133. package/dist/handlers/handleGetTransaction.d.ts.map +1 -0
  134. package/dist/handlers/handleGetTransaction.js +19 -0
  135. package/dist/handlers/handleGetTypeInfo.d.ts +8 -0
  136. package/dist/handlers/handleGetTypeInfo.d.ts.map +1 -0
  137. package/dist/handlers/handleGetTypeInfo.js +32 -0
  138. package/dist/handlers/handleSearchObject.d.ts +8 -0
  139. package/dist/handlers/handleSearchObject.d.ts.map +1 -0
  140. package/dist/handlers/handleSearchObject.js +20 -0
  141. package/dist/handlers/handleZObjectQuery.d.ts +8 -0
  142. package/dist/handlers/handleZObjectQuery.d.ts.map +1 -0
  143. package/dist/handlers/handleZObjectQuery.js +25 -0
  144. package/dist/handlers/handleZTableQuery.d.ts +8 -0
  145. package/dist/handlers/handleZTableQuery.d.ts.map +1 -0
  146. package/dist/handlers/handleZTableQuery.js +20 -0
  147. package/dist/index.d.ts +17 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +45 -0
  150. package/dist/lib/download.d.ts +49 -0
  151. package/dist/lib/download.d.ts.map +1 -0
  152. package/dist/lib/download.js +78 -0
  153. package/dist/lib/index.d.ts +9 -0
  154. package/dist/lib/index.d.ts.map +1 -0
  155. package/dist/lib/index.js +23 -0
  156. package/dist/lib/utils.d.ts +31 -0
  157. package/dist/lib/utils.d.ts.map +1 -0
  158. package/dist/lib/utils.js +272 -0
  159. package/dist/producer/RecursiveQueryProducer.d.ts +92 -0
  160. package/dist/producer/RecursiveQueryProducer.d.ts.map +1 -0
  161. package/dist/producer/RecursiveQueryProducer.js +496 -0
  162. package/dist/producer/handlers/handleZObjectQuery.d.ts +8 -0
  163. package/dist/producer/handlers/handleZObjectQuery.d.ts.map +1 -0
  164. package/dist/producer/handlers/handleZObjectQuery.js +24 -0
  165. package/dist/producer/handlers/index.d.ts +6 -0
  166. package/dist/producer/handlers/index.d.ts.map +1 -0
  167. package/dist/producer/handlers/index.js +21 -0
  168. package/dist/producer/index.d.ts +7 -0
  169. package/dist/producer/index.d.ts.map +1 -0
  170. package/dist/producer/index.js +22 -0
  171. package/dist/recursive/abapStructures.d.ts +377 -0
  172. package/dist/recursive/abapStructures.d.ts.map +1 -0
  173. package/dist/recursive/abapStructures.js +2 -0
  174. package/dist/recursive/generate/baseGenerator.d.ts +34 -0
  175. package/dist/recursive/generate/baseGenerator.d.ts.map +1 -0
  176. package/dist/recursive/generate/baseGenerator.js +112 -0
  177. package/dist/recursive/generate/domainGenerator.d.ts +26 -0
  178. package/dist/recursive/generate/domainGenerator.d.ts.map +1 -0
  179. package/dist/recursive/generate/domainGenerator.js +128 -0
  180. package/dist/recursive/generate/functionGroupGenerator.d.ts +30 -0
  181. package/dist/recursive/generate/functionGroupGenerator.d.ts.map +1 -0
  182. package/dist/recursive/generate/functionGroupGenerator.js +90 -0
  183. package/dist/recursive/generate/index.d.ts +12 -0
  184. package/dist/recursive/generate/index.d.ts.map +1 -0
  185. package/dist/recursive/generate/index.js +34 -0
  186. package/dist/recursive/generate/messageGenerator.d.ts +16 -0
  187. package/dist/recursive/generate/messageGenerator.d.ts.map +1 -0
  188. package/dist/recursive/generate/messageGenerator.js +73 -0
  189. package/dist/recursive/generate/screenGenerator.d.ts +173 -0
  190. package/dist/recursive/generate/screenGenerator.d.ts.map +1 -0
  191. package/dist/recursive/generate/screenGenerator.js +858 -0
  192. package/dist/recursive/generate/structureGenerator.d.ts +22 -0
  193. package/dist/recursive/generate/structureGenerator.d.ts.map +1 -0
  194. package/dist/recursive/generate/structureGenerator.js +88 -0
  195. package/dist/recursive/generate/textElementGenerator.d.ts +16 -0
  196. package/dist/recursive/generate/textElementGenerator.d.ts.map +1 -0
  197. package/dist/recursive/generate/textElementGenerator.js +68 -0
  198. package/dist/recursive/handleRecursiveObjectQuery.d.ts +94 -0
  199. package/dist/recursive/handleRecursiveObjectQuery.d.ts.map +1 -0
  200. package/dist/recursive/handleRecursiveObjectQuery.js +219 -0
  201. package/dist/recursive/recursiveObjectQuery.d.ts +159 -0
  202. package/dist/recursive/recursiveObjectQuery.d.ts.map +1 -0
  203. package/dist/recursive/recursiveObjectQuery.js +1358 -0
  204. package/dist/recursive/recursiveQueryConfig.d.ts +129 -0
  205. package/dist/recursive/recursiveQueryConfig.d.ts.map +1 -0
  206. package/dist/recursive/recursiveQueryConfig.js +133 -0
  207. package/dist/recursive/tableStructures.d.ts +196 -0
  208. package/dist/recursive/tableStructures.d.ts.map +1 -0
  209. package/dist/recursive/tableStructures.js +10 -0
  210. package/package.json +47 -0
@@ -0,0 +1,496 @@
1
+ "use strict";
2
+ /**
3
+ * 递归查询生产者
4
+ * 负责并发递归查询对象,生成所有下载任务
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.RecursiveQueryProducer = void 0;
8
+ const { v4: uuidv4 } = require('uuid');
9
+ const common_1 = require("../common");
10
+ const handlers_1 = require("./handlers");
11
+ /**
12
+ * 异步任务队列(内部并发控制)
13
+ * 复用现有的 AsyncTaskQueue 实现
14
+ */
15
+ class AsyncTaskQueue {
16
+ queue = [];
17
+ running = 0;
18
+ maxConcurrent;
19
+ taskResults = new Map();
20
+ processing = false;
21
+ processingRequests = 0;
22
+ waiters = [];
23
+ constructor(maxConcurrent) {
24
+ this.maxConcurrent = Math.max(1, maxConcurrent);
25
+ }
26
+ async add(taskId, task) {
27
+ if (this.taskResults.has(taskId)) {
28
+ return this.taskResults.get(taskId);
29
+ }
30
+ const taskPromise = new Promise((resolve, reject) => {
31
+ const wrappedTask = async () => {
32
+ try {
33
+ this.running++;
34
+ const result = await task();
35
+ resolve(result);
36
+ return result;
37
+ }
38
+ catch (error) {
39
+ reject(error);
40
+ throw error;
41
+ }
42
+ finally {
43
+ this.running--;
44
+ this.notifyWaiters();
45
+ process.nextTick(() => this.processQueue());
46
+ }
47
+ };
48
+ this.queue.push(wrappedTask);
49
+ });
50
+ this.taskResults.set(taskId, taskPromise);
51
+ taskPromise.finally(() => {
52
+ setTimeout(() => {
53
+ this.taskResults.delete(taskId);
54
+ }, 1000);
55
+ }).catch(() => { });
56
+ this.processQueue();
57
+ return taskPromise;
58
+ }
59
+ processQueue() {
60
+ this.processingRequests++;
61
+ if (this.processing) {
62
+ return;
63
+ }
64
+ this.processing = true;
65
+ process.nextTick(() => {
66
+ try {
67
+ while (this.running < this.maxConcurrent && this.queue.length > 0) {
68
+ const task = this.queue.shift();
69
+ if (task) {
70
+ task().catch(() => { });
71
+ }
72
+ }
73
+ }
74
+ finally {
75
+ this.processing = false;
76
+ this.processingRequests--;
77
+ if (this.processingRequests > 0 || (this.queue.length > 0 && this.running < this.maxConcurrent)) {
78
+ this.processingRequests = 0;
79
+ this.processQueue();
80
+ }
81
+ }
82
+ });
83
+ }
84
+ getQueueSize() {
85
+ return this.queue.length;
86
+ }
87
+ getRunningCount() {
88
+ return this.running;
89
+ }
90
+ notifyWaiters() {
91
+ const waiters = [...this.waiters];
92
+ this.waiters = [];
93
+ waiters.forEach(waiter => waiter());
94
+ }
95
+ async waitForAll() {
96
+ while (this.queue.length > 0 || this.running > 0) {
97
+ await new Promise((resolve) => {
98
+ if (this.queue.length === 0 && this.running === 0) {
99
+ resolve();
100
+ return;
101
+ }
102
+ this.waiters.push(resolve);
103
+ const timeout = setTimeout(() => {
104
+ const index = this.waiters.indexOf(resolve);
105
+ if (index !== -1) {
106
+ this.waiters.splice(index, 1);
107
+ }
108
+ resolve();
109
+ }, 100);
110
+ const originalResolve = resolve;
111
+ this.waiters[this.waiters.length - 1] = () => {
112
+ clearTimeout(timeout);
113
+ originalResolve();
114
+ };
115
+ });
116
+ if (this.queue.length > 0 && this.running < this.maxConcurrent) {
117
+ this.processQueue();
118
+ }
119
+ }
120
+ if (this.taskResults.size > 0) {
121
+ await Promise.allSettled(Array.from(this.taskResults.values()));
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * 递归查询生产者
127
+ */
128
+ class RecursiveQueryProducer {
129
+ queueManager;
130
+ taskQueue;
131
+ jobId;
132
+ filterCache; // ✅ 优化:缓存过滤规则
133
+ constructor(queueManager) {
134
+ this.queueManager = queueManager;
135
+ this.taskQueue = new AsyncTaskQueue(common_1.queueConfig.producerConcurrency);
136
+ }
137
+ /**
138
+ * 创建 Job 并返回 Job ID(不阻塞)
139
+ * @param jobId Job ID(必须由外部传入)
140
+ * @param objectType 对象类型
141
+ * @param objectName 对象名称
142
+ * @param config 递归查询配置
143
+ * @returns Job ID
144
+ */
145
+ async createJob(jobId, objectType, objectName, config) {
146
+ this.jobId = jobId;
147
+ console.error(`[Producer] 创建 Job: ${jobId}`);
148
+ console.error(`[Producer] 对象: ${objectType}/${objectName}`);
149
+ console.error(`[Producer] 配置: 最大深度=${config.maxRecursionDepth}, 并发=${common_1.queueConfig.producerConcurrency}`);
150
+ // ✅ 优化:初始化过滤规则缓存(预编译正则表达式)
151
+ this.initializeFilterCache(config);
152
+ // 创建 Job
153
+ await this.queueManager.createJob(jobId, objectType, objectName, config);
154
+ await this.queueManager.updateJobStatus(jobId, 'RUNNING');
155
+ return jobId;
156
+ }
157
+ /**
158
+ * 提交查询任务(兼容旧版本,阻塞模式)
159
+ * @param objectType 对象类型
160
+ * @param objectName 对象名称
161
+ * @param config 递归查询配置
162
+ * @returns Job ID
163
+ */
164
+ async submitQuery(objectType, objectName, config) {
165
+ const jobId = uuidv4();
166
+ await this.createJob(jobId, objectType, objectName, config);
167
+ // 同步模式: 等待所有任务生成完成
168
+ await this.generateAllTasks(jobId, objectType, objectName, config);
169
+ return jobId;
170
+ }
171
+ /**
172
+ * 生成所有任务(可以被外部调用)
173
+ */
174
+ async generateAllTasks(jobId, objectType, objectName, config) {
175
+ try {
176
+ await this.recursivelyGenerateTasks(jobId, objectType, objectName, 0, config);
177
+ // 等待所有查询任务完成
178
+ await this.taskQueue.waitForAll();
179
+ console.error(`[Producer] Job ${jobId} 所有任务已生成`);
180
+ }
181
+ catch (error) {
182
+ console.error(`[Producer] Job ${jobId} 生成任务失败:`, error);
183
+ await this.queueManager.updateJobStatus(jobId, 'FAILED');
184
+ throw error;
185
+ }
186
+ }
187
+ /**
188
+ * 等待 Job 完成
189
+ */
190
+ async waitForCompletion(jobId, timeout) {
191
+ const startTime = Date.now();
192
+ const maxTimeout = timeout || common_1.queueConfig.jobTimeout;
193
+ console.error(`[Producer] 等待 Job ${jobId} 完成...`);
194
+ while (true) {
195
+ const job = await this.queueManager.getJob(jobId);
196
+ if (!job) {
197
+ throw new Error(`Job ${jobId} 不存在`);
198
+ }
199
+ if (job.status === 'COMPLETED' || job.status === 'FAILED' || job.status === 'CANCELLED') {
200
+ console.error(`[Producer] Job ${jobId} 完成,状态: ${job.status}`);
201
+ return job;
202
+ }
203
+ // 检查超时
204
+ if (Date.now() - startTime > maxTimeout) {
205
+ throw new Error(`Job ${jobId} 超时`);
206
+ }
207
+ // 等待一段时间后重试
208
+ await this.sleep(common_1.queueConfig.pollInterval);
209
+ // 打印进度
210
+ const progress = await this.queueManager.getProgress(jobId);
211
+ if (progress) {
212
+ console.error(`[Producer] Job ${jobId} 进度: ${progress.percentage.toFixed(1)}% (${progress.completedTasks}/${progress.totalTasks})`);
213
+ }
214
+ }
215
+ }
216
+ /**
217
+ * 获取进度
218
+ */
219
+ async getProgress(jobId) {
220
+ return await this.queueManager.getProgress(jobId);
221
+ }
222
+ /**
223
+ * 取消 Job
224
+ */
225
+ async cancelJob(jobId) {
226
+ await this.queueManager.updateJobStatus(jobId, 'CANCELLED');
227
+ console.error(`[Producer] Job ${jobId} 已取消`);
228
+ }
229
+ /**
230
+ * 递归生成任务(并发版本)
231
+ */
232
+ async recursivelyGenerateTasks(jobId, objectType, objectName, depth, config) {
233
+ // 1. 检查深度限制
234
+ if (depth > config.maxRecursionDepth) {
235
+ console.error(`[Producer] 达到最大递归深度 ${config.maxRecursionDepth},停止处理`);
236
+ return;
237
+ }
238
+ const objectKey = `${objectType}/${objectName}`;
239
+ // 2. 应用过滤规则
240
+ if (this.shouldFilter(objectType, objectName, config)) {
241
+ console.error(`[Producer] 对象被过滤规则排除,跳过: ${objectKey}`);
242
+ return;
243
+ }
244
+ // 3. 原子性地检查并标记访问(✅ 优化:从2次Redis操作减少到1次)
245
+ const isFirstVisit = await this.queueManager.markVisitedIfNotExists(jobId, objectKey);
246
+ if (!isFirstVisit) {
247
+ console.error(`[Producer] 对象已访问,跳过: ${objectKey}`);
248
+ return;
249
+ }
250
+ // 5. 背压控制:检查队列大小
251
+ await this.waitForQueueSpace(jobId, common_1.queueConfig.queueMaxSize);
252
+ // 6. 使用内部并发队列处理
253
+ await this.taskQueue.add(objectKey, async () => {
254
+ console.error(`[Producer] 开始处理对象: ${objectType}/${objectName} (深度: ${depth})`);
255
+ console.error(`[Producer] 队列状态 - 运行中: ${this.taskQueue.getRunningCount()}, 等待中: ${this.taskQueue.getQueueSize()}`);
256
+ // 6.1 查询对象
257
+ const queryResult = await this.queryObject(objectType, objectName);
258
+ if (!queryResult || !queryResult.success || !queryResult.data) {
259
+ const errorMsg = `查询对象失败: ${objectType}/${objectName} - ${queryResult?.error || '未知错误'}`;
260
+ console.error(`[Producer] ${errorMsg}`);
261
+ // ✅ 记录错误到 Job
262
+ await this.queueManager.recordJobError(jobId, errorMsg);
263
+ return;
264
+ }
265
+ const objectData = queryResult.data;
266
+ // 6.2 提取依赖关系
267
+ const dependencies = this.extractDependencies(objectData);
268
+ // 6.3 准备缓存数据(移除引用关系)
269
+ const cacheData = this.prepareCacheData(objectData);
270
+ // 6.4 压缩并缓存到Redis
271
+ await this.queueManager.cacheObject(jobId, objectKey, cacheData);
272
+ // 6.5 创建下载任务
273
+ const task = {
274
+ taskId: uuidv4(),
275
+ jobId,
276
+ objectKey,
277
+ taskType: 'DOWNLOAD',
278
+ objectType,
279
+ objectName,
280
+ depth,
281
+ priority: this.calculatePriority(depth),
282
+ status: 'PENDING',
283
+ retryCount: 0,
284
+ maxRetries: common_1.queueConfig.maxRetries,
285
+ createdAt: new Date(),
286
+ metadata: {
287
+ cacheKey: `abap:cache:${jobId}:${objectKey}`,
288
+ dependencyCount: dependencies.length
289
+ }
290
+ };
291
+ await this.queueManager.createTask(task);
292
+ console.error(`[Producer] 创建任务: ${objectKey} (优先级: ${task.priority})`);
293
+ // 6.6 递归处理依赖
294
+ if (depth < config.maxRecursionDepth && dependencies.length > 0) {
295
+ console.error(`[Producer] 处理 ${dependencies.length} 个依赖 (深度: ${depth + 1})`);
296
+ const childPromises = dependencies.map(dep => this.recursivelyGenerateTasks(jobId, dep.objectType, dep.objectName, depth + 1, config));
297
+ await Promise.allSettled(childPromises);
298
+ }
299
+ });
300
+ }
301
+ /**
302
+ * 背压控制:等待队列有空间(✅ 优化:事件驱动替代轮询)
303
+ */
304
+ async waitForQueueSpace(jobId, maxSize) {
305
+ let waitCount = 0;
306
+ while (true) {
307
+ const queueSize = await this.queueManager.getQueueSize(jobId);
308
+ if (queueSize < maxSize) {
309
+ if (waitCount > 0) {
310
+ console.error(`[背压控制] Job ${jobId} 队列恢复 (${queueSize}/${maxSize}),继续生产`);
311
+ }
312
+ break;
313
+ }
314
+ waitCount++;
315
+ if (waitCount === 1) {
316
+ console.error(`[背压控制] Job ${jobId} 队列已满 (${queueSize}/${maxSize}),等待消费者处理...`);
317
+ }
318
+ // ✅ 使用事件通知替代轮询
319
+ await new Promise((resolve) => {
320
+ let unsubscribe = null;
321
+ let timeout = null;
322
+ let resolved = false;
323
+ const cleanup = async () => {
324
+ if (!resolved) {
325
+ resolved = true;
326
+ if (timeout)
327
+ clearTimeout(timeout);
328
+ if (unsubscribe)
329
+ await unsubscribe();
330
+ resolve();
331
+ }
332
+ };
333
+ // 订阅队列空间事件
334
+ this.queueManager.subscribeQueueSpace(jobId, () => {
335
+ cleanup().catch(() => { });
336
+ }).then(unsub => {
337
+ unsubscribe = unsub;
338
+ }).catch(() => {
339
+ cleanup().catch(() => { });
340
+ });
341
+ // 设置超时(防止消息丢失)
342
+ timeout = setTimeout(() => {
343
+ cleanup().catch(() => { });
344
+ }, common_1.queueConfig.backpressureCheckInterval);
345
+ });
346
+ }
347
+ }
348
+ /**
349
+ * 查询对象
350
+ */
351
+ async queryObject(objectType, objectName) {
352
+ try {
353
+ const result = await (0, handlers_1.handleZObjectQuery)({
354
+ query_type: objectType,
355
+ query_name: objectName
356
+ });
357
+ if (result && typeof result === 'object' && 'isError' in result && result.isError) {
358
+ const errorText = Array.isArray(result.content) && result.content.length > 0
359
+ ? result.content[0].text || '未知错误'
360
+ : '未知错误';
361
+ return { success: false, error: errorText };
362
+ }
363
+ let actualData;
364
+ if (result && typeof result === 'object' && 'content' in result && Array.isArray(result.content)) {
365
+ if (result.content.length > 0 && result.content[0].text) {
366
+ actualData = result.content[0].text;
367
+ }
368
+ }
369
+ if (actualData) {
370
+ return { success: true, data: actualData };
371
+ }
372
+ else {
373
+ return { success: false, error: '无有效数据返回' };
374
+ }
375
+ }
376
+ catch (error) {
377
+ return {
378
+ success: false,
379
+ error: error instanceof Error ? error.message : String(error)
380
+ };
381
+ }
382
+ }
383
+ /**
384
+ * 提取依赖关系
385
+ */
386
+ extractDependencies(objectData) {
387
+ const dependencies = [];
388
+ // 提取类依赖
389
+ if (objectData.ET_REFCLASS && Array.isArray(objectData.ET_REFCLASS)) {
390
+ for (const cls of objectData.ET_REFCLASS) {
391
+ if (cls.CLSNAME) {
392
+ dependencies.push({
393
+ objectType: 'CLASS',
394
+ objectName: cls.CLSNAME
395
+ });
396
+ }
397
+ }
398
+ }
399
+ // 提取函数依赖
400
+ if (objectData.ET_REFFUNCTION && Array.isArray(objectData.ET_REFFUNCTION)) {
401
+ for (const func of objectData.ET_REFFUNCTION) {
402
+ // 优先使用 PNAME(程序名),如果不存在则使用 FUNCTIONNAME
403
+ // PNAME 通常是函数组的主程序名,更准确地表示依赖关系
404
+ const dependencyName = func.PNAME || func.FUNCTIONNAME;
405
+ if (dependencyName) {
406
+ dependencies.push({
407
+ objectType: func.PNAME ? 'PROGRAM' : 'FUNCTION',
408
+ objectName: dependencyName
409
+ });
410
+ }
411
+ }
412
+ }
413
+ // 提取程序依赖
414
+ if (objectData.ET_REFPROGRAM && Array.isArray(objectData.ET_REFPROGRAM)) {
415
+ for (const prog of objectData.ET_REFPROGRAM) {
416
+ if (prog.PROGNAME) {
417
+ dependencies.push({
418
+ objectType: 'PROGRAM',
419
+ objectName: prog.PROGNAME
420
+ });
421
+ }
422
+ }
423
+ }
424
+ return dependencies;
425
+ }
426
+ /**
427
+ * 准备缓存数据(包含依赖关系)
428
+ */
429
+ prepareCacheData(objectData) {
430
+ return {
431
+ ET_FUNCTION: objectData.ET_FUNCTION,
432
+ ET_PROGRAM: objectData.ET_PROGRAM,
433
+ ET_CLASSES: objectData.ET_CLASSES,
434
+ // ✅ 缓存依赖关系(用于生成依赖项XML)
435
+ ET_REFCLASS: objectData.ET_REFCLASS,
436
+ ET_REFFUNCTION: objectData.ET_REFFUNCTION,
437
+ ET_REFPROGRAM: objectData.ET_REFPROGRAM
438
+ };
439
+ }
440
+ /**
441
+ * 计算任务优先级
442
+ */
443
+ calculatePriority(depth) {
444
+ // 深度越小,优先级越高(数值越小)
445
+ return depth * 1000 + (Date.now() % 1000);
446
+ }
447
+ /**
448
+ * 初始化过滤规则缓存
449
+ * ✅ 优化:预编译所有正则表达式,避免每次检查时重复编译
450
+ * @param config 递归查询配置
451
+ */
452
+ initializeFilterCache(config) {
453
+ // 合并默认过滤规则和用户自定义过滤规则
454
+ const excludeNames = [
455
+ ...(common_1.defaultFilters.excludeNames || []),
456
+ ...(config.recursionFilters?.excludeNames || [])
457
+ ];
458
+ const excludeNamePatterns = [
459
+ ...(common_1.defaultFilters.excludeNamePatterns || []),
460
+ ...(config.recursionFilters?.excludeNamePatterns || [])
461
+ ];
462
+ // 创建过滤缓存(一次性预编译所有规则)
463
+ this.filterCache = new common_1.FilterCache(excludeNames, excludeNamePatterns);
464
+ const stats = this.filterCache.getStats();
465
+ console.error(`[Producer] 过滤规则已加载: 精确匹配=${stats.exactMatchCount}, 模式匹配=${stats.patternCount}`);
466
+ }
467
+ /**
468
+ * 检查对象是否应该被过滤
469
+ * ✅ 优化:使用预编译的 FilterCache,避免每次检查时重复编译正则表达式
470
+ * @param objectType 对象类型
471
+ * @param objectName 对象名称
472
+ * @param config 递归查询配置
473
+ * @returns true 表示应该过滤(不递归查询),false 表示不过滤
474
+ */
475
+ shouldFilter(objectType, objectName, config) {
476
+ // 如果没有初始化过滤缓存,先初始化
477
+ if (!this.filterCache) {
478
+ this.initializeFilterCache(config);
479
+ }
480
+ // 使用预编译的过滤缓存(性能优化)
481
+ const isFiltered = this.filterCache.matches(objectName);
482
+ if (isFiltered) {
483
+ // 获取匹配的模式(用于日志)
484
+ const matchedPatterns = this.filterCache.getMatchedPatterns(objectName);
485
+ console.error(`[Producer] 过滤规则: 排除 "${objectName}" (${matchedPatterns.join(', ')})`);
486
+ }
487
+ return isFiltered;
488
+ }
489
+ /**
490
+ * 延迟函数
491
+ */
492
+ sleep(ms) {
493
+ return new Promise(resolve => setTimeout(resolve, ms));
494
+ }
495
+ }
496
+ exports.RecursiveQueryProducer = RecursiveQueryProducer;
@@ -0,0 +1,8 @@
1
+ export declare function handleZObjectQuery(args: any): Promise<{
2
+ isError: boolean;
3
+ content: {
4
+ type: string;
5
+ text: any;
6
+ }[];
7
+ }>;
8
+ //# sourceMappingURL=handleZObjectQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handleZObjectQuery.d.ts","sourceRoot":"","sources":["../../../src/producer/handlers/handleZObjectQuery.ts"],"names":[],"mappings":"AAEA,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,GAAG;;;;;;GAmBjD"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleZObjectQuery = handleZObjectQuery;
4
+ const utils_1 = require("../../common/utils");
5
+ async function handleZObjectQuery(args) {
6
+ try {
7
+ if (!args?.query_type) {
8
+ console.error('query_type:', args);
9
+ throw new utils_1.McpError(utils_1.ErrorCode.InvalidParams, 'Query type is required');
10
+ }
11
+ if (!args?.query_name) {
12
+ console.error('query_name:', args);
13
+ throw new utils_1.McpError(utils_1.ErrorCode.InvalidParams, 'Query name is required');
14
+ }
15
+ const encodedQueryType = encodeURIComponent(args.query_type);
16
+ const encodedQueryName = encodeURIComponent(args.query_name);
17
+ const url = `${await (0, utils_1.getBaseUrl)()}/sap/bc/z_object_query?type=${encodedQueryType}&name=${encodedQueryName}`;
18
+ const response = await (0, utils_1.makeAdtRequest)(url, 'GET', 100000);
19
+ return (0, utils_1.return_response)(response);
20
+ }
21
+ catch (error) {
22
+ return (0, utils_1.return_error)(error);
23
+ }
24
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 生产者使用的 Handlers
3
+ * 用于查询对象引用关系
4
+ */
5
+ export * from './handleZObjectQuery';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/producer/handlers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * 生产者使用的 Handlers
4
+ * 用于查询对象引用关系
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./handleZObjectQuery"), exports);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 生产者组件导出
3
+ * 负责递归查询和任务生成
4
+ */
5
+ export * from './RecursiveQueryProducer';
6
+ export * from './handlers';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/producer/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * 生产者组件导出
4
+ * 负责递归查询和任务生成
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./RecursiveQueryProducer"), exports);
22
+ __exportStar(require("./handlers"), exports);