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']) @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,

View File

@@ -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}>