feat: 数据实体Tab加多写方风险维度(老莫思维提升)——holding_strategies 10写方等高风险标红,孤立死实体标注。修复mofin_db elif缩进
This commit is contained in:
@@ -2131,7 +2131,7 @@ def write_holding_strategy(conn, code: str, name: str, data: dict,
|
|||||||
data['stop_loss'] = float(_op[2])
|
data['stop_loss'] = float(_op[2])
|
||||||
data['take_profit'] = float(_op[3])
|
data['take_profit'] = float(_op[3])
|
||||||
print(f" [AUTHORITY-POS] {code} 保护系统仓位'{_old_pos_val[:30]}'", flush=True)
|
print(f" [AUTHORITY-POS] {code} 保护系统仓位'{_old_pos_val[:30]}'", flush=True)
|
||||||
elif _old_ra:
|
elif _old_ra:
|
||||||
try:
|
try:
|
||||||
from datetime import datetime as _ddt3, timedelta as _dtd3
|
from datetime import datetime as _ddt3, timedelta as _dtd3
|
||||||
_ra_dt3 = _ddt3.fromisoformat(str(_old_ra)[:19])
|
_ra_dt3 = _ddt3.fromisoformat(str(_old_ra)[:19])
|
||||||
|
|||||||
@@ -940,6 +940,24 @@ def build_report():
|
|||||||
flow_status = "read_only"
|
flow_status = "read_only"
|
||||||
else:
|
else:
|
||||||
flow_status = "orphan"
|
flow_status = "orphan"
|
||||||
|
wcnt, rcnt = len(writers), len(readers)
|
||||||
|
# 2026-08-19 多写方风险(老莫:数据篡改风险的根源)
|
||||||
|
# 写方>=3 → 高风险(多个模块竞争写同一实体,可能互相覆写/篡改)
|
||||||
|
if wcnt >= 3:
|
||||||
|
risk = "high"
|
||||||
|
risk_note = f"⚠️多写方{wcnt}(竞争写,可能篡改)"
|
||||||
|
elif wcnt == 0:
|
||||||
|
risk = "orphan"
|
||||||
|
risk_note = "无写方(死实体)"
|
||||||
|
elif rcnt == 0:
|
||||||
|
risk = "write_only"
|
||||||
|
risk_note = "写无读(幽灵)"
|
||||||
|
elif wcnt == 2:
|
||||||
|
risk = "watch"
|
||||||
|
risk_note = f"{wcnt}写方(需关注)"
|
||||||
|
else:
|
||||||
|
risk = "ok"
|
||||||
|
risk_note = ""
|
||||||
entities.append({
|
entities.append({
|
||||||
"name": tname,
|
"name": tname,
|
||||||
"desc": TABLES_DESC.get(tname, ""),
|
"desc": TABLES_DESC.get(tname, ""),
|
||||||
@@ -947,11 +965,16 @@ def build_report():
|
|||||||
"rows": cnt,
|
"rows": cnt,
|
||||||
"readers": readers[:10],
|
"readers": readers[:10],
|
||||||
"writers": writers[:10],
|
"writers": writers[:10],
|
||||||
|
"writer_count": wcnt,
|
||||||
|
"reader_count": rcnt,
|
||||||
|
"multi_writer": wcnt >= 3,
|
||||||
|
"risk": risk,
|
||||||
|
"risk_note": risk_note,
|
||||||
"has_input": has_input,
|
"has_input": has_input,
|
||||||
"has_output": has_output,
|
"has_output": has_output,
|
||||||
"orphan": flow_status in ("orphan", "read_only", "write_only"),
|
"orphan": flow_status in ("orphan", "read_only", "write_only"),
|
||||||
"flow_status": flow_status,
|
"flow_status": flow_status,
|
||||||
"warn": flow_status != "healthy",
|
"warn": flow_status != "healthy" or wcnt >= 3,
|
||||||
"flow_detail": flow_detail,
|
"flow_detail": flow_detail,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -269,6 +269,14 @@ function renderDataFlow(entities, jsonFiles, architecture) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html += `显示 ${filtered.length} 个数据表 + ${jf.length} 个JSON文件的读写流向。绿色→写入,蓝色→读取。</div>`;
|
html += `显示 ${filtered.length} 个数据表 + ${jf.length} 个JSON文件的读写流向。绿色→写入,蓝色→读取。</div>`;
|
||||||
|
// 2026-08-19 多写方风险汇总
|
||||||
|
const risky = filtered.filter(e => (e.risk==='high'||(e.writers||[]).length>=3));
|
||||||
|
if (risky.length > 0) {
|
||||||
|
html += `<div style="background:#3d1c02;border:1px solid #f85149;border-radius:6px;padding:8px;margin:8px 0">`;
|
||||||
|
html += `<div style="font-weight:bold;color:#f85149;font-size:12px;margin-bottom:4px">🚨 篡改风险:${risky.length} 个数据实体有多写方竞争</div>`;
|
||||||
|
html += `<div style="font-size:11px;color:#d29922">多写方=多个模块写同一实体,可能互相覆写/篡改。重点:holding_strategies(策略参数)、candidates(候选池)、todos等。</div>`;
|
||||||
|
html += `</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
// 按写入方数量排序,最活跃的排前面
|
// 按写入方数量排序,最活跃的排前面
|
||||||
filtered.sort((a,b) => (b.writers||[]).length - (a.writers||[]).length);
|
filtered.sort((a,b) => (b.writers||[]).length - (a.writers||[]).length);
|
||||||
@@ -282,7 +290,18 @@ function renderDataFlow(entities, jsonFiles, architecture) {
|
|||||||
const rDesc = fd.readers || {};
|
const rDesc = fd.readers || {};
|
||||||
|
|
||||||
html += '<div style="background:#161b22;border:1px solid #30363d;border-radius:6px;padding:10px;margin-bottom:8px">';
|
html += '<div style="background:#161b22;border:1px solid #30363d;border-radius:6px;padding:10px;margin-bottom:8px">';
|
||||||
html += `<div style="font-weight:bold;font-size:14px;color:#58a6ff;margin-bottom:4px">🗄️ ${e.name}</div>`;
|
// 2026-08-19 多写方风险徽标(老莫:数据篡改风险)
|
||||||
|
const wcnt = e.writer_count || (e.writers||[]).length;
|
||||||
|
const rcnt = e.reader_count || (e.readers||[]).length;
|
||||||
|
let riskBadge = '';
|
||||||
|
if (e.risk === 'high' || wcnt >= 3) {
|
||||||
|
riskBadge = `<span style="background:#3d1c02;color:#f85149;border:1px solid #f85149;border-radius:4px;padding:1px 6px;font-size:10px;margin-left:6px">⚠️多写方${wcnt}·篡改风险</span>`;
|
||||||
|
} else if (wcnt === 0) {
|
||||||
|
riskBadge = `<span style="color:#f85149;border:1px solid #f85149;border-radius:4px;padding:1px 6px;font-size:10px;margin-left:6px">☠️无写方·死实体</span>`;
|
||||||
|
} else if (wcnt === 2) {
|
||||||
|
riskBadge = `<span style="color:#d29922;border:1px solid #d29922;border-radius:4px;padding:1px 6px;font-size:10px;margin-left:6px">${wcnt}写方·关注</span>`;
|
||||||
|
}
|
||||||
|
html += `<div style="font-weight:bold;font-size:14px;color:#58a6ff;margin-bottom:4px">🗄️ ${e.name}${riskBadge} <span style="font-size:10px;color:#8b949e;font-weight:normal">(${wcnt}写/ ${rcnt}读)</span></div>`;
|
||||||
html += `<div style="font-size:11px;color:#8b949e;margin-bottom:4px">${e.desc||''}</div>`;
|
html += `<div style="font-size:11px;color:#8b949e;margin-bottom:4px">${e.desc||''}</div>`;
|
||||||
// 综合总结
|
// 综合总结
|
||||||
if (fd.summary) {
|
if (fd.summary) {
|
||||||
|
|||||||
Reference in New Issue
Block a user