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:
@@ -1,15 +1,20 @@
|
||||
/**
|
||||
* PKB个人知识库 API
|
||||
* API路径: /api/v1/pkb/knowledge
|
||||
*
|
||||
* 修复说明(2026-01-27):
|
||||
* - 移除硬编码的 localhost:3000,改用相对路径
|
||||
* - 使用相对路径后,Nginx 会自动代理到后端服务
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { getAccessToken } from '../../../framework/auth/api';
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
|
||||
// 使用相对路径,生产环境由 Nginx 代理到后端
|
||||
const API_BASE_URL = '/api/v1/pkb/knowledge';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: `${API_BASE_URL}/api/v1/pkb/knowledge`,
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -10,9 +10,10 @@ interface AgentModalProps {
|
||||
taskCount: number;
|
||||
onClose: () => void;
|
||||
onConfirm: (agents: AgentType[]) => void;
|
||||
isSubmitting?: boolean; // 🔒 防止重复提交
|
||||
}
|
||||
|
||||
export default function AgentModal({ visible, taskCount, onClose, onConfirm }: AgentModalProps) {
|
||||
export default function AgentModal({ visible, taskCount, onClose, onConfirm, isSubmitting = false }: AgentModalProps) {
|
||||
const [selectedAgents, setSelectedAgents] = useState<AgentType[]>(['editorial']);
|
||||
|
||||
const toggleAgent = (agent: AgentType) => {
|
||||
@@ -110,10 +111,10 @@ export default function AgentModal({ visible, taskCount, onClose, onConfirm }: A
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirm}
|
||||
disabled={selectedAgents.length === 0}
|
||||
disabled={selectedAgents.length === 0 || isSubmitting}
|
||||
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-bold rounded-lg shadow-sm transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
立即运行
|
||||
{isSubmitting ? '提交中...' : '立即运行'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,6 +141,9 @@ export default function AgentModal({ visible, taskCount, onClose, onConfirm }: A
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ export default function Dashboard() {
|
||||
const [filters, setFilters] = useState<TaskFilters>({ status: 'all', timeRange: 'all' });
|
||||
const [agentModalVisible, setAgentModalVisible] = useState(false);
|
||||
const [pendingTaskForRun, setPendingTaskForRun] = useState<ReviewTask | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false); // 🔒 防止重复提交
|
||||
|
||||
// 报告详情
|
||||
const [reportDetail, setReportDetail] = useState<ReviewReport | null>(null);
|
||||
@@ -119,12 +120,19 @@ export default function Dashboard() {
|
||||
};
|
||||
|
||||
const handleConfirmRun = async (agents: AgentType[]) => {
|
||||
// 🔒 防止重复提交
|
||||
if (isSubmitting) {
|
||||
console.warn('[Dashboard] 已在提交中,忽略重复请求');
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔥 保存到局部变量,避免onClose后丢失
|
||||
const taskToRun = pendingTaskForRun;
|
||||
|
||||
// 立即关闭弹窗
|
||||
// 立即关闭弹窗并设置提交状态
|
||||
setAgentModalVisible(false);
|
||||
setPendingTaskForRun(null);
|
||||
setIsSubmitting(true); // 🔒 锁定
|
||||
|
||||
try {
|
||||
if (taskToRun) {
|
||||
@@ -159,6 +167,8 @@ export default function Dashboard() {
|
||||
loadTasks();
|
||||
} catch (error: any) {
|
||||
message.error({ content: error.message || '启动失败', key: 'run', duration: 3 });
|
||||
} finally {
|
||||
setIsSubmitting(false); // 🔓 解锁
|
||||
}
|
||||
};
|
||||
|
||||
@@ -277,6 +287,7 @@ export default function Dashboard() {
|
||||
setPendingTaskForRun(null);
|
||||
}}
|
||||
onConfirm={handleConfirmRun}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -301,6 +312,9 @@ export default function Dashboard() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user