update pay function
This commit is contained in:
79
app.py
79
app.py
@@ -11682,9 +11682,9 @@ def get_value_chain_analysis(company_code):
|
|||||||
@app.route('/api/company/value-chain/related-companies', methods=['GET'])
|
@app.route('/api/company/value-chain/related-companies', methods=['GET'])
|
||||||
def get_related_companies_by_node():
|
def get_related_companies_by_node():
|
||||||
"""
|
"""
|
||||||
根据产业链节点名称查询相关公司
|
根据产业链节点名称查询相关公司(结合nodes和flows表)
|
||||||
参数: node_name - 节点名称(如 "中芯国际"、"EDA/IP"等)
|
参数: node_name - 节点名称(如 "中芯国际"、"EDA/IP"等)
|
||||||
返回: 包含该节点的所有公司列表(去重)
|
返回: 包含该节点的所有公司列表,附带节点层级、类型、关系描述等信息
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
node_name = request.args.get('node_name')
|
node_name = request.args.get('node_name')
|
||||||
@@ -11695,29 +11695,88 @@ def get_related_companies_by_node():
|
|||||||
'error': '缺少必需参数 node_name'
|
'error': '缺少必需参数 node_name'
|
||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
# 查询包含该节点的所有公司
|
# 查询包含该节点的所有公司及其节点信息
|
||||||
query = text("""
|
query = text("""
|
||||||
SELECT DISTINCT
|
SELECT DISTINCT
|
||||||
n.company_code as stock_code,
|
n.company_code as stock_code,
|
||||||
s.SECNAME as stock_name,
|
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
|
FROM company_value_chain_nodes n
|
||||||
LEFT JOIN ea_stocklist s ON n.company_code = s.SECCODE
|
LEFT JOIN ea_stocklist s ON n.company_code = s.SECCODE
|
||||||
WHERE n.node_name = :node_name
|
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:
|
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 = []
|
companies = []
|
||||||
for row in result:
|
for row in nodes_result:
|
||||||
companies.append({
|
company_data = {
|
||||||
'stock_code': row.stock_code,
|
'stock_code': row.stock_code,
|
||||||
'stock_name': row.stock_name or 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({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
|
|||||||
@@ -468,18 +468,41 @@ const ValueChainNodeCard = ({ node, isCompany = false, level = 0 }) => {
|
|||||||
<Spinner size="md" />
|
<Spinner size="md" />
|
||||||
</Center>
|
</Center>
|
||||||
) : relatedCompanies.length > 0 ? (
|
) : relatedCompanies.length > 0 ? (
|
||||||
<VStack align="stretch" spacing={2} maxH="300px" overflowY="auto">
|
<VStack align="stretch" spacing={3} maxH="400px" overflowY="auto">
|
||||||
{relatedCompanies.map((company, idx) => (
|
{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">
|
<Card key={idx} variant="outline" size="sm">
|
||||||
<CardBody p={3}>
|
<CardBody p={3}>
|
||||||
|
<VStack align="stretch" spacing={2}>
|
||||||
|
{/* 公司基本信息 */}
|
||||||
<HStack justify="space-between">
|
<HStack justify="space-between">
|
||||||
<VStack align="start" spacing={1}>
|
<VStack align="start" spacing={1} flex={1}>
|
||||||
<HStack>
|
<HStack flexWrap="wrap">
|
||||||
<Text fontSize="sm" fontWeight="medium">{company.stock_name}</Text>
|
<Text fontSize="sm" fontWeight="bold">{company.stock_name}</Text>
|
||||||
<Badge size="sm" colorScheme="blue">{company.stock_code}</Badge>
|
<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>
|
</HStack>
|
||||||
{company.company_name && (
|
{company.company_name && (
|
||||||
<Text fontSize="xs" color="gray.500">{company.company_name}</Text>
|
<Text fontSize="xs" color="gray.500" noOfLines={1}>
|
||||||
|
{company.company_name}
|
||||||
|
</Text>
|
||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -493,9 +516,78 @@ const ValueChainNodeCard = ({ node, isCompany = false, level = 0 }) => {
|
|||||||
aria-label="查看公司详情"
|
aria-label="查看公司详情"
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
|
{/* 节点描述 */}
|
||||||
|
{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>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</VStack>
|
</VStack>
|
||||||
) : (
|
) : (
|
||||||
<Center py={4}>
|
<Center py={4}>
|
||||||
|
|||||||
Reference in New Issue
Block a user