@
feat: shadcn主题 + 文件关系追踪 + 处理流程修复 前端: - 全站应用 shadcn/ui 主题 (zinc灰调, Inter字体, 1px细边框, 无硬阴影) - 重写 global.css / Dashboard.vue / Login.vue / Layout.vue 样式 - 新增文件处理子页面: 采购单(Orders), 表格处理(Tables), 图片处理(Images) - 侧边栏使用 el-sub-menu 组织文件处理导航 后端: - 新增 file_relations 表追踪 input→output→result 链路 - 新增 /files/relations, /files/stats/detailed 等关系查询API - 新增 ocr-single, excel-single, pipeline-single, merge-batch 端点 - 处理流程增加跳过逻辑 (已处理文件自动跳过) - 全流程不再自动合并, 合并仅在采购单页面手动触发 Bug修复: - TaskManager: asyncio.create_task 在线程池中无事件循环 → 改用 _schedule() 调度 - PurchaseOrderMerger 缺少 config 参数 → 传入 ConfigManager() - FastAPI regex= 弃用 → 改为 pattern= - merger.process() 接收 Path 对象 → 转为字符串 @
This commit is contained in:
@@ -47,10 +47,25 @@ class TaskManager:
|
||||
self._tasks: Dict[str, Task] = {}
|
||||
self._connections: Dict[str, Set] = {}
|
||||
self._db = None # type: ignore
|
||||
self._loop = None # captured event loop
|
||||
|
||||
def set_db_pool(self, db_pool):
|
||||
"""Set the DBPool reference for database persistence."""
|
||||
self._db = db_pool
|
||||
try:
|
||||
self._loop = asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def _schedule(self, coro):
|
||||
"""Schedule a coroutine from either async or thread context."""
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
asyncio.ensure_future(coro, loop=loop)
|
||||
except RuntimeError:
|
||||
# No running loop — we're in a thread; schedule onto the main loop
|
||||
if self._loop and self._loop.is_running():
|
||||
asyncio.run_coroutine_threadsafe(coro, self._loop)
|
||||
|
||||
def create_task(self, name: str) -> Task:
|
||||
task_id = str(uuid.uuid4())[:8]
|
||||
@@ -58,7 +73,7 @@ class TaskManager:
|
||||
self._tasks[task_id] = task
|
||||
self._connections[task_id] = set()
|
||||
if self._db:
|
||||
asyncio.create_task(
|
||||
self._schedule(
|
||||
self._db.execute_write(insert_task, task_id, name, TaskStatus.PENDING.value)
|
||||
)
|
||||
return task
|
||||
@@ -76,13 +91,13 @@ class TaskManager:
|
||||
task.progress = progress
|
||||
task.message = message
|
||||
if self._db:
|
||||
asyncio.create_task(
|
||||
self._schedule(
|
||||
self._db.execute_write(
|
||||
update_task, task_id,
|
||||
status=task.status.value, progress=progress, message=message,
|
||||
)
|
||||
)
|
||||
asyncio.create_task(self._broadcast(task_id))
|
||||
self._schedule(self._broadcast(task_id))
|
||||
|
||||
def add_log(self, task_id: str, line: str):
|
||||
task = self._tasks.get(task_id)
|
||||
@@ -90,13 +105,13 @@ class TaskManager:
|
||||
return
|
||||
task.log_lines.append(line)
|
||||
if self._db:
|
||||
asyncio.create_task(
|
||||
self._schedule(
|
||||
self._db.execute_write(
|
||||
update_task, task_id,
|
||||
log_lines=json.dumps(task.log_lines[-200:]),
|
||||
)
|
||||
)
|
||||
asyncio.create_task(self._broadcast(task_id))
|
||||
self._schedule(self._broadcast(task_id))
|
||||
|
||||
def set_completed(self, task_id: str, result_files: List[str] = None, message: str = ""):
|
||||
task = self._tasks.get(task_id)
|
||||
@@ -109,7 +124,7 @@ class TaskManager:
|
||||
task.result_files = result_files
|
||||
now = datetime.now().isoformat()
|
||||
if self._db:
|
||||
asyncio.create_task(
|
||||
self._schedule(
|
||||
self._db.execute_write(
|
||||
update_task, task_id,
|
||||
status=TaskStatus.COMPLETED.value, progress=100,
|
||||
@@ -118,7 +133,7 @@ class TaskManager:
|
||||
completed_at=now,
|
||||
)
|
||||
)
|
||||
asyncio.create_task(self._broadcast(task_id))
|
||||
self._schedule(self._broadcast(task_id))
|
||||
|
||||
def set_failed(self, task_id: str, error: str):
|
||||
task = self._tasks.get(task_id)
|
||||
@@ -129,14 +144,14 @@ class TaskManager:
|
||||
task.message = f"处理失败: {error}"
|
||||
now = datetime.now().isoformat()
|
||||
if self._db:
|
||||
asyncio.create_task(
|
||||
self._schedule(
|
||||
self._db.execute_write(
|
||||
update_task, task_id,
|
||||
status=TaskStatus.FAILED.value, error=error,
|
||||
message=task.message, completed_at=now,
|
||||
)
|
||||
)
|
||||
asyncio.create_task(self._broadcast(task_id))
|
||||
self._schedule(self._broadcast(task_id))
|
||||
|
||||
def subscribe(self, task_id: str, websocket):
|
||||
if task_id in self._connections:
|
||||
|
||||
Reference in New Issue
Block a user