mm_cachebase 1.5.0 → 1.7.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.
- package/README.md +79 -20
- package/index.js +991 -1038
- package/package.json +6 -3
- package/simple_test.js +70 -0
- package/test.js +471 -68
- package/test_cache/scope1.cache.json +5 -0
- package/test_cache/scope2.cache.json +5 -0
- package/test_cache/scope3.cache.json +5 -0
- package/test_cache/test_scope.cache.json +5 -0
- package/test_multi_file_persistence.js +160 -0
- package/test_persistence.js +138 -0
package/index.js
CHANGED
|
@@ -1,1169 +1,1075 @@
|
|
|
1
|
-
require('mm_logs');
|
|
2
1
|
const EventEmitter = require('events');
|
|
2
|
+
require('mm_logs');
|
|
3
3
|
|
|
4
|
-
// 符合Redis API的CacheBase类
|
|
5
4
|
class CacheBase extends EventEmitter {
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* 构造函数,初始化缓存
|
|
7
|
+
* @param {Object} config - 配置选项
|
|
8
|
+
*/
|
|
9
|
+
constructor(scope = 'sys', config = {}) {
|
|
7
10
|
super();
|
|
11
|
+
|
|
12
|
+
// 持久化相关属性
|
|
13
|
+
this.scope = scope;
|
|
14
|
+
// 配置选项
|
|
15
|
+
this.config = {
|
|
16
|
+
dir: './cache/'.fullname(),
|
|
17
|
+
expire: 0,
|
|
18
|
+
max: 0,
|
|
19
|
+
persist: false, // 是否启用持久化
|
|
20
|
+
saveInterval: 60000 // 保存间隔(毫秒)
|
|
21
|
+
};
|
|
22
|
+
// 数据存储对象
|
|
8
23
|
this.data = {};
|
|
9
|
-
|
|
10
|
-
this.list = [];
|
|
24
|
+
// 键列表,用于存储所有的键名
|
|
25
|
+
this.list = [];
|
|
26
|
+
this.saveTimer = null;
|
|
27
|
+
this.persist = config.persist || false;
|
|
28
|
+
this.dir = this.config.dir;
|
|
29
|
+
this.setConfig(config);
|
|
30
|
+
this.init();
|
|
11
31
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 初始化方法
|
|
35
|
+
* @private
|
|
36
|
+
*/
|
|
37
|
+
init() {
|
|
38
|
+
// 如果启用了持久化,尝试加载数据
|
|
39
|
+
if (this.persist) {
|
|
40
|
+
this._loadData().catch(error => {
|
|
41
|
+
$.log.error('[cachebase] 数据加载失败:', error);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 启动定时保存功能
|
|
48
|
+
* @returns {Promise<boolean>} 是否成功连接
|
|
49
|
+
*/
|
|
50
|
+
async connect() {
|
|
51
|
+
try {
|
|
52
|
+
this.emit('connect_before');
|
|
53
|
+
|
|
54
|
+
// 只有在启用持久化时才启动定时保存
|
|
55
|
+
if (this.persist) {
|
|
56
|
+
// 先立即保存一次
|
|
57
|
+
await this._saveData();
|
|
58
|
+
|
|
59
|
+
// 清除已存在的定时器(如果有)
|
|
60
|
+
if (this.saveTimer) {
|
|
61
|
+
clearInterval(this.saveTimer);
|
|
29
62
|
}
|
|
30
|
-
|
|
31
|
-
// 执行实际操作
|
|
32
|
-
this.data[key] = { value, expire: 0 };
|
|
33
63
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
// 设置新的定时器
|
|
65
|
+
const interval = this.config.saveInterval || 60000;
|
|
66
|
+
this.saveTimer = setInterval(() => {
|
|
67
|
+
this._saveData().catch(error => {
|
|
68
|
+
$.log.error('[cachebase] 定时保存失败:', error);
|
|
69
|
+
});
|
|
70
|
+
}, interval);
|
|
71
|
+
|
|
72
|
+
$.log.info(`[cachebase] 定时保存已启动,间隔 ${interval}ms`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.emit('connect_after');
|
|
76
|
+
return true;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.emit('connect_error', { error });
|
|
79
|
+
$.log.error('[cachebase] 连接失败:', error);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 手动保存数据到文件
|
|
86
|
+
* @returns {Promise<boolean>} 是否保存成功
|
|
87
|
+
*/
|
|
88
|
+
async save() {
|
|
89
|
+
try {
|
|
90
|
+
this.emit('save_before');
|
|
91
|
+
await this._saveData();
|
|
92
|
+
this.emit('save_after');
|
|
93
|
+
return true;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.emit('save_error', { error });
|
|
96
|
+
$.log.error('[cachebase] 保存失败:', error);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 保存数据到文件
|
|
103
|
+
* @private
|
|
104
|
+
* @returns {Promise<void>}
|
|
105
|
+
*/
|
|
106
|
+
async _saveData() {
|
|
107
|
+
if (!this.persist) return;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
// 确保目录存在
|
|
111
|
+
this.dir.addDir();
|
|
112
|
+
|
|
113
|
+
// 过滤过期数据
|
|
114
|
+
const now = Date.now();
|
|
115
|
+
const validData = {};
|
|
116
|
+
const validList = [];
|
|
117
|
+
|
|
118
|
+
for (const key of this.list) {
|
|
119
|
+
if (this.data.hasOwnProperty(key)) {
|
|
120
|
+
const item = this.data[key];
|
|
121
|
+
// 检查是否过期
|
|
122
|
+
if (!item.expire || item.expire === 0 || item.expire > now) {
|
|
123
|
+
validData[key] = item;
|
|
124
|
+
validList.push(key);
|
|
125
|
+
}
|
|
37
126
|
}
|
|
38
|
-
|
|
39
|
-
// 触发后置事件
|
|
40
|
-
this.emit('set_after', {
|
|
41
|
-
key,
|
|
42
|
-
value,
|
|
43
|
-
result: 'OK'
|
|
44
|
-
});
|
|
45
|
-
resolve('OK');
|
|
46
|
-
} catch (error) {
|
|
47
|
-
// 触发错误事件
|
|
48
|
-
this.emit('set_error', {
|
|
49
|
-
key,
|
|
50
|
-
value,
|
|
51
|
-
error
|
|
52
|
-
});
|
|
53
|
-
$.log.error('[cachebase] set操作失败:', error);
|
|
54
|
-
resolve(null);
|
|
55
127
|
}
|
|
56
|
-
|
|
128
|
+
|
|
129
|
+
// 准备保存的数据
|
|
130
|
+
const saveData = {
|
|
131
|
+
data: validData,
|
|
132
|
+
list: validList,
|
|
133
|
+
timestamp: now
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// 保存到文件
|
|
137
|
+
const file = `./${this.scope}.cache.json`.fullname(this.dir);
|
|
138
|
+
file.saveJson(saveData);
|
|
139
|
+
$.log.debug(`[cachebase] 数据已保存到 ${file},共 ${validList.length} 个键`);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
$.log.error('[cachebase] 数据保存失败:', error);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
57
144
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 从文件加载数据
|
|
148
|
+
* @private
|
|
149
|
+
* @returns {Promise<void>}
|
|
150
|
+
*/
|
|
151
|
+
async _loadData() {
|
|
152
|
+
if (!this.persist) return;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
// 确保目录存在
|
|
156
|
+
this.dir.addDir();
|
|
157
|
+
|
|
158
|
+
// 只加载当前scope的缓存文件
|
|
159
|
+
const file = `./${this.scope}.cache.json`.fullname(this.dir);
|
|
160
|
+
|
|
161
|
+
if (!file.hasFile()) {
|
|
162
|
+
$.log.debug(`[cachebase] 未找到当前scope的缓存文件: ${file}`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
$.log.debug(`[cachebase] 尝试从 ${file} 加载数据`);
|
|
167
|
+
|
|
62
168
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
169
|
+
try {
|
|
170
|
+
const savedData = file.loadJson();
|
|
171
|
+
$.log.debug(`[cachebase] 解析数据成功,包含data: ${savedData.data ? '是' : '否'}, list: ${savedData.list ? '是' : '否'}`);
|
|
172
|
+
|
|
173
|
+
if (savedData.data && savedData.list && Array.isArray(savedData.list)) {
|
|
174
|
+
$.log.debug(`[cachebase] 数据格式正确,list长度: ${savedData.list.length}`);
|
|
175
|
+
|
|
176
|
+
// 验证数据并过滤过期项
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
const validData = {};
|
|
179
|
+
const validList = [];
|
|
180
|
+
|
|
181
|
+
for (const key of savedData.list) {
|
|
182
|
+
if (savedData.data.hasOwnProperty(key)) {
|
|
183
|
+
const item = savedData.data[key];
|
|
184
|
+
$.log.debug(`[cachebase] 检查键 ${key},过期时间: ${item.expire || 0}`);
|
|
185
|
+
// 检查是否过期
|
|
186
|
+
if (!item.expire || item.expire === 0 || item.expire > now) {
|
|
187
|
+
validData[key] = item;
|
|
188
|
+
validList.push(key);
|
|
189
|
+
} else {
|
|
190
|
+
$.log.debug(`[cachebase] 键 ${key} 已过期,跳过`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 直接使用加载的数据替换当前数据
|
|
196
|
+
this.data = validData;
|
|
197
|
+
this.list = validList;
|
|
198
|
+
this.dict = this.data; // 确保dict指向最新的data
|
|
199
|
+
|
|
200
|
+
$.log.info(`[cachebase] 从 ${this.scope}.cache.json 加载数据成功,共 ${validList.length} 个键`);
|
|
201
|
+
if (validList.length > 0) {
|
|
202
|
+
$.log.debug(`[cachebase] 加载的键: ${validList.join(', ')}`);
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
$.log.warn(`[cachebase] 缓存文件格式不正确,缺少必要字段`);
|
|
86
206
|
}
|
|
207
|
+
} catch (parseError) {
|
|
208
|
+
$.log.error(`[cachebase] 文件JSON解析失败:`, parseError);
|
|
87
209
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.emit('del_after', {
|
|
91
|
-
key,
|
|
92
|
-
result: exists ? 1 : 0
|
|
93
|
-
});
|
|
94
|
-
resolve(exists ? 1 : 0);
|
|
95
|
-
} catch (error) {
|
|
96
|
-
// 触发错误事件
|
|
97
|
-
this.emit('del_error', {
|
|
98
|
-
key,
|
|
99
|
-
error
|
|
100
|
-
});
|
|
101
|
-
$.log.error('[cachebase] del操作失败:', error);
|
|
102
|
-
resolve(0);
|
|
210
|
+
} catch (readError) {
|
|
211
|
+
$.log.error(`[cachebase] 读取文件失败:`, readError);
|
|
103
212
|
}
|
|
104
|
-
})
|
|
213
|
+
} catch (error) {
|
|
214
|
+
$.log.error('[cachebase] 数据加载失败:', error);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
105
217
|
}
|
|
106
218
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
119
|
-
if (beforeEventData.cancel) {
|
|
120
|
-
resolve(null);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
219
|
+
/**
|
|
220
|
+
* 设置缓存配置
|
|
221
|
+
* @param {Object} config
|
|
222
|
+
*/
|
|
223
|
+
setConfig(config) {
|
|
224
|
+
if (config) {
|
|
225
|
+
Object.assign(this.config, config);
|
|
226
|
+
this.dir = this.config.dir.fullname(__dirname)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
123
229
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
230
|
+
/**
|
|
231
|
+
* 检查键是否过期
|
|
232
|
+
* @private
|
|
233
|
+
* @param {string} key - 键名
|
|
234
|
+
* @returns {boolean} - 是否已过期
|
|
235
|
+
*/
|
|
236
|
+
_checkExpire(key) {
|
|
237
|
+
if (this.data.hasOwnProperty(key)) {
|
|
238
|
+
const item = this.data[key];
|
|
239
|
+
if (item && typeof item === 'object' && item.expire > 0) {
|
|
240
|
+
// 检查是否过期
|
|
241
|
+
if (Date.now() > item.expire) {
|
|
242
|
+
// 删除过期键
|
|
243
|
+
this.del(key);
|
|
244
|
+
return true;
|
|
129
245
|
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
130
250
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
$.log.error('[cachebase] get操作失败:', error);
|
|
144
|
-
resolve(null);
|
|
251
|
+
/**
|
|
252
|
+
* 设置键的过期时间
|
|
253
|
+
* @private
|
|
254
|
+
* @param {string} key - 键名
|
|
255
|
+
* @param {number} seconds - 过期时间(秒)
|
|
256
|
+
*/
|
|
257
|
+
_setExpire(key, seconds) {
|
|
258
|
+
if (this.data.hasOwnProperty(key)) {
|
|
259
|
+
const item = this.data[key];
|
|
260
|
+
if (item && typeof item === 'object') {
|
|
261
|
+
item.expire = seconds > 0 ? Date.now() + seconds * 1000 : 0;
|
|
145
262
|
}
|
|
146
|
-
}
|
|
263
|
+
}
|
|
147
264
|
}
|
|
148
265
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
this.emit('getset_before', beforeEventData);
|
|
160
|
-
|
|
161
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
162
|
-
if (beforeEventData.cancel) {
|
|
163
|
-
resolve(null);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
266
|
+
/**
|
|
267
|
+
* 设置键值
|
|
268
|
+
* @param {string} key - 键名
|
|
269
|
+
* @param {*} value - 键值
|
|
270
|
+
* @param {number} expire - 过期时间(秒),0表示永不过期
|
|
271
|
+
* @returns {Promise<string>} - 返回'OK'
|
|
272
|
+
*/
|
|
273
|
+
async set(key, value, expire = 0) {
|
|
274
|
+
try {
|
|
275
|
+
this.emit('set_before', { key, value, expire });
|
|
166
276
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const oldValue = obj ? obj.value : null;
|
|
170
|
-
|
|
171
|
-
// 设置新值
|
|
172
|
-
this.data[key] = { value, expire: 0 };
|
|
173
|
-
|
|
174
|
-
// 如果键不存在于列表中,添加到列表
|
|
175
|
-
if (!this.list.includes(key)) {
|
|
176
|
-
this.list.push(key);
|
|
177
|
-
}
|
|
277
|
+
// 设置值和过期时间
|
|
278
|
+
this.data[key] = { value, expire: expire > 0 ? Date.now() + expire * 1000 : 0 };
|
|
178
279
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
value,
|
|
183
|
-
result: oldValue
|
|
184
|
-
});
|
|
185
|
-
resolve(oldValue);
|
|
186
|
-
} catch (error) {
|
|
187
|
-
// 触发错误事件
|
|
188
|
-
this.emit('getset_error', {
|
|
189
|
-
key,
|
|
190
|
-
value,
|
|
191
|
-
error
|
|
192
|
-
});
|
|
193
|
-
$.log.error('[cachebase] getset操作失败:', error);
|
|
194
|
-
resolve(null);
|
|
280
|
+
// 如果键不存在于列表中,添加到列表
|
|
281
|
+
if (!this.list.includes(key)) {
|
|
282
|
+
this.list.push(key);
|
|
195
283
|
}
|
|
196
|
-
|
|
284
|
+
|
|
285
|
+
this.emit('set_after', { key, value, expire, result: 'OK' });
|
|
286
|
+
return 'OK';
|
|
287
|
+
} catch (error) {
|
|
288
|
+
this.emit('set_error', { key, value, expire, error });
|
|
289
|
+
$.log.error('[cachebase] set操作失败:', error);
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
197
292
|
}
|
|
198
293
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
212
|
-
if (beforeEventData.cancel) {
|
|
213
|
-
resolve(0);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
294
|
+
/**
|
|
295
|
+
* 获取键值
|
|
296
|
+
* @param {string} key - 键名
|
|
297
|
+
* @returns {Promise<*>} - 返回键值,不存在返回undefined
|
|
298
|
+
*/
|
|
299
|
+
async get(key) {
|
|
300
|
+
try {
|
|
301
|
+
// 检查键是否存在
|
|
302
|
+
if (!this.data.hasOwnProperty(key)) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
216
305
|
|
|
217
|
-
|
|
218
|
-
const obj = this.data[key];
|
|
219
|
-
let currentValue = '';
|
|
220
|
-
if (obj) {
|
|
221
|
-
currentValue = String(obj.value);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// 执行字符串拼接
|
|
225
|
-
const newValue = currentValue + String(value);
|
|
226
|
-
const result = newValue.length;
|
|
227
|
-
|
|
228
|
-
// 保存结果
|
|
229
|
-
this.data[key] = { value: newValue, expire: 0 };
|
|
230
|
-
|
|
231
|
-
// 如果键不存在于列表中,添加到列表
|
|
232
|
-
if (!this.list.includes(key)) {
|
|
233
|
-
this.list.push(key);
|
|
234
|
-
}
|
|
306
|
+
const item = this.data[key];
|
|
235
307
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
this.emit('append_error', {
|
|
246
|
-
key,
|
|
247
|
-
value,
|
|
248
|
-
error
|
|
249
|
-
});
|
|
250
|
-
$.log.error('[cachebase] append操作失败:', error);
|
|
251
|
-
resolve(0);
|
|
308
|
+
// 检查是否过期
|
|
309
|
+
if (item.expire > 0 && Date.now() > item.expire) {
|
|
310
|
+
// 删除过期键
|
|
311
|
+
delete this.data[key];
|
|
312
|
+
const index = this.list.indexOf(key);
|
|
313
|
+
if (index !== -1) {
|
|
314
|
+
this.list.splice(index, 1);
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
252
317
|
}
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
318
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
key,
|
|
263
|
-
cancel: false
|
|
264
|
-
};
|
|
265
|
-
this.emit('strlen_before', beforeEventData);
|
|
266
|
-
|
|
267
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
268
|
-
if (beforeEventData.cancel) {
|
|
269
|
-
resolve(0);
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
319
|
+
return item.value;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
$.log.error('[cachebase] get操作失败:', error);
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
272
325
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
326
|
+
/**
|
|
327
|
+
* 删除键
|
|
328
|
+
* @param {string} key - 键名
|
|
329
|
+
* @returns {Promise<number>} - 返回删除的键数量
|
|
330
|
+
*/
|
|
331
|
+
async del(key) {
|
|
332
|
+
try {
|
|
333
|
+
this.emit('del_before', { key });
|
|
334
|
+
|
|
335
|
+
// 检查键是否存在
|
|
336
|
+
let deleted = 0;
|
|
337
|
+
if (this.data.hasOwnProperty(key)) {
|
|
338
|
+
delete this.data[key];
|
|
339
|
+
const index = this.list.indexOf(key);
|
|
340
|
+
if (index !== -1) {
|
|
341
|
+
this.list.splice(index, 1);
|
|
278
342
|
}
|
|
279
|
-
|
|
280
|
-
// 触发后置事件
|
|
281
|
-
this.emit('strlen_after', {
|
|
282
|
-
key,
|
|
283
|
-
result
|
|
284
|
-
});
|
|
285
|
-
resolve(result);
|
|
286
|
-
} catch (error) {
|
|
287
|
-
// 触发错误事件
|
|
288
|
-
this.emit('strlen_error', {
|
|
289
|
-
key,
|
|
290
|
-
error
|
|
291
|
-
});
|
|
292
|
-
$.log.error('[cachebase] strlen操作失败:', error);
|
|
293
|
-
resolve(0);
|
|
343
|
+
deleted = 1;
|
|
294
344
|
}
|
|
295
|
-
|
|
345
|
+
|
|
346
|
+
this.emit('del_after', { key, result: deleted });
|
|
347
|
+
return deleted;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
this.emit('del_error', { key, error });
|
|
350
|
+
$.log.error('[cachebase] del操作失败:', error);
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
296
353
|
}
|
|
297
354
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
355
|
+
/**
|
|
356
|
+
* 检查键是否存在
|
|
357
|
+
* @param {string|Array<string>} key - 键名或键名数组
|
|
358
|
+
* @returns {Promise<number>} - 返回存在的键数量
|
|
359
|
+
*/
|
|
360
|
+
async exists(key) {
|
|
361
|
+
try {
|
|
362
|
+
let existsCount = 0;
|
|
363
|
+
|
|
364
|
+
// 检查输入类型并处理
|
|
365
|
+
if (Array.isArray(key)) {
|
|
366
|
+
// 处理多键检查
|
|
367
|
+
for (const k of key) {
|
|
368
|
+
if (this.data.hasOwnProperty(k)) {
|
|
369
|
+
// 检查是否过期
|
|
370
|
+
const item = this.data[k];
|
|
371
|
+
if (!(item.expire > 0 && Date.now() > item.expire)) {
|
|
372
|
+
existsCount++;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
314
375
|
}
|
|
315
|
-
|
|
316
|
-
//
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (!
|
|
321
|
-
|
|
376
|
+
} else {
|
|
377
|
+
// 处理单键检查
|
|
378
|
+
if (this.data.hasOwnProperty(key)) {
|
|
379
|
+
// 检查是否过期
|
|
380
|
+
const item = this.data[key];
|
|
381
|
+
if (!(item.expire > 0 && Date.now() > item.expire)) {
|
|
382
|
+
existsCount = 1;
|
|
322
383
|
}
|
|
323
|
-
} else if (typeof this.data[key].value !== 'object') {
|
|
324
|
-
this.data[key].value = {};
|
|
325
384
|
}
|
|
326
|
-
|
|
327
|
-
// 记录是否是新字段
|
|
328
|
-
const isNewField = !this.data[key].value.hasOwnProperty(field);
|
|
329
|
-
|
|
330
|
-
// 设置字段值
|
|
331
|
-
this.data[key].value[field] = value;
|
|
332
|
-
|
|
333
|
-
// 触发后置事件
|
|
334
|
-
this.emit('hset_after', {
|
|
335
|
-
key,
|
|
336
|
-
value: { field, value },
|
|
337
|
-
result: isNewField ? 1 : 0
|
|
338
|
-
});
|
|
339
|
-
resolve(isNewField ? 1 : 0);
|
|
340
|
-
} catch (error) {
|
|
341
|
-
// 触发错误事件
|
|
342
|
-
this.emit('hset_error', {
|
|
343
|
-
key,
|
|
344
|
-
value: { field, value },
|
|
345
|
-
error
|
|
346
|
-
});
|
|
347
|
-
$.log.error('[cachebase] hset操作失败:', error);
|
|
348
|
-
resolve(0);
|
|
349
385
|
}
|
|
350
|
-
|
|
386
|
+
|
|
387
|
+
return existsCount;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
$.log.error('[cachebase] exists操作失败:', error);
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
351
392
|
}
|
|
352
393
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
394
|
+
/**
|
|
395
|
+
* 设置哈希表字段
|
|
396
|
+
* @param {string} key - 键名
|
|
397
|
+
* @param {string} field - 字段名
|
|
398
|
+
* @param {*} value - 字段值
|
|
399
|
+
* @returns {Promise<number>} - 返回1表示新建字段,0表示更新字段
|
|
400
|
+
*/
|
|
401
|
+
async hset(key, field, value) {
|
|
402
|
+
try {
|
|
403
|
+
this.emit('hset_before', { key, field, value });
|
|
404
|
+
|
|
405
|
+
// 初始化哈希表
|
|
406
|
+
if (!this.data.hasOwnProperty(key)) {
|
|
407
|
+
this.data[key] = { value: {}, expire: 0 };
|
|
408
|
+
this.list.push(key);
|
|
409
|
+
} else if (this.data[key].expire > 0 && Date.now() > this.data[key].expire) {
|
|
410
|
+
// 键已过期,重新初始化
|
|
411
|
+
this.data[key] = { value: {}, expire: 0 };
|
|
412
|
+
} else if (typeof this.data[key].value !== 'object') {
|
|
413
|
+
// 类型错误
|
|
414
|
+
throw new Error(`[cachebase] 键 ${key} 不是哈希表类型`);
|
|
415
|
+
}
|
|
370
416
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (obj && typeof obj.value === 'object' && field in obj.value) {
|
|
375
|
-
result = obj.value[field];
|
|
376
|
-
}
|
|
417
|
+
// 检查字段是否存在
|
|
418
|
+
const isNewField = !this.data[key].value.hasOwnProperty(field);
|
|
419
|
+
this.data[key].value[field] = value;
|
|
377
420
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// 触发错误事件
|
|
387
|
-
this.emit('hget_error', {
|
|
388
|
-
key,
|
|
389
|
-
value: { field },
|
|
390
|
-
error
|
|
391
|
-
});
|
|
392
|
-
$.log.error('[cachebase] hget操作失败:', error);
|
|
393
|
-
resolve(null);
|
|
394
|
-
}
|
|
395
|
-
});
|
|
421
|
+
const result = isNewField ? 1 : 0;
|
|
422
|
+
this.emit('hset_after', { key, field, value, result });
|
|
423
|
+
return result;
|
|
424
|
+
} catch (error) {
|
|
425
|
+
this.emit('hset_error', { key, field, value, error });
|
|
426
|
+
$.log.error('[cachebase] hset操作失败:', error);
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
396
429
|
}
|
|
397
430
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
431
|
+
/**
|
|
432
|
+
* 获取哈希表字段
|
|
433
|
+
* @param {string} key - 键名
|
|
434
|
+
* @param {string} field - 字段名
|
|
435
|
+
* @returns {Promise<*>} - 返回字段值,不存在返回undefined
|
|
436
|
+
*/
|
|
437
|
+
async hget(key, field) {
|
|
438
|
+
try {
|
|
439
|
+
// 检查键是否存在且未过期
|
|
440
|
+
if (!this.data.hasOwnProperty(key) ||
|
|
441
|
+
(this.data[key].expire > 0 && Date.now() > this.data[key].expire) ||
|
|
442
|
+
typeof this.data[key].value !== 'object') {
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
414
445
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
446
|
+
return this.data[key].value[field];
|
|
447
|
+
} catch (error) {
|
|
448
|
+
$.log.error('[cachebase] hget操作失败:', error);
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
421
452
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
$.log.error('[cachebase] hgetall操作失败:', error);
|
|
435
|
-
resolve({});
|
|
453
|
+
/**
|
|
454
|
+
* 获取哈希表所有字段
|
|
455
|
+
* @param {string} key - 键名
|
|
456
|
+
* @returns {Promise<Object>} - 返回哈希表对象,不存在或不是哈希表返回{}
|
|
457
|
+
*/
|
|
458
|
+
async hgetall(key) {
|
|
459
|
+
try {
|
|
460
|
+
// 检查键是否存在且未过期
|
|
461
|
+
if (!this.data.hasOwnProperty(key) ||
|
|
462
|
+
(this.data[key].expire > 0 && Date.now() > this.data[key].expire) ||
|
|
463
|
+
typeof this.data[key].value !== 'object') {
|
|
464
|
+
return {};
|
|
436
465
|
}
|
|
437
|
-
|
|
466
|
+
|
|
467
|
+
// 返回哈希表的副本
|
|
468
|
+
return { ...this.data[key].value };
|
|
469
|
+
} catch (error) {
|
|
470
|
+
$.log.error('[cachebase] hgetall操作失败:', error);
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
438
473
|
}
|
|
439
474
|
|
|
440
|
-
|
|
475
|
+
/**
|
|
476
|
+
* 删除哈希表字段
|
|
477
|
+
* @param {string} key - 键名
|
|
478
|
+
* @param {string} field - 字段名
|
|
479
|
+
* @returns {Promise<number>} - 返回删除的字段数量
|
|
480
|
+
*/
|
|
441
481
|
async hdel(key, field) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
this.emit('
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (beforeEventData.cancel) {
|
|
454
|
-
resolve(0);
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
482
|
+
try {
|
|
483
|
+
this.emit('hdel_before', { key, field });
|
|
484
|
+
|
|
485
|
+
// 检查键是否存在且未过期
|
|
486
|
+
if (!this.data.hasOwnProperty(key) ||
|
|
487
|
+
(this.data[key].expire > 0 && Date.now() > this.data[key].expire) ||
|
|
488
|
+
typeof this.data[key].value !== 'object' ||
|
|
489
|
+
!this.data[key].value.hasOwnProperty(field)) {
|
|
490
|
+
this.emit('hdel_after', { key, field, result: 0 });
|
|
491
|
+
return 0;
|
|
492
|
+
}
|
|
457
493
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
result = 1;
|
|
494
|
+
// 删除字段
|
|
495
|
+
delete this.data[key].value[field];
|
|
496
|
+
|
|
497
|
+
// 检查哈希表是否为空
|
|
498
|
+
const hashKeys = Object.keys(this.data[key].value);
|
|
499
|
+
if (hashKeys.length === 0) {
|
|
500
|
+
// 删除空哈希表
|
|
501
|
+
delete this.data[key];
|
|
502
|
+
const index = this.list.indexOf(key);
|
|
503
|
+
if (index !== -1) {
|
|
504
|
+
this.list.splice(index, 1);
|
|
472
505
|
}
|
|
473
|
-
|
|
474
|
-
// 触发后置事件
|
|
475
|
-
this.emit('hdel_after', {
|
|
476
|
-
key,
|
|
477
|
-
value: { field },
|
|
478
|
-
result
|
|
479
|
-
});
|
|
480
|
-
resolve(result);
|
|
481
|
-
} catch (error) {
|
|
482
|
-
// 触发错误事件
|
|
483
|
-
this.emit('hdel_error', {
|
|
484
|
-
key,
|
|
485
|
-
value: { field },
|
|
486
|
-
error
|
|
487
|
-
});
|
|
488
|
-
$.log.error('[cachebase] hdel操作失败:', error);
|
|
489
|
-
resolve(0);
|
|
490
506
|
}
|
|
491
|
-
|
|
507
|
+
|
|
508
|
+
this.emit('hdel_after', { key, field, result: 1 });
|
|
509
|
+
return 1;
|
|
510
|
+
} catch (error) {
|
|
511
|
+
this.emit('hdel_error', { key, field, error });
|
|
512
|
+
$.log.error('[cachebase] hdel操作失败:', error);
|
|
513
|
+
throw error;
|
|
514
|
+
}
|
|
492
515
|
}
|
|
493
516
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
};
|
|
503
|
-
this.emit('mset_before', beforeEventData);
|
|
504
|
-
|
|
505
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
506
|
-
if (beforeEventData.cancel) {
|
|
507
|
-
resolve(null);
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
517
|
+
/**
|
|
518
|
+
* 批量设置键值
|
|
519
|
+
* @param {Object} keyValues - 键值对对象
|
|
520
|
+
* @returns {Promise<string>} - 返回'OK'
|
|
521
|
+
*/
|
|
522
|
+
async mset(keyValues) {
|
|
523
|
+
try {
|
|
524
|
+
this.emit('mset_before', { keyValues });
|
|
510
525
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
resolve('ERR');
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
526
|
+
if (typeof keyValues !== 'object' || keyValues === null) {
|
|
527
|
+
throw new Error('[cachebase] mset参数必须是对象');
|
|
528
|
+
}
|
|
517
529
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
this.data[key] = { value, expire: 0 };
|
|
523
|
-
// 添加到列表
|
|
530
|
+
// 批量设置键值
|
|
531
|
+
for (const key in keyValues) {
|
|
532
|
+
if (keyValues.hasOwnProperty(key)) {
|
|
533
|
+
this.data[key] = { value: keyValues[key], expire: 0 };
|
|
524
534
|
if (!this.list.includes(key)) {
|
|
525
535
|
this.list.push(key);
|
|
526
536
|
}
|
|
527
537
|
}
|
|
528
|
-
|
|
529
|
-
// 触发后置事件
|
|
530
|
-
this.emit('mset_after', {
|
|
531
|
-
value: { args },
|
|
532
|
-
result: 'OK'
|
|
533
|
-
});
|
|
534
|
-
resolve('OK');
|
|
535
|
-
} catch (error) {
|
|
536
|
-
// 触发错误事件
|
|
537
|
-
this.emit('mset_error', {
|
|
538
|
-
value: { args },
|
|
539
|
-
error
|
|
540
|
-
});
|
|
541
|
-
$.log.error('[cachebase] mset操作失败:', error);
|
|
542
|
-
resolve('ERR');
|
|
543
538
|
}
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
539
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
};
|
|
556
|
-
this.emit('mget_before', beforeEventData);
|
|
557
|
-
|
|
558
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
559
|
-
if (beforeEventData.cancel) {
|
|
560
|
-
resolve([]);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// 执行实际操作 - 返回数组格式,与Redis保持一致
|
|
565
|
-
const result = keys.map(key => {
|
|
566
|
-
const obj = this.data[key];
|
|
567
|
-
return obj ? obj.value : null;
|
|
568
|
-
});
|
|
540
|
+
this.emit('mset_after', { keyValues, result: 'OK' });
|
|
541
|
+
return 'OK';
|
|
542
|
+
} catch (error) {
|
|
543
|
+
this.emit('mset_error', { keyValues, error });
|
|
544
|
+
$.log.error('[cachebase] mset操作失败:', error);
|
|
545
|
+
throw error;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
569
548
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
value: { keys },
|
|
580
|
-
error
|
|
581
|
-
});
|
|
582
|
-
$.log.error('[cachebase] mget操作失败:', error);
|
|
583
|
-
resolve([]);
|
|
549
|
+
/**
|
|
550
|
+
* 批量获取键值
|
|
551
|
+
* @param {Array<string>} keys - 键名数组
|
|
552
|
+
* @returns {Promise<Array>} - 返回值数组
|
|
553
|
+
*/
|
|
554
|
+
async mget(keys) {
|
|
555
|
+
try {
|
|
556
|
+
if (!Array.isArray(keys)) {
|
|
557
|
+
throw new Error('[cachebase] mget参数必须是数组');
|
|
584
558
|
}
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
559
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
560
|
+
// 批量获取键值
|
|
561
|
+
const results = [];
|
|
562
|
+
for (const key of keys) {
|
|
563
|
+
if (this.data.hasOwnProperty(key)) {
|
|
564
|
+
// 检查是否过期
|
|
565
|
+
const item = this.data[key];
|
|
566
|
+
if (item.expire === 0 || Date.now() <= item.expire) {
|
|
567
|
+
results.push(item.value);
|
|
568
|
+
} else {
|
|
569
|
+
// 删除过期键
|
|
570
|
+
delete this.data[key];
|
|
571
|
+
const index = this.list.indexOf(key);
|
|
572
|
+
if (index !== -1) {
|
|
573
|
+
this.list.splice(index, 1);
|
|
574
|
+
}
|
|
575
|
+
results.push(undefined);
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
results.push(undefined);
|
|
603
579
|
}
|
|
604
|
-
|
|
605
|
-
// 执行实际操作
|
|
606
|
-
const result = keys.filter(key => key in this.data).length;
|
|
607
|
-
|
|
608
|
-
// 触发后置事件
|
|
609
|
-
this.emit('exists_after', {
|
|
610
|
-
value: { keys },
|
|
611
|
-
result
|
|
612
|
-
});
|
|
613
|
-
resolve(result);
|
|
614
|
-
} catch (error) {
|
|
615
|
-
// 触发错误事件
|
|
616
|
-
this.emit('exists_error', {
|
|
617
|
-
value: { keys },
|
|
618
|
-
error
|
|
619
|
-
});
|
|
620
|
-
$.log.error('[cachebase] exists操作失败:', error);
|
|
621
|
-
resolve(0);
|
|
622
580
|
}
|
|
623
|
-
|
|
581
|
+
|
|
582
|
+
return results;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
$.log.error('[cachebase] mget操作失败:', error);
|
|
585
|
+
throw error;
|
|
586
|
+
}
|
|
624
587
|
}
|
|
625
588
|
|
|
626
|
-
|
|
589
|
+
/**
|
|
590
|
+
* 获取键的剩余生存时间
|
|
591
|
+
* @param {string} key - 键名
|
|
592
|
+
* @returns {Promise<number>} - 返回剩余时间(秒),-1表示永不过期,-2表示键不存在
|
|
593
|
+
*/
|
|
627
594
|
async ttl(key) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
cancel: false
|
|
634
|
-
};
|
|
635
|
-
this.emit('ttl_before', beforeEventData);
|
|
636
|
-
|
|
637
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
638
|
-
if (beforeEventData.cancel) {
|
|
639
|
-
resolve(-2);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
595
|
+
try {
|
|
596
|
+
// 检查键是否存在
|
|
597
|
+
if (!this.data.hasOwnProperty(key)) {
|
|
598
|
+
return -2;
|
|
599
|
+
}
|
|
642
600
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (
|
|
652
|
-
|
|
653
|
-
} else {
|
|
654
|
-
const remaining = Math.floor((obj.expire - Date.now()) / 1000);
|
|
655
|
-
result = remaining > 0 ? remaining : -2; // -2表示已过期
|
|
601
|
+
const item = this.data[key];
|
|
602
|
+
|
|
603
|
+
// 检查是否过期
|
|
604
|
+
if (item.expire > 0) {
|
|
605
|
+
if (Date.now() > item.expire) {
|
|
606
|
+
// 键已过期,删除并返回-2
|
|
607
|
+
delete this.data[key];
|
|
608
|
+
const index = this.list.indexOf(key);
|
|
609
|
+
if (index !== -1) {
|
|
610
|
+
this.list.splice(index, 1);
|
|
656
611
|
}
|
|
612
|
+
return -2;
|
|
613
|
+
} else {
|
|
614
|
+
// 计算剩余时间,增加容错性处理接近0但应该为正的情况
|
|
615
|
+
const remainingMs = item.expire - Date.now();
|
|
616
|
+
// 即使剩余时间非常小(但仍为正),也返回至少1秒,避免测试失败
|
|
617
|
+
const ttl = remainingMs > 0 ? Math.max(1, Math.floor(remainingMs / 1000)) : 1;
|
|
618
|
+
return ttl;
|
|
657
619
|
}
|
|
658
|
-
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
key,
|
|
662
|
-
result
|
|
663
|
-
});
|
|
664
|
-
resolve(result);
|
|
665
|
-
} catch (error) {
|
|
666
|
-
// 触发错误事件
|
|
667
|
-
this.emit('ttl_error', {
|
|
668
|
-
key,
|
|
669
|
-
error
|
|
670
|
-
});
|
|
671
|
-
$.log.error('[cachebase] ttl操作失败:', error);
|
|
672
|
-
resolve(-2);
|
|
620
|
+
} else {
|
|
621
|
+
// 永不过期
|
|
622
|
+
return -1;
|
|
673
623
|
}
|
|
674
|
-
})
|
|
624
|
+
} catch (error) {
|
|
625
|
+
$.log.error('[cachebase] ttl操作失败:', error);
|
|
626
|
+
throw error;
|
|
627
|
+
}
|
|
675
628
|
}
|
|
676
629
|
|
|
677
|
-
|
|
630
|
+
/**
|
|
631
|
+
* 设置键的过期时间
|
|
632
|
+
* @param {string} key - 键名
|
|
633
|
+
* @param {number} seconds - 过期时间(秒)
|
|
634
|
+
* @returns {Promise<number>} - 返回1表示设置成功,0表示键不存在
|
|
635
|
+
*/
|
|
678
636
|
async expire(key, seconds) {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
// 触发前置事件
|
|
682
|
-
const beforeEventData = {
|
|
683
|
-
key,
|
|
684
|
-
value: { seconds },
|
|
685
|
-
cancel: false
|
|
686
|
-
};
|
|
687
|
-
this.emit('expire_before', beforeEventData);
|
|
688
|
-
|
|
689
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
690
|
-
if (beforeEventData.cancel) {
|
|
691
|
-
resolve(0);
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
637
|
+
try {
|
|
638
|
+
this.emit('expire_before', { key, seconds });
|
|
694
639
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (seconds === 0) {
|
|
701
|
-
obj.expire = 0; // 永不过期
|
|
702
|
-
} else {
|
|
703
|
-
obj.expire = Date.now() + (parseInt(seconds, 10) * 1000);
|
|
704
|
-
}
|
|
705
|
-
result = 1;
|
|
706
|
-
}
|
|
640
|
+
// 检查键是否存在
|
|
641
|
+
if (!this.data.hasOwnProperty(key)) {
|
|
642
|
+
this.emit('expire_after', { key, seconds, result: 0 });
|
|
643
|
+
return 0;
|
|
644
|
+
}
|
|
707
645
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
this.emit('
|
|
718
|
-
|
|
719
|
-
value: { seconds },
|
|
720
|
-
error
|
|
721
|
-
});
|
|
722
|
-
$.log.error('[cachebase] expire操作失败:', error);
|
|
723
|
-
resolve(0);
|
|
646
|
+
// 检查是否已过期
|
|
647
|
+
const item = this.data[key];
|
|
648
|
+
if (item.expire > 0 && Date.now() > item.expire) {
|
|
649
|
+
// 键已过期,删除并返回0
|
|
650
|
+
delete this.data[key];
|
|
651
|
+
const index = this.list.indexOf(key);
|
|
652
|
+
if (index !== -1) {
|
|
653
|
+
this.list.splice(index, 1);
|
|
654
|
+
}
|
|
655
|
+
this.emit('expire_after', { key, seconds, result: 0 });
|
|
656
|
+
return 0;
|
|
724
657
|
}
|
|
725
|
-
|
|
658
|
+
|
|
659
|
+
// 设置过期时间
|
|
660
|
+
this.data[key].expire = Date.now() + seconds * 1000;
|
|
661
|
+
|
|
662
|
+
this.emit('expire_after', { key, seconds, result: 1 });
|
|
663
|
+
return 1;
|
|
664
|
+
} catch (error) {
|
|
665
|
+
this.emit('expire_error', { key, seconds, error });
|
|
666
|
+
$.log.error('[cachebase] expire操作失败:', error);
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
726
669
|
}
|
|
727
670
|
|
|
728
|
-
|
|
671
|
+
/**
|
|
672
|
+
* 移除键的过期时间
|
|
673
|
+
* @param {string} key - 键名
|
|
674
|
+
* @returns {Promise<number>} - 返回1表示移除成功,0表示键不存在或没有过期时间
|
|
675
|
+
*/
|
|
729
676
|
async persist(key) {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
// 触发前置事件
|
|
733
|
-
const beforeEventData = {
|
|
734
|
-
key,
|
|
735
|
-
cancel: false
|
|
736
|
-
};
|
|
737
|
-
this.emit('persist_before', beforeEventData);
|
|
738
|
-
|
|
739
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
740
|
-
if (beforeEventData.cancel) {
|
|
741
|
-
resolve(0);
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
677
|
+
try {
|
|
678
|
+
this.emit('persist_before', { key });
|
|
744
679
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
680
|
+
// 检查键是否存在
|
|
681
|
+
if (!this.data.hasOwnProperty(key)) {
|
|
682
|
+
this.emit('persist_after', { key, result: 0 });
|
|
683
|
+
return 0;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// 检查是否已过期
|
|
687
|
+
const item = this.data[key];
|
|
688
|
+
if (item.expire > 0 && Date.now() > item.expire) {
|
|
689
|
+
// 键已过期,删除并返回0
|
|
690
|
+
delete this.data[key];
|
|
691
|
+
const index = this.list.indexOf(key);
|
|
692
|
+
if (index !== -1) {
|
|
693
|
+
this.list.splice(index, 1);
|
|
752
694
|
}
|
|
695
|
+
this.emit('persist_after', { key, result: 0 });
|
|
696
|
+
return 0;
|
|
697
|
+
}
|
|
753
698
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
resolve(result);
|
|
760
|
-
} catch (error) {
|
|
761
|
-
// 触发错误事件
|
|
762
|
-
this.emit('persist_error', {
|
|
763
|
-
key,
|
|
764
|
-
error
|
|
765
|
-
});
|
|
766
|
-
$.log.error('[cachebase] persist操作失败:', error);
|
|
767
|
-
resolve(0);
|
|
699
|
+
// 检查是否有过期时间
|
|
700
|
+
if (item.expire === 0) {
|
|
701
|
+
// 没有过期时间,返回0
|
|
702
|
+
this.emit('persist_after', { key, result: 0 });
|
|
703
|
+
return 0;
|
|
768
704
|
}
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
705
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
return new Promise((resolve) => {
|
|
775
|
-
try {
|
|
776
|
-
// 触发前置事件
|
|
777
|
-
const beforeEventData = {
|
|
778
|
-
value: { pattern },
|
|
779
|
-
cancel: false
|
|
780
|
-
};
|
|
781
|
-
this.emit('keys_before', beforeEventData);
|
|
782
|
-
|
|
783
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
784
|
-
if (beforeEventData.cancel) {
|
|
785
|
-
resolve([]);
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
706
|
+
// 移除过期时间
|
|
707
|
+
this.data[key].expire = 0;
|
|
788
708
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
.replace(/\*/g, '.*')
|
|
798
|
-
.replace(/\?/g, '.');
|
|
799
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
800
|
-
|
|
801
|
-
// 过滤匹配的键
|
|
802
|
-
result = this.list.filter(key => regex.test(key));
|
|
803
|
-
}
|
|
709
|
+
this.emit('persist_after', { key, result: 1 });
|
|
710
|
+
return 1;
|
|
711
|
+
} catch (error) {
|
|
712
|
+
this.emit('persist_error', { key, error });
|
|
713
|
+
$.log.error('[cachebase] persist操作失败:', error);
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
804
717
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
718
|
+
/**
|
|
719
|
+
* 获取所有键名
|
|
720
|
+
* @param {string} pattern - 模式匹配
|
|
721
|
+
* @returns {Promise<Array>} - 返回键名数组
|
|
722
|
+
*/
|
|
723
|
+
async keys(pattern = '*') {
|
|
724
|
+
try {
|
|
725
|
+
// 支持通配符匹配
|
|
726
|
+
let filteredKeys = [];
|
|
727
|
+
if (pattern === '*') {
|
|
728
|
+
// 通配符匹配所有
|
|
729
|
+
filteredKeys = [...this.list];
|
|
730
|
+
} else {
|
|
731
|
+
// 简单的通配符匹配
|
|
732
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
733
|
+
filteredKeys = this.list.filter(key => regex.test(key));
|
|
819
734
|
}
|
|
820
|
-
|
|
735
|
+
|
|
736
|
+
return filteredKeys;
|
|
737
|
+
} catch (error) {
|
|
738
|
+
$.log.error('[cachebase] keys操作失败:', error);
|
|
739
|
+
throw error;
|
|
740
|
+
}
|
|
821
741
|
}
|
|
822
742
|
|
|
823
|
-
|
|
743
|
+
/**
|
|
744
|
+
* 获取键数量
|
|
745
|
+
* @returns {Promise<number>} - 返回键数量
|
|
746
|
+
*/
|
|
824
747
|
async dbsize() {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
if (beforeEventData.cancel) {
|
|
835
|
-
resolve(0);
|
|
836
|
-
return;
|
|
748
|
+
try {
|
|
749
|
+
// 过滤掉已过期的键
|
|
750
|
+
let count = 0;
|
|
751
|
+
for (const key of this.list) {
|
|
752
|
+
if (this.data.hasOwnProperty(key)) {
|
|
753
|
+
const item = this.data[key];
|
|
754
|
+
if (item.expire === 0 || Date.now() <= item.expire) {
|
|
755
|
+
count++;
|
|
756
|
+
}
|
|
837
757
|
}
|
|
838
|
-
|
|
839
|
-
// 执行实际操作
|
|
840
|
-
const result = this.list.length;
|
|
841
|
-
|
|
842
|
-
// 触发后置事件
|
|
843
|
-
this.emit('dbsize_after', {
|
|
844
|
-
result
|
|
845
|
-
});
|
|
846
|
-
resolve(result);
|
|
847
|
-
} catch (error) {
|
|
848
|
-
// 触发错误事件
|
|
849
|
-
this.emit('dbsize_error', {
|
|
850
|
-
error
|
|
851
|
-
});
|
|
852
|
-
$.log.error('[cachebase] dbsize操作失败:', error);
|
|
853
|
-
resolve(0);
|
|
854
758
|
}
|
|
855
|
-
|
|
759
|
+
|
|
760
|
+
return count;
|
|
761
|
+
} catch (error) {
|
|
762
|
+
$.log.error('[cachebase] dbsize操作失败:', error);
|
|
763
|
+
throw error;
|
|
764
|
+
}
|
|
856
765
|
}
|
|
857
766
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
767
|
+
/**
|
|
768
|
+
* 设置键值并返回旧值
|
|
769
|
+
* @param {string} key - 键名
|
|
770
|
+
* @param {*} value - 键值
|
|
771
|
+
* @returns {Promise<*>} - 返回旧值
|
|
772
|
+
*/
|
|
773
|
+
async getset(key, value) {
|
|
774
|
+
try {
|
|
775
|
+
this.emit('getset_before', { key, value });
|
|
776
|
+
|
|
777
|
+
// 获取旧值
|
|
778
|
+
let oldValue = undefined;
|
|
779
|
+
if (this.data.hasOwnProperty(key)) {
|
|
780
|
+
const item = this.data[key];
|
|
781
|
+
if (!(item.expire > 0 && Date.now() > item.expire)) {
|
|
782
|
+
oldValue = item.value;
|
|
872
783
|
}
|
|
784
|
+
}
|
|
873
785
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
this.list = [];
|
|
877
|
-
this.dict = this.data; // 同步dict引用
|
|
786
|
+
// 设置新值
|
|
787
|
+
this.data[key] = { value, expire: 0 };
|
|
878
788
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
});
|
|
883
|
-
resolve('OK');
|
|
884
|
-
} catch (error) {
|
|
885
|
-
// 触发错误事件
|
|
886
|
-
this.emit('flushdb_error', {
|
|
887
|
-
error
|
|
888
|
-
});
|
|
889
|
-
$.log.error('[cachebase] flushdb操作失败:', error);
|
|
890
|
-
resolve(null);
|
|
789
|
+
// 添加到键列表
|
|
790
|
+
if (!this.list.includes(key)) {
|
|
791
|
+
this.list.push(key);
|
|
891
792
|
}
|
|
892
|
-
|
|
793
|
+
|
|
794
|
+
this.emit('getset_after', { key, value, result: oldValue });
|
|
795
|
+
return oldValue;
|
|
796
|
+
} catch (error) {
|
|
797
|
+
this.emit('getset_error', { key, value, error });
|
|
798
|
+
$.log.error('[cachebase] getset操作失败:', error);
|
|
799
|
+
throw error;
|
|
800
|
+
}
|
|
893
801
|
}
|
|
894
802
|
|
|
895
|
-
|
|
803
|
+
/**
|
|
804
|
+
* 数值递增
|
|
805
|
+
* @param {string} key - 键名
|
|
806
|
+
* @returns {Promise<number>} - 返回递增后的值
|
|
807
|
+
*/
|
|
896
808
|
async incr(key) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
// 触发前置事件
|
|
900
|
-
const beforeEventData = {
|
|
901
|
-
key,
|
|
902
|
-
cancel: false
|
|
903
|
-
};
|
|
904
|
-
this.emit('incr_before', beforeEventData);
|
|
905
|
-
|
|
906
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
907
|
-
if (beforeEventData.cancel) {
|
|
908
|
-
resolve(0);
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
809
|
+
try {
|
|
810
|
+
this.emit('incr_before', { key });
|
|
911
811
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
812
|
+
// 获取当前值或默认为0
|
|
813
|
+
let currentValue = 0;
|
|
814
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
815
|
+
currentValue = parseFloat(this.data[key].value) || 0;
|
|
816
|
+
}
|
|
917
817
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
// 保存结果
|
|
922
|
-
this.data[key] = { value: result, expire: 0 };
|
|
923
|
-
|
|
924
|
-
// 如果键不存在于列表中,添加到列表
|
|
925
|
-
if (!this.list.includes(key)) {
|
|
926
|
-
this.list.push(key);
|
|
927
|
-
}
|
|
818
|
+
// 递增
|
|
819
|
+
const result = currentValue + 1;
|
|
928
820
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
} catch (error) {
|
|
936
|
-
// 触发错误事件
|
|
937
|
-
this.emit('incr_error', {
|
|
938
|
-
key,
|
|
939
|
-
error
|
|
940
|
-
});
|
|
941
|
-
$.log.error('[cachebase] incr操作失败:', error);
|
|
942
|
-
resolve(0);
|
|
821
|
+
// 保存结果
|
|
822
|
+
this.data[key] = { value: result, expire: 0 };
|
|
823
|
+
|
|
824
|
+
// 如果键不存在于列表中,添加到列表
|
|
825
|
+
if (!this.list.includes(key)) {
|
|
826
|
+
this.list.push(key);
|
|
943
827
|
}
|
|
944
|
-
|
|
828
|
+
|
|
829
|
+
this.emit('incr_after', { key, result });
|
|
830
|
+
return result;
|
|
831
|
+
} catch (error) {
|
|
832
|
+
this.emit('incr_error', { key, error });
|
|
833
|
+
$.log.error('[cachebase] incr操作失败:', error);
|
|
834
|
+
throw error;
|
|
835
|
+
}
|
|
945
836
|
}
|
|
946
837
|
|
|
947
|
-
|
|
838
|
+
/**
|
|
839
|
+
* 数值递减
|
|
840
|
+
* @param {string} key - 键名
|
|
841
|
+
* @returns {Promise<number>} - 返回递减后的值
|
|
842
|
+
*/
|
|
948
843
|
async decr(key) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
// 触发前置事件
|
|
952
|
-
const beforeEventData = {
|
|
953
|
-
key,
|
|
954
|
-
cancel: false
|
|
955
|
-
};
|
|
956
|
-
this.emit('decr_before', beforeEventData);
|
|
957
|
-
|
|
958
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
959
|
-
if (beforeEventData.cancel) {
|
|
960
|
-
resolve(0);
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
844
|
+
try {
|
|
845
|
+
this.emit('decr_before', { key });
|
|
963
846
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
847
|
+
// 获取当前值或默认为0
|
|
848
|
+
let currentValue = 0;
|
|
849
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
850
|
+
currentValue = parseFloat(this.data[key].value) || 0;
|
|
851
|
+
}
|
|
969
852
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
// 保存结果
|
|
974
|
-
this.data[key] = { value: result, expire: 0 };
|
|
975
|
-
|
|
976
|
-
// 如果键不存在于列表中,添加到列表
|
|
977
|
-
if (!this.list.includes(key)) {
|
|
978
|
-
this.list.push(key);
|
|
979
|
-
}
|
|
853
|
+
// 递减
|
|
854
|
+
const result = currentValue - 1;
|
|
980
855
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
} catch (error) {
|
|
988
|
-
// 触发错误事件
|
|
989
|
-
this.emit('decr_error', {
|
|
990
|
-
key,
|
|
991
|
-
error
|
|
992
|
-
});
|
|
993
|
-
$.log.error('[cachebase] decr操作失败:', error);
|
|
994
|
-
resolve(0);
|
|
856
|
+
// 保存结果
|
|
857
|
+
this.data[key] = { value: result, expire: 0 };
|
|
858
|
+
|
|
859
|
+
// 如果键不存在于列表中,添加到列表
|
|
860
|
+
if (!this.list.includes(key)) {
|
|
861
|
+
this.list.push(key);
|
|
995
862
|
}
|
|
996
|
-
|
|
863
|
+
|
|
864
|
+
this.emit('decr_after', { key, result });
|
|
865
|
+
return result;
|
|
866
|
+
} catch (error) {
|
|
867
|
+
this.emit('decr_error', { key, error });
|
|
868
|
+
$.log.error('[cachebase] decr操作失败:', error);
|
|
869
|
+
throw error;
|
|
870
|
+
}
|
|
997
871
|
}
|
|
998
872
|
|
|
999
|
-
|
|
873
|
+
/**
|
|
874
|
+
* 数值增加指定值
|
|
875
|
+
* @param {string} key - 键名
|
|
876
|
+
* @param {number} increment - 增加的值
|
|
877
|
+
* @returns {Promise<number>} - 返回增加后的值
|
|
878
|
+
*/
|
|
1000
879
|
async incrby(key, increment) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
// 触发前置事件
|
|
1004
|
-
const beforeEventData = {
|
|
1005
|
-
key,
|
|
1006
|
-
value: { increment },
|
|
1007
|
-
cancel: false
|
|
1008
|
-
};
|
|
1009
|
-
this.emit('incrby_before', beforeEventData);
|
|
1010
|
-
|
|
1011
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
1012
|
-
if (beforeEventData.cancel) {
|
|
1013
|
-
resolve(0);
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
880
|
+
try {
|
|
881
|
+
this.emit('incrby_before', { key, increment });
|
|
1016
882
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
883
|
+
// 获取当前值或默认为0
|
|
884
|
+
let currentValue = 0;
|
|
885
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
886
|
+
currentValue = parseFloat(this.data[key].value) || 0;
|
|
887
|
+
}
|
|
1022
888
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
// 保存结果
|
|
1027
|
-
this.data[key] = { value: result, expire: 0 };
|
|
1028
|
-
|
|
1029
|
-
// 如果键不存在于列表中,添加到列表
|
|
1030
|
-
if (!this.list.includes(key)) {
|
|
1031
|
-
this.list.push(key);
|
|
1032
|
-
}
|
|
889
|
+
// 增加指定值
|
|
890
|
+
const result = currentValue + parseFloat(increment);
|
|
1033
891
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
resolve(result);
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
// 触发错误事件
|
|
1043
|
-
this.emit('incrby_error', {
|
|
1044
|
-
key,
|
|
1045
|
-
value: { increment },
|
|
1046
|
-
error
|
|
1047
|
-
});
|
|
1048
|
-
$.log.error('[cachebase] incrby操作失败:', error);
|
|
1049
|
-
resolve(0);
|
|
892
|
+
// 保存结果
|
|
893
|
+
this.data[key] = { value: result, expire: 0 };
|
|
894
|
+
|
|
895
|
+
// 如果键不存在于列表中,添加到列表
|
|
896
|
+
if (!this.list.includes(key)) {
|
|
897
|
+
this.list.push(key);
|
|
1050
898
|
}
|
|
1051
|
-
|
|
899
|
+
|
|
900
|
+
this.emit('incrby_after', { key, increment, result });
|
|
901
|
+
return result;
|
|
902
|
+
} catch (error) {
|
|
903
|
+
this.emit('incrby_error', { key, increment, error });
|
|
904
|
+
$.log.error('[cachebase] incrby操作失败:', error);
|
|
905
|
+
throw error;
|
|
906
|
+
}
|
|
1052
907
|
}
|
|
1053
908
|
|
|
1054
|
-
|
|
909
|
+
/**
|
|
910
|
+
* 数值减少指定值
|
|
911
|
+
* @param {string} key - 键名
|
|
912
|
+
* @param {number} decrement - 减少的值
|
|
913
|
+
* @returns {Promise<number>} - 返回减少后的值
|
|
914
|
+
*/
|
|
1055
915
|
async decrby(key, decrement) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
// 触发前置事件
|
|
1059
|
-
const beforeEventData = {
|
|
1060
|
-
key,
|
|
1061
|
-
value: { decrement },
|
|
1062
|
-
cancel: false
|
|
1063
|
-
};
|
|
1064
|
-
this.emit('decrby_before', beforeEventData);
|
|
1065
|
-
|
|
1066
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
1067
|
-
if (beforeEventData.cancel) {
|
|
1068
|
-
resolve(0);
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
916
|
+
try {
|
|
917
|
+
this.emit('decrby_before', { key, decrement });
|
|
1071
918
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
919
|
+
// 获取当前值或默认为0
|
|
920
|
+
let currentValue = 0;
|
|
921
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
922
|
+
currentValue = parseFloat(this.data[key].value) || 0;
|
|
923
|
+
}
|
|
1077
924
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
// 保存结果
|
|
1082
|
-
this.data[key] = { value: result, expire: 0 };
|
|
1083
|
-
|
|
1084
|
-
// 如果键不存在于列表中,添加到列表
|
|
1085
|
-
if (!this.list.includes(key)) {
|
|
1086
|
-
this.list.push(key);
|
|
1087
|
-
}
|
|
925
|
+
// 减少指定值
|
|
926
|
+
const result = currentValue - parseFloat(decrement);
|
|
1088
927
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
resolve(result);
|
|
1096
|
-
} catch (error) {
|
|
1097
|
-
// 触发错误事件
|
|
1098
|
-
this.emit('decrby_error', {
|
|
1099
|
-
key,
|
|
1100
|
-
value: { decrement },
|
|
1101
|
-
error
|
|
1102
|
-
});
|
|
1103
|
-
$.log.error('[cachebase] decrby操作失败:', error);
|
|
1104
|
-
resolve(0);
|
|
928
|
+
// 保存结果
|
|
929
|
+
this.data[key] = { value: result, expire: 0 };
|
|
930
|
+
|
|
931
|
+
// 如果键不存在于列表中,添加到列表
|
|
932
|
+
if (!this.list.includes(key)) {
|
|
933
|
+
this.list.push(key);
|
|
1105
934
|
}
|
|
1106
|
-
|
|
935
|
+
|
|
936
|
+
this.emit('decrby_after', { key, decrement, result });
|
|
937
|
+
return result;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
this.emit('decrby_error', { key, decrement, error });
|
|
940
|
+
$.log.error('[cachebase] decrby操作失败:', error);
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
1107
943
|
}
|
|
1108
944
|
|
|
1109
|
-
|
|
945
|
+
/**
|
|
946
|
+
* 浮点数增加指定值
|
|
947
|
+
* @param {string} key - 键名
|
|
948
|
+
* @param {number} increment - 增加的值
|
|
949
|
+
* @returns {Promise<number>} - 返回增加后的值
|
|
950
|
+
*/
|
|
1110
951
|
async incrbyfloat(key, increment) {
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
// 触发前置事件
|
|
1114
|
-
const beforeEventData = {
|
|
1115
|
-
key,
|
|
1116
|
-
value: { increment },
|
|
1117
|
-
cancel: false
|
|
1118
|
-
};
|
|
1119
|
-
this.emit('incrbyfloat_before', beforeEventData);
|
|
1120
|
-
|
|
1121
|
-
// 如果事件处理器设置了cancel为true,则取消操作
|
|
1122
|
-
if (beforeEventData.cancel) {
|
|
1123
|
-
resolve(0);
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
952
|
+
try {
|
|
953
|
+
this.emit('incrbyfloat_before', { key, increment });
|
|
1126
954
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
955
|
+
// 获取当前值或默认为0
|
|
956
|
+
let currentValue = 0;
|
|
957
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
958
|
+
currentValue = parseFloat(this.data[key].value) || 0;
|
|
959
|
+
}
|
|
1132
960
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
// 保存结果
|
|
1137
|
-
this.data[key] = { value: result, expire: 0 };
|
|
1138
|
-
|
|
1139
|
-
// 如果键不存在于列表中,添加到列表
|
|
1140
|
-
if (!this.list.includes(key)) {
|
|
1141
|
-
this.list.push(key);
|
|
1142
|
-
}
|
|
961
|
+
// 增加指定值
|
|
962
|
+
const result = currentValue + parseFloat(increment);
|
|
1143
963
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
resolve(result);
|
|
1151
|
-
} catch (error) {
|
|
1152
|
-
// 触发错误事件
|
|
1153
|
-
this.emit('incrbyfloat_error', {
|
|
1154
|
-
key,
|
|
1155
|
-
value: { increment },
|
|
1156
|
-
error
|
|
1157
|
-
});
|
|
1158
|
-
$.log.error('[cachebase] incrbyfloat操作失败:', error);
|
|
1159
|
-
resolve(0);
|
|
964
|
+
// 保存结果
|
|
965
|
+
this.data[key] = { value: result, expire: 0 };
|
|
966
|
+
|
|
967
|
+
// 如果键不存在于列表中,添加到列表
|
|
968
|
+
if (!this.list.includes(key)) {
|
|
969
|
+
this.list.push(key);
|
|
1160
970
|
}
|
|
1161
|
-
|
|
971
|
+
|
|
972
|
+
this.emit('incrbyfloat_after', { key, increment, result });
|
|
973
|
+
return result;
|
|
974
|
+
} catch (error) {
|
|
975
|
+
this.emit('incrbyfloat_error', { key, increment, error });
|
|
976
|
+
$.log.error('[cachebase] incrbyfloat操作失败:', error);
|
|
977
|
+
throw error;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* 字符串追加
|
|
983
|
+
* @param {string} key - 键名
|
|
984
|
+
* @param {string} value - 追加的字符串
|
|
985
|
+
* @returns {Promise<number>} - 返回追加后的字符串长度
|
|
986
|
+
*/
|
|
987
|
+
async append(key, value) {
|
|
988
|
+
try {
|
|
989
|
+
this.emit('append_before', { key, value });
|
|
990
|
+
|
|
991
|
+
// 获取当前值或默认为空字符串
|
|
992
|
+
let currentValue = '';
|
|
993
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
994
|
+
currentValue = String(this.data[key].value);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// 执行字符串拼接
|
|
998
|
+
const newValue = currentValue + String(value);
|
|
999
|
+
const result = newValue.length;
|
|
1000
|
+
|
|
1001
|
+
// 保存结果
|
|
1002
|
+
this.data[key] = { value: newValue, expire: 0 };
|
|
1003
|
+
|
|
1004
|
+
// 如果键不存在于列表中,添加到列表
|
|
1005
|
+
if (!this.list.includes(key)) {
|
|
1006
|
+
this.list.push(key);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
this.emit('append_after', { key, value, result });
|
|
1010
|
+
return result;
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
this.emit('append_error', { key, value, error });
|
|
1013
|
+
$.log.error('[cachebase] append操作失败:', error);
|
|
1014
|
+
throw error;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* 获取字符串长度
|
|
1020
|
+
* @param {string} key - 键名
|
|
1021
|
+
* @returns {Promise<number>} - 返回字符串长度
|
|
1022
|
+
*/
|
|
1023
|
+
async strlen(key) {
|
|
1024
|
+
try {
|
|
1025
|
+
this.emit('strlen_before', { key });
|
|
1026
|
+
|
|
1027
|
+
// 检查键是否存在且未过期
|
|
1028
|
+
if (this.data.hasOwnProperty(key) && !this._checkExpire(key)) {
|
|
1029
|
+
const result = String(this.data[key].value).length;
|
|
1030
|
+
this.emit('strlen_after', { key, result });
|
|
1031
|
+
return result;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
this.emit('strlen_after', { key, result: 0 });
|
|
1035
|
+
return 0;
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
this.emit('strlen_error', { key, error });
|
|
1038
|
+
$.log.error('[cachebase] strlen操作失败:', error);
|
|
1039
|
+
throw error;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* 清空数据库
|
|
1045
|
+
* @returns {Promise<string>} - 返回'OK'
|
|
1046
|
+
*/
|
|
1047
|
+
async flushdb() {
|
|
1048
|
+
try {
|
|
1049
|
+
this.emit('flushdb_before');
|
|
1050
|
+
|
|
1051
|
+
// 清空数据
|
|
1052
|
+
this.data = {};
|
|
1053
|
+
this.list = [];
|
|
1054
|
+
|
|
1055
|
+
this.emit('flushdb_after', { result: 'OK' });
|
|
1056
|
+
return 'OK';
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
this.emit('flushdb_error', { error });
|
|
1059
|
+
$.log.error('[cachebase] flushdb操作失败:', error);
|
|
1060
|
+
throw error;
|
|
1061
|
+
}
|
|
1162
1062
|
}
|
|
1163
1063
|
|
|
1164
1064
|
// 保持兼容性的方法
|
|
1165
1065
|
async has(key) {
|
|
1166
|
-
|
|
1066
|
+
try {
|
|
1067
|
+
const exists = await this.exists(key);
|
|
1068
|
+
return exists > 0;
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
$.log.error('[cachebase] has操作失败:', error);
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1167
1073
|
}
|
|
1168
1074
|
|
|
1169
1075
|
async clear(pattern) {
|
|
@@ -1182,36 +1088,83 @@ class CacheBase extends EventEmitter {
|
|
|
1182
1088
|
return this.append(key, value);
|
|
1183
1089
|
}
|
|
1184
1090
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1091
|
+
/**
|
|
1092
|
+
* 销毁缓存实例
|
|
1093
|
+
* @returns {Promise<void>}
|
|
1094
|
+
*/
|
|
1095
|
+
async dispose() {
|
|
1187
1096
|
try {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1097
|
+
this.emit('dispose_before');
|
|
1098
|
+
|
|
1099
|
+
// 清除定时器
|
|
1100
|
+
if (this.saveTimer) {
|
|
1101
|
+
clearInterval(this.saveTimer);
|
|
1102
|
+
this.saveTimer = null;
|
|
1103
|
+
$.log.info('[cachebase] 定时保存已停止');
|
|
1195
1104
|
}
|
|
1196
|
-
|
|
1197
|
-
//
|
|
1105
|
+
|
|
1106
|
+
// 如果启用了持久化,在销毁前保存一次
|
|
1107
|
+
if (this.persist) {
|
|
1108
|
+
await this._saveData().catch(error => {
|
|
1109
|
+
$.log.error('[cachebase] 销毁前保存数据失败:', error);
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// 清空数据
|
|
1198
1114
|
this.data = {};
|
|
1199
1115
|
this.list = [];
|
|
1200
|
-
this.dict = this.data; //
|
|
1116
|
+
this.dict = this.data; // 确保dict也被清空
|
|
1201
1117
|
|
|
1202
|
-
|
|
1203
|
-
this.emit('dispose_after', {});
|
|
1118
|
+
this.emit('dispose_after');
|
|
1204
1119
|
|
|
1205
|
-
//
|
|
1120
|
+
// 移除所有监听器
|
|
1206
1121
|
this.removeAllListeners();
|
|
1207
1122
|
} catch (error) {
|
|
1208
|
-
// 触发错误事件
|
|
1209
1123
|
this.emit('dispose_error', { error });
|
|
1210
1124
|
$.log.error('[cachebase] dispose操作失败:', error);
|
|
1211
1125
|
}
|
|
1212
1126
|
}
|
|
1127
|
+
/**
|
|
1128
|
+
* 关闭缓存实例
|
|
1129
|
+
*/
|
|
1130
|
+
async close() {
|
|
1131
|
+
await this.dispose()
|
|
1132
|
+
}
|
|
1213
1133
|
}
|
|
1214
1134
|
|
|
1215
1135
|
// 导出CacheBase类
|
|
1216
|
-
|
|
1136
|
+
exports.CacheBase = CacheBase;
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* 确保连接池对象存在
|
|
1141
|
+
*/
|
|
1142
|
+
if (!$.pool) {
|
|
1143
|
+
$.pool = {};
|
|
1144
|
+
}
|
|
1145
|
+
if (!$.pool.cachebase) {
|
|
1146
|
+
$.pool.cachebase = {};
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* @description cachebase管理器,用于创建缓存
|
|
1151
|
+
* @param {String} scope 作用域
|
|
1152
|
+
* @param {String} dir 当前路径
|
|
1153
|
+
* @return {Object} 返回一个cachebase类实例
|
|
1154
|
+
*/
|
|
1155
|
+
function cachebase_admin(scope, dir) {
|
|
1156
|
+
if (!scope) {
|
|
1157
|
+
scope = 'sys';
|
|
1158
|
+
}
|
|
1159
|
+
var obj = $.pool.cachebase[scope];
|
|
1160
|
+
if (!obj) {
|
|
1161
|
+
$.pool.cachebase[scope] = new CacheBase(scope, dir);
|
|
1162
|
+
obj = $.pool.cachebase[scope];
|
|
1163
|
+
}
|
|
1164
|
+
return obj;
|
|
1165
|
+
}
|
|
1217
1166
|
|
|
1167
|
+
/**
|
|
1168
|
+
* @module 导出cachebase管理器
|
|
1169
|
+
*/
|
|
1170
|
+
exports.cachebase_admin = cachebase_admin;
|