diff --git a/app/api/routers/remote.py b/app/api/routers/remote.py index e6ed2b9..8a1c71c 100644 --- a/app/api/routers/remote.py +++ b/app/api/routers/remote.py @@ -151,7 +151,7 @@ def _run_remote_processing( state.history_manager.update(task_id, file_count=download_result.file_count) - refresh_cell_data(app_config, work_dir, logger) + _try_refresh_cell_data(app_config, work_dir, logger) if app_config.warehouse_type == "metrix": started = time.time() @@ -210,6 +210,21 @@ def _run_remote_processing( on_finish(task_id, final_status) +def _try_refresh_cell_data(app_config: AppConfig, work_dir: Path, logger: ProcessLogger) -> None: + if not app_config.cell_data.remote_data.enabled: + return + logger.set_stage("cell_data") + logger.info("── CellData 更新 ──") + try: + result = refresh_cell_data(app_config, work_dir, logger) + logger.success( + f"CellData 更新完成:{result.imported_rows} 行" + f"(解析 {result.parsed_rows},跳过 {result.skipped_rows})" + ) + except Exception as exc: + logger.warning(f"CellData 更新失败,继续容量处理: {exc}") + + def _format_bytes(size: int) -> str: value = float(size) for unit in ("B", "KB", "MB", "GB"): diff --git a/app/api/routers/tasks.py b/app/api/routers/tasks.py index 3921470..528a02a 100644 --- a/app/api/routers/tasks.py +++ b/app/api/routers/tasks.py @@ -169,7 +169,7 @@ def _task_finished(task_id: str) -> bool: def _run_processing(task_id: str, work_dir: Path, logger: ProcessLogger, app_config: AppConfig) -> None: try: - refresh_cell_data(app_config, work_dir, logger) + _try_refresh_cell_data(app_config, work_dir, logger) if app_config.warehouse_type == "metrix": import time @@ -214,3 +214,18 @@ def _run_processing(task_id: str, work_dir: Path, logger: ProcessLogger, app_con finally: apply_history_retention_safely() state.reset_task_lock() + + +def _try_refresh_cell_data(app_config: AppConfig, work_dir: Path, logger: ProcessLogger) -> None: + if not app_config.cell_data.remote_data.enabled: + return + logger.set_stage("cell_data") + logger.info("── CellData 更新 ──") + try: + result = refresh_cell_data(app_config, work_dir, logger) + logger.success( + f"CellData 更新完成:{result.imported_rows} 行" + f"(解析 {result.parsed_rows},跳过 {result.skipped_rows})" + ) + except Exception as exc: + logger.warning(f"CellData 更新失败,继续容量处理: {exc}") diff --git a/docs/project_context.md b/docs/project_context.md index 3667943..436815c 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -846,3 +846,11 @@ - `/api/remote/test`、`/api/config/cell-data/remote/test`、`/api/config/cell-data/mysql/test` 改为同步路由函数,保持响应结构不变,但让 FastAPI 在线程池中执行 FTP/SFTP、Metrix 平台 HTTP 与 MySQL 连接测试,避免慢连接或超时阻塞事件循环。 - 验证:`.venv\Scripts\python.exe -m compileall -q app` 通过;`frontend` `npm run build` 通过;构建产物已清理。 + +## 2026-06-25:CellData 容量处理集成改进与前端处理进度优化 + +- 容量处理集成:`tasks.py` 和 `remote.py` 中的 `refresh_cell_data()` 调用改为 `_try_refresh_cell_data()` 包装函数,CellData 数据源未启用时直接跳过,CellData 处理失败时记录警告但继续执行容量处理,不再因 CellData SFTP 连接失败或无数据等原因导致整个容量处理任务失败。 +- CellData 阶段标识:容量处理流程中 CellData 更新阶段会设置独立的 `cell_data` stage,前端可显示"更新 CellData..."状态文案;CellData 完成后日志输出导入行数、解析行数和跳过行数摘要。 +- 前端阶段标签:`stageLabels` 新增 `cell_data: '更新 CellData...'`;`importing` 标签从"上传数据中..."改为"导入数据中..."以避免与文件上传混淆。 +- 前端结果摘要:CellData 独立处理或容量处理完成后,处理进度区域显示成功摘要(文件数、导入行数、跳过行数、耗时);`TaskStatus` 类型新增 `result?: CellDataResult` 字段。 +- 验证:`python -m compileall -q app` 通过;`frontend` `npm run build`(vue-tsc)通过;构建产物已清理。 diff --git a/frontend/src/components/FileWorkflow.vue b/frontend/src/components/FileWorkflow.vue index b1dde50..51c27a4 100644 --- a/frontend/src/components/FileWorkflow.vue +++ b/frontend/src/components/FileWorkflow.vue @@ -213,6 +213,9 @@ 当前任务:{{ activeTask.task_id }} / {{ currentStageText }} + + {{ taskResultSummary }} + {{ taskStatus.error }} @@ -326,7 +329,7 @@ import { } from '@vicons/ionicons5'; import { ApiRequestError, apiGet, apiPost, upload } from '../api/client'; -import type { ActiveTask, ApiErrorDetail, LicenseStatus, TaskStatus } from '../types'; +import type { ActiveTask, ApiErrorDetail, CellDataResult, LicenseStatus, TaskStatus } from '../types'; import { toColoredLogLines } from '../composables/logLines'; type FileStatus = 'pending' | 'uploading' | 'uploaded' | 'error'; @@ -380,6 +383,7 @@ let timer: number | undefined; const stageLabels: Record = { license: '授权校验中...', + cell_data: '更新 CellData...', locating: '定位数据中...', downloading: '远程下载中...', uploading: '上传文件中...', @@ -387,7 +391,7 @@ const stageLabels: Record = { extracting: '解压数据中...', converting: '转换 Excel 中...', parsing: '解析数据中...', - importing: '上传数据中...', + importing: '导入数据中...', scripting: '运行脚本中...', completed: '处理完成', failed: '处理失败' @@ -420,6 +424,18 @@ const processStatusText = computed(() => { if (taskStatus.value?.status === 'failed') return '处理失败'; return currentStageText.value; }); +const taskResultSummary = computed(() => { + const status = taskStatus.value; + if (!status || status.status !== 'completed') return ''; + const parts: string[] = []; + if (status.result) { + parts.push(formatCellDataResult(status.result)); + } + if (status.elapsed_time != null) { + parts.push(`耗时 ${status.elapsed_time.toFixed(1)}s`); + } + return parts.join(','); +}); watch( () => taskStatus.value?.logs?.length ?? 0, @@ -969,6 +985,14 @@ function fileStatusText(item: PickedFile): string { return '等待上传'; } +function formatCellDataResult(result: CellDataResult): string { + const parts: string[] = []; + if (result.selected_files > 0) parts.push(`${result.selected_files} 个文件`); + parts.push(`导入 ${result.imported_rows} 行`); + if (result.skipped_rows > 0) parts.push(`跳过 ${result.skipped_rows} 行`); + return parts.join(','); +} + function formatBytes(size: number): string { if (size === 0) return '0 B'; const units = ['B', 'KB', 'MB', 'GB']; diff --git a/frontend/src/types.ts b/frontend/src/types.ts index e566cde..e3428ea 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -27,6 +27,13 @@ export interface LicenseStatus { zip_count?: number; } +export interface CellDataResult { + selected_files: number; + parsed_rows: number; + imported_rows: number; + skipped_rows: number; +} + export interface TaskStatus { task_id: string; status: 'pending' | 'processing' | 'completed' | 'failed' | string; @@ -35,6 +42,7 @@ export interface TaskStatus { elapsed_time?: number; error?: string; error_detail?: ApiErrorDetail; + result?: CellDataResult; } export interface ActiveTask {