fix(system): three next-runs, completed-date data_date, running mark

Split /system schedule into fans / snapshot+UP / discovery next-runs.
Drive data_date and snapshot_days from fully completed video snapshots
only, keep prior success date on failure, show task failure records, and
mark in-progress tasks as 运行中 without wall-clock next-run jumps.
This commit is contained in:
bot-ci committed 2026-11-02 00:46:20 +08:00
1 parent 0b92e10317
commit af90ce8f87
9 files changed
+411 -52

No files matched your search

+44 -12
View File
@@ -227,15 +227,20 @@ class DashboardQueries:
@staticmethod
def _latest_date(conn: sqlite3.Connection) -> str | None:
"""数据日期:仅取视频快照任务完整成功后的完成日,避免采集中途抢跑。"""
row = conn.execute(
"SELECT MAX(stat_date) AS d FROM snapshot WHERE ok = 1"
"SELECT v FROM kv WHERE k = 'snapshot:last_completed'"
).fetchone()
return None if row is None or row["d"] is None else str(row["d"])
if row is not None and row["v"]:
return str(row["v"])
return None
@staticmethod
def _snapshot_days(conn: sqlite3.Connection) -> int:
"""快照天数:统计已完整完成的采集日(completed=1),与数据日期口径一致。"""
row = conn.execute(
"SELECT COUNT(DISTINCT stat_date) AS n FROM snapshot WHERE ok = 1"
"SELECT COUNT(*) AS n FROM kv "
"WHERE k LIKE 'snapshot:completed:%' AND v = '1'"
).fetchone()
return int(row["n"] or 0)
@@ -580,6 +585,23 @@ class DashboardQueries:
result[name] = value
return result
def _task_running(self, conn: sqlite3.Connection) -> dict[str, Any]:
rows = conn.execute(
"SELECT k, v FROM kv WHERE k LIKE 'task:running:%' ORDER BY k"
).fetchall()
result: dict[str, Any] = {}
for row in rows:
name = str(row["k"]).removeprefix("task:running:")
try:
value = json.loads(str(row["v"]))
except (TypeError, json.JSONDecodeError):
value = {"raw": row["v"]}
if not isinstance(value, dict):
value = {"raw": value}
value["running"] = True
result[name] = value
return result
def _folders_overview(self, conn: sqlite3.Connection) -> list[dict[str, Any]]:
rows = conn.execute(
"""
@@ -1722,21 +1744,30 @@ class DashboardQueries:
).fetchall()
]
tasks = self._task_records(conn)
running = self._task_running(conn)
now = self._now()
discover_interval = int(cfg.schedule.discover_minutes) * 60
next_runs = {
"discover": int(now + cfg.schedule.discover_minutes * 60),
"fans": int(next_daily_run(now, cfg.schedule.snapshot_at)),
"snapshot": int(next_daily_run(now, cfg.schedule.snapshot_at)),
"discover": int(now + discover_interval),
}
for name, key, interval_minutes in (
("discover", "discovery", cfg.schedule.discover_minutes),
for name, key in (
("fans", "fans"),
("snapshot", "snapshot"),
("discover", "discovery"),
):
record = tasks.get(key)
if isinstance(record, dict) and record.get("finished_at"):
interval = int(interval_minutes) * 60
candidate = int(record["finished_at"]) + interval
while candidate <= now:
candidate += interval
next_runs[name] = candidate
if not (isinstance(record, dict) and record.get("finished_at")):
continue
finished_at = int(record["finished_at"])
if name == "discover":
# 只按上次完成推下一档;进行中由 running 展示,不按墙钟抢跑。
next_runs[name] = finished_at + discover_interval
else:
next_runs[name] = int(
next_daily_run(finished_at, cfg.schedule.snapshot_at)
)
errors = [
{"task": name, **record}
for name, record in tasks.items()
@@ -1754,6 +1785,7 @@ class DashboardQueries:
"counts": counts,
"blacklist": blacklist,
"tasks": tasks,
"running": running,
"errors": errors,
"next_runs": next_runs,
"auth": {