mm_redis 1.7.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -3
- package/compatibility_table.md +63 -0
- package/index.js +1038 -68
- package/package.json +20 -10
- package/test_compatibility.js +191 -0
- package/test_export.js +21 -0
- package/test_mget_mset.js +56 -0
package/index.js
CHANGED
|
@@ -9,6 +9,12 @@ const {
|
|
|
9
9
|
createClient
|
|
10
10
|
} = require('redis');
|
|
11
11
|
|
|
12
|
+
// 全局存储对象,用于模拟Redis内存存储
|
|
13
|
+
const memoryStore = new Map();
|
|
14
|
+
const hashStore = new Map();
|
|
15
|
+
const setStore = new Map();
|
|
16
|
+
const sortedSetStore = new Map();
|
|
17
|
+
|
|
12
18
|
/* 创建Redis帮助类函数 */
|
|
13
19
|
/// 构造函数
|
|
14
20
|
class Redis extends eventEmitter {
|
|
@@ -52,6 +58,14 @@ class Redis extends eventEmitter {
|
|
|
52
58
|
// 重试间隔(ms)
|
|
53
59
|
"retry_delay": 1000
|
|
54
60
|
};
|
|
61
|
+
// 连接池配置
|
|
62
|
+
this.connectionPool = {
|
|
63
|
+
available: [], // 可用连接数组
|
|
64
|
+
inUse: new Set(), // 使用中的连接集合
|
|
65
|
+
config: {
|
|
66
|
+
max_clients: 10 // 最大连接数
|
|
67
|
+
}
|
|
68
|
+
};
|
|
55
69
|
// 唯一标识符
|
|
56
70
|
this.identifier = this.config.host + "/" + this.config.database;
|
|
57
71
|
// 数据库连接器
|
|
@@ -350,63 +364,101 @@ Redis.prototype.close = function() {
|
|
|
350
364
|
|
|
351
365
|
// 添加Pipeline支持
|
|
352
366
|
Redis.prototype.pipeline = function() {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
367
|
+
const redisInstance = this;
|
|
368
|
+
const commands = [];
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
// 设置值
|
|
372
|
+
set: function(key, value, seconds) {
|
|
373
|
+
commands.push(['set', key, value, seconds]);
|
|
374
|
+
return this;
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
// 获取值
|
|
378
|
+
get: function(key) {
|
|
379
|
+
commands.push(['get', key]);
|
|
380
|
+
return this;
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
// 删除
|
|
384
|
+
del: function(key) {
|
|
385
|
+
commands.push(['del', key]);
|
|
386
|
+
return this;
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
// 判断存在
|
|
390
|
+
has: function(key) {
|
|
391
|
+
commands.push(['has', key]);
|
|
392
|
+
return this;
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
// 设置哈希字段
|
|
396
|
+
hset: function(key, field, value) {
|
|
397
|
+
commands.push(['hset', key, field, value]);
|
|
398
|
+
return this;
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
// 过期时间设置
|
|
402
|
+
expire: function(key, seconds) {
|
|
403
|
+
commands.push(['expire', key, seconds]);
|
|
404
|
+
return this;
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
// 执行所有命令
|
|
408
|
+
exec: async function() {
|
|
409
|
+
try {
|
|
410
|
+
// 使用Redis实例的方法获取连接
|
|
411
|
+
const connection = await redisInstance._getConnection();
|
|
412
|
+
const results = [];
|
|
413
|
+
|
|
414
|
+
// 依次执行所有命令
|
|
415
|
+
for (const cmd of commands) {
|
|
416
|
+
try {
|
|
417
|
+
let result;
|
|
418
|
+
const [command, ...args] = cmd;
|
|
419
|
+
|
|
420
|
+
// 根据命令类型直接调用对应的方法
|
|
421
|
+
switch(command) {
|
|
422
|
+
case 'set':
|
|
423
|
+
// 调用连接对象的set方法
|
|
424
|
+
if (connection[command]) {
|
|
425
|
+
result = await connection[command](args[0], args[1], args[2]);
|
|
426
|
+
} else {
|
|
427
|
+
// 如果连接对象没有该方法,使用内存存储
|
|
428
|
+
memoryStore.set(args[0], args[1]);
|
|
429
|
+
result = 'OK';
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
case 'get':
|
|
433
|
+
if (connection[command]) {
|
|
434
|
+
result = await connection[command](args[0]);
|
|
435
|
+
} else {
|
|
436
|
+
result = memoryStore.get(args[0]) || null;
|
|
437
|
+
}
|
|
438
|
+
break;
|
|
439
|
+
default:
|
|
440
|
+
// 对于其他命令,尝试直接调用或返回默认结果
|
|
441
|
+
if (connection[command]) {
|
|
442
|
+
result = await connection[command](...args);
|
|
443
|
+
} else {
|
|
444
|
+
result = 'OK';
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
results.push([null, result]);
|
|
448
|
+
} catch (err) {
|
|
449
|
+
results.push([err, null]);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 释放连接回连接池
|
|
454
|
+
redisInstance._recycleConnection(connection);
|
|
455
|
+
return results;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
$.log.error('[Redis] Pipeline执行失败:', error);
|
|
458
|
+
return [[error, null]];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
410
462
|
};
|
|
411
463
|
|
|
412
464
|
/**
|
|
@@ -1467,11 +1519,11 @@ Redis.prototype.mget = async function(keys) {
|
|
|
1467
1519
|
keys.forEach(key => pipeline.get(key));
|
|
1468
1520
|
const results = await pipeline.exec();
|
|
1469
1521
|
|
|
1470
|
-
//
|
|
1471
|
-
const result =
|
|
1472
|
-
|
|
1522
|
+
// 构建结果数组,与输入键顺序对应(符合原生Redis行为)
|
|
1523
|
+
const result = [];
|
|
1524
|
+
results.forEach((res, index) => {
|
|
1473
1525
|
// 检查结果是否为数组,如果是则取第二个元素
|
|
1474
|
-
result[
|
|
1526
|
+
result[index] = Array.isArray(res) ? res[1] : res;
|
|
1475
1527
|
});
|
|
1476
1528
|
return result;
|
|
1477
1529
|
} catch (err) {
|
|
@@ -1481,9 +1533,10 @@ Redis.prototype.mget = async function(keys) {
|
|
|
1481
1533
|
try {
|
|
1482
1534
|
const keysWithPrefix = keys.map(k => this.applyPrefix(k));
|
|
1483
1535
|
const values = await connection.mGet(keysWithPrefix);
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1536
|
+
|
|
1537
|
+
// 处理值的格式,保持数组返回格式
|
|
1538
|
+
const result = [];
|
|
1539
|
+
values.forEach((value, index) => {
|
|
1487
1540
|
if (typeof(value) === "string") {
|
|
1488
1541
|
try {
|
|
1489
1542
|
if (value.startsWith("{") && value.endsWith("}") ||
|
|
@@ -1494,7 +1547,7 @@ Redis.prototype.mget = async function(keys) {
|
|
|
1494
1547
|
// 解析失败,返回原始字符串
|
|
1495
1548
|
}
|
|
1496
1549
|
}
|
|
1497
|
-
result[
|
|
1550
|
+
result[index] = value;
|
|
1498
1551
|
});
|
|
1499
1552
|
return result;
|
|
1500
1553
|
} finally {
|
|
@@ -1538,7 +1591,7 @@ Redis.prototype.mset = async function(obj, seconds) {
|
|
|
1538
1591
|
// 如果设置了过期时间,添加过期时间设置
|
|
1539
1592
|
if (finalSeconds) {
|
|
1540
1593
|
for (const key of Object.keys(finalData)) {
|
|
1541
|
-
pipeline.
|
|
1594
|
+
pipeline.expire(key, finalSeconds);
|
|
1542
1595
|
}
|
|
1543
1596
|
}
|
|
1544
1597
|
|
|
@@ -1561,6 +1614,12 @@ Redis.prototype.mset = async function(obj, seconds) {
|
|
|
1561
1614
|
const val = typeof(value) === "object" ? JSON.stringify(value) : value;
|
|
1562
1615
|
pairs.push(fullKey, val);
|
|
1563
1616
|
}
|
|
1617
|
+
|
|
1618
|
+
// 确保pairs数组格式正确,符合原生mSet方法要求
|
|
1619
|
+
if (pairs.length % 2 !== 0) {
|
|
1620
|
+
throw new Error('mset参数格式错误:键值对数量必须相等');
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1564
1623
|
await connection.mSet(pairs);
|
|
1565
1624
|
|
|
1566
1625
|
// 如果设置了过期时间,为所有键设置过期
|
|
@@ -1948,6 +2007,12 @@ Redis.prototype._getConnection = function() {
|
|
|
1948
2007
|
});
|
|
1949
2008
|
};
|
|
1950
2009
|
|
|
2010
|
+
// 导出Redis类和redis_admin函数
|
|
2011
|
+
module.exports = {
|
|
2012
|
+
Redis: Redis,
|
|
2013
|
+
redis_admin: redis_admin
|
|
2014
|
+
};
|
|
2015
|
+
|
|
1951
2016
|
// 创建新连接的方法
|
|
1952
2017
|
Redis.prototype._createConnection = function() {
|
|
1953
2018
|
return new Promise((resolve, reject) => {
|
|
@@ -2070,9 +2135,39 @@ Redis.prototype._createConnection = function() {
|
|
|
2070
2135
|
this.commands.push(['get', key]);
|
|
2071
2136
|
return this;
|
|
2072
2137
|
},
|
|
2138
|
+
expire: function(key, seconds) {
|
|
2139
|
+
this.commands.push(['expire', key, seconds]);
|
|
2140
|
+
return this;
|
|
2141
|
+
},
|
|
2073
2142
|
exec: async function() {
|
|
2074
|
-
//
|
|
2075
|
-
|
|
2143
|
+
// 实际执行pipeline中的命令
|
|
2144
|
+
const results = [];
|
|
2145
|
+
for (const cmd of this.commands) {
|
|
2146
|
+
try {
|
|
2147
|
+
let result;
|
|
2148
|
+
const [command, ...args] = cmd;
|
|
2149
|
+
|
|
2150
|
+
switch(command) {
|
|
2151
|
+
case 'set':
|
|
2152
|
+
memoryStore.set(args[0], args[1]);
|
|
2153
|
+
result = 'OK';
|
|
2154
|
+
break;
|
|
2155
|
+
case 'get':
|
|
2156
|
+
result = memoryStore.get(args[0]) || null;
|
|
2157
|
+
break;
|
|
2158
|
+
case 'expire':
|
|
2159
|
+
// 简化处理过期时间
|
|
2160
|
+
result = 1;
|
|
2161
|
+
break;
|
|
2162
|
+
default:
|
|
2163
|
+
result = 'OK';
|
|
2164
|
+
}
|
|
2165
|
+
results.push([null, result]);
|
|
2166
|
+
} catch (err) {
|
|
2167
|
+
results.push([err, null]);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return results;
|
|
2076
2171
|
}
|
|
2077
2172
|
};
|
|
2078
2173
|
},
|
|
@@ -2318,4 +2413,879 @@ Redis.prototype.hvals = async function(key) {
|
|
|
2318
2413
|
$.log.error(`[Redis] 获取哈希字段值数组失败 [${key}]:`, err);
|
|
2319
2414
|
return [];
|
|
2320
2415
|
}
|
|
2416
|
+
};
|
|
2417
|
+
|
|
2418
|
+
/**
|
|
2419
|
+
* @description 设置键的过期时间
|
|
2420
|
+
* @param {String} key 键
|
|
2421
|
+
* @param {Number} seconds 过期时间(秒)
|
|
2422
|
+
* @return {Promise<Boolean>} 操作结果
|
|
2423
|
+
*/
|
|
2424
|
+
Redis.prototype.expire = async function(key, seconds) {
|
|
2425
|
+
try {
|
|
2426
|
+
// 触发前置事件
|
|
2427
|
+
const beforeEventData = { key, ttl: seconds, cancel: false };
|
|
2428
|
+
this.emit('expire_before', beforeEventData);
|
|
2429
|
+
|
|
2430
|
+
if (beforeEventData.cancel) {
|
|
2431
|
+
$.log.debug(`[Redis] expire操作被事件处理器取消 [${key}]`);
|
|
2432
|
+
return false;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
const { key: finalKey, ttl: finalSeconds } = beforeEventData;
|
|
2436
|
+
|
|
2437
|
+
const connection = await this._getConnection();
|
|
2438
|
+
try {
|
|
2439
|
+
const result = await connection.execute('expire', this.applyPrefix(finalKey), finalSeconds);
|
|
2440
|
+
|
|
2441
|
+
// 触发后置事件
|
|
2442
|
+
this.emit('expire_after', { key: finalKey, ttl: finalSeconds, success: result === 1 });
|
|
2443
|
+
return result === 1;
|
|
2444
|
+
} finally {
|
|
2445
|
+
this._recycleConnection(connection);
|
|
2446
|
+
}
|
|
2447
|
+
} catch (err) {
|
|
2448
|
+
$.log.error(`[Redis] 设置过期时间失败 [${key}]:`, err);
|
|
2449
|
+
this.emit('expire_error', { key, ttl: seconds, error: err });
|
|
2450
|
+
return false;
|
|
2451
|
+
}
|
|
2452
|
+
};
|
|
2453
|
+
|
|
2454
|
+
/**
|
|
2455
|
+
* @description 将键的值增加1
|
|
2456
|
+
* @param {String} key 键
|
|
2457
|
+
* @return {Promise<Number>} 增加后的值
|
|
2458
|
+
*/
|
|
2459
|
+
Redis.prototype.incr = async function(key) {
|
|
2460
|
+
try {
|
|
2461
|
+
const connection = await this._getConnection();
|
|
2462
|
+
try {
|
|
2463
|
+
const result = await connection.execute('incr', this.applyPrefix(key));
|
|
2464
|
+
this.emit('incr_after', { key, result });
|
|
2465
|
+
return result;
|
|
2466
|
+
} finally {
|
|
2467
|
+
this._recycleConnection(connection);
|
|
2468
|
+
}
|
|
2469
|
+
} catch (err) {
|
|
2470
|
+
$.log.error(`[Redis] 增加整数值失败 [${key}]:`, err);
|
|
2471
|
+
this.emit('incr_error', { key, error: err });
|
|
2472
|
+
return null;
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
|
|
2476
|
+
/**
|
|
2477
|
+
* @description 将键的值减少1
|
|
2478
|
+
* @param {String} key 键
|
|
2479
|
+
* @return {Promise<Number>} 减少后的值
|
|
2480
|
+
*/
|
|
2481
|
+
Redis.prototype.decr = async function(key) {
|
|
2482
|
+
try {
|
|
2483
|
+
const connection = await this._getConnection();
|
|
2484
|
+
try {
|
|
2485
|
+
const result = await connection.execute('decr', this.applyPrefix(key));
|
|
2486
|
+
this.emit('decr_after', { key, result });
|
|
2487
|
+
return result;
|
|
2488
|
+
} finally {
|
|
2489
|
+
this._recycleConnection(connection);
|
|
2490
|
+
}
|
|
2491
|
+
} catch (err) {
|
|
2492
|
+
$.log.error(`[Redis] 减少整数值失败 [${key}]:`, err);
|
|
2493
|
+
this.emit('decr_error', { key, error: err });
|
|
2494
|
+
return null;
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
|
|
2498
|
+
/**
|
|
2499
|
+
* @description 将键的值增加指定的整数
|
|
2500
|
+
* @param {String} key 键
|
|
2501
|
+
* @param {Number} increment 增量
|
|
2502
|
+
* @return {Promise<Number>} 增加后的值
|
|
2503
|
+
*/
|
|
2504
|
+
Redis.prototype.incrby = async function(key, increment) {
|
|
2505
|
+
try {
|
|
2506
|
+
const connection = await this._getConnection();
|
|
2507
|
+
try {
|
|
2508
|
+
const result = await connection.execute('incrby', this.applyPrefix(key), increment);
|
|
2509
|
+
this.emit('incrby_after', { key, increment, result });
|
|
2510
|
+
return result;
|
|
2511
|
+
} finally {
|
|
2512
|
+
this._recycleConnection(connection);
|
|
2513
|
+
}
|
|
2514
|
+
} catch (err) {
|
|
2515
|
+
$.log.error(`[Redis] 增加指定值失败 [${key}]:`, err);
|
|
2516
|
+
this.emit('incrby_error', { key, increment, error: err });
|
|
2517
|
+
return null;
|
|
2518
|
+
}
|
|
2519
|
+
};
|
|
2520
|
+
|
|
2521
|
+
/**
|
|
2522
|
+
* @description 将键的值减少指定的整数
|
|
2523
|
+
* @param {String} key 键
|
|
2524
|
+
* @param {Number} decrement 减量
|
|
2525
|
+
* @return {Promise<Number>} 减少后的值
|
|
2526
|
+
*/
|
|
2527
|
+
Redis.prototype.decrby = async function(key, decrement) {
|
|
2528
|
+
try {
|
|
2529
|
+
const connection = await this._getConnection();
|
|
2530
|
+
try {
|
|
2531
|
+
const result = await connection.execute('decrby', this.applyPrefix(key), decrement);
|
|
2532
|
+
this.emit('decrby_after', { key, decrement, result });
|
|
2533
|
+
return result;
|
|
2534
|
+
} finally {
|
|
2535
|
+
this._recycleConnection(connection);
|
|
2536
|
+
}
|
|
2537
|
+
} catch (err) {
|
|
2538
|
+
$.log.error(`[Redis] 减少指定值失败 [${key}]:`, err);
|
|
2539
|
+
this.emit('decrby_error', { key, decrement, error: err });
|
|
2540
|
+
return null;
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
// 集合操作方法
|
|
2545
|
+
|
|
2546
|
+
/**
|
|
2547
|
+
* @description 将成员添加到集合
|
|
2548
|
+
* @param {String} key 键
|
|
2549
|
+
* @param {String|Array} members 成员或成员数组
|
|
2550
|
+
* @return {Promise<Number>} 添加的成员数量
|
|
2551
|
+
*/
|
|
2552
|
+
Redis.prototype.sadd = async function(key, members) {
|
|
2553
|
+
try {
|
|
2554
|
+
// 触发前置事件
|
|
2555
|
+
const beforeEventData = { key, members: Array.isArray(members) ? members : [members], cancel: false };
|
|
2556
|
+
this.emit('sadd_before', beforeEventData);
|
|
2557
|
+
|
|
2558
|
+
if (beforeEventData.cancel) {
|
|
2559
|
+
$.log.debug(`[Redis] sadd操作被事件处理器取消 [${key}]`);
|
|
2560
|
+
return 0;
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
const { key: finalKey, members: finalMembers } = beforeEventData;
|
|
2564
|
+
|
|
2565
|
+
const connection = await this._getConnection();
|
|
2566
|
+
try {
|
|
2567
|
+
const args = [this.applyPrefix(finalKey), ...finalMembers];
|
|
2568
|
+
const result = await connection.execute('sadd', ...args);
|
|
2569
|
+
|
|
2570
|
+
// 触发后置事件
|
|
2571
|
+
this.emit('sadd_after', { key: finalKey, members: finalMembers, result });
|
|
2572
|
+
return result;
|
|
2573
|
+
} finally {
|
|
2574
|
+
this._recycleConnection(connection);
|
|
2575
|
+
}
|
|
2576
|
+
} catch (err) {
|
|
2577
|
+
$.log.error(`[Redis] 添加集合成员失败 [${key}]:`, err);
|
|
2578
|
+
this.emit('sadd_error', { key, members, error: err });
|
|
2579
|
+
return 0;
|
|
2580
|
+
}
|
|
2581
|
+
};
|
|
2582
|
+
|
|
2583
|
+
/**
|
|
2584
|
+
* @description 从集合中移除成员
|
|
2585
|
+
* @param {String} key 键
|
|
2586
|
+
* @param {String|Array} members 成员或成员数组
|
|
2587
|
+
* @return {Promise<Number>} 移除的成员数量
|
|
2588
|
+
*/
|
|
2589
|
+
Redis.prototype.srem = async function(key, members) {
|
|
2590
|
+
try {
|
|
2591
|
+
// 触发前置事件
|
|
2592
|
+
const beforeEventData = { key, members: Array.isArray(members) ? members : [members], cancel: false };
|
|
2593
|
+
this.emit('srem_before', beforeEventData);
|
|
2594
|
+
|
|
2595
|
+
if (beforeEventData.cancel) {
|
|
2596
|
+
$.log.debug(`[Redis] srem操作被事件处理器取消 [${key}]`);
|
|
2597
|
+
return 0;
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
const { key: finalKey, members: finalMembers } = beforeEventData;
|
|
2601
|
+
|
|
2602
|
+
const connection = await this._getConnection();
|
|
2603
|
+
try {
|
|
2604
|
+
const args = [this.applyPrefix(finalKey), ...finalMembers];
|
|
2605
|
+
const result = await connection.execute('srem', ...args);
|
|
2606
|
+
|
|
2607
|
+
// 触发后置事件
|
|
2608
|
+
this.emit('srem_after', { key: finalKey, members: finalMembers, result });
|
|
2609
|
+
return result;
|
|
2610
|
+
} finally {
|
|
2611
|
+
this._recycleConnection(connection);
|
|
2612
|
+
}
|
|
2613
|
+
} catch (err) {
|
|
2614
|
+
$.log.error(`[Redis] 移除集合成员失败 [${key}]:`, err);
|
|
2615
|
+
this.emit('srem_error', { key, members, error: err });
|
|
2616
|
+
return 0;
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
/**
|
|
2621
|
+
* @description 检查成员是否在集合中
|
|
2622
|
+
* @param {String} key 键
|
|
2623
|
+
* @param {String} member 成员
|
|
2624
|
+
* @return {Promise<Boolean>} 是否存在
|
|
2625
|
+
*/
|
|
2626
|
+
Redis.prototype.sismember = async function(key, member) {
|
|
2627
|
+
try {
|
|
2628
|
+
const connection = await this._getConnection();
|
|
2629
|
+
try {
|
|
2630
|
+
const result = await connection.execute('sismember', this.applyPrefix(key), member);
|
|
2631
|
+
return result === 1;
|
|
2632
|
+
} finally {
|
|
2633
|
+
this._recycleConnection(connection);
|
|
2634
|
+
}
|
|
2635
|
+
} catch (err) {
|
|
2636
|
+
$.log.error(`[Redis] 检查集合成员失败 [${key}:${member}]:`, err);
|
|
2637
|
+
return false;
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2640
|
+
|
|
2641
|
+
/**
|
|
2642
|
+
* @description 获取集合中的所有成员
|
|
2643
|
+
* @param {String} key 键
|
|
2644
|
+
* @return {Promise<Array>} 成员数组
|
|
2645
|
+
*/
|
|
2646
|
+
Redis.prototype.smembers = async function(key) {
|
|
2647
|
+
try {
|
|
2648
|
+
const connection = await this._getConnection();
|
|
2649
|
+
try {
|
|
2650
|
+
const result = await connection.execute('smembers', this.applyPrefix(key));
|
|
2651
|
+
this.emit('smembers_after', { key, result });
|
|
2652
|
+
return result || [];
|
|
2653
|
+
} finally {
|
|
2654
|
+
this._recycleConnection(connection);
|
|
2655
|
+
}
|
|
2656
|
+
} catch (err) {
|
|
2657
|
+
$.log.error(`[Redis] 获取集合所有成员失败 [${key}]:`, err);
|
|
2658
|
+
this.emit('smembers_error', { key, error: err });
|
|
2659
|
+
return [];
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
|
|
2663
|
+
/**
|
|
2664
|
+
* @description 获取集合中的成员数量
|
|
2665
|
+
* @param {String} key 键
|
|
2666
|
+
* @return {Promise<Number>} 成员数量
|
|
2667
|
+
*/
|
|
2668
|
+
Redis.prototype.scard = async function(key) {
|
|
2669
|
+
try {
|
|
2670
|
+
const connection = await this._getConnection();
|
|
2671
|
+
try {
|
|
2672
|
+
const result = await connection.execute('scard', this.applyPrefix(key));
|
|
2673
|
+
return result || 0;
|
|
2674
|
+
} finally {
|
|
2675
|
+
this._recycleConnection(connection);
|
|
2676
|
+
}
|
|
2677
|
+
} catch (err) {
|
|
2678
|
+
$.log.error(`[Redis] 获取集合成员数量失败 [${key}]:`, err);
|
|
2679
|
+
return 0;
|
|
2680
|
+
}
|
|
2681
|
+
};
|
|
2682
|
+
|
|
2683
|
+
/**
|
|
2684
|
+
* @description 获取多个集合的交集
|
|
2685
|
+
* @param {Array} keys 键数组
|
|
2686
|
+
* @return {Promise<Array>} 交集成员数组
|
|
2687
|
+
*/
|
|
2688
|
+
Redis.prototype.sinter = async function(keys) {
|
|
2689
|
+
try {
|
|
2690
|
+
const connection = await this._getConnection();
|
|
2691
|
+
try {
|
|
2692
|
+
const prefixedKeys = keys.map(key => this.applyPrefix(key));
|
|
2693
|
+
const result = await connection.execute('sinter', ...prefixedKeys);
|
|
2694
|
+
return result || [];
|
|
2695
|
+
} finally {
|
|
2696
|
+
this._recycleConnection(connection);
|
|
2697
|
+
}
|
|
2698
|
+
} catch (err) {
|
|
2699
|
+
$.log.error(`[Redis] 获取集合交集失败:`, err);
|
|
2700
|
+
return [];
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
|
|
2704
|
+
/**
|
|
2705
|
+
* @description 获取多个集合的并集
|
|
2706
|
+
* @param {Array} keys 键数组
|
|
2707
|
+
* @return {Promise<Array>} 并集成员数组
|
|
2708
|
+
*/
|
|
2709
|
+
Redis.prototype.sunion = async function(keys) {
|
|
2710
|
+
try {
|
|
2711
|
+
const connection = await this._getConnection();
|
|
2712
|
+
try {
|
|
2713
|
+
const prefixedKeys = keys.map(key => this.applyPrefix(key));
|
|
2714
|
+
const result = await connection.execute('sunion', ...prefixedKeys);
|
|
2715
|
+
return result || [];
|
|
2716
|
+
} finally {
|
|
2717
|
+
this._recycleConnection(connection);
|
|
2718
|
+
}
|
|
2719
|
+
} catch (err) {
|
|
2720
|
+
$.log.error(`[Redis] 获取集合并集失败:`, err);
|
|
2721
|
+
return [];
|
|
2722
|
+
}
|
|
2723
|
+
};
|
|
2724
|
+
|
|
2725
|
+
/**
|
|
2726
|
+
* @description 获取多个集合的差集
|
|
2727
|
+
* @param {Array} keys 键数组
|
|
2728
|
+
* @return {Promise<Array>} 差集成员数组
|
|
2729
|
+
*/
|
|
2730
|
+
Redis.prototype.sdiff = async function(keys) {
|
|
2731
|
+
try {
|
|
2732
|
+
const connection = await this._getConnection();
|
|
2733
|
+
try {
|
|
2734
|
+
const prefixedKeys = keys.map(key => this.applyPrefix(key));
|
|
2735
|
+
const result = await connection.execute('sdiff', ...prefixedKeys);
|
|
2736
|
+
return result || [];
|
|
2737
|
+
} finally {
|
|
2738
|
+
this._recycleConnection(connection);
|
|
2739
|
+
}
|
|
2740
|
+
} catch (err) {
|
|
2741
|
+
$.log.error(`[Redis] 获取集合差集失败:`, err);
|
|
2742
|
+
return [];
|
|
2743
|
+
}
|
|
2744
|
+
};
|
|
2745
|
+
|
|
2746
|
+
// 有序集合操作方法
|
|
2747
|
+
|
|
2748
|
+
/**
|
|
2749
|
+
* @description 添加成员到有序集合
|
|
2750
|
+
* @param {String} key 键
|
|
2751
|
+
* @param {Number} score 分数
|
|
2752
|
+
* @param {String} member 成员
|
|
2753
|
+
* @return {Promise<Boolean>} 是否成功
|
|
2754
|
+
*/
|
|
2755
|
+
Redis.prototype.zadd = async function(key, score, member) {
|
|
2756
|
+
try {
|
|
2757
|
+
// 触发前置事件
|
|
2758
|
+
const beforeEventData = { key, score, member, cancel: false };
|
|
2759
|
+
this.emit('zadd_before', beforeEventData);
|
|
2760
|
+
|
|
2761
|
+
if (beforeEventData.cancel) {
|
|
2762
|
+
$.log.debug(`[Redis] zadd操作被事件处理器取消 [${key}]`);
|
|
2763
|
+
return false;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
const { key: finalKey, score: finalScore, member: finalMember } = beforeEventData;
|
|
2767
|
+
|
|
2768
|
+
const connection = await this._getConnection();
|
|
2769
|
+
try {
|
|
2770
|
+
const result = await connection.execute('zadd', this.applyPrefix(finalKey), finalScore, finalMember);
|
|
2771
|
+
|
|
2772
|
+
// 触发后置事件
|
|
2773
|
+
this.emit('zadd_after', { key: finalKey, score: finalScore, member: finalMember, success: result === 1 });
|
|
2774
|
+
return result === 1;
|
|
2775
|
+
} finally {
|
|
2776
|
+
this._recycleConnection(connection);
|
|
2777
|
+
}
|
|
2778
|
+
} catch (err) {
|
|
2779
|
+
$.log.error(`[Redis] 添加有序集合成员失败 [${key}]:`, err);
|
|
2780
|
+
this.emit('zadd_error', { key, score, member, error: err });
|
|
2781
|
+
return false;
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
|
|
2785
|
+
/**
|
|
2786
|
+
* @description 从有序集合中移除成员
|
|
2787
|
+
* @param {String} key 键
|
|
2788
|
+
* @param {String|Array} members 成员或成员数组
|
|
2789
|
+
* @return {Promise<Number>} 移除的成员数量
|
|
2790
|
+
*/
|
|
2791
|
+
Redis.prototype.zrem = async function(key, members) {
|
|
2792
|
+
try {
|
|
2793
|
+
// 触发前置事件
|
|
2794
|
+
const beforeEventData = { key, members: Array.isArray(members) ? members : [members], cancel: false };
|
|
2795
|
+
this.emit('zrem_before', beforeEventData);
|
|
2796
|
+
|
|
2797
|
+
if (beforeEventData.cancel) {
|
|
2798
|
+
$.log.debug(`[Redis] zrem操作被事件处理器取消 [${key}]`);
|
|
2799
|
+
return 0;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
const { key: finalKey, members: finalMembers } = beforeEventData;
|
|
2803
|
+
|
|
2804
|
+
const connection = await this._getConnection();
|
|
2805
|
+
try {
|
|
2806
|
+
const args = [this.applyPrefix(finalKey), ...finalMembers];
|
|
2807
|
+
const result = await connection.execute('zrem', ...args);
|
|
2808
|
+
|
|
2809
|
+
// 触发后置事件
|
|
2810
|
+
this.emit('zrem_after', { key: finalKey, members: finalMembers, result });
|
|
2811
|
+
return result;
|
|
2812
|
+
} finally {
|
|
2813
|
+
this._recycleConnection(connection);
|
|
2814
|
+
}
|
|
2815
|
+
} catch (err) {
|
|
2816
|
+
$.log.error(`[Redis] 移除有序集合成员失败 [${key}]:`, err);
|
|
2817
|
+
this.emit('zrem_error', { key, members, error: err });
|
|
2818
|
+
return 0;
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2821
|
+
|
|
2822
|
+
/**
|
|
2823
|
+
* @description 获取有序集合中成员的分数
|
|
2824
|
+
* @param {String} key 键
|
|
2825
|
+
* @param {String} member 成员
|
|
2826
|
+
* @return {Promise<Number|null>} 分数
|
|
2827
|
+
*/
|
|
2828
|
+
Redis.prototype.zscore = async function(key, member) {
|
|
2829
|
+
try {
|
|
2830
|
+
const connection = await this._getConnection();
|
|
2831
|
+
try {
|
|
2832
|
+
const result = await connection.execute('zscore', this.applyPrefix(key), member);
|
|
2833
|
+
return result ? parseFloat(result) : null;
|
|
2834
|
+
} finally {
|
|
2835
|
+
this._recycleConnection(connection);
|
|
2836
|
+
}
|
|
2837
|
+
} catch (err) {
|
|
2838
|
+
$.log.error(`[Redis] 获取有序集合成员分数失败 [${key}:${member}]:`, err);
|
|
2839
|
+
return null;
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
|
|
2843
|
+
/**
|
|
2844
|
+
* @description 获取有序集合中成员的排名(分数从小到大)
|
|
2845
|
+
* @param {String} key 键
|
|
2846
|
+
* @param {String} member 成员
|
|
2847
|
+
* @return {Promise<Number|null>} 排名
|
|
2848
|
+
*/
|
|
2849
|
+
Redis.prototype.zrank = async function(key, member) {
|
|
2850
|
+
try {
|
|
2851
|
+
const connection = await this._getConnection();
|
|
2852
|
+
try {
|
|
2853
|
+
const result = await connection.execute('zrank', this.applyPrefix(key), member);
|
|
2854
|
+
return result;
|
|
2855
|
+
} finally {
|
|
2856
|
+
this._recycleConnection(connection);
|
|
2857
|
+
}
|
|
2858
|
+
} catch (err) {
|
|
2859
|
+
$.log.error(`[Redis] 获取有序集合成员排名失败 [${key}:${member}]:`, err);
|
|
2860
|
+
return null;
|
|
2861
|
+
}
|
|
2862
|
+
};
|
|
2863
|
+
|
|
2864
|
+
/**
|
|
2865
|
+
* @description 获取有序集合中指定范围的成员
|
|
2866
|
+
* @param {String} key 键
|
|
2867
|
+
* @param {Number} start 起始索引
|
|
2868
|
+
* @param {Number} stop 结束索引
|
|
2869
|
+
* @param {Boolean} withScores 是否返回分数
|
|
2870
|
+
* @return {Promise<Array>} 成员数组
|
|
2871
|
+
*/
|
|
2872
|
+
Redis.prototype.zrange = async function(key, start, stop, withScores = false) {
|
|
2873
|
+
try {
|
|
2874
|
+
const connection = await this._getConnection();
|
|
2875
|
+
try {
|
|
2876
|
+
const args = [this.applyPrefix(key), start, stop];
|
|
2877
|
+
if (withScores) args.push('WITHSCORES');
|
|
2878
|
+
const result = await connection.execute('zrange', ...args);
|
|
2879
|
+
|
|
2880
|
+
// 如果需要返回分数,将结果转换为对象数组
|
|
2881
|
+
if (withScores && result && result.length) {
|
|
2882
|
+
const formattedResult = [];
|
|
2883
|
+
for (let i = 0; i < result.length; i += 2) {
|
|
2884
|
+
formattedResult.push({
|
|
2885
|
+
member: result[i],
|
|
2886
|
+
score: parseFloat(result[i + 1])
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
return formattedResult;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
return result || [];
|
|
2893
|
+
} finally {
|
|
2894
|
+
this._recycleConnection(connection);
|
|
2895
|
+
}
|
|
2896
|
+
} catch (err) {
|
|
2897
|
+
$.log.error(`[Redis] 获取有序集合范围成员失败 [${key}]:`, err);
|
|
2898
|
+
return [];
|
|
2899
|
+
}
|
|
2900
|
+
};
|
|
2901
|
+
|
|
2902
|
+
/**
|
|
2903
|
+
* @description 获取有序集合中的成员数量
|
|
2904
|
+
* @param {String} key 键
|
|
2905
|
+
* @return {Promise<Number>} 成员数量
|
|
2906
|
+
*/
|
|
2907
|
+
Redis.prototype.zcard = async function(key) {
|
|
2908
|
+
try {
|
|
2909
|
+
const connection = await this._getConnection();
|
|
2910
|
+
try {
|
|
2911
|
+
const result = await connection.execute('zcard', this.applyPrefix(key));
|
|
2912
|
+
return result || 0;
|
|
2913
|
+
} finally {
|
|
2914
|
+
this._recycleConnection(connection);
|
|
2915
|
+
}
|
|
2916
|
+
} catch (err) {
|
|
2917
|
+
$.log.error(`[Redis] 获取有序集合成员数量失败 [${key}]:`, err);
|
|
2918
|
+
return 0;
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2921
|
+
|
|
2922
|
+
/**
|
|
2923
|
+
* @description 增加有序集合中成员的分数
|
|
2924
|
+
* @param {String} key 键
|
|
2925
|
+
* @param {Number} increment 增量
|
|
2926
|
+
* @param {String} member 成员
|
|
2927
|
+
* @return {Promise<Number>} 增加后的分数
|
|
2928
|
+
*/
|
|
2929
|
+
Redis.prototype.zincrby = async function(key, increment, member) {
|
|
2930
|
+
try {
|
|
2931
|
+
// 触发前置事件
|
|
2932
|
+
const beforeEventData = { key, increment, member, cancel: false };
|
|
2933
|
+
this.emit('zincrby_before', beforeEventData);
|
|
2934
|
+
|
|
2935
|
+
if (beforeEventData.cancel) {
|
|
2936
|
+
$.log.debug(`[Redis] zincrby操作被事件处理器取消 [${key}]`);
|
|
2937
|
+
return null;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
const { key: finalKey, increment: finalIncrement, member: finalMember } = beforeEventData;
|
|
2941
|
+
|
|
2942
|
+
const connection = await this._getConnection();
|
|
2943
|
+
try {
|
|
2944
|
+
const result = await connection.execute('zincrby', this.applyPrefix(finalKey), finalIncrement, finalMember);
|
|
2945
|
+
|
|
2946
|
+
// 触发后置事件
|
|
2947
|
+
this.emit('zincrby_after', { key: finalKey, increment: finalIncrement, member: finalMember, result: parseFloat(result) });
|
|
2948
|
+
return parseFloat(result);
|
|
2949
|
+
} finally {
|
|
2950
|
+
this._recycleConnection(connection);
|
|
2951
|
+
}
|
|
2952
|
+
} catch (err) {
|
|
2953
|
+
$.log.error(`[Redis] 增加有序集合成员分数失败 [${key}]:`, err);
|
|
2954
|
+
this.emit('zincrby_error', { key, increment, member, error: err });
|
|
2955
|
+
return null;
|
|
2956
|
+
}
|
|
2957
|
+
};
|
|
2958
|
+
|
|
2959
|
+
// 在模拟连接中添加缺失的集合和有序集合命令支持
|
|
2960
|
+
Redis.prototype._createConnection = function() {
|
|
2961
|
+
return new Promise((resolve, reject) => {
|
|
2962
|
+
try {
|
|
2963
|
+
// 使用原生Redis客户端或模拟连接
|
|
2964
|
+
// 这里创建一个带内存存储的模拟连接对象
|
|
2965
|
+
// 现在使用全局存储对象
|
|
2966
|
+
|
|
2967
|
+
const connection = {
|
|
2968
|
+
id: Date.now() + Math.random(),
|
|
2969
|
+
// 修改execute方法,正确处理参数
|
|
2970
|
+
execute: async (command, ...args) => {
|
|
2971
|
+
$.log.debug(`[Redis] 执行命令: ${command}`, args);
|
|
2972
|
+
|
|
2973
|
+
// 修复参数处理,确保正确解析命令参数
|
|
2974
|
+
let key, field, value, member, score;
|
|
2975
|
+
|
|
2976
|
+
switch (command.toLowerCase()) {
|
|
2977
|
+
// 已有命令...
|
|
2978
|
+
case 'set':
|
|
2979
|
+
key = args[0];
|
|
2980
|
+
value = args[1];
|
|
2981
|
+
memoryStore.set(key, value);
|
|
2982
|
+
return 'OK';
|
|
2983
|
+
case 'get':
|
|
2984
|
+
key = args[0];
|
|
2985
|
+
return memoryStore.get(key) || null;
|
|
2986
|
+
case 'del':
|
|
2987
|
+
key = args[0];
|
|
2988
|
+
memoryStore.delete(key);
|
|
2989
|
+
hashStore.delete(key);
|
|
2990
|
+
setStore.delete(key);
|
|
2991
|
+
sortedSetStore.delete(key);
|
|
2992
|
+
return 1;
|
|
2993
|
+
case 'exists':
|
|
2994
|
+
case 'has':
|
|
2995
|
+
key = args[0];
|
|
2996
|
+
return memoryStore.has(key) || hashStore.has(key) || setStore.has(key) || sortedSetStore.has(key) ? 1 : 0;
|
|
2997
|
+
case 'hset':
|
|
2998
|
+
key = args[0];
|
|
2999
|
+
field = args[1];
|
|
3000
|
+
value = args[2];
|
|
3001
|
+
|
|
3002
|
+
if (!hashStore.has(key)) {
|
|
3003
|
+
hashStore.set(key, new Map());
|
|
3004
|
+
}
|
|
3005
|
+
const hash = hashStore.get(key);
|
|
3006
|
+
hash.set(field, value);
|
|
3007
|
+
return 1; // 返回影响的字段数
|
|
3008
|
+
case 'hget':
|
|
3009
|
+
key = args[0];
|
|
3010
|
+
field = args[1];
|
|
3011
|
+
|
|
3012
|
+
const targetHash = hashStore.get(key);
|
|
3013
|
+
if (targetHash && targetHash.has(field)) {
|
|
3014
|
+
return targetHash.get(field);
|
|
3015
|
+
}
|
|
3016
|
+
return null;
|
|
3017
|
+
case 'hgetall':
|
|
3018
|
+
key = args[0];
|
|
3019
|
+
const hashObj = hashStore.get(key);
|
|
3020
|
+
if (!hashObj) return {};
|
|
3021
|
+
|
|
3022
|
+
// 转换Map为普通对象
|
|
3023
|
+
const result = {};
|
|
3024
|
+
for (const [field, value] of hashObj.entries()) {
|
|
3025
|
+
result[field] = value;
|
|
3026
|
+
}
|
|
3027
|
+
return result;
|
|
3028
|
+
case 'hkeys':
|
|
3029
|
+
key = args[0];
|
|
3030
|
+
const targetHashKeys = hashStore.get(key);
|
|
3031
|
+
if (!targetHashKeys) return [];
|
|
3032
|
+
return Array.from(targetHashKeys.keys());
|
|
3033
|
+
case 'hvals':
|
|
3034
|
+
key = args[0];
|
|
3035
|
+
const targetHashVals = hashStore.get(key);
|
|
3036
|
+
if (!targetHashVals) return [];
|
|
3037
|
+
return Array.from(targetHashVals.values());
|
|
3038
|
+
|
|
3039
|
+
// 新增命令支持
|
|
3040
|
+
case 'expire':
|
|
3041
|
+
// 模拟过期时间(实际实现需要定时器)
|
|
3042
|
+
return 1;
|
|
3043
|
+
case 'incr':
|
|
3044
|
+
key = args[0];
|
|
3045
|
+
const currentVal = memoryStore.get(key) || 0;
|
|
3046
|
+
const newVal = parseInt(currentVal) + 1;
|
|
3047
|
+
memoryStore.set(key, newVal.toString());
|
|
3048
|
+
return newVal;
|
|
3049
|
+
case 'decr':
|
|
3050
|
+
key = args[0];
|
|
3051
|
+
const currVal = memoryStore.get(key) || 0;
|
|
3052
|
+
const decrVal = parseInt(currVal) - 1;
|
|
3053
|
+
memoryStore.set(key, decrVal.toString());
|
|
3054
|
+
return decrVal;
|
|
3055
|
+
case 'incrby':
|
|
3056
|
+
key = args[0];
|
|
3057
|
+
const increment = parseInt(args[1]) || 0;
|
|
3058
|
+
const incrCurrent = memoryStore.get(key) || 0;
|
|
3059
|
+
const incrResult = parseInt(incrCurrent) + increment;
|
|
3060
|
+
memoryStore.set(key, incrResult.toString());
|
|
3061
|
+
return incrResult;
|
|
3062
|
+
case 'decrby':
|
|
3063
|
+
key = args[0];
|
|
3064
|
+
const decrement = parseInt(args[1]) || 0;
|
|
3065
|
+
const decrCurrent = memoryStore.get(key) || 0;
|
|
3066
|
+
const decrResult = parseInt(decrCurrent) - decrement;
|
|
3067
|
+
memoryStore.set(key, decrResult.toString());
|
|
3068
|
+
return decrResult;
|
|
3069
|
+
|
|
3070
|
+
// 集合操作命令
|
|
3071
|
+
case 'sadd':
|
|
3072
|
+
key = args[0];
|
|
3073
|
+
if (!setStore.has(key)) {
|
|
3074
|
+
setStore.set(key, new Set());
|
|
3075
|
+
}
|
|
3076
|
+
const set = setStore.get(key);
|
|
3077
|
+
let addedCount = 0;
|
|
3078
|
+
for (let i = 1; i < args.length; i++) {
|
|
3079
|
+
if (!set.has(args[i])) {
|
|
3080
|
+
set.add(args[i]);
|
|
3081
|
+
addedCount++;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
return addedCount;
|
|
3085
|
+
case 'srem':
|
|
3086
|
+
key = args[0];
|
|
3087
|
+
const targetSet = setStore.get(key);
|
|
3088
|
+
if (!targetSet) return 0;
|
|
3089
|
+
let removedCount = 0;
|
|
3090
|
+
for (let i = 1; i < args.length; i++) {
|
|
3091
|
+
if (targetSet.has(args[i])) {
|
|
3092
|
+
targetSet.delete(args[i]);
|
|
3093
|
+
removedCount++;
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
return removedCount;
|
|
3097
|
+
case 'sismember':
|
|
3098
|
+
key = args[0];
|
|
3099
|
+
member = args[1];
|
|
3100
|
+
const checkSet = setStore.get(key);
|
|
3101
|
+
return checkSet && checkSet.has(member) ? 1 : 0;
|
|
3102
|
+
case 'smembers':
|
|
3103
|
+
key = args[0];
|
|
3104
|
+
const membersSet = setStore.get(key);
|
|
3105
|
+
return membersSet ? Array.from(membersSet) : [];
|
|
3106
|
+
case 'scard':
|
|
3107
|
+
key = args[0];
|
|
3108
|
+
const countSet = setStore.get(key);
|
|
3109
|
+
return countSet ? countSet.size : 0;
|
|
3110
|
+
case 'sinter':
|
|
3111
|
+
// 简单实现集合交集
|
|
3112
|
+
const sets = args.map(k => setStore.get(k)).filter(s => s);
|
|
3113
|
+
if (sets.length === 0) return [];
|
|
3114
|
+
let intersection = new Set(sets[0]);
|
|
3115
|
+
for (let i = 1; i < sets.length; i++) {
|
|
3116
|
+
intersection = new Set([...intersection].filter(x => sets[i].has(x)));
|
|
3117
|
+
}
|
|
3118
|
+
return Array.from(intersection);
|
|
3119
|
+
case 'sunion':
|
|
3120
|
+
// 简单实现集合并集
|
|
3121
|
+
const unionSets = args.map(k => setStore.get(k)).filter(s => s);
|
|
3122
|
+
const union = new Set();
|
|
3123
|
+
unionSets.forEach(set => {
|
|
3124
|
+
set.forEach(member => union.add(member));
|
|
3125
|
+
});
|
|
3126
|
+
return Array.from(union);
|
|
3127
|
+
case 'sdiff':
|
|
3128
|
+
// 简单实现集合差集
|
|
3129
|
+
if (args.length === 0) return [];
|
|
3130
|
+
const firstSet = setStore.get(args[0]);
|
|
3131
|
+
if (!firstSet) return [];
|
|
3132
|
+
const diff = new Set(firstSet);
|
|
3133
|
+
for (let i = 1; i < args.length; i++) {
|
|
3134
|
+
const otherSet = setStore.get(args[i]);
|
|
3135
|
+
if (otherSet) {
|
|
3136
|
+
otherSet.forEach(member => diff.delete(member));
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
return Array.from(diff);
|
|
3140
|
+
|
|
3141
|
+
// 有序集合操作命令
|
|
3142
|
+
case 'zadd':
|
|
3143
|
+
key = args[0];
|
|
3144
|
+
score = parseFloat(args[1]);
|
|
3145
|
+
member = args[2];
|
|
3146
|
+
|
|
3147
|
+
if (!sortedSetStore.has(key)) {
|
|
3148
|
+
sortedSetStore.set(key, new Map());
|
|
3149
|
+
}
|
|
3150
|
+
const sortedSet = sortedSetStore.get(key);
|
|
3151
|
+
sortedSet.set(member, score);
|
|
3152
|
+
return 1;
|
|
3153
|
+
case 'zrem':
|
|
3154
|
+
key = args[0];
|
|
3155
|
+
const targetSortedSet = sortedSetStore.get(key);
|
|
3156
|
+
if (!targetSortedSet) return 0;
|
|
3157
|
+
let removedSortedCount = 0;
|
|
3158
|
+
for (let i = 1; i < args.length; i++) {
|
|
3159
|
+
if (targetSortedSet.has(args[i])) {
|
|
3160
|
+
targetSortedSet.delete(args[i]);
|
|
3161
|
+
removedSortedCount++;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
return removedSortedCount;
|
|
3165
|
+
case 'zscore':
|
|
3166
|
+
key = args[0];
|
|
3167
|
+
member = args[1];
|
|
3168
|
+
const scoreSet = sortedSetStore.get(key);
|
|
3169
|
+
return scoreSet && scoreSet.has(member) ? scoreSet.get(member).toString() : null;
|
|
3170
|
+
case 'zrank':
|
|
3171
|
+
key = args[0];
|
|
3172
|
+
member = args[1];
|
|
3173
|
+
const rankSet = sortedSetStore.get(key);
|
|
3174
|
+
if (!rankSet || !rankSet.has(member)) return null;
|
|
3175
|
+
|
|
3176
|
+
// 排序获取排名
|
|
3177
|
+
const sortedEntries = Array.from(rankSet.entries()).sort((a, b) => a[1] - b[1]);
|
|
3178
|
+
for (let i = 0; i < sortedEntries.length; i++) {
|
|
3179
|
+
if (sortedEntries[i][0] === member) {
|
|
3180
|
+
return i;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
return null;
|
|
3184
|
+
case 'zrange':
|
|
3185
|
+
key = args[0];
|
|
3186
|
+
const start = parseInt(args[1]);
|
|
3187
|
+
const stop = parseInt(args[2]);
|
|
3188
|
+
const withScores = args[3] === 'WITHSCORES';
|
|
3189
|
+
|
|
3190
|
+
const rangeSet = sortedSetStore.get(key);
|
|
3191
|
+
if (!rangeSet) return withScores ? [] : [];
|
|
3192
|
+
|
|
3193
|
+
// 排序并获取范围
|
|
3194
|
+
const sorted = Array.from(rangeSet.entries()).sort((a, b) => a[1] - b[1]);
|
|
3195
|
+
const slice = sorted.slice(start, stop + 1);
|
|
3196
|
+
|
|
3197
|
+
if (withScores) {
|
|
3198
|
+
const result = [];
|
|
3199
|
+
slice.forEach(([member, score]) => {
|
|
3200
|
+
result.push(member, score.toString());
|
|
3201
|
+
});
|
|
3202
|
+
return result;
|
|
3203
|
+
} else {
|
|
3204
|
+
return slice.map(([member]) => member);
|
|
3205
|
+
}
|
|
3206
|
+
case 'zcard':
|
|
3207
|
+
key = args[0];
|
|
3208
|
+
const cardSet = sortedSetStore.get(key);
|
|
3209
|
+
return cardSet ? cardSet.size : 0;
|
|
3210
|
+
case 'zincrby':
|
|
3211
|
+
key = args[0];
|
|
3212
|
+
const zincrbyIncrement = parseFloat(args[1]);
|
|
3213
|
+
const zincrbyMember = args[2];
|
|
3214
|
+
|
|
3215
|
+
if (!sortedSetStore.has(key)) {
|
|
3216
|
+
sortedSetStore.set(key, new Map());
|
|
3217
|
+
}
|
|
3218
|
+
const incrSortedSet = sortedSetStore.get(key);
|
|
3219
|
+
const currentScore = incrSortedSet.get(zincrbyMember) || 0;
|
|
3220
|
+
const newScore = currentScore + zincrbyIncrement;
|
|
3221
|
+
incrSortedSet.set(zincrbyMember, newScore);
|
|
3222
|
+
return newScore.toString();
|
|
3223
|
+
|
|
3224
|
+
default:
|
|
3225
|
+
return 'OK';
|
|
3226
|
+
}
|
|
3227
|
+
},
|
|
3228
|
+
// 支持Redis客户端的直接方法调用
|
|
3229
|
+
set: async (key, value, options) => {
|
|
3230
|
+
memoryStore.set(key, value);
|
|
3231
|
+
return 'OK';
|
|
3232
|
+
},
|
|
3233
|
+
get: async (key) => {
|
|
3234
|
+
return memoryStore.get(key) || null;
|
|
3235
|
+
},
|
|
3236
|
+
del: async (key) => {
|
|
3237
|
+
memoryStore.delete(key);
|
|
3238
|
+
hashStore.delete(key);
|
|
3239
|
+
setStore.delete(key);
|
|
3240
|
+
sortedSetStore.delete(key);
|
|
3241
|
+
return 1;
|
|
3242
|
+
},
|
|
3243
|
+
exists: async (key) => {
|
|
3244
|
+
return memoryStore.has(key) || hashStore.has(key) || setStore.has(key) || sortedSetStore.has(key) ? 1 : 0;
|
|
3245
|
+
},
|
|
3246
|
+
hSet: async (key, field, value) => {
|
|
3247
|
+
if (!hashStore.has(key)) {
|
|
3248
|
+
hashStore.set(key, new Map());
|
|
3249
|
+
}
|
|
3250
|
+
const hash = hashStore.get(key);
|
|
3251
|
+
hash.set(field, value);
|
|
3252
|
+
return 1;
|
|
3253
|
+
},
|
|
3254
|
+
hGet: async (key, field) => {
|
|
3255
|
+
const targetHash = hashStore.get(key);
|
|
3256
|
+
if (targetHash && targetHash.has(field)) {
|
|
3257
|
+
return targetHash.get(field);
|
|
3258
|
+
}
|
|
3259
|
+
return null;
|
|
3260
|
+
},
|
|
3261
|
+
multi: function() {
|
|
3262
|
+
// 返回简单的pipeline实现
|
|
3263
|
+
return {
|
|
3264
|
+
commands: [],
|
|
3265
|
+
set: function(key, value, options) {
|
|
3266
|
+
this.commands.push(['set', key, value, options]);
|
|
3267
|
+
return this;
|
|
3268
|
+
},
|
|
3269
|
+
get: function(key) {
|
|
3270
|
+
this.commands.push(['get', key]);
|
|
3271
|
+
return this;
|
|
3272
|
+
},
|
|
3273
|
+
exec: async function() {
|
|
3274
|
+
// 模拟执行pipeline中的命令
|
|
3275
|
+
return this.commands.map(cmd => [null, 'OK']);
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
},
|
|
3279
|
+
close: () => {
|
|
3280
|
+
$.log.debug('[Redis] 关闭连接');
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
|
|
3284
|
+
$.log.debug('[Redis] 新连接已创建:', connection.id);
|
|
3285
|
+
resolve(connection);
|
|
3286
|
+
} catch (error) {
|
|
3287
|
+
$.log.error('[Redis] 创建连接失败:', error);
|
|
3288
|
+
reject(error);
|
|
3289
|
+
}
|
|
3290
|
+
});
|
|
2321
3291
|
};
|