# -*- 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(""" **交互操作** - 鼠标悬浮: 查看节点详情 - 拖拽: 移动节点 - 滚轮: 缩放图谱 - 双击: 聚焦节点 """)