聊天配对群组ID与准备接口:打手商家同群、订单卡片推送

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-23 22:49:28 +08:00
parent 7daa05c8c6
commit 2db394b2f9
3 changed files with 229 additions and 20 deletions

View File

@@ -7,7 +7,7 @@ from .views import CreateOrderView, WechatPayNotifyView, \
ShangjiaChexiaoView, DashouDingdanHuoquView, ShangpinLeixingHuoquView, QiangdanView, \
DashouDingdanHuoquView1, DashouCOSZhengshuView, DashouTijiaoView, DashouDingdanXiangqingView, AdGetDingdanXiangQing, \
AdTongYiTuiKuanPingTai, AdTongYiTuiKuanShangJia, AdQiangZhiJieDan, AdJuJueTuiKuan,\
AdGengHuanDaShou,ShangjiaCOSZhengshuView, DashouHuoquLeixingView,AdJuJueJieSuan, AdZhuanYiDaTing, AdQuXiaoZhiDing,PartnerNotifyClaimView, PartnerSyncOrderView, PartnerCheckClaimView, PartnerRefundNotifyView,DashouXiugaiView,VirtualCreateOrderView, VirtualPayNotifyView, VirtualPaymentVerifyView,VirtualPaymentFailView,DaiLiQunLiaoXiaoXiView,\
AdGengHuanDaShou,ShangjiaCOSZhengshuView, DashouHuoquLeixingView,AdJuJueJieSuan, AdZhuanYiDaTing, AdQuXiaoZhiDing,PartnerNotifyClaimView, PartnerSyncOrderView, PartnerCheckClaimView, PartnerRefundNotifyView,DashouXiugaiView,VirtualCreateOrderView, VirtualPayNotifyView, VirtualPaymentVerifyView,VirtualPaymentFailView,DaiLiQunLiaoXiaoXiView,DingdanLiaotianZhunbeiView,\
ShangjiaLianjieLiuYanZhuanFaView, ZxsjghdsView, \
ShangjiaFakuanApplyView, ShangjiaFakuaiXiugaiView
@@ -78,6 +78,7 @@ urlpatterns = [
path('dsxiugaidd', DashouXiugaiView.as_view(), name='打手修改订单详情'),
path('kptxxfs', DaiLiQunLiaoXiaoXiView.as_view(), name='前端聊天转发'), # 群聊消息代理发送
path('ltdhzb', DingdanLiaotianZhunbeiView.as_view(), name='订单聊天准备'),
path('virtual_xiadan', VirtualCreateOrderView.as_view(),name='虚拟支付下单'),

View File

@@ -4421,7 +4421,10 @@ from rest_framework import status, permissions
from .models import Dingdan, DingdanPingtai, DingdanShangjia
from yonghu.models import UserMain, UserDashou, UserBoss, UserShangjia
from peizhi.models import ClubConfig
from utils.chat_utils import _send_group_message, _subscribe_users_to_group
from utils.chat_utils import (
_send_group_message, _subscribe_users_to_group,
resolve_group_id_for_order, prepare_order_group_chat,
)
logger = logging.getLogger('dingdan')
@@ -4593,6 +4596,44 @@ def get_partner_club_config(partner_club_id):
# ----------------- 主视图 -----------------
class DingdanLiaotianZhunbeiView(APIView):
"""订单详情进入聊天前:确保配对群订阅并返回 groupId"""
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
dingdan_id = request.data.get('dingdan_id') or request.data.get('orderId')
identity_type = request.data.get('identityType')
push_order_card = request.data.get('push_order_card', True)
if isinstance(push_order_card, str):
push_order_card = push_order_card.lower() in ('1', 'true', 'yes')
if not dingdan_id:
return Response({'code': 400, 'msg': '缺少 dingdan_id'})
if not identity_type:
return Response({'code': 400, 'msg': '缺少 identityType'})
if identity_type not in ('dashou', 'shangjia', 'boss'):
return Response({'code': 400, 'msg': 'identityType 无效'})
uid = request.user.yonghuid
try:
order = Dingdan.objects.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(dingdan_id=dingdan_id)
except Dingdan.DoesNotExist:
return Response({'code': 404, 'msg': '订单不存在'})
valid, err_msg = check_user_permission(identity_type, uid, order)
if not valid:
return Response({'code': 403, 'msg': err_msg})
prefix_map = {'dashou': 'Ds', 'shangjia': 'Sj', 'boss': 'Boss'}
caller_goeasy_id = prefix_map[identity_type] + uid
result = prepare_order_group_chat(dingdan_id, caller_goeasy_id, push_order_card=push_order_card)
if not result:
return Response({'code': 500, 'msg': '准备群聊失败,请稍后重试'})
return Response({'code': 0, 'msg': 'ok', 'data': result})
class DaiLiQunLiaoXiaoXiView(APIView):
"""
代理发送群聊消息
@@ -4648,7 +4689,7 @@ class DaiLiQunLiaoXiaoXiView(APIView):
return Response({'code': 500, 'msg': '系统配置错误(APPKEY)'})
# ===== 6. 公共群信息 =====
real_group_id = f"group_{order.dingdan_id}"
real_group_id = resolve_group_id_for_order(order)
group_name = (order.jieshao[:20] + '') if order.jieshao and len(order.jieshao) > 20 else f"订单{order.dingdan_id}"
sender_avatar = full_avatar(sender_avatar_raw) if sender_avatar_raw else full_avatar('')

View File

@@ -10,6 +10,65 @@ from yonghu.models import UserMain, UserDashou, UserBoss, UserShangjia
logger = logging.getLogger('chat_utils')
ORDER_CARD_PREFIX = '[ORDER_CARD]'
def make_dashou_shangjia_group_id(dashou_yonghuid, shangjia_yonghuid):
return f"group_Ds{dashou_yonghuid}_Sj{shangjia_yonghuid}"
def make_dashou_boss_group_id(dashou_yonghuid, boss_yonghuid):
return f"group_Ds{dashou_yonghuid}_Boss{boss_yonghuid}"
def _get_shangjia_id_from_order(order):
try:
return order.shangjia_kuozhan.shangjia_id
except Exception:
ext = DingdanShangjia.objects.filter(dingdan__dingdan_id=order.dingdan_id).first()
return ext.shangjia_id if ext else None
def _get_boss_id_from_order(order):
try:
return order.pingtai_kuozhan.laoban_id
except Exception:
ext = DingdanPingtai.objects.filter(dingdan__dingdan_id=order.dingdan_id).first()
return ext.laoban_id if ext else None
def resolve_group_id_for_order(order, dashou_yonghuid=None):
"""打手+商家/老板配对群 ID跨平台或缺信息时回退订单群 ID"""
dashou_yonghuid = dashou_yonghuid or order.jiedan_dashou_id
if order.is_cross == 1 and order.dispatch_type == 2:
return f"group_{order.dingdan_id}"
if not dashou_yonghuid:
return f"group_{order.dingdan_id}"
if order.fadan_pingtai == 2:
shangjia_id = _get_shangjia_id_from_order(order)
if shangjia_id:
return make_dashou_shangjia_group_id(dashou_yonghuid, shangjia_id)
elif order.fadan_pingtai == 1:
boss_id = _get_boss_id_from_order(order)
if boss_id:
return make_dashou_boss_group_id(dashou_yonghuid, boss_id)
return f"group_{order.dingdan_id}"
def _build_order_card_text(order):
payload = {
'dingdan_id': order.dingdan_id,
'jieshao': order.jieshao or '',
'beizhu': order.beizhu or '',
'jine': str(order.jine),
'zhuangtai': order.zhuangtai,
'nicheng': order.nicheng or '',
}
if order.create_time:
payload['create_time'] = order.create_time.strftime('%Y-%m-%d %H:%M:%S')
return ORDER_CARD_PREFIX + json.dumps(payload, ensure_ascii=False)
# ========== 辅助配置获取(从 ClubConfig ==========
def _get_self_club_config():
if not hasattr(_get_self_club_config, '_config'):
@@ -91,7 +150,8 @@ 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=None, order_id=None, is_cross=0):
message_text, custom_payload=None, group_name=None, order_id=None, is_cross=0,
list_avatar=None, order_desc=None):
if not appkey:
appkey = _get_self_goeasy_appkey()
if not secret:
@@ -105,10 +165,12 @@ def _send_group_message(appkey, secret, group_id, sender_id, sender_name, sender
# to.data 包含群聊展示信息及业务字段
to_data = {
"name": group_name or group_id,
"avatar": sender_avatar or "",
"avatar": list_avatar or sender_avatar or "",
}
if order_id:
to_data["orderId"] = order_id
if order_desc:
to_data["orderDesc"] = order_desc[:300]
to_data["isCross"] = is_cross
payload_str = custom_payload.get("text", message_text) if custom_payload else message_text
@@ -305,12 +367,106 @@ def establish_order_chat_with_retry(dingdan_id, max_rounds=3):
return False
# ========== 普通订单 / 我方派单 ==========
def _push_order_card_message(appkey, secret, order, group_id, sender_id, sender_name, sender_avatar,
group_name, list_avatar, order_desc):
card_text = _build_order_card_text(order)
return _send_group_message(
appkey, secret, group_id, sender_id, sender_name, sender_avatar,
card_text, None, group_name=group_name, order_id=order.dingdan_id,
is_cross=order.is_cross or 0, list_avatar=list_avatar, order_desc=order_desc
)
def prepare_order_group_chat(dingdan_id, caller_goeasy_id, push_order_card=False):
"""
订单详情进入聊天前:确保配对群已订阅,可选推送订单卡片。
返回前端跳转所需字段 dict失败返回 None。
"""
try:
order = Dingdan.objects.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(dingdan_id=dingdan_id)
except Dingdan.DoesNotExist:
logger.error(f"prepare_order_group_chat: 订单 {dingdan_id} 不存在")
return None
dashou_uid = order.jiedan_dashou_id
if not dashou_uid:
logger.error(f"prepare_order_group_chat: 订单 {dingdan_id} 无接单打手")
return None
dashou_goeasy_id = f"Ds{dashou_uid}"
partner_goeasy_id, partner_name, partner_avatar = _get_local_partner_info(order)
if not partner_goeasy_id:
logger.error(f"prepare_order_group_chat: 订单 {dingdan_id} 无下单方")
return None
group_id = resolve_group_id_for_order(order, dashou_uid)
dashou_name, dashou_avatar = _resolve_goeasy_user_display(dashou_goeasy_id)
if caller_goeasy_id == dashou_goeasy_id:
counterpart_id = partner_goeasy_id
counterpart_name = partner_name
counterpart_avatar = partner_avatar
elif caller_goeasy_id == partner_goeasy_id:
counterpart_id = dashou_goeasy_id
counterpart_name = dashou_name
counterpart_avatar = dashou_avatar
else:
logger.warning(f"prepare_order_group_chat: caller {caller_goeasy_id} 不属于订单 {dingdan_id}")
return None
appkey = _get_self_goeasy_appkey()
secret = _get_self_goeasy_secret()
if not appkey:
return None
order_desc = (order.jieshao or '').strip()
if order.beizhu:
order_desc = f"{order_desc} | 备注:{order.beizhu}" if order_desc else f"备注:{order.beizhu}"
display_group_name = counterpart_name or '聊天'
user_ids = list({dashou_goeasy_id, partner_goeasy_id, caller_goeasy_id})
subscribe_ok = _retry_call(
_subscribe_users_to_group, 5, 0.4,
user_ids, [group_id], appkey, secret
)
card_ok = False
if push_order_card:
caller_name, caller_avatar = _resolve_goeasy_user_display(caller_goeasy_id)
card_ok = _retry_call(
_push_order_card_message, 3, 0.4,
appkey, secret, order, group_id, caller_goeasy_id, caller_name, caller_avatar,
display_group_name, counterpart_avatar, order_desc
)
_ensure_liaotian_record(group_id, partner_goeasy_id, dashou_goeasy_id)
return {
'groupId': group_id,
'orderId': order.dingdan_id,
'groupName': display_group_name,
'groupAvatar': counterpart_avatar or '',
'counterpartId': counterpart_id,
'counterpartName': counterpart_name,
'counterpartAvatar': counterpart_avatar or '',
'orderDesc': order_desc,
'orderJine': str(order.jine),
'orderZhuangtai': order.zhuangtai,
'isCross': 1 if order.is_cross == 1 else 0,
'subscribeOk': subscribe_ok,
'orderCardPushed': card_ok,
}
def _handle_local_order(order, dashou_goeasy_id, dashou_name, dashou_avatar, appkey, secret):
group_id = f"group_{order.dingdan_id}"
group_name = (order.jieshao[:20] + '') if order.jieshao and len(order.jieshao) > 20 else (order.jieshao or f"订单{order.dingdan_id}")
group_avatar = _full_local_avatar(order.tupian) if order.tupian else _full_local_avatar('')
dashou_uid = dashou_goeasy_id[2:] if dashou_goeasy_id.startswith('Ds') else dashou_goeasy_id
group_id = resolve_group_id_for_order(order, dashou_uid)
partner_goeasy_id, partner_name, partner_avatar = _get_local_partner_info(order)
group_name = partner_name or f"用户{partner_goeasy_id[-6:] if partner_goeasy_id else ''}"
order_desc = (order.jieshao or '').strip()
if order.beizhu:
order_desc = f"{order_desc} | 备注:{order.beizhu}" if order_desc else f"备注:{order.beizhu}"
if not partner_goeasy_id:
logger.error(f"订单 {order.dingdan_id} 无法获取下单方 GoEasy 标识")
subscribe_ok = _retry_call(
@@ -319,9 +475,10 @@ def _handle_local_order(order, dashou_goeasy_id, dashou_name, dashou_avatar, app
)
msg_ok = _retry_call(
_send_group_message, 3, 0.4,
appkey, secret, group_id, dashou_goeasy_id, dashou_name, group_avatar,
appkey, secret, group_id, dashou_goeasy_id, dashou_name, dashou_avatar,
"订单已接单,请等待对方上线。", None, group_name=group_name,
order_id=order.dingdan_id, is_cross=order.is_cross
order_id=order.dingdan_id, is_cross=order.is_cross,
list_avatar=partner_avatar, order_desc=order_desc
)
return subscribe_ok or msg_ok
@@ -333,9 +490,9 @@ def _handle_local_order(order, dashou_goeasy_id, dashou_name, dashou_avatar, app
init_msg_text = f"订单已接单,内容:{order.jieshao},备注:{order.beizhu}游戏ID{order.nicheng}"
success1 = _retry_call(
_send_group_message, 3, 0.4,
appkey, secret, group_id, dashou_goeasy_id, dashou_name, group_avatar,
appkey, secret, group_id, dashou_goeasy_id, dashou_name, dashou_avatar,
init_msg_text, None, group_name=group_name, order_id=order.dingdan_id,
is_cross=order.is_cross
is_cross=order.is_cross, list_avatar=partner_avatar, order_desc=order_desc
)
partner_msg = "订单已确认,请开始服务。"
@@ -343,16 +500,22 @@ def _handle_local_order(order, dashou_goeasy_id, dashou_name, dashou_avatar, app
_send_group_message, 3, 0.4,
appkey, secret, group_id, partner_goeasy_id, partner_name, partner_avatar,
partner_msg, None, group_name=group_name, order_id=order.dingdan_id,
is_cross=order.is_cross
is_cross=order.is_cross, list_avatar=dashou_avatar, order_desc=order_desc
)
liaotian_ok = _ensure_liaotian_record(order.dingdan_id, partner_goeasy_id, dashou_goeasy_id)
card_ok = _retry_call(
_push_order_card_message, 3, 0.4,
appkey, secret, order, group_id, dashou_goeasy_id, dashou_name, dashou_avatar,
group_name, partner_avatar, order_desc
)
liaotian_ok = _ensure_liaotian_record(group_id, partner_goeasy_id, dashou_goeasy_id)
private_ok = _establish_private_chat(
appkey, secret, dashou_goeasy_id, dashou_name, dashou_avatar,
partner_goeasy_id, partner_name, partner_avatar
)
result = subscribe_ok or success1 or success2 or liaotian_ok or private_ok
result = subscribe_ok or success1 or success2 or card_ok or liaotian_ok or private_ok
if not result:
logger.error(f"订单 {order.dingdan_id} 群聊/私聊均未建立成功")
return result
@@ -525,17 +688,21 @@ def _get_cross_partner_identity(order, partner_order_id):
return f"User{uid}"
def _create_local_group_only(order, dashou_goeasy_id, dashou_name, dashou_avatar, appkey, secret):
group_id = f"group_{order.dingdan_id}"
group_name = (order.jieshao[:20] + '') if order.jieshao and len(order.jieshao) > 20 else (order.jieshao or f"订单{order.dingdan_id}")
group_avatar = _full_local_avatar(order.tupian) if order.tupian else _full_local_avatar('')
dashou_uid = dashou_goeasy_id[2:] if dashou_goeasy_id.startswith('Ds') else dashou_goeasy_id
group_id = resolve_group_id_for_order(order, dashou_uid)
partner_goeasy_id, partner_name, partner_avatar = _get_local_partner_info(order)
group_name = partner_name or (order.jieshao or f"订单{order.dingdan_id}")
order_desc = (order.jieshao or '').strip()
if order.beizhu:
order_desc = f"{order_desc} | 备注:{order.beizhu}" if order_desc else f"备注:{order.beizhu}"
subscribe_ok = _retry_call(
_subscribe_users_to_group, 5, 0.4,
[dashou_goeasy_id], [group_id], appkey, secret
)
msg_ok = _retry_call(
_send_group_message, 3, 0.4,
appkey, secret, group_id, dashou_goeasy_id, dashou_name, group_avatar,
appkey, secret, group_id, dashou_goeasy_id, dashou_name, dashou_avatar,
"订单已接单。", None, group_name=group_name, order_id=order.dingdan_id,
is_cross=order.is_cross
is_cross=order.is_cross, list_avatar=partner_avatar, order_desc=order_desc
)
return subscribe_ok or msg_ok