update pay function

This commit is contained in:
2025-11-23 16:57:02 +08:00
parent a778f94b68
commit acb7862789
2 changed files with 186 additions and 35 deletions

79
app.py
View File

@@ -11682,9 +11682,9 @@ def get_value_chain_analysis(company_code):
@app.route('/api/company/value-chain/related-companies', methods=['GET'])
def get_related_companies_by_node():
"""
根据产业链节点名称查询相关公司
根据产业链节点名称查询相关公司结合nodes和flows表
参数: node_name - 节点名称(如 "中芯国际""EDA/IP"等)
返回: 包含该节点的所有公司列表(去重)
返回: 包含该节点的所有公司列表,附带节点层级、类型、关系描述等信息
"""
try:
node_name = request.args.get('node_name')
@@ -11695,29 +11695,88 @@ def get_related_companies_by_node():
'error': '缺少必需参数 node_name'
}), 400
# 查询包含该节点的所有公司
# 查询包含该节点的所有公司及其节点信息
query = text("""
SELECT DISTINCT
n.company_code as stock_code,
s.SECNAME as stock_name,
s.ORGNAME as company_name
s.ORGNAME as company_name,
n.node_level,
n.node_type,
n.node_description,
n.importance_score,
n.market_share,
n.dependency_degree
FROM company_value_chain_nodes n
LEFT JOIN ea_stocklist s ON n.company_code = s.SECCODE
WHERE n.node_name = :node_name
ORDER BY n.company_code
ORDER BY n.importance_score DESC, n.company_code
""")
with engine.connect() as conn:
result = conn.execute(query, {'node_name': node_name}).fetchall()
nodes_result = conn.execute(query, {'node_name': node_name}).fetchall()
# 构建返回数据
companies = []
for row in result:
companies.append({
for row in nodes_result:
company_data = {
'stock_code': row.stock_code,
'stock_name': row.stock_name or row.stock_code,
'company_name': row.company_name
})
'company_name': row.company_name,
'node_info': {
'node_level': row.node_level,
'node_type': row.node_type,
'node_description': row.node_description,
'importance_score': row.importance_score,
'market_share': format_decimal(row.market_share),
'dependency_degree': format_decimal(row.dependency_degree)
},
'relationships': []
}
# 查询该节点在该公司产业链中的流向关系
flows_query = text("""
SELECT
source_node,
source_type,
source_level,
target_node,
target_type,
target_level,
flow_type,
relationship_desc,
flow_value,
flow_ratio
FROM company_value_chain_flows
WHERE company_code = :company_code
AND (source_node = :node_name OR target_node = :node_name)
ORDER BY flow_ratio DESC
LIMIT 5
""")
with engine.connect() as conn:
flows_result = conn.execute(flows_query, {
'company_code': row.stock_code,
'node_name': node_name
}).fetchall()
# 添加流向关系信息
for flow in flows_result:
# 判断节点在流向中的角色
is_source = (flow.source_node == node_name)
relationship = {
'role': 'source' if is_source else 'target',
'connected_node': flow.target_node if is_source else flow.source_node,
'connected_type': flow.target_type if is_source else flow.source_type,
'connected_level': flow.target_level if is_source else flow.source_level,
'flow_type': flow.flow_type,
'relationship_desc': flow.relationship_desc,
'flow_ratio': format_decimal(flow.flow_ratio)
}
company_data['relationships'].append(relationship)
companies.append(company_data)
return jsonify({
'success': True,

View File

@@ -468,34 +468,126 @@ const ValueChainNodeCard = ({ node, isCompany = false, level = 0 }) => {
<Spinner size="md" />
</Center>
) : relatedCompanies.length > 0 ? (
<VStack align="stretch" spacing={2} maxH="300px" overflowY="auto">
{relatedCompanies.map((company, idx) => (
<Card key={idx} variant="outline" size="sm">
<CardBody p={3}>
<HStack justify="space-between">
<VStack align="start" spacing={1}>
<HStack>
<Text fontSize="sm" fontWeight="medium">{company.stock_name}</Text>
<Badge size="sm" colorScheme="blue">{company.stock_code}</Badge>
<VStack align="stretch" spacing={3} maxH="400px" overflowY="auto">
{relatedCompanies.map((company, idx) => {
// 获取节点层级标签
const getLevelLabel = (level) => {
if (level < 0) return { text: '上游', color: 'orange' };
if (level === 0) return { text: '核心', color: 'blue' };
if (level > 0) return { text: '下游', color: 'green' };
return { text: '未知', color: 'gray' };
};
const levelInfo = getLevelLabel(company.node_info?.node_level);
return (
<Card key={idx} variant="outline" size="sm">
<CardBody p={3}>
<VStack align="stretch" spacing={2}>
{/* 公司基本信息 */}
<HStack justify="space-between">
<VStack align="start" spacing={1} flex={1}>
<HStack flexWrap="wrap">
<Text fontSize="sm" fontWeight="bold">{company.stock_name}</Text>
<Badge size="sm" colorScheme="blue">{company.stock_code}</Badge>
<Badge size="sm" colorScheme={levelInfo.color} variant="solid">
{levelInfo.text}
</Badge>
{company.node_info?.node_type && (
<Badge size="sm" colorScheme="purple" variant="outline">
{company.node_info.node_type}
</Badge>
)}
</HStack>
{company.company_name && (
<Text fontSize="xs" color="gray.500" noOfLines={1}>
{company.company_name}
</Text>
)}
</VStack>
<IconButton
size="sm"
icon={<ExternalLinkIcon />}
variant="ghost"
colorScheme="blue"
onClick={() => {
window.location.href = `/company?stock_code=${company.stock_code}`;
}}
aria-label="查看公司详情"
/>
</HStack>
{company.company_name && (
<Text fontSize="xs" color="gray.500">{company.company_name}</Text>
{/* 节点描述 */}
{company.node_info?.node_description && (
<Text fontSize="xs" color="gray.600" noOfLines={2}>
{company.node_info.node_description}
</Text>
)}
{/* 节点指标 */}
{(company.node_info?.importance_score || company.node_info?.market_share || company.node_info?.dependency_degree) && (
<HStack spacing={3} pt={1} borderTop="1px" borderColor="gray.100">
{company.node_info.importance_score && (
<HStack spacing={1}>
<Text fontSize="xs" color="gray.500">重要度:</Text>
<Badge size="xs" colorScheme="red">{company.node_info.importance_score}</Badge>
</HStack>
)}
{company.node_info.market_share && (
<HStack spacing={1}>
<Text fontSize="xs" color="gray.500">市场份额:</Text>
<Text fontSize="xs" fontWeight="medium">{company.node_info.market_share}%</Text>
</HStack>
)}
{company.node_info.dependency_degree && (
<HStack spacing={1}>
<Text fontSize="xs" color="gray.500">依赖度:</Text>
<Text fontSize="xs" fontWeight="medium">{company.node_info.dependency_degree}%</Text>
</HStack>
)}
</HStack>
)}
{/* 流向关系 */}
{company.relationships && company.relationships.length > 0 && (
<Box pt={2} borderTop="1px" borderColor="gray.100">
<Text fontSize="xs" fontWeight="bold" color="gray.600" mb={1}>
产业链关系:
</Text>
<VStack align="stretch" spacing={1}>
{company.relationships.map((rel, ridx) => (
<HStack key={ridx} fontSize="xs" spacing={2}>
<Icon
as={rel.role === 'source' ? FaArrowRight : FaArrowLeft}
color={rel.role === 'source' ? 'green.500' : 'orange.500'}
boxSize={3}
/>
<Text color="gray.700" noOfLines={1}>
{rel.role === 'source' ? '流向' : '来自'}
<Text as="span" fontWeight="medium" mx={1}>
{rel.connected_node}
</Text>
</Text>
{rel.relationship_desc && (
<Badge size="xs" colorScheme="cyan" variant="subtle" noOfLines={1}>
{rel.relationship_desc}
</Badge>
)}
{rel.flow_ratio && (
<Text color="purple.600" fontWeight="medium">
{rel.flow_ratio}%
</Text>
)}
</HStack>
))}
</VStack>
</Box>
)}
</VStack>
<IconButton
size="sm"
icon={<ExternalLinkIcon />}
variant="ghost"
colorScheme="blue"
onClick={() => {
window.location.href = `/company?stock_code=${company.stock_code}`;
}}
aria-label="查看公司详情"
/>
</HStack>
</CardBody>
</Card>
))}
</CardBody>
</Card>
);
})}
</VStack>
) : (
<Center py={4}>