Files
Django/jituan/views_order_deal.py
XingQue 1b733d21cc feat: 订单列表/详情返回自动结算倒计时字段,并加小程序结算率接口
仅 eligible 结算中订单下发预计时间;支持 auto_settle_pending 筛选;新增 /dingdan/wo-de-deal-stat。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 02:08:32 +08:00

168 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""客服:成交指标配置 / 商家·打手指标查询。"""
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from backend.utils import verify_kefu_permission, has_merchant_order_permission
from jituan.services.club_context import resolve_club_id_from_request
from jituan.services import order_deal as deal_cfg
from jituan.services import deal_stats as stats_svc
def _auth_order(request):
username = (request.data.get('username') or request.data.get('phone') or '').strip()
if not username:
return None, Response({'code': 400, 'msg': '缺少username'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return None, permissions
codes = set(permissions or [])
user = getattr(request, 'user', None)
if getattr(user, 'IsSuperuser', False) or '000001' in codes:
return kefu, None
if has_merchant_order_permission(permissions) or '002ab' in codes:
return kefu, None
return None, Response({'code': 403, 'msg': '无订单管理权限'})
class OrderDealConfigView(APIView):
"""
POST /jituan/houtai/order-deal-config
action: get | save
"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
kefu, err = _auth_order(request)
if err:
return err
club_id = (resolve_club_id_from_request(request) or '').strip()
if not club_id:
return Response({'code': 400, 'msg': '请先选择俱乐部'})
action = (request.data.get('action') or 'get').strip().lower()
if action == 'get':
row = deal_cfg.get_or_create_deal_config(club_id)
return Response({'code': 0, 'msg': 'ok', 'data': deal_cfg.config_to_dict(row)})
if action == 'save':
try:
operator = getattr(request.user, 'UserUID', '') or ''
data = deal_cfg.save_deal_config(club_id, request.data, updated_by=operator)
return Response({'code': 0, 'msg': '已保存', 'data': data})
except ValueError as e:
return Response({'code': 400, 'msg': str(e)})
return Response({'code': 400, 'msg': '无效 action'})
class MerchantDealStatView(APIView):
"""POST /jituan/houtai/merchant-deal-stat action: list|detail"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
kefu, err = _auth_order(request)
if err:
return err
club_id = (resolve_club_id_from_request(request) or '').strip()
from jituan.models import MerchantDealStat
action = (request.data.get('action') or 'list').strip().lower()
uid = (request.data.get('merchant_uid') or '').strip()
if action == 'detail' and uid:
row = MerchantDealStat.query.filter(club_id=club_id, merchant_uid=uid).first()
return Response({
'code': 0,
'msg': 'ok',
'data': stats_svc.serialize_merchant_stat(row) if row else {
'merchant_uid': uid, 'order_count': 0, 'deal_rate': 0, 'refund_rate': 0,
'fine_rate': 0, 'avg_deal_hours': 0,
},
})
qs = MerchantDealStat.query.filter(club_id=club_id).order_by('-order_count')[:200]
return Response({
'code': 0,
'msg': 'ok',
'data': {'list': [stats_svc.serialize_merchant_stat(r) for r in qs]},
})
class DashouDealStatView(APIView):
"""POST /jituan/houtai/dashou-deal-stat action: list|detail"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
kefu, err = _auth_order(request)
if err:
return err
club_id = (resolve_club_id_from_request(request) or '').strip()
from jituan.models import DashouDealStat
action = (request.data.get('action') or 'list').strip().lower()
uid = (request.data.get('dashou_uid') or '').strip()
if action == 'detail' and uid:
row = DashouDealStat.query.filter(club_id=club_id, dashou_uid=uid).first()
return Response({
'code': 0,
'msg': 'ok',
'data': stats_svc.serialize_dashou_stat(row) if row else {
'dashou_uid': uid, 'order_count': 0, 'deal_rate': 0, 'fined_rate': 0,
},
})
qs = DashouDealStat.query.filter(club_id=club_id).order_by('-order_count')[:200]
return Response({
'code': 0,
'msg': 'ok',
'data': {'list': [stats_svc.serialize_dashou_stat(r) for r in qs]},
})
class MiniappMyDealStatView(APIView):
"""
POST /dingdan/wo-de-deal-stat
小程序订单列表顶:结算率/罚款率。
body.role: merchant | dashou默认按身份推断
商家客服看所属商家指标;无样本返回 data=null。
"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
club_id = (resolve_club_id_from_request(request) or '').strip()
if not club_id:
return Response({'code': 400, 'msg': '缺少俱乐部', 'data': None})
user = request.user
uid = getattr(user, 'UserUID', '') or ''
role = (request.data.get('role') or '').strip().lower()
if role not in ('merchant', 'dashou'):
# 推断:活跃子客服 → merchant否则看商家认证 / 打手
from merchant_ops.services.authz import get_active_staff_member, is_merchant_owner
if get_active_staff_member(user) or is_merchant_owner(user):
role = 'merchant'
else:
role = 'dashou'
if role == 'merchant':
merchant_uid = uid
from merchant_ops.services.authz import get_active_staff_member, is_merchant_owner, member_has_perm
member = get_active_staff_member(user)
if member and not is_merchant_owner(user):
if not member_has_perm(member, 'order_view_all') and not member_has_perm(member, 'order_view_self'):
return Response({'code': 403, 'msg': '无订单查看权限', 'data': None})
merchant_uid = member.merchant_id
from jituan.models import MerchantDealStat
row = MerchantDealStat.query.filter(club_id=club_id, merchant_uid=merchant_uid).first()
return Response({
'code': 0,
'msg': 'ok',
'data': stats_svc.miniapp_merchant_deal_payload(row),
})
from jituan.models import DashouDealStat
row = DashouDealStat.query.filter(club_id=club_id, dashou_uid=uid).first()
return Response({
'code': 0,
'msg': 'ok',
'data': stats_svc.miniapp_dashou_deal_payload(row),
})