update pay ui

This commit is contained in:
2025-12-12 01:14:31 +08:00
parent 8af8d40f0c
commit 63345184c9
4 changed files with 253 additions and 25 deletions

122
app.py
View File

@@ -2156,35 +2156,47 @@ def create_payment_order():
db.session.rollback()
return jsonify({'success': False, 'error': f'订单创建失败: {str(e)}'}), 500
# 尝试调用真实的微信支付API
# 尝试调用真实的微信支付API(使用 subprocess 绕过 eventlet DNS 问题)
try:
from wechat_pay import create_wechat_pay_instance, check_wechat_pay_ready
import subprocess
import urllib.parse
# 检查微信支付是否就绪
is_ready, ready_msg = check_wechat_pay_ready()
if not is_ready:
# 使用模拟二维码
# 使用独立脚本检查配置
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'wechat_pay_worker.py')
# 先检查配置
check_result = subprocess.run(
[sys.executable, script_path, 'check'],
capture_output=True, text=True, timeout=10
)
if check_result.returncode != 0:
check_data = json.loads(check_result.stdout) if check_result.stdout else {}
ready_msg = check_data.get('error', check_data.get('message', '未知错误'))
order.qr_code_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=wxpay://order/{order.order_no}"
order.remark = f"演示模式 - {ready_msg}"
else:
wechat_pay = create_wechat_pay_instance()
# 创建微信支付订单
plan_display_name = f"{plan_name.upper()}版本-{billing_cycle}"
wechat_result = wechat_pay.create_native_order(
order_no=order.order_no,
total_fee=float(amount),
body=f"VFr-{plan_display_name}",
product_id=f"{plan_name}_{billing_cycle}"
body = f"VFr-{plan_display_name}"
product_id = f"{plan_name}_{billing_cycle}"
create_result = subprocess.run(
[sys.executable, script_path, 'create', order.order_no, str(float(amount)), body, product_id],
capture_output=True, text=True, timeout=60
)
if wechat_result['success']:
print(f"[微信支付] 创建订单返回: {create_result.stdout}")
if create_result.stderr:
print(f"[微信支付] 错误输出: {create_result.stderr}")
wechat_result = json.loads(create_result.stdout) if create_result.stdout else {'success': False, 'error': '无返回'}
if wechat_result.get('success'):
# 获取微信返回的原始code_url
wechat_code_url = wechat_result['code_url']
# 将微信协议URL转换为二维码图片URL
import urllib.parse
encoded_url = urllib.parse.quote(wechat_code_url, safe='')
qr_image_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data={encoded_url}"
@@ -2196,10 +2208,16 @@ def create_payment_order():
order.qr_code_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=wxpay://order/{order.order_no}"
order.remark = f"微信支付失败: {wechat_result.get('error')}"
except ImportError as e:
except subprocess.TimeoutExpired:
order.qr_code_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=wxpay://order/{order.order_no}"
order.remark = "微信支付模块未配置"
order.remark = "微信支付超时"
except json.JSONDecodeError as e:
order.qr_code_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=wxpay://order/{order.order_no}"
order.remark = f"微信支付返回解析失败: {str(e)}"
except Exception as e:
import traceback
print(f"[微信支付] Exception: {e}")
traceback.print_exc()
order.qr_code_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=wxpay://order/{order.order_no}"
order.remark = f"支付异常: {str(e)}"
@@ -10455,12 +10473,39 @@ def broadcast_new_event(event):
# Redis Key 用于多 Worker 协调
REDIS_KEY_LAST_MAX_EVENT_ID = 'vf:event_polling:last_max_id'
REDIS_KEY_POLLING_LOCK = 'vf:event_polling:lock'
REDIS_KEY_PENDING_EVENTS = 'vf:event_polling:pending_events' # 待推送事件集合(没有 related_stocks 的事件)
# 本地缓存(减少 Redis 查询)
_local_last_max_event_id = 0
_polling_initialized = False
def _add_pending_event(event_id):
"""将事件添加到待推送列表"""
try:
redis_client.sadd(REDIS_KEY_PENDING_EVENTS, str(event_id))
except Exception as e:
print(f'[轮询 WARN] 添加待推送事件失败: {e}')
def _remove_pending_event(event_id):
"""从待推送列表移除事件"""
try:
redis_client.srem(REDIS_KEY_PENDING_EVENTS, str(event_id))
except Exception as e:
print(f'[轮询 WARN] 移除待推送事件失败: {e}')
def _get_pending_events():
"""获取所有待推送事件ID"""
try:
pending = redis_client.smembers(REDIS_KEY_PENDING_EVENTS)
return [int(eid) for eid in pending] if pending else []
except Exception as e:
print(f'[轮询 WARN] 获取待推送事件失败: {e}')
return []
def _get_last_max_event_id():
"""从 Redis 获取最大事件 ID"""
try:
@@ -10491,6 +10536,11 @@ def poll_new_events():
1. 使用 Redis 分布式锁,确保同一时刻只有一个 Worker 执行轮询
2. 使用 Redis 存储 last_max_event_id所有 Worker 共享状态
3. 通过 Redis 消息队列广播到所有 Worker 的客户端
待推送事件机制:
- 当事件首次被检测到但没有 related_stocks 时,加入待推送列表
- 每次轮询时检查待推送列表中的事件是否已有 related_stocks
- 有则推送并从列表移除超过24小时的事件自动清理
"""
import os
@@ -10528,6 +10578,36 @@ def poll_new_events():
print(f'[轮询] 数据库查询: 找到 {len(events_in_24h)} 个近24小时内的事件')
# 创建事件ID到事件对象的映射
events_map = {event.id: event for event in events_in_24h}
# === 步骤1: 检查待推送列表中的事件 ===
pending_event_ids = _get_pending_events()
print(f'[轮询] 待推送列表: {len(pending_event_ids)} 个事件')
pushed_from_pending = 0
for pending_id in pending_event_ids:
if pending_id in events_map:
event = events_map[pending_id]
related_stocks_count = event.related_stocks.count()
if related_stocks_count > 0:
# 事件现在有 related_stocks 了,推送它
broadcast_new_event(event)
_remove_pending_event(pending_id)
pushed_from_pending += 1
print(f'[轮询] ✓ 待推送事件 ID={pending_id} 现在有 {related_stocks_count} 个关联股票,已推送')
else:
print(f'[轮询] - 待推送事件 ID={pending_id} 仍无关联股票,继续等待')
else:
# 事件已超过24小时或已删除从待推送列表移除
_remove_pending_event(pending_id)
print(f'[轮询] × 待推送事件 ID={pending_id} 已过期或不存在,已移除')
if pushed_from_pending > 0:
print(f'[轮询] 从待推送列表推送了 {pushed_from_pending} 个事件')
# === 步骤2: 检查新事件 ===
# 找出新插入的事件ID > last_max_event_id
new_events = [
event for event in events_in_24h
@@ -10540,6 +10620,7 @@ def poll_new_events():
print(f'[轮询] 发现 {len(new_events)} 个新事件')
pushed_count = 0
pending_count = 0
for event in new_events:
# 检查事件是否有关联股票(只推送有关联股票的事件)
related_stocks_count = event.related_stocks.count()
@@ -10552,9 +10633,12 @@ def poll_new_events():
pushed_count += 1
print(f'[轮询] ✓ 已推送事件 ID={event.id}')
else:
print(f'[轮询] - 跳过(暂无关联股票)')
# 没有关联股票,加入待推送列表
_add_pending_event(event.id)
pending_count += 1
print(f'[轮询] → 加入待推送列表(暂无关联股票)')
print(f'[轮询] 本轮推送 {pushed_count}/{len(new_events)}事件')
print(f'[轮询] 本轮: 推送 {pushed_count} 个, 加入待推送 {pending_count}')
# 更新最大事件ID
new_max_id = max(event.id for event in events_in_24h)