update pay ui
This commit is contained in:
@@ -749,6 +749,130 @@ async def get_hierarchy():
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# 注意:/hierarchy/price 必须在 /hierarchy/{lv1_id} 之前定义,否则会被错误匹配
|
||||
@app.get("/hierarchy/price", tags=["Hierarchy"])
|
||||
async def get_hierarchy_price_early(
|
||||
trade_date: Optional[date] = Query(None, description="交易日期,默认最新"),
|
||||
lv1_filter: Optional[str] = Query(None, description="筛选特定一级分类")
|
||||
):
|
||||
"""获取层级概念(lv1/lv2/lv3)的涨跌幅数据"""
|
||||
logger.info(f"[hierarchy/price] 请求参数: trade_date={trade_date}, lv1_filter={lv1_filter}")
|
||||
|
||||
if not mysql_pool:
|
||||
logger.error("[hierarchy/price] MySQL连接池不可用")
|
||||
raise HTTPException(status_code=503, detail="MySQL连接不可用")
|
||||
|
||||
try:
|
||||
async with mysql_pool.acquire() as conn:
|
||||
async with conn.cursor(aiomysql.DictCursor) as cursor:
|
||||
# 获取交易日期
|
||||
query_date = trade_date
|
||||
if query_date is None:
|
||||
# 优先从母概念查最新日期
|
||||
await cursor.execute(
|
||||
"SELECT MAX(trade_date) as max_date FROM concept_daily_stats WHERE concept_type IN ('lv1', 'lv2', 'lv3')"
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
logger.info(f"[hierarchy/price] 查询母概念最新日期: {result}")
|
||||
if result and result['max_date']:
|
||||
query_date = result['max_date']
|
||||
else:
|
||||
# 尝试从全部数据获取
|
||||
await cursor.execute("SELECT MAX(trade_date) as max_date FROM concept_daily_stats")
|
||||
result = await cursor.fetchone()
|
||||
logger.info(f"[hierarchy/price] 查询全部最新日期: {result}")
|
||||
if not result or not result['max_date']:
|
||||
raise HTTPException(status_code=404, detail="无涨跌幅数据")
|
||||
query_date = result['max_date']
|
||||
|
||||
logger.info(f"[hierarchy/price] 使用查询日期: {query_date}")
|
||||
|
||||
# 构建查询
|
||||
base_query = """
|
||||
SELECT concept_id, concept_name, concept_type, trade_date, avg_change_pct, stock_count
|
||||
FROM concept_daily_stats
|
||||
WHERE trade_date = %s AND concept_type = %s
|
||||
"""
|
||||
|
||||
if lv1_filter:
|
||||
base_query += " AND concept_name LIKE %s"
|
||||
|
||||
base_query += " ORDER BY avg_change_pct DESC"
|
||||
|
||||
lv1_concepts = []
|
||||
lv2_concepts = []
|
||||
lv3_concepts = []
|
||||
|
||||
# 查询 lv1
|
||||
if lv1_filter:
|
||||
await cursor.execute(base_query, (query_date, 'lv1', f"%{lv1_filter}%"))
|
||||
else:
|
||||
await cursor.execute(base_query, (query_date, 'lv1'))
|
||||
lv1_rows = await cursor.fetchall()
|
||||
logger.info(f"[hierarchy/price] lv1查询结果数量: {len(lv1_rows)}")
|
||||
for row in lv1_rows:
|
||||
lv1_concepts.append({
|
||||
"concept_id": row['concept_id'],
|
||||
"concept_name": row['concept_name'],
|
||||
"concept_type": 'lv1',
|
||||
"trade_date": str(row['trade_date']),
|
||||
"avg_change_pct": float(row['avg_change_pct']) if row['avg_change_pct'] else None,
|
||||
"stock_count": row['stock_count']
|
||||
})
|
||||
|
||||
# 查询 lv2
|
||||
if lv1_filter:
|
||||
await cursor.execute(base_query, (query_date, 'lv2', f"%{lv1_filter}%"))
|
||||
else:
|
||||
await cursor.execute(base_query, (query_date, 'lv2'))
|
||||
lv2_rows = await cursor.fetchall()
|
||||
logger.info(f"[hierarchy/price] lv2查询结果数量: {len(lv2_rows)}")
|
||||
for row in lv2_rows:
|
||||
lv2_concepts.append({
|
||||
"concept_id": row['concept_id'],
|
||||
"concept_name": row['concept_name'],
|
||||
"concept_type": 'lv2',
|
||||
"trade_date": str(row['trade_date']),
|
||||
"avg_change_pct": float(row['avg_change_pct']) if row['avg_change_pct'] else None,
|
||||
"stock_count": row['stock_count']
|
||||
})
|
||||
|
||||
# 查询 lv3
|
||||
if lv1_filter:
|
||||
await cursor.execute(base_query, (query_date, 'lv3', f"%{lv1_filter}%"))
|
||||
else:
|
||||
await cursor.execute(base_query, (query_date, 'lv3'))
|
||||
lv3_rows = await cursor.fetchall()
|
||||
logger.info(f"[hierarchy/price] lv3查询结果数量: {len(lv3_rows)}")
|
||||
for row in lv3_rows:
|
||||
lv3_concepts.append({
|
||||
"concept_id": row['concept_id'],
|
||||
"concept_name": row['concept_name'],
|
||||
"concept_type": 'lv3',
|
||||
"trade_date": str(row['trade_date']),
|
||||
"avg_change_pct": float(row['avg_change_pct']) if row['avg_change_pct'] else None,
|
||||
"stock_count": row['stock_count']
|
||||
})
|
||||
|
||||
logger.info(f"[hierarchy/price] 返回结果: lv1={len(lv1_concepts)}, lv2={len(lv2_concepts)}, lv3={len(lv3_concepts)}")
|
||||
return {
|
||||
"trade_date": str(query_date),
|
||||
"lv1_concepts": lv1_concepts,
|
||||
"lv2_concepts": lv2_concepts,
|
||||
"lv3_concepts": lv3_concepts,
|
||||
"total_count": len(lv1_concepts) + len(lv2_concepts) + len(lv3_concepts)
|
||||
}
|
||||
|
||||
except HTTPException as he:
|
||||
logger.error(f"[hierarchy/price] HTTPException: {he.status_code} - {he.detail}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[hierarchy/price] 获取层级涨跌幅失败: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/hierarchy/{lv1_id}", tags=["Hierarchy"])
|
||||
async def get_hierarchy_level(
|
||||
lv1_id: str,
|
||||
@@ -1494,12 +1618,33 @@ class HierarchyPriceResponse(BaseModel):
|
||||
total_count: int
|
||||
|
||||
|
||||
@app.get("/hierarchy/price", response_model=HierarchyPriceResponse, tags=["Hierarchy"])
|
||||
async def get_hierarchy_price(
|
||||
class ConceptPriceItem(BaseModel):
|
||||
"""概念价格项"""
|
||||
concept_id: str
|
||||
concept_name: str
|
||||
concept_type: str # leaf/lv1/lv2/lv3
|
||||
trade_date: date
|
||||
avg_change_pct: Optional[float] = None
|
||||
stock_count: Optional[int] = None
|
||||
hierarchy: Optional[HierarchyInfo] = None
|
||||
|
||||
|
||||
class ConceptPriceListResponse(BaseModel):
|
||||
"""概念价格列表响应"""
|
||||
trade_date: date
|
||||
total: int
|
||||
concepts: List[ConceptPriceItem]
|
||||
|
||||
|
||||
@app.get("/price/list", response_model=ConceptPriceListResponse, tags=["Price"])
|
||||
async def get_concept_price_list(
|
||||
trade_date: Optional[date] = Query(None, description="交易日期,默认最新"),
|
||||
lv1_filter: Optional[str] = Query(None, description="筛选特定一级分类")
|
||||
concept_type: Optional[str] = Query(None, description="概念类型: leaf/lv1/lv2/lv3,默认全部"),
|
||||
sort_by: str = Query("change_desc", description="排序: change_desc(涨幅降序), change_asc(涨幅升序), stock_count(股票数)"),
|
||||
limit: int = Query(100, ge=1, le=1000, description="返回数量"),
|
||||
offset: int = Query(0, ge=0, description="偏移量")
|
||||
):
|
||||
"""获取层级概念(lv1/lv2/lv3)的涨跌幅数据"""
|
||||
"""批量获取概念涨跌幅列表"""
|
||||
if not mysql_pool:
|
||||
raise HTTPException(status_code=503, detail="MySQL连接不可用")
|
||||
|
||||
@@ -1508,87 +1653,74 @@ async def get_hierarchy_price(
|
||||
async with conn.cursor(aiomysql.DictCursor) as cursor:
|
||||
# 获取交易日期
|
||||
if trade_date is None:
|
||||
await cursor.execute(
|
||||
"SELECT MAX(trade_date) as max_date FROM concept_daily_stats WHERE concept_type != 'leaf'"
|
||||
)
|
||||
await cursor.execute("SELECT MAX(trade_date) as max_date FROM concept_daily_stats")
|
||||
result = await cursor.fetchone()
|
||||
if not result or not result['max_date']:
|
||||
raise HTTPException(status_code=404, detail="无母概念涨跌幅数据")
|
||||
raise HTTPException(status_code=404, detail="无涨跌幅数据")
|
||||
trade_date = result['max_date']
|
||||
|
||||
# 构建查询
|
||||
base_query = """
|
||||
where_conditions = ["trade_date = %s"]
|
||||
params = [trade_date]
|
||||
|
||||
if concept_type and concept_type in ['leaf', 'lv1', 'lv2', 'lv3']:
|
||||
where_conditions.append("concept_type = %s")
|
||||
params.append(concept_type)
|
||||
|
||||
# 排序
|
||||
order_clause = "avg_change_pct DESC"
|
||||
if sort_by == "change_asc":
|
||||
order_clause = "avg_change_pct ASC"
|
||||
elif sort_by == "stock_count":
|
||||
order_clause = "stock_count DESC"
|
||||
|
||||
# 获取总数
|
||||
count_query = f"SELECT COUNT(*) as cnt FROM concept_daily_stats WHERE {' AND '.join(where_conditions)}"
|
||||
await cursor.execute(count_query, params)
|
||||
total = (await cursor.fetchone())['cnt']
|
||||
|
||||
# 获取数据
|
||||
query = f"""
|
||||
SELECT concept_id, concept_name, concept_type, trade_date, avg_change_pct, stock_count
|
||||
FROM concept_daily_stats
|
||||
WHERE trade_date = %s AND concept_type = %s
|
||||
WHERE {' AND '.join(where_conditions)}
|
||||
ORDER BY {order_clause}
|
||||
LIMIT %s OFFSET %s
|
||||
"""
|
||||
params.extend([limit, offset])
|
||||
await cursor.execute(query, params)
|
||||
rows = await cursor.fetchall()
|
||||
|
||||
if lv1_filter:
|
||||
base_query += " AND concept_name LIKE %s"
|
||||
concepts = []
|
||||
for row in rows:
|
||||
concept_name = row['concept_name']
|
||||
# 获取层级信息(仅对叶子概念)
|
||||
hierarchy = None
|
||||
if row['concept_type'] == 'leaf':
|
||||
hierarchy_info = get_concept_hierarchy(concept_name)
|
||||
if hierarchy_info:
|
||||
hierarchy = HierarchyInfo(**hierarchy_info)
|
||||
|
||||
base_query += " ORDER BY avg_change_pct DESC"
|
||||
|
||||
lv1_concepts = []
|
||||
lv2_concepts = []
|
||||
lv3_concepts = []
|
||||
|
||||
# 查询 lv1
|
||||
if lv1_filter:
|
||||
await cursor.execute(base_query, (trade_date, 'lv1', f"%{lv1_filter}%"))
|
||||
else:
|
||||
await cursor.execute(base_query, (trade_date, 'lv1'))
|
||||
for row in await cursor.fetchall():
|
||||
lv1_concepts.append(HierarchyPriceItem(
|
||||
concepts.append(ConceptPriceItem(
|
||||
concept_id=row['concept_id'],
|
||||
concept_name=row['concept_name'],
|
||||
concept_type='lv1',
|
||||
concept_name=concept_name,
|
||||
concept_type=row['concept_type'],
|
||||
trade_date=row['trade_date'],
|
||||
avg_change_pct=float(row['avg_change_pct']) if row['avg_change_pct'] else None,
|
||||
stock_count=row['stock_count']
|
||||
stock_count=row['stock_count'],
|
||||
hierarchy=hierarchy
|
||||
))
|
||||
|
||||
# 查询 lv2
|
||||
if lv1_filter:
|
||||
await cursor.execute(base_query, (trade_date, 'lv2', f"%{lv1_filter}%"))
|
||||
else:
|
||||
await cursor.execute(base_query, (trade_date, 'lv2'))
|
||||
for row in await cursor.fetchall():
|
||||
lv2_concepts.append(HierarchyPriceItem(
|
||||
concept_id=row['concept_id'],
|
||||
concept_name=row['concept_name'],
|
||||
concept_type='lv2',
|
||||
trade_date=row['trade_date'],
|
||||
avg_change_pct=float(row['avg_change_pct']) if row['avg_change_pct'] else None,
|
||||
stock_count=row['stock_count']
|
||||
))
|
||||
|
||||
# 查询 lv3
|
||||
if lv1_filter:
|
||||
await cursor.execute(base_query, (trade_date, 'lv3', f"%{lv1_filter}%"))
|
||||
else:
|
||||
await cursor.execute(base_query, (trade_date, 'lv3'))
|
||||
for row in await cursor.fetchall():
|
||||
lv3_concepts.append(HierarchyPriceItem(
|
||||
concept_id=row['concept_id'],
|
||||
concept_name=row['concept_name'],
|
||||
concept_type='lv3',
|
||||
trade_date=row['trade_date'],
|
||||
avg_change_pct=float(row['avg_change_pct']) if row['avg_change_pct'] else None,
|
||||
stock_count=row['stock_count']
|
||||
))
|
||||
|
||||
return HierarchyPriceResponse(
|
||||
return ConceptPriceListResponse(
|
||||
trade_date=trade_date,
|
||||
lv1_concepts=lv1_concepts,
|
||||
lv2_concepts=lv2_concepts,
|
||||
lv3_concepts=lv3_concepts,
|
||||
total_count=len(lv1_concepts) + len(lv2_concepts) + len(lv3_concepts)
|
||||
total=total,
|
||||
concepts=concepts
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"获取层级涨跌幅失败: {e}")
|
||||
logger.error(f"获取概念涨跌幅列表失败: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user