feat: 数据流四态分类(healthy/write_only/read_only/orphan)+僵尸表清理

This commit is contained in:
知微
2026-07-08 19:43:04 +08:00
parent 9e3c2ef170
commit 84a147b6f2
2 changed files with 24 additions and 7 deletions
+14 -3
View File
@@ -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文件
+10 -4
View File
@@ -236,7 +236,10 @@ function loadData() {
`<span>生成: ${d.generated_at}</span>` +
`<span>功能: ✅${cnt.ok} ⚠️${cnt.warn}${cnt.fail}</span>` +
`<span>Cron: ✅${pipeOk}${pipeErr}${d.pipelines.length}</span>` +
`<span>数据表: ${d.entities.length} | 孤立: <strong style="color:#f85149">${d.entities.filter(e=>e.orphan).length}</strong></span>`;
`<span>数据表: ${d.entities.length} | ` +
`<span style="color:#f85149">孤立${d.entities.filter(e=>e.flow_status==='orphan').length}</span> ` +
`<span style="color:#d29922">写无读${d.entities.filter(e=>e.flow_status==='write_only').length}</span> ` +
`<span style="color:#58a6ff">读无写${d.entities.filter(e=>e.flow_status==='read_only').length}</span></span>`;
// 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 = '<table><thead><tr><th>表名</th><th>作用</th><th>行数</th><th>写入方</th><th>读取方</th><th>状态</th></tr></thead><tbody>';
let h2 = '<table><thead><tr><th>表名</th><th>作用</th><th>行数</th><th>写入方</th><th>读取方</th><th>数据流</th></tr></thead><tbody>';
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 += `<tr class="${cls}"><td><strong>${e.name}</strong></td><td class="desc">${e.desc||''}</td><td>${e.rows}</td>`;
h2 += `<td class="wrap">${(e.writers||[]).slice(0,4).join(', ')||'<span style="color:#f85149">无</span>'}</td>`;
h2 += `<td class="wrap">${(e.readers||[]).slice(0,4).join(', ')||'<span style="color:#f85149">无</span>'}</td>`;
h2 += `<td>${badge}</td></tr>`;
h2 += `<td style="color:${color}">${badge}</td></tr>`;
});
(d.json_files||[]).forEach(j => {
h2 += `<tr><td>📄 ${j.name}</td><td class="desc">${j.desc||'JSON文件'}</td><td>${j.size_kb}KB</td>`;