llm-content-creator 0.1.0 → 0.1.1
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/dist/application/workflow/SyncExecutor.d.ts.map +1 -1
- package/dist/application/workflow/SyncExecutor.js +1 -0
- package/dist/application/workflow/SyncExecutor.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +3 -3
- package/dist/config/index.js.map +1 -1
- package/dist/domain/repositories/TaskRepository.d.ts +1 -0
- package/dist/domain/repositories/TaskRepository.d.ts.map +1 -1
- package/dist/infrastructure/database/MemoryTaskRepository.d.ts +17 -1
- package/dist/infrastructure/database/MemoryTaskRepository.d.ts.map +1 -1
- package/dist/infrastructure/database/MemoryTaskRepository.js +76 -1
- package/dist/infrastructure/database/MemoryTaskRepository.js.map +1 -1
- package/dist/infrastructure/database/SQLiteQualityCheckRepository.d.ts +27 -0
- package/dist/infrastructure/database/SQLiteQualityCheckRepository.d.ts.map +1 -0
- package/dist/infrastructure/database/SQLiteQualityCheckRepository.js +80 -0
- package/dist/infrastructure/database/SQLiteQualityCheckRepository.js.map +1 -0
- package/dist/infrastructure/database/SQLiteResultRepository.d.ts +31 -0
- package/dist/infrastructure/database/SQLiteResultRepository.d.ts.map +1 -0
- package/dist/infrastructure/database/SQLiteResultRepository.js +77 -0
- package/dist/infrastructure/database/SQLiteResultRepository.js.map +1 -0
- package/dist/infrastructure/database/SQLiteTaskRepository.d.ts +12 -0
- package/dist/infrastructure/database/SQLiteTaskRepository.d.ts.map +1 -1
- package/dist/infrastructure/database/SQLiteTaskRepository.js +69 -2
- package/dist/infrastructure/database/SQLiteTaskRepository.js.map +1 -1
- package/dist/infrastructure/database/index.d.ts +23 -2
- package/dist/infrastructure/database/index.d.ts.map +1 -1
- package/dist/infrastructure/database/index.js +33 -2
- package/dist/infrastructure/database/index.js.map +1 -1
- package/dist/infrastructure/redis/connection.d.ts.map +1 -1
- package/dist/infrastructure/redis/connection.js +14 -3
- package/dist/infrastructure/redis/connection.js.map +1 -1
- package/dist/presentation/cli/commands/cancel.d.ts.map +1 -1
- package/dist/presentation/cli/commands/cancel.js +1 -0
- package/dist/presentation/cli/commands/cancel.js.map +1 -1
- package/dist/presentation/cli/commands/create.d.ts.map +1 -1
- package/dist/presentation/cli/commands/create.js +29 -14
- package/dist/presentation/cli/commands/create.js.map +1 -1
- package/dist/presentation/cli/commands/list.d.ts +8 -0
- package/dist/presentation/cli/commands/list.d.ts.map +1 -0
- package/dist/presentation/cli/commands/list.js +154 -0
- package/dist/presentation/cli/commands/list.js.map +1 -0
- package/dist/presentation/cli/commands/result.d.ts.map +1 -1
- package/dist/presentation/cli/commands/result.js +33 -3
- package/dist/presentation/cli/commands/result.js.map +1 -1
- package/dist/presentation/cli/commands/retry.d.ts +8 -0
- package/dist/presentation/cli/commands/retry.d.ts.map +1 -0
- package/dist/presentation/cli/commands/retry.js +203 -0
- package/dist/presentation/cli/commands/retry.js.map +1 -0
- package/dist/presentation/cli/commands/status.d.ts.map +1 -1
- package/dist/presentation/cli/commands/status.js +3 -2
- package/dist/presentation/cli/commands/status.js.map +1 -1
- package/dist/presentation/cli/index.js +4 -0
- package/dist/presentation/cli/index.js.map +1 -1
- package/dist/presentation/cli/utils/cleanup.d.ts +1 -3
- package/dist/presentation/cli/utils/cleanup.d.ts.map +1 -1
- package/dist/presentation/cli/utils/cleanup.js.map +1 -1
- package/dist/presentation/monitor-cli.js +2 -4
- package/dist/presentation/monitor-cli.js.map +1 -1
- package/dist/presentation/worker-cli.js +8 -5
- package/dist/presentation/worker-cli.js.map +1 -1
- package/dist/schedulers/TaskScheduler.d.ts.map +1 -1
- package/dist/schedulers/TaskScheduler.js +13 -5
- package/dist/schedulers/TaskScheduler.js.map +1 -1
- package/dist/workers/TaskWorker.d.ts.map +1 -1
- package/dist/workers/TaskWorker.js +39 -21
- package/dist/workers/TaskWorker.js.map +1 -1
- package/docs/cli-reference.md +623 -0
- package/docs/quick-start.md +47 -0
- package/docs/user-guide.md +191 -24
- package/package.json +3 -1
- package/src/application/workflow/SyncExecutor.ts +1 -0
- package/src/config/index.ts +3 -3
- package/src/domain/repositories/TaskRepository.ts +1 -0
- package/src/infrastructure/database/MemoryTaskRepository.ts +89 -1
- package/src/infrastructure/database/SQLiteQualityCheckRepository.ts +102 -0
- package/src/infrastructure/database/SQLiteResultRepository.ts +95 -0
- package/src/infrastructure/database/SQLiteTaskRepository.ts +80 -2
- package/src/infrastructure/database/index.ts +39 -2
- package/src/infrastructure/redis/connection.ts +16 -3
- package/src/presentation/cli/commands/cancel.ts +1 -0
- package/src/presentation/cli/commands/create.ts +30 -14
- package/src/presentation/cli/commands/list.ts +172 -0
- package/src/presentation/cli/commands/result.ts +33 -3
- package/src/presentation/cli/commands/retry.ts +241 -0
- package/src/presentation/cli/commands/status.ts +3 -2
- package/src/presentation/cli/index.ts +4 -0
- package/src/presentation/cli/utils/cleanup.ts +1 -1
- package/src/presentation/monitor-cli.ts +2 -4
- package/src/presentation/worker-cli.ts +8 -5
- package/src/schedulers/TaskScheduler.ts +13 -5
- package/src/workers/TaskWorker.ts +47 -22
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Retry Command
|
|
3
|
+
*
|
|
4
|
+
* 重新执行历史任务
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { createTaskRepository } from '../../../infrastructure/database/index.js';
|
|
10
|
+
import { TaskQueue } from '../../../infrastructure/queue/TaskQueue.js';
|
|
11
|
+
import { createLogger } from '../../../infrastructure/logging/logger.js';
|
|
12
|
+
|
|
13
|
+
const logger = createLogger('CLI:Retry');
|
|
14
|
+
|
|
15
|
+
function printSeparator() {
|
|
16
|
+
console.log(chalk.gray('─'.repeat(80)));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const retryCommand = new Command('retry')
|
|
20
|
+
.description('重新执行任务')
|
|
21
|
+
.option('-t, --task-id <id>', '重新执行指定任务')
|
|
22
|
+
.option('-a, --all', '重新执行所有等待中的任务')
|
|
23
|
+
.option('-s, --status <status>', '按状态筛选任务 (pending, failed, cancelled)', 'pending')
|
|
24
|
+
.option('-l, --limit <number>', '处理数量限制', '10')
|
|
25
|
+
.option('--dry-run', '模拟运行,不实际执行')
|
|
26
|
+
.action(async (options) => {
|
|
27
|
+
try {
|
|
28
|
+
printSeparator();
|
|
29
|
+
console.log(chalk.bold.blue('🔄 重新执行任务'));
|
|
30
|
+
printSeparator();
|
|
31
|
+
|
|
32
|
+
const repository = createTaskRepository();
|
|
33
|
+
const queue = new TaskQueue();
|
|
34
|
+
|
|
35
|
+
// 方案 1: 重新执行指定任务
|
|
36
|
+
if (options.taskId) {
|
|
37
|
+
await retrySingleTask(options.taskId, repository, queue, options.dryRun);
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 方案 2: 批量重新执行
|
|
42
|
+
if (options.all) {
|
|
43
|
+
const limit = parseInt(options.limit) || 10;
|
|
44
|
+
await retryBatchTasks(options.status, limit, repository, queue, options.dryRun);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 没有指定参数,显示帮助
|
|
49
|
+
console.log(chalk.yellow('请指定要重新执行的任务:'));
|
|
50
|
+
console.log(chalk.white('1. 重新执行单个任务:'));
|
|
51
|
+
console.log(chalk.gray(' pnpm run cli:retry --task-id <任务ID>'));
|
|
52
|
+
console.log(chalk.white('\n2. 批量重新执行所有等待任务:'));
|
|
53
|
+
console.log(chalk.gray(' pnpm run cli:retry --all'));
|
|
54
|
+
console.log(chalk.white('\n3. 批量重新执行失败任务:'));
|
|
55
|
+
console.log(chalk.gray(' pnpm run cli:retry --all --status failed'));
|
|
56
|
+
console.log(chalk.white('\n4. 模拟运行(不实际执行):'));
|
|
57
|
+
console.log(chalk.gray(' pnpm run cli:retry --all --dry-run'));
|
|
58
|
+
console.log();
|
|
59
|
+
|
|
60
|
+
process.exit(0);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
logger.error('Failed to retry tasks', error as Error);
|
|
63
|
+
console.error(chalk.red('❌ 重新执行失败:'), (error as Error).message);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
} finally {
|
|
66
|
+
// 清理队列连接
|
|
67
|
+
try {
|
|
68
|
+
const queue = new TaskQueue();
|
|
69
|
+
await queue.close();
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// 忽略清理错误
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 重新执行单个任务
|
|
78
|
+
*/
|
|
79
|
+
async function retrySingleTask(
|
|
80
|
+
taskId: string,
|
|
81
|
+
repository: any,
|
|
82
|
+
queue: TaskQueue,
|
|
83
|
+
dryRun: boolean = false
|
|
84
|
+
): Promise<void> {
|
|
85
|
+
console.log(chalk.white(`📝 任务 ID: ${chalk.bold(taskId)}`));
|
|
86
|
+
console.log();
|
|
87
|
+
|
|
88
|
+
// 1. 查询任务
|
|
89
|
+
const task = await repository.findById(taskId);
|
|
90
|
+
|
|
91
|
+
if (!task) {
|
|
92
|
+
console.log(chalk.red('❌ 任务不存在'));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(chalk.gray('任务信息:'));
|
|
97
|
+
console.log(chalk.gray(` 主题: ${task.topic}`));
|
|
98
|
+
console.log(chalk.gray(` 状态: ${task.status}`));
|
|
99
|
+
console.log(chalk.gray(` 模式: ${task.mode}`));
|
|
100
|
+
console.log(chalk.gray(` 创建时间: ${task.createdAt?.toLocaleString('zh-CN')}`));
|
|
101
|
+
console.log();
|
|
102
|
+
|
|
103
|
+
// 2. 检查任务状态
|
|
104
|
+
if (task.status === 'running' || task.status === 'processing') {
|
|
105
|
+
console.log(chalk.yellow('⚠️ 任务正在运行中,无需重新执行'));
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (task.status === 'completed') {
|
|
110
|
+
console.log(chalk.yellow('⚠️ 任务已完成,如需重新执行请创建新任务'));
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 3. 模拟运行
|
|
115
|
+
if (dryRun) {
|
|
116
|
+
console.log(chalk.blue('🔍 [模拟运行] 将会执行以下操作:'));
|
|
117
|
+
console.log(chalk.gray(` 1. 将任务添加到 Redis 队列`));
|
|
118
|
+
console.log(chalk.gray(` 2. Worker 将会获取并处理该任务`));
|
|
119
|
+
console.log();
|
|
120
|
+
console.log(chalk.yellow('💡 这是模拟运行,没有实际执行'));
|
|
121
|
+
console.log(chalk.gray(' 去掉 --dry-run 参数即可实际执行'));
|
|
122
|
+
console.log();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 4. 添加到队列
|
|
127
|
+
try {
|
|
128
|
+
console.log(chalk.white('⏳ 正在添加到队列...'));
|
|
129
|
+
|
|
130
|
+
await queue.addTask({
|
|
131
|
+
taskId: task.taskId,
|
|
132
|
+
mode: task.mode === 'sync' ? 'sync' : 'async',
|
|
133
|
+
topic: task.topic,
|
|
134
|
+
requirements: task.requirements,
|
|
135
|
+
hardConstraints: task.hardConstraints,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
console.log(chalk.green('✅ 任务已添加到队列!'));
|
|
139
|
+
console.log();
|
|
140
|
+
console.log(chalk.white('💡 后续操作:'));
|
|
141
|
+
console.log(chalk.gray(' 1. 确保 Worker 正在运行: pnpm run worker'));
|
|
142
|
+
console.log(chalk.gray(' 2. 查看任务状态: pnpm run cli:status --task-id ' + task.taskId));
|
|
143
|
+
console.log();
|
|
144
|
+
|
|
145
|
+
logger.info('Task retried', { taskId, topic: task.topic });
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.log(chalk.red('❌ 添加到队列失败:'), (error as Error).message);
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 批量重新执行任务
|
|
154
|
+
*/
|
|
155
|
+
async function retryBatchTasks(
|
|
156
|
+
status: string,
|
|
157
|
+
limit: number,
|
|
158
|
+
repository: any,
|
|
159
|
+
queue: TaskQueue,
|
|
160
|
+
dryRun: boolean = false
|
|
161
|
+
): Promise<void> {
|
|
162
|
+
console.log(chalk.white(`📊 批量重新执行 ${chalk.bold(status)} 状态的任务`));
|
|
163
|
+
console.log(chalk.gray(`限制: ${limit} 个`));
|
|
164
|
+
console.log();
|
|
165
|
+
|
|
166
|
+
// 1. 查询任务
|
|
167
|
+
const tasks = await repository.findMany({ status }, { limit });
|
|
168
|
+
|
|
169
|
+
if (tasks.length === 0) {
|
|
170
|
+
console.log(chalk.yellow('✅ 没有找到需要重新执行的任务'));
|
|
171
|
+
console.log();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log(chalk.gray(`找到 ${tasks.length} 个任务\n`));
|
|
176
|
+
|
|
177
|
+
// 2. 显示任务列表
|
|
178
|
+
console.log(chalk.gray('任务列表:'));
|
|
179
|
+
tasks.forEach((task: any, index: number) => {
|
|
180
|
+
console.log(chalk.gray(` ${index + 1}. ${task.taskId}`));
|
|
181
|
+
console.log(chalk.gray(` 主题: ${task.topic}`));
|
|
182
|
+
console.log(chalk.gray(` 状态: ${task.status}`));
|
|
183
|
+
console.log();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// 3. 模拟运行
|
|
187
|
+
if (dryRun) {
|
|
188
|
+
console.log(chalk.blue('🔍 [模拟运行] 将会执行以下操作:'));
|
|
189
|
+
console.log(chalk.gray(` 将 ${tasks.length} 个任务添加到 Redis 队列`));
|
|
190
|
+
console.log();
|
|
191
|
+
console.log(chalk.yellow('💡 这是模拟运行,没有实际执行'));
|
|
192
|
+
console.log(chalk.gray(' 去掉 --dry-run 参数即可实际执行'));
|
|
193
|
+
console.log();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 4. 批量添加到队列
|
|
198
|
+
console.log(chalk.white('⏳ 正在批量添加到队列...\n'));
|
|
199
|
+
|
|
200
|
+
let successCount = 0;
|
|
201
|
+
let failCount = 0;
|
|
202
|
+
|
|
203
|
+
for (const task of tasks) {
|
|
204
|
+
try {
|
|
205
|
+
await queue.addTask({
|
|
206
|
+
taskId: task.taskId,
|
|
207
|
+
mode: task.mode === 'sync' ? 'sync' : 'async',
|
|
208
|
+
topic: task.topic,
|
|
209
|
+
requirements: task.requirements,
|
|
210
|
+
hardConstraints: task.hardConstraints,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
console.log(chalk.green(`✅ ${task.taskId}`));
|
|
214
|
+
successCount++;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.log(chalk.red(`❌ ${task.taskId} - ${(error as Error).message}`));
|
|
217
|
+
failCount++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log();
|
|
222
|
+
console.log(chalk.white('📊 执行结果:'));
|
|
223
|
+
console.log(chalk.green(` 成功: ${successCount} 个`));
|
|
224
|
+
if (failCount > 0) {
|
|
225
|
+
console.log(chalk.red(` 失败: ${failCount} 个`));
|
|
226
|
+
}
|
|
227
|
+
console.log();
|
|
228
|
+
|
|
229
|
+
if (successCount > 0) {
|
|
230
|
+
console.log(chalk.white('💡 后续操作:'));
|
|
231
|
+
console.log(chalk.gray(' 1. 确保 Worker 正在运行: pnpm run worker'));
|
|
232
|
+
console.log(chalk.gray(' 2. 查看任务列表: pnpm run cli:list'));
|
|
233
|
+
console.log();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
logger.info('Batch retry completed', {
|
|
237
|
+
total: tasks.length,
|
|
238
|
+
success: successCount,
|
|
239
|
+
failed: failCount,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
-
import {
|
|
9
|
+
import { createTaskRepository } from '../../../infrastructure/database/index.js';
|
|
10
10
|
import { getStatusText, formatDate, formatDuration, printSeparator } from '../utils/formatter.js';
|
|
11
11
|
import { cleanupResources } from '../utils/cleanup.js';
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ export const statusCommand = new Command('status')
|
|
|
14
14
|
.description('查询任务状态')
|
|
15
15
|
.requiredOption('-t, --task-id <taskId>', '任务ID')
|
|
16
16
|
.action(async (options) => {
|
|
17
|
-
const taskRepo =
|
|
17
|
+
const taskRepo = createTaskRepository();
|
|
18
18
|
|
|
19
19
|
try {
|
|
20
20
|
const task = await taskRepo.findById(options.taskId);
|
|
@@ -71,6 +71,7 @@ export const statusCommand = new Command('status')
|
|
|
71
71
|
|
|
72
72
|
// 清理资源并正常退出
|
|
73
73
|
await cleanupResources(taskRepo);
|
|
74
|
+
process.exit(0);
|
|
74
75
|
|
|
75
76
|
} catch (error) {
|
|
76
77
|
console.error(chalk.red(`❌ 错误: ${error instanceof Error ? error.message : String(error)}`));
|
|
@@ -11,6 +11,8 @@ import { createCommand } from './commands/create.js';
|
|
|
11
11
|
import { statusCommand } from './commands/status.js';
|
|
12
12
|
import { resultCommand } from './commands/result.js';
|
|
13
13
|
import { cancelCommand } from './commands/cancel.js';
|
|
14
|
+
import { listCommand } from './commands/list.js';
|
|
15
|
+
import { retryCommand } from './commands/retry.js';
|
|
14
16
|
import { createLogger } from '../../infrastructure/logging/logger.js';
|
|
15
17
|
|
|
16
18
|
const logger = createLogger('CLI');
|
|
@@ -26,6 +28,8 @@ program
|
|
|
26
28
|
|
|
27
29
|
// 添加命令
|
|
28
30
|
program.addCommand(createCommand);
|
|
31
|
+
program.addCommand(listCommand);
|
|
32
|
+
program.addCommand(retryCommand);
|
|
29
33
|
program.addCommand(statusCommand);
|
|
30
34
|
program.addCommand(resultCommand);
|
|
31
35
|
program.addCommand(cancelCommand);
|
|
@@ -11,7 +11,7 @@ import { closeLogger } from '../../../infrastructure/logging/logger.js';
|
|
|
11
11
|
*
|
|
12
12
|
* @param repositories - 需要关闭的 Repository 数组
|
|
13
13
|
*/
|
|
14
|
-
export async function cleanupResources(...repositories: Array<
|
|
14
|
+
export async function cleanupResources(...repositories: Array<any>): Promise<void> {
|
|
15
15
|
const errors: Array<Error> = [];
|
|
16
16
|
|
|
17
17
|
// 1. 关闭所有 Repository 连接池
|
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Command } from 'commander';
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
// @ts-ignore - Module resolution issue with NodeNext
|
|
12
|
-
import { createLogger } from '../../infrastructure/logging/logger';
|
|
9
|
+
import { startMonitorServer } from '../monitoring/index.js';
|
|
10
|
+
import { createLogger } from '../infrastructure/logging/logger.js';
|
|
13
11
|
|
|
14
12
|
const logger = createLogger('MonitorCLI');
|
|
15
13
|
const program = new Command();
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Command } from 'commander';
|
|
9
|
-
|
|
10
|
-
import { createTaskWorker } from '
|
|
11
|
-
|
|
12
|
-
import { createLogger } from '../../infrastructure/logging/logger';
|
|
9
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
10
|
+
import { createTaskWorker } from '../workers/TaskWorker.js';
|
|
11
|
+
import { createLogger } from '../infrastructure/logging/logger.js';
|
|
13
12
|
|
|
14
13
|
const logger = createLogger('WorkerCLI');
|
|
15
14
|
const program = new Command();
|
|
@@ -22,7 +21,7 @@ program
|
|
|
22
21
|
program
|
|
23
22
|
.command('start')
|
|
24
23
|
.description('启动 Worker')
|
|
25
|
-
.option('-w, --worker-id <id>', 'Worker ID', process.env.WORKER_ID || `worker-${
|
|
24
|
+
.option('-w, --worker-id <id>', 'Worker ID', process.env.WORKER_ID || `worker-${uuidv4()}`)
|
|
26
25
|
.option('-c, --concurrency <number>', '并发数', '2')
|
|
27
26
|
.action(async (options) => {
|
|
28
27
|
try {
|
|
@@ -55,6 +54,10 @@ program
|
|
|
55
54
|
logger.info('Worker running, press Ctrl+C to stop');
|
|
56
55
|
} catch (error) {
|
|
57
56
|
logger.error('Failed to start worker', error as Error);
|
|
57
|
+
console.error('Detailed error:', error);
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
console.error('Stack trace:', error.stack);
|
|
60
|
+
}
|
|
58
61
|
process.exit(1);
|
|
59
62
|
}
|
|
60
63
|
});
|
|
@@ -126,7 +126,17 @@ export class TaskScheduler {
|
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
// 4. 保存到数据库
|
|
129
|
-
await this.repository.create(
|
|
129
|
+
await this.repository.create({
|
|
130
|
+
id: task.id!,
|
|
131
|
+
traceId: undefined,
|
|
132
|
+
mode: task.mode!,
|
|
133
|
+
type: (task.type as any) || 'article',
|
|
134
|
+
topic: task.topic!,
|
|
135
|
+
requirements: task.requirements!,
|
|
136
|
+
hardConstraints: task.hardConstraints,
|
|
137
|
+
priority: task.priority,
|
|
138
|
+
maxRetries: 3,
|
|
139
|
+
});
|
|
130
140
|
|
|
131
141
|
logger.info('Task created', {
|
|
132
142
|
taskId,
|
|
@@ -263,10 +273,8 @@ export class TaskScheduler {
|
|
|
263
273
|
return false;
|
|
264
274
|
}
|
|
265
275
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
updatedAt: new Date().toISOString(),
|
|
269
|
-
});
|
|
276
|
+
// 使用 delete 方法标记任务为已取消
|
|
277
|
+
await this.repository.delete(taskId);
|
|
270
278
|
|
|
271
279
|
logger.info('Task cancelled', { taskId });
|
|
272
280
|
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Worker, Job } from 'bullmq';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
8
9
|
import { getRedisClient } from '../infrastructure/redis/connection.js';
|
|
9
|
-
import { createTaskRepository } from '../infrastructure/database/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import { createTaskRepository, createResultRepository } from '../infrastructure/database/index.js';
|
|
11
|
+
import { createContentCreatorGraph, createInitialState } from '../domain/workflow/index.js';
|
|
11
12
|
import { createLogger } from '../infrastructure/logging/logger.js';
|
|
12
13
|
import type { TaskJobData } from '../infrastructure/queue/TaskQueue.js';
|
|
13
14
|
import { ExecutionMode } from '../domain/entities/Task.js';
|
|
@@ -88,24 +89,35 @@ export class TaskWorker {
|
|
|
88
89
|
// 更新任务进度
|
|
89
90
|
await job.updateProgress(0);
|
|
90
91
|
|
|
92
|
+
let task: Awaited<ReturnType<typeof this.repository.findById>> | null = null;
|
|
93
|
+
|
|
91
94
|
try {
|
|
92
95
|
// 1. 抢占任务(使用乐观锁)
|
|
93
|
-
|
|
96
|
+
task = await this.repository.findById(data.taskId);
|
|
97
|
+
if (!task) {
|
|
98
|
+
throw new Error(`Task not found: ${data.taskId}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const claimed = await this.repository.claimTask(
|
|
94
102
|
data.taskId,
|
|
95
|
-
this.workerId
|
|
103
|
+
this.workerId,
|
|
104
|
+
task.version
|
|
96
105
|
);
|
|
97
106
|
|
|
98
107
|
if (!claimed) {
|
|
99
|
-
throw new Error(`Failed to claim task ${data.taskId}`);
|
|
108
|
+
throw new Error(`Failed to claim task ${data.taskId} (version mismatch or already claimed)`);
|
|
100
109
|
}
|
|
101
110
|
|
|
111
|
+
// 更新任务版本号(claimTask 会将版本号 +1)
|
|
112
|
+
task.version = task.version + 1;
|
|
113
|
+
|
|
102
114
|
logger.info('Task claimed', { taskId: data.taskId });
|
|
103
115
|
|
|
104
116
|
// 更新进度 10%
|
|
105
117
|
await job.updateProgress(10);
|
|
106
118
|
|
|
107
119
|
// 2. 创建工作流图
|
|
108
|
-
const graph =
|
|
120
|
+
const graph = createContentCreatorGraph();
|
|
109
121
|
|
|
110
122
|
// 3. 创建初始状态
|
|
111
123
|
const initialState = createInitialState({
|
|
@@ -130,16 +142,31 @@ export class TaskWorker {
|
|
|
130
142
|
await job.updateProgress(90);
|
|
131
143
|
|
|
132
144
|
// 5. 保存结果
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
const resultRepo = createResultRepository();
|
|
146
|
+
|
|
147
|
+
if (result.articleContent) {
|
|
148
|
+
await resultRepo.create({
|
|
149
|
+
taskId: data.taskId,
|
|
150
|
+
resultType: 'article',
|
|
151
|
+
content: result.articleContent,
|
|
152
|
+
metadata: {
|
|
153
|
+
wordCount: result.articleContent.length,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (result.images && result.images.length > 0) {
|
|
159
|
+
for (const image of result.images) {
|
|
160
|
+
await resultRepo.create({
|
|
161
|
+
taskId: data.taskId,
|
|
162
|
+
resultType: 'image',
|
|
163
|
+
content: image.url,
|
|
164
|
+
metadata: image.metadata,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
await this.repository.markAsCompleted(data.taskId, task.version);
|
|
143
170
|
|
|
144
171
|
const duration = Date.now() - startTime;
|
|
145
172
|
|
|
@@ -169,11 +196,9 @@ export class TaskWorker {
|
|
|
169
196
|
|
|
170
197
|
// 保存错误信息
|
|
171
198
|
try {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
completedAt: new Date().toISOString(),
|
|
176
|
-
});
|
|
199
|
+
if (task) {
|
|
200
|
+
await this.repository.markAsFailed(data.taskId, errorMessage, task.version);
|
|
201
|
+
}
|
|
177
202
|
} catch (updateError) {
|
|
178
203
|
logger.error('Failed to update task error status', updateError as Error, {
|
|
179
204
|
taskId: data.taskId,
|
|
@@ -315,6 +340,6 @@ export function createTaskWorker(
|
|
|
315
340
|
workerId?: string,
|
|
316
341
|
concurrency?: number
|
|
317
342
|
): TaskWorker {
|
|
318
|
-
const id = workerId || process.env.WORKER_ID || `worker-${
|
|
343
|
+
const id = workerId || process.env.WORKER_ID || `worker-${uuidv4()}`;
|
|
319
344
|
return new TaskWorker(id, concurrency);
|
|
320
345
|
}
|