update pay ui

This commit is contained in:
2025-12-03 12:39:59 +08:00
parent ae9904cd03
commit dd975a65b2
3 changed files with 166 additions and 65 deletions

131
app.py
View File

@@ -5801,6 +5801,23 @@ def get_stock_quotes():
if not codes:
return jsonify({'success': False, 'error': '请提供股票代码'}), 400
# 标准化股票代码(确保带后缀,用于 ClickHouse 查询)
def normalize_stock_code(code):
"""将股票代码标准化为带后缀格式(如 300274.SZ"""
if '.' in code:
return code # 已经带后缀
# 根据代码规则添加后缀6/0/3开头为深圳其他为上海
if code.startswith(('6',)):
return f"{code}.SH"
else:
return f"{code}.SZ"
# 保留原始代码用于返回结果,同时创建标准化代码用于 ClickHouse 查询
original_codes = codes
normalized_codes = [normalize_stock_code(code) for code in codes]
# 创建原始代码到标准化代码的映射
code_mapping = dict(zip(original_codes, normalized_codes))
# 处理事件时间
if event_time_str:
try:
@@ -5829,13 +5846,12 @@ def get_stock_quotes():
# 构建代码到名称的映射
base_name_map = {row[0]: row[1] for row in result}
# 为每个完整代码(带后缀)分配名称
for code in codes:
base_code = code.split('.')[0]
if base_code in base_name_map:
stock_names[code] = base_name_map[base_code]
else:
stock_names[code] = f"股票{base_code}"
# 为原始代码和标准化代码都分配名称
for orig_code, norm_code in code_mapping.items():
base_code = orig_code.split('.')[0]
name = base_name_map.get(base_code, f"股票{base_code}")
stock_names[orig_code] = name
stock_names[norm_code] = name
def get_trading_day_and_times(event_datetime):
event_date = event_datetime.date()
@@ -5948,11 +5964,11 @@ def get_stock_quotes():
# 构建代码到收盘价的映射(需要匹配完整代码格式)
base_close_map = {row[0]: float(row[1]) if row[1] else None for row in prev_close_result}
# 为每个完整代码(带后缀)分配收盘价
for code in codes:
base_code = code.split('.')[0]
# 为每个标准化代码(带后缀)分配收盘价,用于 ClickHouse 查询结果匹配
for norm_code in normalized_codes:
base_code = norm_code.split('.')[0]
if base_code in base_close_map:
prev_close_map[code] = base_close_map[base_code]
prev_close_map[norm_code] = base_close_map[base_code]
print(f"前一交易日({prev_trading_day})收盘价查询返回 {len(prev_close_result)} 条数据")
@@ -5974,7 +5990,7 @@ def get_stock_quotes():
"""
batch_data = client.execute(batch_price_query, {
'codes': codes,
'codes': normalized_codes, # 使用标准化后的代码查询 ClickHouse
'start': start_datetime,
'end': end_datetime
})
@@ -5998,41 +6014,43 @@ def get_stock_quotes():
'change': change_pct
}
# 组装结果(所有股票)
for code in codes:
price_info = price_data_map.get(code)
# 组装结果(所有股票)- 使用原始代码作为 key 返回
for orig_code in original_codes:
norm_code = code_mapping[orig_code]
price_info = price_data_map.get(norm_code)
if price_info:
results[code] = {
results[orig_code] = {
'price': price_info['price'],
'change': price_info['change'],
'name': stock_names.get(code, f'股票{code.split(".")[0]}')
'name': stock_names.get(orig_code, stock_names.get(norm_code, f'股票{orig_code.split(".")[0]}'))
}
else:
# 批量查询没有返回的股票
results[code] = {
results[orig_code] = {
'price': None,
'change': None,
'name': stock_names.get(code, f'股票{code.split(".")[0]}')
'name': stock_names.get(orig_code, stock_names.get(norm_code, f'股票{orig_code.split(".")[0]}'))
}
except Exception as e:
print(f"批量查询 ClickHouse 失败: {e},回退到逐只查询")
# 降级方案:逐只股票查询(使用前一交易日收盘价计算涨跌幅)
for code in codes:
for orig_code in original_codes:
norm_code = code_mapping[orig_code]
try:
# 查询当前价格
# 查询当前价格(使用标准化代码查询 ClickHouse
current_data = client.execute("""
SELECT close FROM stock_minute
WHERE code = %(code)s AND timestamp >= %(start)s AND timestamp <= %(end)s
ORDER BY timestamp DESC LIMIT 1
""", {'code': code, 'start': start_datetime, 'end': end_datetime})
""", {'code': norm_code, 'start': start_datetime, 'end': end_datetime})
last_price = float(current_data[0][0]) if current_data and current_data[0] and current_data[0][0] else None
# 从 MySQL ea_trade 表查询前一交易日收盘价
prev_close = None
if prev_trading_day and last_price is not None:
base_code = code.split('.')[0]
base_code = orig_code.split('.')[0]
with engine.connect() as conn:
prev_result = conn.execute(text("""
SELECT F007N as close_price
@@ -6046,14 +6064,15 @@ def get_stock_quotes():
if last_price is not None and prev_close is not None and prev_close > 0:
change_pct = (last_price - prev_close) / prev_close * 100
results[code] = {
# 使用原始代码作为 key 返回
results[orig_code] = {
'price': last_price,
'change': change_pct,
'name': stock_names.get(code, f'股票{code.split(".")[0]}')
'name': stock_names.get(orig_code, f'股票{orig_code.split(".")[0]}')
}
except Exception as inner_e:
print(f"Error processing stock {code}: {inner_e}")
results[code] = {'price': None, 'change': None, 'name': stock_names.get(code, f'股票{code.split(".")[0]}')}
print(f"Error processing stock {orig_code}: {inner_e}")
results[orig_code] = {'price': None, 'change': None, 'name': stock_names.get(orig_code, f'股票{orig_code.split(".")[0]}')}
# 返回标准格式
return jsonify({'success': True, 'data': results})
@@ -6303,6 +6322,23 @@ def get_batch_kline_data():
if len(codes) > 50:
return jsonify({'success': False, 'error': '单次最多查询50只股票'}), 400
# 标准化股票代码(确保带后缀,用于 ClickHouse 查询)
def normalize_stock_code(code):
"""将股票代码标准化为带后缀格式(如 300274.SZ"""
if '.' in code:
return code # 已经带后缀
# 根据代码规则添加后缀6开头为上海其他为深圳
if code.startswith(('6',)):
return f"{code}.SH"
else:
return f"{code}.SZ"
# 保留原始代码用于返回结果,同时创建标准化代码用于 ClickHouse 查询
original_codes = codes
normalized_codes = [normalize_stock_code(code) for code in codes]
code_mapping = dict(zip(original_codes, normalized_codes))
reverse_mapping = dict(zip(normalized_codes, original_codes))
try:
event_datetime = datetime.fromisoformat(event_time) if event_time else datetime.now()
except ValueError:
@@ -6333,10 +6369,10 @@ def get_batch_kline_data():
target_date = next_trade_date
if not target_date:
# 返回空数据
# 返回空数据(使用原始代码作为 key
return jsonify({
'success': True,
'data': {code: {'data': [], 'trade_date': event_datetime.date().strftime('%Y-%m-%d'), 'type': chart_type} for code in codes}
'data': {code: {'data': [], 'trade_date': event_datetime.date().strftime('%Y-%m-%d'), 'type': chart_type} for code in original_codes}
})
start_time = datetime.combine(target_date, dt_time(9, 30))
@@ -6345,7 +6381,7 @@ def get_batch_kline_data():
results = {}
if chart_type == 'timeline':
# 批量查询分时数据
# 批量查询分时数据(使用标准化代码查询 ClickHouse
batch_data = client.execute("""
SELECT code, timestamp, close, volume
FROM stock_minute
@@ -6353,31 +6389,32 @@ def get_batch_kline_data():
AND timestamp BETWEEN %(start)s AND %(end)s
ORDER BY code, timestamp
""", {
'codes': codes,
'codes': normalized_codes, # 使用标准化代码
'start': start_time,
'end': end_time
})
# 按股票代码分组
# 按股票代码分组(标准化代码 -> 数据列表)
stock_data = {}
for row in batch_data:
code = row[0]
if code not in stock_data:
stock_data[code] = []
stock_data[code].append({
norm_code = row[0]
if norm_code not in stock_data:
stock_data[norm_code] = []
stock_data[norm_code].append({
'time': row[1].strftime('%H:%M'),
'price': float(row[2]),
'volume': float(row[3])
})
# 组装结果
for code in codes:
base_code = code.split('.')[0]
# 组装结果(使用原始代码作为 key 返回)
for orig_code in original_codes:
norm_code = code_mapping[orig_code]
base_code = orig_code.split('.')[0]
stock_name = stock_names.get(base_code, f'股票{base_code}')
data_list = stock_data.get(code, [])
data_list = stock_data.get(norm_code, [])
results[code] = {
'code': code,
results[orig_code] = {
'code': orig_code,
'name': stock_name,
'data': data_list,
'trade_date': target_date.strftime('%Y-%m-%d'),
@@ -6417,14 +6454,14 @@ def get_batch_kline_data():
'volume': float(row[6]) if row[6] else 0
})
# 组装结果
for code in codes:
base_code = code.split('.')[0]
# 组装结果(使用原始代码作为 key 返回)
for orig_code in original_codes:
base_code = orig_code.split('.')[0]
stock_name = stock_names.get(base_code, f'股票{base_code}')
data_list = stock_data.get(base_code, [])
results[code] = {
'code': code,
results[orig_code] = {
'code': orig_code,
'name': stock_name,
'data': data_list,
'trade_date': target_date.strftime('%Y-%m-%d'),