Files
Django/merchant_ops/views_action_stats.py
XingQue 9c029288ba feat(merchant): 订单操作流水表与按真实操作人统计
新增 merchant_order_action_log,在结算/撤单/退款/罚单/派单/换打手成功路径写入真实操作人;提供 action-stats / action-operators 接口供经营数据页筛选统计。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 23:42:39 +08:00

103 lines
3.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.
"""商家订单操作人统计 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})