Files
XH-202626/XH-202626_原型系统源码与部署手册/01_原型系统源码/🏠_系统首页.py
T

305 lines
9.3 KiB
Python

# -*- coding: utf-8 -*-
"""
🛡️ 科创企业智能风控与核保系统 - 首页 / 风控大屏入口
"""
import sys
import os
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
# 确保项目根目录在 Python 路径中
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from config import PAGE_TITLE, PAGE_ICON, LAYOUT
from collectors.financial_collector import get_all_companies
from risk_engine.risk_scorer import calculate_six_dimension_scores, get_risk_level
# ============================================================
# 页面配置
# ============================================================
st.set_page_config(
page_title=PAGE_TITLE,
page_icon=PAGE_ICON,
layout=LAYOUT,
initial_sidebar_state="expanded",
)
# ============================================================
# 自定义样式
# ============================================================
st.markdown("""
<style>
/* 主标题渐变 */
.main-title {
background: linear-gradient(120deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
padding: 30px;
border-radius: 15px;
text-align: center;
margin-bottom: 30px;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
}
.main-title h1 {
color: #e94560;
font-size: 2.2em;
margin: 0;
}
.main-title p {
color: #a8a8b3;
font-size: 1.1em;
margin-top: 10px;
}
/* 统计卡片 */
.stat-card {
background: linear-gradient(135deg, #1a1a2e, #16213e);
padding: 20px;
border-radius: 12px;
text-align: center;
border: 1px solid #2a2a4a;
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
}
.stat-card .number {
font-size: 2.5em;
font-weight: bold;
color: #e94560;
}
.stat-card .label {
color: #a8a8b3;
font-size: 0.95em;
margin-top: 5px;
}
/* 企业卡片 */
.company-card {
background: #16213e;
padding: 15px;
border-radius: 10px;
margin: 8px 0;
border-left: 4px solid;
}
/* 侧边栏 */
[data-testid="stSidebar"] {
background: linear-gradient(180deg, #1a1a2e 0%, #0f0f23 100%);
}
</style>
""", unsafe_allow_html=True)
from utils.session_helper import render_sidebar_global_company_selector
# ============================================================
# 侧边栏
# ============================================================
with st.sidebar:
render_sidebar_global_company_selector()
st.markdown("---")
st.markdown("### 🛡️ 系统导航")
st.markdown("---")
st.markdown("""
**功能模块**
- 🏠 系统首页
- 📊 企业风险概览
- 🕸️ 供应链知识图谱
- ⚖️ 多智能体辩论诊断
- 💰 动态定价与核保
""")
st.markdown("---")
st.markdown("""
**技术栈**
- 🤖 DeepSeek API (LLM)
- 🕸️ NetworkX (知识图谱)
- 📊 Plotly (可视化)
- 🔧 Streamlit (Web框架)
""")
st.markdown("---")
st.caption("中国平安 × 挑战杯 · 科创风控原型")
# ============================================================
# 主页内容
# ============================================================
# 标题
st.markdown("""
<div class="main-title">
<h1>🛡️ 科创企业智能风控与核保系统</h1>
<p>基于多智能体辩论 × 知识图谱 × 动态定价的全链条风控平台</p>
<p style="font-size: 0.85em; color: #666;">中国青基会平安励志计划 · XH-202626 · 科创企业特有风险的识别与管理</p>
</div>
""", unsafe_allow_html=True)
# 加载企业数据
companies = get_all_companies()
# ============================================================
# 统计概览卡片
# ============================================================
st.markdown("### 📊 系统概览")
# 计算所有企业的风险评分
risk_data = []
for comp in companies:
scores = calculate_six_dimension_scores(comp)
risk_data.append({
"company": comp["short_name"],
"stock_code": comp["stock_code"],
"industry": comp["industry"],
"sector": comp["sector"],
"comprehensive_score": scores["comprehensive_score"],
"risk_level": scores["risk_level"]["level"],
**scores["scores"],
})
df = pd.DataFrame(risk_data)
# 统计卡片
col1, col2, col3, col4, col5 = st.columns(5)
with col1:
st.markdown(f"""
<div class="stat-card">
<div class="number">{len(companies)}</div>
<div class="label">监控企业总数</div>
</div>
""", unsafe_allow_html=True)
with col2:
high_risk = len(df[df["comprehensive_score"] >= 70])
st.markdown(f"""
<div class="stat-card">
<div class="number" style="color: #F44336;">{high_risk}</div>
<div class="label">⚠️ 高风险企业</div>
</div>
""", unsafe_allow_html=True)
with col3:
med_risk = len(df[(df["comprehensive_score"] >= 40) & (df["comprehensive_score"] < 70)])
st.markdown(f"""
<div class="stat-card">
<div class="number" style="color: #FF9800;">{med_risk}</div>
<div class="label">🟡 中风险企业</div>
</div>
""", unsafe_allow_html=True)
with col4:
low_risk = len(df[df["comprehensive_score"] < 40])
st.markdown(f"""
<div class="stat-card">
<div class="number" style="color: #4CAF50;">{low_risk}</div>
<div class="label">🟢 低风险企业</div>
</div>
""", unsafe_allow_html=True)
with col5:
sanctioned = len([c for c in companies if "被列入" in c.get("compliance", {}).get("entity_list_status", "")])
st.markdown(f"""
<div class="stat-card">
<div class="number" style="color: #B71C1C;">{sanctioned}</div>
<div class="label">⛔ 受制裁企业</div>
</div>
""", unsafe_allow_html=True)
st.markdown("<br>", unsafe_allow_html=True)
# ============================================================
# 风险分布图
# ============================================================
col_left, col_right = st.columns([3, 2])
with col_left:
st.markdown("#### 🎯 企业综合风险评分分布")
# 水平柱状图,按风险排序
df_sorted = df.sort_values("comprehensive_score", ascending=True)
colors = []
for score in df_sorted["comprehensive_score"]:
if score >= 70:
colors.append("#F44336")
elif score >= 50:
colors.append("#FF9800")
elif score >= 30:
colors.append("#FFC107")
else:
colors.append("#4CAF50")
fig = go.Figure(go.Bar(
x=df_sorted["comprehensive_score"],
y=df_sorted["company"],
orientation="h",
marker_color=colors,
text=df_sorted["comprehensive_score"],
textposition="outside",
))
fig.update_layout(
height=400,
margin=dict(l=0, r=30, t=10, b=10),
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font=dict(color="white"),
xaxis=dict(
title="综合风险评分",
range=[0, 105],
gridcolor="rgba(255,255,255,0.1)",
),
yaxis=dict(gridcolor="rgba(255,255,255,0.1)"),
)
# 添加阈值线
fig.add_vline(x=70, line_dash="dash", line_color="#F44336",
annotation_text="高风险线(70)", annotation_position="top right")
fig.add_vline(x=40, line_dash="dash", line_color="#FF9800",
annotation_text="中风险线(40)", annotation_position="top right")
st.plotly_chart(fig, use_container_width=True)
with col_right:
st.markdown("#### 🏷️ 行业风险热力")
# 按行业汇总
sector_risk = df.groupby("sector")["comprehensive_score"].mean().reset_index()
sector_risk.columns = ["行业", "平均风险"]
sector_risk = sector_risk.sort_values("平均风险", ascending=False)
fig2 = px.bar(
sector_risk, x="行业", y="平均风险",
color="平均风险",
color_continuous_scale=["#4CAF50", "#FFC107", "#F44336"],
range_color=[0, 100],
)
fig2.update_layout(
height=400,
margin=dict(l=0, r=0, t=10, b=10),
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font=dict(color="white"),
showlegend=False,
coloraxis_showscale=False,
)
st.plotly_chart(fig2, use_container_width=True)
# ============================================================
# 企业列表
# ============================================================
st.markdown("### 📋 企业风险速览")
# 格式化数据表格
display_df = df[["company", "stock_code", "industry", "comprehensive_score", "risk_level"]].copy()
display_df.columns = ["企业名称", "股票代码", "行业", "综合风险评分", "风险等级"]
st.dataframe(display_df, use_container_width=True, height=400)
# ============================================================
# 底部信息
# ============================================================
st.markdown("---")
st.markdown("""
<div style="text-align: center; color: #666; font-size: 0.85em;">
<p>🛡️ 科创企业智能风控与核保系统 v1.0</p>
<p>技术架构: Multi-Agent 交叉验证 × 供应链知识图谱 × 动态保险定价</p>
<p>数据来源: 科创板公开年报 · BIS 实体清单 · 网信办算法备案公示</p>
</div>
""", unsafe_allow_html=True)