fix(backend): Resolve PgBoss infinite loop issue and cleanup unused files

Backend fixes:
- Fix PgBoss task infinite loop on SAE (root cause: missing queue table constraints)
- Add singletonKey to prevent duplicate job enqueueing
- Add idempotency check in reviewWorker (skip completed tasks)
- Add optimistic locking in reviewService (atomic status update)

Frontend fixes:
- Add isSubmitting state to prevent duplicate submissions in RVW Dashboard
- Fix API baseURL in knowledgeBaseApi (relative path)

Cleanup (removed):
- Old frontend/ directory (migrated to frontend-v2)
- python-microservice/ (unused, replaced by extraction_service)
- Root package.json and node_modules (accidentally created)
- redcap-docker-dev/ (external dependency)
- Various temporary files and outdated docs in root

New documentation:
- docs/07-运维文档/01-PgBoss队列监控与维护.md
- docs/07-运维文档/02-故障预防检查清单.md
- docs/07-运维文档/03-数据库迁移注意事项.md

Database fix applied to RDS:
- Added PRIMARY KEY to platform_schema.queue
- Added 3 missing foreign key constraints

Tested: Local build passed, RDS constraints verified
This commit is contained in:
2026-01-27 18:16:22 +08:00
parent 2481b786d8
commit bbf98c4d5c
214 changed files with 4318 additions and 44920 deletions

View File

@@ -156,9 +156,14 @@ export async function runReview(params: RunReviewParams): Promise<{ jobId: strin
throw new Error('文档尚未提取完成,请稍后再试');
}
// 更新任务状态为reviewing
await prisma.reviewTask.update({
where: { id: taskId },
// 🔒 使用原子操作实现幂等性(防止并发请求重复入队)
// updateMany 只会更新匹配条件的记录,返回更新数量
const updateResult = await prisma.reviewTask.updateMany({
where: {
id: taskId,
// 只有当状态是 pending/completed/failed 时才允许启动
status: { in: ['pending', 'completed', 'failed'] }
},
data: {
status: 'reviewing',
selectedAgents: agents,
@@ -171,6 +176,14 @@ export async function runReview(params: RunReviewParams): Promise<{ jobId: strin
},
});
// 🔒 幂等性检查:如果没有更新任何记录,说明任务已经在运行中
if (updateResult.count === 0) {
logger.warn('[RVW] ⚠️ 任务已在运行中,拒绝重复启动', { taskId, currentStatus: task.status });
throw new Error('任务已在运行中,请勿重复提交');
}
logger.info('[RVW] ✅ 任务状态已原子更新为 reviewing', { taskId });
// ✅ 推送任务到 pg-boss 队列Platform-Only架构
const job = await jobQueue.push('rvw_review_task', {
taskId,

View File

@@ -63,6 +63,29 @@ export function registerReviewWorker() {
console.log(` 文本长度: ${extractedText.length} 字符`);
try {
// ✅ 检查任务是否已经完成(防止重复处理)
const existingTask = await prisma.reviewTask.findUnique({
where: { id: taskId },
select: { status: true, completedAt: true, overallScore: true },
});
if (existingTask?.status === 'completed' && existingTask.completedAt) {
logger.warn('[reviewWorker] ⚠️ Task already completed, skipping', {
jobId: job.id,
taskId,
completedAt: existingTask.completedAt,
overallScore: existingTask.overallScore,
});
console.log(`\n⚠ 任务已完成,跳过重复处理`);
console.log(` Task ID: ${taskId}`);
console.log(` 完成时间: ${existingTask.completedAt}`);
console.log(` 得分: ${existingTask.overallScore}`);
return {
taskId,
skipped: true,
reason: 'Task already completed',
};
}
// ========================================
// 1. 运行选中的智能体
// ========================================