优化服务号订单广播(Celery并发)与群聊状态实时推送

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-24 00:43:30 +08:00
parent 56c66846cc
commit 49ba67ecaf
9 changed files with 458 additions and 331 deletions

View File

@@ -1,71 +1,39 @@
# dingdan/tongzhi_tasks.py
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from django.conf import settings
from a_long_dianjing.celery_app import app
from yonghu.models import OfficialAccountUser
from utils.weixin_broadcast import WeixinBroadcastSender
logger = logging.getLogger('weixin_broadcast')
@app.task(bind=True, max_retries=3, default_retry_delay=60)
@app.task(
bind=True,
name='dingdan.tongzhi_tasks.dingdan_guangbo',
max_retries=2,
default_retry_delay=30,
queue='broadcast',
soft_time_limit=240,
time_limit=300,
)
def dingdan_guangbo(self, order_info):
"""
异步并发广播订单模板消息给所有关注服务号的用户
order_info 字典字段示例:
{
'dingdan_id': 'PT123456',
'game_type': '王者荣耀',
'amount': '88.00',
'order_desc': '星耀上王者'
}
Celery并发向所有关注服务号的用户推送订单模板消息
"""
sender = WeixinBroadcastSender()
template_data = sender.build_order_message_data(order_info)
if not getattr(settings, 'WEIXIN_BROADCAST_ENABLED', True):
return {'success': True, 'msg': '广播已关闭'}
order_id = order_info.get('dingdan_id')
# 查询所有已关注用户
try:
openids = list(
OfficialAccountUser.objects
.filter(is_subscribed=True)
.values_list('official_openid', flat=True)
)
sender = WeixinBroadcastSender()
result = sender.broadcast_parallel(order_info)
if not result.get('success') and self.request.retries < self.max_retries:
raise self.retry(countdown=30)
return result
except Exception as e:
logger.error(f'查询订阅用户失败: {e}')
raise self.retry(exc=e)
if not openids:
return {'success': True, 'msg': '无订阅用户'}
total = len(openids)
logger.info(f'📊 Celery 广播订单 {order_id} 开始,目标人数: {total}')
success = 0
fail = 0
# 20 个线程并发发送,微信接口耗时 200-500ms1000 人约 15 秒完成
with ThreadPoolExecutor(max_workers=20) as executor:
future_map = {
executor.submit(sender.send_to_user, openid, template_data, order_id): openid
for openid in openids
}
for future in as_completed(future_map):
openid = future_map[future]
try:
res = future.result(timeout=30)
if res.get('success'):
success += 1
else:
fail += 1
except Exception as e:
fail += 1
logger.error(f'发送异常 {openid[:10]}...: {e}')
logger.info(f'🏁 广播完成:成功 {success},失败 {fail},总计 {total}')
return {
'success': True,
'total': total,
'success_count': success,
'fail_count': fail
}
logger.error(f'广播任务异常 订单={order_id}: {e}', exc_info=True)
if self.request.retries < self.max_retries:
raise self.retry(exc=e)
return {'success': False, 'msg': str(e), 'order_id': order_id}