Files
XH-202626/pages/1_📊_企业风险概览.py

220 lines
8.8 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 plotly.graph_objects as go
import plotly.express as px
from collectors.financial_collector import get_all_companies, get_company_by_code
from risk_engine.risk_scorer import calculate_six_dimension_scores, get_risk_level
from utils.session_helper import render_company_selector, render_sidebar_global_company_selector
st.set_page_config(page_title="企业风险概览", page_icon="📊", layout="wide")
with st.sidebar:
render_sidebar_global_company_selector()
st.markdown("---")
st.markdown("# 📊 企业风险概览")
st.markdown("选择一家科创企业,查看其六维风险画像和关键指标。")
# 企业选择(全局同步)
company = render_company_selector("🏢 选择目标企业", key_suffix="overview_page")
stock_code = company["stock_code"] if company else "688256"
if company:
# 计算六维评分
risk_result = calculate_six_dimension_scores(company)
scores = risk_result["scores"]
comprehensive = risk_result["comprehensive_score"]
level_info = risk_result["risk_level"]
# ============================================================
# 企业信息 + 综合评分
# ============================================================
col1, col2 = st.columns([2, 1])
with col1:
st.markdown(f"### {company['short_name']}")
st.markdown(f"**行业**: {company['industry']} | **领域**: {company['sector']} | **代码**: {company['stock_code']}")
st.markdown(f"**简介**: {company['description']}")
# 核心人员
st.markdown("#### 👤 核心技术人员")
for p in company.get("core_tech_personnel", []):
status_emoji = "✅" if "在职" in p["status"] else "⚠️"
st.markdown(f"- {status_emoji} **{p['name']}** ({p['title']}) - 重要性: {p['importance']} - 状态: {p['status']}")
with col2:
# 综合风险仪表盘
fig = go.Figure(go.Indicator(
mode="gauge+number",
value=comprehensive,
title={"text": "综合风险评分", "font": {"color": "white"}},
number={"font": {"color": "white", "size": 48}},
gauge={
"axis": {"range": [0, 100], "tickcolor": "white"},
"bar": {"color": level_info["color"]},
"steps": [
{"range": [0, 30], "color": "rgba(76,175,80,0.3)"},
{"range": [30, 50], "color": "rgba(255,193,7,0.3)"},
{"range": [50, 70], "color": "rgba(255,152,0,0.3)"},
{"range": [70, 100], "color": "rgba(244,67,54,0.3)"},
],
"threshold": {
"line": {"color": "red", "width": 4},
"thickness": 0.75,
"value": 70,
},
},
))
fig.update_layout(
height=250,
margin=dict(l=20, r=20, t=40, b=10),
paper_bgcolor="rgba(0,0,0,0)",
font=dict(color="white"),
)
st.plotly_chart(fig, use_container_width=True)
st.markdown(f"<div style='text-align:center; font-size:1.2em;'>"
f"{level_info['emoji']} 风险等级: <b>{level_info['level']}</b></div>",
unsafe_allow_html=True)
st.markdown("---")
# ============================================================
# 六维风险雷达图
# ============================================================
col_radar, col_detail = st.columns([1, 1])
with col_radar:
st.markdown("#### 🎯 六维风险雷达图")
dim_names_cn = ["技术路线颠覆", "核心人员流失", "算法/数据合规",
"地缘政治/出口管制", "研发资本化操纵", "客户/供应商集中"]
dim_keys = ["tech_disruption", "talent_loss", "algo_compliance",
"geopolitical", "rd_capitalization", "concentration"]
values = [scores[k] for k in dim_keys]
fig = go.Figure()
fig.add_trace(go.Scatterpolar(
r=values + [values[0]],
theta=dim_names_cn + [dim_names_cn[0]],
fill="toself",
fillcolor="rgba(233,69,96,0.3)",
line=dict(color="#e94560", width=2),
marker=dict(size=8, color="#e94560"),
name=company["short_name"],
))
# 添加警戒线
fig.add_trace(go.Scatterpolar(
r=[70] * 7,
theta=dim_names_cn + [dim_names_cn[0]],
line=dict(color="rgba(244,67,54,0.5)", dash="dash", width=1),
name="高风险线(70)",
))
fig.update_layout(
polar=dict(
radialaxis=dict(visible=True, range=[0, 100], tickfont=dict(color="white")),
angularaxis=dict(tickfont=dict(color="white", size=11)),
bgcolor="rgba(0,0,0,0)",
),
height=420,
margin=dict(l=60, r=60, t=30, b=30),
paper_bgcolor="rgba(0,0,0,0)",
font=dict(color="white"),
showlegend=True,
legend=dict(x=0, y=-0.15),
)
st.plotly_chart(fig, use_container_width=True)
with col_detail:
st.markdown("#### 📋 各维度风险详情")
for k in dim_keys:
detail = risk_result["dimension_details"][k]
score = detail["score"]
level = detail["level"]
emoji = level["emoji"]
color = level["color"]
st.markdown(
f"<div style='background:{color}22; padding:10px; border-radius:8px; "
f"margin:5px 0; border-left:4px solid {color};'>"
f"<b>{emoji} {detail['name']}</b>: {score}分 ({level['level']})</div>",
unsafe_allow_html=True,
)
st.markdown("---")
# ============================================================
# 关键财务指标
# ============================================================
st.markdown("#### 💰 关键财务指标")
fin = company.get("financials", {})
col_f1, col_f2, col_f3, col_f4 = st.columns(4)
with col_f1:
revenue = fin.get("revenue_2024", 0)
st.metric("营业收入", f{revenue/1e8:.1f}亿")
with col_f2:
profit = fin.get("net_profit_2024", 0)
st.metric("净利润", f{profit/1e8:.1f}亿",
delta="盈利" if profit > 0 else "亏损",
delta_color="normal" if profit > 0 else "inverse")
with col_f3:
rd = fin.get("rd_expense_2024", 0)
st.metric("研发费用", f{rd/1e8:.1f}亿")
with col_f4:
cap_rate = fin.get("rd_capitalization_rate", 0)
st.metric("研发资本化率", f"{cap_rate:.0%}",
delta="⚠️ 偏高" if cap_rate > 0.3 else "正常",
delta_color="inverse" if cap_rate > 0.3 else "normal")
col_f5, col_f6, col_f7, col_f8 = st.columns(4)
with col_f5:
st.metric("研发/营收比", f"{fin.get('rd_revenue_ratio', 0):.1%}")
with col_f6:
st.metric("前5大客户占比", f"{fin.get('top5_customer_ratio', 0):.0%}")
with col_f7:
st.metric("应收周转率", f"{fin.get('receivable_turnover', 0):.1f}次/年")
with col_f8:
st.metric("现金流比率", f"{fin.get('cash_flow_ratio', 0):.2f}")
# ============================================================
# 技术路线 & 合规信息
# ============================================================
st.markdown("---")
col_tech, col_comp = st.columns(2)
with col_tech:
st.markdown("#### 🔬 技术路线")
tech = company.get("tech_route", {})
st.markdown(f"**当前技术**: {tech.get('current_tech', '未知')}")
st.markdown(f"**技术壁垒**: {tech.get('tech_moat', '未知')}")
st.markdown(f"**专利数量**: {tech.get('patent_count', 0)} 件")
st.markdown("**竞争技术路线**:")
for ct in tech.get("competing_techs", []):
st.markdown(f" - ⚔️ {ct}")
with col_comp:
st.markdown("#### 📋 合规状态")
comp_info = company.get("compliance", {})
st.markdown(f"**算法备案**: {comp_info.get('algo_filing_status', '未知')}")
st.markdown(f"**数据出境风险**: {comp_info.get('data_export_risk', '未知')}")
entity_status = comp_info.get("entity_list_status", "未知")
if "被列入" in entity_status:
st.error(f"⛔ 实体清单: {entity_status}")
if comp_info.get("sanctions_detail"):
st.warning(f"制裁详情: {comp_info['sanctions_detail']}")
else:
st.success(f"✅ 实体清单: {entity_status}")