@taicode/common-server 1.0.10 → 1.0.11

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.
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { createClient } from 'redis';
3
3
  import { catchIt } from '@taicode/common-base';
4
4
  /**
5
- * 通用任务队列类
5
+ * 通用任务队列类(泛型)
6
6
  *
7
7
  * 提供基于 Redis 的任务队列功能,支持:
8
8
  * - 任务入队和持久化
@@ -10,24 +10,30 @@ import { catchIt } from '@taicode/common-base';
10
10
  * - 任务状态追踪
11
11
  * - 分布式消费
12
12
  *
13
+ * @template T 任务数据类型
14
+ *
13
15
  * @example
14
16
  * ```ts
15
- * const queue = new RedisQueue({
16
- * redisUrl: 'redis://localhost:6379',
17
- * queueKey: 'my-queue',
18
- * concurrency: 5, // 并发处理 5 个任务
19
- * })
17
+ * interface EmailTask {
18
+ * to: string
19
+ * subject: string
20
+ * }
20
21
  *
21
- * // 注册任务处理器
22
- * queue.handle('send-email', async (data) => {
23
- * await sendEmail(data.to, data.subject)
22
+ * const queue = new RedisQueue<EmailTask>({
23
+ * redisUrl: 'redis://localhost:6379',
24
+ * queueKey: 'email-queue',
25
+ * concurrency: 5,
26
+ * handler: async (data) => {
27
+ * await sendEmail(data.to, data.subject)
28
+ * return catchIt(() => {})
29
+ * }
24
30
  * })
25
31
  *
26
32
  * // 连接 Redis(自动启动消费者)
27
33
  * await queue.connect()
28
34
  *
29
35
  * // 入队任务(自动开始处理)
30
- * await queue.enqueue('send-email', { to: 'user@example.com', subject: 'Hello' })
36
+ * await queue.enqueue({ to: 'user@example.com', subject: 'Hello' })
31
37
  *
32
38
  * // 获取队列统计信息(O(1) 时间复杂度)
33
39
  * const stats = await queue.statistics()
@@ -41,7 +47,7 @@ export class RedisQueue {
41
47
  recoveryInterval = null;
42
48
  processingTasks = 0; // 当前正在处理的任务数
43
49
  config;
44
- handlers = new Map();
50
+ handler;
45
51
  // 不同状态队列的键名
46
52
  failedQueue;
47
53
  pendingQueue;
@@ -58,6 +64,10 @@ export class RedisQueue {
58
64
  if (config.queueKey.length < 6) {
59
65
  throw new Error('[TaskQueue] queueKey must be at least 6 characters long');
60
66
  }
67
+ if (!config.handler) {
68
+ throw new Error('[TaskQueue] handler is required');
69
+ }
70
+ this.handler = config.handler;
61
71
  this.config = {
62
72
  queueKey: config.queueKey,
63
73
  redisUrl: config.redisUrl,
@@ -105,13 +115,7 @@ export class RedisQueue {
105
115
  });
106
116
  }
107
117
  }
108
- /**
109
- * 注册任务处理器
110
- */
111
- handle(type, handler) {
112
- this.handlers.set(type, handler);
113
- }
114
- async enqueue(type, data) {
118
+ async enqueue(data) {
115
119
  if (!this.redis) {
116
120
  console.warn('[TaskQueue] Redis not available, skipping task enqueue');
117
121
  return Array.isArray(data) ? [] : '';
@@ -141,11 +145,10 @@ export class RedisQueue {
141
145
  for (const item of dataList) {
142
146
  // 检查 data 中是否包含自定义 id
143
147
  const customId = item.id;
144
- const taskId = customId ? `${type}:${customId}` : `${type}:${randomUUID()}`;
148
+ const taskId = customId || randomUUID();
145
149
  const taskKey = `${this.config.queueKey}:task:${taskId}`;
146
150
  const task = {
147
151
  id: taskId,
148
- type,
149
152
  data: item,
150
153
  retryCount: 0,
151
154
  maxRetries: this.config.maxRetries,
@@ -162,7 +165,7 @@ export class RedisQueue {
162
165
  console.log(`[TaskQueue] Task already exists, skipping: ${taskId}`);
163
166
  }
164
167
  else {
165
- console.log(`[TaskQueue] Enqueued task: ${task.type} (${task.id})`);
168
+ console.log(`[TaskQueue] Enqueued task: ${task.id}`);
166
169
  }
167
170
  taskIds.push(taskId);
168
171
  }
@@ -314,12 +317,6 @@ export class RedisQueue {
314
317
  await this.applyStatus(taskId, task.status, 'failed');
315
318
  return;
316
319
  }
317
- const handler = this.handlers.get(task.type);
318
- if (!handler) {
319
- console.error(`[TaskQueue] No handler registered for task type: ${task.type}`);
320
- await this.applyStatus(taskId, 'pending', 'failed');
321
- return;
322
- }
323
320
  try {
324
321
  // 任务已在 processing 队列中(由 Lua 脚本完成),只需更新状态和开始时间
325
322
  task.status = 'processing';
@@ -327,7 +324,7 @@ export class RedisQueue {
327
324
  const taskKey = `${this.config.queueKey}:task:${taskId}`;
328
325
  await this.redis.setEx(taskKey, this.config.cleanupDelay, JSON.stringify(task));
329
326
  // 执行任务处理器
330
- await handler(task.data);
327
+ await this.handler(task.data);
331
328
  // 更新状态为完成
332
329
  await this.applyStatus(taskId, 'processing', 'completed');
333
330
  console.log(`[TaskQueue] Task completed: ${taskId}`);