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/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 eventEmitter = require('events');
7
- require('mm_logs');
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 eventEmitter {
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(scope, config) {
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
- this.setConfig(config);
47
- // 连接池配置
48
- this.connectionPool = {
49
- available: [], // 可用连接数组
50
- inUse: new Set(), // 使用中的连接集合
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.retryCount = 0;
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 (key) {
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 (key) {
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 (channel, func) {
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 (channel) {
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 (channel, message) {
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
- throw err;
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 (channel, message) {
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 (channel, message) {
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} cg 配置对象或配置路径
169
+ * @param {Object} config 配置对象或配置路径
170
170
  */
171
- // 修复setConfig方法,移除其中的_initPool定义
172
- Redis.prototype.setConfig = function (cg) {
171
+ Redis.prototype.setConfig = function(config) {
173
172
  let obj = {};
174
- if (typeof cg === 'string') {
175
- obj = cg.loadJson(__dirname);
176
- } else if (cg) {
177
- // 合并配置参数
178
- Object.assign(this.config, cg);
179
- }
180
- else {
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
- // 使用Object.assign替代$.push来合并配置
184
- this.config = Object.assign({}, this.config || {}, obj);
185
- // 检查是否存在连接池配置,如果没有则使用默认配置
193
+
194
+ // 确保连接池配置存在且包含所有必要字段
186
195
  if (!this.config.pool) {
187
- this.config.pool = {
188
- max_clients: 10,
189
- min_idle: 2,
190
- max_idle: 5,
191
- idle_timeout: 30000
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.connectionPool = {
238
+ this.connection_pool = {
205
239
  available: [],
206
- inUse: new Set(),
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.retryCount = 0;
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.retryCount = 0;
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.retryCount >= this.config.retry_count) {
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.retryCount++;
302
- $.log.debug(`[Redis] 尝试第 ${this.retryCount} 次重连...`);
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.connectionPool && this.connectionPool.inUse) {
324
- for (const connection of this.connectionPool.inUse) {
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.connectionPool.inUse.clear();
360
+ this.connection_pool.in_use.clear();
332
361
  }
333
362
 
334
363
  // 关闭所有可用连接
335
- if (this.connectionPool && this.connectionPool.available) {
336
- for (const connection of this.connectionPool.available) {
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.connectionPool.available = [];
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 (key, value, seconds) {
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 (key) {
405
+ get: function(key) {
377
406
  commands.push(['get', key]);
378
407
  return this;
379
408
  },
380
409
 
381
410
  // 删除
382
- del: function (key) {
411
+ del: function(key) {
383
412
  commands.push(['del', key]);
384
413
  return this;
385
414
  },
386
415
 
387
416
  // 判断存在
388
- has: function (key) {
417
+ has: function(key) {
389
418
  commands.push(['has', key]);
390
419
  return this;
391
420
  },
392
421
 
393
422
  // 设置哈希字段
394
- hset: function (key, field, value) {
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 (key, seconds) {
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
- Redis.prototype.ttl = async function (key) {
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
- // 只获取过期时间,符合ttl的标准功能
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] 获取过期时间失败 [${key}]:`, err);
534
- return -2; // -2表示出错
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 (key, num) {
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 = { key, value: num, cancel: false };
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 { key: finalKey, value: finalNum } = beforeEventData;
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
- finalKey = this.applyPrefix(finalKey);
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(finalKey, finalNum);
641
+ result = await connection.incrBy(prefixedKey, finalNum);
567
642
  } else if (typeof connection.execute === 'function') {
568
- result = await connection.execute('INCRBY', [finalKey, finalNum]);
643
+ result = await connection.execute('INCRBY', [prefixedKey, finalNum]);
569
644
  } else {
570
645
  // 如果出错,回退到先获取再设置的方式
571
646
  $.log.warn(`[Redis] 使用原子操作增加整数值失败,使用回退方案`);
572
- let value = await this.get(finalKey);
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(finalKey, n);
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(finalKey, -finalNum);
659
+ result = await connection.decrBy(prefixedKey, -finalNum);
585
660
  } else if (typeof connection.execute === 'function') {
586
- result = await connection.execute('DECRBY', [finalKey, -finalNum]);
661
+ result = await connection.execute('DECRBY', [prefixedKey, -finalNum]);
587
662
  } else {
588
663
  // 如果出错,回退到先获取再设置的方式
589
664
  $.log.warn(`[Redis] 使用原子操作减少整数值失败,使用回退方案`);
590
- let value = await this.get(finalKey);
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(finalKey, n);
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', { key: finalKey, value: finalNum, result, success: true });
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', { key, value: num, error: err });
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 (key, num) {
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 = { key, value: num, cancel: false };
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 { key: finalKey, value: finalNum } = beforeEventData;
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
- finalKey = this.applyPrefix(finalKey);
758
+ const prefixedKey = this.applyPrefix(finalKey);
641
759
 
642
760
  // 优先使用原生方法
643
761
  if (typeof connection.incrByFloat === 'function') {
644
- result = parseFloat(await connection.incrByFloat(finalKey, finalNum));
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', [finalKey, finalNum]));
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(finalKey);
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(finalKey, n);
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', { key: finalKey, value: finalNum, result, success: true });
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(finalKey);
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(finalKey, n);
808
+ await this.set(prefixedKey, n, finalSeconds);
676
809
 
677
810
  // 触发后置事件
678
- this.emit('addFloat_after', { key: finalKey, value: finalNum, result: n, success: true });
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', { key, value: num, error: err });
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 (key, str) {
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 = { key, value: str, cancel: false };
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 { key: finalKey, value: finalStr } = beforeEventData;
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
- finalKey = this.applyPrefix(finalKey);
883
+ const prefixedKey = this.applyPrefix(finalKey);
717
884
 
718
885
  // 优先使用原生方法
719
886
  if (typeof connection.append === 'function') {
720
- result = await connection.append(finalKey, finalStr);
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', [finalKey, finalStr]);
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(finalKey) || '';
896
+ let value = await this.get(prefixedKey) || '';
730
897
  value += finalStr;
731
- await this.set(finalKey, value);
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', { key: finalKey, value: finalStr, result, success: true });
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(finalKey) || '';
923
+ let value = await this.get(prefixedKey) || '';
742
924
  value += finalStr;
743
- await this.set(finalKey, value);
925
+ await this.set(prefixedKey, value, finalSeconds);
744
926
 
745
927
  // 触发后置事件
746
- this.emit('addStr_after', { key: finalKey, value: finalStr, result: value.length, success: true });
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', { key, value: str, error: err });
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 (key) {
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 = { key, cancel: false };
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 { key: finalKey } = beforeEventData;
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', { key: finalKey, success, deleted });
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', { key: finalKey, success, deleted: success ? [finalKey] : [] });
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', { key, error: err });
812
- $.log.error('[Redis] 删除缓存失败:', err);
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
- Redis.prototype.set = async function (key, value, seconds) {
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 = { key, value, ttl: seconds, cancel: false };
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 { key: finalKey, value: finalValue, ttl: finalSeconds } = beforeEventData;
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
- finalKey = this.applyPrefix(finalKey);
842
- if (typeof (finalValue) === "object") {
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(finalKey, finalValue, {
1103
+ if (finalSeconds > 0) {
1104
+ await connection.set(prefixedKey, finalValue, {
848
1105
  EX: finalSeconds
849
1106
  });
850
1107
  } else {
851
- await connection.set(finalKey, finalValue);
1108
+ await connection.set(prefixedKey, finalValue);
852
1109
  }
853
1110
 
854
1111
  // 触发后置事件
855
- this.emit('set_after', { key, value, ttl: seconds, success: true });
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', { key, value, error: err });
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 (key, value, seconds) {
1142
+ Redis.prototype.add = async function(key, value, seconds) {
876
1143
  try {
877
1144
  // 触发前置事件,允许修改或阻止操作
878
- const beforeEventData = { key, value, ttl: seconds, cancel: false };
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 { key: finalKey, value: finalValue, ttl: finalSeconds } = beforeEventData;
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 (finalValue) === "object") {
1169
+ if (typeof(finalValue) === "object") {
894
1170
  finalValue = JSON.stringify(finalValue);
895
1171
  }
896
1172
 
897
- const options = finalSeconds ? { NX: true, EX: finalSeconds } : { NX: true };
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', { key: finalKey, value: finalValue, ttl: finalSeconds, success });
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', { key, value, ttl: seconds, error: err });
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
- Redis.prototype.get = async function (key) {
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
- key = this.applyPrefix(key);
925
- const value = await connection.get(key);
926
- if (typeof (value) === "string") {
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
- return JSON.parse(value);
1251
+ result = JSON.parse(value);
932
1252
  }
933
1253
  } catch (e) {
934
1254
  // 解析失败,返回原始字符串
935
1255
  }
936
1256
  }
937
- return value;
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 (key) {
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
- key = this.applyPrefix(key);
957
- const result = await connection.exists(key);
958
- return result > 0;
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 (key) {
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
- key = this.applyPrefix(key);
978
- const result = await connection.exists(key);
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 (key, start, end) {
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 (str, index, str_sub) {
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 (key, index, value) {
1467
+ Redis.prototype.setrange = async function(key, index, value) {
1038
1468
  try {
1039
1469
  // 触发前置事件,允许修改或阻止操作
1040
- const beforeEventData = { key, index, value, cancel: false };
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 { key: finalKey, index: finalIndex, value: finalValue } = beforeEventData;
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', { key: finalKey, index: finalIndex, value: finalValue, result, success: true });
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', { key: finalKey, index: finalIndex, value: finalValue, result: str.length, success: true });
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', { key, index, value, error: err });
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 (key) {
1567
+ Redis.prototype.clear = async function(key) {
1112
1568
  try {
1113
1569
  // 触发前置事件,允许修改或阻止操作
1114
- const beforeEventData = { key, cancel: false };
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 { key: finalKey } = beforeEventData;
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', { key: finalKey, success });
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', { key, error: err });
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 (key, options = {}) {
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.list_get(key);
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 (pattern = '*') {
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.list_set = async function (key, value, seconds) {
1715
+ Redis.prototype.setForList = async function(key, value, seconds) {
1249
1716
  try {
1250
1717
  // 触发前置事件,允许修改或阻止操作
1251
- const beforeEventData = { key, value, ttl: seconds, cancel: false };
1252
- this.emit('list_set_before', beforeEventData);
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] list_set操作被事件处理器取消 [${key}]`);
1728
+ $.log.debug(`[Redis] setForList操作被事件处理器取消 [${key}]`);
1257
1729
  return 0;
1258
1730
  }
1259
1731
 
1260
1732
  // 使用可能被修改的值
1261
- let { key: finalKey, value: finalValue, ttl: finalSeconds } = beforeEventData;
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('list_set_after', { key: finalKey, value: finalValue, ttl: finalSeconds, result: len, success: len > 0 });
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('list_set_error', { key, value, error: err });
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 (key, value) {
1814
+ Redis.prototype.list_push = async function(key, value) {
1329
1815
  try {
1330
1816
  // 触发前置事件,允许修改或阻止操作
1331
- const beforeEventData = { key, value, cancel: false };
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 { key: finalKey, value: finalValue } = beforeEventData;
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', { key: finalKey, value: finalValue, result: len, success });
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', { key, value, error: err });
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 数组缓存追加对象 (list_push的别名,兼容API)
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.list_add = Redis.prototype.list_push;
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
- * @description 判断成员是否存在
1397
- * @param {String} key 键
1398
- * @param {Object} value 值
1399
- * @return {Promise<Boolean>} 存在返回true, 否则返回false
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
- try {
1407
- // 尝试使用Redis的LREM命令来检查是否存在
1408
- const keyWithPrefix = this.applyPrefix(key);
1409
- // 转换value为字符串以便比较
1410
- const valueStr = JSON.stringify(value);
1411
- // 使用scan来查找,避免在大列表上的性能问题
1412
- const arr = await this.list_get(key);
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
- key = this.applyPrefix(key);
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方法,不支持则使用通用execute方法
2047
+ // 检查连接是否支持lRange方法,Redis v4 API
1437
2048
  if (typeof connection.lRange === 'function') {
1438
- result = await connection.lRange(key, start, end);
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
- result = await connection.execute('LRANGE', [key, start, end]);
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
- return result.map(item => {
1447
- if (typeof (item) === "string") {
2067
+ const parsedResult = result.map(item => {
2068
+ if (typeof item === "string") {
1448
2069
  try {
1449
- if (item.startsWith("{") && item.endsWith("}") ||
1450
- item.startsWith("[") && item.endsWith("]")) {
1451
- return JSON.parse(item);
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.list_clear = async function (key, value) {
2112
+ Redis.prototype.clearForList = async function(key) {
1475
2113
  if (!key) {
1476
- throw new Error('键不能为空');
2114
+ $.log.error('[Redis] 参数错误: 键不能为空');
2115
+ return false;
1477
2116
  }
1478
2117
 
1479
2118
  try {
1480
2119
  // 触发前置事件,允许修改或阻止操作
1481
- const beforeEventData = { key, value, cancel: false };
1482
- this.emit('list_clear_before', beforeEventData);
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] list_clear操作被事件处理器取消 [${key}]`);
2128
+ $.log.debug(`[Redis] clearForList操作被事件处理器取消 [${key}]`);
1487
2129
  return false;
1488
2130
  }
1489
2131
 
1490
2132
  // 使用可能被修改的值
1491
- let { key: finalKey, value: finalValue } = beforeEventData;
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
- if (typeof connection.del === 'function') {
1500
- await connection.del(finalKey);
1501
- } else if (typeof connection.execute === 'function') {
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
- if (finalValue && success) {
1510
- try {
1511
- if (Array.isArray(finalValue)) {
1512
- if (finalValue.length > 0) {
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('list_clear_error', { key, value, error: err });
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
- Redis.prototype.mget = async function (keys) {
1560
- const connection = await this._getConnection();
2162
+ /**
2163
+ * @description 批量获取缓存(兼容方法)
2164
+ * @param {Array<String>} keys 键数组
2165
+ * @return {Promise<Array>} 返回值数组,顺序与键数组一致
2166
+ */
2167
+ Redis.prototype.mget = async function(keys) {
1561
2168
  try {
1562
- // 直接使用Redis v4的mGet方法,应用键前缀
1563
- const keysWithPrefix = keys.map(k => this.applyPrefix(k));
1564
- const values = await connection.mGet(keysWithPrefix);
1565
-
1566
- // 处理值的格式,保持数组返回格式
1567
- const result = [];
1568
- values.forEach((value, index) => {
1569
- if (typeof (value) === "string") {
1570
- try {
1571
- if (value.startsWith("{") && value.endsWith("}") ||
1572
- value.startsWith("[") && value.endsWith("]")) {
1573
- value = JSON.parse(value);
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
- result[index] = value;
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
- return result;
1582
- } finally {
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<Boolean>} 操作结果
2247
+ * @return {Promise<boolean>} 返回true
1592
2248
  */
1593
- Redis.prototype.mset = async function (obj, seconds) {
2249
+ Redis.prototype.mset = async function(obj, seconds = 0) {
1594
2250
  try {
1595
2251
  // 触发前置事件,允许修改或阻止操作
1596
- const beforeEventData = { value: obj, ttl: seconds, cancel: false };
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('[Redis] mset操作被事件处理器取消');
2261
+ $.log.debug(`[Redis] mset操作被事件处理器取消`);
1602
2262
  return false;
1603
2263
  }
1604
2264
 
1605
2265
  // 使用可能被修改的值
1606
- let { value: finalData, ttl: finalSeconds } = beforeEventData;
2266
+ let {
2267
+ value: finalData,
2268
+ seconds: finalSeconds
2269
+ } = beforeEventData;
1607
2270
 
1608
2271
  // 检查最终数据是否有效
1609
- if (!finalData || typeof finalData !== 'object') {
1610
- throw new Error('事件处理后的value必须是非空对象');
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', { value: finalData, ttl: finalSeconds, success: true });
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', { value: obj, ttl: seconds, error: err });
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 (value) === "object" ? JSON.stringify(value) : value;
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
- throw new Error('mset参数格式错误:键值对数量必须相等');
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', { value: obj, ttl: seconds, success: true });
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', { value: obj, ttl: seconds, error: fallbackErr });
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 (connection) {
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 (key, fields) {
2366
+ Redis.prototype.hdel = async function(key, fields) {
1700
2367
  try {
1701
2368
  // 触发前置事件,允许修改或阻止操作
1702
- const beforeEventData = { key, value: fields, cancel: false };
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 { key: finalKey, value: finalFields } = beforeEventData;
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', { key: finalKey, value: finalFields, success });
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', { key, value: fields, error: err });
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 (key) {
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 { name, args } = cmd;
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 (args[1]) === 'object') {
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], { EX: processedArgs[2] });
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 (value) === 'string') {
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
- * @description redis连接池
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 redis_admin(scope, dir) {
1912
- if (!scope) {
1913
- scope = $.val.scope
1914
- }
1915
- var obj = $.pool.redis[scope];
1916
- if (!obj) {
1917
- $.pool.redis[scope] = new Redis(scope, dir);
1918
- obj = $.pool.redis[scope];
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
- return obj;
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.connectionPool.available.length > 0) {
1934
- const connection = this.connectionPool.available.pop();
1935
- this.connectionPool.inUse.add(connection);
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.connectionPool.available.length + this.connectionPool.inUse.size;
1942
- if (totalConnections < this.connectionPool.config.max_clients) {
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.connectionPool.inUse.add(connection);
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.connectionPool.inUse.add(connection);
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 (connection) {
2646
+ Redis.prototype._releaseConnection = function(connection) {
1971
2647
  try {
1972
- if (this.connectionPool.inUse.has(connection)) {
1973
- this.connectionPool.inUse.delete(connection);
2648
+ if (this.connection_pool.in_use.has(connection)) {
2649
+ this.connection_pool.in_use.delete(connection);
1974
2650
 
1975
2651
  // 检查是否超过最大空闲连接数
1976
- if (this.connectionPool.available.length < this.connectionPool.config.max_idle) {
1977
- this.connectionPool.available.push(connection);
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 (key, field, value) {
2687
+ Redis.prototype.hset = async function(key, field, value) {
2013
2688
  try {
2014
2689
  // 触发前置事件,允许修改或阻止操作
2015
- const beforeEventData = { key, field, value, cancel: false };
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 { key: finalKey, field: finalField, value: finalValue } = beforeEventData;
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 (finalValue) !== "string") {
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', { key: finalKey, field: finalField, value: finalValue, success: true });
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', { key, field, value, error: err });
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 (key, field) {
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 (value) === 'string') {
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 (key, obj) {
2786
+ Redis.prototype.hmset = async function(key, obj) {
2093
2787
  try {
2094
2788
  // 触发前置事件,允许修改或阻止操作
2095
- const beforeEventData = { key, value: obj, cancel: false };
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 { key: finalKey, value: finalObj } = beforeEventData;
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
- if (typeof connection.hSet === 'function') {
2113
- for (const field in finalObj) {
2114
- if (finalObj.hasOwnProperty(field)) {
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
- await connection.hSet(finalKey, field, value);
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', { key: finalKey, value: finalObj, success: true });
2130
- return true;
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', { key, value: obj, error: err });
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 (key) {
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
- if (typeof connection.hGetAll === 'function') {
2153
- const result = await connection.hGetAll(key);
2154
- return result;
2155
- } else {
2156
- $.log.error('[Redis] 连接对象不支持hGetAll操作');
2157
- return {};
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._releaseConnection(connection);
2909
+ this._recycleConnection(connection);
2161
2910
  }
2162
2911
  } catch (err) {
2163
2912
  $.log.error(`[Redis] 获取哈希所有字段和值失败 [${key}]:`, err);
2164
- return null;
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 (key, seconds) {
2989
+ Redis.prototype.expire = async function(key, seconds) {
2175
2990
  try {
2176
2991
  // 触发前置事件
2177
- const beforeEventData = { key, ttl: seconds, cancel: false };
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 { key: finalKey, ttl: finalSeconds } = beforeEventData;
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', { key: finalKey, ttl: finalSeconds, success: result === 1 });
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', { key: finalKey, ttl: finalSeconds, error: err });
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', { key, ttl: seconds, error: err });
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 (key) {
3122
+ Redis.prototype.persist = async function(key) {
2211
3123
  try {
2212
3124
  // 触发前置事件
2213
- const beforeEventData = { key, cancel: false };
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 { key: finalKey } = beforeEventData;
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', { key: finalKey, success: result === 1 });
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', { key: finalKey, error: err });
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', { key, error: err });
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 (key) {
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 = { key, cancel: false };
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 { key: finalKey } = beforeEventData;
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(this.applyPrefix(finalKey));
2267
- this.emit('incr_after', { key: finalKey, result });
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', { key: finalKey, error: err });
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', { key, error: err });
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 (key) {
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 = { key, cancel: false };
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 { key: finalKey } = beforeEventData;
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(this.applyPrefix(finalKey));
2305
- this.emit('decr_after', { key: finalKey, result });
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', { key: finalKey, error: err });
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', { key, error: err });
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 (key, increment) {
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 = { key, increment, cancel: false };
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 { key: finalKey, increment: finalIncrement } = beforeEventData;
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(this.applyPrefix(finalKey), finalIncrement);
2344
- this.emit('incrby_after', { key: finalKey, increment: finalIncrement, result });
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', { key: finalKey, increment: finalIncrement, error: err });
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', { key, increment, error: err });
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 (key, decrement) {
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 = { key, decrement, cancel: false };
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 { key: finalKey, decrement: finalDecrement } = beforeEventData;
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(this.applyPrefix(finalKey), finalDecrement);
2383
- this.emit('decrby_after', { key: finalKey, decrement: finalDecrement, result });
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', { key: finalKey, decrement: finalDecrement, error: err });
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', { key, decrement, error: err });
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 (key, members) {
3585
+ Redis.prototype.sadd = async function(key, members) {
2408
3586
  try {
2409
3587
  // 触发前置事件
2410
- const beforeEventData = { key, members: Array.isArray(members) ? members : [members], cancel: false };
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 { key: finalKey, members: finalMembers } = beforeEventData;
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', { key: finalKey, members: finalMembers, result });
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', { key, members, error: err });
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 (key, members) {
3637
+ Redis.prototype.srem = async function(key, members) {
2445
3638
  try {
2446
3639
  // 触发前置事件
2447
- const beforeEventData = { key, members: Array.isArray(members) ? members : [members], cancel: false };
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 { key: finalKey, members: finalMembers } = beforeEventData;
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', { key: finalKey, members: finalMembers, result });
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', { key, members, error: err });
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 (key, member) {
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
- const result = await connection.sIsMember(prefixedKey, member);
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 (key) {
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', { key, result });
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', { key, error: err });
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 (key) {
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 (keys) {
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 (keys) {
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 (keys) {
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 (key, score, member) {
3856
+ Redis.prototype.zadd = async function(key, score, member) {
2617
3857
  try {
2618
3858
  // 触发前置事件
2619
- const beforeEventData = { key, score, member, cancel: false };
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 { key: finalKey, score: finalScore, member: finalMember } = beforeEventData;
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, { score: finalScore, value: finalMember });
3881
+ const result = await connection.zAdd(prefixedKey, {
3882
+ score: finalScore,
3883
+ value: finalMember
3884
+ });
2633
3885
 
2634
3886
  // 触发后置事件
2635
- this.emit('zadd_after', { key: finalKey, score: finalScore, member: finalMember, success: result === 1 });
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', { key, score, member, error: err });
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 (key, members) {
3915
+ Redis.prototype.zrem = async function(key, members) {
2654
3916
  try {
2655
3917
  // 触发前置事件
2656
- const beforeEventData = { key, members: Array.isArray(members) ? members : [members], cancel: false };
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 { key: finalKey, members: finalMembers } = beforeEventData;
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', { key: finalKey, members: finalMembers, result });
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', { key: finalKey, members: finalMembers, error: err });
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', { key, members, error: err });
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 (key, member) {
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 (key, member) {
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 (key, start, stop, withScores = false) {
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({ member, score });
4039
+ results.push({
4040
+ member,
4041
+ score
4042
+ });
2759
4043
  } catch (scoreErr) {
2760
4044
  // 如果获取分数失败,设置默认值0
2761
- results.push({ member, score: 0 });
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 (key) {
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 (key, increment, member) {
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 = { key, increment, member, cancel: false };
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 { key: finalKey, increment: finalIncrement, member: finalMember } = beforeEventData;
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(this.applyPrefix(finalKey), finalIncrement, finalMember);
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', { key: finalKey, increment: finalIncrement, member: finalMember, result: parseFloat(result) });
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', { key: finalKey, increment: finalIncrement, member: finalMember, error: err });
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', { key, increment, member, error: err });
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(scope, config);
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;