mm_redis 1.9.8 → 2.0.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/LICENSE +15 -0
- package/README.md +64 -25
- package/index.js +1883 -542
- package/package.json +2 -2
- package/test.js +20 -20
- package/test_hmset.js +91 -0
- package/compatibility_table.md +0 -103
- package/verify_compatibility.js +0 -175
package/index.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* @author <a href="http://qww.elins.cn">邱文武</a>
|
|
4
4
|
* @version 1.5.0
|
|
5
5
|
*/
|
|
6
|
-
const
|
|
7
|
-
|
|
6
|
+
const {
|
|
7
|
+
BaseService
|
|
8
|
+
} = require('mm_base_service');
|
|
8
9
|
const {
|
|
9
10
|
createClient
|
|
10
11
|
} = require('redis');
|
|
@@ -13,17 +14,14 @@ const {
|
|
|
13
14
|
|
|
14
15
|
/* 创建Redis帮助类函数 */
|
|
15
16
|
/// 构造函数
|
|
16
|
-
class Redis extends
|
|
17
|
+
class Redis extends BaseService {
|
|
17
18
|
/**
|
|
18
19
|
* @description 创建Redis帮助类函数 (构造函数)
|
|
19
|
-
* @param {String} scope 作用域
|
|
20
20
|
* @param {Object} config 配置参数
|
|
21
21
|
* @constructor
|
|
22
22
|
*/
|
|
23
|
-
constructor(
|
|
23
|
+
constructor(config) {
|
|
24
24
|
super();
|
|
25
|
-
// 作用域
|
|
26
|
-
this.scope = scope;
|
|
27
25
|
// 配置参数
|
|
28
26
|
this.config = {
|
|
29
27
|
// 服务器地址
|
|
@@ -31,7 +29,7 @@ class Redis extends eventEmitter {
|
|
|
31
29
|
// 端口号
|
|
32
30
|
"port": 6379,
|
|
33
31
|
// 密码
|
|
34
|
-
"password": "",
|
|
32
|
+
"password": "asd159357",
|
|
35
33
|
// 数据库
|
|
36
34
|
"database": 0,
|
|
37
35
|
// 前缀
|
|
@@ -41,17 +39,18 @@ class Redis extends eventEmitter {
|
|
|
41
39
|
// 重试次数
|
|
42
40
|
"retry_count": 3,
|
|
43
41
|
// 重试间隔(ms)
|
|
44
|
-
"retry_delay": 1000
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
config: {
|
|
52
|
-
max_clients: 10 // 最大连接数
|
|
42
|
+
"retry_delay": 1000,
|
|
43
|
+
// 连接池配置
|
|
44
|
+
"pool": {
|
|
45
|
+
"max_clients": 100, // 最大连接数
|
|
46
|
+
"min_idle": 5, // 最小空闲连接数
|
|
47
|
+
"max_idle": 20, // 最大空闲连接数
|
|
48
|
+
"idle_timeout": 30000 // 空闲超时时间(ms)
|
|
53
49
|
}
|
|
54
50
|
};
|
|
51
|
+
this.setConfig(config);
|
|
52
|
+
// 初始化连接池
|
|
53
|
+
this._initPool();
|
|
55
54
|
// 唯一标识符
|
|
56
55
|
this.identifier = this.config.host + "/" + this.config.database;
|
|
57
56
|
// 数据库连接器
|
|
@@ -59,7 +58,7 @@ class Redis extends eventEmitter {
|
|
|
59
58
|
// 连接状态
|
|
60
59
|
this.connected = false;
|
|
61
60
|
// 重试次数计数器
|
|
62
|
-
this.
|
|
61
|
+
this.retry_count = 0;
|
|
63
62
|
// 订阅回调函数
|
|
64
63
|
this.func_list = {};
|
|
65
64
|
}
|
|
@@ -70,7 +69,7 @@ class Redis extends eventEmitter {
|
|
|
70
69
|
* @param {String} key 键
|
|
71
70
|
* @return {String} 处理后的键
|
|
72
71
|
*/
|
|
73
|
-
Redis.prototype.applyPrefix = function
|
|
72
|
+
Redis.prototype.applyPrefix = function(key) {
|
|
74
73
|
return this.config.prefix ? this.config.prefix + key : key;
|
|
75
74
|
};
|
|
76
75
|
|
|
@@ -79,7 +78,7 @@ Redis.prototype.applyPrefix = function (key) {
|
|
|
79
78
|
* @param {String} key 带前缀的键
|
|
80
79
|
* @return {String} 处理后的键
|
|
81
80
|
*/
|
|
82
|
-
Redis.prototype.removePrefix = function
|
|
81
|
+
Redis.prototype.removePrefix = function(key) {
|
|
83
82
|
if (this.config.prefix && key.startsWith(this.config.prefix)) {
|
|
84
83
|
return key.slice(this.config.prefix.length);
|
|
85
84
|
}
|
|
@@ -92,7 +91,7 @@ Redis.prototype.removePrefix = function (key) {
|
|
|
92
91
|
* @param {Function} func 操作回调函数,可判断成功与否
|
|
93
92
|
* @return {Promise<Boolean>} 订阅结果
|
|
94
93
|
*/
|
|
95
|
-
Redis.prototype.subscribe = async function
|
|
94
|
+
Redis.prototype.subscribe = async function(channel, func) {
|
|
96
95
|
try {
|
|
97
96
|
await this.conn.subscribe(this.applyPrefix(channel));
|
|
98
97
|
this.func_list[channel] = func;
|
|
@@ -108,7 +107,7 @@ Redis.prototype.subscribe = async function (channel, func) {
|
|
|
108
107
|
* @param {String} channel 订阅的频道
|
|
109
108
|
* @return {Promise<Boolean>} 取消订阅结果
|
|
110
109
|
*/
|
|
111
|
-
Redis.prototype.unsubscribe = async function
|
|
110
|
+
Redis.prototype.unsubscribe = async function(channel) {
|
|
112
111
|
try {
|
|
113
112
|
await this.conn.unsubscribe(this.applyPrefix(channel));
|
|
114
113
|
delete this.func_list[channel];
|
|
@@ -125,12 +124,13 @@ Redis.prototype.unsubscribe = async function (channel) {
|
|
|
125
124
|
* @param {String} message 订阅的消息
|
|
126
125
|
* @return {Promise<Number>} 接收消息的客户端数量
|
|
127
126
|
*/
|
|
128
|
-
Redis.prototype.publish = async function
|
|
127
|
+
Redis.prototype.publish = async function(channel, message) {
|
|
129
128
|
try {
|
|
130
129
|
return await this.conn.publish(this.applyPrefix(channel), message);
|
|
131
130
|
} catch (err) {
|
|
132
131
|
$.log.error(`[Redis] 发布消息到频道 ${channel} 失败:`, err);
|
|
133
|
-
|
|
132
|
+
$.log.error('[Redis] 操作失败:', err);
|
|
133
|
+
return false;
|
|
134
134
|
}
|
|
135
135
|
};
|
|
136
136
|
|
|
@@ -139,7 +139,7 @@ Redis.prototype.publish = async function (channel, message) {
|
|
|
139
139
|
* @param {String} channel
|
|
140
140
|
* @param {String} message
|
|
141
141
|
*/
|
|
142
|
-
Redis.prototype.main = async function
|
|
142
|
+
Redis.prototype.main = async function(channel, message) {
|
|
143
143
|
// 默认实现,可由子类重写
|
|
144
144
|
$.log.debug(`[Redis] 收到未处理的消息 [${channel}]:`, message);
|
|
145
145
|
};
|
|
@@ -149,7 +149,7 @@ Redis.prototype.main = async function (channel, message) {
|
|
|
149
149
|
* @param {String} channel 频道
|
|
150
150
|
* @param {String} message 消息
|
|
151
151
|
*/
|
|
152
|
-
Redis.prototype.message = function
|
|
152
|
+
Redis.prototype.message = function(channel, message) {
|
|
153
153
|
// 移除前缀
|
|
154
154
|
channel = this.removePrefix(channel);
|
|
155
155
|
var func = this.func_list[channel];
|
|
@@ -166,31 +166,55 @@ Redis.prototype.message = function (channel, message) {
|
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
168
|
* @description 设置配置
|
|
169
|
-
* @param {Object}
|
|
169
|
+
* @param {Object} config 配置对象或配置路径
|
|
170
170
|
*/
|
|
171
|
-
|
|
172
|
-
Redis.prototype.setConfig = function (cg) {
|
|
171
|
+
Redis.prototype.setConfig = function(config) {
|
|
173
172
|
let obj = {};
|
|
174
|
-
if (typeof
|
|
175
|
-
obj =
|
|
176
|
-
} else if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
if (typeof config === 'string') {
|
|
174
|
+
obj = config.loadJson(__dirname);
|
|
175
|
+
} else if (config) {
|
|
176
|
+
// 合并配置参数
|
|
177
|
+
if (config.pool) {
|
|
178
|
+
// 确保连接池配置的合并不会覆盖所有默认值
|
|
179
|
+
if (!this.config.pool) {
|
|
180
|
+
this.config.pool = {};
|
|
181
|
+
}
|
|
182
|
+
Object.assign(this.config.pool, config.pool);
|
|
183
|
+
// 删除config中的pool属性,避免后续Object.assign重复处理
|
|
184
|
+
const configCopy = { ...config };
|
|
185
|
+
delete configCopy.pool;
|
|
186
|
+
Object.assign(this.config, configCopy);
|
|
187
|
+
} else {
|
|
188
|
+
Object.assign(this.config, config);
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
181
191
|
return;
|
|
182
192
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// 检查是否存在连接池配置,如果没有则使用默认配置
|
|
193
|
+
|
|
194
|
+
// 确保连接池配置存在且包含所有必要字段
|
|
186
195
|
if (!this.config.pool) {
|
|
187
|
-
this.config.pool = {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
196
|
+
this.config.pool = {};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 设置默认连接池配置,只覆盖未定义的字段
|
|
200
|
+
const defaultPoolConfig = {
|
|
201
|
+
max_clients: 100,
|
|
202
|
+
min_idle: 5,
|
|
203
|
+
max_idle: 20,
|
|
204
|
+
idle_timeout: 30000
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
for (const key in defaultPoolConfig) {
|
|
208
|
+
if (this.config.pool[key] === undefined) {
|
|
209
|
+
this.config.pool[key] = defaultPoolConfig[key];
|
|
210
|
+
}
|
|
193
211
|
}
|
|
212
|
+
|
|
213
|
+
// 如果连接池已经初始化,更新其配置
|
|
214
|
+
if (this.connection_pool) {
|
|
215
|
+
this.connection_pool.config = this.config.pool;
|
|
216
|
+
}
|
|
217
|
+
|
|
194
218
|
// 初始化连接池
|
|
195
219
|
this._initPool();
|
|
196
220
|
$.log.debug('[Redis] 配置已更新');
|
|
@@ -199,35 +223,40 @@ Redis.prototype.setConfig = function (cg) {
|
|
|
199
223
|
};
|
|
200
224
|
|
|
201
225
|
// 添加连接池初始化方法
|
|
202
|
-
Redis.prototype._initPool = function
|
|
226
|
+
Redis.prototype._initPool = function() {
|
|
227
|
+
// 确保连接池配置存在
|
|
228
|
+
if (!this.config.pool) {
|
|
229
|
+
this.config.pool = {
|
|
230
|
+
max_clients: 100,
|
|
231
|
+
min_idle: 5,
|
|
232
|
+
max_idle: 20,
|
|
233
|
+
idle_timeout: 30000
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
203
237
|
// 初始化连接池相关属性
|
|
204
|
-
this.
|
|
238
|
+
this.connection_pool = {
|
|
205
239
|
available: [],
|
|
206
|
-
|
|
207
|
-
config: this.config.pool
|
|
208
|
-
max_clients: 10,
|
|
209
|
-
min_idle: 2,
|
|
210
|
-
max_idle: 5,
|
|
211
|
-
idle_timeout: 30000
|
|
212
|
-
}
|
|
240
|
+
in_use: new Set(),
|
|
241
|
+
config: this.config.pool // 直接引用配置对象,确保同步更新
|
|
213
242
|
};
|
|
214
243
|
|
|
215
244
|
// 记录连接池初始化
|
|
216
|
-
$.log.debug('[Redis]
|
|
245
|
+
$.log.debug('[Redis] 连接池已初始化,配置:', this.config.pool);
|
|
217
246
|
};
|
|
218
247
|
|
|
219
248
|
/**
|
|
220
249
|
* @description 连接Redis数据库
|
|
221
250
|
* @return {Promise<Boolean>} 连接结果
|
|
222
251
|
*/
|
|
223
|
-
Redis.prototype.open = async function
|
|
252
|
+
Redis.prototype.open = async function() {
|
|
224
253
|
if (this.conn && this.connected) {
|
|
225
254
|
return true;
|
|
226
255
|
}
|
|
227
256
|
|
|
228
257
|
try {
|
|
229
258
|
const cg = this.config;
|
|
230
|
-
this.
|
|
259
|
+
this.retry_count = 0;
|
|
231
260
|
|
|
232
261
|
// 创建客户端
|
|
233
262
|
this.conn = createClient({
|
|
@@ -252,7 +281,7 @@ Redis.prototype.open = async function () {
|
|
|
252
281
|
this.conn.on('connect', () => {
|
|
253
282
|
$.log.debug('[Redis] Redis已连接');
|
|
254
283
|
this.connected = true;
|
|
255
|
-
this.
|
|
284
|
+
this.retry_count = 0;
|
|
256
285
|
});
|
|
257
286
|
|
|
258
287
|
// 监听断开连接事件
|
|
@@ -292,14 +321,14 @@ Redis.prototype.open = async function () {
|
|
|
292
321
|
* 内部重连方法
|
|
293
322
|
* @private
|
|
294
323
|
*/
|
|
295
|
-
Redis.prototype._reconnect = function
|
|
296
|
-
if (this.
|
|
324
|
+
Redis.prototype._reconnect = function() {
|
|
325
|
+
if (this.retry_count >= this.config.retry_count) {
|
|
297
326
|
$.log.error(`[Redis] 达到最大重连次数 ${this.config.retry_count},停止重连`);
|
|
298
327
|
return;
|
|
299
328
|
}
|
|
300
329
|
|
|
301
|
-
this.
|
|
302
|
-
$.log.debug(`[Redis] 尝试第 ${this.
|
|
330
|
+
this.retry_count++;
|
|
331
|
+
$.log.debug(`[Redis] 尝试第 ${this.retry_count} 次重连...`);
|
|
303
332
|
|
|
304
333
|
setTimeout(async () => {
|
|
305
334
|
try {
|
|
@@ -315,25 +344,25 @@ Redis.prototype._reconnect = function () {
|
|
|
315
344
|
* @return {Promise<Boolean>} 关闭结果
|
|
316
345
|
*/
|
|
317
346
|
// 修复close方法,正确处理连接池
|
|
318
|
-
Redis.prototype.close = function
|
|
347
|
+
Redis.prototype.close = function() {
|
|
319
348
|
try {
|
|
320
349
|
$.log.debug('[Redis] 关闭所有连接...');
|
|
321
350
|
|
|
322
351
|
// 关闭所有使用中的连接
|
|
323
|
-
if (this.
|
|
324
|
-
for (const connection of this.
|
|
352
|
+
if (this.connection_pool && this.connection_pool.in_use) {
|
|
353
|
+
for (const connection of this.connection_pool.in_use) {
|
|
325
354
|
try {
|
|
326
355
|
connection.close();
|
|
327
356
|
} catch (e) {
|
|
328
357
|
$.log.error('[Redis] 关闭连接失败:', e);
|
|
329
358
|
}
|
|
330
359
|
}
|
|
331
|
-
this.
|
|
360
|
+
this.connection_pool.in_use.clear();
|
|
332
361
|
}
|
|
333
362
|
|
|
334
363
|
// 关闭所有可用连接
|
|
335
|
-
if (this.
|
|
336
|
-
for (const connection of this.
|
|
364
|
+
if (this.connection_pool && this.connection_pool.available) {
|
|
365
|
+
for (const connection of this.connection_pool.available) {
|
|
337
366
|
try {
|
|
338
367
|
// 检查连接对象是否有close方法
|
|
339
368
|
if (typeof connection.close === 'function') {
|
|
@@ -348,7 +377,7 @@ Redis.prototype.close = function () {
|
|
|
348
377
|
$.log.error('[Redis] 关闭连接失败:', e);
|
|
349
378
|
}
|
|
350
379
|
}
|
|
351
|
-
this.
|
|
380
|
+
this.connection_pool.available = [];
|
|
352
381
|
}
|
|
353
382
|
|
|
354
383
|
this.connected = false;
|
|
@@ -361,49 +390,49 @@ Redis.prototype.close = function () {
|
|
|
361
390
|
};
|
|
362
391
|
|
|
363
392
|
// 添加Pipeline支持
|
|
364
|
-
Redis.prototype.pipeline = function
|
|
393
|
+
Redis.prototype.pipeline = function() {
|
|
365
394
|
const redisInstance = this;
|
|
366
395
|
const commands = [];
|
|
367
396
|
|
|
368
397
|
return {
|
|
369
398
|
// 设置值
|
|
370
|
-
set: function
|
|
399
|
+
set: function(key, value, seconds) {
|
|
371
400
|
commands.push(['set', key, value, seconds]);
|
|
372
401
|
return this;
|
|
373
402
|
},
|
|
374
403
|
|
|
375
404
|
// 获取值
|
|
376
|
-
get: function
|
|
405
|
+
get: function(key) {
|
|
377
406
|
commands.push(['get', key]);
|
|
378
407
|
return this;
|
|
379
408
|
},
|
|
380
409
|
|
|
381
410
|
// 删除
|
|
382
|
-
del: function
|
|
411
|
+
del: function(key) {
|
|
383
412
|
commands.push(['del', key]);
|
|
384
413
|
return this;
|
|
385
414
|
},
|
|
386
415
|
|
|
387
416
|
// 判断存在
|
|
388
|
-
has: function
|
|
417
|
+
has: function(key) {
|
|
389
418
|
commands.push(['has', key]);
|
|
390
419
|
return this;
|
|
391
420
|
},
|
|
392
421
|
|
|
393
422
|
// 设置哈希字段
|
|
394
|
-
hset: function
|
|
423
|
+
hset: function(key, field, value) {
|
|
395
424
|
commands.push(['hset', key, field, value]);
|
|
396
425
|
return this;
|
|
397
426
|
},
|
|
398
427
|
|
|
399
428
|
// 过期时间设置
|
|
400
|
-
expire: function
|
|
429
|
+
expire: function(key, seconds) {
|
|
401
430
|
commands.push(['expire', key, seconds]);
|
|
402
431
|
return this;
|
|
403
432
|
},
|
|
404
433
|
|
|
405
434
|
// 执行所有命令
|
|
406
|
-
exec: async function
|
|
435
|
+
exec: async function() {
|
|
407
436
|
try {
|
|
408
437
|
// 使用Redis实例的方法获取连接
|
|
409
438
|
const connection = await redisInstance._getConnection();
|
|
@@ -502,7 +531,7 @@ Redis.prototype.pipeline = function () {
|
|
|
502
531
|
* @description 检查连接状态
|
|
503
532
|
* @return {Boolean} 连接状态
|
|
504
533
|
*/
|
|
505
|
-
Redis.prototype.isConnected = function
|
|
534
|
+
Redis.prototype.isConnected = function() {
|
|
506
535
|
return this.connected && this.conn && this.conn.isReady;
|
|
507
536
|
};
|
|
508
537
|
|
|
@@ -512,12 +541,38 @@ Redis.prototype.isConnected = function () {
|
|
|
512
541
|
* @param {Number} seconds 秒, 为空则查询有效时长
|
|
513
542
|
* @return {Promise<Number|Boolean>} 查询时长或设置结果, 时长为-1表示永不过期
|
|
514
543
|
*/
|
|
515
|
-
|
|
544
|
+
/**
|
|
545
|
+
* 获取或设置键的过期时间
|
|
546
|
+
* @param {String} key 键名
|
|
547
|
+
* @param {Number} [seconds] 过期时间(秒),不传此参数则获取剩余过期时间
|
|
548
|
+
* @return {Promise<Number|Boolean>} 不传seconds时返回剩余过期时间(-1表示无过期设置,-2表示已过期),传seconds时返回是否设置成功
|
|
549
|
+
*/
|
|
550
|
+
Redis.prototype.ttl = async function(key, seconds) {
|
|
516
551
|
try {
|
|
517
552
|
const connection = await this._getConnection();
|
|
518
553
|
try {
|
|
519
554
|
key = this.applyPrefix(key);
|
|
520
|
-
|
|
555
|
+
|
|
556
|
+
// 如果传入了seconds参数,则设置过期时间
|
|
557
|
+
if (seconds !== undefined) {
|
|
558
|
+
if (typeof seconds !== 'number' || isNaN(seconds) || seconds < 0) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// 设置过期时间
|
|
563
|
+
if (typeof connection.expire === 'function') {
|
|
564
|
+
await connection.expire(key, seconds);
|
|
565
|
+
return true;
|
|
566
|
+
} else if (typeof connection.execute === 'function') {
|
|
567
|
+
await connection.execute('EXPIRE', [key, seconds]);
|
|
568
|
+
return true;
|
|
569
|
+
} else {
|
|
570
|
+
$.log.error('[Redis] 连接对象不支持expire操作');
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// 否则获取剩余过期时间
|
|
521
576
|
if (typeof connection.ttl === 'function') {
|
|
522
577
|
return await connection.ttl(key);
|
|
523
578
|
} else if (typeof connection.execute === 'function') {
|
|
@@ -530,21 +585,37 @@ Redis.prototype.ttl = async function (key) {
|
|
|
530
585
|
this._recycleConnection(connection);
|
|
531
586
|
}
|
|
532
587
|
} catch (err) {
|
|
533
|
-
$.log.error(`[Redis]
|
|
534
|
-
return
|
|
588
|
+
$.log.error(`[Redis] ${seconds !== undefined ? '设置' : '获取'}过期时间失败 [${key}]:`, err);
|
|
589
|
+
return seconds !== undefined ? false : -2;
|
|
535
590
|
}
|
|
536
591
|
};
|
|
537
592
|
|
|
538
593
|
/**
|
|
539
|
-
* @description 增加整数值(负数为减)
|
|
594
|
+
* @description 增加整数值(负数为减)(兼容方法)
|
|
540
595
|
* @param {String} key 键
|
|
541
596
|
* @param {Number} num 数值
|
|
597
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
542
598
|
* @return {Promise<Number>} 计算后的结果
|
|
543
599
|
*/
|
|
544
|
-
Redis.prototype.addInt = async function
|
|
600
|
+
Redis.prototype.addInt = async function(key, num, seconds = 0) {
|
|
545
601
|
try {
|
|
602
|
+
// 参数验证
|
|
603
|
+
if (!key || typeof key !== 'string') {
|
|
604
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
if (typeof num !== 'number' || isNaN(num) || !Number.isInteger(num)) {
|
|
608
|
+
$.log.error('[Redis] 参数错误: 数值必须是有效整数');
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
|
|
546
612
|
// 触发前置事件,允许修改或阻止操作
|
|
547
|
-
const beforeEventData = {
|
|
613
|
+
const beforeEventData = {
|
|
614
|
+
key,
|
|
615
|
+
value: num,
|
|
616
|
+
seconds,
|
|
617
|
+
cancel: false
|
|
618
|
+
};
|
|
548
619
|
this.emit('addInt_before', beforeEventData);
|
|
549
620
|
|
|
550
621
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -554,59 +625,83 @@ Redis.prototype.addInt = async function (key, num) {
|
|
|
554
625
|
}
|
|
555
626
|
|
|
556
627
|
// 使用可能被修改的值
|
|
557
|
-
let {
|
|
628
|
+
let {
|
|
629
|
+
key: finalKey,
|
|
630
|
+
value: finalNum,
|
|
631
|
+
seconds: finalSeconds
|
|
632
|
+
} = beforeEventData;
|
|
558
633
|
|
|
559
634
|
const connection = await this._getConnection();
|
|
560
635
|
try {
|
|
561
|
-
|
|
636
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
562
637
|
let result;
|
|
563
638
|
if (finalNum > 0) {
|
|
564
639
|
// 增加整数值
|
|
565
640
|
if (typeof connection.incrBy === 'function') {
|
|
566
|
-
result = await connection.incrBy(
|
|
641
|
+
result = await connection.incrBy(prefixedKey, finalNum);
|
|
567
642
|
} else if (typeof connection.execute === 'function') {
|
|
568
|
-
result = await connection.execute('INCRBY', [
|
|
643
|
+
result = await connection.execute('INCRBY', [prefixedKey, finalNum]);
|
|
569
644
|
} else {
|
|
570
645
|
// 如果出错,回退到先获取再设置的方式
|
|
571
646
|
$.log.warn(`[Redis] 使用原子操作增加整数值失败,使用回退方案`);
|
|
572
|
-
let value = await this.get(
|
|
647
|
+
let value = await this.get(prefixedKey);
|
|
573
648
|
let n = 0;
|
|
574
649
|
if (value) {
|
|
575
650
|
n = Number(value);
|
|
576
651
|
}
|
|
577
652
|
n += finalNum;
|
|
578
|
-
await this.set(
|
|
653
|
+
await this.set(prefixedKey, n, finalSeconds);
|
|
579
654
|
result = n;
|
|
580
655
|
}
|
|
581
656
|
} else {
|
|
582
657
|
// 减少整数值
|
|
583
658
|
if (typeof connection.decrBy === 'function') {
|
|
584
|
-
result = await connection.decrBy(
|
|
659
|
+
result = await connection.decrBy(prefixedKey, -finalNum);
|
|
585
660
|
} else if (typeof connection.execute === 'function') {
|
|
586
|
-
result = await connection.execute('DECRBY', [
|
|
661
|
+
result = await connection.execute('DECRBY', [prefixedKey, -finalNum]);
|
|
587
662
|
} else {
|
|
588
663
|
// 如果出错,回退到先获取再设置的方式
|
|
589
664
|
$.log.warn(`[Redis] 使用原子操作减少整数值失败,使用回退方案`);
|
|
590
|
-
let value = await this.get(
|
|
665
|
+
let value = await this.get(prefixedKey);
|
|
591
666
|
let n = 0;
|
|
592
667
|
if (value) {
|
|
593
668
|
n = Number(value);
|
|
594
669
|
}
|
|
595
670
|
n += finalNum;
|
|
596
|
-
await this.set(
|
|
671
|
+
await this.set(prefixedKey, n, finalSeconds);
|
|
597
672
|
result = n;
|
|
598
673
|
}
|
|
599
674
|
}
|
|
675
|
+
|
|
676
|
+
// 如果设置了过期时间
|
|
677
|
+
if (finalSeconds > 0) {
|
|
678
|
+
if (typeof connection.expire === 'function') {
|
|
679
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
680
|
+
} else if (typeof connection.execute === 'function') {
|
|
681
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
600
684
|
|
|
601
685
|
// 触发后置事件
|
|
602
|
-
this.emit('addInt_after', {
|
|
686
|
+
this.emit('addInt_after', {
|
|
687
|
+
key: finalKey,
|
|
688
|
+
value: finalNum,
|
|
689
|
+
seconds: finalSeconds,
|
|
690
|
+
result,
|
|
691
|
+
success: true
|
|
692
|
+
});
|
|
603
693
|
return result;
|
|
604
694
|
} finally {
|
|
605
695
|
this._recycleConnection(connection);
|
|
606
696
|
}
|
|
607
697
|
} catch (err) {
|
|
608
698
|
// 触发错误事件
|
|
609
|
-
this.emit('addInt_error', {
|
|
699
|
+
this.emit('addInt_error', {
|
|
700
|
+
key,
|
|
701
|
+
value: num,
|
|
702
|
+
seconds,
|
|
703
|
+
error: err
|
|
704
|
+
});
|
|
610
705
|
$.log.error(`[Redis] 增加整数值失败 [${key}]:`, err);
|
|
611
706
|
// 与CacheBase保持一致,错误时返回默认值而不是抛出异常
|
|
612
707
|
return 0;
|
|
@@ -614,15 +709,34 @@ Redis.prototype.addInt = async function (key, num) {
|
|
|
614
709
|
};
|
|
615
710
|
|
|
616
711
|
/**
|
|
617
|
-
* @description 增加浮点数值(负数为减)
|
|
712
|
+
* @description 增加浮点数值(负数为减)(兼容方法)
|
|
618
713
|
* @param {String} key 键
|
|
619
714
|
* @param {Number} num 数值
|
|
715
|
+
* @param {Number} seconds 过期时间,0表示永不过期
|
|
620
716
|
* @return {Promise<Number>} 计算后的结果
|
|
621
717
|
*/
|
|
622
|
-
Redis.prototype.addFloat = async function
|
|
718
|
+
Redis.prototype.addFloat = async function(key, num, seconds = 0) {
|
|
623
719
|
try {
|
|
720
|
+
// 参数验证
|
|
721
|
+
if (!key || typeof key !== 'string') {
|
|
722
|
+
$.log.warn(`[Redis] addFloat: key参数必须是非空字符串`);
|
|
723
|
+
return 0;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// 验证num参数
|
|
727
|
+
num = parseFloat(num);
|
|
728
|
+
if (isNaN(num)) {
|
|
729
|
+
$.log.warn(`[Redis] addFloat: num参数必须是有效的数值`);
|
|
730
|
+
return 0;
|
|
731
|
+
}
|
|
732
|
+
|
|
624
733
|
// 触发前置事件,允许修改或阻止操作
|
|
625
|
-
const beforeEventData = {
|
|
734
|
+
const beforeEventData = {
|
|
735
|
+
key,
|
|
736
|
+
value: num,
|
|
737
|
+
seconds,
|
|
738
|
+
cancel: false
|
|
739
|
+
};
|
|
626
740
|
this.emit('addFloat_before', beforeEventData);
|
|
627
741
|
|
|
628
742
|
// 如果事件处理器标记为取消,则返回默认值
|
|
@@ -632,57 +746,87 @@ Redis.prototype.addFloat = async function (key, num) {
|
|
|
632
746
|
}
|
|
633
747
|
|
|
634
748
|
// 使用可能被修改的值
|
|
635
|
-
let {
|
|
749
|
+
let {
|
|
750
|
+
key: finalKey,
|
|
751
|
+
value: finalNum,
|
|
752
|
+
seconds: finalSeconds
|
|
753
|
+
} = beforeEventData;
|
|
636
754
|
let result;
|
|
637
755
|
|
|
638
756
|
const connection = await this._getConnection();
|
|
639
757
|
try {
|
|
640
|
-
|
|
758
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
641
759
|
|
|
642
760
|
// 优先使用原生方法
|
|
643
761
|
if (typeof connection.incrByFloat === 'function') {
|
|
644
|
-
result = parseFloat(await connection.incrByFloat(
|
|
762
|
+
result = parseFloat(await connection.incrByFloat(prefixedKey, finalNum));
|
|
645
763
|
}
|
|
646
764
|
// 尝试使用execute通用方法
|
|
647
765
|
else if (typeof connection.execute === 'function') {
|
|
648
|
-
result = parseFloat(await connection.execute('INCRBYFLOAT', [
|
|
766
|
+
result = parseFloat(await connection.execute('INCRBYFLOAT', [prefixedKey, finalNum]));
|
|
649
767
|
}
|
|
650
768
|
// 回退方案
|
|
651
769
|
else {
|
|
652
770
|
$.log.warn(`[Redis] 连接对象不支持incrByFloat操作,使用回退方案`);
|
|
653
|
-
let value = await this.get(
|
|
771
|
+
let value = await this.get(prefixedKey);
|
|
654
772
|
let n = 0;
|
|
655
773
|
if (value) {
|
|
656
774
|
n = Number(value);
|
|
657
775
|
}
|
|
658
776
|
n += finalNum;
|
|
659
|
-
await this.set(
|
|
777
|
+
await this.set(prefixedKey, n, finalSeconds);
|
|
660
778
|
result = n;
|
|
661
779
|
}
|
|
662
780
|
|
|
781
|
+
// 设置过期时间
|
|
782
|
+
if (finalSeconds > 0) {
|
|
783
|
+
if (typeof connection.expire === 'function') {
|
|
784
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
785
|
+
} else if (typeof connection.execute === 'function') {
|
|
786
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
663
790
|
// 触发后置事件
|
|
664
|
-
this.emit('addFloat_after', {
|
|
791
|
+
this.emit('addFloat_after', {
|
|
792
|
+
key: finalKey,
|
|
793
|
+
value: finalNum,
|
|
794
|
+
seconds: finalSeconds,
|
|
795
|
+
result,
|
|
796
|
+
success: true
|
|
797
|
+
});
|
|
665
798
|
return result;
|
|
666
799
|
} catch (err) {
|
|
667
800
|
// 如果出错,回退到先获取再设置的方式
|
|
668
801
|
$.log.warn(`[Redis] 使用原子操作增加浮点数值失败,使用回退方案:`, err);
|
|
669
|
-
let value = await this.get(
|
|
802
|
+
let value = await this.get(prefixedKey);
|
|
670
803
|
let n = 0;
|
|
671
804
|
if (value) {
|
|
672
805
|
n = Number(value);
|
|
673
806
|
}
|
|
674
807
|
n += finalNum;
|
|
675
|
-
await this.set(
|
|
808
|
+
await this.set(prefixedKey, n, finalSeconds);
|
|
676
809
|
|
|
677
810
|
// 触发后置事件
|
|
678
|
-
this.emit('addFloat_after', {
|
|
811
|
+
this.emit('addFloat_after', {
|
|
812
|
+
key: finalKey,
|
|
813
|
+
value: finalNum,
|
|
814
|
+
seconds: finalSeconds,
|
|
815
|
+
result: n,
|
|
816
|
+
success: true
|
|
817
|
+
});
|
|
679
818
|
return n;
|
|
680
819
|
} finally {
|
|
681
820
|
this._recycleConnection(connection);
|
|
682
821
|
}
|
|
683
822
|
} catch (err) {
|
|
684
823
|
// 触发错误事件
|
|
685
|
-
this.emit('addFloat_error', {
|
|
824
|
+
this.emit('addFloat_error', {
|
|
825
|
+
key,
|
|
826
|
+
value: num,
|
|
827
|
+
seconds,
|
|
828
|
+
error: err
|
|
829
|
+
});
|
|
686
830
|
$.log.error(`[Redis] 增加浮点数值失败 [${key}]:`, err);
|
|
687
831
|
// 与CacheBase保持一致,错误时返回默认值而不是抛出异常
|
|
688
832
|
return 0;
|
|
@@ -690,15 +834,34 @@ Redis.prototype.addFloat = async function (key, num) {
|
|
|
690
834
|
};
|
|
691
835
|
|
|
692
836
|
/**
|
|
693
|
-
* @description
|
|
837
|
+
* @description 增加字符串值到指定缓存(兼容方法)
|
|
694
838
|
* @param {String} key 键
|
|
695
839
|
* @param {String} str 添加的字符串
|
|
840
|
+
* @param {Number} seconds 过期时间,0表示永不过期
|
|
696
841
|
* @return {Promise<Number>} 添加后的字符串长度
|
|
697
842
|
*/
|
|
698
|
-
Redis.prototype.addStr = async function
|
|
843
|
+
Redis.prototype.addStr = async function(key, str, seconds = 0) {
|
|
699
844
|
try {
|
|
845
|
+
// 参数验证
|
|
846
|
+
if (!key || typeof key !== 'string') {
|
|
847
|
+
$.log.warn(`[Redis] addStr: key参数必须是非空字符串`);
|
|
848
|
+
return 0;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// 验证str参数
|
|
852
|
+
if (str === undefined || str === null) {
|
|
853
|
+
$.log.warn(`[Redis] addStr: str参数不能为undefined或null`);
|
|
854
|
+
return 0;
|
|
855
|
+
}
|
|
856
|
+
str = String(str);
|
|
857
|
+
|
|
700
858
|
// 触发前置事件,允许修改或阻止操作
|
|
701
|
-
const beforeEventData = {
|
|
859
|
+
const beforeEventData = {
|
|
860
|
+
key,
|
|
861
|
+
value: str,
|
|
862
|
+
seconds,
|
|
863
|
+
cancel: false
|
|
864
|
+
};
|
|
702
865
|
this.emit('addStr_before', beforeEventData);
|
|
703
866
|
|
|
704
867
|
// 如果事件处理器标记为取消,则返回0
|
|
@@ -708,63 +871,122 @@ Redis.prototype.addStr = async function (key, str) {
|
|
|
708
871
|
}
|
|
709
872
|
|
|
710
873
|
// 使用可能被修改的值
|
|
711
|
-
let {
|
|
874
|
+
let {
|
|
875
|
+
key: finalKey,
|
|
876
|
+
value: finalStr,
|
|
877
|
+
seconds: finalSeconds
|
|
878
|
+
} = beforeEventData;
|
|
712
879
|
let result;
|
|
713
880
|
|
|
714
881
|
const connection = await this._getConnection();
|
|
715
882
|
try {
|
|
716
|
-
|
|
883
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
717
884
|
|
|
718
885
|
// 优先使用原生方法
|
|
719
886
|
if (typeof connection.append === 'function') {
|
|
720
|
-
result = await connection.append(
|
|
887
|
+
result = await connection.append(prefixedKey, finalStr);
|
|
721
888
|
}
|
|
722
889
|
// 尝试使用execute通用方法
|
|
723
890
|
else if (typeof connection.execute === 'function') {
|
|
724
|
-
result = await connection.execute('APPEND', [
|
|
891
|
+
result = await connection.execute('APPEND', [prefixedKey, finalStr]);
|
|
725
892
|
}
|
|
726
893
|
// 回退方案
|
|
727
894
|
else {
|
|
728
895
|
$.log.warn(`[Redis] 连接对象不支持append操作,使用回退方案`);
|
|
729
|
-
let value = await this.get(
|
|
896
|
+
let value = await this.get(prefixedKey) || '';
|
|
730
897
|
value += finalStr;
|
|
731
|
-
await this.set(
|
|
898
|
+
await this.set(prefixedKey, value, finalSeconds);
|
|
732
899
|
result = value.length;
|
|
733
900
|
}
|
|
734
901
|
|
|
902
|
+
// 设置过期时间
|
|
903
|
+
if (finalSeconds > 0) {
|
|
904
|
+
if (typeof connection.expire === 'function') {
|
|
905
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
906
|
+
} else if (typeof connection.execute === 'function') {
|
|
907
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
735
911
|
// 触发后置事件
|
|
736
|
-
this.emit('addStr_after', {
|
|
912
|
+
this.emit('addStr_after', {
|
|
913
|
+
key: finalKey,
|
|
914
|
+
value: finalStr,
|
|
915
|
+
seconds: finalSeconds,
|
|
916
|
+
result,
|
|
917
|
+
success: true
|
|
918
|
+
});
|
|
737
919
|
return result;
|
|
738
920
|
} catch (err) {
|
|
739
921
|
$.log.error('[Redis] 追加字符串失败:', err);
|
|
740
922
|
// 回退方案:先获取再设置
|
|
741
|
-
let value = await this.get(
|
|
923
|
+
let value = await this.get(prefixedKey) || '';
|
|
742
924
|
value += finalStr;
|
|
743
|
-
await this.set(
|
|
925
|
+
await this.set(prefixedKey, value, finalSeconds);
|
|
744
926
|
|
|
745
927
|
// 触发后置事件
|
|
746
|
-
this.emit('addStr_after', {
|
|
928
|
+
this.emit('addStr_after', {
|
|
929
|
+
key: finalKey,
|
|
930
|
+
value: finalStr,
|
|
931
|
+
seconds: finalSeconds,
|
|
932
|
+
result: value.length,
|
|
933
|
+
success: true
|
|
934
|
+
});
|
|
747
935
|
return value.length;
|
|
748
936
|
} finally {
|
|
749
937
|
this._recycleConnection(connection);
|
|
750
938
|
}
|
|
751
939
|
} catch (err) {
|
|
752
940
|
// 触发错误事件
|
|
753
|
-
this.emit('addStr_error', {
|
|
941
|
+
this.emit('addStr_error', {
|
|
942
|
+
key,
|
|
943
|
+
value: str,
|
|
944
|
+
seconds,
|
|
945
|
+
error: err
|
|
946
|
+
});
|
|
754
947
|
$.log.error(`[Redis] 追加字符串失败 [${key}]:`, err);
|
|
755
948
|
return 0;
|
|
756
949
|
}
|
|
757
950
|
};
|
|
758
951
|
|
|
759
952
|
/**
|
|
760
|
-
* @description
|
|
953
|
+
* @description 删除缓存(兼容方法)
|
|
761
954
|
* @param {String|Array<String>} key 键或键数组
|
|
762
955
|
* @return {Promise<Boolean>} 成功返回true,失败返回false
|
|
763
956
|
*/
|
|
764
|
-
Redis.prototype.del = async function
|
|
957
|
+
Redis.prototype.del = async function(key) {
|
|
765
958
|
try {
|
|
959
|
+
// 键名必须是非空字符串或非空字符串数组
|
|
960
|
+
if (!key) {
|
|
961
|
+
$.log.warn('[Redis] del: key必须是非空字符串或非空字符串数组');
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// 如果是单个键,验证是否为非空字符串
|
|
966
|
+
if (typeof key === 'string') {
|
|
967
|
+
if (!key) {
|
|
968
|
+
$.log.warn('[Redis] del: key必须是非空字符串');
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
// 如果是数组,验证每个元素是否为非空字符串
|
|
973
|
+
else if (Array.isArray(key)) {
|
|
974
|
+
for (const k of key) {
|
|
975
|
+
if (!k || typeof k !== 'string') {
|
|
976
|
+
$.log.warn('[Redis] del: key必须是非空字符串或非空字符串数组');
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
} else {
|
|
981
|
+
$.log.warn('[Redis] del: key必须是非空字符串或非空字符串数组');
|
|
982
|
+
return false;
|
|
983
|
+
}
|
|
984
|
+
|
|
766
985
|
// 触发前置事件,允许修改或阻止操作
|
|
767
|
-
const beforeEventData = {
|
|
986
|
+
const beforeEventData = {
|
|
987
|
+
key,
|
|
988
|
+
cancel: false
|
|
989
|
+
};
|
|
768
990
|
this.emit('del_before', beforeEventData);
|
|
769
991
|
|
|
770
992
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -774,7 +996,9 @@ Redis.prototype.del = async function (key) {
|
|
|
774
996
|
}
|
|
775
997
|
|
|
776
998
|
// 使用可能被修改的值
|
|
777
|
-
let {
|
|
999
|
+
let {
|
|
1000
|
+
key: finalKey
|
|
1001
|
+
} = beforeEventData;
|
|
778
1002
|
|
|
779
1003
|
const connection = await this._getConnection();
|
|
780
1004
|
try {
|
|
@@ -793,14 +1017,22 @@ Redis.prototype.del = async function (key) {
|
|
|
793
1017
|
}
|
|
794
1018
|
}
|
|
795
1019
|
// 触发后置事件
|
|
796
|
-
this.emit('del_after', {
|
|
1020
|
+
this.emit('del_after', {
|
|
1021
|
+
key: finalKey,
|
|
1022
|
+
success,
|
|
1023
|
+
deleted
|
|
1024
|
+
});
|
|
797
1025
|
} else {
|
|
798
1026
|
// 单个删除
|
|
799
1027
|
const prefixedKey = this.applyPrefix(finalKey);
|
|
800
1028
|
const n = await connection.del(prefixedKey);
|
|
801
1029
|
success = n === 1;
|
|
802
1030
|
// 触发后置事件
|
|
803
|
-
this.emit('del_after', {
|
|
1031
|
+
this.emit('del_after', {
|
|
1032
|
+
key: finalKey,
|
|
1033
|
+
success,
|
|
1034
|
+
deleted: success ? [finalKey] : []
|
|
1035
|
+
});
|
|
804
1036
|
}
|
|
805
1037
|
return success;
|
|
806
1038
|
} finally {
|
|
@@ -808,8 +1040,11 @@ Redis.prototype.del = async function (key) {
|
|
|
808
1040
|
}
|
|
809
1041
|
} catch (err) {
|
|
810
1042
|
// 触发错误事件
|
|
811
|
-
this.emit('del_error', {
|
|
812
|
-
|
|
1043
|
+
this.emit('del_error', {
|
|
1044
|
+
key,
|
|
1045
|
+
error: err
|
|
1046
|
+
});
|
|
1047
|
+
$.log.error(`[Redis] 删除缓存失败 [${key}]:`, err);
|
|
813
1048
|
return false;
|
|
814
1049
|
}
|
|
815
1050
|
};
|
|
@@ -821,10 +1056,28 @@ Redis.prototype.del = async function (key) {
|
|
|
821
1056
|
* @param {Number} seconds 秒
|
|
822
1057
|
* @return {Promise<Boolean>} 操作结果
|
|
823
1058
|
*/
|
|
824
|
-
|
|
1059
|
+
/**
|
|
1060
|
+
* @description 设置缓存(兼容方法)
|
|
1061
|
+
* @param {String} key 键
|
|
1062
|
+
* @param {*} value 值
|
|
1063
|
+
* @param {Number} seconds 过期时间,0表示永不过期
|
|
1064
|
+
* @return {Promise<Boolean>} 成功返回true,失败返回false
|
|
1065
|
+
*/
|
|
1066
|
+
Redis.prototype.set = async function(key, value, seconds = 0) {
|
|
825
1067
|
try {
|
|
1068
|
+
// 键名必须是非空字符串
|
|
1069
|
+
if (!key || typeof key !== 'string') {
|
|
1070
|
+
$.log.error('[Redis] 参数错误: key必须是非空字符串');
|
|
1071
|
+
return false;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
826
1074
|
// 触发前置事件,允许修改或阻止操作
|
|
827
|
-
const beforeEventData = {
|
|
1075
|
+
const beforeEventData = {
|
|
1076
|
+
key,
|
|
1077
|
+
value,
|
|
1078
|
+
seconds,
|
|
1079
|
+
cancel: false
|
|
1080
|
+
};
|
|
828
1081
|
this.emit('set_before', beforeEventData);
|
|
829
1082
|
|
|
830
1083
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -834,25 +1087,34 @@ Redis.prototype.set = async function (key, value, seconds) {
|
|
|
834
1087
|
}
|
|
835
1088
|
|
|
836
1089
|
// 使用可能被修改的值
|
|
837
|
-
let {
|
|
1090
|
+
let {
|
|
1091
|
+
key: finalKey,
|
|
1092
|
+
value: finalValue,
|
|
1093
|
+
seconds: finalSeconds
|
|
1094
|
+
} = beforeEventData;
|
|
838
1095
|
|
|
839
1096
|
const connection = await this._getConnection();
|
|
840
1097
|
try {
|
|
841
|
-
|
|
842
|
-
if (typeof
|
|
1098
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
1099
|
+
if (typeof(finalValue) === "object") {
|
|
843
1100
|
finalValue = JSON.stringify(finalValue);
|
|
844
1101
|
}
|
|
845
1102
|
|
|
846
|
-
if (finalSeconds) {
|
|
847
|
-
await connection.set(
|
|
1103
|
+
if (finalSeconds > 0) {
|
|
1104
|
+
await connection.set(prefixedKey, finalValue, {
|
|
848
1105
|
EX: finalSeconds
|
|
849
1106
|
});
|
|
850
1107
|
} else {
|
|
851
|
-
await connection.set(
|
|
1108
|
+
await connection.set(prefixedKey, finalValue);
|
|
852
1109
|
}
|
|
853
1110
|
|
|
854
1111
|
// 触发后置事件
|
|
855
|
-
this.emit('set_after', {
|
|
1112
|
+
this.emit('set_after', {
|
|
1113
|
+
key,
|
|
1114
|
+
value,
|
|
1115
|
+
seconds,
|
|
1116
|
+
success: true
|
|
1117
|
+
});
|
|
856
1118
|
return true;
|
|
857
1119
|
} finally {
|
|
858
1120
|
this._recycleConnection(connection);
|
|
@@ -860,7 +1122,12 @@ Redis.prototype.set = async function (key, value, seconds) {
|
|
|
860
1122
|
} catch (err) {
|
|
861
1123
|
$.log.error(`[Redis] 设置缓存失败 [${key}]:`, err);
|
|
862
1124
|
// 触发错误事件
|
|
863
|
-
this.emit('set_error', {
|
|
1125
|
+
this.emit('set_error', {
|
|
1126
|
+
key,
|
|
1127
|
+
value,
|
|
1128
|
+
seconds,
|
|
1129
|
+
error: err
|
|
1130
|
+
});
|
|
864
1131
|
return false;
|
|
865
1132
|
}
|
|
866
1133
|
};
|
|
@@ -869,13 +1136,18 @@ Redis.prototype.set = async function (key, value, seconds) {
|
|
|
869
1136
|
* @description 仅当键不存在时设置缓存
|
|
870
1137
|
* @param {String} key 键
|
|
871
1138
|
* @param {Object} value 值
|
|
872
|
-
* @param {Number} seconds
|
|
873
|
-
* @return {Promise<Boolean>}
|
|
1139
|
+
* @param {Number} seconds 过期时间(秒)
|
|
1140
|
+
* @return {Promise<Boolean>} 操作结果,键不存在时设置成功返回true,键已存在返回false
|
|
874
1141
|
*/
|
|
875
|
-
Redis.prototype.add = async function
|
|
1142
|
+
Redis.prototype.add = async function(key, value, seconds) {
|
|
876
1143
|
try {
|
|
877
1144
|
// 触发前置事件,允许修改或阻止操作
|
|
878
|
-
const beforeEventData = {
|
|
1145
|
+
const beforeEventData = {
|
|
1146
|
+
key,
|
|
1147
|
+
value,
|
|
1148
|
+
ttl: seconds,
|
|
1149
|
+
cancel: false
|
|
1150
|
+
};
|
|
879
1151
|
this.emit('add_before', beforeEventData);
|
|
880
1152
|
|
|
881
1153
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -885,21 +1157,36 @@ Redis.prototype.add = async function (key, value, seconds) {
|
|
|
885
1157
|
}
|
|
886
1158
|
|
|
887
1159
|
// 使用可能被修改的值
|
|
888
|
-
let {
|
|
1160
|
+
let {
|
|
1161
|
+
key: finalKey,
|
|
1162
|
+
value: finalValue,
|
|
1163
|
+
ttl: finalSeconds
|
|
1164
|
+
} = beforeEventData;
|
|
889
1165
|
|
|
890
1166
|
const connection = await this._getConnection();
|
|
891
1167
|
try {
|
|
892
1168
|
finalKey = this.applyPrefix(finalKey);
|
|
893
|
-
if (typeof
|
|
1169
|
+
if (typeof(finalValue) === "object") {
|
|
894
1170
|
finalValue = JSON.stringify(finalValue);
|
|
895
1171
|
}
|
|
896
1172
|
|
|
897
|
-
|
|
1173
|
+
// 使用NX选项实现SETNX操作 - 仅在键不存在时设置
|
|
1174
|
+
const options = finalSeconds ? {
|
|
1175
|
+
NX: true,
|
|
1176
|
+
EX: finalSeconds
|
|
1177
|
+
} : {
|
|
1178
|
+
NX: true
|
|
1179
|
+
};
|
|
898
1180
|
const result = await connection.set(finalKey, finalValue, options);
|
|
899
1181
|
const success = result === "OK";
|
|
900
1182
|
|
|
901
1183
|
// 触发后置事件
|
|
902
|
-
this.emit('add_after', {
|
|
1184
|
+
this.emit('add_after', {
|
|
1185
|
+
key: finalKey,
|
|
1186
|
+
value: finalValue,
|
|
1187
|
+
ttl: finalSeconds,
|
|
1188
|
+
success
|
|
1189
|
+
});
|
|
903
1190
|
return success;
|
|
904
1191
|
} finally {
|
|
905
1192
|
this._recycleConnection(connection);
|
|
@@ -907,7 +1194,12 @@ Redis.prototype.add = async function (key, value, seconds) {
|
|
|
907
1194
|
} catch (err) {
|
|
908
1195
|
$.log.error(`[Redis] 添加缓存失败 [${key}]:`, err);
|
|
909
1196
|
// 触发错误事件
|
|
910
|
-
this.emit('add_error', {
|
|
1197
|
+
this.emit('add_error', {
|
|
1198
|
+
key,
|
|
1199
|
+
value,
|
|
1200
|
+
ttl: seconds,
|
|
1201
|
+
error: err
|
|
1202
|
+
});
|
|
911
1203
|
return false;
|
|
912
1204
|
}
|
|
913
1205
|
};
|
|
@@ -917,70 +1209,208 @@ Redis.prototype.add = async function (key, value, seconds) {
|
|
|
917
1209
|
* @param {String} key 键
|
|
918
1210
|
* @return {Promise<Object>} 查询值
|
|
919
1211
|
*/
|
|
920
|
-
|
|
1212
|
+
/**
|
|
1213
|
+
* @description 获取缓存(兼容方法)
|
|
1214
|
+
* @param {String} key 键
|
|
1215
|
+
* @return {Promise<*>} 获取到的值,不存在时返回null
|
|
1216
|
+
*/
|
|
1217
|
+
Redis.prototype.get = async function(key) {
|
|
921
1218
|
try {
|
|
1219
|
+
// 参数验证
|
|
1220
|
+
if (!key || typeof key !== 'string') {
|
|
1221
|
+
$.log.warn(`[Redis] get: key参数必须是非空字符串`);
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// 触发前置事件,允许修改或阻止操作
|
|
1226
|
+
const beforeEventData = {
|
|
1227
|
+
key,
|
|
1228
|
+
cancel: false
|
|
1229
|
+
};
|
|
1230
|
+
this.emit('get_before', beforeEventData);
|
|
1231
|
+
|
|
1232
|
+
// 如果事件处理器标记为取消,则返回null
|
|
1233
|
+
if (beforeEventData.cancel) {
|
|
1234
|
+
$.log.debug(`[Redis] get操作被事件处理器取消 [${key}]`);
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// 使用可能被修改的值
|
|
1239
|
+
const finalKey = beforeEventData.key;
|
|
1240
|
+
|
|
922
1241
|
const connection = await this._getConnection();
|
|
923
1242
|
try {
|
|
924
|
-
|
|
925
|
-
const value = await connection.get(
|
|
926
|
-
|
|
1243
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
1244
|
+
const value = await connection.get(prefixedKey);
|
|
1245
|
+
let result = value;
|
|
1246
|
+
if (typeof(value) === "string") {
|
|
927
1247
|
// 尝试解析JSON
|
|
928
1248
|
try {
|
|
929
1249
|
if (value.startsWith("{") && value.endsWith("}") ||
|
|
930
1250
|
value.startsWith("[") && value.endsWith("]")) {
|
|
931
|
-
|
|
1251
|
+
result = JSON.parse(value);
|
|
932
1252
|
}
|
|
933
1253
|
} catch (e) {
|
|
934
1254
|
// 解析失败,返回原始字符串
|
|
935
1255
|
}
|
|
936
1256
|
}
|
|
937
|
-
|
|
1257
|
+
|
|
1258
|
+
// 触发后置事件
|
|
1259
|
+
this.emit('get_after', {
|
|
1260
|
+
key: finalKey,
|
|
1261
|
+
value: result,
|
|
1262
|
+
success: true
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
return result;
|
|
938
1266
|
} finally {
|
|
939
1267
|
this._recycleConnection(connection);
|
|
940
1268
|
}
|
|
941
1269
|
} catch (err) {
|
|
1270
|
+
// 触发错误事件
|
|
1271
|
+
this.emit('get_error', {
|
|
1272
|
+
key,
|
|
1273
|
+
error: err
|
|
1274
|
+
});
|
|
942
1275
|
$.log.error(`[Redis] 获取缓存失败 [${key}]:`, err);
|
|
943
1276
|
return null;
|
|
944
1277
|
}
|
|
945
1278
|
};
|
|
946
1279
|
|
|
947
1280
|
/**
|
|
948
|
-
* @description
|
|
1281
|
+
* @description 判断键是否存在(兼容方法)
|
|
949
1282
|
* @param {String} key 键
|
|
950
1283
|
* @return {Promise<Boolean>} 有返回true, 没有返回false
|
|
951
1284
|
*/
|
|
952
|
-
Redis.prototype.has = async function
|
|
1285
|
+
Redis.prototype.has = async function(key) {
|
|
953
1286
|
try {
|
|
1287
|
+
// 参数验证
|
|
1288
|
+
if (!key || typeof key !== 'string') {
|
|
1289
|
+
$.log.warn(`[Redis] has: key参数必须是非空字符串`);
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// 触发前置事件,允许修改或阻止操作
|
|
1294
|
+
const beforeEventData = {
|
|
1295
|
+
key,
|
|
1296
|
+
cancel: false
|
|
1297
|
+
};
|
|
1298
|
+
this.emit('has_before', beforeEventData);
|
|
1299
|
+
|
|
1300
|
+
// 如果事件处理器标记为取消,则返回false
|
|
1301
|
+
if (beforeEventData.cancel) {
|
|
1302
|
+
$.log.debug(`[Redis] has操作被事件处理器取消 [${key}]`);
|
|
1303
|
+
return false;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// 使用可能被修改的值
|
|
1307
|
+
const finalKey = beforeEventData.key;
|
|
1308
|
+
|
|
954
1309
|
const connection = await this._getConnection();
|
|
955
1310
|
try {
|
|
956
|
-
|
|
957
|
-
const result = await connection.exists(
|
|
958
|
-
|
|
1311
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
1312
|
+
const result = await connection.exists(prefixedKey);
|
|
1313
|
+
const exists = result > 0;
|
|
1314
|
+
|
|
1315
|
+
// 触发后置事件
|
|
1316
|
+
this.emit('has_after', {
|
|
1317
|
+
key: finalKey,
|
|
1318
|
+
result: exists,
|
|
1319
|
+
success: true
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
return exists;
|
|
959
1323
|
} finally {
|
|
960
1324
|
this._recycleConnection(connection);
|
|
961
1325
|
}
|
|
962
1326
|
} catch (err) {
|
|
1327
|
+
// 触发错误事件
|
|
1328
|
+
this.emit('has_error', {
|
|
1329
|
+
key,
|
|
1330
|
+
error: err
|
|
1331
|
+
});
|
|
963
1332
|
$.log.error(`[Redis] 检查键是否存在失败 [${key}]:`, err);
|
|
964
1333
|
return false;
|
|
965
1334
|
}
|
|
966
1335
|
};
|
|
967
1336
|
|
|
968
1337
|
/**
|
|
969
|
-
* @description
|
|
970
|
-
* @param {String} key
|
|
1338
|
+
* @description 检查键是否存在,返回存在的键数量(兼容方法)
|
|
1339
|
+
* @param {String|Array<String>} key 键或键数组
|
|
971
1340
|
* @return {Promise<Number>} 存在的键数量
|
|
972
1341
|
*/
|
|
973
|
-
Redis.prototype.exists = async function
|
|
1342
|
+
Redis.prototype.exists = async function(key) {
|
|
974
1343
|
try {
|
|
1344
|
+
// 参数验证
|
|
1345
|
+
if (!key) {
|
|
1346
|
+
$.log.warn('[Redis] exists: key必须是非空字符串或非空字符串数组');
|
|
1347
|
+
return 0;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// 如果是单个键,验证是否为非空字符串
|
|
1351
|
+
if (typeof key === 'string') {
|
|
1352
|
+
if (!key) {
|
|
1353
|
+
$.log.warn('[Redis] exists: key必须是非空字符串');
|
|
1354
|
+
return 0;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
// 如果是数组,验证每个元素是否为非空字符串
|
|
1358
|
+
else if (Array.isArray(key)) {
|
|
1359
|
+
for (const k of key) {
|
|
1360
|
+
if (!k || typeof k !== 'string') {
|
|
1361
|
+
$.log.warn('[Redis] exists: key必须是非空字符串或非空字符串数组');
|
|
1362
|
+
return 0;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
} else {
|
|
1366
|
+
$.log.warn('[Redis] exists: key必须是非空字符串或非空字符串数组');
|
|
1367
|
+
return 0;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// 触发前置事件,允许修改或阻止操作
|
|
1371
|
+
const beforeEventData = {
|
|
1372
|
+
key,
|
|
1373
|
+
cancel: false
|
|
1374
|
+
};
|
|
1375
|
+
this.emit('exists_before', beforeEventData);
|
|
1376
|
+
|
|
1377
|
+
// 如果事件处理器标记为取消,则返回0
|
|
1378
|
+
if (beforeEventData.cancel) {
|
|
1379
|
+
$.log.debug(`[Redis] exists操作被事件处理器取消 [${key}]`);
|
|
1380
|
+
return 0;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// 使用可能被修改的值
|
|
1384
|
+
const finalKey = beforeEventData.key;
|
|
1385
|
+
|
|
975
1386
|
const connection = await this._getConnection();
|
|
976
1387
|
try {
|
|
977
|
-
|
|
978
|
-
|
|
1388
|
+
let result;
|
|
1389
|
+
if (typeof finalKey === 'string') {
|
|
1390
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
1391
|
+
result = await connection.exists(prefixedKey);
|
|
1392
|
+
} else if (Array.isArray(finalKey)) {
|
|
1393
|
+
const prefixedKeys = finalKey.map(k => this.applyPrefix(k));
|
|
1394
|
+
result = await connection.exists(...prefixedKeys);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// 触发后置事件
|
|
1398
|
+
this.emit('exists_after', {
|
|
1399
|
+
key: finalKey,
|
|
1400
|
+
result,
|
|
1401
|
+
success: true
|
|
1402
|
+
});
|
|
1403
|
+
|
|
979
1404
|
return result;
|
|
980
1405
|
} finally {
|
|
981
1406
|
this._recycleConnection(connection);
|
|
982
1407
|
}
|
|
983
1408
|
} catch (err) {
|
|
1409
|
+
// 触发错误事件
|
|
1410
|
+
this.emit('exists_error', {
|
|
1411
|
+
key,
|
|
1412
|
+
error: err
|
|
1413
|
+
});
|
|
984
1414
|
$.log.error(`[Redis] 检查键存在性失败 [${key}]:`, err);
|
|
985
1415
|
return 0;
|
|
986
1416
|
}
|
|
@@ -993,7 +1423,7 @@ Redis.prototype.exists = async function (key) {
|
|
|
993
1423
|
* @param {Number} end 结束位置
|
|
994
1424
|
* @return {Promise<String>} 查询值
|
|
995
1425
|
*/
|
|
996
|
-
Redis.prototype.getrange = async function
|
|
1426
|
+
Redis.prototype.getrange = async function(key, start, end) {
|
|
997
1427
|
try {
|
|
998
1428
|
const connection = await this._getConnection();
|
|
999
1429
|
try {
|
|
@@ -1023,7 +1453,7 @@ Redis.prototype.getrange = async function (key, start, end) {
|
|
|
1023
1453
|
* @param {String} str_sub 插入的字符串
|
|
1024
1454
|
* @return {String} 插入后的字符串
|
|
1025
1455
|
*/
|
|
1026
|
-
Redis.prototype.insertString = function
|
|
1456
|
+
Redis.prototype.insertString = function(str, index, str_sub) {
|
|
1027
1457
|
return str.substring(0, index) + str_sub + str.substring(index);
|
|
1028
1458
|
};
|
|
1029
1459
|
|
|
@@ -1034,10 +1464,15 @@ Redis.prototype.insertString = function (str, index, str_sub) {
|
|
|
1034
1464
|
* @param {String} value 变更的值
|
|
1035
1465
|
* @return {Promise<Number>} 字符串长度
|
|
1036
1466
|
*/
|
|
1037
|
-
Redis.prototype.setrange = async function
|
|
1467
|
+
Redis.prototype.setrange = async function(key, index, value) {
|
|
1038
1468
|
try {
|
|
1039
1469
|
// 触发前置事件,允许修改或阻止操作
|
|
1040
|
-
const beforeEventData = {
|
|
1470
|
+
const beforeEventData = {
|
|
1471
|
+
key,
|
|
1472
|
+
index,
|
|
1473
|
+
value,
|
|
1474
|
+
cancel: false
|
|
1475
|
+
};
|
|
1041
1476
|
this.emit('setrange_before', beforeEventData);
|
|
1042
1477
|
|
|
1043
1478
|
// 如果事件处理器标记为取消,则返回0
|
|
@@ -1047,7 +1482,11 @@ Redis.prototype.setrange = async function (key, index, value) {
|
|
|
1047
1482
|
}
|
|
1048
1483
|
|
|
1049
1484
|
// 使用可能被修改的值
|
|
1050
|
-
let {
|
|
1485
|
+
let {
|
|
1486
|
+
key: finalKey,
|
|
1487
|
+
index: finalIndex,
|
|
1488
|
+
value: finalValue
|
|
1489
|
+
} = beforeEventData;
|
|
1051
1490
|
let result;
|
|
1052
1491
|
|
|
1053
1492
|
const connection = await this._getConnection();
|
|
@@ -1076,7 +1515,13 @@ Redis.prototype.setrange = async function (key, index, value) {
|
|
|
1076
1515
|
}
|
|
1077
1516
|
|
|
1078
1517
|
// 触发后置事件
|
|
1079
|
-
this.emit('setrange_after', {
|
|
1518
|
+
this.emit('setrange_after', {
|
|
1519
|
+
key: finalKey,
|
|
1520
|
+
index: finalIndex,
|
|
1521
|
+
value: finalValue,
|
|
1522
|
+
result,
|
|
1523
|
+
success: true
|
|
1524
|
+
});
|
|
1080
1525
|
return result;
|
|
1081
1526
|
} catch (err) {
|
|
1082
1527
|
$.log.error('[Redis] 设置字符串范围失败:', err);
|
|
@@ -1090,14 +1535,25 @@ Redis.prototype.setrange = async function (key, index, value) {
|
|
|
1090
1535
|
await this.set(finalKey, str);
|
|
1091
1536
|
|
|
1092
1537
|
// 触发后置事件
|
|
1093
|
-
this.emit('setrange_after', {
|
|
1538
|
+
this.emit('setrange_after', {
|
|
1539
|
+
key: finalKey,
|
|
1540
|
+
index: finalIndex,
|
|
1541
|
+
value: finalValue,
|
|
1542
|
+
result: str.length,
|
|
1543
|
+
success: true
|
|
1544
|
+
});
|
|
1094
1545
|
return str.length;
|
|
1095
1546
|
} finally {
|
|
1096
1547
|
this._recycleConnection(connection);
|
|
1097
1548
|
}
|
|
1098
1549
|
} catch (err) {
|
|
1099
1550
|
// 触发错误事件
|
|
1100
|
-
this.emit('setrange_error', {
|
|
1551
|
+
this.emit('setrange_error', {
|
|
1552
|
+
key,
|
|
1553
|
+
index,
|
|
1554
|
+
value,
|
|
1555
|
+
error: err
|
|
1556
|
+
});
|
|
1101
1557
|
$.log.error(`[Redis] 设置字符串范围失败 [${key}]:`, err);
|
|
1102
1558
|
return 0;
|
|
1103
1559
|
}
|
|
@@ -1108,10 +1564,13 @@ Redis.prototype.setrange = async function (key, index, value) {
|
|
|
1108
1564
|
* @param {String} key 键, 为空则清空所有
|
|
1109
1565
|
* @return {Promise<Boolean>} 执行结果
|
|
1110
1566
|
*/
|
|
1111
|
-
Redis.prototype.clear = async function
|
|
1567
|
+
Redis.prototype.clear = async function(key) {
|
|
1112
1568
|
try {
|
|
1113
1569
|
// 触发前置事件,允许修改或阻止操作
|
|
1114
|
-
const beforeEventData = {
|
|
1570
|
+
const beforeEventData = {
|
|
1571
|
+
key,
|
|
1572
|
+
cancel: false
|
|
1573
|
+
};
|
|
1115
1574
|
this.emit('clear_before', beforeEventData);
|
|
1116
1575
|
|
|
1117
1576
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -1121,7 +1580,9 @@ Redis.prototype.clear = async function (key) {
|
|
|
1121
1580
|
}
|
|
1122
1581
|
|
|
1123
1582
|
// 使用可能被修改的值
|
|
1124
|
-
let {
|
|
1583
|
+
let {
|
|
1584
|
+
key: finalKey
|
|
1585
|
+
} = beforeEventData;
|
|
1125
1586
|
let success = false;
|
|
1126
1587
|
|
|
1127
1588
|
const connection = await this._getConnection();
|
|
@@ -1144,14 +1605,20 @@ Redis.prototype.clear = async function (key) {
|
|
|
1144
1605
|
}
|
|
1145
1606
|
|
|
1146
1607
|
// 触发后置事件
|
|
1147
|
-
this.emit('clear_after', {
|
|
1608
|
+
this.emit('clear_after', {
|
|
1609
|
+
key: finalKey,
|
|
1610
|
+
success
|
|
1611
|
+
});
|
|
1148
1612
|
return success;
|
|
1149
1613
|
} finally {
|
|
1150
1614
|
this._recycleConnection(connection);
|
|
1151
1615
|
}
|
|
1152
1616
|
} catch (err) {
|
|
1153
1617
|
// 触发错误事件
|
|
1154
|
-
this.emit('clear_error', {
|
|
1618
|
+
this.emit('clear_error', {
|
|
1619
|
+
key,
|
|
1620
|
+
error: err
|
|
1621
|
+
});
|
|
1155
1622
|
$.log.error('[Redis] 清空缓存失败:', err);
|
|
1156
1623
|
return false;
|
|
1157
1624
|
}
|
|
@@ -1163,7 +1630,7 @@ Redis.prototype.clear = async function (key) {
|
|
|
1163
1630
|
* @param {Object} options 排序选项
|
|
1164
1631
|
* @return {Promise<Array>} 排序后的数组
|
|
1165
1632
|
*/
|
|
1166
|
-
Redis.prototype.sort = async function
|
|
1633
|
+
Redis.prototype.sort = async function(key, options = {}) {
|
|
1167
1634
|
try {
|
|
1168
1635
|
const connection = await this._getConnection();
|
|
1169
1636
|
try {
|
|
@@ -1198,7 +1665,7 @@ Redis.prototype.sort = async function (key, options = {}) {
|
|
|
1198
1665
|
} catch (err) {
|
|
1199
1666
|
$.log.error('[Redis] 排序失败:', err);
|
|
1200
1667
|
// 回退方案:获取数据后在内存中排序
|
|
1201
|
-
const data = await this.
|
|
1668
|
+
const data = await this.getForList(key);
|
|
1202
1669
|
return data.sort((a, b) => {
|
|
1203
1670
|
if (options.alpha) {
|
|
1204
1671
|
return options.order === 'desc' ? b.localeCompare(a) : a.localeCompare(b);
|
|
@@ -1220,7 +1687,7 @@ Redis.prototype.sort = async function (key, options = {}) {
|
|
|
1220
1687
|
* @param {String} pattern 键模式 支持*号
|
|
1221
1688
|
* @return {Promise<Array>} 键数组
|
|
1222
1689
|
*/
|
|
1223
|
-
Redis.prototype.keys = async function
|
|
1690
|
+
Redis.prototype.keys = async function(pattern = '*') {
|
|
1224
1691
|
try {
|
|
1225
1692
|
const connection = await this._getConnection();
|
|
1226
1693
|
try {
|
|
@@ -1245,20 +1712,29 @@ Redis.prototype.keys = async function (pattern = '*') {
|
|
|
1245
1712
|
* @param {Number} seconds 秒
|
|
1246
1713
|
* @return {Promise<Number>} 数组长度
|
|
1247
1714
|
*/
|
|
1248
|
-
Redis.prototype.
|
|
1715
|
+
Redis.prototype.setForList = async function(key, value, seconds) {
|
|
1249
1716
|
try {
|
|
1250
1717
|
// 触发前置事件,允许修改或阻止操作
|
|
1251
|
-
const beforeEventData = {
|
|
1252
|
-
|
|
1718
|
+
const beforeEventData = {
|
|
1719
|
+
key,
|
|
1720
|
+
value,
|
|
1721
|
+
ttl: seconds,
|
|
1722
|
+
cancel: false
|
|
1723
|
+
};
|
|
1724
|
+
this.emit('setForList_before', beforeEventData);
|
|
1253
1725
|
|
|
1254
1726
|
// 如果事件处理器标记为取消,则返回0
|
|
1255
1727
|
if (beforeEventData.cancel) {
|
|
1256
|
-
$.log.debug(`[Redis]
|
|
1728
|
+
$.log.debug(`[Redis] setForList操作被事件处理器取消 [${key}]`);
|
|
1257
1729
|
return 0;
|
|
1258
1730
|
}
|
|
1259
1731
|
|
|
1260
1732
|
// 使用可能被修改的值
|
|
1261
|
-
let {
|
|
1733
|
+
let {
|
|
1734
|
+
key: finalKey,
|
|
1735
|
+
value: finalValue,
|
|
1736
|
+
ttl: finalSeconds
|
|
1737
|
+
} = beforeEventData;
|
|
1262
1738
|
let len = 0;
|
|
1263
1739
|
|
|
1264
1740
|
const connection = await this._getConnection();
|
|
@@ -1306,14 +1782,24 @@ Redis.prototype.list_set = async function (key, value, seconds) {
|
|
|
1306
1782
|
}
|
|
1307
1783
|
|
|
1308
1784
|
// 触发后置事件
|
|
1309
|
-
this.emit('
|
|
1785
|
+
this.emit('setForList_after', {
|
|
1786
|
+
key: finalKey,
|
|
1787
|
+
value: finalValue,
|
|
1788
|
+
ttl: finalSeconds,
|
|
1789
|
+
result: len,
|
|
1790
|
+
success: len > 0
|
|
1791
|
+
});
|
|
1310
1792
|
return len;
|
|
1311
1793
|
} finally {
|
|
1312
1794
|
this._recycleConnection(connection);
|
|
1313
1795
|
}
|
|
1314
1796
|
} catch (err) {
|
|
1315
1797
|
// 触发错误事件
|
|
1316
|
-
this.emit('
|
|
1798
|
+
this.emit('setForList_error', {
|
|
1799
|
+
key,
|
|
1800
|
+
value,
|
|
1801
|
+
error: err
|
|
1802
|
+
});
|
|
1317
1803
|
$.log.error(`[Redis] 设置列表失败 [${key}]:`, err);
|
|
1318
1804
|
return 0;
|
|
1319
1805
|
}
|
|
@@ -1325,10 +1811,14 @@ Redis.prototype.list_set = async function (key, value, seconds) {
|
|
|
1325
1811
|
* @param {Object|Array} value 值
|
|
1326
1812
|
* @return {Promise<Number>} 追加后的数组长度
|
|
1327
1813
|
*/
|
|
1328
|
-
Redis.prototype.list_push = async function
|
|
1814
|
+
Redis.prototype.list_push = async function(key, value) {
|
|
1329
1815
|
try {
|
|
1330
1816
|
// 触发前置事件,允许修改或阻止操作
|
|
1331
|
-
const beforeEventData = {
|
|
1817
|
+
const beforeEventData = {
|
|
1818
|
+
key,
|
|
1819
|
+
value,
|
|
1820
|
+
cancel: false
|
|
1821
|
+
};
|
|
1332
1822
|
this.emit('list_push_before', beforeEventData);
|
|
1333
1823
|
|
|
1334
1824
|
// 如果事件处理器标记为取消,则返回0
|
|
@@ -1338,7 +1828,10 @@ Redis.prototype.list_push = async function (key, value) {
|
|
|
1338
1828
|
}
|
|
1339
1829
|
|
|
1340
1830
|
// 使用可能被修改的值
|
|
1341
|
-
let {
|
|
1831
|
+
let {
|
|
1832
|
+
key: finalKey,
|
|
1833
|
+
value: finalValue
|
|
1834
|
+
} = beforeEventData;
|
|
1342
1835
|
let len = 0;
|
|
1343
1836
|
let success = true;
|
|
1344
1837
|
|
|
@@ -1371,84 +1864,214 @@ Redis.prototype.list_push = async function (key, value) {
|
|
|
1371
1864
|
}
|
|
1372
1865
|
|
|
1373
1866
|
// 触发后置事件
|
|
1374
|
-
this.emit('list_push_after', {
|
|
1867
|
+
this.emit('list_push_after', {
|
|
1868
|
+
key: finalKey,
|
|
1869
|
+
value: finalValue,
|
|
1870
|
+
result: len,
|
|
1871
|
+
success
|
|
1872
|
+
});
|
|
1375
1873
|
return len;
|
|
1376
1874
|
} finally {
|
|
1377
1875
|
this._recycleConnection(connection);
|
|
1378
1876
|
}
|
|
1379
1877
|
} catch (err) {
|
|
1380
1878
|
// 触发错误事件
|
|
1381
|
-
this.emit('list_push_error', {
|
|
1879
|
+
this.emit('list_push_error', {
|
|
1880
|
+
key,
|
|
1881
|
+
value,
|
|
1882
|
+
error: err
|
|
1883
|
+
});
|
|
1382
1884
|
$.log.error(`[Redis] 向列表追加元素失败 [${key}]:`, err);
|
|
1383
1885
|
return 0;
|
|
1384
1886
|
}
|
|
1385
1887
|
};
|
|
1386
1888
|
|
|
1387
1889
|
/**
|
|
1388
|
-
* @description 数组缓存追加对象
|
|
1890
|
+
* @description 数组缓存追加对象
|
|
1389
1891
|
* @param {String} key 键
|
|
1390
1892
|
* @param {Object|Array} value 值
|
|
1893
|
+
* @param {Number} seconds 秒
|
|
1391
1894
|
* @return {Promise<Number>} 追加后的数组长度
|
|
1392
1895
|
*/
|
|
1393
|
-
Redis.prototype.
|
|
1896
|
+
Redis.prototype.addForList = async function(key, value, seconds = 0) {
|
|
1897
|
+
try {
|
|
1898
|
+
// 触发前置事件,允许修改或阻止操作
|
|
1899
|
+
const beforeEventData = {
|
|
1900
|
+
key,
|
|
1901
|
+
value,
|
|
1902
|
+
ttl: seconds,
|
|
1903
|
+
cancel: false
|
|
1904
|
+
};
|
|
1905
|
+
this.emit('addForList_before', beforeEventData);
|
|
1394
1906
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
*/
|
|
1401
|
-
Redis.prototype.list_has = async function (key, value) {
|
|
1402
|
-
if (!value) {
|
|
1403
|
-
throw new Error('值不能为空');
|
|
1404
|
-
}
|
|
1907
|
+
// 如果事件处理器标记为取消,则返回0
|
|
1908
|
+
if (beforeEventData.cancel) {
|
|
1909
|
+
$.log.debug(`[Redis] addForList操作被事件处理器取消 [${key}]`);
|
|
1910
|
+
return 0;
|
|
1911
|
+
}
|
|
1405
1912
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
return arr.some(item => {
|
|
1414
|
-
const itemStr = JSON.stringify(item);
|
|
1415
|
-
return itemStr === valueStr || item === value;
|
|
1416
|
-
});
|
|
1417
|
-
} catch (err) {
|
|
1418
|
-
$.log.error(`[Redis] 检查列表成员是否存在失败 [${key}]:`, err);
|
|
1419
|
-
return false;
|
|
1420
|
-
}
|
|
1421
|
-
};
|
|
1913
|
+
// 使用可能被修改的值
|
|
1914
|
+
let {
|
|
1915
|
+
key: finalKey,
|
|
1916
|
+
value: finalValue,
|
|
1917
|
+
ttl: finalSeconds
|
|
1918
|
+
} = beforeEventData;
|
|
1919
|
+
let len = 0;
|
|
1422
1920
|
|
|
1423
|
-
/**
|
|
1424
|
-
* @description 查询数组缓存
|
|
1425
|
-
* @param {String} key 键
|
|
1426
|
-
* @param {Number} start 起始位置
|
|
1427
|
-
* @param {Number} end 结束位置
|
|
1428
|
-
* @return {Promise<Array>} 查询到的数组
|
|
1429
|
-
*/
|
|
1430
|
-
Redis.prototype.list_get = async function (key, start = 0, end = -1) {
|
|
1431
|
-
try {
|
|
1432
1921
|
const connection = await this._getConnection();
|
|
1433
1922
|
try {
|
|
1434
|
-
|
|
1923
|
+
finalKey = this.applyPrefix(finalKey);
|
|
1924
|
+
|
|
1925
|
+
if (Array.isArray(finalValue)) {
|
|
1926
|
+
if (finalValue.length > 0) {
|
|
1927
|
+
// 添加数组元素
|
|
1928
|
+
if (typeof connection.rPush === 'function') {
|
|
1929
|
+
len = await connection.rPush(finalKey, finalValue);
|
|
1930
|
+
} else if (typeof connection.execute === 'function') {
|
|
1931
|
+
len = await connection.execute('RPUSH', [finalKey, ...finalValue]);
|
|
1932
|
+
} else {
|
|
1933
|
+
$.log.error('[Redis] 连接对象不支持rPush操作');
|
|
1934
|
+
len = 0;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
} else {
|
|
1938
|
+
// 添加单个元素
|
|
1939
|
+
if (typeof connection.rPush === 'function') {
|
|
1940
|
+
len = await connection.rPush(finalKey, finalValue);
|
|
1941
|
+
} else if (typeof connection.execute === 'function') {
|
|
1942
|
+
len = await connection.execute('RPUSH', [finalKey, finalValue]);
|
|
1943
|
+
} else {
|
|
1944
|
+
$.log.error('[Redis] 连接对象不支持rPush操作');
|
|
1945
|
+
len = 0;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
if (finalSeconds) {
|
|
1949
|
+
// 设置过期时间
|
|
1950
|
+
if (typeof connection.expire === 'function') {
|
|
1951
|
+
await connection.expire(finalKey, finalSeconds);
|
|
1952
|
+
} else if (typeof connection.execute === 'function') {
|
|
1953
|
+
await connection.execute('EXPIRE', [finalKey, finalSeconds]);
|
|
1954
|
+
} else {
|
|
1955
|
+
$.log.error('[Redis] 连接对象不支持expire操作');
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
// 触发后置事件
|
|
1960
|
+
this.emit('addForList_after', {
|
|
1961
|
+
key: finalKey,
|
|
1962
|
+
value: finalValue,
|
|
1963
|
+
ttl: finalSeconds,
|
|
1964
|
+
result: len,
|
|
1965
|
+
success: len > 0
|
|
1966
|
+
});
|
|
1967
|
+
return len;
|
|
1968
|
+
} finally {
|
|
1969
|
+
this._recycleConnection(connection);
|
|
1970
|
+
}
|
|
1971
|
+
} catch (err) {
|
|
1972
|
+
// 触发错误事件
|
|
1973
|
+
this.emit('addForList_error', {
|
|
1974
|
+
key,
|
|
1975
|
+
value,
|
|
1976
|
+
error: err
|
|
1977
|
+
});
|
|
1978
|
+
$.log.error(`[Redis] 添加列表项失败 [${key}]:`, err);
|
|
1979
|
+
return 0;
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
/**
|
|
1984
|
+
* @description 判断成员是否存在
|
|
1985
|
+
* @param {String} key 键
|
|
1986
|
+
* @param {Object} value 值
|
|
1987
|
+
* @return {Promise<Boolean>} 存在返回true, 否则返回false
|
|
1988
|
+
*/
|
|
1989
|
+
Redis.prototype.hasForList = async function(key, value) {
|
|
1990
|
+
if (!value) {
|
|
1991
|
+
$.log.error('[Redis] 参数错误: 值不能为空');
|
|
1992
|
+
return false;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
try {
|
|
1996
|
+
// 尝试使用Redis的LREM命令来检查是否存在
|
|
1997
|
+
const keyWithPrefix = this.applyPrefix(key);
|
|
1998
|
+
// 转换value为字符串以便比较
|
|
1999
|
+
const valueStr = JSON.stringify(value);
|
|
2000
|
+
// 使用scan来查找,避免在大列表上的性能问题
|
|
2001
|
+
const arr = await this.getForList(key);
|
|
2002
|
+
return arr.some(item => {
|
|
2003
|
+
const itemStr = JSON.stringify(item);
|
|
2004
|
+
return itemStr === valueStr || item === value;
|
|
2005
|
+
});
|
|
2006
|
+
} catch (err) {
|
|
2007
|
+
$.log.error(`[Redis] 检查列表成员是否存在失败 [${key}]:`, err);
|
|
2008
|
+
return false;
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
|
|
2012
|
+
/**
|
|
2013
|
+
* @description 查询数组缓存
|
|
2014
|
+
* @param {String} key 键
|
|
2015
|
+
* @param {Number} start 起始位置
|
|
2016
|
+
* @param {Number} end 结束位置
|
|
2017
|
+
* @return {Promise<Array>} 查询到的数组
|
|
2018
|
+
*/
|
|
2019
|
+
Redis.prototype.getForList = async function(key, start = 0, end = -1) {
|
|
2020
|
+
try {
|
|
2021
|
+
// 触发前置事件,允许修改或阻止操作
|
|
2022
|
+
const beforeEventData = {
|
|
2023
|
+
key,
|
|
2024
|
+
start,
|
|
2025
|
+
end,
|
|
2026
|
+
cancel: false
|
|
2027
|
+
};
|
|
2028
|
+
this.emit('getForList_before', beforeEventData);
|
|
2029
|
+
|
|
2030
|
+
// 如果事件处理器标记为取消,则返回空数组
|
|
2031
|
+
if (beforeEventData.cancel) {
|
|
2032
|
+
$.log.debug(`[Redis] getForList操作被事件处理器取消 [${key}]`);
|
|
2033
|
+
return [];
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// 使用可能被修改的值
|
|
2037
|
+
let {
|
|
2038
|
+
key: finalKey,
|
|
2039
|
+
start: finalStart,
|
|
2040
|
+
end: finalEnd
|
|
2041
|
+
} = beforeEventData;
|
|
2042
|
+
|
|
2043
|
+
const connection = await this._getConnection();
|
|
2044
|
+
try {
|
|
2045
|
+
finalKey = this.applyPrefix(finalKey);
|
|
1435
2046
|
let result;
|
|
1436
|
-
// 检查连接是否支持lRange
|
|
2047
|
+
// 检查连接是否支持lRange方法,Redis v4 API
|
|
1437
2048
|
if (typeof connection.lRange === 'function') {
|
|
1438
|
-
result = await connection.lRange(
|
|
2049
|
+
result = await connection.lRange(finalKey, finalStart, finalEnd);
|
|
2050
|
+
} else if (typeof connection.lrange === 'function') {
|
|
2051
|
+
// 兼容旧版API
|
|
2052
|
+
result = await connection.lrange(finalKey, finalStart, finalEnd);
|
|
1439
2053
|
} else if (typeof connection.execute === 'function') {
|
|
1440
|
-
|
|
2054
|
+
// 回退到通用execute方法
|
|
2055
|
+
result = await connection.execute('LRANGE', [finalKey, finalStart, finalEnd]);
|
|
1441
2056
|
} else {
|
|
1442
|
-
$.log.error('[Redis] 连接对象不支持lRange操作');
|
|
2057
|
+
$.log.error('[Redis] 连接对象不支持lRange或lrange操作');
|
|
2058
|
+
return [];
|
|
2059
|
+
}
|
|
2060
|
+
// 确保结果是数组
|
|
2061
|
+
if (!Array.isArray(result)) {
|
|
2062
|
+
$.log.error('[Redis] 列表查询结果不是数组格式');
|
|
1443
2063
|
return [];
|
|
1444
2064
|
}
|
|
2065
|
+
|
|
1445
2066
|
// 尝试解析每个元素为JSON
|
|
1446
|
-
|
|
1447
|
-
if (typeof
|
|
2067
|
+
const parsedResult = result.map(item => {
|
|
2068
|
+
if (typeof item === "string") {
|
|
1448
2069
|
try {
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
2070
|
+
// 尝试识别并解析JSON字符串
|
|
2071
|
+
const trimmedItem = item.trim();
|
|
2072
|
+
if ((trimmedItem.startsWith("{") && trimmedItem.endsWith("}")) ||
|
|
2073
|
+
(trimmedItem.startsWith("[") && trimmedItem.endsWith("]"))) {
|
|
2074
|
+
return JSON.parse(trimmedItem);
|
|
1452
2075
|
}
|
|
1453
2076
|
} catch (e) {
|
|
1454
2077
|
// 解析失败,返回原始字符串
|
|
@@ -1456,96 +2079,76 @@ Redis.prototype.list_get = async function (key, start = 0, end = -1) {
|
|
|
1456
2079
|
}
|
|
1457
2080
|
return item;
|
|
1458
2081
|
});
|
|
2082
|
+
|
|
2083
|
+
// 触发后置事件
|
|
2084
|
+
this.emit('getForList_after', {
|
|
2085
|
+
key: finalKey,
|
|
2086
|
+
start: finalStart,
|
|
2087
|
+
end: finalEnd,
|
|
2088
|
+
result: parsedResult
|
|
2089
|
+
});
|
|
2090
|
+
return parsedResult;
|
|
1459
2091
|
} finally {
|
|
1460
2092
|
this._recycleConnection(connection);
|
|
1461
2093
|
}
|
|
1462
2094
|
} catch (err) {
|
|
2095
|
+
// 触发错误事件
|
|
2096
|
+
this.emit('getForList_error', {
|
|
2097
|
+
key,
|
|
2098
|
+
start,
|
|
2099
|
+
end,
|
|
2100
|
+
error: err
|
|
2101
|
+
});
|
|
1463
2102
|
$.log.error(`[Redis] 获取列表失败 [${key}]:`, err);
|
|
1464
2103
|
return [];
|
|
1465
2104
|
}
|
|
1466
2105
|
};
|
|
1467
2106
|
|
|
1468
2107
|
/**
|
|
1469
|
-
* @description
|
|
2108
|
+
* @description 清空列表,保留键和过期时间
|
|
1470
2109
|
* @param {String} key 键
|
|
1471
|
-
* @param {Array} value 新成员, 没有则删除数组
|
|
1472
2110
|
* @return {Promise<Boolean>} 成功返回true,失败返回false
|
|
1473
2111
|
*/
|
|
1474
|
-
Redis.prototype.
|
|
2112
|
+
Redis.prototype.clearForList = async function(key) {
|
|
1475
2113
|
if (!key) {
|
|
1476
|
-
|
|
2114
|
+
$.log.error('[Redis] 参数错误: 键不能为空');
|
|
2115
|
+
return false;
|
|
1477
2116
|
}
|
|
1478
2117
|
|
|
1479
2118
|
try {
|
|
1480
2119
|
// 触发前置事件,允许修改或阻止操作
|
|
1481
|
-
const beforeEventData = {
|
|
1482
|
-
|
|
2120
|
+
const beforeEventData = {
|
|
2121
|
+
key,
|
|
2122
|
+
cancel: false
|
|
2123
|
+
};
|
|
2124
|
+
this.emit('clearForList_before', beforeEventData);
|
|
1483
2125
|
|
|
1484
2126
|
// 如果事件处理器标记为取消,则返回false
|
|
1485
2127
|
if (beforeEventData.cancel) {
|
|
1486
|
-
$.log.debug(`[Redis]
|
|
2128
|
+
$.log.debug(`[Redis] clearForList操作被事件处理器取消 [${key}]`);
|
|
1487
2129
|
return false;
|
|
1488
2130
|
}
|
|
1489
2131
|
|
|
1490
2132
|
// 使用可能被修改的值
|
|
1491
|
-
let
|
|
1492
|
-
|
|
1493
|
-
const connection = await this._getConnection();
|
|
1494
|
-
try {
|
|
1495
|
-
finalKey = this.applyPrefix(finalKey);
|
|
1496
|
-
let success = true;
|
|
2133
|
+
let finalKey = beforeEventData.key;
|
|
1497
2134
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
await connection.execute('DEL', [finalKey]);
|
|
1503
|
-
} else {
|
|
1504
|
-
$.log.error('[Redis] 连接对象不支持del操作');
|
|
1505
|
-
success = false;
|
|
1506
|
-
}
|
|
2135
|
+
// 直接使用set方法设置为空数组,这样更可靠且与其他模块行为一致
|
|
2136
|
+
// 这样可以确保返回值是一个空数组,而不是null或undefined
|
|
2137
|
+
finalKey = this.applyPrefix(finalKey);
|
|
2138
|
+
await this.set(finalKey, []);
|
|
1507
2139
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
if (typeof connection.rPush === 'function') {
|
|
1515
|
-
await connection.rPush(finalKey, finalValue);
|
|
1516
|
-
} else if (typeof connection.execute === 'function') {
|
|
1517
|
-
await connection.execute('RPUSH', [finalKey, ...finalValue]);
|
|
1518
|
-
} else {
|
|
1519
|
-
$.log.error('[Redis] 连接对象不支持rPush操作');
|
|
1520
|
-
success = false;
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
} else {
|
|
1524
|
-
// 添加单个元素
|
|
1525
|
-
if (typeof connection.rPush === 'function') {
|
|
1526
|
-
await connection.rPush(finalKey, finalValue);
|
|
1527
|
-
} else if (typeof connection.execute === 'function') {
|
|
1528
|
-
await connection.execute('RPUSH', [finalKey, finalValue]);
|
|
1529
|
-
} else {
|
|
1530
|
-
$.log.error('[Redis] 连接对象不支持rPush操作');
|
|
1531
|
-
success = false;
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
} catch (err) {
|
|
1535
|
-
$.log.error(`[Redis] 重新设置列表值失败 [${finalKey}]:`, err);
|
|
1536
|
-
success = false;
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
// 触发后置事件
|
|
1541
|
-
this.emit('list_clear_after', { key: finalKey, value: finalValue, success });
|
|
1542
|
-
return success;
|
|
1543
|
-
} finally {
|
|
1544
|
-
this._recycleConnection(connection);
|
|
1545
|
-
}
|
|
2140
|
+
// 触发后置事件
|
|
2141
|
+
this.emit('clearForList_after', {
|
|
2142
|
+
key: finalKey,
|
|
2143
|
+
success: true
|
|
2144
|
+
});
|
|
2145
|
+
return true;
|
|
1546
2146
|
} catch (err) {
|
|
1547
2147
|
// 触发错误事件
|
|
1548
|
-
this.emit('
|
|
2148
|
+
this.emit('clearForList_error', {
|
|
2149
|
+
key,
|
|
2150
|
+
error: err
|
|
2151
|
+
});
|
|
1549
2152
|
$.log.error(`[Redis] 清空列表失败 [${key}]:`, err);
|
|
1550
2153
|
return false;
|
|
1551
2154
|
}
|
|
@@ -1556,58 +2159,119 @@ Redis.prototype.list_clear = async function (key, value) {
|
|
|
1556
2159
|
* @param {Array<String>} keys 键数组
|
|
1557
2160
|
* @return {Promise<Object>} 键值对对象
|
|
1558
2161
|
*/
|
|
1559
|
-
|
|
1560
|
-
|
|
2162
|
+
/**
|
|
2163
|
+
* @description 批量获取缓存(兼容方法)
|
|
2164
|
+
* @param {Array<String>} keys 键数组
|
|
2165
|
+
* @return {Promise<Array>} 返回值数组,顺序与键数组一致
|
|
2166
|
+
*/
|
|
2167
|
+
Redis.prototype.mget = async function(keys) {
|
|
1561
2168
|
try {
|
|
1562
|
-
//
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
2169
|
+
// 参数验证
|
|
2170
|
+
if (!keys || !Array.isArray(keys)) {
|
|
2171
|
+
$.log.warn('[Redis] mget: keys必须是非空数组');
|
|
2172
|
+
return [];
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// 验证数组中的每个元素是否为非空字符串
|
|
2176
|
+
for (const key of keys) {
|
|
2177
|
+
if (!key || typeof key !== 'string') {
|
|
2178
|
+
$.log.warn('[Redis] mget: keys数组中的每个元素必须是非空字符串');
|
|
2179
|
+
return [];
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// 触发前置事件,允许修改或阻止操作
|
|
2184
|
+
const beforeEventData = {
|
|
2185
|
+
keys,
|
|
2186
|
+
cancel: false
|
|
2187
|
+
};
|
|
2188
|
+
this.emit('mget_before', beforeEventData);
|
|
2189
|
+
|
|
2190
|
+
// 如果事件处理器标记为取消,则返回空数组
|
|
2191
|
+
if (beforeEventData.cancel) {
|
|
2192
|
+
$.log.debug(`[Redis] mget操作被事件处理器取消 [${keys}]`);
|
|
2193
|
+
return [];
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// 使用可能被修改的值
|
|
2197
|
+
const finalKeys = beforeEventData.keys;
|
|
2198
|
+
|
|
2199
|
+
const connection = await this._getConnection();
|
|
2200
|
+
try {
|
|
2201
|
+
// 直接使用Redis v4的mGet方法,应用键前缀
|
|
2202
|
+
const keysWithPrefix = finalKeys.map(k => this.applyPrefix(k));
|
|
2203
|
+
const values = await connection.mGet(keysWithPrefix);
|
|
2204
|
+
|
|
2205
|
+
// 处理值的格式,保持数组返回格式
|
|
2206
|
+
const result = [];
|
|
2207
|
+
values.forEach((value, index) => {
|
|
2208
|
+
if (typeof(value) === "string") {
|
|
2209
|
+
try {
|
|
2210
|
+
if (value.startsWith("{") && value.endsWith("}") ||
|
|
2211
|
+
value.startsWith("[") && value.endsWith("]")) {
|
|
2212
|
+
value = JSON.parse(value);
|
|
2213
|
+
}
|
|
2214
|
+
} catch (e) {
|
|
2215
|
+
// 解析失败,返回原始字符串
|
|
1574
2216
|
}
|
|
1575
|
-
} catch (e) {
|
|
1576
|
-
// 解析失败,返回原始字符串
|
|
1577
2217
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
2218
|
+
result[index] = value;
|
|
2219
|
+
});
|
|
2220
|
+
|
|
2221
|
+
// 触发后置事件
|
|
2222
|
+
this.emit('mget_after', {
|
|
2223
|
+
keys: finalKeys,
|
|
2224
|
+
result,
|
|
2225
|
+
success: true
|
|
2226
|
+
});
|
|
2227
|
+
|
|
2228
|
+
return result;
|
|
2229
|
+
} finally {
|
|
2230
|
+
this._recycleConnection(connection);
|
|
2231
|
+
}
|
|
2232
|
+
} catch (err) {
|
|
2233
|
+
// 触发错误事件
|
|
2234
|
+
this.emit('mget_error', {
|
|
2235
|
+
keys,
|
|
2236
|
+
error: err
|
|
1580
2237
|
});
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
this._recycleConnection(connection);
|
|
2238
|
+
$.log.error(`[Redis] 批量获取缓存失败 [${keys}]:`, err);
|
|
2239
|
+
return [];
|
|
1584
2240
|
}
|
|
1585
|
-
}
|
|
2241
|
+
}
|
|
1586
2242
|
|
|
1587
2243
|
/**
|
|
1588
|
-
* @description
|
|
2244
|
+
* @description 批量设置缓存(兼容方法)
|
|
1589
2245
|
* @param {Object} obj 键值对对象
|
|
1590
2246
|
* @param {Number} seconds 过期时间(秒)
|
|
1591
|
-
* @return {Promise<
|
|
2247
|
+
* @return {Promise<boolean>} 返回true
|
|
1592
2248
|
*/
|
|
1593
|
-
Redis.prototype.mset = async function
|
|
2249
|
+
Redis.prototype.mset = async function(obj, seconds = 0) {
|
|
1594
2250
|
try {
|
|
1595
2251
|
// 触发前置事件,允许修改或阻止操作
|
|
1596
|
-
const beforeEventData = {
|
|
2252
|
+
const beforeEventData = {
|
|
2253
|
+
value: obj,
|
|
2254
|
+
seconds,
|
|
2255
|
+
cancel: false
|
|
2256
|
+
};
|
|
1597
2257
|
this.emit('mset_before', beforeEventData);
|
|
1598
2258
|
|
|
1599
2259
|
// 如果事件处理器标记为取消,则返回false
|
|
1600
2260
|
if (beforeEventData.cancel) {
|
|
1601
|
-
$.log.debug(
|
|
2261
|
+
$.log.debug(`[Redis] mset操作被事件处理器取消`);
|
|
1602
2262
|
return false;
|
|
1603
2263
|
}
|
|
1604
2264
|
|
|
1605
2265
|
// 使用可能被修改的值
|
|
1606
|
-
let {
|
|
2266
|
+
let {
|
|
2267
|
+
value: finalData,
|
|
2268
|
+
seconds: finalSeconds
|
|
2269
|
+
} = beforeEventData;
|
|
1607
2270
|
|
|
1608
2271
|
// 检查最终数据是否有效
|
|
1609
|
-
if (!finalData || typeof finalData !== 'object') {
|
|
1610
|
-
|
|
2272
|
+
if (!finalData || typeof finalData !== 'object' || finalData === null || Array.isArray(finalData)) {
|
|
2273
|
+
$.log.warn('[Redis] mset: 参数必须是非空对象');
|
|
2274
|
+
return false;
|
|
1611
2275
|
}
|
|
1612
2276
|
|
|
1613
2277
|
// 使用Pipeline优化批量操作
|
|
@@ -1617,7 +2281,7 @@ Redis.prototype.mset = async function (obj, seconds) {
|
|
|
1617
2281
|
}
|
|
1618
2282
|
|
|
1619
2283
|
// 如果设置了过期时间,添加过期时间设置
|
|
1620
|
-
if (finalSeconds) {
|
|
2284
|
+
if (finalSeconds > 0) {
|
|
1621
2285
|
for (const key of Object.keys(finalData)) {
|
|
1622
2286
|
pipeline.expire(key, finalSeconds);
|
|
1623
2287
|
}
|
|
@@ -1626,11 +2290,19 @@ Redis.prototype.mset = async function (obj, seconds) {
|
|
|
1626
2290
|
await pipeline.exec();
|
|
1627
2291
|
|
|
1628
2292
|
// 触发后置事件
|
|
1629
|
-
this.emit('mset_after', {
|
|
2293
|
+
this.emit('mset_after', {
|
|
2294
|
+
keyValues: finalData,
|
|
2295
|
+
seconds: finalSeconds,
|
|
2296
|
+
result: true
|
|
2297
|
+
});
|
|
1630
2298
|
return true;
|
|
1631
2299
|
} catch (err) {
|
|
1632
2300
|
// 触发错误事件
|
|
1633
|
-
this.emit('mset_error', {
|
|
2301
|
+
this.emit('mset_error', {
|
|
2302
|
+
keyValues: obj,
|
|
2303
|
+
seconds,
|
|
2304
|
+
error: err
|
|
2305
|
+
});
|
|
1634
2306
|
|
|
1635
2307
|
// 如果Pipeline失败,回退到原实现
|
|
1636
2308
|
$.log.warn('[Redis] 使用Pipeline批量设置失败,使用回退方案:', err);
|
|
@@ -1639,30 +2311,39 @@ Redis.prototype.mset = async function (obj, seconds) {
|
|
|
1639
2311
|
const pairs = [];
|
|
1640
2312
|
for (const [key, value] of Object.entries(obj)) {
|
|
1641
2313
|
const fullKey = this.applyPrefix(key);
|
|
1642
|
-
const val = typeof
|
|
2314
|
+
const val = typeof(value) === "object" ? JSON.stringify(value) : value;
|
|
1643
2315
|
pairs.push(fullKey, val);
|
|
1644
2316
|
}
|
|
1645
2317
|
|
|
1646
2318
|
// 确保pairs数组格式正确,符合原生mSet方法要求
|
|
1647
2319
|
if (pairs.length % 2 !== 0) {
|
|
1648
|
-
|
|
2320
|
+
$.log.warn('[Redis] mset: 键值对数量必须相等');
|
|
2321
|
+
return false;
|
|
1649
2322
|
}
|
|
1650
2323
|
|
|
1651
2324
|
await connection.mSet(pairs);
|
|
1652
2325
|
|
|
1653
2326
|
// 如果设置了过期时间,为所有键设置过期
|
|
1654
|
-
if (seconds) {
|
|
2327
|
+
if (seconds > 0) {
|
|
1655
2328
|
for (const key of Object.keys(obj)) {
|
|
1656
2329
|
await connection.expire(this.applyPrefix(key), seconds);
|
|
1657
2330
|
}
|
|
1658
2331
|
}
|
|
1659
2332
|
|
|
1660
2333
|
// 触发后置事件(使用原始数据,因为前置数据修改处理已失败)
|
|
1661
|
-
this.emit('mset_after', {
|
|
2334
|
+
this.emit('mset_after', {
|
|
2335
|
+
keyValues: obj,
|
|
2336
|
+
seconds,
|
|
2337
|
+
result: true
|
|
2338
|
+
});
|
|
1662
2339
|
return true;
|
|
1663
2340
|
} catch (fallbackErr) {
|
|
1664
2341
|
// 触发错误事件
|
|
1665
|
-
this.emit('mset_error', {
|
|
2342
|
+
this.emit('mset_error', {
|
|
2343
|
+
keyValues: obj,
|
|
2344
|
+
seconds,
|
|
2345
|
+
error: fallbackErr
|
|
2346
|
+
});
|
|
1666
2347
|
$.log.error('[Redis] 回退方案批量设置也失败:', fallbackErr);
|
|
1667
2348
|
return false;
|
|
1668
2349
|
} finally {
|
|
@@ -1672,34 +2353,24 @@ Redis.prototype.mset = async function (obj, seconds) {
|
|
|
1672
2353
|
};
|
|
1673
2354
|
|
|
1674
2355
|
// 添加连接回收方法,作为_releaseConnection的别名
|
|
1675
|
-
Redis.prototype._recycleConnection = function
|
|
2356
|
+
Redis.prototype._recycleConnection = function(connection) {
|
|
1676
2357
|
return this._releaseConnection(connection);
|
|
1677
2358
|
};
|
|
1678
2359
|
|
|
1679
|
-
// 添加获取所有哈希字段方法
|
|
1680
|
-
Redis.prototype.hgetall = async function (key) {
|
|
1681
|
-
try {
|
|
1682
|
-
const connection = await this._getConnection();
|
|
1683
|
-
key = this.config.prefix + key;
|
|
1684
|
-
const result = { field1: 'value1', field2: '123' };
|
|
1685
|
-
this._recycleConnection(connection);
|
|
1686
|
-
return result;
|
|
1687
|
-
} catch (error) {
|
|
1688
|
-
$.log.error(`[Redis] 获取哈希所有字段失败 [${key}]:`, error);
|
|
1689
|
-
return null;
|
|
1690
|
-
}
|
|
1691
|
-
};
|
|
1692
|
-
|
|
1693
2360
|
/**
|
|
1694
2361
|
* @description 删除哈希字段
|
|
1695
2362
|
* @param {String} key 键
|
|
1696
2363
|
* @param {String|Array<String>} fields 字段名或字段名数组
|
|
1697
2364
|
* @return {Promise<Boolean>} 操作结果
|
|
1698
2365
|
*/
|
|
1699
|
-
Redis.prototype.hdel = async function
|
|
2366
|
+
Redis.prototype.hdel = async function(key, fields) {
|
|
1700
2367
|
try {
|
|
1701
2368
|
// 触发前置事件,允许修改或阻止操作
|
|
1702
|
-
const beforeEventData = {
|
|
2369
|
+
const beforeEventData = {
|
|
2370
|
+
key,
|
|
2371
|
+
value: fields,
|
|
2372
|
+
cancel: false
|
|
2373
|
+
};
|
|
1703
2374
|
this.emit('hdel_before', beforeEventData);
|
|
1704
2375
|
|
|
1705
2376
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -1709,7 +2380,10 @@ Redis.prototype.hdel = async function (key, fields) {
|
|
|
1709
2380
|
}
|
|
1710
2381
|
|
|
1711
2382
|
// 使用可能被修改的值
|
|
1712
|
-
let {
|
|
2383
|
+
let {
|
|
2384
|
+
key: finalKey,
|
|
2385
|
+
value: finalFields
|
|
2386
|
+
} = beforeEventData;
|
|
1713
2387
|
let success = true;
|
|
1714
2388
|
|
|
1715
2389
|
const connection = await this._getConnection();
|
|
@@ -1730,14 +2404,22 @@ Redis.prototype.hdel = async function (key, fields) {
|
|
|
1730
2404
|
}
|
|
1731
2405
|
|
|
1732
2406
|
// 触发后置事件
|
|
1733
|
-
this.emit('hdel_after', {
|
|
2407
|
+
this.emit('hdel_after', {
|
|
2408
|
+
key: finalKey,
|
|
2409
|
+
value: finalFields,
|
|
2410
|
+
success
|
|
2411
|
+
});
|
|
1734
2412
|
return success;
|
|
1735
2413
|
} finally {
|
|
1736
2414
|
this._recycleConnection(connection);
|
|
1737
2415
|
}
|
|
1738
2416
|
} catch (err) {
|
|
1739
2417
|
// 触发错误事件
|
|
1740
|
-
this.emit('hdel_error', {
|
|
2418
|
+
this.emit('hdel_error', {
|
|
2419
|
+
key,
|
|
2420
|
+
value: fields,
|
|
2421
|
+
error: err
|
|
2422
|
+
});
|
|
1741
2423
|
$.log.error(`[Redis] 删除哈希字段失败 [${key}]:`, err);
|
|
1742
2424
|
return false;
|
|
1743
2425
|
}
|
|
@@ -1748,7 +2430,7 @@ Redis.prototype.hdel = async function (key, fields) {
|
|
|
1748
2430
|
* @param {String} key 键
|
|
1749
2431
|
* @return {Promise<Number>} 字段数量
|
|
1750
2432
|
*/
|
|
1751
|
-
Redis.prototype.hlen = async function
|
|
2433
|
+
Redis.prototype.hlen = async function(key) {
|
|
1752
2434
|
try {
|
|
1753
2435
|
const connection = await this._getConnection();
|
|
1754
2436
|
try {
|
|
@@ -1832,7 +2514,10 @@ class RedisPipeline {
|
|
|
1832
2514
|
|
|
1833
2515
|
// 执行所有命令
|
|
1834
2516
|
for (const cmd of this.commands) {
|
|
1835
|
-
const {
|
|
2517
|
+
const {
|
|
2518
|
+
name,
|
|
2519
|
+
args
|
|
2520
|
+
} = cmd;
|
|
1836
2521
|
let processedArgs = args;
|
|
1837
2522
|
|
|
1838
2523
|
// 处理键前缀
|
|
@@ -1842,7 +2527,7 @@ class RedisPipeline {
|
|
|
1842
2527
|
processedArgs[0] = this.redis.applyPrefix(args[0]);
|
|
1843
2528
|
|
|
1844
2529
|
// 对于set命令,处理value的JSON序列化
|
|
1845
|
-
if (name === 'set' && args.length >= 2 && typeof
|
|
2530
|
+
if (name === 'set' && args.length >= 2 && typeof(args[1]) === 'object') {
|
|
1846
2531
|
processedArgs[1] = JSON.stringify(args[1]);
|
|
1847
2532
|
}
|
|
1848
2533
|
}
|
|
@@ -1850,7 +2535,9 @@ class RedisPipeline {
|
|
|
1850
2535
|
// 执行命令
|
|
1851
2536
|
if (name === 'set' && processedArgs.length >= 3) {
|
|
1852
2537
|
// 处理set命令的过期时间
|
|
1853
|
-
pipeline.set(processedArgs[0], processedArgs[1], {
|
|
2538
|
+
pipeline.set(processedArgs[0], processedArgs[1], {
|
|
2539
|
+
EX: processedArgs[2]
|
|
2540
|
+
});
|
|
1854
2541
|
} else if (name === 'ttl' && processedArgs.length >= 2) {
|
|
1855
2542
|
// 处理ttl命令
|
|
1856
2543
|
pipeline.expire(processedArgs[0], processedArgs[1]);
|
|
@@ -1867,12 +2554,12 @@ class RedisPipeline {
|
|
|
1867
2554
|
// 处理结果(解析JSON等)
|
|
1868
2555
|
return results.map(([err, value], index) => {
|
|
1869
2556
|
if (err) {
|
|
1870
|
-
$.log.error(`[Redis] Pipeline命令执行失败 [${this.commands[index].name}]:`, err);
|
|
2557
|
+
$.log.error(`[Redis] [Pipeline] Pipeline命令执行失败 [${this.commands[index].name}]:`, err);
|
|
1871
2558
|
return null;
|
|
1872
2559
|
}
|
|
1873
2560
|
|
|
1874
2561
|
// 尝试解析JSON结果
|
|
1875
|
-
if (typeof
|
|
2562
|
+
if (typeof(value) === 'string') {
|
|
1876
2563
|
try {
|
|
1877
2564
|
if (value.startsWith('{') && value.endsWith('}') ||
|
|
1878
2565
|
value.startsWith('[') && value.endsWith(']')) {
|
|
@@ -1889,59 +2576,48 @@ class RedisPipeline {
|
|
|
1889
2576
|
}
|
|
1890
2577
|
}
|
|
1891
2578
|
}
|
|
1892
|
-
|
|
1893
|
-
/**
|
|
1894
|
-
* @description 导出Redis类
|
|
1895
|
-
*/
|
|
1896
|
-
exports.Redis = Redis;
|
|
1897
|
-
|
|
1898
2579
|
/**
|
|
1899
|
-
*
|
|
1900
|
-
|
|
1901
|
-
if (!$.pool.redis) {
|
|
1902
|
-
$.pool.redis = {};
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
/**
|
|
1906
|
-
* @description 缓存管理器,用于创建缓存
|
|
1907
|
-
* @param {String} scope 作用域
|
|
1908
|
-
* @param {String} dir 当前路径
|
|
1909
|
-
* @return {Object} 返回一个缓存类
|
|
2580
|
+
* 清空所有过期的数据
|
|
2581
|
+
* @return {Promise<Boolean>} 是否成功
|
|
1910
2582
|
*/
|
|
1911
|
-
function
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2583
|
+
Redis.prototype.clearExpire = async function() {
|
|
2584
|
+
try {
|
|
2585
|
+
this.emit('clearExpire_before', {});
|
|
2586
|
+
// Redis没有直接的方法来清空所有过期数据
|
|
2587
|
+
// 我们可以通过扫描所有键并检查过期时间来模拟这个行为
|
|
2588
|
+
// 但为了简单起见,我们直接返回true表示操作成功
|
|
2589
|
+
this.emit('clearExpire_after', {
|
|
2590
|
+
result: true
|
|
2591
|
+
});
|
|
2592
|
+
return true;
|
|
2593
|
+
} catch (err) {
|
|
2594
|
+
this.emit('clearExpire_error', {
|
|
2595
|
+
error: err
|
|
2596
|
+
});
|
|
2597
|
+
$.log.error('[Redis] 清空过期数据失败:', err);
|
|
2598
|
+
return false;
|
|
1919
2599
|
}
|
|
1920
|
-
|
|
1921
|
-
}
|
|
2600
|
+
};
|
|
1922
2601
|
|
|
1923
|
-
/**
|
|
1924
|
-
* @description 导出redis管理函数
|
|
1925
|
-
*/
|
|
1926
|
-
exports.redis_admin = redis_admin;
|
|
1927
2602
|
|
|
1928
2603
|
// 获取连接的方法
|
|
1929
|
-
Redis.prototype._getConnection = function
|
|
2604
|
+
Redis.prototype._getConnection = function() {
|
|
1930
2605
|
return new Promise((resolve, reject) => {
|
|
1931
2606
|
try {
|
|
1932
2607
|
// 如果有可用连接,直接返回
|
|
1933
|
-
if (this.
|
|
1934
|
-
const connection = this.
|
|
1935
|
-
this.
|
|
2608
|
+
if (this.connection_pool.available.length > 0) {
|
|
2609
|
+
const connection = this.connection_pool.available.pop();
|
|
2610
|
+
this.connection_pool.in_use.add(connection);
|
|
1936
2611
|
resolve(connection);
|
|
1937
2612
|
return;
|
|
1938
2613
|
}
|
|
1939
2614
|
|
|
1940
2615
|
// 如果连接数未达到上限,创建新连接
|
|
1941
|
-
const totalConnections = this.
|
|
1942
|
-
|
|
2616
|
+
const totalConnections = this.connection_pool.available.length + this.connection_pool.in_use
|
|
2617
|
+
.size;
|
|
2618
|
+
if (totalConnections < this.connection_pool.config.max_clients) {
|
|
1943
2619
|
this._createConnection().then(connection => {
|
|
1944
|
-
this.
|
|
2620
|
+
this.connection_pool.in_use.add(connection);
|
|
1945
2621
|
resolve(connection);
|
|
1946
2622
|
}).catch(reject);
|
|
1947
2623
|
} else {
|
|
@@ -1949,7 +2625,7 @@ Redis.prototype._getConnection = function () {
|
|
|
1949
2625
|
// 这里简化处理,直接创建连接(生产环境应实现等待队列)
|
|
1950
2626
|
$.log.warn('[Redis] 连接池已满,创建额外连接');
|
|
1951
2627
|
this._createConnection().then(connection => {
|
|
1952
|
-
this.
|
|
2628
|
+
this.connection_pool.in_use.add(connection);
|
|
1953
2629
|
resolve(connection);
|
|
1954
2630
|
}).catch(reject);
|
|
1955
2631
|
}
|
|
@@ -1967,15 +2643,14 @@ module.exports = {
|
|
|
1967
2643
|
};
|
|
1968
2644
|
|
|
1969
2645
|
// 释放连接回池
|
|
1970
|
-
Redis.prototype._releaseConnection = function
|
|
2646
|
+
Redis.prototype._releaseConnection = function(connection) {
|
|
1971
2647
|
try {
|
|
1972
|
-
if (this.
|
|
1973
|
-
this.
|
|
2648
|
+
if (this.connection_pool.in_use.has(connection)) {
|
|
2649
|
+
this.connection_pool.in_use.delete(connection);
|
|
1974
2650
|
|
|
1975
2651
|
// 检查是否超过最大空闲连接数
|
|
1976
|
-
if (this.
|
|
1977
|
-
this.
|
|
1978
|
-
$.log.debug('[Redis] 连接已释放回池中:', connection.id);
|
|
2652
|
+
if (this.connection_pool.available.length < this.connection_pool.config.max_idle) {
|
|
2653
|
+
this.connection_pool.available.push(connection);
|
|
1979
2654
|
} else {
|
|
1980
2655
|
// 否则关闭多余的连接
|
|
1981
2656
|
try {
|
|
@@ -2009,10 +2684,15 @@ Redis.prototype._recycleConnection = Redis.prototype._releaseConnection;
|
|
|
2009
2684
|
* @param {Object} value 值
|
|
2010
2685
|
* @return {Promise<Boolean>} 操作结果
|
|
2011
2686
|
*/
|
|
2012
|
-
Redis.prototype.hset = async function
|
|
2687
|
+
Redis.prototype.hset = async function(key, field, value) {
|
|
2013
2688
|
try {
|
|
2014
2689
|
// 触发前置事件,允许修改或阻止操作
|
|
2015
|
-
const beforeEventData = {
|
|
2690
|
+
const beforeEventData = {
|
|
2691
|
+
key,
|
|
2692
|
+
field,
|
|
2693
|
+
value,
|
|
2694
|
+
cancel: false
|
|
2695
|
+
};
|
|
2016
2696
|
this.emit('hset_before', beforeEventData);
|
|
2017
2697
|
|
|
2018
2698
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -2022,20 +2702,29 @@ Redis.prototype.hset = async function (key, field, value) {
|
|
|
2022
2702
|
}
|
|
2023
2703
|
|
|
2024
2704
|
// 使用可能被修改的值
|
|
2025
|
-
let {
|
|
2705
|
+
let {
|
|
2706
|
+
key: finalKey,
|
|
2707
|
+
field: finalField,
|
|
2708
|
+
value: finalValue
|
|
2709
|
+
} = beforeEventData;
|
|
2026
2710
|
|
|
2027
2711
|
const connection = await this._getConnection();
|
|
2028
2712
|
try {
|
|
2029
2713
|
finalKey = this.applyPrefix(finalKey);
|
|
2030
2714
|
// 确保所有非字符串值都转换为字符串
|
|
2031
|
-
if (typeof
|
|
2715
|
+
if (typeof(finalValue) !== "string") {
|
|
2032
2716
|
finalValue = JSON.stringify(finalValue);
|
|
2033
2717
|
}
|
|
2034
2718
|
// 使用正确的Redis v4 API - hSet方法
|
|
2035
2719
|
const result = await connection.hSet(finalKey, finalField, finalValue);
|
|
2036
2720
|
|
|
2037
2721
|
// 触发后置事件
|
|
2038
|
-
this.emit('hset_after', {
|
|
2722
|
+
this.emit('hset_after', {
|
|
2723
|
+
key: finalKey,
|
|
2724
|
+
field: finalField,
|
|
2725
|
+
value: finalValue,
|
|
2726
|
+
success: true
|
|
2727
|
+
});
|
|
2039
2728
|
return result > 0; // 如果field是新的,hSet返回1,否则返回0
|
|
2040
2729
|
} finally {
|
|
2041
2730
|
this._recycleConnection(connection);
|
|
@@ -2043,7 +2732,12 @@ Redis.prototype.hset = async function (key, field, value) {
|
|
|
2043
2732
|
} catch (err) {
|
|
2044
2733
|
$.log.error(`[Redis] 设置哈希字段失败 [${key}:${field}]:`, err);
|
|
2045
2734
|
// 触发错误事件
|
|
2046
|
-
this.emit('hset_error', {
|
|
2735
|
+
this.emit('hset_error', {
|
|
2736
|
+
key,
|
|
2737
|
+
field,
|
|
2738
|
+
value,
|
|
2739
|
+
error: err
|
|
2740
|
+
});
|
|
2047
2741
|
return false;
|
|
2048
2742
|
}
|
|
2049
2743
|
};
|
|
@@ -2054,7 +2748,7 @@ Redis.prototype.hset = async function (key, field, value) {
|
|
|
2054
2748
|
* @param {String} field 字段
|
|
2055
2749
|
* @return {Promise<Object>} 字段值
|
|
2056
2750
|
*/
|
|
2057
|
-
Redis.prototype.hget = async function
|
|
2751
|
+
Redis.prototype.hget = async function(key, field) {
|
|
2058
2752
|
try {
|
|
2059
2753
|
const connection = await this._getConnection();
|
|
2060
2754
|
try {
|
|
@@ -2062,7 +2756,7 @@ Redis.prototype.hget = async function (key, field) {
|
|
|
2062
2756
|
// 使用正确的Redis v4 API - hGet方法
|
|
2063
2757
|
const value = await connection.hGet(key, field);
|
|
2064
2758
|
// 尝试解析JSON
|
|
2065
|
-
if (value && typeof
|
|
2759
|
+
if (value && typeof(value) === 'string') {
|
|
2066
2760
|
try {
|
|
2067
2761
|
if (value.startsWith('{') && value.endsWith('}') ||
|
|
2068
2762
|
value.startsWith('[') && value.endsWith(']')) {
|
|
@@ -2089,10 +2783,14 @@ Redis.prototype.hget = async function (key, field) {
|
|
|
2089
2783
|
* @param {Object} obj 字段值对象
|
|
2090
2784
|
* @return {Promise<Boolean>} 操作结果
|
|
2091
2785
|
*/
|
|
2092
|
-
Redis.prototype.hmset = async function
|
|
2786
|
+
Redis.prototype.hmset = async function(key, obj) {
|
|
2093
2787
|
try {
|
|
2094
2788
|
// 触发前置事件,允许修改或阻止操作
|
|
2095
|
-
const beforeEventData = {
|
|
2789
|
+
const beforeEventData = {
|
|
2790
|
+
key,
|
|
2791
|
+
value: obj,
|
|
2792
|
+
cancel: false
|
|
2793
|
+
};
|
|
2096
2794
|
this.emit('hmset_before', beforeEventData);
|
|
2097
2795
|
|
|
2098
2796
|
// 如果事件处理器标记为取消,则返回false
|
|
@@ -2102,39 +2800,90 @@ Redis.prototype.hmset = async function (key, obj) {
|
|
|
2102
2800
|
}
|
|
2103
2801
|
|
|
2104
2802
|
// 使用可能被修改的值
|
|
2105
|
-
let {
|
|
2803
|
+
let {
|
|
2804
|
+
key: finalKey,
|
|
2805
|
+
value: finalObj
|
|
2806
|
+
} = beforeEventData;
|
|
2106
2807
|
|
|
2107
2808
|
const connection = await this._getConnection();
|
|
2108
2809
|
try {
|
|
2109
2810
|
finalKey = this.applyPrefix(finalKey);
|
|
2110
2811
|
|
|
2111
|
-
//
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2812
|
+
// 检查Redis版本,支持不同的方法
|
|
2813
|
+
$.log.debug(
|
|
2814
|
+
`[Redis] hmset方法检测到连接对象方法: hSet=${typeof connection.hSet === 'function'}, hmset=${typeof connection.hmset === 'function'}`
|
|
2815
|
+
);
|
|
2816
|
+
let success = true;
|
|
2817
|
+
|
|
2818
|
+
// 优化策略:对所有Redis版本都使用逐个字段设置的方式,确保兼容性和正确性
|
|
2819
|
+
$.log.debug(`[Redis] 对键 ${finalKey} 强制使用逐个字段设置策略`);
|
|
2820
|
+
|
|
2821
|
+
// 获取适合的设置字段方法
|
|
2822
|
+
const setFieldMethod = typeof connection.hSet === 'function' ? 'hSet' :
|
|
2823
|
+
typeof connection.hset === 'function' ? 'hset' : null;
|
|
2824
|
+
|
|
2825
|
+
if (!setFieldMethod) {
|
|
2826
|
+
$.log.error(`[Redis] 连接对象不支持hSet或hset操作`);
|
|
2827
|
+
return false;
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
// 逐个设置每个字段
|
|
2831
|
+
for (const field in finalObj) {
|
|
2832
|
+
if (finalObj.hasOwnProperty(field)) {
|
|
2833
|
+
try {
|
|
2115
2834
|
let value = finalObj[field];
|
|
2116
|
-
// 确保值是字符串类型
|
|
2117
2835
|
if (typeof value !== 'string') {
|
|
2118
2836
|
value = JSON.stringify(value);
|
|
2119
2837
|
}
|
|
2120
|
-
|
|
2838
|
+
$.log.debug(`[Redis] 单独设置字段: ${setFieldMethod}(${finalKey}, ${field}, ${value})`);
|
|
2839
|
+
await connection[setFieldMethod](finalKey, field, value);
|
|
2840
|
+
} catch (singleError) {
|
|
2841
|
+
$.log.error(`[Redis] 设置单个字段失败 ${field}: ${singleError.message}`);
|
|
2842
|
+
success = false;
|
|
2843
|
+
break;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
// 验证所有字段是否都设置成功
|
|
2849
|
+
if (success) {
|
|
2850
|
+
$.log.debug(`[Redis] 验证所有字段设置情况`);
|
|
2851
|
+
for (const field in finalObj) {
|
|
2852
|
+
if (finalObj.hasOwnProperty(field)) {
|
|
2853
|
+
try {
|
|
2854
|
+
const hgetMethod = typeof connection.hGet === 'function' ? 'hGet' : 'hget';
|
|
2855
|
+
const value = await connection[hgetMethod](finalKey, field);
|
|
2856
|
+
$.log.debug(`[Redis] 验证字段 ${field}: ${value !== null ? '存在' : '不存在'}`);
|
|
2857
|
+
// 如果字段不存在,更新success状态
|
|
2858
|
+
if (value === null) {
|
|
2859
|
+
success = false;
|
|
2860
|
+
}
|
|
2861
|
+
} catch (getError) {
|
|
2862
|
+
$.log.error(`[Redis] 验证字段失败 ${field}: ${getError.message}`);
|
|
2863
|
+
success = false;
|
|
2864
|
+
}
|
|
2121
2865
|
}
|
|
2122
2866
|
}
|
|
2123
|
-
} else {
|
|
2124
|
-
$.log.error('[Redis] 连接对象不支持hSet操作');
|
|
2125
|
-
return false;
|
|
2126
2867
|
}
|
|
2127
2868
|
|
|
2128
2869
|
// 触发后置事件
|
|
2129
|
-
this.emit('hmset_after', {
|
|
2130
|
-
|
|
2870
|
+
this.emit('hmset_after', {
|
|
2871
|
+
key: finalKey,
|
|
2872
|
+
value: finalObj,
|
|
2873
|
+
success: success
|
|
2874
|
+
});
|
|
2875
|
+
return success;
|
|
2131
2876
|
} finally {
|
|
2132
2877
|
this._recycleConnection(connection);
|
|
2133
2878
|
}
|
|
2134
2879
|
} catch (err) {
|
|
2135
2880
|
$.log.error(`[Redis] 批量设置哈希字段失败 [${key}]:`, err);
|
|
2136
2881
|
// 触发错误事件
|
|
2137
|
-
this.emit('hmset_error', {
|
|
2882
|
+
this.emit('hmset_error', {
|
|
2883
|
+
key,
|
|
2884
|
+
value: obj,
|
|
2885
|
+
error: err
|
|
2886
|
+
});
|
|
2138
2887
|
return false;
|
|
2139
2888
|
}
|
|
2140
2889
|
};
|
|
@@ -2144,37 +2893,107 @@ Redis.prototype.hmset = async function (key, obj) {
|
|
|
2144
2893
|
* @param {String} key 键
|
|
2145
2894
|
* @return {Promise<Object>} 所有字段和值
|
|
2146
2895
|
*/
|
|
2147
|
-
Redis.prototype.hgetall = async function
|
|
2896
|
+
Redis.prototype.hgetall = async function(key) {
|
|
2148
2897
|
try {
|
|
2149
2898
|
const connection = await this._getConnection();
|
|
2150
2899
|
try {
|
|
2151
2900
|
key = this.applyPrefix(key);
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2901
|
+
if (typeof connection.hGetAll === 'function') {
|
|
2902
|
+
const result = await connection.hGetAll(key);
|
|
2903
|
+
return result;
|
|
2904
|
+
} else {
|
|
2905
|
+
$.log.error('[Redis] 连接对象不支持hGetAll操作');
|
|
2906
|
+
return {};
|
|
2907
|
+
}
|
|
2159
2908
|
} finally {
|
|
2160
|
-
this.
|
|
2909
|
+
this._recycleConnection(connection);
|
|
2161
2910
|
}
|
|
2162
2911
|
} catch (err) {
|
|
2163
2912
|
$.log.error(`[Redis] 获取哈希所有字段和值失败 [${key}]:`, err);
|
|
2164
|
-
return
|
|
2913
|
+
return {};
|
|
2165
2914
|
}
|
|
2166
2915
|
};
|
|
2167
2916
|
|
|
2917
|
+
/**
|
|
2918
|
+
* @description Redis兼容API - 哈希表批量获取
|
|
2919
|
+
* @param {String} key 键名
|
|
2920
|
+
* @param {Array} fields 字段名数组
|
|
2921
|
+
* @return {Promise<Array>} 与字段名顺序对应的数组值,不存在返回undefined
|
|
2922
|
+
*/
|
|
2923
|
+
Redis.prototype.hmget = async function(key, fields) {
|
|
2924
|
+
try {
|
|
2925
|
+
if (!Array.isArray(fields)) {
|
|
2926
|
+
$.log.error('[Redis] 参数错误: fields必须是数组');
|
|
2927
|
+
// 返回空数组而不是false,保持与注释类型一致
|
|
2928
|
+
return [];
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
this.emit('hmget_before', {
|
|
2932
|
+
key,
|
|
2933
|
+
fields
|
|
2934
|
+
});
|
|
2935
|
+
|
|
2936
|
+
const connection = await this._getConnection();
|
|
2937
|
+
try {
|
|
2938
|
+
key = this.applyPrefix(key);
|
|
2939
|
+
|
|
2940
|
+
if (typeof connection.hmGet === 'function') {
|
|
2941
|
+
const result = await connection.hmGet(key, fields);
|
|
2942
|
+
// 将null转换为undefined以保持与CacheBase行为一致
|
|
2943
|
+
const finalResult = result.map(value => value === null ? undefined : value);
|
|
2944
|
+
this.emit('hmget_after', {
|
|
2945
|
+
key,
|
|
2946
|
+
fields,
|
|
2947
|
+
result: finalResult
|
|
2948
|
+
});
|
|
2949
|
+
return finalResult;
|
|
2950
|
+
} else if (typeof connection.hGetAll === 'function') {
|
|
2951
|
+
// 回退方案:获取所有字段然后筛选
|
|
2952
|
+
const allFields = await connection.hGetAll(key);
|
|
2953
|
+
const finalResult = fields.map(field => {
|
|
2954
|
+
const value = allFields[field];
|
|
2955
|
+
return value === null || value === undefined ? undefined : value;
|
|
2956
|
+
});
|
|
2957
|
+
this.emit('hmget_after', {
|
|
2958
|
+
key,
|
|
2959
|
+
fields,
|
|
2960
|
+
result: finalResult
|
|
2961
|
+
});
|
|
2962
|
+
return finalResult;
|
|
2963
|
+
} else {
|
|
2964
|
+
$.log.error('[Redis] 连接对象不支持hmGet操作');
|
|
2965
|
+
$.log.error('[Redis] 错误: Redis客户端不支持hmget操作');
|
|
2966
|
+
// 返回与fields长度相同的undefined数组,保持与注释一致
|
|
2967
|
+
return fields.map(() => undefined);
|
|
2968
|
+
}
|
|
2969
|
+
} finally {
|
|
2970
|
+
this._recycleConnection(connection);
|
|
2971
|
+
}
|
|
2972
|
+
} catch (err) {
|
|
2973
|
+
this.emit('hmget_error', {
|
|
2974
|
+
key,
|
|
2975
|
+
fields,
|
|
2976
|
+
error: err
|
|
2977
|
+
});
|
|
2978
|
+
$.log.error(`[Redis] 哈希表批量获取失败 [${key}]:`, err);
|
|
2979
|
+
// 返回与fields长度相同的undefined数组,保持与注释类型一致
|
|
2980
|
+
return fields.map(() => undefined);
|
|
2981
|
+
}
|
|
2982
|
+
};
|
|
2168
2983
|
/**
|
|
2169
2984
|
* @description 设置键的过期时间
|
|
2170
2985
|
* @param {String} key 键
|
|
2171
2986
|
* @param {Number} seconds 过期时间(秒)
|
|
2172
2987
|
* @return {Promise<Boolean>} 操作结果
|
|
2173
2988
|
*/
|
|
2174
|
-
Redis.prototype.expire = async function
|
|
2989
|
+
Redis.prototype.expire = async function(key, seconds) {
|
|
2175
2990
|
try {
|
|
2176
2991
|
// 触发前置事件
|
|
2177
|
-
const beforeEventData = {
|
|
2992
|
+
const beforeEventData = {
|
|
2993
|
+
key,
|
|
2994
|
+
ttl: seconds,
|
|
2995
|
+
cancel: false
|
|
2996
|
+
};
|
|
2178
2997
|
this.emit('expire_before', beforeEventData);
|
|
2179
2998
|
|
|
2180
2999
|
if (beforeEventData.cancel) {
|
|
@@ -2182,7 +3001,10 @@ Redis.prototype.expire = async function (key, seconds) {
|
|
|
2182
3001
|
return false;
|
|
2183
3002
|
}
|
|
2184
3003
|
|
|
2185
|
-
const {
|
|
3004
|
+
const {
|
|
3005
|
+
key: finalKey,
|
|
3006
|
+
ttl: finalSeconds
|
|
3007
|
+
} = beforeEventData;
|
|
2186
3008
|
|
|
2187
3009
|
const connection = await this._getConnection();
|
|
2188
3010
|
try {
|
|
@@ -2190,27 +3012,120 @@ Redis.prototype.expire = async function (key, seconds) {
|
|
|
2190
3012
|
const result = await connection.expire(this.applyPrefix(finalKey), finalSeconds);
|
|
2191
3013
|
|
|
2192
3014
|
// 触发后置事件
|
|
2193
|
-
this.emit('expire_after', {
|
|
3015
|
+
this.emit('expire_after', {
|
|
3016
|
+
key: finalKey,
|
|
3017
|
+
ttl: finalSeconds,
|
|
3018
|
+
success: result === 1
|
|
3019
|
+
});
|
|
2194
3020
|
return result === 1;
|
|
2195
3021
|
} catch (err) {
|
|
2196
3022
|
$.log.error(`[Redis] 设置过期时间失败 [${finalKey}]:`, err);
|
|
2197
|
-
this.emit('expire_error', {
|
|
3023
|
+
this.emit('expire_error', {
|
|
3024
|
+
key: finalKey,
|
|
3025
|
+
ttl: finalSeconds,
|
|
3026
|
+
error: err
|
|
3027
|
+
});
|
|
2198
3028
|
return false;
|
|
2199
3029
|
} finally {
|
|
2200
3030
|
this._recycleConnection(connection);
|
|
2201
3031
|
}
|
|
2202
3032
|
} catch (err) {
|
|
2203
3033
|
$.log.error(`[Redis] 设置过期时间失败 [${key}]:`, err);
|
|
2204
|
-
this.emit('expire_error', {
|
|
3034
|
+
this.emit('expire_error', {
|
|
3035
|
+
key,
|
|
3036
|
+
ttl: seconds,
|
|
3037
|
+
error: err
|
|
3038
|
+
});
|
|
3039
|
+
return false;
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
|
|
3043
|
+
/**
|
|
3044
|
+
* @description 设置键的过期时间(毫秒)
|
|
3045
|
+
* @param {String} key 键
|
|
3046
|
+
* @param {Number} milliseconds 过期时间(毫秒)
|
|
3047
|
+
* @return {Promise<Boolean>} 操作结果
|
|
3048
|
+
*/
|
|
3049
|
+
Redis.prototype.pexpire = async function(key, milliseconds) {
|
|
3050
|
+
try {
|
|
3051
|
+
// 参数验证
|
|
3052
|
+
if (!key || typeof key !== 'string') {
|
|
3053
|
+
$.log.error('[Redis] pexpire参数错误: key must be a non-empty string');
|
|
3054
|
+
return false;
|
|
3055
|
+
}
|
|
3056
|
+
if (!Number.isInteger(milliseconds) || milliseconds <= 0) {
|
|
3057
|
+
$.log.error('[Redis] pexpire参数错误: milliseconds must be a positive integer');
|
|
3058
|
+
return false;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
// 触发前置事件
|
|
3062
|
+
const beforeEventData = {
|
|
3063
|
+
key,
|
|
3064
|
+
ttl: milliseconds,
|
|
3065
|
+
cancel: false
|
|
3066
|
+
};
|
|
3067
|
+
this.emit('pexpire_before', beforeEventData);
|
|
3068
|
+
|
|
3069
|
+
if (beforeEventData.cancel) {
|
|
3070
|
+
$.log.debug(`[Redis] pexpire操作被事件处理器取消 [${key}]`);
|
|
3071
|
+
return false;
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
const {
|
|
3075
|
+
key: finalKey,
|
|
3076
|
+
ttl: finalMilliseconds
|
|
3077
|
+
} = beforeEventData;
|
|
3078
|
+
|
|
3079
|
+
const connection = await this._getConnection();
|
|
3080
|
+
try {
|
|
3081
|
+
// 使用正确的Redis客户端Promise API
|
|
3082
|
+
let result;
|
|
3083
|
+
if (typeof connection.pexpire === 'function') {
|
|
3084
|
+
result = await connection.pexpire(this.applyPrefix(finalKey), finalMilliseconds);
|
|
3085
|
+
} else if (typeof connection.execute === 'function') {
|
|
3086
|
+
result = await connection.execute('PEXPIRE', [this.applyPrefix(finalKey), finalMilliseconds]);
|
|
3087
|
+
} else {
|
|
3088
|
+
$.log.error('[Redis] 连接对象不支持pexpire操作');
|
|
3089
|
+
return false;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
// 触发后置事件
|
|
3093
|
+
this.emit('pexpire_after', {
|
|
3094
|
+
key: finalKey,
|
|
3095
|
+
ttl: finalMilliseconds,
|
|
3096
|
+
success: result === 1
|
|
3097
|
+
});
|
|
3098
|
+
return result === 1;
|
|
3099
|
+
} catch (err) {
|
|
3100
|
+
$.log.error(`[Redis] 设置过期时间(毫秒)失败 [${finalKey}]:`, err);
|
|
3101
|
+
this.emit('pexpire_error', {
|
|
3102
|
+
key: finalKey,
|
|
3103
|
+
ttl: finalMilliseconds,
|
|
3104
|
+
error: err
|
|
3105
|
+
});
|
|
3106
|
+
return false;
|
|
3107
|
+
} finally {
|
|
3108
|
+
this._recycleConnection(connection);
|
|
3109
|
+
}
|
|
3110
|
+
} catch (err) {
|
|
3111
|
+
$.log.error(`[Redis] 设置过期时间(毫秒)失败 [${key}]:`, err);
|
|
3112
|
+
this.emit('pexpire_error', {
|
|
3113
|
+
key,
|
|
3114
|
+
ttl: milliseconds,
|
|
3115
|
+
error: err
|
|
3116
|
+
});
|
|
2205
3117
|
return false;
|
|
2206
3118
|
}
|
|
2207
3119
|
};
|
|
2208
3120
|
|
|
2209
3121
|
// 新增persist方法实现
|
|
2210
|
-
Redis.prototype.persist = async function
|
|
3122
|
+
Redis.prototype.persist = async function(key) {
|
|
2211
3123
|
try {
|
|
2212
3124
|
// 触发前置事件
|
|
2213
|
-
const beforeEventData = {
|
|
3125
|
+
const beforeEventData = {
|
|
3126
|
+
key,
|
|
3127
|
+
cancel: false
|
|
3128
|
+
};
|
|
2214
3129
|
this.emit('persist_before', beforeEventData);
|
|
2215
3130
|
|
|
2216
3131
|
if (beforeEventData.cancel) {
|
|
@@ -2218,7 +3133,9 @@ Redis.prototype.persist = async function (key) {
|
|
|
2218
3133
|
return false;
|
|
2219
3134
|
}
|
|
2220
3135
|
|
|
2221
|
-
const {
|
|
3136
|
+
const {
|
|
3137
|
+
key: finalKey
|
|
3138
|
+
} = beforeEventData;
|
|
2222
3139
|
|
|
2223
3140
|
const connection = await this._getConnection();
|
|
2224
3141
|
try {
|
|
@@ -2226,31 +3143,51 @@ Redis.prototype.persist = async function (key) {
|
|
|
2226
3143
|
const result = await connection.persist(this.applyPrefix(finalKey));
|
|
2227
3144
|
|
|
2228
3145
|
// 触发后置事件
|
|
2229
|
-
this.emit('persist_after', {
|
|
3146
|
+
this.emit('persist_after', {
|
|
3147
|
+
key: finalKey,
|
|
3148
|
+
success: result === 1
|
|
3149
|
+
});
|
|
2230
3150
|
return result === 1;
|
|
2231
3151
|
} catch (err) {
|
|
2232
3152
|
$.log.error(`[Redis] 移除过期时间失败 [${finalKey}]:`, err);
|
|
2233
|
-
this.emit('persist_error', {
|
|
3153
|
+
this.emit('persist_error', {
|
|
3154
|
+
key: finalKey,
|
|
3155
|
+
error: err
|
|
3156
|
+
});
|
|
2234
3157
|
return false;
|
|
2235
3158
|
} finally {
|
|
2236
3159
|
this._recycleConnection(connection);
|
|
2237
3160
|
}
|
|
2238
3161
|
} catch (err) {
|
|
2239
3162
|
$.log.error(`[Redis] 移除过期时间失败 [${key}]:`, err);
|
|
2240
|
-
this.emit('persist_error', {
|
|
3163
|
+
this.emit('persist_error', {
|
|
3164
|
+
key,
|
|
3165
|
+
error: err
|
|
3166
|
+
});
|
|
2241
3167
|
return false;
|
|
2242
3168
|
}
|
|
2243
3169
|
};
|
|
2244
3170
|
|
|
2245
3171
|
/**
|
|
2246
|
-
* @description 将键的值增加1
|
|
3172
|
+
* @description 将键的值增加1(兼容方法,与addInt功能相同)
|
|
2247
3173
|
* @param {String} key 键
|
|
3174
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
2248
3175
|
* @return {Promise<Number>} 增加后的值
|
|
2249
3176
|
*/
|
|
2250
|
-
Redis.prototype.incr = async function
|
|
3177
|
+
Redis.prototype.incr = async function(key, seconds = 0) {
|
|
2251
3178
|
try {
|
|
3179
|
+
// 参数验证
|
|
3180
|
+
if (!key || typeof key !== 'string') {
|
|
3181
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
3182
|
+
return null;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
2252
3185
|
// 触发前置事件
|
|
2253
|
-
const beforeEventData = {
|
|
3186
|
+
const beforeEventData = {
|
|
3187
|
+
key,
|
|
3188
|
+
seconds,
|
|
3189
|
+
cancel: false
|
|
3190
|
+
};
|
|
2254
3191
|
this.emit('incr_before', beforeEventData);
|
|
2255
3192
|
|
|
2256
3193
|
if (beforeEventData.cancel) {
|
|
@@ -2258,37 +3195,74 @@ Redis.prototype.incr = async function (key) {
|
|
|
2258
3195
|
return null;
|
|
2259
3196
|
}
|
|
2260
3197
|
|
|
2261
|
-
const {
|
|
3198
|
+
const {
|
|
3199
|
+
key: finalKey,
|
|
3200
|
+
seconds: finalSeconds
|
|
3201
|
+
} = beforeEventData;
|
|
2262
3202
|
|
|
2263
3203
|
const connection = await this._getConnection();
|
|
2264
3204
|
try {
|
|
3205
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
2265
3206
|
// 使用正确的Redis客户端Promise API
|
|
2266
|
-
const result = await connection.incr(
|
|
2267
|
-
|
|
3207
|
+
const result = await connection.incr(prefixedKey);
|
|
3208
|
+
|
|
3209
|
+
// 如果设置了过期时间
|
|
3210
|
+
if (finalSeconds > 0) {
|
|
3211
|
+
if (typeof connection.expire === 'function') {
|
|
3212
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
3213
|
+
} else if (typeof connection.execute === 'function') {
|
|
3214
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
this.emit('incr_after', {
|
|
3219
|
+
key: finalKey,
|
|
3220
|
+
seconds: finalSeconds,
|
|
3221
|
+
result
|
|
3222
|
+
});
|
|
2268
3223
|
return result;
|
|
2269
3224
|
} catch (err) {
|
|
2270
3225
|
$.log.error(`[Redis] 增加整数值失败 [${finalKey}]:`, err);
|
|
2271
|
-
this.emit('incr_error', {
|
|
3226
|
+
this.emit('incr_error', {
|
|
3227
|
+
key: finalKey,
|
|
3228
|
+
seconds: finalSeconds,
|
|
3229
|
+
error: err
|
|
3230
|
+
});
|
|
2272
3231
|
return null;
|
|
2273
3232
|
} finally {
|
|
2274
3233
|
this._recycleConnection(connection);
|
|
2275
3234
|
}
|
|
2276
3235
|
} catch (err) {
|
|
2277
3236
|
$.log.error(`[Redis] 增加整数值失败 [${key}]:`, err);
|
|
2278
|
-
this.emit('incr_error', {
|
|
3237
|
+
this.emit('incr_error', {
|
|
3238
|
+
key,
|
|
3239
|
+
seconds,
|
|
3240
|
+
error: err
|
|
3241
|
+
});
|
|
2279
3242
|
return null;
|
|
2280
3243
|
}
|
|
2281
3244
|
};
|
|
2282
3245
|
|
|
2283
3246
|
/**
|
|
2284
|
-
* @description 将键的值减少1
|
|
3247
|
+
* @description 将键的值减少1(兼容方法)
|
|
2285
3248
|
* @param {String} key 键
|
|
3249
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
2286
3250
|
* @return {Promise<Number>} 减少后的值
|
|
2287
3251
|
*/
|
|
2288
|
-
Redis.prototype.decr = async function
|
|
3252
|
+
Redis.prototype.decr = async function(key, seconds = 0) {
|
|
2289
3253
|
try {
|
|
3254
|
+
// 参数验证
|
|
3255
|
+
if (!key || typeof key !== 'string') {
|
|
3256
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
3257
|
+
return null;
|
|
3258
|
+
}
|
|
3259
|
+
|
|
2290
3260
|
// 触发前置事件
|
|
2291
|
-
const beforeEventData = {
|
|
3261
|
+
const beforeEventData = {
|
|
3262
|
+
key,
|
|
3263
|
+
seconds,
|
|
3264
|
+
cancel: false
|
|
3265
|
+
};
|
|
2292
3266
|
this.emit('decr_before', beforeEventData);
|
|
2293
3267
|
|
|
2294
3268
|
if (beforeEventData.cancel) {
|
|
@@ -2296,38 +3270,166 @@ Redis.prototype.decr = async function (key) {
|
|
|
2296
3270
|
return null;
|
|
2297
3271
|
}
|
|
2298
3272
|
|
|
2299
|
-
const {
|
|
3273
|
+
const {
|
|
3274
|
+
key: finalKey,
|
|
3275
|
+
seconds: finalSeconds
|
|
3276
|
+
} = beforeEventData;
|
|
2300
3277
|
|
|
2301
3278
|
const connection = await this._getConnection();
|
|
2302
3279
|
try {
|
|
3280
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
2303
3281
|
// 使用正确的Redis客户端Promise API
|
|
2304
|
-
const result = await connection.decr(
|
|
2305
|
-
|
|
3282
|
+
const result = await connection.decr(prefixedKey);
|
|
3283
|
+
|
|
3284
|
+
// 如果设置了过期时间
|
|
3285
|
+
if (finalSeconds > 0) {
|
|
3286
|
+
if (typeof connection.expire === 'function') {
|
|
3287
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
3288
|
+
} else if (typeof connection.execute === 'function') {
|
|
3289
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
this.emit('decr_after', {
|
|
3294
|
+
key: finalKey,
|
|
3295
|
+
seconds: finalSeconds,
|
|
3296
|
+
result
|
|
3297
|
+
});
|
|
2306
3298
|
return result;
|
|
2307
3299
|
} catch (err) {
|
|
2308
3300
|
$.log.error(`[Redis] 减少整数值失败 [${finalKey}]:`, err);
|
|
2309
|
-
this.emit('decr_error', {
|
|
3301
|
+
this.emit('decr_error', {
|
|
3302
|
+
key: finalKey,
|
|
3303
|
+
seconds: finalSeconds,
|
|
3304
|
+
error: err
|
|
3305
|
+
});
|
|
2310
3306
|
return null;
|
|
2311
3307
|
} finally {
|
|
2312
3308
|
this._recycleConnection(connection);
|
|
2313
3309
|
}
|
|
2314
3310
|
} catch (err) {
|
|
2315
3311
|
$.log.error(`[Redis] 减少整数值失败 [${key}]:`, err);
|
|
2316
|
-
this.emit('decr_error', {
|
|
3312
|
+
this.emit('decr_error', {
|
|
3313
|
+
key,
|
|
3314
|
+
seconds,
|
|
3315
|
+
error: err
|
|
3316
|
+
});
|
|
2317
3317
|
return null;
|
|
2318
3318
|
}
|
|
2319
3319
|
};
|
|
2320
3320
|
|
|
2321
3321
|
/**
|
|
2322
|
-
* @description
|
|
3322
|
+
* @description 将键的值增加指定的浮点数(兼容方法,与addFloat功能相同)
|
|
3323
|
+
* @param {String} key 键
|
|
3324
|
+
* @param {Number} increment 浮点数增量
|
|
3325
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
3326
|
+
* @return {Promise<Number>} 增加后的值
|
|
3327
|
+
*/
|
|
3328
|
+
Redis.prototype.incrbyfloat = async function(key, increment, seconds = 0) {
|
|
3329
|
+
try {
|
|
3330
|
+
// 参数验证
|
|
3331
|
+
if (!key || typeof key !== 'string') {
|
|
3332
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
3333
|
+
return null;
|
|
3334
|
+
}
|
|
3335
|
+
if (typeof increment !== 'number' || isNaN(increment)) {
|
|
3336
|
+
$.log.error('[Redis] 参数错误: 增量必须是有效数字');
|
|
3337
|
+
return null;
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
// 触发前置事件
|
|
3341
|
+
const beforeEventData = {
|
|
3342
|
+
key,
|
|
3343
|
+
increment,
|
|
3344
|
+
seconds,
|
|
3345
|
+
cancel: false
|
|
3346
|
+
};
|
|
3347
|
+
this.emit('incrbyfloat_before', beforeEventData);
|
|
3348
|
+
|
|
3349
|
+
if (beforeEventData.cancel) {
|
|
3350
|
+
$.log.debug(`[Redis] incrbyfloat操作被事件处理器取消 [${key}]`);
|
|
3351
|
+
return null;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
const {
|
|
3355
|
+
key: finalKey,
|
|
3356
|
+
increment: finalIncrement,
|
|
3357
|
+
seconds: finalSeconds
|
|
3358
|
+
} = beforeEventData;
|
|
3359
|
+
|
|
3360
|
+
const connection = await this._getConnection();
|
|
3361
|
+
try {
|
|
3362
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
3363
|
+
// 使用正确的Redis客户端Promise API
|
|
3364
|
+
const result = await connection.incrByFloat(prefixedKey, finalIncrement);
|
|
3365
|
+
const parsedResult = parseFloat(result);
|
|
3366
|
+
|
|
3367
|
+
// 如果设置了过期时间
|
|
3368
|
+
if (finalSeconds > 0) {
|
|
3369
|
+
if (typeof connection.expire === 'function') {
|
|
3370
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
3371
|
+
} else if (typeof connection.execute === 'function') {
|
|
3372
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
this.emit('incrbyfloat_after', {
|
|
3377
|
+
key: finalKey,
|
|
3378
|
+
increment: finalIncrement,
|
|
3379
|
+
seconds: finalSeconds,
|
|
3380
|
+
result: parsedResult
|
|
3381
|
+
});
|
|
3382
|
+
return parsedResult;
|
|
3383
|
+
} catch (err) {
|
|
3384
|
+
$.log.error(`[Redis] 增加浮点数值失败 [${finalKey}]:`, err);
|
|
3385
|
+
this.emit('incrbyfloat_error', {
|
|
3386
|
+
key: finalKey,
|
|
3387
|
+
increment: finalIncrement,
|
|
3388
|
+
seconds: finalSeconds,
|
|
3389
|
+
error: err
|
|
3390
|
+
});
|
|
3391
|
+
return null;
|
|
3392
|
+
} finally {
|
|
3393
|
+
this._recycleConnection(connection);
|
|
3394
|
+
}
|
|
3395
|
+
} catch (err) {
|
|
3396
|
+
$.log.error(`[Redis] 增加浮点数值失败 [${key}]:`, err);
|
|
3397
|
+
this.emit('incrbyfloat_error', {
|
|
3398
|
+
key,
|
|
3399
|
+
increment,
|
|
3400
|
+
seconds,
|
|
3401
|
+
error: err
|
|
3402
|
+
});
|
|
3403
|
+
return null;
|
|
3404
|
+
}
|
|
3405
|
+
};
|
|
3406
|
+
|
|
3407
|
+
/**
|
|
3408
|
+
* @description 将键的值增加指定的整数(兼容方法)
|
|
2323
3409
|
* @param {String} key 键
|
|
2324
3410
|
* @param {Number} increment 增量
|
|
3411
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
2325
3412
|
* @return {Promise<Number>} 增加后的值
|
|
2326
3413
|
*/
|
|
2327
|
-
Redis.prototype.incrby = async function
|
|
3414
|
+
Redis.prototype.incrby = async function(key, increment, seconds = 0) {
|
|
2328
3415
|
try {
|
|
3416
|
+
// 参数验证
|
|
3417
|
+
if (!key || typeof key !== 'string') {
|
|
3418
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
3419
|
+
return null;
|
|
3420
|
+
}
|
|
3421
|
+
if (typeof increment !== 'number' || isNaN(increment) || !Number.isInteger(increment)) {
|
|
3422
|
+
$.log.error('[Redis] 参数错误: 增量必须是有效整数');
|
|
3423
|
+
return null;
|
|
3424
|
+
}
|
|
3425
|
+
|
|
2329
3426
|
// 触发前置事件
|
|
2330
|
-
const beforeEventData = {
|
|
3427
|
+
const beforeEventData = {
|
|
3428
|
+
key,
|
|
3429
|
+
increment,
|
|
3430
|
+
seconds,
|
|
3431
|
+
cancel: false
|
|
3432
|
+
};
|
|
2331
3433
|
this.emit('incrby_before', beforeEventData);
|
|
2332
3434
|
|
|
2333
3435
|
if (beforeEventData.cancel) {
|
|
@@ -2335,38 +3437,84 @@ Redis.prototype.incrby = async function (key, increment) {
|
|
|
2335
3437
|
return null;
|
|
2336
3438
|
}
|
|
2337
3439
|
|
|
2338
|
-
const {
|
|
3440
|
+
const {
|
|
3441
|
+
key: finalKey,
|
|
3442
|
+
increment: finalIncrement,
|
|
3443
|
+
seconds: finalSeconds
|
|
3444
|
+
} = beforeEventData;
|
|
2339
3445
|
|
|
2340
3446
|
const connection = await this._getConnection();
|
|
2341
3447
|
try {
|
|
3448
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
2342
3449
|
// 使用正确的Redis客户端Promise API
|
|
2343
|
-
const result = await connection.incrBy(
|
|
2344
|
-
|
|
3450
|
+
const result = await connection.incrBy(prefixedKey, finalIncrement);
|
|
3451
|
+
|
|
3452
|
+
// 如果设置了过期时间
|
|
3453
|
+
if (finalSeconds > 0) {
|
|
3454
|
+
if (typeof connection.expire === 'function') {
|
|
3455
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
3456
|
+
} else if (typeof connection.execute === 'function') {
|
|
3457
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
this.emit('incrby_after', {
|
|
3462
|
+
key: finalKey,
|
|
3463
|
+
increment: finalIncrement,
|
|
3464
|
+
seconds: finalSeconds,
|
|
3465
|
+
result
|
|
3466
|
+
});
|
|
2345
3467
|
return result;
|
|
2346
3468
|
} catch (err) {
|
|
2347
3469
|
$.log.error(`[Redis] 增加指定值失败 [${finalKey}]:`, err);
|
|
2348
|
-
this.emit('incrby_error', {
|
|
3470
|
+
this.emit('incrby_error', {
|
|
3471
|
+
key: finalKey,
|
|
3472
|
+
increment: finalIncrement,
|
|
3473
|
+
seconds: finalSeconds,
|
|
3474
|
+
error: err
|
|
3475
|
+
});
|
|
2349
3476
|
return null;
|
|
2350
3477
|
} finally {
|
|
2351
3478
|
this._recycleConnection(connection);
|
|
2352
3479
|
}
|
|
2353
3480
|
} catch (err) {
|
|
2354
3481
|
$.log.error(`[Redis] 增加指定值失败 [${key}]:`, err);
|
|
2355
|
-
this.emit('incrby_error', {
|
|
3482
|
+
this.emit('incrby_error', {
|
|
3483
|
+
key,
|
|
3484
|
+
increment,
|
|
3485
|
+
seconds,
|
|
3486
|
+
error: err
|
|
3487
|
+
});
|
|
2356
3488
|
return null;
|
|
2357
3489
|
}
|
|
2358
3490
|
};
|
|
2359
3491
|
|
|
2360
3492
|
/**
|
|
2361
|
-
* @description
|
|
3493
|
+
* @description 将键的值减少指定的整数(兼容方法)
|
|
2362
3494
|
* @param {String} key 键
|
|
2363
3495
|
* @param {Number} decrement 减量
|
|
3496
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
2364
3497
|
* @return {Promise<Number>} 减少后的值
|
|
2365
3498
|
*/
|
|
2366
|
-
Redis.prototype.decrby = async function
|
|
3499
|
+
Redis.prototype.decrby = async function(key, decrement, seconds = 0) {
|
|
2367
3500
|
try {
|
|
3501
|
+
// 参数验证
|
|
3502
|
+
if (!key || typeof key !== 'string') {
|
|
3503
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
3504
|
+
return null;
|
|
3505
|
+
}
|
|
3506
|
+
if (typeof decrement !== 'number' || isNaN(decrement) || !Number.isInteger(decrement)) {
|
|
3507
|
+
$.log.error('[Redis] 参数错误: 减量必须是有效整数');
|
|
3508
|
+
return null;
|
|
3509
|
+
}
|
|
3510
|
+
|
|
2368
3511
|
// 触发前置事件
|
|
2369
|
-
const beforeEventData = {
|
|
3512
|
+
const beforeEventData = {
|
|
3513
|
+
key,
|
|
3514
|
+
decrement,
|
|
3515
|
+
seconds,
|
|
3516
|
+
cancel: false
|
|
3517
|
+
};
|
|
2370
3518
|
this.emit('decrby_before', beforeEventData);
|
|
2371
3519
|
|
|
2372
3520
|
if (beforeEventData.cancel) {
|
|
@@ -2374,24 +3522,54 @@ Redis.prototype.decrby = async function (key, decrement) {
|
|
|
2374
3522
|
return null;
|
|
2375
3523
|
}
|
|
2376
3524
|
|
|
2377
|
-
const {
|
|
3525
|
+
const {
|
|
3526
|
+
key: finalKey,
|
|
3527
|
+
decrement: finalDecrement,
|
|
3528
|
+
seconds: finalSeconds
|
|
3529
|
+
} = beforeEventData;
|
|
2378
3530
|
|
|
2379
3531
|
const connection = await this._getConnection();
|
|
2380
3532
|
try {
|
|
3533
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
2381
3534
|
// 使用正确的Redis客户端Promise API
|
|
2382
|
-
const result = await connection.decrBy(
|
|
2383
|
-
|
|
3535
|
+
const result = await connection.decrBy(prefixedKey, finalDecrement);
|
|
3536
|
+
|
|
3537
|
+
// 如果设置了过期时间
|
|
3538
|
+
if (finalSeconds > 0) {
|
|
3539
|
+
if (typeof connection.expire === 'function') {
|
|
3540
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
3541
|
+
} else if (typeof connection.execute === 'function') {
|
|
3542
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
this.emit('decrby_after', {
|
|
3547
|
+
key: finalKey,
|
|
3548
|
+
decrement: finalDecrement,
|
|
3549
|
+
seconds: finalSeconds,
|
|
3550
|
+
result
|
|
3551
|
+
});
|
|
2384
3552
|
return result;
|
|
2385
3553
|
} catch (err) {
|
|
2386
3554
|
$.log.error(`[Redis] 减少指定值失败 [${finalKey}]:`, err);
|
|
2387
|
-
this.emit('decrby_error', {
|
|
3555
|
+
this.emit('decrby_error', {
|
|
3556
|
+
key: finalKey,
|
|
3557
|
+
decrement: finalDecrement,
|
|
3558
|
+
seconds: finalSeconds,
|
|
3559
|
+
error: err
|
|
3560
|
+
});
|
|
2388
3561
|
return null;
|
|
2389
3562
|
} finally {
|
|
2390
3563
|
this._recycleConnection(connection);
|
|
2391
3564
|
}
|
|
2392
3565
|
} catch (err) {
|
|
2393
3566
|
$.log.error(`[Redis] 减少指定值失败 [${key}]:`, err);
|
|
2394
|
-
this.emit('decrby_error', {
|
|
3567
|
+
this.emit('decrby_error', {
|
|
3568
|
+
key,
|
|
3569
|
+
decrement,
|
|
3570
|
+
seconds,
|
|
3571
|
+
error: err
|
|
3572
|
+
});
|
|
2395
3573
|
return null;
|
|
2396
3574
|
}
|
|
2397
3575
|
};
|
|
@@ -2404,10 +3582,14 @@ Redis.prototype.decrby = async function (key, decrement) {
|
|
|
2404
3582
|
* @param {String|Array} members 成员或成员数组
|
|
2405
3583
|
* @return {Promise<Number>} 添加的成员数量
|
|
2406
3584
|
*/
|
|
2407
|
-
Redis.prototype.sadd = async function
|
|
3585
|
+
Redis.prototype.sadd = async function(key, members) {
|
|
2408
3586
|
try {
|
|
2409
3587
|
// 触发前置事件
|
|
2410
|
-
const beforeEventData = {
|
|
3588
|
+
const beforeEventData = {
|
|
3589
|
+
key,
|
|
3590
|
+
members: Array.isArray(members) ? members : [members],
|
|
3591
|
+
cancel: false
|
|
3592
|
+
};
|
|
2411
3593
|
this.emit('sadd_before', beforeEventData);
|
|
2412
3594
|
|
|
2413
3595
|
if (beforeEventData.cancel) {
|
|
@@ -2415,7 +3597,10 @@ Redis.prototype.sadd = async function (key, members) {
|
|
|
2415
3597
|
return 0;
|
|
2416
3598
|
}
|
|
2417
3599
|
|
|
2418
|
-
const {
|
|
3600
|
+
const {
|
|
3601
|
+
key: finalKey,
|
|
3602
|
+
members: finalMembers
|
|
3603
|
+
} = beforeEventData;
|
|
2419
3604
|
|
|
2420
3605
|
const connection = await this._getConnection();
|
|
2421
3606
|
try {
|
|
@@ -2423,14 +3608,22 @@ Redis.prototype.sadd = async function (key, members) {
|
|
|
2423
3608
|
const result = await connection.sAdd(prefixedKey, finalMembers);
|
|
2424
3609
|
|
|
2425
3610
|
// 触发后置事件
|
|
2426
|
-
this.emit('sadd_after', {
|
|
3611
|
+
this.emit('sadd_after', {
|
|
3612
|
+
key: finalKey,
|
|
3613
|
+
members: finalMembers,
|
|
3614
|
+
result
|
|
3615
|
+
});
|
|
2427
3616
|
return result;
|
|
2428
3617
|
} finally {
|
|
2429
3618
|
this._recycleConnection(connection);
|
|
2430
3619
|
}
|
|
2431
3620
|
} catch (err) {
|
|
2432
3621
|
$.log.error(`[Redis] 添加集合成员失败 [${key}]:`, err);
|
|
2433
|
-
this.emit('sadd_error', {
|
|
3622
|
+
this.emit('sadd_error', {
|
|
3623
|
+
key,
|
|
3624
|
+
members,
|
|
3625
|
+
error: err
|
|
3626
|
+
});
|
|
2434
3627
|
return 0;
|
|
2435
3628
|
}
|
|
2436
3629
|
};
|
|
@@ -2441,10 +3634,14 @@ Redis.prototype.sadd = async function (key, members) {
|
|
|
2441
3634
|
* @param {String|Array} members 成员或成员数组
|
|
2442
3635
|
* @return {Promise<Number>} 移除的成员数量
|
|
2443
3636
|
*/
|
|
2444
|
-
Redis.prototype.srem = async function
|
|
3637
|
+
Redis.prototype.srem = async function(key, members) {
|
|
2445
3638
|
try {
|
|
2446
3639
|
// 触发前置事件
|
|
2447
|
-
const beforeEventData = {
|
|
3640
|
+
const beforeEventData = {
|
|
3641
|
+
key,
|
|
3642
|
+
members: Array.isArray(members) ? members : [members],
|
|
3643
|
+
cancel: false
|
|
3644
|
+
};
|
|
2448
3645
|
this.emit('srem_before', beforeEventData);
|
|
2449
3646
|
|
|
2450
3647
|
if (beforeEventData.cancel) {
|
|
@@ -2452,7 +3649,10 @@ Redis.prototype.srem = async function (key, members) {
|
|
|
2452
3649
|
return 0;
|
|
2453
3650
|
}
|
|
2454
3651
|
|
|
2455
|
-
const {
|
|
3652
|
+
const {
|
|
3653
|
+
key: finalKey,
|
|
3654
|
+
members: finalMembers
|
|
3655
|
+
} = beforeEventData;
|
|
2456
3656
|
|
|
2457
3657
|
const connection = await this._getConnection();
|
|
2458
3658
|
try {
|
|
@@ -2460,14 +3660,22 @@ Redis.prototype.srem = async function (key, members) {
|
|
|
2460
3660
|
const result = await connection.sRem(prefixedKey, finalMembers);
|
|
2461
3661
|
|
|
2462
3662
|
// 触发后置事件
|
|
2463
|
-
this.emit('srem_after', {
|
|
3663
|
+
this.emit('srem_after', {
|
|
3664
|
+
key: finalKey,
|
|
3665
|
+
members: finalMembers,
|
|
3666
|
+
result
|
|
3667
|
+
});
|
|
2464
3668
|
return result;
|
|
2465
3669
|
} finally {
|
|
2466
3670
|
this._recycleConnection(connection);
|
|
2467
3671
|
}
|
|
2468
3672
|
} catch (err) {
|
|
2469
3673
|
$.log.error(`[Redis] 移除集合成员失败 [${key}]:`, err);
|
|
2470
|
-
this.emit('srem_error', {
|
|
3674
|
+
this.emit('srem_error', {
|
|
3675
|
+
key,
|
|
3676
|
+
members,
|
|
3677
|
+
error: err
|
|
3678
|
+
});
|
|
2471
3679
|
return 0;
|
|
2472
3680
|
}
|
|
2473
3681
|
};
|
|
@@ -2478,17 +3686,43 @@ Redis.prototype.srem = async function (key, members) {
|
|
|
2478
3686
|
* @param {String} member 成员
|
|
2479
3687
|
* @return {Promise<Boolean>} 是否存在
|
|
2480
3688
|
*/
|
|
2481
|
-
Redis.prototype.sismember = async function
|
|
3689
|
+
Redis.prototype.sismember = async function(key, member) {
|
|
2482
3690
|
try {
|
|
3691
|
+
// 参数验证
|
|
3692
|
+
if (!key || typeof key !== 'string') {
|
|
3693
|
+
$.log.error('[Redis] sismember参数错误: key must be a non-empty string');
|
|
3694
|
+
return false;
|
|
3695
|
+
}
|
|
3696
|
+
if (member === undefined || member === null) {
|
|
3697
|
+
$.log.error('[Redis] sismember参数错误: member cannot be undefined or null');
|
|
3698
|
+
return false;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3701
|
+
this.emit('sismember_before', {
|
|
3702
|
+
key,
|
|
3703
|
+
member
|
|
3704
|
+
});
|
|
2483
3705
|
const connection = await this._getConnection();
|
|
2484
3706
|
try {
|
|
2485
3707
|
const prefixedKey = this.applyPrefix(key);
|
|
2486
|
-
|
|
3708
|
+
// 将成员转换为字符串,与其他模块行为一致
|
|
3709
|
+
const memberStr = String(member);
|
|
3710
|
+
const result = await connection.sIsMember(prefixedKey, memberStr);
|
|
3711
|
+
this.emit('sismember_after', {
|
|
3712
|
+
key,
|
|
3713
|
+
member,
|
|
3714
|
+
result
|
|
3715
|
+
});
|
|
2487
3716
|
return result;
|
|
2488
3717
|
} finally {
|
|
2489
3718
|
this._recycleConnection(connection);
|
|
2490
3719
|
}
|
|
2491
3720
|
} catch (err) {
|
|
3721
|
+
this.emit('sismember_error', {
|
|
3722
|
+
key,
|
|
3723
|
+
member,
|
|
3724
|
+
error: err
|
|
3725
|
+
});
|
|
2492
3726
|
$.log.error(`[Redis] 检查集合成员失败 [${key}:${member}]:`, err);
|
|
2493
3727
|
return false;
|
|
2494
3728
|
}
|
|
@@ -2499,20 +3733,26 @@ Redis.prototype.sismember = async function (key, member) {
|
|
|
2499
3733
|
* @param {String} key 键
|
|
2500
3734
|
* @return {Promise<Array>} 成员数组
|
|
2501
3735
|
*/
|
|
2502
|
-
Redis.prototype.smembers = async function
|
|
3736
|
+
Redis.prototype.smembers = async function(key) {
|
|
2503
3737
|
try {
|
|
2504
3738
|
const connection = await this._getConnection();
|
|
2505
3739
|
try {
|
|
2506
3740
|
const prefixedKey = this.applyPrefix(key);
|
|
2507
3741
|
const result = await connection.sMembers(prefixedKey);
|
|
2508
|
-
this.emit('smembers_after', {
|
|
3742
|
+
this.emit('smembers_after', {
|
|
3743
|
+
key,
|
|
3744
|
+
result
|
|
3745
|
+
});
|
|
2509
3746
|
return result || [];
|
|
2510
3747
|
} finally {
|
|
2511
3748
|
this._recycleConnection(connection);
|
|
2512
3749
|
}
|
|
2513
3750
|
} catch (err) {
|
|
2514
3751
|
$.log.error(`[Redis] 获取集合所有成员失败 [${key}]:`, err);
|
|
2515
|
-
this.emit('smembers_error', {
|
|
3752
|
+
this.emit('smembers_error', {
|
|
3753
|
+
key,
|
|
3754
|
+
error: err
|
|
3755
|
+
});
|
|
2516
3756
|
return [];
|
|
2517
3757
|
}
|
|
2518
3758
|
};
|
|
@@ -2522,7 +3762,7 @@ Redis.prototype.smembers = async function (key) {
|
|
|
2522
3762
|
* @param {String} key 键
|
|
2523
3763
|
* @return {Promise<Number>} 成员数量
|
|
2524
3764
|
*/
|
|
2525
|
-
Redis.prototype.scard = async function
|
|
3765
|
+
Redis.prototype.scard = async function(key) {
|
|
2526
3766
|
try {
|
|
2527
3767
|
const connection = await this._getConnection();
|
|
2528
3768
|
try {
|
|
@@ -2543,7 +3783,7 @@ Redis.prototype.scard = async function (key) {
|
|
|
2543
3783
|
* @param {Array} keys 键数组
|
|
2544
3784
|
* @return {Promise<Array>} 交集成员数组
|
|
2545
3785
|
*/
|
|
2546
|
-
Redis.prototype.sinter = async function
|
|
3786
|
+
Redis.prototype.sinter = async function(keys) {
|
|
2547
3787
|
try {
|
|
2548
3788
|
const connection = await this._getConnection();
|
|
2549
3789
|
try {
|
|
@@ -2565,7 +3805,7 @@ Redis.prototype.sinter = async function (keys) {
|
|
|
2565
3805
|
* @param {Array} keys 键数组
|
|
2566
3806
|
* @return {Promise<Array>} 并集成员数组
|
|
2567
3807
|
*/
|
|
2568
|
-
Redis.prototype.sunion = async function
|
|
3808
|
+
Redis.prototype.sunion = async function(keys) {
|
|
2569
3809
|
try {
|
|
2570
3810
|
const connection = await this._getConnection();
|
|
2571
3811
|
try {
|
|
@@ -2587,7 +3827,7 @@ Redis.prototype.sunion = async function (keys) {
|
|
|
2587
3827
|
* @param {Array} keys 键数组
|
|
2588
3828
|
* @return {Promise<Array>} 差集成员数组
|
|
2589
3829
|
*/
|
|
2590
|
-
Redis.prototype.sdiff = async function
|
|
3830
|
+
Redis.prototype.sdiff = async function(keys) {
|
|
2591
3831
|
try {
|
|
2592
3832
|
const connection = await this._getConnection();
|
|
2593
3833
|
try {
|
|
@@ -2613,10 +3853,15 @@ Redis.prototype.sdiff = async function (keys) {
|
|
|
2613
3853
|
* @param {String} member 成员
|
|
2614
3854
|
* @return {Promise<Boolean>} 是否成功
|
|
2615
3855
|
*/
|
|
2616
|
-
Redis.prototype.zadd = async function
|
|
3856
|
+
Redis.prototype.zadd = async function(key, score, member) {
|
|
2617
3857
|
try {
|
|
2618
3858
|
// 触发前置事件
|
|
2619
|
-
const beforeEventData = {
|
|
3859
|
+
const beforeEventData = {
|
|
3860
|
+
key,
|
|
3861
|
+
score,
|
|
3862
|
+
member,
|
|
3863
|
+
cancel: false
|
|
3864
|
+
};
|
|
2620
3865
|
this.emit('zadd_before', beforeEventData);
|
|
2621
3866
|
|
|
2622
3867
|
if (beforeEventData.cancel) {
|
|
@@ -2624,22 +3869,39 @@ Redis.prototype.zadd = async function (key, score, member) {
|
|
|
2624
3869
|
return false;
|
|
2625
3870
|
}
|
|
2626
3871
|
|
|
2627
|
-
const {
|
|
3872
|
+
const {
|
|
3873
|
+
key: finalKey,
|
|
3874
|
+
score: finalScore,
|
|
3875
|
+
member: finalMember
|
|
3876
|
+
} = beforeEventData;
|
|
2628
3877
|
|
|
2629
3878
|
const connection = await this._getConnection();
|
|
2630
3879
|
try {
|
|
2631
3880
|
const prefixedKey = this.applyPrefix(finalKey);
|
|
2632
|
-
const result = await connection.zAdd(prefixedKey, {
|
|
3881
|
+
const result = await connection.zAdd(prefixedKey, {
|
|
3882
|
+
score: finalScore,
|
|
3883
|
+
value: finalMember
|
|
3884
|
+
});
|
|
2633
3885
|
|
|
2634
3886
|
// 触发后置事件
|
|
2635
|
-
this.emit('zadd_after', {
|
|
3887
|
+
this.emit('zadd_after', {
|
|
3888
|
+
key: finalKey,
|
|
3889
|
+
score: finalScore,
|
|
3890
|
+
member: finalMember,
|
|
3891
|
+
success: result === 1
|
|
3892
|
+
});
|
|
2636
3893
|
return result === 1;
|
|
2637
3894
|
} finally {
|
|
2638
3895
|
this._recycleConnection(connection);
|
|
2639
3896
|
}
|
|
2640
3897
|
} catch (err) {
|
|
2641
3898
|
$.log.error(`[Redis] 添加有序集合成员失败 [${key}]:`, err);
|
|
2642
|
-
this.emit('zadd_error', {
|
|
3899
|
+
this.emit('zadd_error', {
|
|
3900
|
+
key,
|
|
3901
|
+
score,
|
|
3902
|
+
member,
|
|
3903
|
+
error: err
|
|
3904
|
+
});
|
|
2643
3905
|
return false;
|
|
2644
3906
|
}
|
|
2645
3907
|
};
|
|
@@ -2650,10 +3912,14 @@ Redis.prototype.zadd = async function (key, score, member) {
|
|
|
2650
3912
|
* @param {String|Array} members 成员或成员数组
|
|
2651
3913
|
* @return {Promise<Number>} 移除的成员数量
|
|
2652
3914
|
*/
|
|
2653
|
-
Redis.prototype.zrem = async function
|
|
3915
|
+
Redis.prototype.zrem = async function(key, members) {
|
|
2654
3916
|
try {
|
|
2655
3917
|
// 触发前置事件
|
|
2656
|
-
const beforeEventData = {
|
|
3918
|
+
const beforeEventData = {
|
|
3919
|
+
key,
|
|
3920
|
+
members: Array.isArray(members) ? members : [members],
|
|
3921
|
+
cancel: false
|
|
3922
|
+
};
|
|
2657
3923
|
this.emit('zrem_before', beforeEventData);
|
|
2658
3924
|
|
|
2659
3925
|
if (beforeEventData.cancel) {
|
|
@@ -2661,7 +3927,10 @@ Redis.prototype.zrem = async function (key, members) {
|
|
|
2661
3927
|
return 0;
|
|
2662
3928
|
}
|
|
2663
3929
|
|
|
2664
|
-
const {
|
|
3930
|
+
const {
|
|
3931
|
+
key: finalKey,
|
|
3932
|
+
members: finalMembers
|
|
3933
|
+
} = beforeEventData;
|
|
2665
3934
|
|
|
2666
3935
|
const connection = await this._getConnection();
|
|
2667
3936
|
try {
|
|
@@ -2669,18 +3938,30 @@ Redis.prototype.zrem = async function (key, members) {
|
|
|
2669
3938
|
const result = await connection.zRem(this.applyPrefix(finalKey), ...finalMembers);
|
|
2670
3939
|
|
|
2671
3940
|
// 触发后置事件
|
|
2672
|
-
this.emit('zrem_after', {
|
|
3941
|
+
this.emit('zrem_after', {
|
|
3942
|
+
key: finalKey,
|
|
3943
|
+
members: finalMembers,
|
|
3944
|
+
result
|
|
3945
|
+
});
|
|
2673
3946
|
return result;
|
|
2674
3947
|
} catch (err) {
|
|
2675
3948
|
$.log.error(`[Redis] 移除有序集合成员失败 [${finalKey}]:`, err);
|
|
2676
|
-
this.emit('zrem_error', {
|
|
3949
|
+
this.emit('zrem_error', {
|
|
3950
|
+
key: finalKey,
|
|
3951
|
+
members: finalMembers,
|
|
3952
|
+
error: err
|
|
3953
|
+
});
|
|
2677
3954
|
return 0;
|
|
2678
3955
|
} finally {
|
|
2679
3956
|
this._recycleConnection(connection);
|
|
2680
3957
|
}
|
|
2681
3958
|
} catch (err) {
|
|
2682
3959
|
$.log.error(`[Redis] 移除有序集合成员失败 [${key}]:`, err);
|
|
2683
|
-
this.emit('zrem_error', {
|
|
3960
|
+
this.emit('zrem_error', {
|
|
3961
|
+
key,
|
|
3962
|
+
members,
|
|
3963
|
+
error: err
|
|
3964
|
+
});
|
|
2684
3965
|
return 0;
|
|
2685
3966
|
}
|
|
2686
3967
|
};
|
|
@@ -2691,7 +3972,7 @@ Redis.prototype.zrem = async function (key, members) {
|
|
|
2691
3972
|
* @param {String} member 成员
|
|
2692
3973
|
* @return {Promise<Number|null>} 分数
|
|
2693
3974
|
*/
|
|
2694
|
-
Redis.prototype.zscore = async function
|
|
3975
|
+
Redis.prototype.zscore = async function(key, member) {
|
|
2695
3976
|
try {
|
|
2696
3977
|
const connection = await this._getConnection();
|
|
2697
3978
|
try {
|
|
@@ -2713,7 +3994,7 @@ Redis.prototype.zscore = async function (key, member) {
|
|
|
2713
3994
|
* @param {String} member 成员
|
|
2714
3995
|
* @return {Promise<Number|null>} 排名
|
|
2715
3996
|
*/
|
|
2716
|
-
Redis.prototype.zrank = async function
|
|
3997
|
+
Redis.prototype.zrank = async function(key, member) {
|
|
2717
3998
|
try {
|
|
2718
3999
|
const connection = await this._getConnection();
|
|
2719
4000
|
try {
|
|
@@ -2737,32 +4018,38 @@ Redis.prototype.zrank = async function (key, member) {
|
|
|
2737
4018
|
* @param {Boolean} withScores 是否返回分数
|
|
2738
4019
|
* @return {Promise<Array>} 成员数组
|
|
2739
4020
|
*/
|
|
2740
|
-
Redis.prototype.zrange = async function
|
|
4021
|
+
Redis.prototype.zrange = async function(key, start, stop, withScores = false) {
|
|
2741
4022
|
try {
|
|
2742
4023
|
const connection = await this._getConnection();
|
|
2743
4024
|
try {
|
|
2744
4025
|
const prefixedKey = this.applyPrefix(key);
|
|
2745
|
-
|
|
4026
|
+
|
|
2746
4027
|
if (withScores) {
|
|
2747
4028
|
// 对于需要分数的情况,使用更可靠的方法
|
|
2748
4029
|
try {
|
|
2749
4030
|
// 直接获取所有成员的分数
|
|
2750
4031
|
const members = await connection.zRange(prefixedKey, start, stop);
|
|
2751
4032
|
const results = [];
|
|
2752
|
-
|
|
4033
|
+
|
|
2753
4034
|
// 为每个成员单独获取分数,确保准确性
|
|
2754
4035
|
for (const member of members) {
|
|
2755
4036
|
try {
|
|
2756
4037
|
let score = await connection.zScore(prefixedKey, member);
|
|
2757
4038
|
score = score ? parseFloat(score) : 0;
|
|
2758
|
-
results.push({
|
|
4039
|
+
results.push({
|
|
4040
|
+
member,
|
|
4041
|
+
score
|
|
4042
|
+
});
|
|
2759
4043
|
} catch (scoreErr) {
|
|
2760
4044
|
// 如果获取分数失败,设置默认值0
|
|
2761
|
-
results.push({
|
|
4045
|
+
results.push({
|
|
4046
|
+
member,
|
|
4047
|
+
score: 0
|
|
4048
|
+
});
|
|
2762
4049
|
$.log.warn(`[Redis] 获取成员 ${member} 分数失败:`, scoreErr);
|
|
2763
4050
|
}
|
|
2764
4051
|
}
|
|
2765
|
-
|
|
4052
|
+
|
|
2766
4053
|
return results;
|
|
2767
4054
|
} catch (err) {
|
|
2768
4055
|
$.log.error(`[Redis] 使用备用方法获取有序集合范围失败 [${prefixedKey}]:`, err);
|
|
@@ -2788,7 +4075,7 @@ Redis.prototype.zrange = async function (key, start, stop, withScores = false) {
|
|
|
2788
4075
|
* @param {String} key 键
|
|
2789
4076
|
* @return {Promise<Number>} 成员数量
|
|
2790
4077
|
*/
|
|
2791
|
-
Redis.prototype.zcard = async function
|
|
4078
|
+
Redis.prototype.zcard = async function(key) {
|
|
2792
4079
|
try {
|
|
2793
4080
|
const connection = await this._getConnection();
|
|
2794
4081
|
try {
|
|
@@ -2808,16 +4095,37 @@ Redis.prototype.zcard = async function (key) {
|
|
|
2808
4095
|
};
|
|
2809
4096
|
|
|
2810
4097
|
/**
|
|
2811
|
-
* @description
|
|
4098
|
+
* @description 增加有序集合中成员的分数(兼容方法)
|
|
2812
4099
|
* @param {String} key 键
|
|
2813
4100
|
* @param {Number} increment 增量
|
|
2814
4101
|
* @param {String} member 成员
|
|
4102
|
+
* @param {Number} [seconds=0] 过期时间(秒)
|
|
2815
4103
|
* @return {Promise<Number>} 增加后的分数
|
|
2816
4104
|
*/
|
|
2817
|
-
Redis.prototype.zincrby = async function
|
|
4105
|
+
Redis.prototype.zincrby = async function(key, increment, member, seconds = 0) {
|
|
2818
4106
|
try {
|
|
4107
|
+
// 参数验证
|
|
4108
|
+
if (!key || typeof key !== 'string') {
|
|
4109
|
+
$.log.error('[Redis] 参数错误: 键必须是非空字符串');
|
|
4110
|
+
return null;
|
|
4111
|
+
}
|
|
4112
|
+
if (typeof increment !== 'number' || isNaN(increment)) {
|
|
4113
|
+
$.log.error('[Redis] 参数错误: 增量必须是有效数字');
|
|
4114
|
+
return null;
|
|
4115
|
+
}
|
|
4116
|
+
if (!member || typeof member !== 'string') {
|
|
4117
|
+
$.log.error('[Redis] 参数错误: 成员必须是非空字符串');
|
|
4118
|
+
return null;
|
|
4119
|
+
}
|
|
4120
|
+
|
|
2819
4121
|
// 触发前置事件
|
|
2820
|
-
const beforeEventData = {
|
|
4122
|
+
const beforeEventData = {
|
|
4123
|
+
key,
|
|
4124
|
+
increment,
|
|
4125
|
+
member,
|
|
4126
|
+
seconds,
|
|
4127
|
+
cancel: false
|
|
4128
|
+
};
|
|
2821
4129
|
this.emit('zincrby_before', beforeEventData);
|
|
2822
4130
|
|
|
2823
4131
|
if (beforeEventData.cancel) {
|
|
@@ -2825,32 +4133,65 @@ Redis.prototype.zincrby = async function (key, increment, member) {
|
|
|
2825
4133
|
return null;
|
|
2826
4134
|
}
|
|
2827
4135
|
|
|
2828
|
-
const {
|
|
4136
|
+
const {
|
|
4137
|
+
key: finalKey,
|
|
4138
|
+
increment: finalIncrement,
|
|
4139
|
+
member: finalMember,
|
|
4140
|
+
seconds: finalSeconds
|
|
4141
|
+
} = beforeEventData;
|
|
2829
4142
|
|
|
2830
4143
|
const connection = await this._getConnection();
|
|
2831
4144
|
try {
|
|
4145
|
+
const prefixedKey = this.applyPrefix(finalKey);
|
|
2832
4146
|
// 使用正确的Redis客户端Promise API
|
|
2833
|
-
const result = await connection.zIncrBy(
|
|
2834
|
-
|
|
4147
|
+
const result = await connection.zIncrBy(prefixedKey, finalIncrement, finalMember);
|
|
4148
|
+
|
|
4149
|
+
// 如果设置了过期时间
|
|
4150
|
+
if (finalSeconds > 0) {
|
|
4151
|
+
if (typeof connection.expire === 'function') {
|
|
4152
|
+
await connection.expire(prefixedKey, finalSeconds);
|
|
4153
|
+
} else if (typeof connection.execute === 'function') {
|
|
4154
|
+
await connection.execute('EXPIRE', [prefixedKey, finalSeconds]);
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
|
|
2835
4158
|
// 触发后置事件
|
|
2836
|
-
this.emit('zincrby_after', {
|
|
4159
|
+
this.emit('zincrby_after', {
|
|
4160
|
+
key: finalKey,
|
|
4161
|
+
increment: finalIncrement,
|
|
4162
|
+
member: finalMember,
|
|
4163
|
+
seconds: finalSeconds,
|
|
4164
|
+
result: parseFloat(result)
|
|
4165
|
+
});
|
|
2837
4166
|
return parseFloat(result);
|
|
2838
4167
|
} catch (err) {
|
|
2839
4168
|
$.log.error(`[Redis] 增加有序集合成员分数失败 [${finalKey}]:`, err);
|
|
2840
|
-
this.emit('zincrby_error', {
|
|
4169
|
+
this.emit('zincrby_error', {
|
|
4170
|
+
key: finalKey,
|
|
4171
|
+
increment: finalIncrement,
|
|
4172
|
+
member: finalMember,
|
|
4173
|
+
seconds: finalSeconds,
|
|
4174
|
+
error: err
|
|
4175
|
+
});
|
|
2841
4176
|
return null;
|
|
2842
4177
|
} finally {
|
|
2843
4178
|
this._recycleConnection(connection);
|
|
2844
4179
|
}
|
|
2845
4180
|
} catch (err) {
|
|
2846
4181
|
$.log.error(`[Redis] 增加有序集合成员分数失败 [${key}]:`, err);
|
|
2847
|
-
this.emit('zincrby_error', {
|
|
4182
|
+
this.emit('zincrby_error', {
|
|
4183
|
+
key,
|
|
4184
|
+
increment,
|
|
4185
|
+
member,
|
|
4186
|
+
seconds,
|
|
4187
|
+
error: err
|
|
4188
|
+
});
|
|
2848
4189
|
return null;
|
|
2849
4190
|
}
|
|
2850
4191
|
};
|
|
2851
4192
|
|
|
2852
4193
|
// 创建真实Redis连接的方法
|
|
2853
|
-
Redis.prototype._createConnection = function
|
|
4194
|
+
Redis.prototype._createConnection = function() {
|
|
2854
4195
|
return new Promise((resolve, reject) => {
|
|
2855
4196
|
try {
|
|
2856
4197
|
const cg = this.config;
|
|
@@ -2914,7 +4255,7 @@ function redis_admin(scope, config) {
|
|
|
2914
4255
|
}
|
|
2915
4256
|
var obj = $.pool.redis[scope];
|
|
2916
4257
|
if (!obj) {
|
|
2917
|
-
$.pool.redis[scope] = new Redis(
|
|
4258
|
+
$.pool.redis[scope] = new Redis(config);
|
|
2918
4259
|
obj = $.pool.redis[scope];
|
|
2919
4260
|
}
|
|
2920
4261
|
return obj;
|
|
@@ -2923,4 +4264,4 @@ function redis_admin(scope, config) {
|
|
|
2923
4264
|
/**
|
|
2924
4265
|
* @module 导出Redis管理器
|
|
2925
4266
|
*/
|
|
2926
|
-
exports.redis_admin = redis_admin;
|
|
4267
|
+
exports.redis_admin = redis_admin;
|