修复了客服数据统计,强化了商家数据获取接口获取更多数据

This commit is contained in:
XingQue
2026-06-20 04:12:57 +08:00
parent 4b704d48fa
commit 993c0c54b0
4 changed files with 131 additions and 12 deletions

View File

@@ -0,0 +1,86 @@
"""订单响应增强:派单客服信息、头像相对路径。"""
from merchant_ops.constants import ACTOR_MERCHANT, ACTOR_STAFF
from merchant_ops.models import MerchantOrderDispatch, MerchantStaffMember
from users.business_models import User
def user_avatar_relative(user):
"""返回用户表 Avatar 相对路径(前端自行拼接 OSS"""
if not user:
return ''
av = (getattr(user, 'Avatar', None) or '').strip()
if not av:
return ''
if av.startswith('http://') or av.startswith('https://'):
return av
return av.lstrip('/')
def get_user_avatar_by_uid(uid):
if not uid:
return ''
u = User.query.filter(UserUID=uid).first()
return user_avatar_relative(u)
def get_dispatch_staff_payload(order_id):
"""订单派单归属;无记录则 None商家老板派单可能无 dispatch 记录)。"""
if not order_id:
return None
disp = MerchantOrderDispatch.query.filter(order_id=order_id).first()
if not disp:
return None
if disp.dispatch_actor_type == ACTOR_MERCHANT:
return {
'dispatch_staff_type': ACTOR_MERCHANT,
'dispatch_staff_uid': disp.dispatch_actor_id or '',
'dispatch_staff_name': '商家老板',
'dispatch_role_code': '',
'dispatch_role_name': '商家老板',
'dispatch_member_id': None,
}
if disp.staff_member_id:
m = MerchantStaffMember.query.filter(id=disp.staff_member_id).select_related('role').first()
if m:
return {
'dispatch_staff_type': ACTOR_STAFF,
'dispatch_staff_uid': m.staff_user_id,
'dispatch_staff_name': m.display_name or (m.role.role_name if m.role else m.staff_user_id),
'dispatch_role_code': m.role.role_code if m.role else '',
'dispatch_role_name': m.role.role_name if m.role else '',
'dispatch_member_id': m.id,
}
return {
'dispatch_staff_type': disp.dispatch_actor_type,
'dispatch_staff_uid': disp.dispatch_actor_id or '',
'dispatch_staff_name': disp.dispatch_actor_id or '',
'dispatch_role_code': '',
'dispatch_role_name': '',
'dispatch_member_id': disp.staff_member_id,
}
def enrich_order_detail_data(data, order_id):
"""订单详情追加派单客服;打手头像优先用户表相对路径。"""
if not data or not isinstance(data, dict):
return data
payload = get_dispatch_staff_payload(order_id)
if payload:
data['dispatch_staff'] = payload
else:
data['dispatch_staff'] = None
dashou_uid = data.get('dashou_yonghuid') or data.get('fuwudashou_id') or ''
av = get_user_avatar_by_uid(dashou_uid)
if av:
data['dashou_avatar'] = av
return data
def staff_order_list_item(ext):
"""子客服订单列表项:不含派单客服字段,打手头像走用户表。"""
from merchant_ops.services.order_query import staff_order_to_dict
d = staff_order_to_dict(ext)
pid = d.get('jiedan_dashou_id')
if pid:
d['jiedan_dashou_avatar'] = get_user_avatar_by_uid(pid)
return d

View File

@@ -126,30 +126,49 @@ class StaffMemberListView(APIView):
def post(self, request):
try:
from datetime import date
from users.business_models import User
from merchant_ops.services.order_enrich import user_avatar_relative
mid = request.user.UserUID
ensure_owner_merchant(mid, request.user)
today = date.today()
members = MerchantStaffMember.query.filter(
merchant_id=mid, status=MEMBER_STATUS_ACTIVE,
merchant_id=mid,
status__in=[MEMBER_STATUS_ACTIVE, MEMBER_STATUS_DISABLED],
).select_related('role')
result = []
for m in members:
stats = MerchantStaffDailyStats.query.filter(member_id=m.id).aggregate(
total_stats = MerchantStaffDailyStats.query.filter(member_id=m.id).aggregate(
dispatch_count=Sum('dispatch_count'),
settle_count=Sum('settle_count'),
settle_amount=Sum('settle_amount'),
refund_count=Sum('refund_count'),
)
wallet = m.wallets.first()
today_stat = MerchantStaffDailyStats.query.filter(
member_id=m.id, stat_date=today,
).first()
wallet = get_or_create_wallet(m)
u = User.query.filter(UserUID=m.staff_user_id).first()
result.append({
'id': m.id,
'uid': m.staff_user_id,
'nickname': m.display_name or m.staff_user_id,
'isChief': m.role.role_code == 'CHIEF',
'isFinance': m.role.role_code == 'FINANCE',
'role_name': m.role.role_name,
'orderCount': stats['dispatch_count'] or 0,
'settleCount': stats['settle_count'] or 0,
'settleAmount': str(stats['settle_amount'] or '0.00'),
'quota_available': str(wallet.quota_available if wallet else 0),
'avatar': user_avatar_relative(u),
'role_name': m.role.role_name if m.role else '',
'isChief': m.role.role_code == 'CHIEF' if m.role else False,
'isFinance': m.role.role_code == 'FINANCE' if m.role else False,
'status': m.status,
'isDisabled': m.status == MEMBER_STATUS_DISABLED,
'todayDispatchCount': today_stat.dispatch_count if today_stat else int(m.today_dispatch_count or 0),
'dispatchCount': total_stats['dispatch_count'] or 0,
'orderCount': total_stats['dispatch_count'] or 0,
'settleCount': total_stats['settle_count'] or 0,
'refundCount': total_stats['refund_count'] or 0,
'settleAmount': str(total_stats['settle_amount'] or '0.00'),
'quota_available': str(wallet.quota_available),
'quota_total': str(wallet.quota_total),
'quota_used': str(wallet.quota_used),
})
return Response({'code': 0, 'msg': '成功', 'data': {'list': result}})
except Exception as e:

View File

@@ -13,8 +13,8 @@ from merchant_ops.services.order_query import (
staff_apply_order_filters,
staff_can_view_all_orders,
staff_order_ext_qs,
staff_order_to_dict,
)
from merchant_ops.services.order_enrich import staff_order_list_item
from merchant_ops.services.audit import audit_staff
from merchant_ops.services.dispatch import staff_dispatch_order
from merchant_ops.services.stats import bump_cancel_count, bump_penalty_count, update_merchant_and_staff_stats
@@ -77,7 +77,7 @@ class StaffOrderListView(APIView):
start = (page - 1) * page_size
page_qs = base_qs.order_by('-Order__CreateTime')[start:start + page_size]
order_list = [staff_order_to_dict(ext) for ext in page_qs]
order_list = [staff_order_list_item(ext) for ext in page_qs]
return Response({
'code': 0,
@@ -105,6 +105,9 @@ class StaffOrderDetailView(APIView):
resp = proxy_merchant_order_view(
request, member, ShangjiaDingdanXiangqingView, 'order_detail',
)
if resp.status_code == 200 and resp.data.get('code') == 0 and resp.data.get('data'):
from merchant_ops.services.order_enrich import enrich_order_detail_data
enrich_order_detail_data(resp.data['data'], pick_order_id(request.data))
return resp
except Exception as e:
return _err(e)
@@ -186,6 +189,11 @@ class StaffRefundView(APIView):
resp = proxy_merchant_order_view(request, member, ShangjiaTuikuanShenqingView, 'order_refund_apply')
if resp.data.get('code') in (0, 200):
audit_staff(member, 'REFUND_APPLY', request, resource_id=pick_order_id(request.data))
from orders.models import Order
oid = pick_order_id(request.data)
o = Order.query.filter(OrderID=oid).first()
if o:
update_merchant_and_staff_stats(member.merchant_id, member, o.Amount, STAT_ACTION_REFUND)
return resp
except Exception as e:
return _err(e)

View File

@@ -2310,6 +2310,12 @@ class ShangjiaDingdanXiangqingView(APIView):
'xin_dingdan_id': xin_dingdan_id,
}
try:
from merchant_ops.services.order_enrich import enrich_order_detail_data
enrich_order_detail_data(response_data, dingdan_id)
except Exception as e:
logger.warning(f'派单客服信息附加失败: {e}')
return Response({
'code': 0,
'msg': '成功',