feat: 数据流四态分类(healthy/write_only/read_only/orphan)+僵尸表清理
This commit is contained in:
+14
-3
@@ -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文件
|
||||
|
||||
@@ -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>`;
|
||||
|
||||
Reference in New Issue
Block a user