update pay ui

This commit is contained in:
2025-12-05 14:38:47 +08:00
parent 4df5f4dda2
commit bf1e9d3ae6
2 changed files with 361 additions and 165 deletions

View File

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