mcp-cnbs 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +86 -273
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -15,128 +15,75 @@ import axios from "axios";
|
|
|
15
15
|
import https from "https";
|
|
16
16
|
|
|
17
17
|
// src/services/cache.ts
|
|
18
|
-
import fs from "fs";
|
|
19
|
-
import path from "path";
|
|
20
|
-
import { promisify } from "util";
|
|
21
|
-
var writeFileAsync = promisify(fs.writeFile);
|
|
22
|
-
var readFileAsync = promisify(fs.readFile);
|
|
23
|
-
var mkdirAsync = promisify(fs.mkdir);
|
|
24
|
-
var existsAsync = promisify(fs.exists);
|
|
25
18
|
var CnbsLruCache = class {
|
|
26
19
|
entryMap = /* @__PURE__ */ new Map();
|
|
27
20
|
head = null;
|
|
28
21
|
tail = null;
|
|
29
|
-
persistPath = null;
|
|
30
22
|
capacity;
|
|
31
23
|
defaultExpire;
|
|
32
24
|
maxMemorySize;
|
|
33
25
|
currentMemorySize = 0;
|
|
34
|
-
persistInterval;
|
|
35
26
|
cleanupInterval;
|
|
36
|
-
|
|
37
|
-
// 统计信息
|
|
27
|
+
lastCleanupTime = 0;
|
|
38
28
|
totalHits = 0;
|
|
39
29
|
totalMisses = 0;
|
|
40
30
|
evictionCount = 0;
|
|
41
31
|
expirationCount = 0;
|
|
42
|
-
persistenceCount = 0;
|
|
43
|
-
// 定时器
|
|
44
|
-
persistTimer = null;
|
|
45
|
-
cleanupTimer = null;
|
|
46
|
-
// 并发控制
|
|
47
|
-
persistLock = false;
|
|
48
32
|
constructor(options = {}) {
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// 前置条目
|
|
33
|
+
this.capacity = options.capacity ?? 1e3;
|
|
34
|
+
this.defaultExpire = options.defaultExpire ?? 24 * 60 * 60 * 1e3;
|
|
35
|
+
this.maxMemorySize = options.maxMemorySize ?? 100 * 1024 * 1024;
|
|
36
|
+
this.cleanupInterval = options.cleanupInterval ?? 60 * 1e3;
|
|
37
|
+
}
|
|
38
|
+
// ─── 按需清理(替代 setInterval)────────────────────────────────────────────
|
|
39
|
+
maybeCleanup() {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
if (now - this.lastCleanupTime < this.cleanupInterval) return;
|
|
42
|
+
this.lastCleanupTime = now;
|
|
43
|
+
this.cleanupExpired();
|
|
44
|
+
}
|
|
45
|
+
cleanupExpired() {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const expiredKeys = [];
|
|
48
|
+
this.entryMap.forEach((entry, key) => {
|
|
49
|
+
if (now > entry.expireAt) expiredKeys.push(key);
|
|
50
|
+
});
|
|
51
|
+
for (const key of expiredKeys) {
|
|
52
|
+
const entry = this.entryMap.get(key);
|
|
53
|
+
if (entry) {
|
|
54
|
+
this.removeEntryFromList(entry);
|
|
55
|
+
this.entryMap.delete(key);
|
|
56
|
+
this.expirationCount++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// ─── 双向链表操作 ────────────────────────────────────────────────────────────
|
|
80
61
|
prependEntry(entry) {
|
|
81
62
|
entry.prev = null;
|
|
82
63
|
entry.next = this.head;
|
|
83
|
-
if (this.head !== null)
|
|
84
|
-
this.head.prev = entry;
|
|
85
|
-
}
|
|
64
|
+
if (this.head !== null) this.head.prev = entry;
|
|
86
65
|
this.head = entry;
|
|
87
|
-
if (this.tail === null)
|
|
88
|
-
this.tail = entry;
|
|
89
|
-
}
|
|
66
|
+
if (this.tail === null) this.tail = entry;
|
|
90
67
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
this.head = entry.next;
|
|
97
|
-
}
|
|
98
|
-
if (entry.next !== null) {
|
|
99
|
-
entry.next.prev = entry.prev;
|
|
100
|
-
} else {
|
|
101
|
-
this.tail = entry.prev;
|
|
102
|
-
}
|
|
68
|
+
removeEntryFromList(entry) {
|
|
69
|
+
if (entry.prev !== null) entry.prev.next = entry.next;
|
|
70
|
+
else this.head = entry.next;
|
|
71
|
+
if (entry.next !== null) entry.next.prev = entry.prev;
|
|
72
|
+
else this.tail = entry.prev;
|
|
103
73
|
this.currentMemorySize -= entry.size;
|
|
104
74
|
}
|
|
105
|
-
// 提升条目
|
|
106
75
|
promoteEntry(entry) {
|
|
107
|
-
this.
|
|
76
|
+
this.removeEntryFromList(entry);
|
|
108
77
|
this.prependEntry(entry);
|
|
109
78
|
}
|
|
110
|
-
// 删除尾部条目
|
|
111
79
|
dropTail() {
|
|
112
80
|
if (this.tail === null) return null;
|
|
113
81
|
const tailEntry = this.tail;
|
|
114
|
-
this.
|
|
82
|
+
this.removeEntryFromList(tailEntry);
|
|
115
83
|
this.evictionCount++;
|
|
116
84
|
return tailEntry;
|
|
117
85
|
}
|
|
118
|
-
//
|
|
119
|
-
cleanupExpired() {
|
|
120
|
-
const now = Date.now();
|
|
121
|
-
const expiredKeys = [];
|
|
122
|
-
this.entryMap.forEach((entry, key) => {
|
|
123
|
-
if (now > entry.expireAt) {
|
|
124
|
-
expiredKeys.push(key);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
for (const key of expiredKeys) {
|
|
128
|
-
const entry = this.entryMap.get(key);
|
|
129
|
-
if (entry) {
|
|
130
|
-
this.deleteEntry(entry);
|
|
131
|
-
this.entryMap.delete(key);
|
|
132
|
-
this.expirationCount++;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (expiredKeys.length > 0) {
|
|
136
|
-
console.info(`Cleaned up ${expiredKeys.length} expired cache entries`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// 计算值的大小
|
|
86
|
+
// ─── 工具 ────────────────────────────────────────────────────────────────────
|
|
140
87
|
calculateSize(value) {
|
|
141
88
|
try {
|
|
142
89
|
return JSON.stringify(value).length;
|
|
@@ -144,15 +91,16 @@ var CnbsLruCache = class {
|
|
|
144
91
|
return 0;
|
|
145
92
|
}
|
|
146
93
|
}
|
|
147
|
-
//
|
|
94
|
+
// ─── 公开 API ────────────────────────────────────────────────────────────────
|
|
148
95
|
fetch(key) {
|
|
96
|
+
this.maybeCleanup();
|
|
149
97
|
const entry = this.entryMap.get(key);
|
|
150
98
|
if (!entry) {
|
|
151
99
|
this.totalMisses++;
|
|
152
100
|
return null;
|
|
153
101
|
}
|
|
154
102
|
if (Date.now() > entry.expireAt) {
|
|
155
|
-
this.
|
|
103
|
+
this.removeEntryFromList(entry);
|
|
156
104
|
this.entryMap.delete(key);
|
|
157
105
|
this.expirationCount++;
|
|
158
106
|
this.totalMisses++;
|
|
@@ -164,42 +112,34 @@ var CnbsLruCache = class {
|
|
|
164
112
|
this.totalHits++;
|
|
165
113
|
return entry.value;
|
|
166
114
|
}
|
|
167
|
-
// 批量获取缓存
|
|
168
115
|
fetchMultiple(keys) {
|
|
169
116
|
const result = /* @__PURE__ */ new Map();
|
|
170
117
|
for (const key of keys) {
|
|
171
118
|
const value = this.fetch(key);
|
|
172
|
-
if (value !== null)
|
|
173
|
-
result.set(key, value);
|
|
174
|
-
}
|
|
119
|
+
if (value !== null) result.set(key, value);
|
|
175
120
|
}
|
|
176
121
|
return result;
|
|
177
122
|
}
|
|
178
|
-
// 存储缓存
|
|
179
123
|
store(key, value, ttl = this.defaultExpire) {
|
|
180
124
|
const size = this.calculateSize(value);
|
|
181
125
|
while (this.currentMemorySize + size > this.maxMemorySize && this.entryMap.size > 0) {
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
existingEntry.lastHit = Date.now();
|
|
195
|
-
this.promoteEntry(existingEntry);
|
|
126
|
+
const tail = this.dropTail();
|
|
127
|
+
if (tail) this.entryMap.delete(tail.key);
|
|
128
|
+
}
|
|
129
|
+
const existing = this.entryMap.get(key);
|
|
130
|
+
if (existing) {
|
|
131
|
+
this.currentMemorySize -= existing.size;
|
|
132
|
+
existing.value = value;
|
|
133
|
+
existing.size = size;
|
|
134
|
+
existing.expireAt = Date.now() + ttl;
|
|
135
|
+
existing.hitCount = 1;
|
|
136
|
+
existing.lastHit = Date.now();
|
|
137
|
+
this.promoteEntry(existing);
|
|
196
138
|
this.currentMemorySize += size;
|
|
197
139
|
} else {
|
|
198
140
|
if (this.entryMap.size >= this.capacity) {
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
this.entryMap.delete(tailEntry.key);
|
|
202
|
-
}
|
|
141
|
+
const tail = this.dropTail();
|
|
142
|
+
if (tail) this.entryMap.delete(tail.key);
|
|
203
143
|
}
|
|
204
144
|
const newEntry = {
|
|
205
145
|
key,
|
|
@@ -216,75 +156,40 @@ var CnbsLruCache = class {
|
|
|
216
156
|
this.currentMemorySize += size;
|
|
217
157
|
}
|
|
218
158
|
}
|
|
219
|
-
// 批量存储缓存
|
|
220
159
|
storeMultiple(items) {
|
|
221
|
-
for (const item of items)
|
|
222
|
-
this.store(item.key, item.value, item.ttl);
|
|
223
|
-
}
|
|
160
|
+
for (const item of items) this.store(item.key, item.value, item.ttl);
|
|
224
161
|
}
|
|
225
|
-
// 删除缓存
|
|
226
162
|
remove(key) {
|
|
227
163
|
const entry = this.entryMap.get(key);
|
|
228
164
|
if (entry) {
|
|
229
|
-
this.
|
|
165
|
+
this.removeEntryFromList(entry);
|
|
230
166
|
this.entryMap.delete(key);
|
|
231
167
|
}
|
|
232
168
|
}
|
|
233
|
-
// 批量删除缓存
|
|
234
169
|
removeMultiple(keys) {
|
|
235
|
-
for (const key of keys)
|
|
236
|
-
this.remove(key);
|
|
237
|
-
}
|
|
170
|
+
for (const key of keys) this.remove(key);
|
|
238
171
|
}
|
|
239
|
-
// 清空缓存
|
|
240
172
|
flush() {
|
|
241
173
|
this.entryMap.clear();
|
|
242
174
|
this.head = null;
|
|
243
175
|
this.tail = null;
|
|
244
176
|
this.currentMemorySize = 0;
|
|
245
|
-
this.saveToDisk();
|
|
246
177
|
}
|
|
247
|
-
// 获取缓存数量
|
|
248
178
|
count() {
|
|
249
179
|
return this.entryMap.size;
|
|
250
180
|
}
|
|
251
|
-
// 获取内存使用
|
|
252
181
|
getMemorySize() {
|
|
253
182
|
return this.currentMemorySize;
|
|
254
183
|
}
|
|
255
|
-
// 获取统计信息
|
|
256
184
|
getStats() {
|
|
257
|
-
if (this.entryMap.size === 0) {
|
|
258
|
-
return {
|
|
259
|
-
size: 0,
|
|
260
|
-
capacity: this.capacity,
|
|
261
|
-
memorySize: this.currentMemorySize,
|
|
262
|
-
maxMemorySize: this.maxMemorySize,
|
|
263
|
-
oldestEntry: null,
|
|
264
|
-
topHit: null,
|
|
265
|
-
hitRate: 0,
|
|
266
|
-
missRate: 0,
|
|
267
|
-
totalHits: this.totalHits,
|
|
268
|
-
totalMisses: this.totalMisses,
|
|
269
|
-
evictionCount: this.evictionCount,
|
|
270
|
-
expirationCount: this.expirationCount,
|
|
271
|
-
persistenceCount: this.persistenceCount
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
185
|
let oldestEntry = null;
|
|
275
186
|
let topHit = null;
|
|
276
187
|
this.entryMap.forEach((item, key) => {
|
|
277
188
|
const age = Date.now() - item.lastHit;
|
|
278
|
-
if (!oldestEntry || age > oldestEntry.age) {
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
if (!topHit || item.hitCount > topHit.count) {
|
|
282
|
-
topHit = { key, count: item.hitCount };
|
|
283
|
-
}
|
|
189
|
+
if (!oldestEntry || age > oldestEntry.age) oldestEntry = { key, age };
|
|
190
|
+
if (!topHit || item.hitCount > topHit.count) topHit = { key, count: item.hitCount };
|
|
284
191
|
});
|
|
285
|
-
const
|
|
286
|
-
const hitRate = totalRequests > 0 ? this.totalHits / totalRequests : 0;
|
|
287
|
-
const missRate = totalRequests > 0 ? this.totalMisses / totalRequests : 0;
|
|
192
|
+
const total = this.totalHits + this.totalMisses;
|
|
288
193
|
return {
|
|
289
194
|
size: this.entryMap.size,
|
|
290
195
|
capacity: this.capacity,
|
|
@@ -292,16 +197,16 @@ var CnbsLruCache = class {
|
|
|
292
197
|
maxMemorySize: this.maxMemorySize,
|
|
293
198
|
oldestEntry,
|
|
294
199
|
topHit,
|
|
295
|
-
hitRate: parseFloat((
|
|
296
|
-
missRate: parseFloat((
|
|
200
|
+
hitRate: total > 0 ? parseFloat((this.totalHits / total * 100).toFixed(2)) : 0,
|
|
201
|
+
missRate: total > 0 ? parseFloat((this.totalMisses / total * 100).toFixed(2)) : 0,
|
|
297
202
|
totalHits: this.totalHits,
|
|
298
203
|
totalMisses: this.totalMisses,
|
|
299
204
|
evictionCount: this.evictionCount,
|
|
300
205
|
expirationCount: this.expirationCount,
|
|
301
|
-
persistenceCount:
|
|
206
|
+
persistenceCount: 0
|
|
207
|
+
// Workers 环境无持久化
|
|
302
208
|
};
|
|
303
209
|
}
|
|
304
|
-
// 获取缓存信息
|
|
305
210
|
getCacheInfo(key) {
|
|
306
211
|
const entry = this.entryMap.get(key);
|
|
307
212
|
if (!entry) return null;
|
|
@@ -312,96 +217,8 @@ var CnbsLruCache = class {
|
|
|
312
217
|
hits: entry.hitCount
|
|
313
218
|
};
|
|
314
219
|
}
|
|
315
|
-
//
|
|
316
|
-
saveToDisk() {
|
|
317
|
-
if (!this.persistPath) return;
|
|
318
|
-
try {
|
|
319
|
-
const dir = path.dirname(this.persistPath);
|
|
320
|
-
if (!fs.existsSync(dir)) {
|
|
321
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
322
|
-
}
|
|
323
|
-
const dataToSave = [];
|
|
324
|
-
let current = this.head;
|
|
325
|
-
while (current !== null) {
|
|
326
|
-
dataToSave.push({
|
|
327
|
-
key: current.key,
|
|
328
|
-
value: current.value,
|
|
329
|
-
expireAt: current.expireAt,
|
|
330
|
-
hitCount: current.hitCount,
|
|
331
|
-
lastHit: current.lastHit,
|
|
332
|
-
size: current.size
|
|
333
|
-
});
|
|
334
|
-
current = current.next;
|
|
335
|
-
}
|
|
336
|
-
fs.writeFileSync(this.persistPath, JSON.stringify(dataToSave));
|
|
337
|
-
this.persistenceCount++;
|
|
338
|
-
} catch (error) {
|
|
339
|
-
console.error("Failed to save cache to disk:", error);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
// 异步保存到磁盘
|
|
343
|
-
async saveToDiskAsync() {
|
|
344
|
-
if (!this.persistPath || this.persistLock) return;
|
|
345
|
-
this.persistLock = true;
|
|
346
|
-
try {
|
|
347
|
-
const dir = path.dirname(this.persistPath);
|
|
348
|
-
if (!await existsAsync(dir)) {
|
|
349
|
-
await mkdirAsync(dir, { recursive: true });
|
|
350
|
-
}
|
|
351
|
-
const dataToSave = [];
|
|
352
|
-
let current = this.head;
|
|
353
|
-
while (current !== null) {
|
|
354
|
-
dataToSave.push({
|
|
355
|
-
key: current.key,
|
|
356
|
-
value: current.value,
|
|
357
|
-
expireAt: current.expireAt,
|
|
358
|
-
hitCount: current.hitCount,
|
|
359
|
-
lastHit: current.lastHit,
|
|
360
|
-
size: current.size
|
|
361
|
-
});
|
|
362
|
-
current = current.next;
|
|
363
|
-
}
|
|
364
|
-
await writeFileAsync(this.persistPath, JSON.stringify(dataToSave));
|
|
365
|
-
this.persistenceCount++;
|
|
366
|
-
} catch (error) {
|
|
367
|
-
console.error("Failed to save cache to disk:", error);
|
|
368
|
-
} finally {
|
|
369
|
-
this.persistLock = false;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
// 从磁盘加载
|
|
373
|
-
loadFromDisk() {
|
|
374
|
-
if (!this.persistPath || !fs.existsSync(this.persistPath)) return;
|
|
375
|
-
try {
|
|
376
|
-
const data = fs.readFileSync(this.persistPath, "utf8");
|
|
377
|
-
const cachedItems = JSON.parse(data);
|
|
378
|
-
const now = Date.now();
|
|
379
|
-
for (const item of cachedItems) {
|
|
380
|
-
if (item.expireAt > now) {
|
|
381
|
-
const newEntry = {
|
|
382
|
-
key: item.key,
|
|
383
|
-
value: item.value,
|
|
384
|
-
expireAt: item.expireAt,
|
|
385
|
-
hitCount: item.hitCount,
|
|
386
|
-
lastHit: item.lastHit,
|
|
387
|
-
prev: null,
|
|
388
|
-
next: null,
|
|
389
|
-
size: item.size
|
|
390
|
-
};
|
|
391
|
-
this.entryMap.set(item.key, newEntry);
|
|
392
|
-
this.prependEntry(newEntry);
|
|
393
|
-
this.currentMemorySize += item.size;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
console.info(`Loaded ${this.entryMap.size} cache entries from disk`);
|
|
397
|
-
} catch (error) {
|
|
398
|
-
console.error("Failed to load cache from disk:", error);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
// 关闭缓存
|
|
220
|
+
// close() 保留空实现,防止上层调用报错
|
|
402
221
|
close() {
|
|
403
|
-
this.stopTimers();
|
|
404
|
-
this.saveToDisk();
|
|
405
222
|
}
|
|
406
223
|
};
|
|
407
224
|
var CnbsCacheHub = class {
|
|
@@ -410,19 +227,14 @@ var CnbsCacheHub = class {
|
|
|
410
227
|
capacity: 1e3,
|
|
411
228
|
defaultExpire: 24 * 60 * 60 * 1e3,
|
|
412
229
|
maxMemorySize: 100 * 1024 * 1024,
|
|
413
|
-
|
|
414
|
-
cleanupInterval: 60 * 1e3,
|
|
415
|
-
compression: false
|
|
230
|
+
cleanupInterval: 60 * 1e3
|
|
416
231
|
};
|
|
417
|
-
// 获取缓存
|
|
418
232
|
getCache(name, options = {}) {
|
|
419
233
|
if (!this.caches.has(name)) {
|
|
420
|
-
|
|
421
|
-
this.caches.set(name, new CnbsLruCache(cacheOptions));
|
|
234
|
+
this.caches.set(name, new CnbsLruCache({ ...this.defaultOptions, ...options }));
|
|
422
235
|
}
|
|
423
236
|
return this.caches.get(name);
|
|
424
237
|
}
|
|
425
|
-
// 删除缓存
|
|
426
238
|
removeCache(name) {
|
|
427
239
|
const cache = this.caches.get(name);
|
|
428
240
|
if (cache) {
|
|
@@ -430,11 +242,9 @@ var CnbsCacheHub = class {
|
|
|
430
242
|
this.caches.delete(name);
|
|
431
243
|
}
|
|
432
244
|
}
|
|
433
|
-
// 清空所有缓存
|
|
434
245
|
flushAll() {
|
|
435
246
|
this.caches.forEach((cache) => cache.flush());
|
|
436
247
|
}
|
|
437
|
-
// 获取所有缓存统计
|
|
438
248
|
getAllStats() {
|
|
439
249
|
const stats = {};
|
|
440
250
|
this.caches.forEach((cache, name) => {
|
|
@@ -442,34 +252,37 @@ var CnbsCacheHub = class {
|
|
|
442
252
|
});
|
|
443
253
|
return stats;
|
|
444
254
|
}
|
|
445
|
-
// 关闭所有缓存
|
|
446
255
|
closeAll() {
|
|
447
256
|
this.caches.forEach((cache) => cache.close());
|
|
448
257
|
this.caches.clear();
|
|
449
258
|
}
|
|
450
259
|
};
|
|
451
|
-
var
|
|
260
|
+
var _hub = null;
|
|
261
|
+
function getCnbsCacheHub() {
|
|
262
|
+
if (!_hub) _hub = new CnbsCacheHub();
|
|
263
|
+
return _hub;
|
|
264
|
+
}
|
|
265
|
+
var cnbsCacheHub = new Proxy({}, {
|
|
266
|
+
get(_target, prop) {
|
|
267
|
+
return getCnbsCacheHub()[prop];
|
|
268
|
+
}
|
|
269
|
+
});
|
|
452
270
|
var CacheKeyGenerator = class {
|
|
453
|
-
// 生成搜索缓存键
|
|
454
271
|
static generateSearchKey(keyword, pageNum = 1, pageSize = 10) {
|
|
455
272
|
return `search_${keyword.toLowerCase()}_${pageNum}_${pageSize}`;
|
|
456
273
|
}
|
|
457
|
-
// 生成节点缓存键
|
|
458
274
|
static generateNodeKey(category, parentId) {
|
|
459
|
-
return `node_${category}_${parentId
|
|
275
|
+
return `node_${category}_${parentId ?? "root"}`;
|
|
460
276
|
}
|
|
461
|
-
// 生成指标缓存键
|
|
462
277
|
static generateMetricKey(setId, name) {
|
|
463
|
-
return `metric_${setId}_${name
|
|
278
|
+
return `metric_${setId}_${name ?? "all"}`;
|
|
464
279
|
}
|
|
465
|
-
// 生成数据系列缓存键
|
|
466
280
|
static generateSeriesKey(setId, metricIds, periods, areas) {
|
|
467
|
-
const metricKey = metricIds.sort().join("_");
|
|
468
|
-
const periodKey = periods.sort().join("_");
|
|
281
|
+
const metricKey = [...metricIds].sort().join("_");
|
|
282
|
+
const periodKey = [...periods].sort().join("_");
|
|
469
283
|
const areaKey = areas ? areas.map((a) => a.code).sort().join("_") : "000000000000";
|
|
470
284
|
return `series_${setId}_${metricKey}_${periodKey}_${areaKey}`;
|
|
471
285
|
}
|
|
472
|
-
// 生成数据源缓存键
|
|
473
286
|
static generateDataSourceKey(source, params) {
|
|
474
287
|
const sortedParams = Object.keys(params).sort().map((key) => `${key}=${params[key]}`).join("&");
|
|
475
288
|
return `datasource_${source}_${sortedParams}`;
|
|
@@ -827,11 +640,11 @@ var CnbsBoundaryHandler = class {
|
|
|
827
640
|
return array[index];
|
|
828
641
|
}
|
|
829
642
|
// 检查对象属性
|
|
830
|
-
static safePropertyAccess(obj,
|
|
643
|
+
static safePropertyAccess(obj, path, defaultValue) {
|
|
831
644
|
if (!obj) {
|
|
832
645
|
return defaultValue;
|
|
833
646
|
}
|
|
834
|
-
const parts =
|
|
647
|
+
const parts = path.split(".");
|
|
835
648
|
let current = obj;
|
|
836
649
|
for (const part of parts) {
|
|
837
650
|
if (current[part] === void 0) {
|