diff --git a/orders/services/team_recruit.py b/orders/services/team_recruit.py index d034b39..c2e0a10 100644 --- a/orders/services/team_recruit.py +++ b/orders/services/team_recruit.py @@ -124,6 +124,99 @@ def _subscribe_uids_to_order_group(order_id: str, uids: list): return False +def _dashou_display_name(uid: str) -> str: + try: + from users.business_models import User + from users.models import UserDashou + u = User.query.filter(UserUID=uid).first() + if not u: + return f'打手{(uid or "")[:6]}' + try: + p = u.DashouProfile + if p and p.nicheng: + return p.nicheng + except Exception: + pass + try: + p = UserDashou.query.filter(user=u).first() + if p and p.nicheng: + return p.nicheng + except Exception: + pass + return f'打手{(uid or "")[:6]}' + except Exception: + return f'打手{(uid or "")[:6]}' + + +def _sync_team_order_group_chat(order, dashou_uids: list, *, announce_uid=None, announce_text=None): + """ + 组队群统一 group_{订单ID}: + - 订阅:打手们 + 商家/老板(派单方) + - 可选:发送入队/开组系统消息,触发监听中用户实时收到 + """ + oid = (getattr(order, 'OrderID', None) or '').strip() + if not oid: + return False + try: + from utils.chat_utils import ( + _subscribe_users_to_group, + _send_group_message, + _get_self_goeasy_appkey, + _get_self_goeasy_secret, + _get_local_partner_info, + _full_local_avatar, + ) + appkey = _get_self_goeasy_appkey() + secret = _get_self_goeasy_secret() + if not appkey: + return False + + group_id = f'group_{oid}' + user_ids = [] + for u in dashou_uids or []: + u = (u or '').strip() + if u: + gid = f'Ds{u}' + if gid not in user_ids: + user_ids.append(gid) + + partner_id, partner_name, partner_avatar = _get_local_partner_info(order) + if partner_id and partner_id not in user_ids: + user_ids.append(partner_id) + + if not user_ids: + return False + + ok = _subscribe_users_to_group(user_ids, [group_id], appkey, secret) + if not ok: + logger.warning('组队订阅群失败 order=%s users=%s', oid, user_ids) + + if announce_uid and announce_text: + sender_id = f'Ds{announce_uid}' + sender_name = _dashou_display_name(announce_uid) + sender_avatar = '' + try: + from users.business_models import User + uu = User.query.filter(UserUID=announce_uid).first() + if uu: + sender_avatar = _full_local_avatar(getattr(uu, 'Avatar', None) or '') + except Exception: + pass + desc = (order.Description or '')[:20] + group_name = (desc + '…') if order.Description and len(order.Description) > 20 else (order.Description or f'订单{oid}') + sent = _send_group_message( + appkey, secret, group_id, sender_id, sender_name, sender_avatar, + announce_text, None, group_name=group_name, order_id=oid, + ) + if not sent: + logger.warning('组队群消息发送失败 order=%s text=%s', oid, announce_text) + return bool(ok and sent) + return bool(ok) + except Exception as e: + logger.warning('组队群同步失败 order=%s: %s', oid, e) + return False + + @transaction.atomic def open_recruit( *, @@ -210,7 +303,13 @@ def open_recruit( joined_at=now, ).save() - _subscribe_uids_to_order_group(oid, [leader_uid]) + name = _dashou_display_name(leader_uid) + _sync_team_order_group_chat( + order, + [leader_uid], + announce_uid=leader_uid, + announce_text=f'{name} 已开启组队招募,请在组队群沟通', + ) _log(club_id, oid, rec.id, leader_uid, 'open', f'mode={mode} need={need} display={display}') return rec @@ -429,7 +528,13 @@ def join_recruit(*, recruit_id: int, user_uid: str, game_id: str, request=None, TeamRecruit.objects.filter(id=rec.id).update(joined_count=F('joined_count') + 1, UpdateTime=now) rec.refresh_from_db() _set_dashou_busy(user_uid, True) - _subscribe_uids_to_order_group(rec.order_id, [user_uid, rec.leader_uid]) + name = _dashou_display_name(user_uid) + _sync_team_order_group_chat( + order, + [user_uid, rec.leader_uid, order.PlayerID], + announce_uid=user_uid, + announce_text=f'{name} 已组队', + ) _log(rec.club_id, rec.order_id, rec.id, user_uid, 'join', game_id) if int(rec.joined_count or 0) >= int(rec.need_count or 0): diff --git a/orders/views/chat.py b/orders/views/chat.py index 4bb1b04..dfe29b6 100644 --- a/orders/views/chat.py +++ b/orders/views/chat.py @@ -42,7 +42,7 @@ from utils.weixin_broadcast import WeixinBroadcastSender from utils.money import yuan_to_fen from utils.chat_utils import ( _send_group_message, _subscribe_users_to_group, establish_order_chat, - prepare_order_group_chat, resolve_pair_group_id, + prepare_order_group_chat, resolve_pair_group_id, resolve_order_chat_group_id, parse_pair_from_group_id, resolve_pair_from_order, check_pair_chat_permission, query_pair_orders, order_to_chat_dict, ) @@ -205,7 +205,22 @@ def check_user_permission(identity_type, uid, order): except UserDashou.DoesNotExist: return False, '该用户不是打手' if order.PlayerID != uid: - return False, '你不是该订单的接单打手' + # 组队队友也可进订单组队群 + try: + from orders.models import TeamMember, TeamRecruit + in_team = TeamMember.query.filter( + order_id=order.OrderID, + user_uid=uid, + join_status=TeamMember.JOIN_ACTIVE, + ).exists() + recruit_ok = TeamRecruit.query.filter( + order_id=order.OrderID, + status__in=[TeamRecruit.STATUS_RECRUITING, TeamRecruit.STATUS_LOCKED], + ).exists() + if not (in_team and recruit_ok): + return False, '你不是该订单的接单打手' + except Exception: + return False, '你不是该订单的接单打手' elif identity_type == 'shangjia': try: @@ -248,13 +263,16 @@ class DaiLiQunLiaoXiaoXiView(APIView): def post(self, request): # ===== 1. 参数解析 ===== order_id = request.data.get('orderId') - identity_type = request.data.get('identityType') # dashou / shangjia / boss + identity_type = request.data.get('identityType') # dashou / PlayerID / shangjia / boss sender_name = request.data.get('senderName', '') sender_avatar_raw = request.data.get('senderAvatar', '') message_type = request.data.get('messageType', 'text') message_text = request.data.get('text', '') order_payload = request.data.get('orderPayload', None) + if identity_type == 'dashou': + identity_type = 'PlayerID' + logger.info(f"[群聊代理] 收到请求: orderId={order_id}, identityType={identity_type}, " f"messageType={message_type}, text={message_text[:20]}...") @@ -289,8 +307,8 @@ class DaiLiQunLiaoXiaoXiView(APIView): logger.error("[群聊代理] 我方 GoEasy AppKey 未配置") return Response({'code': 500, 'msg': '系统配置错误(APPKEY)'}) - # ===== 6. 公共群信息(新单走配对群,与文赫一致) ===== - real_group_id = resolve_pair_group_id(order) or f"group_{order.OrderID}" + # ===== 6. 有组队用 group_{订单},否则配对群 ===== + real_group_id = resolve_order_chat_group_id(order) or f"group_{order.OrderID}" group_name = (order.Description[:20] + '…') if order.Description and len(order.Description) > 20 else f"订单{order.OrderID}" sender_avatar = full_avatar(sender_avatar_raw) if sender_avatar_raw else full_avatar('') diff --git a/utils/chat_utils.py b/utils/chat_utils.py index 127411a..23d8d1b 100644 --- a/utils/chat_utils.py +++ b/utils/chat_utils.py @@ -104,6 +104,23 @@ def resolve_pair_group_id(order): return None +def resolve_order_chat_group_id(order): + """ + 进订单聊天用的群 ID: + - 存在进行中组队 → 统一 group_{订单ID}(组队成员 + 商家/老板同群) + - 否则 → 配对群 + """ + oid = (getattr(order, 'OrderID', None) or '').strip() + if oid: + try: + from orders.services.team_recruit import get_active_recruit + if get_active_recruit(oid): + return f'group_{oid}' + except Exception as e: + logger.warning('resolve_order_chat_group_id 查组队失败 order=%s: %s', oid, e) + return resolve_pair_group_id(order) or (f'group_{oid}' if oid else None) + + def parse_pair_from_group_id(group_id): """从配对群 ID 解析打手 uid、对方 uid、对方角色(shangjia/boss)""" if not group_id: @@ -202,8 +219,8 @@ def _get_dashou_profile(order): def build_order_group_chat_payload(order, viewer_backend_identity, viewer_uid): - """构建前端 ltdhzb 所需群聊元数据(新群用打手+商家/老板配对 groupId)""" - group_id = resolve_pair_group_id(order) or f'group_{order.OrderID}' + """构建前端 ltdhzb 所需群聊元数据(有组队时用 group_{订单ID})""" + group_id = resolve_order_chat_group_id(order) or f'group_{order.OrderID}' order_desc = order.Description or '' group_name = ( (order_desc[:20] + '…') if order_desc and len(order_desc) > 20