diff --git a/admin/dashboard/blueprints/plugin_schedules.py b/admin/dashboard/blueprints/plugin_schedules.py index 3f4a7b2..aed8c00 100644 --- a/admin/dashboard/blueprints/plugin_schedules.py +++ b/admin/dashboard/blueprints/plugin_schedules.py @@ -40,7 +40,7 @@ def api_list_schedules(): data = server.plugin_schedule_manager.list_schedules_with_runtime() # 后端统一格式化时间字段,避免前端出现 Fri, 17 Apr 2026 ... 这类 RFC 时间串。 for row in data: - for key in ("next_run_at", "last_run_at", "latest_success_at", "latest_failed_at", "created_at", "updated_at"): + for key in ("next_run_at", "last_run_at", "created_at", "updated_at"): if key in row: row[key] = _normalize_datetime_text(row.get(key)) return jsonify({"success": True, "data": data}) diff --git a/admin/dashboard/blueprints/system_jobs.py b/admin/dashboard/blueprints/system_jobs.py index 54fdfa3..d994e2f 100644 --- a/admin/dashboard/blueprints/system_jobs.py +++ b/admin/dashboard/blueprints/system_jobs.py @@ -21,40 +21,6 @@ def _normalize_datetime_text(value): return text -def _build_job_health_status(*, enabled: bool, running: bool, last_status: str, latest_success_at, latest_failure_summary: str) -> str: - """根据任务启停、运行态和历史结果输出后台可读的健康状态。""" - # 状态设计尽量贴近运维判断顺序: - # 1. 停用态单独标记,避免和“从未执行”混淆; - # 2. 执行中的任务优先展示 running,方便后台快速识别实时动作; - # 3. 最近一次执行失败时直接标记 failed,让异常任务在列表里一眼可见; - # 4. 有成功历史且最近不是失败时视为 healthy,否则落到 idle。 - if not enabled: - return "disabled" - if running: - return "running" - if str(last_status or "").strip().lower() == "failed": - return "failed" - if latest_success_at or str(last_status or "").strip().lower() == "success": - return "healthy" - if str(latest_failure_summary or "").strip(): - return "failed" - return "idle" - - -def _build_job_health_message(*, health_status: str, latest_success_at, latest_failure_summary: str) -> str: - """为后台列表生成一句简短的任务健康提示。""" - if health_status == "disabled": - return "任务已停用" - if health_status == "running": - return "任务正在执行中" - if health_status == "failed": - return str(latest_failure_summary or "最近一次执行失败").strip() - if health_status == "healthy": - success_text = _normalize_datetime_text(latest_success_at) - return f"最近成功于 {success_text}" if success_text else "任务近期执行正常" - return "暂无执行记录" - - @system_jobs_bp.route("/") @login_required def page_system_jobs(): @@ -68,31 +34,11 @@ def api_list_jobs(): db_rows = server.system_job_db.list_jobs() runtime_rows = async_job.get_jobs_snapshot() runtime_by_key = {row.get("job_key", ""): row for row in runtime_rows if row.get("job_key")} - job_keys = [str(row.get("job_key") or "").strip() for row in db_rows if str(row.get("job_key") or "").strip()] - latest_log_by_key = server.system_job_db.get_latest_logs_map(job_keys) - history_summary_by_key = server.system_job_db.get_job_history_summary_map(job_keys) result = [] for row in db_rows: job_key = row.get("job_key") runtime = runtime_by_key.get(job_key, {}) - latest_log = latest_log_by_key.get(job_key, {}) - history_summary = history_summary_by_key.get(job_key, {}) - last_status = runtime.get("last_status") or latest_log.get("status") or "never" - last_run_at = runtime.get("last_run_at") or latest_log.get("triggered_at") - last_error = runtime.get("last_error") or "" - if not last_error and str(last_status or "").strip().lower() == "failed": - last_error = ( - str(latest_log.get("summary") or "").strip() - or str(history_summary.get("latest_failure_summary") or "").strip() - ) - health_status = _build_job_health_status( - enabled=bool(row.get("enabled", 0)), - running=bool(runtime.get("running", False)), - last_status=str(last_status or ""), - latest_success_at=history_summary.get("latest_success_at"), - latest_failure_summary=str(history_summary.get("latest_failure_summary") or ""), - ) result.append( { "job_key": job_key, @@ -105,26 +51,14 @@ def api_list_jobs(): "runtime_enabled": runtime.get("enabled"), "running": runtime.get("running", False), "trigger_text": runtime.get("trigger_text", ""), - "last_run_at": _normalize_datetime_text(last_run_at), - "last_status": last_status, - "last_error": last_error, - "last_duration_ms": runtime.get("last_duration_ms") or latest_log.get("duration_ms"), + "last_run_at": _normalize_datetime_text(runtime.get("last_run_at")), + "last_status": runtime.get("last_status"), + "last_error": runtime.get("last_error"), + "last_duration_ms": runtime.get("last_duration_ms"), "next_run_at": _normalize_datetime_text(runtime.get("next_run_at")), "run_count": runtime.get("run_count", 0), "success_count": runtime.get("success_count", 0), "fail_count": runtime.get("fail_count", 0), - "latest_success_at": _normalize_datetime_text(history_summary.get("latest_success_at")), - "latest_failed_at": _normalize_datetime_text(history_summary.get("latest_failed_at")), - "latest_failure_summary": str(history_summary.get("latest_failure_summary") or "").strip(), - "history_success_count": int(history_summary.get("history_success_count", 0) or 0), - "history_fail_count": int(history_summary.get("history_fail_count", 0) or 0), - "history_total_count": int(history_summary.get("history_total_count", 0) or 0), - "health_status": health_status, - "health_message": _build_job_health_message( - health_status=health_status, - latest_success_at=history_summary.get("latest_success_at"), - latest_failure_summary=str(history_summary.get("latest_failure_summary") or "").strip(), - ), } ) diff --git a/admin/dashboard/templates/plugin_schedules.html b/admin/dashboard/templates/plugin_schedules.html index cc792da..1952250 100644 --- a/admin/dashboard/templates/plugin_schedules.html +++ b/admin/dashboard/templates/plugin_schedules.html @@ -42,44 +42,11 @@ {% raw %}{{ scope.row.last_status || 'never' }}{% endraw %} - - - - - - - - - - - - - + @@ -230,25 +197,6 @@ new Vue({ if (status === 'running') return 'warning' return 'info' }, - healthTag(status) { - if (status === 'healthy') return 'success' - if (status === 'running') return 'warning' - if (status === 'failed') return 'danger' - if (status === 'degraded') return 'warning' - if (status === 'disabled') return 'info' - return '' - }, - healthLabel(status) { - const mapping = { - healthy: '健康', - running: '执行中', - failed: '异常', - degraded: '有告警', - disabled: '停用', - idle: '待运行' - } - return mapping[status] || '待运行' - }, formatDateTime(value) { // 统一清洗时间展示:去掉 ISO 'T',并兼容字符串与日期对象。 if (!value) return '' @@ -382,25 +330,6 @@ new Vue({ } await this.loadSchedules() }, - async toggleEnabled(row) { - const payload = { - action_name: row.action_name, - description: row.description, - enabled: !row.enabled, - trigger_type: row.trigger_type, - trigger_config: row.trigger_config, - target_scope: row.target_scope, - target_config: row.target_config, - payload: row.payload || {} - } - const resp = await axios.put(`/plugin_schedules/api/schedules/${row.id}`, payload) - if (resp.data.success) { - this.$message.success(row.enabled ? '已停用' : '已启用') - await this.loadSchedules() - } else { - this.$message.error(resp.data.message || '更新失败') - } - }, async viewLogs(row) { const resp = await axios.get(`/plugin_schedules/api/schedules/${row.id}/logs`) if (resp.data.success) { @@ -423,10 +352,5 @@ new Vue({ .page-hero-copy p{color:#64748b;font-size:14px} .action-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap} .detail-pre{white-space:pre-wrap;word-break:break-word;background:rgba(248,250,252,.85);border:1px solid rgba(148,163,184,.12);border-radius:14px;padding:10px;color:#334155} -.cell-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#475569} -.history-metrics{display:flex;align-items:center;justify-content:center;gap:8px} -.metric-success{color:#16a34a;font-weight:600} -.metric-fail{color:#dc2626;font-weight:600} -.history-total{margin-top:4px;color:#64748b;font-size:12px} {% endblock %} diff --git a/admin/dashboard/templates/system_jobs.html b/admin/dashboard/templates/system_jobs.html index af46ddd..35d3de9 100644 --- a/admin/dashboard/templates/system_jobs.html +++ b/admin/dashboard/templates/system_jobs.html @@ -33,28 +33,6 @@ {% raw %}{{ scope.row.last_status || 'never' }}{% endraw %} - - - - - - - - - -