优化服务号订单广播(Celery并发)与群聊状态实时推送
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
176
dingdan/views.py
176
dingdan/views.py
@@ -666,34 +666,12 @@ class WechatPayNotifyView(APIView):
|
||||
logger.error(f"更新收支记录失败: {e}")
|
||||
|
||||
def _broadcast_async(self, dingdan):
|
||||
"""异步广播通知"""
|
||||
"""服务号模板消息广播(Celery 优先队列,接口不阻塞)"""
|
||||
try:
|
||||
def task():
|
||||
try:
|
||||
from . import WeixinBroadcastSender # 请根据实际路径修改
|
||||
order_info = {
|
||||
'dingdan_id': dingdan.dingdan_id,
|
||||
'game_type': self._get_game_type_name(dingdan.leixing_id),
|
||||
'amount': str(dingdan.jine),
|
||||
'order_desc': (dingdan.jieshao or '')[:50],
|
||||
}
|
||||
logger.info(f"准备广播订单: {order_info}")
|
||||
sender = WeixinBroadcastSender()
|
||||
result = sender.broadcast_order(order_info)
|
||||
if result.get('success'):
|
||||
logger.info(f"订单广播完成: {result.get('msg')}")
|
||||
else:
|
||||
logger.warning(f"订单广播异常: {result.get('msg')}")
|
||||
except Exception as e:
|
||||
logger.error(f"广播任务异常: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
thread = threading.Thread(target=task)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
logger.info("广播线程已启动")
|
||||
from utils.order_broadcast import schedule_dingdan_guangbo_from_order
|
||||
schedule_dingdan_guangbo_from_order(dingdan)
|
||||
except Exception as e:
|
||||
logger.error(f"启动广播线程异常: {e}")
|
||||
logger.error(f'提交广播任务失败: {e}', exc_info=True)
|
||||
|
||||
def _get_game_type_name(self, leixing_id):
|
||||
try:
|
||||
@@ -1297,27 +1275,11 @@ class VirtualPayNotifyView(APIView):
|
||||
logger.error(f"更新收支记录失败: {e}")
|
||||
|
||||
def _broadcast_async(self, dingdan):
|
||||
"""异步广播通知(同旧)"""
|
||||
try:
|
||||
def task():
|
||||
try:
|
||||
from . import WeixinBroadcastSender
|
||||
order_info = {
|
||||
'dingdan_id': dingdan.dingdan_id,
|
||||
'game_type': self._get_game_type_name(dingdan.leixing_id),
|
||||
'amount': str(dingdan.jine),
|
||||
'order_desc': (dingdan.jieshao or '')[:50],
|
||||
}
|
||||
sender = WeixinBroadcastSender()
|
||||
sender.broadcast_order(order_info)
|
||||
except Exception as e:
|
||||
logger.error(f"广播任务异常: {e}")
|
||||
|
||||
thread = threading.Thread(target=task)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
from utils.order_broadcast import schedule_dingdan_guangbo_from_order
|
||||
schedule_dingdan_guangbo_from_order(dingdan)
|
||||
except Exception as e:
|
||||
logger.error(f"启动广播线程异常: {e}")
|
||||
logger.error(f'提交广播任务失败: {e}', exc_info=True)
|
||||
|
||||
def _get_game_type_name(self, leixing_id):
|
||||
try:
|
||||
@@ -2292,6 +2254,9 @@ class JiedanView(APIView):
|
||||
dingdan.clkf = current_user.phone if hasattr(current_user, 'phone') else yonghuid
|
||||
dingdan.save()
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan.dingdan_id)
|
||||
|
||||
# 事务提交后异步通知对方平台
|
||||
transaction.on_commit(
|
||||
lambda: self._notify_partner_jiedan(
|
||||
@@ -2341,6 +2306,9 @@ class JiedanView(APIView):
|
||||
dingdan.zhuangtai = 3
|
||||
dingdan.save()
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan.dingdan_id)
|
||||
|
||||
# 9. 返回成功响应
|
||||
return Response({
|
||||
'code': 0,
|
||||
@@ -2738,16 +2706,10 @@ class ShangjiaPaifaView(APIView):
|
||||
except Exception as e:
|
||||
logger.error(f"跨平台同步失败: {e}", exc_info=True)
|
||||
|
||||
# Celery 广播任务(异步)
|
||||
# Celery 广播任务(broadcast 优先队列)
|
||||
try:
|
||||
order_info = {
|
||||
'dingdan_id': dingdan.dingdan_id,
|
||||
'game_type': self._get_game_type_name(dingdan.leixing_id),
|
||||
'amount': str(dingdan.jine),
|
||||
'order_desc': (dingdan.jieshao or '')[:50],
|
||||
}
|
||||
dingdan_guangbo.delay(order_info)
|
||||
logger.info(f"已提交广播任务: {dingdan.dingdan_id}")
|
||||
from utils.order_broadcast import schedule_dingdan_guangbo_from_order
|
||||
schedule_dingdan_guangbo_from_order(dingdan, order_type='商家派单')
|
||||
except Exception as e:
|
||||
logger.error(f"广播任务提交失败: {e}", exc_info=True)
|
||||
|
||||
@@ -3045,6 +3007,8 @@ class ShangjiaDingdanXiangqingView(APIView):
|
||||
'beizhu': order.beizhu or '',
|
||||
'nicheng': order.nicheng or '',
|
||||
'create_time': order.create_time.strftime('%Y-%m-%d %H:%M:%S') if order.create_time else '',
|
||||
'fadanpingtai': order.fadan_pingtai or 0,
|
||||
'is_cross': order.is_cross or 0,
|
||||
|
||||
# 打手信息
|
||||
'dashou_yonghuid': dashou_yonghuid,
|
||||
@@ -3313,6 +3277,9 @@ class ShangjiaJiesuanView(APIView):
|
||||
action=2 # 2 = 结算
|
||||
)
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan_id)
|
||||
|
||||
settle_ctx = (dingdan_id, jiedan_dashou_id)
|
||||
success_response = Response({
|
||||
'code': 0,
|
||||
@@ -3468,6 +3435,9 @@ class ShangjiaChexiaoView(APIView):
|
||||
|
||||
dingdan.save()
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan_id)
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '撤销成功',
|
||||
@@ -3592,6 +3562,9 @@ class ShangjiaTuikuanShenqingView(APIView):
|
||||
dingdan.tkly = tuikuan_liyou
|
||||
dingdan.save()
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan_id)
|
||||
|
||||
# ====== 原有功能:更新打手状态 ======
|
||||
if dingdan.jiedan_dashou_id:
|
||||
try:
|
||||
@@ -4417,7 +4390,7 @@ import logging
|
||||
import requests
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status, permissions
|
||||
from rest_framework import status, permissions, parsers
|
||||
from .models import Dingdan, DingdanPingtai, DingdanShangjia
|
||||
from yonghu.models import UserMain, UserDashou, UserBoss, UserShangjia
|
||||
from peizhi.models import ClubConfig
|
||||
@@ -4497,12 +4470,13 @@ def subscribe_users_to_group(user_ids, group_ids, appkey=None, secret=None):
|
||||
def send_group_message(
|
||||
appkey, secret, group_id, sender_id, sender_name, sender_avatar,
|
||||
message_text='', custom_payload=None, group_name='', order_id='', is_cross=0,
|
||||
to_data_extra=None,
|
||||
to_data_extra=None, message_type='text', image_url=None,
|
||||
):
|
||||
"""
|
||||
通过 GoEasy REST API 发送群消息。
|
||||
message_text: 普通文本内容
|
||||
custom_payload: 自定义消息体(如订单卡片),此时 type 设为 'order'
|
||||
image_url: 图片完整 URL,message_type='image' 时使用
|
||||
"""
|
||||
url = 'https://rest-hangzhou.goeasy.io/v2/im/message'
|
||||
sender_avatar = sender_avatar or ''
|
||||
@@ -4519,8 +4493,16 @@ def send_group_message(
|
||||
"orderId": order_id,
|
||||
"isCross": is_cross
|
||||
}
|
||||
msg_type = 'order' if custom_payload else 'text'
|
||||
payload = custom_payload if custom_payload else message_text
|
||||
|
||||
if message_type == 'image' and image_url:
|
||||
msg_type = 'image'
|
||||
payload = {"url": image_url}
|
||||
elif custom_payload:
|
||||
msg_type = 'order'
|
||||
payload = custom_payload
|
||||
else:
|
||||
msg_type = 'text'
|
||||
payload = message_text
|
||||
|
||||
body = {
|
||||
"appkey": appkey,
|
||||
@@ -4653,6 +4635,7 @@ class DaiLiQunLiaoXiaoXiView(APIView):
|
||||
跨平台判断条件:订单同时存在 partner_order_id 和 partner_club_id 即视为跨平台
|
||||
"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [parsers.MultiPartParser, parsers.FormParser, parsers.JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
# ===== 1. 参数解析 =====
|
||||
@@ -4663,6 +4646,7 @@ class DaiLiQunLiaoXiaoXiView(APIView):
|
||||
message_type = request.data.get('messageType', 'text')
|
||||
message_text = request.data.get('text', '')
|
||||
order_payload = request.data.get('orderPayload', None)
|
||||
image_file = request.FILES.get('file')
|
||||
|
||||
logger.info(f"[群聊代理] 收到请求: orderId={order_id}, identityType={identity_type}, "
|
||||
f"messageType={message_type}, text={message_text[:20]}...")
|
||||
@@ -4716,24 +4700,56 @@ class DaiLiQunLiaoXiaoXiView(APIView):
|
||||
|
||||
custom_payload = order_payload if message_type == 'order' else None
|
||||
text_content = message_text
|
||||
image_url = None
|
||||
|
||||
if message_type == 'image':
|
||||
if not image_file:
|
||||
return Response({'code': 400, 'msg': '缺少图片文件'})
|
||||
try:
|
||||
from utils.oss_utils import upload_to_oss, validate_image
|
||||
import uuid as _uuid
|
||||
validate_image(image_file)
|
||||
oss_path = f"chat/images/{order_id}/{_uuid.uuid4().hex}.jpg"
|
||||
image_url = upload_to_oss(image_file, oss_path)
|
||||
if not image_url:
|
||||
return Response({'code': 500, 'msg': '图片上传失败'})
|
||||
except Exception as e:
|
||||
logger.error(f"[群聊代理] 图片上传失败: {e}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '图片上传失败'})
|
||||
|
||||
pair_to_data = build_pair_to_data_for_order(order, viewer_goeasy_id=sender_id)
|
||||
|
||||
# ---------- 先向我方群发送消息(永远执行) ----------
|
||||
local_ok = send_group_message(
|
||||
appkey=self_appkey,
|
||||
secret=self_secret,
|
||||
group_id=real_group_id,
|
||||
sender_id=sender_id,
|
||||
sender_name=sender_name,
|
||||
sender_avatar=sender_avatar,
|
||||
message_text=text_content,
|
||||
custom_payload=custom_payload,
|
||||
group_name=group_name,
|
||||
order_id=order.dingdan_id,
|
||||
is_cross=is_cross,
|
||||
to_data_extra=pair_to_data,
|
||||
)
|
||||
if message_type == 'image' and image_url:
|
||||
local_ok = send_group_message(
|
||||
appkey=self_appkey,
|
||||
secret=self_secret,
|
||||
group_id=real_group_id,
|
||||
sender_id=sender_id,
|
||||
sender_name=sender_name,
|
||||
sender_avatar=sender_avatar,
|
||||
group_name=group_name,
|
||||
order_id=order.dingdan_id,
|
||||
is_cross=is_cross,
|
||||
to_data_extra=pair_to_data,
|
||||
message_type='image',
|
||||
image_url=image_url,
|
||||
)
|
||||
else:
|
||||
local_ok = send_group_message(
|
||||
appkey=self_appkey,
|
||||
secret=self_secret,
|
||||
group_id=real_group_id,
|
||||
sender_id=sender_id,
|
||||
sender_name=sender_name,
|
||||
sender_avatar=sender_avatar,
|
||||
message_text=text_content,
|
||||
custom_payload=custom_payload,
|
||||
group_name=group_name,
|
||||
order_id=order.dingdan_id,
|
||||
is_cross=is_cross,
|
||||
to_data_extra=pair_to_data,
|
||||
)
|
||||
if not local_ok:
|
||||
return Response({'code': 500, 'msg': '消息发送失败'})
|
||||
|
||||
@@ -5626,6 +5642,9 @@ class DashouTijiaoView(APIView):
|
||||
dingdan.update_time = timezone.now()
|
||||
dingdan.save()
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan_id)
|
||||
|
||||
# 7. 更新打手扩展表状态
|
||||
dashou_profile.zhuangtai = 1 # 改为数字1(根据你之前说的)
|
||||
dashou_profile.save()
|
||||
@@ -5720,7 +5739,8 @@ class DashouDingdanXiangqingView(APIView):
|
||||
'dashou_liuyan': dingdan.dashou_liuyan or '',
|
||||
'fuwudashou_id': dingdan.jiedan_dashou_id or '',
|
||||
'fadanpingtai': dingdan.fadan_pingtai or '',
|
||||
'tuikuan_liyou': dingdan.tkly or ''
|
||||
'tuikuan_liyou': dingdan.tkly or '',
|
||||
'is_cross': dingdan.is_cross or 0,
|
||||
}
|
||||
|
||||
# 6. 查询打手提交的图片
|
||||
@@ -6348,6 +6368,9 @@ class AdQiangZhiJieDan(APIView):
|
||||
dingdan_obj.zhuangtai = 3
|
||||
dingdan_obj.save()
|
||||
|
||||
from utils.chat_utils import schedule_order_status_chat_push
|
||||
schedule_order_status_chat_push(dingdan_id)
|
||||
|
||||
# 10.2 处理打手结算
|
||||
if jiedan_dashou_id:
|
||||
try:
|
||||
@@ -9012,13 +9035,14 @@ class ZxsjghdsView(APIView):
|
||||
|
||||
# 7.4 异步通知打手(广播新订单)
|
||||
try:
|
||||
order_info = {
|
||||
from utils.order_broadcast import submit_dingdan_guangbo
|
||||
submit_dingdan_guangbo({
|
||||
'dingdan_id': new_dingdan_id,
|
||||
'game_type': self._get_game_type_name(leixing_id),
|
||||
'amount': str(jine),
|
||||
'order_desc': (jieshao or '')[:50],
|
||||
}
|
||||
dingdan_guangbo.delay(order_info)
|
||||
'order_type': '商家派单',
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"广播通知失败: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user