From 272ff38070a076ba356f3eea324fa0600c384ffd Mon Sep 17 00:00:00 2001 From: Nixevol Date: Mon, 25 May 2026 17:54:56 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=B7=B3=E8=BF=87=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E7=A9=BA=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ app/services/auto_scheduler.py | 34 ++++++++++++++++++++--- docs/auto_scheduler_plan.md | 3 +- docs/project_context.md | 1 + frontend/src/components/SettingsPanel.vue | 4 +-- frontend/src/types.ts | 2 ++ 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a5d0dfe..dd08e83 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,8 @@ Server Portable 和桌面版需要在目标系统原生构建:Windows 包在 W 自动调度开启后,系统会强制开启 `RemoteData.enabled` 和 `auto_delete_source`。调度器每轮先检查 `cache/auto_scheduler/ready.flag`;如果标识存在则直接触发远程下载并处理。没有标识时,会按 ZIP 文件名中的第一个时间戳判断每个目录是否覆盖目标自然周 7 天;全部就绪后写入标识,下一个检查周期再启动处理。处理成功并完成远程源文件清理后会删除标识;处理失败或源文件清理失败会保留标识,后续自动重试。 +如果配置了 `expected_directories`,其中某个目录可访问但完全没有 ZIP 文件,会视为该目录已停推并跳过,不再阻塞其他目录;但所有目录都为空时不会触发自动处理。 + 无论手动上传还是远程下载,处理流程都会按文件名日期对每个目录只保留最近 7 天文件。文件名支持 `XXX_YYYYMMDDHHMM_YYYYMMDDHHMM` 和 `XXX_YYYYMMDDHHMM` 两类格式,数据日期始终取第一个时间戳。 登录密码保存在本地 `auth.ini`,该文件不应提交到版本库。 diff --git a/app/services/auto_scheduler.py b/app/services/auto_scheduler.py index 735d960..a79d9a8 100644 --- a/app/services/auto_scheduler.py +++ b/app/services/auto_scheduler.py @@ -30,6 +30,8 @@ class DirectoryReadyStatus: missing_days: list[date] file_count: int error: str | None = None + skipped: bool = False + skip_reason: str | None = None def to_dict(self) -> dict[str, Any]: return { @@ -40,6 +42,8 @@ class DirectoryReadyStatus: "required_count": len(self.found_days) + len(self.missing_days), "file_count": self.file_count, "error": self.error, + "skipped": self.skipped, + "skip_reason": self.skip_reason, } @@ -145,7 +149,6 @@ class AutoScheduler: target_days = required_week_days(scheduler.week_offset) directory_status = self._check_remote_ready(remote_config, scheduler, target_days) - ready = bool(directory_status) and all(item.ready for item in directory_status.values()) self._set_directory_status(directory_status) error_count = sum(1 for item in directory_status.values() if item.error) @@ -156,18 +159,30 @@ class AutoScheduler: manual, ) + active_status = [item for item in directory_status.values() if not item.skipped] + skipped_count = len(directory_status) - len(active_status) + ready = bool(active_status) and all(item.ready for item in active_status) if ready: self._mark_ready(target_days, directory_status) + skipped_text = f",已跳过 {skipped_count} 个停推目录" if skipped_count else "" return self._finish_check( "marked_ready", - "远程数据已满足目标周 7 天,已写入就绪标识,下次检查将自动处理", + f"远程数据已满足目标周 7 天{skipped_text},已写入就绪标识,下次检查将自动处理", manual, ) - ready_count = sum(1 for item in directory_status.values() if item.ready) + if not active_status and directory_status: + return self._finish_check( + "waiting", + f"远程数据未就绪,{len(directory_status)} 个目录均为空,已视为停推但不会触发处理", + manual, + ) + + ready_count = sum(1 for item in active_status if item.ready) + skipped_text = f",跳过 {skipped_count} 个停推目录" if skipped_count else "" return self._finish_check( "waiting", - f"远程数据未就绪,{ready_count}/{len(directory_status)} 个目录满足目标周 7 天", + f"远程数据未就绪,{ready_count}/{len(active_status)} 个有效目录满足目标周 7 天{skipped_text}", manual, ) @@ -249,6 +264,17 @@ class AutoScheduler: error="远程目录不存在或无法访问", ) + if not files: + return DirectoryReadyStatus( + directory=directory, + ready=True, + found_days=[], + missing_days=[], + file_count=0, + skipped=True, + skip_reason="目录为空,视为已停推并跳过", + ) + required = set(target_days) found = { file_date diff --git a/docs/auto_scheduler_plan.md b/docs/auto_scheduler_plan.md index 5611d02..c5209a8 100644 --- a/docs/auto_scheduler_plan.md +++ b/docs/auto_scheduler_plan.md @@ -202,6 +202,7 @@ def get_target_week_range(week_offset: int = 0) -> tuple[date, date]: 1. **自动调度开启时,`auto_delete_source` 强制为 `true`**(前端灰显、后端校验) 2. `expected_directories` 为空时按远程 ZIP 实际所在目录逐个检测 3. `check_interval_hours` 最小值为 1 +4. 配置了 `expected_directories` 时,某个预期目录可访问但完全没有 ZIP 文件,视为该目录已停推并跳过;所有目录都为空时不触发自动处理 --- @@ -308,7 +309,7 @@ GET /api/remote/scheduler/status | 连续失败 ≥3 次 | 在前端调度状态面板显示红色失败状态 | | 处理失败 | **保留就绪标识**,下次检查时直接触发处理(不重新检测) | | 远程目录不存在 | 跳过该目录,记录警告,其他目录正常检测 | -| 目录内无 ZIP 文件 | 该目录判定为未就绪 | +| 预期目录内无 ZIP 文件 | 视为该目录已停推并跳过,不阻塞其他目录;如果所有目录都为空,则继续等待,不触发处理 | | 手动触发远程处理 | 自动调度运行中禁止手动触发,反之亦然(互斥锁复用 `global_task_lock`) | --- diff --git a/docs/project_context.md b/docs/project_context.md index 2d6b0ef..e337cad 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -10,6 +10,7 @@ - `app/api/routers/remote.py` 新增 `/api/remote/scheduler/status` 和 `/api/remote/scheduler/trigger`,并将远程处理启动逻辑抽成 `start_remote_processing_job()` 供手动按钮和调度器复用。 - `frontend/src/components/SettingsPanel.vue` 在远程数据源配置中加入自动调度区域:启用开关、检查间隔、目标周期、预期目录维护、调度状态、刷新状态和立即检查。配置下载/上传会随 `RemoteData` 一起携带自动调度配置。 - 已验证:文件名日期解析、目标周计算、每目录 7 天筛选、调度开启强制删除源文件配置、调度日期精确筛选逻辑均通过临时 Python 片段;`.venv\Scripts\python.exe -m compileall app` 与 `npm run build` 通过,前端构建仅保留既有 Vite 大 chunk 警告。 +- 后续调整:预期目录可访问但完全没有 ZIP 文件时,自动调度会把该目录标记为 `skipped/已停推`,不再阻塞其他目录就绪;但所有目录都为空时不会写入 ready flag,也不会触发处理。 ## 2026-05-25:修复 API Token 指定日期输入不可见 diff --git a/frontend/src/components/SettingsPanel.vue b/frontend/src/components/SettingsPanel.vue index 72d96ea..8deccc3 100644 --- a/frontend/src/components/SettingsPanel.vue +++ b/frontend/src/components/SettingsPanel.vue @@ -230,8 +230,8 @@
{{ row.name }} - - {{ row.ready ? '就绪' : `缺 ${row.missing_days.length} 天` }} + + {{ row.skipped ? '已停推' : row.ready ? '就绪' : `缺 ${row.missing_days.length} 天` }}
diff --git a/frontend/src/types.ts b/frontend/src/types.ts index cf48c16..2f15e05 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -140,6 +140,8 @@ export interface RemoteSchedulerStatus { required_count: number; file_count: number; error?: string | null; + skipped?: boolean; + skip_reason?: string | null; } >; message?: string;