P1 性能优化(避免循环内 N+1 / N 次 DB 查询): - shop_order_views._batch_images: 移除 ThreadPoolExecutor 掩盖的 N 次查询,改单次批量 + Python 侧分组 - product_query.DashouHuiyuanList: 5N 查询 -> 3 次批量预取 + 本地闭包判断 - roles.GetRolePermissionView / 用户列表: 循环内 RolePermission/Permission/UserRole/Role -> 批量 __in 预取 - guanli / zuzhang 杜次分红 4.2/4.3: 循环内 update_or_create + 单条 delete -> bulk_create + bulk_update + 批量 delete - admin_config QQ 群配置: 循环内 exists/first/save/create -> 批量预取 + bulk_create + bulk_update - jituan 服务层多处合并统计查询、批量预取 map - rank 多处循环 N+1 改批量预取 - backend 多处循环内 count/create 改批量 - config/orders/merchant_ops 多处循环 N+1 改批量预取 其他改动: - users/views/kefu.py 拆分为 kefu_base/kefu_dashou/kefu_orders/kefu_punishment/kefu_withdraw 5 个文件 - 删除遗留脚本 check_prod_uid.py / create_rbac_tables.sql
544 lines
22 KiB
Python
544 lines
22 KiB
Python
"""orders.views.chat - auto-generated by split script."""
|
||
# ==================== 标准库 ====================
|
||
import hmac
|
||
import os
|
||
import json
|
||
import time
|
||
import random
|
||
import string
|
||
import traceback
|
||
import threading
|
||
import requests
|
||
import hashlib
|
||
import xml.etree.ElementTree as ET
|
||
import xmltodict
|
||
import logging
|
||
from decimal import Decimal
|
||
|
||
from django.conf import settings
|
||
from django.db import models, transaction
|
||
from gvsdsdk.fluent import db, func, FQ
|
||
from django.db.models import F, Q, OuterRef, Subquery
|
||
from django.core.cache import cache
|
||
from django.core.exceptions import ObjectDoesNotExist
|
||
from django.http import HttpResponse
|
||
from django.utils import timezone
|
||
|
||
from rest_framework.views import APIView
|
||
from rest_framework.response import Response
|
||
from rest_framework import permissions, status
|
||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||
from rest_framework.parsers import JSONParser
|
||
from rest_framework.throttling import AnonRateThrottle
|
||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||
|
||
from tencentcloud.common import credential
|
||
from tencentcloud.common.profile.client_profile import ClientProfile
|
||
from tencentcloud.common.profile.http_profile import HttpProfile
|
||
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
||
from tencentcloud.sts.v20180813 import sts_client, models
|
||
|
||
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,
|
||
parse_pair_from_group_id, resolve_pair_from_order, check_pair_chat_permission,
|
||
query_pair_orders, order_to_chat_dict,
|
||
)
|
||
from utils.fadan_utils import check_fadan_qiangdan_eligible
|
||
|
||
from orders.utils import (
|
||
calc_shangjia_order_fencheng,
|
||
update_daily_payout,
|
||
settle_shangjia_order_guanshi_fenhong
|
||
)
|
||
from shop.utils import calculate_pingtai_and_dianpu_shouyi, validate_shangpin_and_dianpu, update_dianpu_daily_stat
|
||
from products.utils import update_shangpin_daily_stat
|
||
from backend.utils import update_shangjia_daily, update_dashou_daily_by_action, pick_leixing_id, datetime_aliases, fmt_datetime
|
||
from rank.services import record_dashou_biaoxian
|
||
from rank.utils import check_dashou_biaoqian_required
|
||
|
||
from orders.notice_tasks import dingdan_guangbo
|
||
|
||
from ..models import (
|
||
Order, MerchantOrderExt, PlatformOrderExt, PlayerDeliveryImage,
|
||
PenaltyRecord, PenaltyEvidenceImage, Penalty, PenaltyAppealImage, PenaltyBonus, PenaltyBonusRate,
|
||
CommissionRate, PlayerRating, RefundRecord,
|
||
)
|
||
from jituan.services.club_config import get_commission_rate, get_commission_rate_object
|
||
from jituan.services.club_context import resolve_club_id_from_request
|
||
from jituan.services.club_user import get_payment_openid
|
||
from jituan.services.club_penalty import resolve_penalty_club_id, resolve_penalty_record_club_id
|
||
from users.fadan_fenhong_utils import lock_penalty_bonus
|
||
from users.fadan_fenhong_utils import lock_penalty_bonus
|
||
from products.models import Shangpin, ShangpinLeixing, Huiyuangoumai
|
||
from users.models import UserDashou, UserShangjia, UserBoss
|
||
from users.business_models import User
|
||
from rank.models import DashouBiaoxian, Chenghao, DingdanBiaoqian, YonghuChenghao
|
||
from config.models import (
|
||
ShangjiaLianjie
|
||
)
|
||
|
||
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
def get_self_goeasy_appkey():
|
||
try:
|
||
return getattr(settings, 'GOEASY_APPKEY', None)
|
||
except Exception:
|
||
return None
|
||
|
||
|
||
def get_self_goeasy_secret():
|
||
try:
|
||
return getattr(settings, 'GOEASY_SECRET', None)
|
||
except Exception:
|
||
return None
|
||
|
||
|
||
def get_self_storage_domain():
|
||
try:
|
||
return getattr(settings, 'COS_DOMAIN', '')
|
||
except Exception:
|
||
return ''
|
||
|
||
|
||
def full_avatar(relative_url, bucket_domain=None):
|
||
if not relative_url:
|
||
return ''
|
||
if relative_url.startswith('http'):
|
||
return relative_url
|
||
domain = bucket_domain or get_self_storage_domain()
|
||
if domain:
|
||
domain = domain.rstrip('/') + '/'
|
||
return domain + relative_url.lstrip('/')
|
||
return relative_url
|
||
|
||
|
||
# ----------------- GoEasy API 封装 -----------------
|
||
def subscribe_users_to_group(user_ids, group_ids, appkey=None, secret=None):
|
||
"""订阅用户到群组,确保能收发该群消息"""
|
||
if not appkey:
|
||
appkey = get_self_goeasy_appkey()
|
||
if not secret:
|
||
secret = get_self_goeasy_secret()
|
||
if not appkey:
|
||
logger.error("订阅失败:GoEasy AppKey 未配置")
|
||
return False
|
||
url = 'https://rest-hangzhou.goeasy.io/v2/im/subscribe-groups'
|
||
body = {"appkey": appkey, "userIds": user_ids, "groupIds": group_ids}
|
||
headers = {"Content-Type": "application/json"}
|
||
if secret:
|
||
headers["Authorization"] = f"Bearer {secret}"
|
||
try:
|
||
r = requests.post(url, headers=headers, json=body, timeout=10)
|
||
if r.status_code == 200:
|
||
logger.info(f"订阅成功:用户 {user_ids} -> 群 {group_ids}")
|
||
return True
|
||
logger.error(f"订阅失败:{r.status_code} {r.text}")
|
||
return False
|
||
except Exception as e:
|
||
logger.exception("订阅异常")
|
||
return False
|
||
|
||
|
||
def send_group_message(
|
||
appkey, secret, group_id, sender_id, sender_name, sender_avatar,
|
||
message_text='', custom_payload=None, group_name='', order_id=''
|
||
):
|
||
"""
|
||
通过 GoEasy REST API 发送群消息。
|
||
message_text: 普通文本内容
|
||
custom_payload: 自定义消息体(如订单卡片),此时 type 设为 'order'
|
||
"""
|
||
url = 'https://rest-hangzhou.goeasy.io/v2/im/message'
|
||
to_data = {
|
||
"name": group_name or group_id,
|
||
"avatar": sender_avatar or '',
|
||
"orderId": order_id,
|
||
}
|
||
msg_type = 'order' if custom_payload else 'text'
|
||
payload = custom_payload if custom_payload else message_text
|
||
|
||
body = {
|
||
"appkey": appkey,
|
||
"senderId": sender_id,
|
||
"senderData": {"avatar": sender_avatar, "name": sender_name},
|
||
"to": {"type": "group", "id": group_id, "data": to_data},
|
||
"type": msg_type,
|
||
"payload": payload
|
||
}
|
||
headers = {"Content-Type": "application/json"}
|
||
if secret:
|
||
headers["Authorization"] = f"Bearer {secret}"
|
||
|
||
for attempt in range(3): # 最多重试3次
|
||
try:
|
||
r = requests.post(url, headers=headers, json=body, timeout=10)
|
||
if r.status_code == 200:
|
||
logger.info(f"消息发送成功:sender={sender_id}, group={group_id}, type={msg_type}")
|
||
return True
|
||
logger.warning(f"消息发送失败 (尝试 {attempt+1}/3):状态码 {r.status_code}, 响应 {r.text}")
|
||
except Exception as e:
|
||
logger.warning(f"消息发送异常 (尝试 {attempt+1}/3):{e}")
|
||
if attempt < 2:
|
||
time.sleep(1)
|
||
logger.error(f"消息最终发送失败:sender={sender_id}, group={group_id}")
|
||
return False
|
||
|
||
|
||
# ----------------- 权限校验 -----------------
|
||
def check_user_permission(identity_type, uid, order):
|
||
"""验证用户身份、账号状态、以及是否属于该订单"""
|
||
try:
|
||
user = User.query.select_related('DashouProfile').get(UserUID=uid)
|
||
except User.DoesNotExist:
|
||
return False, '用户不存在'
|
||
|
||
if identity_type == 'PlayerID':
|
||
try:
|
||
dashou = user.DashouProfile
|
||
if dashou.zhanghaozhuangtai != 1 :
|
||
return False, '打手账号已被封禁或状态异常'
|
||
except UserDashou.DoesNotExist:
|
||
return False, '该用户不是打手'
|
||
if order.PlayerID != uid:
|
||
return False, '你不是该订单的接单打手'
|
||
|
||
elif identity_type == 'shangjia':
|
||
try:
|
||
shop = UserShangjia.query.get(user=user)
|
||
if shop.zhuangtai != 1:
|
||
return False, '商家账号已被禁用'
|
||
except UserShangjia.DoesNotExist:
|
||
return False, '该用户不是商家'
|
||
try:
|
||
if order.shangjia_kuozhan.MerchantID != uid:
|
||
return False, '你不是该订单的发布商家'
|
||
except MerchantOrderExt.DoesNotExist:
|
||
return False, '订单缺少商家信息'
|
||
|
||
elif identity_type == 'boss':
|
||
try:
|
||
UserBoss.query.get(user=user) # 老板暂无封禁字段,仅需存在
|
||
except UserBoss.DoesNotExist:
|
||
return False, '该用户不是老板'
|
||
try:
|
||
if order.pingtai_kuozhan.BossID != uid:
|
||
return False, '你不是该订单的老板'
|
||
except PlatformOrderExt.DoesNotExist:
|
||
return False, '订单缺少平台信息'
|
||
|
||
else:
|
||
return False, '不支持的身份类型'
|
||
return True, ''
|
||
|
||
|
||
# ----------------- 主视图 -----------------
|
||
|
||
|
||
class DaiLiQunLiaoXiaoXiView(APIView):
|
||
"""
|
||
代理发送群聊消息
|
||
"""
|
||
permission_classes = [permissions.IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
# ===== 1. 参数解析 =====
|
||
order_id = request.data.get('orderId')
|
||
identity_type = request.data.get('identityType') # dashou / 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)
|
||
|
||
logger.info(f"[群聊代理] 收到请求: orderId={order_id}, identityType={identity_type}, "
|
||
f"messageType={message_type}, text={message_text[:20]}...")
|
||
|
||
if not order_id or not identity_type:
|
||
return Response({'code': 400, 'msg': '缺少 orderId 或 identityType'})
|
||
if identity_type not in ('PlayerID', 'shangjia', 'boss'):
|
||
return Response({'code': 400, 'msg': 'identityType 无效'})
|
||
|
||
# ===== 2. 构造 senderId =====
|
||
uid = request.user.UserUID
|
||
prefix_map = {'PlayerID': 'Ds', 'shangjia': 'Sj', 'boss': 'Boss'}
|
||
sender_id = prefix_map[identity_type] + uid
|
||
logger.info(f"[群聊代理] 登录用户 UserID={uid}, 构造 senderId={sender_id}")
|
||
|
||
# ===== 3. 查询订单 =====
|
||
try:
|
||
order = Order.query.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(OrderID=order_id)
|
||
except Order.DoesNotExist:
|
||
logger.warning(f"[群聊代理] 订单 {order_id} 不存在")
|
||
return Response({'code': 404, 'msg': '订单不存在'})
|
||
|
||
# ===== 4. 权限校验 =====
|
||
valid, err_msg = check_user_permission(identity_type, uid, order)
|
||
if not valid:
|
||
logger.warning(f"[群聊代理] 权限校验失败: {err_msg}, uid={uid}, identity={identity_type}")
|
||
return Response({'code': 403, 'msg': err_msg})
|
||
|
||
# ===== 5. 我方 GoEasy 配置 =====
|
||
self_appkey = get_self_goeasy_appkey()
|
||
self_secret = get_self_goeasy_secret()
|
||
if not self_appkey:
|
||
logger.error("[群聊代理] 我方 GoEasy AppKey 未配置")
|
||
return Response({'code': 500, 'msg': '系统配置错误(APPKEY)'})
|
||
|
||
# ===== 6. 公共群信息(新单走配对群,与文赫一致) =====
|
||
real_group_id = resolve_pair_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('')
|
||
|
||
# ===== 7. 订阅发送者到群 =====
|
||
if not subscribe_users_to_group([sender_id], [real_group_id], self_appkey, self_secret):
|
||
logger.error("[群聊代理] 订阅群组失败")
|
||
return Response({'code': 500, 'msg': '订阅群组失败'})
|
||
|
||
custom_payload = order_payload if message_type == 'order' else None
|
||
text_content = message_text
|
||
|
||
# ---------- 向我方群发送消息 ----------
|
||
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.OrderID,
|
||
)
|
||
if not local_ok:
|
||
return Response({'code': 500, 'msg': '消息发送失败'})
|
||
|
||
return Response({'code': 200, 'msg': '发送成功'})
|
||
|
||
|
||
|
||
class ShangjiaLianjieLiuYanZhuanFaView(APIView):
|
||
"""
|
||
商家链接留言转发接口
|
||
路径:POST /dingdan/sjljlbxxzf
|
||
固定以 B{商家ID} 身份发送群消息。
|
||
"""
|
||
permission_classes = []
|
||
|
||
def post(self, request):
|
||
token = request.data.get('token', '').strip()
|
||
dingdan_id = str(request.data.get('dingdan_id') or request.data.get('dingdanId') or '').strip()
|
||
text = (request.data.get('text') or '').strip()
|
||
|
||
if not text or (not token and not dingdan_id):
|
||
return Response({'code': 400, 'msg': '缺少参数'})
|
||
|
||
if token:
|
||
try:
|
||
lianjie = ShangjiaLianjie.query.get(LinkToken=token)
|
||
except ShangjiaLianjie.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '链接不存在或已失效'})
|
||
else:
|
||
try:
|
||
lianjie = ShangjiaLianjie.query.get(OrderID=dingdan_id)
|
||
except ShangjiaLianjie.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '订单链接不存在或已失效'})
|
||
|
||
if not lianjie.OrderID:
|
||
return Response({'code': 400, 'msg': '链接缺少订单ID'})
|
||
|
||
try:
|
||
order = Order.query.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(OrderID=lianjie.OrderID)
|
||
except Order.DoesNotExist:
|
||
return Response({'code': 500, 'msg': '订单数据异常'})
|
||
|
||
shangjia_uid = lianjie.UserID
|
||
sender_id = f"B{shangjia_uid}"
|
||
sender_name = "老板"
|
||
sender_avatar = ''
|
||
|
||
appkey = get_self_goeasy_appkey()
|
||
secret = get_self_goeasy_secret()
|
||
if not appkey:
|
||
return Response({'code': 500, 'msg': '系统配置错误(APPKEY)'})
|
||
|
||
group_id = resolve_pair_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}"
|
||
|
||
if not _subscribe_users_to_group([sender_id], [group_id], appkey, secret):
|
||
return Response({'code': 500, 'msg': '订阅群组失败'})
|
||
|
||
local_ok = _send_group_message(
|
||
appkey=appkey,
|
||
secret=secret,
|
||
group_id=group_id,
|
||
sender_id=sender_id,
|
||
sender_name=sender_name,
|
||
sender_avatar=sender_avatar,
|
||
message_text=text,
|
||
custom_payload=None,
|
||
group_name=group_name,
|
||
order_id=order.OrderID,
|
||
)
|
||
if not local_ok:
|
||
return Response({'code': 500, 'msg': '消息发送失败'})
|
||
|
||
return Response({'code': 200, 'msg': '发送成功'})
|
||
|
||
|
||
class LianTongDuiHuaZhunBeiView(APIView):
|
||
"""进入订单群聊前准备:返回配对 groupId 及双方资料,并订阅 GoEasy 群(不影响抢单校验)"""
|
||
authentication_classes = [JWTAuthentication]
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
IDENTITY_MAP = {
|
||
'dashou': 'PlayerID',
|
||
'shangjia': 'shangjia',
|
||
'normal': 'boss',
|
||
'boss': 'boss',
|
||
}
|
||
|
||
def post(self, request):
|
||
dingdan_id = request.data.get('dingdan_id') or request.data.get('orderId')
|
||
identity_type = request.data.get('identityType') or ''
|
||
if not dingdan_id:
|
||
return Response({'code': 400, 'msg': '缺少 dingdan_id'})
|
||
|
||
backend_identity = self.IDENTITY_MAP.get(identity_type, identity_type)
|
||
if backend_identity not in ('PlayerID', 'shangjia', 'boss'):
|
||
return Response({'code': 400, 'msg': 'identityType 无效'})
|
||
|
||
uid = request.user.UserUID
|
||
try:
|
||
order = Order.query.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(OrderID=dingdan_id)
|
||
except Order.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '订单不存在'})
|
||
|
||
valid, err_msg = check_user_permission(backend_identity, uid, order)
|
||
if not valid:
|
||
return Response({'code': 403, 'msg': err_msg})
|
||
|
||
if backend_identity == 'PlayerID' and not order.PlayerID:
|
||
return Response({'code': 400, 'msg': '订单尚未接单,暂无法进入群聊'})
|
||
|
||
data, subscribed = prepare_order_group_chat(order, backend_identity, uid, subscribe=True)
|
||
if not subscribed:
|
||
logger.warning(f'ltdhzb 订阅群失败 order={dingdan_id} uid={uid}')
|
||
|
||
return Response({'code': 0, 'msg': 'ok', 'data': data})
|
||
|
||
|
||
class LianTongPeiDuiDingDanLieBiaoView(APIView):
|
||
"""聊天页:获取打手与商家/老板之间的历史订单列表(可搜索)"""
|
||
authentication_classes = [JWTAuthentication]
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
IDENTITY_MAP = {
|
||
'dashou': 'PlayerID',
|
||
'shangjia': 'shangjia',
|
||
'normal': 'boss',
|
||
'boss': 'boss',
|
||
}
|
||
|
||
def post(self, request):
|
||
group_id = request.data.get('groupId') or request.data.get('group_id')
|
||
dingdan_id = request.data.get('dingdan_id') or request.data.get('orderId')
|
||
keyword = (request.data.get('keyword') or '').strip()
|
||
identity_type = request.data.get('identityType') or ''
|
||
backend_identity = self.IDENTITY_MAP.get(identity_type, identity_type)
|
||
if backend_identity not in ('PlayerID', 'shangjia', 'boss'):
|
||
return Response({'code': 400, 'msg': 'identityType 无效'})
|
||
|
||
dashou_uid, partner_uid, partner_role = parse_pair_from_group_id(group_id)
|
||
if not dashou_uid and dingdan_id:
|
||
try:
|
||
order = Order.query.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(OrderID=dingdan_id)
|
||
except Order.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '订单不存在'})
|
||
dashou_uid, partner_uid, partner_role = resolve_pair_from_order(order)
|
||
|
||
if not dashou_uid or not partner_uid:
|
||
return Response({'code': 400, 'msg': '无法解析配对信息,请确认订单已接单'})
|
||
|
||
uid = request.user.UserUID
|
||
valid, err_msg = check_pair_chat_permission(backend_identity, uid, dashou_uid, partner_uid, partner_role)
|
||
if not valid:
|
||
return Response({'code': 403, 'msg': err_msg})
|
||
|
||
orders = query_pair_orders(dashou_uid, partner_uid, partner_role, keyword=keyword, limit=40)
|
||
latest = orders[0] if orders else None
|
||
return Response({'code': 0, 'msg': 'ok', 'data': {'list': orders, 'latest': latest}})
|
||
|
||
|
||
class LianTongDingDanZhuangTaiView(APIView):
|
||
"""聊天页:刷新单个订单状态"""
|
||
authentication_classes = [JWTAuthentication]
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
IDENTITY_MAP = LianTongPeiDuiDingDanLieBiaoView.IDENTITY_MAP
|
||
|
||
def post(self, request):
|
||
dingdan_id = request.data.get('dingdan_id') or request.data.get('orderId')
|
||
identity_type = request.data.get('identityType') or ''
|
||
if not dingdan_id:
|
||
return Response({'code': 400, 'msg': '缺少 dingdan_id'})
|
||
|
||
backend_identity = self.IDENTITY_MAP.get(identity_type, identity_type)
|
||
if backend_identity not in ('PlayerID', 'shangjia', 'boss'):
|
||
return Response({'code': 400, 'msg': 'identityType 无效'})
|
||
|
||
try:
|
||
order = Order.query.select_related('pingtai_kuozhan', 'shangjia_kuozhan').get(OrderID=dingdan_id)
|
||
except Order.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '订单不存在'})
|
||
|
||
valid, err_msg = check_user_permission(backend_identity, request.user.UserUID, order)
|
||
if not valid:
|
||
return Response({'code': 403, 'msg': err_msg})
|
||
|
||
data = order_to_chat_dict(order)
|
||
return Response({'code': 0, 'msg': 'ok', 'data': data})
|
||
|
||
|
||
class LianTongDingDanZhuangTaiPiLiangView(APIView):
|
||
"""消息列表:批量刷新订单状态"""
|
||
authentication_classes = [JWTAuthentication]
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
order_ids = request.data.get('order_ids') or []
|
||
if not isinstance(order_ids, list):
|
||
return Response({'code': 400, 'msg': 'order_ids 格式错误'})
|
||
|
||
uid = request.user.UserUID
|
||
# 批量查询订单,避免循环内 N+1
|
||
oids = [str(oid).strip() for oid in order_ids[:20] if str(oid).strip()]
|
||
orders_map = {
|
||
o.OrderID: o
|
||
for o in Order.query.select_related('pingtai_kuozhan', 'shangjia_kuozhan').filter(OrderID__in=oids)
|
||
}
|
||
result = {}
|
||
for oid in oids:
|
||
order = orders_map.get(oid)
|
||
if not order:
|
||
continue
|
||
for identity in ('PlayerID', 'shangjia', 'boss'):
|
||
valid, _ = check_user_permission(identity, uid, order)
|
||
if valid:
|
||
d = order_to_chat_dict(order)
|
||
result[oid] = {
|
||
'jieshao': d['jieshao'],
|
||
'jine': d['jine'],
|
||
'zhuangtai': d['zhuangtai'],
|
||
'zhuangtaiText': d['zhuangtaiText'],
|
||
}
|
||
break
|
||
return Response({'code': 0, 'msg': 'ok', 'data': result})
|
||
|
||
|