# -*- 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="供应链知识图谱", 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"]: status_tag = "[受限]" if risk.get("status") == "受限" else "[关注]" critical_tag = " [关键供应]" if risk.get("is_critical") else "" st.markdown( f"- {status_tag} **{risk['entity']}** ({risk['node_type']}) — " f"{risk['relation']} — 状态: {risk.get('status', '未知')}{critical_tag}" ) # 间接风险 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_tag = "[高风险]" if path["severity"] == "高" else "[中风险]" st.markdown(f"- {severity_tag} `{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(""" **交互操作** - 鼠标悬浮: 查看节点详情 - 拖拽: 移动节点 - 滚轮: 缩放图谱 - 双击: 聚焦节点 """)