feat(all): 源精度命名体系、工作流可观测台、节点停用管理与白名单归档

核心变更:

  1. GridAxisValue 源精度命名
     - 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
     - config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
     - runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
       修复 REAL 列回读丢精度导致的 model_name 错配

  2. 工作流执行可观测台
     - 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
       停滞预警、逐点明细分页、收敛性热力图数据)
     - 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
     - runner 携带 last_iter/worst_depth/n_depths 进 conv.json
     - 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器

  3. 节点停用/启用管理
     - 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
       worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
       移除 host_name 字段

  4. 白名单结果归档
     - 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
     - executor 原子写入归档 + 200 点 LRU 上限

  5. 历史数据导入
     - sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
       按新版命名迁移产物树

  6. 部署与目录重规划
     - data/results→seeds、data/archive→result + migrate_data_dirs.sh
     - deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身

  7. 文档同步更新 api/database/architecture/deployment
This commit is contained in:
fmq
2026-07-31 01:34:05 +08:00
parent b91f1e4fa5
commit 1bfa240cb0
73 changed files with 12332 additions and 1608 deletions
+96 -27
View File
@@ -6,14 +6,24 @@
<title>DCTS — 分布式恒星大气网格计算控制台</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
<script>
/* 首帧前落主题,避免 prefers-color-scheme 先绘制导致深/浅闪变 (FOUC) */
(function () {
try {
var saved = localStorage.getItem('dcts_theme');
var theme = saved || (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
} catch (e) { /* localStorage 不可用时回退媒体查询 */ }
})();
</script>
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<div id="app">
<!-- ===== 登录遮罩(鉴权) ===== -->
<div id="login-overlay" class="login-overlay">
<div class="login-card glass">
<div class="login-card card">
<div class="login-header">
<h2>DCTS 控制台登录</h2>
<p>请输入管理员密码以访问管理控制台</p>
@@ -21,7 +31,7 @@
<form id="form-login" class="stack-col stack-col-md">
<div class="form-group">
<label for="input-admin-token">管理员密码</label>
<input type="password" id="input-admin-token" class="form-input" placeholder="输入管理员密码..." required autocomplete="off" />
<input type="password" id="input-admin-token" class="form-input" placeholder="输入管理员密码..." required autocomplete="current-password" />
</div>
<div id="login-error-msg" class="login-error-msg alert-error hidden"></div>
<button type="submit" id="btn-submit-login" class="btn btn-primary">登录</button>
@@ -51,7 +61,7 @@
</div>
<div class="header-status">
<div class="status-indicator online" id="status-indicator-box">
<div class="status-indicator online" id="status-indicator-box" role="status" aria-live="polite">
<span class="status-dot"></span>
<span id="server-status-text">服务端正常运行</span>
</div>
@@ -69,6 +79,22 @@
</svg>
刷新
</button>
<button id="btn-theme-toggle" class="btn btn-secondary" title="切换主题模式" aria-label="切换主题模式">
<svg class="icon-theme-sun hidden" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
<svg class="icon-theme-moon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</button>
<button id="btn-logout" class="btn btn-secondary hidden" title="登出并清除 Token">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
@@ -80,11 +106,16 @@
</div>
</header>
<!-- 主内容区 -->
<!-- 主内容区(视图由 src/router.js 挂载到 #view-root:首页 / 工作流详情页) -->
<main class="main-container">
<div id="view-root"></div>
</main>
<!-- 首页视图模板:router 挂载时克隆(见 src/views/home.js),ID 为渲染锚点,保持原样 -->
<template id="view-home-template">
<!-- 核心指标卡片 -->
<section class="metrics-grid">
<div class="metric-card glass">
<div class="metric-card card">
<div class="metric-header">
<span class="metric-title">在线计算节点</span>
<div class="metric-icon nodes-icon">
@@ -99,10 +130,13 @@
<div class="metric-body">
<span class="metric-value" id="val-active-nodes">0</span>
<span class="metric-sub" id="val-total-slots">0 / 0 CPU 槽位占用</span>
<div class="progress-bar-track" aria-hidden="true">
<div class="progress-bar-fill" id="slots-progress-fill"></div>
</div>
</div>
</div>
<div class="metric-card glass">
<div class="metric-card card">
<div class="metric-header">
<span class="metric-title">待计算网格点</span>
<div class="metric-icon pending-icon">
@@ -118,7 +152,7 @@
</div>
</div>
<div class="metric-card glass">
<div class="metric-card card">
<div class="metric-header">
<span class="metric-title">已计算网格模型</span>
<div class="metric-icon success-icon">
@@ -131,10 +165,13 @@
<div class="metric-body">
<span class="metric-value" id="val-completed-tasks">0</span>
<span class="metric-sub" id="val-completion-rate">网格总数: 0</span>
<div class="progress-bar-track" aria-hidden="true">
<div class="progress-bar-fill progress-fill-emerald" id="converged-progress-fill"></div>
</div>
</div>
</div>
<div class="metric-card glass">
<div class="metric-card card">
<div class="metric-header">
<span class="metric-title">计算工作流</span>
<div class="metric-icon workflow-icon">
@@ -155,7 +192,7 @@
<!-- 主要内容双栏 -->
<div class="content-grid">
<!-- 左侧:计算节点集群与凭据管理 (统一全功能控制大表) -->
<div class="panel glass">
<div class="panel card">
<div class="panel-header">
<h2>计算节点集群与凭据管理</h2>
<div class="row-wrap" style="align-items: center; gap: 0.5rem;">
@@ -168,7 +205,6 @@
<thead>
<tr>
<th class="col-node-id">节点 ID</th>
<th class="col-hostname">主机名</th>
<th class="col-slots">CPU 槽位</th>
<th class="col-usage">CPU/内存</th>
<th class="col-status">节点状态</th>
@@ -179,7 +215,7 @@
</thead>
<tbody id="nodes-table-body">
<tr>
<td colspan="8" class="empty-cell">正在连接服务端获取计算节点...</td>
<td colspan="7" class="empty-cell">正在连接服务端获取计算节点...</td>
</tr>
</tbody>
</table>
@@ -189,7 +225,7 @@
<!-- 右侧:工作流管理 -->
<div class="stack-col stack-col-lg">
<!-- 工作流面板 -->
<div class="panel glass">
<div class="panel card">
<div class="panel-header">
<h2>恒星大气网格工作流</h2>
</div>
@@ -201,10 +237,10 @@
</div>
</div>
</div>
</main>
</template>
<!-- 创建 / 编辑工作流 模态弹窗 -->
<div id="modal-create-wf" class="modal-backdrop hidden">
<div id="modal-create-wf" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal-card">
<div class="modal-header">
<h3 id="modal-title">注册新的计算工作流</h3>
@@ -222,7 +258,10 @@
<input type="text" id="input-wf-desc" class="form-input" placeholder="例如: sdB 6维恒星大气模型计算网格" />
</div>
<div class="form-group">
<label for="input-wf-yaml">YAML 配置内容 (GridConfig)</label>
<div class="row-wrap" style="justify-content: space-between; align-items: center;">
<label for="input-wf-yaml">YAML 配置内容 (GridConfig)</label>
<button type="button" id="btn-load-preset-yaml" class="btn btn-secondary btn-sm">填入默认示例</button>
</div>
<textarea id="input-wf-yaml" class="form-textarea" required placeholder="请粘贴工作流 YAML 参数网格定义..."></textarea>
</div>
<div class="modal-footer">
@@ -233,27 +272,57 @@
</div>
</div>
<!-- 查看 YAML 模态弹窗 -->
<div id="modal-view-wf" class="modal-backdrop hidden">
<div class="modal-card">
<!-- 工作流 YAML 配置编辑器(查看/编辑双模式,逻辑见 src/components/yamlEditor.js -->
<div id="modal-view-wf" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="view-wf-title">
<div class="modal-card modal-wide yaml-editor">
<div class="modal-header">
<h3 id="view-wf-title">工作流 YAML 配置详情</h3>
<div class="yaml-editor-head">
<h3 id="view-wf-title">工作流配置</h3>
<span id="yaml-editor-status"></span>
<span id="yaml-editor-dirty" class="dirty-dot hidden" title="有未保存的修改" aria-label="有未保存的修改"></span>
</div>
<button id="close-view-wf" class="modal-close" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<pre id="view-wf-yaml" class="code-pre"></pre>
<div class="modal-footer">
<button type="button" id="btn-close-view-wf" class="btn btn-secondary">关闭</button>
<!-- 查看模式:语法高亮 + 行号 -->
<div id="yaml-view-pane" class="yaml-view-pane">
<div class="yaml-scroll"><pre id="view-wf-yaml" class="yaml-highlight"></pre></div>
</div>
<!-- 编辑模式:等宽编辑区 + 校验错误/状态重置提示 -->
<div id="yaml-edit-pane" class="yaml-edit-pane hidden">
<div id="yaml-edit-error" class="yaml-edit-error hidden" role="alert"></div>
<div id="yaml-reset-warn" class="yaml-reset-warn hidden">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<span>保存后工作流状态将重置为「闲置」,需重新启动才能使新配置生效</span>
</div>
<textarea id="yaml-edit-area" class="yaml-edit-area" spellcheck="false" aria-label="YAML 配置编辑区"></textarea>
</div>
<div class="modal-footer yaml-editor-footer">
<span id="yaml-editor-meta" class="yaml-editor-meta tabular-num"></span>
<span id="yaml-editor-lock" class="yaml-editor-lock hidden"></span>
<button type="button" id="btn-edit-yaml" class="btn btn-primary btn-sm">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>
编辑
</button>
<button type="button" id="btn-cancel-edit-yaml" class="btn btn-secondary btn-sm hidden">取消</button>
<button type="button" id="btn-save-yaml" class="btn btn-primary btn-sm hidden">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
保存配置
</button>
<button type="button" id="btn-close-view-wf" class="btn btn-secondary btn-sm">关闭</button>
</div>
</div>
</div>
<!-- 重发 Token 结果展示模态弹窗 -->
<div id="modal-reissue-token" class="modal-backdrop hidden">
<div id="modal-reissue-token" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="reissue-modal-title">
<div class="modal-card">
<div class="modal-header">
<h3>节点专属 Token 已重新颁发</h3>
<h3 id="reissue-modal-title">节点专属 Token 已重新颁发</h3>
<button id="reissue-close-btn" class="modal-close" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
@@ -280,8 +349,8 @@
<div id="toast-container" class="toast-container"></div>
<!-- ===== 通用自定义 Confirm 模态弹窗 ===== -->
<div id="modal-confirm" class="login-overlay hidden">
<div class="login-card glass modal-card">
<div id="modal-confirm" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="confirm-modal-title" aria-describedby="confirm-modal-msg">
<div class="login-card card modal-card">
<div class="login-header">
<h3 id="confirm-modal-title" class="confirm-modal-title">确认操作</h3>
<p id="confirm-modal-msg" class="confirm-modal-msg">您确定要执行此操作吗?</p>