新增 merchant_order_action_log,在结算/撤单/退款/罚单/派单/换打手成功路径写入真实操作人;提供 action-stats / action-operators 接口供经营数据页筛选统计。 Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
3.9 KiB
Python
103 lines
3.9 KiB
Python
"""商家订单操作人统计 API(新接口,不影响旧统计)。"""
|
||
import logging
|
||
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from rest_framework.response import Response
|
||
from rest_framework.views import APIView
|
||
|
||
from merchant_ops.services.authz import (
|
||
StaffAuthError,
|
||
ensure_owner_merchant,
|
||
get_active_staff_member,
|
||
is_merchant_owner,
|
||
member_has_perm,
|
||
)
|
||
from merchant_ops.services.action_stats import (
|
||
build_operator_action_stats,
|
||
list_operator_candidates,
|
||
)
|
||
from merchant_ops.services.bootstrap import ensure_merchant_roles
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def _resolve_stats_merchant(request):
|
||
"""商家老板,或具备 stats_view/finance_view/audit_view 的子客服。"""
|
||
if is_merchant_owner(request.user):
|
||
ensure_owner_merchant(request.user.UserUID, request.user)
|
||
return request.user.UserUID, None
|
||
member = get_active_staff_member(request.user)
|
||
if not member:
|
||
raise StaffAuthError(403, '无权限')
|
||
if not (
|
||
member_has_perm(member, 'stats_view')
|
||
or member_has_perm(member, 'finance_view')
|
||
or member_has_perm(member, 'audit_view')
|
||
):
|
||
raise StaffAuthError(403, '无统计权限')
|
||
ensure_merchant_roles(member.merchant_id)
|
||
return member.merchant_id, member
|
||
|
||
|
||
def _parse_filters(data):
|
||
action_codes = data.get('action_codes') or data.get('action_code_list')
|
||
if isinstance(action_codes, str):
|
||
action_codes = [x.strip() for x in action_codes.split(',') if x.strip()]
|
||
return {
|
||
'operator_user_id': (data.get('operator_user_id') or data.get('operator_id') or '').strip() or None,
|
||
'operator_type': (data.get('operator_type') or '').strip() or None,
|
||
'role_code': (data.get('role_code') or '').strip() or None,
|
||
'action_code': data.get('action_code'),
|
||
'action_codes': action_codes,
|
||
'leixing_id': data.get('leixing_id'),
|
||
'start_time': data.get('start_time') or data.get('start') or data.get('begin_time'),
|
||
'end_time': data.get('end_time') or data.get('end'),
|
||
}
|
||
|
||
|
||
class StaffOrderActionStatsView(APIView):
|
||
"""
|
||
POST /yonghu/merchant-staff/order/action-stats
|
||
按操作人/行为/时间/游戏类型统计结算·撤单·退款·罚单等操作量与金额。
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
merchant_id, _member = _resolve_stats_merchant(request)
|
||
filters = _parse_filters(request.data or {})
|
||
data = build_operator_action_stats(merchant_id, filters)
|
||
data['merchant_id'] = merchant_id
|
||
data['filters'] = {k: v for k, v in filters.items() if v not in (None, '', [])}
|
||
return Response({'code': 0, 'msg': 'ok', 'data': data})
|
||
except StaffAuthError as e:
|
||
return Response({'code': e.code, 'msg': e.msg, 'data': None})
|
||
except Exception as e:
|
||
logger.error('操作人统计失败: %s', e, exc_info=True)
|
||
return Response({'code': 99, 'msg': '统计失败', 'data': None})
|
||
|
||
|
||
class StaffOrderActionOperatorsView(APIView):
|
||
"""
|
||
POST /yonghu/merchant-staff/order/action-operators
|
||
返回可筛选的操作人候选(商家本人 + 子客服)。
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
merchant_id, _member = _resolve_stats_merchant(request)
|
||
return Response({
|
||
'code': 0,
|
||
'msg': 'ok',
|
||
'data': {
|
||
'merchant_id': merchant_id,
|
||
'operators': list_operator_candidates(merchant_id),
|
||
},
|
||
})
|
||
except StaffAuthError as e:
|
||
return Response({'code': e.code, 'msg': e.msg, 'data': None})
|
||
except Exception as e:
|
||
logger.error('操作人候选列表失败: %s', e, exc_info=True)
|
||
return Response({'code': 99, 'msg': '获取失败', 'data': None})
|