@taicode/common-server 3.7.11 → 3.7.13

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.
Files changed (54) hide show
  1. package/output/index.d.ts +1 -0
  2. package/output/index.d.ts.map +1 -1
  3. package/output/index.js +1 -0
  4. package/output/rate-limiter/index.d.ts +1 -0
  5. package/output/rate-limiter/index.d.ts.map +1 -1
  6. package/output/rate-limiter/index.js +1 -0
  7. package/output/rate-limiter/rate-limiter.d.ts.map +1 -1
  8. package/output/rate-limiter/redis-store.d.ts +45 -0
  9. package/output/rate-limiter/redis-store.d.ts.map +1 -0
  10. package/output/rate-limiter/redis-store.js +114 -0
  11. package/output/rate-limiter/redis-store.test.d.ts +2 -0
  12. package/output/rate-limiter/redis-store.test.d.ts.map +1 -0
  13. package/output/rate-limiter/redis-store.test.js +70 -0
  14. package/output/redis-cache/index.d.ts +4 -0
  15. package/output/redis-cache/index.d.ts.map +1 -0
  16. package/output/redis-cache/index.js +1 -0
  17. package/output/redis-cache/redis-cache.d.ts +139 -0
  18. package/output/redis-cache/redis-cache.d.ts.map +1 -0
  19. package/output/redis-cache/redis-cache.js +282 -0
  20. package/output/redis-cache/redis-cache.test.d.ts +2 -0
  21. package/output/redis-cache/redis-cache.test.d.ts.map +1 -0
  22. package/output/redis-cache/redis-cache.test.js +156 -0
  23. package/output/redis-queue/redis-queue-batch-consumer.d.ts +2 -2
  24. package/output/redis-queue/redis-queue-batch-consumer.d.ts.map +1 -1
  25. package/output/redis-queue/redis-queue-batch-consumer.js +5 -6
  26. package/output/redis-queue/redis-queue-batch-consumer.test.d.ts +1 -1
  27. package/output/redis-queue/redis-queue-batch-consumer.test.d.ts.map +1 -1
  28. package/output/redis-queue/redis-queue-batch-consumer.test.js +1 -3
  29. package/output/redis-queue/redis-queue-common.d.ts.map +1 -1
  30. package/output/redis-queue/redis-queue-common.test.d.ts +17 -13
  31. package/output/redis-queue/redis-queue-common.test.d.ts.map +1 -1
  32. package/output/redis-queue/redis-queue-consumer.d.ts +2 -2
  33. package/output/redis-queue/redis-queue-consumer.d.ts.map +1 -1
  34. package/output/redis-queue/redis-queue-consumer.js +2 -3
  35. package/output/redis-queue/redis-queue-consumer.test.d.ts +1 -1
  36. package/output/redis-queue/redis-queue-consumer.test.d.ts.map +1 -1
  37. package/output/redis-queue/redis-queue-consumer.test.js +1 -1
  38. package/output/redis-queue/redis-queue-provider.d.ts +2 -2
  39. package/output/redis-queue/redis-queue-provider.d.ts.map +1 -1
  40. package/output/redis-queue/redis-queue-provider.js +1 -1
  41. package/output/redis-queue/redis-queue-provider.test.d.ts +2 -2
  42. package/output/redis-queue/redis-queue-provider.test.d.ts.map +1 -1
  43. package/output/redis-queue/redis-queue-provider.test.js +42 -42
  44. package/output/redis-queue/test-helpers.d.ts +11 -14
  45. package/output/redis-queue/test-helpers.d.ts.map +1 -1
  46. package/output/redis-queue/test-helpers.js +9 -23
  47. package/output/redis-queue/test-helpers.test.d.ts +1 -1
  48. package/output/redis-queue/test-helpers.test.d.ts.map +1 -1
  49. package/output/redis-queue/test-helpers.test.js +7 -13
  50. package/output/redis-queue/types.d.ts +7 -0
  51. package/output/redis-queue/types.d.ts.map +1 -1
  52. package/output/schema/index.d.ts +1 -1
  53. package/output/schema/index.d.ts.map +1 -1
  54. package/package.json +11 -3
@@ -0,0 +1,282 @@
1
+ import { LruCache, logger } from '@taicode/common-base';
2
+ import { createClient } from 'redis';
3
+ /** 单次日志的最小间隔(毫秒),避免 Redis 故障时刷爆日志 */
4
+ const ERROR_LOG_INTERVAL_MS = 30_000;
5
+ /** 删除键时的批大小 */
6
+ const SCAN_COUNT = 200;
7
+ /**
8
+ * Redis 缓存(可选带进程内一级缓存)。
9
+ *
10
+ * 设计原则:
11
+ * 1. **缓存故障绝不影响业务**:所有方法都不会抛出异常,Redis 不可用时
12
+ * 自动退化为「不带缓存的直读」或「仅一级缓存」,调用方无需 try/catch。
13
+ * 2. **一级缓存是可选加速层**:`l1` 关闭时退化为纯 Redis 缓存;
14
+ * 不传 `redisUrl`/`redisClient` 时退化为纯一级缓存(单测里就这么用)。
15
+ * 3. **空值也缓存**:防止不存在的 key 每次穿透到数据库。
16
+ * 4. **TTL 带抖动**:避免同一时刻写入的大量键同时过期,形成瞬时数据库尖峰。
17
+ */
18
+ export class RedisCache {
19
+ prefix;
20
+ redis = null;
21
+ isExternalRedis = false;
22
+ defaultTtlSeconds;
23
+ nullTtlSeconds;
24
+ l1;
25
+ l1TtlMs;
26
+ /** 请求合并:同一个 key 的并发 miss 只触发一次 loader,防止缓存击穿 */
27
+ inFlight = new Map();
28
+ lastErrorLogTime = 0;
29
+ constructor(options) {
30
+ if (!options.prefix) {
31
+ throw new Error('[RedisCache] prefix is required');
32
+ }
33
+ if (options.redisUrl && options.redisClient) {
34
+ throw new Error('[RedisCache] Cannot specify both redisUrl and redisClient');
35
+ }
36
+ this.prefix = options.prefix;
37
+ this.defaultTtlSeconds = options.defaultTtlSeconds ?? 300;
38
+ this.nullTtlSeconds = options.nullTtlSeconds ?? 10;
39
+ if (options.l1 !== false) {
40
+ const l1Options = options.l1 ?? {};
41
+ this.l1TtlMs = l1Options.ttlMs ?? 5_000;
42
+ this.l1 = new LruCache(l1Options.maxSize ?? 500);
43
+ }
44
+ else {
45
+ this.l1 = null;
46
+ this.l1TtlMs = 0;
47
+ }
48
+ if (options.redisClient) {
49
+ this.redis = options.redisClient;
50
+ this.isExternalRedis = true;
51
+ }
52
+ else if (options.redisUrl) {
53
+ this.redis = createRedisClient({ url: options.redisUrl, logPrefix: this.getLogPrefix() });
54
+ }
55
+ }
56
+ /** 一级缓存条目数(未开启时为 0) */
57
+ get size() {
58
+ return this.l1?.size ?? 0;
59
+ }
60
+ /** 一级缓存容量(未开启时为 0) */
61
+ get capacity() {
62
+ return this.l1?.capacity ?? 0;
63
+ }
64
+ /** 二级缓存是否可用。不可用时所有操作都会安静地退化为直读,不会抛错。 */
65
+ get remoteReady() {
66
+ return this.redis === null || this.redis.isReady;
67
+ }
68
+ /**
69
+ * 建立连接。
70
+ * 外部传入的客户端由持有者负责连接,这里直接返回。
71
+ */
72
+ async connect() {
73
+ if (this.redis === null || this.isExternalRedis)
74
+ return;
75
+ if (this.redis.isOpen)
76
+ return;
77
+ await this.redis.connect();
78
+ }
79
+ /** 断开连接(外部分享的客户端不会被断开) */
80
+ disconnect() {
81
+ if (this.redis === null || this.isExternalRedis)
82
+ return;
83
+ if (!this.redis.isOpen)
84
+ return;
85
+ this.redis.disconnect();
86
+ }
87
+ /**
88
+ * 释放实例资源。
89
+ *
90
+ * 只清理进程内的东西,**不会**动 Redis:二级缓存是多实例共享的,
91
+ * 某个进程退出不应该把别人的缓存一起清掉。
92
+ */
93
+ async dispose() {
94
+ this.l1?.clear();
95
+ this.inFlight.clear();
96
+ this.disconnect();
97
+ }
98
+ /**
99
+ * 清空一级缓存(同步),并尽力异步清空二级缓存里该前缀下的全部键。
100
+ * 二级缓存的清理不阻塞调用方,失败也不影响结果。
101
+ */
102
+ clear() {
103
+ this.l1?.clear();
104
+ if (this.redis?.isReady) {
105
+ void this.deleteByPrefix('');
106
+ }
107
+ }
108
+ async get(key) {
109
+ return await this.getByCacheKey(this.buildKey(key));
110
+ }
111
+ async set(key, value, ttlSeconds) {
112
+ await this.setByCacheKey(this.buildKey(key), value, ttlSeconds);
113
+ }
114
+ async delete(key) {
115
+ const cacheKey = this.buildKey(key);
116
+ this.l1?.delete(cacheKey);
117
+ if (!this.redis?.isReady)
118
+ return;
119
+ try {
120
+ await this.redis.del(cacheKey);
121
+ }
122
+ catch (error) {
123
+ this.logError('delete', cacheKey, error);
124
+ }
125
+ }
126
+ /**
127
+ * 按前缀批量删除。传空字符串表示清空当前缓存实例的全部键。
128
+ *
129
+ * 用于「改了一批数据,相关的缓存全部作废」的场景:
130
+ * 与其维护精细的依赖关系,不如按命名空间整体作废——
131
+ * 这类写操作本身频率很低,一次 SCAN 的代价可以接受。
132
+ */
133
+ async deleteByPrefix(prefix) {
134
+ this.l1?.clear();
135
+ if (!this.redis?.isReady)
136
+ return 0;
137
+ const match = `${this.prefix}${prefix}*`;
138
+ let deleted = 0;
139
+ let cursor = '0';
140
+ try {
141
+ do {
142
+ const result = await this.redis.scan(cursor, { MATCH: match, COUNT: SCAN_COUNT });
143
+ cursor = result.cursor;
144
+ if (result.keys.length > 0) {
145
+ deleted += await this.redis.del(result.keys);
146
+ }
147
+ } while (cursor !== '0');
148
+ }
149
+ catch (error) {
150
+ this.logError('deleteByPrefix', match, error);
151
+ }
152
+ return deleted;
153
+ }
154
+ /**
155
+ * 读缓存,未命中时执行 loader 并回填。
156
+ *
157
+ * 并发调用同一个 key 时只会执行一次 loader(请求合并),
158
+ * 这是鉴权链路上最容易被打穿的场景:热点用户缓存刚失效,
159
+ * 同一瞬间几十个请求同时涌入。
160
+ */
161
+ async getOrSet(key, loader, ttlSeconds) {
162
+ const cacheKey = this.buildKey(key);
163
+ const local = this.getLocal(cacheKey);
164
+ if (local !== undefined)
165
+ return local;
166
+ const remote = await this.getByCacheKey(cacheKey);
167
+ if (remote !== null)
168
+ return remote;
169
+ const pending = this.inFlight.get(cacheKey);
170
+ if (pending !== undefined)
171
+ return await pending;
172
+ const task = (async () => {
173
+ const value = await loader();
174
+ await this.setByCacheKey(cacheKey, value, ttlSeconds);
175
+ return value;
176
+ })();
177
+ this.inFlight.set(cacheKey, task);
178
+ try {
179
+ return await task;
180
+ }
181
+ finally {
182
+ if (this.inFlight.get(cacheKey) === task) {
183
+ this.inFlight.delete(cacheKey);
184
+ }
185
+ }
186
+ }
187
+ /**
188
+ * 生成最终键。
189
+ * 注意:调用方传入的 key 里已经带了业务前缀(如 `user:`),
190
+ * 因此这里只负责拼接实例前缀。
191
+ */
192
+ buildKey(key) {
193
+ return `${this.prefix}${key}`;
194
+ }
195
+ /** 以下两个方法接收「已经拼好前缀的最终键」,供 getOrSet 避免重复拼前缀 */
196
+ async getByCacheKey(cacheKey) {
197
+ const local = this.getLocal(cacheKey);
198
+ if (local !== undefined)
199
+ return local;
200
+ if (!this.redis?.isReady)
201
+ return null;
202
+ try {
203
+ const raw = await this.redis.get(cacheKey);
204
+ if (raw === null)
205
+ return null;
206
+ const value = JSON.parse(raw);
207
+ this.setLocal(cacheKey, value);
208
+ return value;
209
+ }
210
+ catch (error) {
211
+ this.logError('get', cacheKey, error);
212
+ return null;
213
+ }
214
+ }
215
+ async setByCacheKey(cacheKey, value, ttlSeconds) {
216
+ this.setLocal(cacheKey, value);
217
+ if (!this.redis?.isReady)
218
+ return;
219
+ try {
220
+ await this.redis.set(cacheKey, JSON.stringify(value), { EX: this.resolveTtl(value, ttlSeconds) });
221
+ }
222
+ catch (error) {
223
+ this.logError('set', cacheKey, error);
224
+ }
225
+ }
226
+ getLocal(cacheKey) {
227
+ if (this.l1 === null)
228
+ return undefined;
229
+ const entry = this.l1.get(cacheKey);
230
+ if (entry === undefined)
231
+ return undefined;
232
+ if (entry.expiredTime < Date.now()) {
233
+ this.l1.delete(cacheKey);
234
+ return undefined;
235
+ }
236
+ return entry.value;
237
+ }
238
+ setLocal(cacheKey, value) {
239
+ if (this.l1 === null)
240
+ return;
241
+ // 空值不写入一级缓存:一级缓存无法被其他实例失效,
242
+ // 让「不存在」在本地停留可能会显著拖慢新建资源的可见性。
243
+ if (value === null || value === undefined)
244
+ return;
245
+ this.l1.set(cacheKey, { value, expiredTime: Date.now() + this.l1TtlMs });
246
+ }
247
+ resolveTtl(value, ttlSeconds) {
248
+ if (value === null || value === undefined) {
249
+ return this.nullTtlSeconds;
250
+ }
251
+ const base = ttlSeconds ?? this.defaultTtlSeconds;
252
+ // ±10% 抖动:避免大批量键在同毫秒写入后在同一个时刻集体过期
253
+ const jitter = base * (0.9 + Math.random() * 0.2);
254
+ return Math.max(1, Math.round(jitter));
255
+ }
256
+ getLogPrefix() {
257
+ return `RedisCache:${this.prefix}`;
258
+ }
259
+ logError(operation, key, error) {
260
+ const now = Date.now();
261
+ if (now - this.lastErrorLogTime < ERROR_LOG_INTERVAL_MS)
262
+ return;
263
+ this.lastErrorLogTime = now;
264
+ logger.error(`[${this.getLogPrefix()}] ${operation} failed, cache degraded to pass-through:`, key, error);
265
+ }
266
+ }
267
+ export function createRedisClient(options) {
268
+ const logPrefix = options.logPrefix ?? 'Redis';
269
+ const client = createClient({
270
+ url: options.url,
271
+ socket: {
272
+ connectTimeout: options.connectTimeoutMs ?? 5_000,
273
+ reconnectStrategy: (retries) => Math.min(retries * 200, 3_000),
274
+ },
275
+ });
276
+ client.on('error', (error) => {
277
+ logger.error(`[${logPrefix}] Redis Client Error:`, error);
278
+ });
279
+ // 带 socket 配置调用 createClient 时 TS 会把 RESP 推断成联合类型,
280
+ // 与默认的 RESP2 客户端类型不等价,这里收敛回公开的 RedisClientType。
281
+ return client;
282
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=redis-cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache.test.d.ts","sourceRoot":"","sources":["../../source/redis-cache/redis-cache.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { RedisCache, createRedisClient } from './redis-cache.js';
3
+ const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
4
+ describe('RedisCache', () => {
5
+ describe('纯一级缓存(未配置 Redis)', () => {
6
+ let cache;
7
+ beforeEach(() => {
8
+ cache = new RedisCache({ prefix: 'test-l1-only:', l1: { maxSize: 2, ttlMs: 1000 } });
9
+ });
10
+ afterEach(async () => {
11
+ await cache.dispose();
12
+ });
13
+ it('应该在未配置 Redis 时退化为纯进程内缓存', async () => {
14
+ await cache.set('a', { value: 1 });
15
+ expect(await cache.get('a')).toEqual({ value: 1 });
16
+ expect(cache.size).toBe(1);
17
+ expect(cache.capacity).toBe(2);
18
+ });
19
+ it('应该按 LRU 容量淘汰', async () => {
20
+ await cache.set('a', 1);
21
+ await cache.set('b', 2);
22
+ await cache.get('a'); // 让 a 变成最近使用
23
+ await cache.set('c', 3);
24
+ expect(await cache.get('b')).toBeNull();
25
+ expect(await cache.get('a')).toBe(1);
26
+ expect(await cache.get('c')).toBe(3);
27
+ });
28
+ it('一级缓存过期后应该重新读取', async () => {
29
+ await cache.set('a', 1);
30
+ expect(await cache.get('a')).toBe(1);
31
+ vi.spyOn(Date, 'now').mockReturnValue(Date.now() + 60_000);
32
+ expect(await cache.get('a')).toBeNull();
33
+ vi.restoreAllMocks();
34
+ });
35
+ it('clear 应该清空一级缓存', async () => {
36
+ await cache.set('a', 1);
37
+ cache.clear();
38
+ expect(cache.size).toBe(0);
39
+ expect(await cache.get('a')).toBeNull();
40
+ });
41
+ it('getOrSet 应该只在未命中时执行 loader', async () => {
42
+ const loader = vi.fn(async () => ({ value: 42 }));
43
+ expect(await cache.getOrSet('key', loader)).toEqual({ value: 42 });
44
+ expect(await cache.getOrSet('key', loader)).toEqual({ value: 42 });
45
+ expect(loader).toHaveBeenCalledTimes(1);
46
+ });
47
+ it('getOrSet 应该与 get/set/delete 共用同一份缓存', async () => {
48
+ await cache.set('shared', { value: 1 });
49
+ const loader = vi.fn(async () => ({ value: 2 }));
50
+ expect(await cache.getOrSet('shared', loader)).toEqual({ value: 1 });
51
+ expect(loader).not.toHaveBeenCalled();
52
+ await cache.delete('shared');
53
+ expect(await cache.getOrSet('shared', loader)).toEqual({ value: 2 });
54
+ expect(loader).toHaveBeenCalledTimes(1);
55
+ });
56
+ it('并发 getOrSet 应该只执行一次 loader', async () => {
57
+ let release;
58
+ const gate = new Promise(resolve => { release = resolve; });
59
+ const loader = vi.fn(async () => {
60
+ await gate;
61
+ return 7;
62
+ });
63
+ const tasks = [cache.getOrSet('key', loader), cache.getOrSet('key', loader), cache.getOrSet('key', loader)];
64
+ release();
65
+ expect(await Promise.all(tasks)).toEqual([7, 7, 7]);
66
+ expect(loader).toHaveBeenCalledTimes(1);
67
+ });
68
+ });
69
+ describe('二级缓存(Redis)', () => {
70
+ let redis;
71
+ let cache;
72
+ let other;
73
+ beforeEach(async () => {
74
+ redis = createRedisClient({ url: REDIS_URL });
75
+ await redis.connect();
76
+ cache = new RedisCache({ prefix: 'test:redis-cache:', redisClient: redis, defaultTtlSeconds: 60 });
77
+ // 另一个实例模拟「另一个进程」,一级缓存不共享,二级缓存共享
78
+ other = new RedisCache({ prefix: 'test:redis-cache:', redisClient: redis, defaultTtlSeconds: 60 });
79
+ await cache.deleteByPrefix('');
80
+ });
81
+ afterEach(async () => {
82
+ await cache.deleteByPrefix('');
83
+ await cache.dispose();
84
+ await other.dispose();
85
+ if (redis.isOpen)
86
+ redis.disconnect();
87
+ });
88
+ it('应该通过 Redis 让不同实例共享缓存', async () => {
89
+ await cache.set('shared', { hello: 'world' });
90
+ expect(await other.get('shared')).toEqual({ hello: 'world' });
91
+ });
92
+ it('Redis 中的键名应该只拼接一次前缀', async () => {
93
+ await cache.getOrSet('user:1', async () => 'ok');
94
+ expect(await redis.keys('test:redis-cache:*')).toEqual(['test:redis-cache:user:1']);
95
+ expect(await other.get('user:1')).toBe('ok');
96
+ });
97
+ it('应该缓存空值以防止穿透', async () => {
98
+ await cache.set('missing', null);
99
+ expect(await other.get('missing')).toBeNull();
100
+ expect(await redis.get('test:redis-cache:missing')).toBe('null');
101
+ });
102
+ it('空值不应该写入一级缓存', async () => {
103
+ await cache.set('missing', null);
104
+ expect(cache.size).toBe(0);
105
+ });
106
+ it('deleteByPrefix 应该只删除本前缀下的键', async () => {
107
+ const otherPrefix = new RedisCache({ prefix: 'test:other-prefix:', redisClient: redis });
108
+ await otherPrefix.deleteByPrefix('');
109
+ await cache.set('user:1', 1);
110
+ await cache.set('user:2', 2);
111
+ await cache.set('role:1', 3);
112
+ await otherPrefix.set('user:1', 99);
113
+ const deleted = await cache.deleteByPrefix('user:');
114
+ expect(deleted).toBe(2);
115
+ expect(await cache.get('user:1')).toBeNull();
116
+ expect(await cache.get('role:1')).toBe(3);
117
+ expect(await otherPrefix.get('user:1')).toBe(99);
118
+ await otherPrefix.deleteByPrefix('');
119
+ await otherPrefix.dispose();
120
+ });
121
+ it('deleteByPrefix 应该同时清空一级缓存', async () => {
122
+ await cache.set('user:1', 1);
123
+ expect(cache.size).toBe(1);
124
+ await cache.deleteByPrefix('user:');
125
+ expect(cache.size).toBe(0);
126
+ expect(await cache.get('user:1')).toBeNull();
127
+ });
128
+ it('clear 应该同时清理一二级缓存', async () => {
129
+ await cache.set('user:1', 1);
130
+ cache.clear();
131
+ expect(cache.size).toBe(0);
132
+ // clear 内部的二级缓存清理是异步的,等待其落地
133
+ await vi.waitFor(async () => {
134
+ expect(await redis.get('test:redis-cache:user:1')).toBeNull();
135
+ });
136
+ });
137
+ it('delete 应该同时删除一二级缓存', async () => {
138
+ await cache.set('user:1', 1);
139
+ await other.get('user:1'); // 让另一个实例也有一级缓存
140
+ await cache.delete('user:1');
141
+ expect(await cache.get('user:1')).toBeNull();
142
+ expect(await redis.get('test:redis-cache:user:1')).toBeNull();
143
+ });
144
+ it('Redis 不可用时应安静降级而不抛错', async () => {
145
+ redis.disconnect();
146
+ await expect(cache.get('anything')).resolves.toBeNull();
147
+ await expect(cache.set('anything', 1)).resolves.toBeUndefined();
148
+ await expect(cache.delete('anything')).resolves.toBeUndefined();
149
+ await expect(cache.deleteByPrefix('')).resolves.toBe(0);
150
+ });
151
+ it('Redis 不可用时 getOrSet 仍应返回 loader 结果', async () => {
152
+ redis.disconnect();
153
+ await expect(cache.getOrSet('k', async () => 'value')).resolves.toBe('value');
154
+ });
155
+ });
156
+ });
@@ -1,5 +1,5 @@
1
1
  import { RedisQueueCommon } from './redis-queue-common.js';
2
- import type { BatchConsumerConfig, QueueStats, RedisQueueRegistry } from './types.js';
2
+ import type { AnyRegistry, BatchConsumerConfig, QueueStats, RedisQueueRegistry } from './types.js';
3
3
  /**
4
4
  * Redis 批量队列消费者
5
5
  *
@@ -47,7 +47,7 @@ import type { BatchConsumerConfig, QueueStats, RedisQueueRegistry } from './type
47
47
  * consumer.disconnect()
48
48
  * ```
49
49
  */
50
- export declare class RedisQueueBatchConsumer<R extends Record<string, any> = RedisQueueRegistry, K extends keyof R & string = keyof R & string> extends RedisQueueCommon {
50
+ export declare class RedisQueueBatchConsumer<R extends AnyRegistry = RedisQueueRegistry, K extends keyof R & string = keyof R & string> extends RedisQueueCommon {
51
51
  private consumerRunning;
52
52
  private consumerInterval;
53
53
  private recoveryInterval;
@@ -1 +1 @@
1
- {"version":3,"file":"redis-queue-batch-consumer.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAA0B,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE7G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,uBAAuB,CAClC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,kBAAkB,EAClD,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAC7C,SAAQ,gBAAgB;IACxB,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,kBAAkB,CAA2B;IAErD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAOtB;gBAEW,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAuB1D,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBjC;;OAEG;YACW,YAAY;IA2G1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAyErB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAevC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAMjC"}
1
+ {"version":3,"file":"redis-queue-batch-consumer.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAA0B,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE1H;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,uBAAuB,CAClC,CAAC,SAAS,WAAW,GAAG,kBAAkB,EAC1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAC7C,SAAQ,gBAAgB;IACxB,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,kBAAkB,CAA2B;IAErD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAOtB;gBAEW,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAuB1D,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBjC;;OAEG;YACW,YAAY;IA2G1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAyErB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAevC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAMjC"}
@@ -1,5 +1,4 @@
1
- import { catchIt } from '@taicode/common-base';
2
- import { logger } from '@taicode/common-base';
1
+ import { catchIt, logger } from '@taicode/common-base';
3
2
  import { RedisQueueCommon } from './redis-queue-common.js';
4
3
  /**
5
4
  * Redis 批量队列消费者
@@ -99,7 +98,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
99
98
  if (this.processingPromises.size > 0) {
100
99
  logger.info(`[RedisQueueBatchConsumer] Waiting for ${this.processingPromises.size} batches to complete...`);
101
100
  await Promise.allSettled(this.processingPromises);
102
- logger.info(`[RedisQueueBatchConsumer] All batches completed`);
101
+ logger.info('[RedisQueueBatchConsumer] All batches completed');
103
102
  }
104
103
  // 最后断开 Redis 连接
105
104
  super.disconnect();
@@ -116,7 +115,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
116
115
  const tasks = await Promise.all(taskIds.map(id => this.getTask(id)));
117
116
  const validTasks = tasks.filter((task) => task !== null);
118
117
  if (validTasks.length === 0) {
119
- logger.warn(`[RedisQueueBatchConsumer] No valid tasks found in batch`);
118
+ logger.warn('[RedisQueueBatchConsumer] No valid tasks found in batch');
120
119
  return;
121
120
  }
122
121
  // 过滤出有效的待处理任务
@@ -151,7 +150,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
151
150
  logger.info(`[RedisQueueBatchConsumer] Batch completed: ${updatedCount} tasks`);
152
151
  }
153
152
  catch (error) {
154
- logger.error(`[RedisQueueBatchConsumer] Batch failed:`, error);
153
+ logger.error('[RedisQueueBatchConsumer] Batch failed:', error);
155
154
  // 批量重试或失败
156
155
  for (const task of tasksToProcess) {
157
156
  if (task.retryCount < task.maxRetries) {
@@ -294,7 +293,7 @@ export class RedisQueueBatchConsumer extends RedisQueueCommon {
294
293
  if (taskIds.length > 0) {
295
294
  // 处理这批任务(不等待完成)
296
295
  this.processBatch(taskIds).catch(error => {
297
- logger.error(`[RedisQueueBatchConsumer] Unhandled error in processBatch:`, error);
296
+ logger.error('[RedisQueueBatchConsumer] Unhandled error in processBatch:', error);
298
297
  });
299
298
  }
300
299
  }
@@ -15,7 +15,7 @@ type TestTaskData = {
15
15
  value: number;
16
16
  };
17
17
  example?: string;
18
- data?: any;
18
+ data?: unknown;
19
19
  };
20
20
  declare module './types.js' {
21
21
  interface RedisQueueRegistry {
@@ -1 +1 @@
1
- {"version":3,"file":"redis-queue-batch-consumer.test.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.test.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,GAAG,CAAA;CACX,CAAA;AAGD,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,kBAAkB;QAC1B,CAAC,GAAG,EAAE,cAAc,MAAM,EAAE,GAAG,YAAY,CAAA;KAC5C;CACF"}
1
+ {"version":3,"file":"redis-queue-batch-consumer.test.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-batch-consumer.test.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAGD,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,kBAAkB;QAC1B,CAAC,GAAG,EAAE,cAAc,MAAM,EAAE,GAAG,YAAY,CAAA;KAC5C;CACF"}
@@ -145,9 +145,7 @@ describe('RedisQueueBatchConsumer', () => {
145
145
  });
146
146
  describe('重试机制', () => {
147
147
  it('失败的批次应该重试', async () => {
148
- let attemptCount = 0;
149
148
  const { provider, consumer } = createQueue(async () => {
150
- attemptCount++;
151
149
  throw new Error('Batch failed');
152
150
  }, { batchSize: 2, maxRetries: 1 });
153
151
  await provider.connect();
@@ -292,7 +290,7 @@ describe('RedisQueueBatchConsumer', () => {
292
290
  describe('超时恢复', () => {
293
291
  it('超时的任务应该直接标记为失败', { timeout: 15000 }, async () => {
294
292
  let processCount = 0;
295
- const { provider, consumer } = createQueue(async (dataList) => {
293
+ const { provider, consumer } = createQueue(async () => {
296
294
  processCount++;
297
295
  // 模拟处理卡住,永远不完成 (但超时机制会检测到)
298
296
  await new Promise(resolve => setTimeout(resolve, 20000));
@@ -1 +1 @@
1
- {"version":3,"file":"redis-queue-common.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAY,IAAI,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAEhF;;;GAGG;AACH,8BAAsB,gBAAgB;IACpC,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAO;IAC9C,SAAS,CAAC,eAAe,EAAE,OAAO,CAAQ;IAE1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACnC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACpC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAGvC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IACtC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IACvC,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAC1C,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;gBAE7B,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,sBAA2B;IAwCjE;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,MAAM;IAEzC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B;;OAEG;IACH,UAAU,IAAI,IAAI;IAalB;;OAEG;cACa,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAW7D;;OAEG;IACH,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAUlD;;OAEG;cACa,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqChG;;;OAGG;cACa,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqD1G;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAevG;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAUhC;;;;;;;OAOG;cACa,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CA4E/G"}
1
+ {"version":3,"file":"redis-queue-common.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAEtE;;;GAGG;AACH,8BAAsB,gBAAgB;IACpC,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAO;IAC9C,SAAS,CAAC,eAAe,EAAE,OAAO,CAAQ;IAE1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACnC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACpC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAGvC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IACtC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IACvC,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAC1C,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;gBAE7B,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,sBAA2B;IAwCjE;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,MAAM;IAEzC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B;;OAEG;IACH,UAAU,IAAI,IAAI;IAalB;;OAEG;cACa,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAW7D;;OAEG;IACH,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAUlD;;OAEG;cACa,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqChG;;;OAGG;cACa,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqD1G;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAevG;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAUhC;;;;;;;OAOG;cACa,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CA4E/G"}
@@ -1,18 +1,22 @@
1
+ type TestQueueData = {
2
+ id?: string;
3
+ [key: string]: unknown;
4
+ };
1
5
  declare module './types.js' {
2
6
  interface RedisQueueRegistry {
3
- 'test-queue': Record<string, any>;
4
- 'test-queue-error': Record<string, any>;
5
- 'short': Record<string, any>;
6
- 'valid-key': Record<string, any>;
7
- 'test-default': Record<string, any>;
8
- 'test-custom': Record<string, any>;
9
- '': Record<string, any>;
10
- 'my-queue': Record<string, any>;
11
- 'test-error': Record<string, any>;
12
- 'test-bad': Record<string, any>;
13
- 'queue-one': Record<string, any>;
14
- 'queue-two': Record<string, any>;
15
- [key: `test:reconnect:${number}`]: Record<string, any>;
7
+ 'test-queue': TestQueueData;
8
+ 'test-queue-error': TestQueueData;
9
+ 'short': TestQueueData;
10
+ 'valid-key': TestQueueData;
11
+ 'test-default': TestQueueData;
12
+ 'test-custom': TestQueueData;
13
+ '': TestQueueData;
14
+ 'my-queue': TestQueueData;
15
+ 'test-error': TestQueueData;
16
+ 'test-bad': TestQueueData;
17
+ 'queue-one': TestQueueData;
18
+ 'queue-two': TestQueueData;
19
+ [key: `test:reconnect:${number}`]: TestQueueData;
16
20
  }
17
21
  }
18
22
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"redis-queue-common.test.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-common.test.ts"],"names":[],"mappings":"AAOA,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,kBAAkB;QAC1B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACjC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAChC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACnC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAClC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACvB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAChC,CAAC,GAAG,EAAE,kBAAkB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KACvD;CACF"}
1
+ {"version":3,"file":"redis-queue-common.test.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-common.test.ts"],"names":[],"mappings":"AAOA,KAAK,aAAa,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAG5D,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,kBAAkB;QAC1B,YAAY,EAAE,aAAa,CAAA;QAC3B,kBAAkB,EAAE,aAAa,CAAA;QACjC,OAAO,EAAE,aAAa,CAAA;QACtB,WAAW,EAAE,aAAa,CAAA;QAC1B,cAAc,EAAE,aAAa,CAAA;QAC7B,aAAa,EAAE,aAAa,CAAA;QAC5B,EAAE,EAAE,aAAa,CAAA;QACjB,UAAU,EAAE,aAAa,CAAA;QACzB,YAAY,EAAE,aAAa,CAAA;QAC3B,UAAU,EAAE,aAAa,CAAA;QACzB,WAAW,EAAE,aAAa,CAAA;QAC1B,WAAW,EAAE,aAAa,CAAA;QAC1B,CAAC,GAAG,EAAE,kBAAkB,MAAM,EAAE,GAAG,aAAa,CAAA;KACjD;CACF"}
@@ -1,5 +1,5 @@
1
1
  import { RedisQueueCommon } from './redis-queue-common.js';
2
- import type { RedisQueueConsumerConfig, RedisQueueRegistry, QueueStats } from './types.js';
2
+ import type { AnyRegistry, RedisQueueConsumerConfig, RedisQueueRegistry, QueueStats } from './types.js';
3
3
  /**
4
4
  * Redis 队列消费者
5
5
  *
@@ -45,7 +45,7 @@ import type { RedisQueueConsumerConfig, RedisQueueRegistry, QueueStats } from '.
45
45
  * consumer.disconnect()
46
46
  * ```
47
47
  */
48
- export declare class RedisQueueConsumer<R extends Record<string, any> = RedisQueueRegistry, K extends keyof R & string = keyof R & string> extends RedisQueueCommon {
48
+ export declare class RedisQueueConsumer<R extends AnyRegistry = RedisQueueRegistry, K extends keyof R & string = keyof R & string> extends RedisQueueCommon {
49
49
  private consumerRunning;
50
50
  private consumerInterval;
51
51
  private recoveryInterval;
@@ -1 +1 @@
1
- {"version":3,"file":"redis-queue-consumer.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-consumer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAe,kBAAkB,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAA;AAErH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAAa,kBAAkB,CAC7B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,kBAAkB,EAClD,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAC7C,SAAQ,gBAAgB;IACxB,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,kBAAkB,CAA2B;IAErD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAOtB;gBAEW,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAuB/D,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBjC;;OAEG;YACW,WAAW;IA2FzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4ErB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAevC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAMjC"}
1
+ {"version":3,"file":"redis-queue-consumer.d.ts","sourceRoot":"","sources":["../../source/redis-queue/redis-queue-consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,wBAAwB,EAAe,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEpH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAAa,kBAAkB,CAC7B,CAAC,SAAS,WAAW,GAAG,kBAAkB,EAC1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAC7C,SAAQ,gBAAgB;IACxB,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,kBAAkB,CAA2B;IAErD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAOtB;gBAEW,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAuB/D,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBjC;;OAEG;YACW,WAAW;IA2FzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4ErB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAevC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAMjC"}
@@ -1,5 +1,4 @@
1
- import { catchIt } from '@taicode/common-base';
2
- import { logger } from '@taicode/common-base';
1
+ import { catchIt, logger } from '@taicode/common-base';
3
2
  import { RedisQueueCommon } from './redis-queue-common.js';
4
3
  /**
5
4
  * Redis 队列消费者
@@ -97,7 +96,7 @@ export class RedisQueueConsumer extends RedisQueueCommon {
97
96
  if (this.processingPromises.size > 0) {
98
97
  logger.info(`[RedisQueueConsumer] Waiting for ${this.processingPromises.size} tasks to complete...`);
99
98
  await Promise.allSettled(this.processingPromises);
100
- logger.info(`[RedisQueueConsumer] All tasks completed`);
99
+ logger.info('[RedisQueueConsumer] All tasks completed');
101
100
  }
102
101
  // 最后断开 Redis 连接
103
102
  super.disconnect();
@@ -15,7 +15,7 @@ type TestTaskData = {
15
15
  value: number;
16
16
  };
17
17
  example?: string;
18
- data?: any;
18
+ data?: unknown;
19
19
  };
20
20
  declare module './types.js' {
21
21
  interface RedisQueueRegistry {