Explorar o código

fix: 入库任务状态判断增加 progress 检查,完善日志记录

- 样本中心返回 completed 时检查 succeeded/failed 比例:
  succeeded==0 标为 failed,部分成功标为 partial_success
- 前端新增部分完成状态(橙色标记),进度列 hover 显示成功/失败/总计
- callback 和 poller 两处统一判断逻辑
- batch_import/get_import_task 增加详细请求响应日志
kinglee hai 6 días
pai
achega
a8b5dbc18e

+ 13 - 1
app/knowledge_poller.py

@@ -81,7 +81,19 @@ class KnowledgePoller:
                 task.progress = json.dumps(progress, ensure_ascii=False)
 
             if sc_status in ('completed',):
-                task.status = 'success'
+                progress_data = sc_data.get('progress')
+                if progress_data:
+                    succeeded = progress_data.get('succeeded', 0)
+                    failed = progress_data.get('failed', 0)
+                    total = progress_data.get('total', 0)
+                    if total > 0 and succeeded == 0:
+                        task.status = 'failed'
+                    elif total > 0 and succeeded > 0 and failed > 0:
+                        task.status = 'partial_success'
+                    else:
+                        task.status = 'success'
+                else:
+                    task.status = 'success'
                 task.next_poll_at = None
             elif sc_status == 'failed':
                 task.status = 'failed'

+ 26 - 2
app/routes/knowledge_routes.py

@@ -225,7 +225,19 @@ def get_import_task(task_no):
                 task.progress = json.dumps(progress, ensure_ascii=False)
 
             if sc_status in ('completed',):
-                task.status = 'success'
+                progress_data = sc_data.get('progress')
+                if progress_data:
+                    succeeded = progress_data.get('succeeded', 0)
+                    failed = progress_data.get('failed', 0)
+                    total = progress_data.get('total', 0)
+                    if total > 0 and succeeded == 0:
+                        task.status = 'failed'
+                    elif total > 0 and succeeded > 0 and failed > 0:
+                        task.status = 'partial_success'
+                    else:
+                        task.status = 'success'
+                else:
+                    task.status = 'success'
                 task.next_poll_at = None
             elif sc_status == 'failed':
                 task.status = 'failed'
@@ -277,7 +289,19 @@ def knowledge_import_callback():
     task.status_detail = json.dumps(data, ensure_ascii=False)
 
     if status in ('completed',):
-        task.status = 'success'
+        progress = data.get('progress')
+        if progress:
+            succeeded = progress.get('succeeded', 0)
+            failed = progress.get('failed', 0)
+            total = progress.get('total', 0)
+            if total > 0 and succeeded == 0:
+                task.status = 'failed'
+            elif total > 0 and succeeded > 0 and failed > 0:
+                task.status = 'partial_success'
+            else:
+                task.status = 'success'
+        else:
+            task.status = 'success'
     elif status == 'failed':
         task.status = 'failed'
         task.error_message = data.get('error', '')

+ 7 - 1
app/sample_center_client.py

@@ -116,13 +116,18 @@ class SampleCenterClient:
             payload["children"] = children
         if callback_url:
             payload["callback_url"] = callback_url
-        logger.info(f"请求批量入库: url={url}, task_no={task_no}")
+        logger.info(
+            f"请求批量入库: url={url}, task_no={task_no}, "
+            f"parents_count={len(parents)}, children_count={len(children) if children else 0}, "
+            f"parents={parents!r}, children={children!r}, callback_url={callback_url}"
+        )
         resp = requests.post(
             url,
             headers=self._headers(),
             json=payload,
             timeout=60,
         )
+        logger.info(f"批量入库响应: status={resp.status_code}, body={resp.text[:1000]}")
         return self._parse(resp)
 
     def get_import_task(self, task_id):
@@ -130,4 +135,5 @@ class SampleCenterClient:
         url = f"{self.base_url}/api/v1/knowledge-bases/batch-import/{task_id}"
         logger.info(f"请求入库任务: url={url}")
         resp = requests.get(url, headers=self._headers(), timeout=15)
+        logger.info(f"入库任务响应: url={url}, status={resp.status_code}, body={resp.text[:1000]}")
         return self._parse(resp)

+ 13 - 4
app/templates/knowledge_management.html

@@ -160,6 +160,7 @@
 .status-pending { color: #F59E0B; }
 .status-processing { color: #3B82F6; }
 .status-success, .status-completed { color: #10B981; }
+.status-partial-success { color: #F97316; }
 .status-failed { color: #EF4444; }
 .custom-scrollbar::-webkit-scrollbar { width: 6px; }
 .custom-scrollbar::-webkit-scrollbar-track { background: #1F2937; }
@@ -355,13 +356,21 @@ function renderImportTasks(data) {
         'processing': '<span class="status-processing"><i class="fas fa-spinner fa-spin"></i> 处理中</span>',
         'success': '<span class="status-success"><i class="fas fa-check-circle"></i> 已完成</span>',
         'completed': '<span class="status-success"><i class="fas fa-check-circle"></i> 已完成</span>',
+        'partial_success': '<span class="status-partial-success"><i class="fas fa-exclamation-circle"></i> 部分完成</span>',
         'failed': '<span class="status-failed"><i class="fas fa-times-circle"></i> 失败</span>',
     };
 
     items.forEach(function(item) {
-        const progressHtml = item.progress
-            ? item.progress.processed + '/' + item.progress.total
-            : '-';
+        let progressHtml = '-';
+        if (item.progress) {
+            const p = item.progress;
+            if (p.succeeded !== undefined && p.failed !== undefined) {
+                progressHtml = `<span title="成功 ${p.succeeded} / 失败 ${p.failed} / 总计 ${p.total}">${p.processed || 0}/${p.total}</span>`;
+            } else {
+                progressHtml = p.processed + '/' + p.total;
+            }
+        }
+        const hasError = item.status === 'failed' || item.status === 'partial_success';
         const html = `<tr class="border-b border-gray-700 hover:bg-gray-700/30 transition-colors">
             <td class="px-4 py-3 font-mono text-xs text-gray-300">${item.task_no}</td>
             <td class="px-4 py-3 text-white">${item.kb_name || item.kb_id}</td>
@@ -371,7 +380,7 @@ function renderImportTasks(data) {
             <td class="px-4 py-3 text-center">
                 ${item.status === 'processing' || item.status === 'pending'
                     ? '<button onclick="refreshTaskStatus(\'' + item.task_no + '\')" class="text-blue-400 hover:text-blue-300 text-xs"><i class="fas fa-sync-alt"></i> 刷新</button>'
-                    : (item.error_message ? '<span class="text-red-400 text-xs" title="' + item.error_message + '"><i class="fas fa-exclamation-triangle"></i></span>' : '')}
+                    : (hasError && item.error_message ? '<span class="text-red-400 text-xs" title="' + item.error_message + '"><i class="fas fa-exclamation-triangle"></i></span>' : '')}
             </td>
         </tr>`;
         tbody.append(html);