Files
XH-202626/XH-202626_数智风控系统_全国竞赛最终提交材料包/03_原型系统源码/pages/2_🕸️_供应链知识图谱.py
T

178 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
"""
🕸️ 供应链知识图谱页面
交互式图谱展示 + 供应链风险传染路径 + 实体清单命中标记
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import streamlit as st
import streamlit.components.v1 as components
from collectors.financial_collector import get_all_companies
from knowledge_graph.graph_builder import build_graph, get_graph_stats
from knowledge_graph.contagion_analyzer import analyze_contagion
from knowledge_graph.graph_visualizer import (
generate_interactive_graph,
get_subgraph_for_company,
)
st.set_page_config(page_title="供应链知识图谱", page_icon="🕸️", layout="wide")
st.markdown("# 🕸️ 供应链风险传染知识图谱")
st.markdown("可视化科创企业供应链网络,识别风险传染路径和关键断裂节点。")
# 构建图谱
@st.cache_resource
def get_graph():
return build_graph()
G = get_graph()
stats = get_graph_stats(G)
# ============================================================
# 图谱统计
# ============================================================
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("📌 总节点数", stats["total_nodes"])
with col2:
st.metric("🔗 总边数", stats["total_edges"])
with col3:
st.metric("⛔ 受制裁节点", stats["sanctioned_nodes"])
with col4:
st.metric("🔀 图密度", f"{stats['density']:.4f}")
# 节点类型分布
with st.expander("📊 节点类型分布"):
for ntype, count in stats["node_types"].items():
st.markdown(f"- **{ntype}**: {count} 个")
st.markdown("---")
# ============================================================
# 图谱视图选择
# ============================================================
view_mode = st.radio(
"🔍 视图模式",
["全局图谱", "企业中心视图(推荐)"],
horizontal=True,
)
from utils.session_helper import render_company_selector, render_sidebar_global_company_selector
with st.sidebar:
render_sidebar_global_company_selector()
st.markdown("---")
if view_mode == "企业中心视图(推荐)":
target_company = render_company_selector("🏢 选择中心企业", key_suffix="graph_page")
selected_company = target_company["short_name"] if target_company else "寒武纪"
depth = st.slider("穿透深度", 1, 3, 2)
# 提取子图
subgraph = get_subgraph_for_company(G, selected_company, depth=depth)
if subgraph.number_of_nodes() > 0:
html_content = generate_interactive_graph(
subgraph,
highlight_company=selected_company,
height="550px",
)
components.html(html_content, height=600, scrolling=True)
# ============================================================
# 供应链风险传染分析
# ============================================================
st.markdown("---")
st.markdown(f"### ⚠️ {selected_company} 供应链风险传染分析")
contagion = analyze_contagion(G, selected_company)
# 风险评分
supply_risk_score = contagion.get("risk_score", 0)
if supply_risk_score >= 60:
st.error(f"🔴 供应链风险评分: **{supply_risk_score}/100** — 供应链断裂风险极高")
elif supply_risk_score >= 30:
st.warning(f"🟡 供应链风险评分: **{supply_risk_score}/100** — 存在一定供应链风险")
else:
st.success(f"🟢 供应链风险评分: **{supply_risk_score}/100** — 供应链风险可控")
# 直接风险
if contagion.get("direct_risks"):
st.markdown("#### 🔴 直接风险(一度关联)")
for risk in contagion["direct_risks"]:
icon = "⛔" if risk.get("status") == "受限" else "⚠️"
st.markdown(
f"- {icon} **{risk['entity']}** ({risk['node_type']}) — "
f"{risk['relation']} — 状态: {risk.get('status', '未知')} "
f"{'🔑 关键供应' if risk.get('is_critical') else ''}"
)
# 间接风险
if contagion.get("indirect_risks"):
st.markdown("#### 🟡 间接风险(二度及以上关联)")
for risk in contagion["indirect_risks"]:
st.markdown(
f"- ⚠️ **{risk['entity']}** (距离: {risk['distance']}层) — {risk['relation']}"
)
# 传染路径
if contagion.get("contagion_paths"):
st.markdown("#### 🔗 风险传染路径")
for path in contagion["contagion_paths"]:
severity_icon = "🔴" if path["severity"] == "高" else "🟡"
st.markdown(f"- {severity_icon} `{path['path_str']}` (长度: {path['length']})")
# 关键断裂节点
if contagion.get("critical_nodes"):
st.markdown("#### 🔑 关键断裂节点")
for node in contagion["critical_nodes"]:
alt_info = f"✅ 有{node['alternative_count']}个替代" if node["has_alternative"] else "❌ 无替代方案"
st.markdown(
f"- **{node['node']}** — {node['relation']} — "
f"状态: {node['status']}{alt_info}"
)
else:
st.warning(f"未找到 {selected_company} 的相关图谱数据")
else:
# 全局视图
st.info("💡 全局图谱节点较多,加载可能需要几秒钟。推荐使用“企业中心视图”获得更好的体验。")
html_content = generate_interactive_graph(G, height="650px")
components.html(html_content, height=700, scrolling=True)
# ============================================================
# 图例
# ============================================================
st.markdown("---")
st.markdown("#### 🎨 图例说明")
col_l1, col_l2, col_l3 = st.columns(3)
with col_l1:
st.markdown("""
**节点颜色**
- 🟢 绿色: 科创企业 (正常)
- 🟡 金色: 选中的中心企业
- 🟠 橙色: 供应商
- 🟣 紫色: 客户
- 🔵 蓝色: 核心人员
- 🔴 红色: 受制裁实体
""")
with col_l2:
st.markdown("""
**边类型**
- 实线: 正常关系
- 红色虚线: 受限关系
- 黄色粗线: 关键供应关系
""")
with col_l3:
st.markdown("""
**交互操作**
- 鼠标悬浮: 查看节点详情
- 拖拽: 移动节点
- 滚轮: 缩放图谱
- 双击: 聚焦节点
""")