update pay ui
This commit is contained in:
74
app.py
74
app.py
@@ -12507,69 +12507,57 @@ def get_market_statistics():
|
|||||||
|
|
||||||
@app.route('/api/concepts/daily-top', methods=['GET'])
|
@app.route('/api/concepts/daily-top', methods=['GET'])
|
||||||
def get_daily_top_concepts():
|
def get_daily_top_concepts():
|
||||||
"""获取每日涨幅靠前的概念板块"""
|
"""获取每日涨幅靠前的概念板块
|
||||||
|
|
||||||
|
修复:使用 /price/list 接口获取全部概念的正确排序(原 /search 接口只取前500个)
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
# 获取交易日期参数
|
# 获取交易日期参数
|
||||||
trade_date = request.args.get('date')
|
trade_date = request.args.get('date')
|
||||||
limit = request.args.get('limit', 6, type=int)
|
limit = request.args.get('limit', 6, type=int)
|
||||||
|
|
||||||
# 构建概念中心API的URL
|
# 使用 /price/list 接口获取正确排序的涨幅数据
|
||||||
concept_api_url = 'http://222.128.1.157:16801/search'
|
concept_api_url = 'http://222.128.1.157:16801/price/list'
|
||||||
|
|
||||||
# 准备请求数据
|
params = {
|
||||||
request_data = {
|
'sort_by': 'change_desc',
|
||||||
'query': '',
|
'limit': limit,
|
||||||
'size': limit,
|
'offset': 0,
|
||||||
'page': 1,
|
'concept_type': 'leaf' # 只获取叶子概念
|
||||||
'sort_by': 'change_pct'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if trade_date:
|
if trade_date:
|
||||||
request_data['trade_date'] = trade_date
|
params['trade_date'] = trade_date
|
||||||
|
|
||||||
# 调用概念中心API
|
response = requests.get(concept_api_url, params=params, timeout=10)
|
||||||
response = requests.post(concept_api_url, json=request_data, timeout=10)
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
top_concepts = []
|
top_concepts = []
|
||||||
|
|
||||||
for concept in data.get('results', []):
|
for concept in data.get('concepts', []):
|
||||||
# 处理 stocks 字段:兼容 {name, code} 和 {stock_name, stock_code} 两种格式
|
# /price/list 返回的字段较少,适配为前端需要的格式
|
||||||
raw_stocks = concept.get('stocks', [])
|
|
||||||
formatted_stocks = []
|
|
||||||
for stock in raw_stocks:
|
|
||||||
# 优先使用 stock_name,其次使用 name
|
|
||||||
stock_name = stock.get('stock_name') or stock.get('name', '')
|
|
||||||
stock_code = stock.get('stock_code') or stock.get('code', '')
|
|
||||||
formatted_stocks.append({
|
|
||||||
'stock_name': stock_name,
|
|
||||||
'stock_code': stock_code,
|
|
||||||
'name': stock_name, # 兼容旧格式
|
|
||||||
'code': stock_code # 兼容旧格式
|
|
||||||
})
|
|
||||||
|
|
||||||
# 保持与 /concept-api/search 相同的字段结构,并添加新字段
|
|
||||||
top_concepts.append({
|
top_concepts.append({
|
||||||
'concept_id': concept.get('concept_id'),
|
'concept_id': concept.get('concept_id'),
|
||||||
'concept': concept.get('concept'), # 原始字段名
|
'concept': concept.get('concept_name'),
|
||||||
'concept_name': concept.get('concept'), # 兼容旧字段名
|
'concept_name': concept.get('concept_name'),
|
||||||
'description': concept.get('description'),
|
'description': None, # /price/list 不返回此字段
|
||||||
'stock_count': concept.get('stock_count', 0),
|
'stock_count': concept.get('stock_count', 0),
|
||||||
'score': concept.get('score'),
|
'score': None,
|
||||||
'match_type': concept.get('match_type'),
|
'match_type': None,
|
||||||
'price_info': concept.get('price_info', {}), # 完整的价格信息
|
'price_info': {
|
||||||
'change_percent': concept.get('price_info', {}).get('avg_change_pct', 0), # 兼容旧字段
|
'avg_change_pct': concept.get('avg_change_pct')
|
||||||
'tags': concept.get('tags', []), # 标签列表
|
},
|
||||||
'outbreak_dates': concept.get('outbreak_dates', []), # 爆发日期列表
|
'change_percent': concept.get('avg_change_pct', 0),
|
||||||
'hierarchy': concept.get('hierarchy'), # 层级信息 {lv1, lv2, lv3}
|
'tags': [], # /price/list 不返回此字段
|
||||||
'stocks': formatted_stocks, # 返回格式化后的股票列表
|
'outbreak_dates': [], # /price/list 不返回此字段
|
||||||
'hot_score': concept.get('hot_score')
|
'hierarchy': concept.get('hierarchy'),
|
||||||
|
'stocks': [], # /price/list 不返回此字段
|
||||||
|
'hot_score': None
|
||||||
})
|
})
|
||||||
|
|
||||||
# 格式化日期为 YYYY-MM-DD
|
# 格式化日期为 YYYY-MM-DD
|
||||||
price_date = data.get('price_date', '')
|
price_date = data.get('trade_date', '')
|
||||||
formatted_date = str(price_date).split(' ')[0][:10] if price_date else ''
|
formatted_date = str(price_date)[:10] if price_date else ''
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
|
|||||||
@@ -449,7 +449,60 @@ const ConceptCenter = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const sortToUse = customSortBy !== null ? customSortBy : sortBy;
|
const sortToUse = customSortBy !== null ? customSortBy : sortBy;
|
||||||
|
const dateStr = date ? date.toISOString().split('T')[0] : null;
|
||||||
|
|
||||||
|
// 判断是否使用 /price/list 接口
|
||||||
|
// 条件:无搜索词 + 按涨跌幅排序(升序或降序)+ 无层级筛选
|
||||||
|
const isChangePctSort = sortToUse === 'change_pct' || sortToUse === 'change_pct_asc';
|
||||||
|
const hasNoFilter = !filter?.lv1 && !filter?.lv2;
|
||||||
|
const shouldUsePriceList = !query && isChangePctSort && hasNoFilter;
|
||||||
|
|
||||||
|
if (shouldUsePriceList) {
|
||||||
|
// 使用 /price/list 接口获取全部概念的正确排序
|
||||||
|
const sortParam = sortToUse === 'change_pct_asc' ? 'change_asc' : 'change_desc';
|
||||||
|
const offset = (page - 1) * pageSize;
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
sort_by: sortParam,
|
||||||
|
limit: pageSize.toString(),
|
||||||
|
offset: offset.toString(),
|
||||||
|
concept_type: 'leaf' // 只获取叶子概念
|
||||||
|
});
|
||||||
|
if (dateStr) {
|
||||||
|
params.append('trade_date', dateStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${API_BASE_URL}/price/list?${params}`);
|
||||||
|
if (!response.ok) throw new Error('获取涨跌幅数据失败');
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// 适配 /price/list 返回数据为 /search 格式
|
||||||
|
const adaptedResults = data.concepts.map(item => ({
|
||||||
|
concept_id: item.concept_id,
|
||||||
|
concept: item.concept_name,
|
||||||
|
concept_name: item.concept_name,
|
||||||
|
price_info: {
|
||||||
|
avg_change_pct: item.avg_change_pct
|
||||||
|
},
|
||||||
|
stock_count: item.stock_count,
|
||||||
|
hierarchy: item.hierarchy,
|
||||||
|
// 以下字段 /price/list 不返回,填入默认值
|
||||||
|
description: null,
|
||||||
|
tags: [],
|
||||||
|
outbreak_dates: [],
|
||||||
|
stocks: []
|
||||||
|
}));
|
||||||
|
|
||||||
|
setConcepts(adaptedResults);
|
||||||
|
setTotalConcepts(data.total || 0);
|
||||||
|
setTotalPages(Math.ceil((data.total || 0) / pageSize));
|
||||||
|
setCurrentPage(page);
|
||||||
|
|
||||||
|
if (data.trade_date) {
|
||||||
|
setSelectedDate(new Date(data.trade_date));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 使用原有的 /search 接口
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
query: query,
|
query: query,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
@@ -457,8 +510,8 @@ const ConceptCenter = () => {
|
|||||||
sort_by: sortToUse
|
sort_by: sortToUse
|
||||||
};
|
};
|
||||||
|
|
||||||
if (date) {
|
if (dateStr) {
|
||||||
requestBody.trade_date = date.toISOString().split('T')[0];
|
requestBody.trade_date = dateStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加层级筛选参数
|
// 添加层级筛选参数
|
||||||
@@ -489,8 +542,9 @@ const ConceptCenter = () => {
|
|||||||
if (data.price_date) {
|
if (data.price_date) {
|
||||||
setSelectedDate(new Date(data.price_date));
|
setSelectedDate(new Date(data.price_date));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('ConceptCenter', 'fetchConcepts', error, { query, page, date: date?.toISOString(), sortToUse, filter });
|
logger.error('ConceptCenter', 'fetchConcepts', error, { query, page, date: date?.toISOString(), customSortBy, filter });
|
||||||
|
|
||||||
// ❌ 移除获取数据失败toast
|
// ❌ 移除获取数据失败toast
|
||||||
// toast({ title: '获取数据失败', description: error.message, status: 'error', duration: 3000, isClosable: true });
|
// toast({ title: '获取数据失败', description: error.message, status: 'error', duration: 3000, isClosable: true });
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
useColorModeValue,
|
useColorModeValue,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Flex,
|
Flex,
|
||||||
keyframes,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
import { keyframes } from '@emotion/react';
|
||||||
import {
|
import {
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import {
|
|||||||
PopoverBody,
|
PopoverBody,
|
||||||
Portal,
|
Portal,
|
||||||
chakra,
|
chakra,
|
||||||
keyframes,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
import { keyframes } from '@emotion/react';
|
||||||
import {
|
import {
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import {
|
|||||||
GridItem,
|
GridItem,
|
||||||
IconButton,
|
IconButton,
|
||||||
Collapse,
|
Collapse,
|
||||||
keyframes,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
import { keyframes } from '@emotion/react';
|
||||||
import {
|
import {
|
||||||
Flame,
|
Flame,
|
||||||
List,
|
List,
|
||||||
|
|||||||
Reference in New Issue
Block a user