71 lines
2.3 KiB
Python
71 lines
2.3 KiB
Python
# dingdan/notice_tasks.py
|
|
import logging
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from a_long_dianjing.celery import app
|
|
from users.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)
|
|
def dingdan_guangbo(self, order_info):
|
|
"""
|
|
异步并发广播订单模板消息给所有关注服务号的用户
|
|
order_info 字典字段示例:
|
|
{
|
|
'OrderID': 'PT123456',
|
|
'game_type': '王者荣耀',
|
|
'amount': '88.00',
|
|
'order_desc': '星耀上王者'
|
|
}
|
|
"""
|
|
sender = WeixinBroadcastSender()
|
|
template_data = sender.build_order_message_data(order_info)
|
|
order_id = order_info.get('OrderID')
|
|
|
|
# 查询所有已关注用户
|
|
try:
|
|
openids = list(
|
|
OfficialAccountUser.objects
|
|
.filter(is_subscribed=True)
|
|
.values_list('official_openid', flat=True)
|
|
)
|
|
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-500ms,1000 人约 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
|
|
} |