diff --git a/scripts/mofin_health.py b/scripts/mofin_health.py
index 4191f791..3c718e97 100644
--- a/scripts/mofin_health.py
+++ b/scripts/mofin_health.py
@@ -394,7 +394,17 @@ def build_report():
has_output = len(readers) > 0
# 排除系统表
is_system = tname.startswith("sqlite_") or tname.startswith("_")
- orphan = (not has_input or not has_output) and not is_system and cnt > 0
+ if is_system:
+ continue
+ # 数据流状态:healthy / write_only / read_only / orphan
+ if has_input and has_output:
+ flow_status = "healthy"
+ elif has_input and not has_output:
+ flow_status = "write_only"
+ elif not has_input and has_output:
+ flow_status = "read_only"
+ else:
+ flow_status = "orphan"
entities.append({
"name": tname,
"desc": TABLES_DESC.get(tname, ""),
@@ -403,8 +413,9 @@ def build_report():
"writers": writers[:10],
"has_input": has_input,
"has_output": has_output,
- "orphan": orphan,
- "warn": orphan or (cnt == 0 and tname not in ("capital_flow_cache","")),
+ "orphan": flow_status in ("orphan", "read_only", "write_only"),
+ "flow_status": flow_status,
+ "warn": flow_status != "healthy",
})
# JSON文件
diff --git a/static/mofin_health.html b/static/mofin_health.html
index 66774d2f..945cf982 100644
--- a/static/mofin_health.html
+++ b/static/mofin_health.html
@@ -236,7 +236,10 @@ function loadData() {
`生成: ${d.generated_at}` +
`功能: ✅${cnt.ok} ⚠️${cnt.warn} ❌${cnt.fail}` +
`Cron: ✅${pipeOk} ❌${pipeErr} 共${d.pipelines.length}` +
- `数据表: ${d.entities.length} | 孤立: ${d.entities.filter(e=>e.orphan).length}`;
+ `数据表: ${d.entities.length} | ` +
+ `孤立${d.entities.filter(e=>e.flow_status==='orphan').length} ` +
+ `写无读${d.entities.filter(e=>e.flow_status==='write_only').length} ` +
+ `读无写${d.entities.filter(e=>e.flow_status==='read_only').length}`;
// Tab 0: 功能树
document.getElementById('panel0').innerHTML = renderFeatureTree(d.feature_tree);
@@ -245,14 +248,17 @@ function loadData() {
document.getElementById('panel1').innerHTML = renderPipelineTable(d.pipelines);
// Tab 2: 数据实体
- let h2 = '
| 表名 | 作用 | 行数 | 写入方 | 读取方 | 状态 |
';
+ let h2 = '| 表名 | 作用 | 行数 | 写入方 | 读取方 | 数据流 |
';
d.entities.forEach(e => {
+ const flowMap = {healthy:'✅ 正常', write_only:'✏️ 写无读', read_only:'📖 读无写', orphan:'🔴 孤立'};
+ const flowColors = {healthy:'#3fb950', write_only:'#d29922', read_only:'#58a6ff', orphan:'#f85149'};
+ const badge = flowMap[e.flow_status] || '?';
+ const color = flowColors[e.flow_status] || '#8b949e';
const cls = e.orphan ? 'orphan' : '';
- const badge = e.orphan ? '🔴 孤立' : (!e.has_input||!e.has_output) ? '⚠️ 单向' : '✅';
h2 += `| ${e.name} | ${e.desc||''} | ${e.rows} | `;
h2 += `${(e.writers||[]).slice(0,4).join(', ')||'无'} | `;
h2 += `${(e.readers||[]).slice(0,4).join(', ')||'无'} | `;
- h2 += `${badge} |
`;
+ h2 += `${badge} | `;
});
(d.json_files||[]).forEach(j => {
h2 += `| 📄 ${j.name} | ${j.desc||'JSON文件'} | ${j.size_kb}KB | `;