ADR-001 附录 A:Worker 执行依赖闭包审计
审计日期
2026-06-19
目标
确认每个 BullMQ Processor 的完整依赖、事件和副作用,防止 Processor 移到独立 Worker 进程后链路断裂。
一、Processor 依赖矩阵
1.1 AiAnalysisWorker
| 维度 |
事实 |
证据 |
| 队列 |
ai-analysis |
@Processor(QUEUE_AI_ANALYSIS) |
| 注册位置 |
AppModule (line 193) + WorkerModule (line 64) — 双重注册 |
app.module.ts:193、worker.module.ts:64 |
| 注入依赖 |
ActiveRecallAnalysisWorkflow、FeynmanEvaluationWorkflow、AiAnalysisRepository、EventBusService (@Optional)、FocusItemsService (@Optional) |
ai-analysis.worker.ts:22-28 |
| 依赖 Module |
AiModule(提供 Workflow)、AiAnalysisModule(提供 Repository)、EventBusModule(提供 EventBusService)、FocusItemsModule(提供 FocusItemsService) |
— |
| 写入的表 |
AiAnalysisJob(updateJobStatus)、AiAnalysisResult(createResult)、FocusItem(create) |
ai-analysis.worker.ts:47,66,67,87,101 |
| 发布的事件 |
ai.analysis.completed(通过 EventBusService.publish() → EventEmitter2.emit() — 进程内) |
ai-analysis.worker.ts:71 |
| 事件订阅者 |
ReviewCardSubscriber(@OnEvent('ai.analysis.completed') → ReviewModule → AppModule) |
review-card.subscriber.ts:11 |
| WorkerModule 中缺失 |
FocusItemsModule 未导入 → FocusItemsService 在 Worker 进程中为 null |
worker.module.ts:1-69 |
| 移动后断裂链路 |
⚠️ ai.analysis.completed 事件无法到达 ReviewCardSubscriber(进程内 EventEmitter2);⚠️ FocusItemsService 在独立 Worker 进程中为 null |
— |
1.2 DocumentImportWorker
| 维度 |
事实 |
证据 |
| 队列 |
document-import |
@Processor(QUEUE_DOCUMENT_IMPORT) |
| 注册位置 |
AppModule (line 194) + WorkerModule (line 65) — 双重注册 |
app.module.ts:194、worker.module.ts:65 |
| 注入依赖 |
KnowledgeImportWorkflow、KnowledgeItemsRepository、RedisService |
需确认 document-import.worker.ts |
| 写入的表 |
KnowledgeItem |
— |
| 发布的事件 |
无 |
— |
| 移动后断裂链路 |
无事件依赖,但需确认 Redis 连接在 Worker 进程中可用 |
— |
1.3 NotificationWorker
| 维度 |
事实 |
证据 |
| 队列 |
notification |
@Processor(QUEUE_NOTIFICATION) |
| 注册位置 |
AppModule (line 195) + WorkerModule (line 66) — 双重注册 |
app.module.ts:195、worker.module.ts:66 |
| 注入依赖 |
NotificationsService |
notification.worker.ts:8,12 |
| 依赖链 |
NotificationsService → NotificationsRepository(必需)+ EventBusService(@Optional) |
notifications.service.ts:13-15 |
| 依赖 Module |
NotificationsModule(提供 NotificationsService + NotificationsRepository)、EventBusModule(提供 EventBusService) |
notifications.module.ts:9-14 |
| 写入的表 |
Notification(更新发送状态 — 通过 NotificationsRepository) |
— |
| 发布的事件 |
无(EventBusService 为 @Optional 注入,NotificationWorker 自身不发布事件) |
— |
| 移动后断裂链路 |
无 — NotificationsModule 已在 WorkerModule.imports 中(line 61),NotificationsModule 自身 imports EventBusModule,PrismaService 由 PrismaModule 提供。依赖链完整,Worker 进程启动不会因缺失依赖崩溃 |
worker.module.ts:61 |
1.4 AuditLogProcessor
| 维度 |
事实 |
证据 |
| 队列 |
audit-logs |
@Processor(QUEUE_AUDIT_LOG) |
| 注册位置 |
AdminAuditLogModule providers(仅注册一次,未双重) |
admin-audit-log.module.ts:13 |
| 注入依赖 |
PrismaService |
audit-log.processor.ts:7 |
| 写入的表 |
AdminAuditLog |
audit-log.processor.ts:12-23 |
| 发布的事件 |
无 |
— |
| 移动后断裂链路 |
无业务事件依赖,但 AdminAuditLogModule 同时包含 Controller 和 Processor,需要拆分 |
— |
1.5 FileCleanupProcessor
| 维度 |
事实 |
证据 |
| 队列 |
file-cleanup |
@Processor(QUEUE_FILE_CLEANUP) |
| 注册位置 |
⚠️ FilesModule 导入了 FileCleanupProcessor 但未在 providers 中注册 — BullExplorer 扫描可能仍能发现,但依赖注入不可靠 |
files.module.ts:3,12-14 |
| 注入依赖 |
CosStorageProvider |
file-cleanup.processor.ts:10 |
| 写入的表 |
无(直接操作 COS) |
— |
| 发布的事件 |
无 |
— |
| 移动后断裂链路 |
需确认 CosStorageProvider 在 Worker 进程中可注入 |
— |
二、事件链路审计
2.1 EventBusService 实现
| 方法 |
机制 |
跨进程 |
证据 |
publish(event) |
EventEmitter2.emit() — 进程内同步 |
❌ |
event-bus.service.ts:19-22 |
publishAsync(event) |
BullMQ domain-events 队列 — 跨进程异步 |
✅ |
event-bus.service.ts:26-36 |
2.2 当前事件使用
| 事件 |
发布者 |
发布方式 |
订阅者 |
订阅方式 |
位置 |
跨进程安全 |
ai.analysis.completed |
AiAnalysisWorker |
publish()(进程内) |
ReviewCardSubscriber |
@OnEvent(EventEmitter2) |
review-card.subscriber.ts:11 |
❌ |
task.enqueued |
QueueService |
publish()(进程内) |
无消费者 |
— |
queue.service.ts:34 |
— |
streak.updated |
GrowthService |
publish()(进程内) |
无消费者 |
— |
growth.service.ts:53 |
— |
2.3 Domain Events 队列
- 生产者:
EventBusService.publishAsync() → queue.add('domain-events', ...)
- 消费者:无 — 代码库中无任何
@Processor('domain-events')
- 状态:死队列 — 事件被写入 Redis 但永不被处理
- 结论:
publishAsync() 当前无实际效果
2.4 移动后的断裂链路
移动前(同进程):
AiAnalysisWorker → EventBusService.publish() → EventEmitter2.emit('ai.analysis.completed')
→ ReviewCardSubscriber.@OnEvent('ai.analysis.completed')
→ reviewService.generateCards() → ReviewCard 创建 ✅
移动后(跨进程):
Worker 进程: AiAnalysisWorker → EventBusService.publish() → EventEmitter2.emit()
API 进程: ReviewCardSubscriber.@OnEvent() — 收不到 ❌
影响:AI 分析完成后不再自动生成复习卡片。
三、Module 注册审计
3.1 双重注册的 Processor
| Processor |
AppModule |
WorkerModule |
业务 Module |
问题 |
| AiAnalysisWorker |
✅ (line 193) |
✅ (line 62) |
— |
若两个 Module 均加载,BullMQ 创建两个 Worker 实例消费同一队列 → 并发双消费 |
| DocumentImportWorker |
✅ (line 194) |
✅ (line 65) |
— |
同上 |
| NotificationWorker |
✅ (line 195) |
✅ (line 66) |
— |
同上 |
3.2 需拆分的 Module(同时包含 Controller 和 Processor)
| Module |
Controller |
Processor |
Producer |
AdminAuditLogModule |
✅ |
✅ (AuditLogProcessor) |
— |
FilesModule |
✅ |
⚠️ (FileCleanupProcessor 导入但未在 providers 注册) |
✅ (FilesService) |
四、WorkerModule 缺失依赖
当前 WorkerModule(worker.module.ts:1-69)导入的 Module:
PrismaModule, RedisModule, QueueModule, AiModule, StorageModule,
LoggerModule, AiAnalysisModule, DocumentImportModule, KnowledgeItemsModule, NotificationsModule
缺失但 AiAnalysisWorker 需要的 Module:
| 缺失 Module |
提供的 Service |
影响 |
FocusItemsModule |
FocusItemsService |
AiAnalysisWorker 中 focusItems 为 null,不创建 FocusItem |
EventBusModule |
EventBusService |
AiAnalysisWorker 中 eventBus 为 null,不发布事件。但当前有 @Optional() 标记,不会崩溃 |
ReviewModule |
ReviewCardSubscriber |
即使事件发布成功(使用 publishAsync),无订阅者处理 |
五、修复建议矩阵(供 01-03 使用)
| 问题 |
修复方向 |
优先级 |
| AiAnalysisWorker/NotificationWorker/DocumentImportWorker 双重注册 |
从 AppModule providers 中移除,仅保留在 WorkerModule |
阻塞 |
EventBusService.publish() 无法跨进程 |
选项 A:将 ReviewCardSubscriber 移入 WorkerModule;选项 B:改为 publishAsync() + 新增 DomainEventsProcessor 消费 domain-events 队列 |
阻塞 |
FocusItemsModule 未在 WorkerModule |
添加 FocusItemsModule 到 WorkerModule.imports |
阻塞 |
ReviewModule 未在 WorkerModule |
如果选 A,需在 WorkerModule 中导入 ReviewModule(或拆出 ReviewWorkerModule) |
阻塞 |
AuditLogProcessor 在 AdminAuditLogModule 中 |
拆分为 AdminAuditLogProducerModule + AdminAuditLogWorkerModule,或将其移入 WorkerModule 并保留 Controller 在原 Module |
高 |
FileCleanupProcessor 未在 providers 中注册 |
补充注册,或显式移入 WorkerModule |
高 |
六、无法确认项
| 项目 |
状态 |
原因 |
| Docker Compose 当前是否同时启动 API 和 Worker |
无法确认 |
需检查 docker-compose.yml 中 command 或 entrypoint 指向 dist/main.js 还是两者 |
| 生产 systemd 是否使用独立 Worker 进程 |
无法确认 |
无法访问生产服务器 |