diff --git a/backend/utils.py b/backend/utils.py index 9cb54c7..93679c5 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -198,7 +198,7 @@ def update_dashou_daily_by_action(yonghuid, amount, action): PlayerDailyStats.query.filter(id=stat.id).update(**update_fields) -def update_shangjia_daily(yonghuid, amount, action): +def update_shangjia_daily(yonghuid, amount, action, order_id=None): """ 根据操作行为更新商家每日统计(派发、结算、退款) @@ -206,6 +206,7 @@ def update_shangjia_daily(yonghuid, amount, action): yonghuid: 商家用户ID amount: 当前订单涉及的金额(Decimal) action: 操作类型,1 = 派发,2 = 结算(成交),3 = 退款 + order_id: 订单ID;传入时同步更新该订单派单归属客服的日统计 说明: - 金额必须大于0,否则不执行任何操作 @@ -253,6 +254,12 @@ def update_shangjia_daily(yonghuid, amount, action): # 执行原子更新 MerchantDailyStats.query.filter(id=stat.id).update(**update_fields) + if order_id: + try: + from merchant_ops.services.stats import sync_staff_stats_by_order_id + sync_staff_stats_by_order_id(order_id, amount, action) + except Exception: + logger.warning('同步客服日统计失败 order_id=%s', order_id, exc_info=True) def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')): diff --git a/config/views.py b/config/views.py index 1e9b89b..14ddc1c 100644 --- a/config/views.py +++ b/config/views.py @@ -2596,7 +2596,8 @@ class KehuTianxieDingdanView(APIView): update_shangjia_daily( yonghuid=lianjie_obj.UserID, amount=Decimal(str(dingdan_obj.Amount)), - action=1 # 1 = 派发 + action=1, # 1 = 派发 + order_id=dingdan_obj.OrderID, ) return Response({ diff --git a/merchant_ops/services/dispatch.py b/merchant_ops/services/dispatch.py index b0a0923..cd31721 100644 --- a/merchant_ops/services/dispatch.py +++ b/merchant_ops/services/dispatch.py @@ -11,7 +11,6 @@ from django.db.models import F from backend.utils import update_shangjia_daily from merchant_ops.constants import ACTOR_STAFF, STAT_ACTION_DISPATCH from merchant_ops.models import MerchantOrderDispatch -from merchant_ops.services.stats import update_staff_daily from merchant_ops.services.wallet import check_wallet_available, confirm_dispatch_deduct from orders.models import MerchantOrderExt, Order from orders.notice_tasks import dingdan_guangbo @@ -160,11 +159,6 @@ def staff_dispatch_order(member, staff_user, data): jinyueliushui=F('jinyueliushui') + Decimal(str(jiage)), ) - update_shangjia_daily(merchant_id, Decimal(str(jiage)), STAT_ACTION_DISPATCH) - update_staff_daily( - merchant_id, member.staff_user_id, member.id, jiage, STAT_ACTION_DISPATCH, - ) - MerchantOrderDispatch.objects.create( order_id=dingdan.OrderID, merchant_id=merchant_id, @@ -174,11 +168,9 @@ def staff_dispatch_order(member, staff_user, data): ) confirm_dispatch_deduct(member, jiage, dingdan.OrderID, member.staff_user_id) - - from merchant_ops.models import MerchantStaffMember - MerchantStaffMember.query.filter(id=member.id).update( - today_dispatch_count=F('today_dispatch_count') + 1, - today_dispatch_amount=F('today_dispatch_amount') + Decimal(str(jiage)), + update_shangjia_daily( + merchant_id, Decimal(str(jiage)), STAT_ACTION_DISPATCH, + order_id=dingdan.OrderID, ) except ValueError as e: diff --git a/merchant_ops/services/link.py b/merchant_ops/services/link.py index 2fe011d..26a5412 100644 --- a/merchant_ops/services/link.py +++ b/merchant_ops/services/link.py @@ -13,11 +13,9 @@ from django.db import transaction from django.db.models import F from django.utils import timezone -from backend.utils import update_shangjia_daily from config.models import ShangjiaLianjie, ShangjiaMoban -from merchant_ops.constants import ACTOR_STAFF, STAT_ACTION_DISPATCH -from merchant_ops.models import MerchantOrderDispatch, MerchantStaffMember -from merchant_ops.services.stats import update_staff_daily +from merchant_ops.constants import ACTOR_STAFF +from merchant_ops.models import MerchantOrderDispatch from merchant_ops.services.wallet import check_wallet_available, confirm_dispatch_deduct from orders.models import MerchantOrderExt, Order, CommissionRate from products.models import ShangpinLeixing @@ -194,14 +192,6 @@ def staff_generate_link(member, staff_user, data): ) confirm_dispatch_deduct(member, jiage, dingdan_id, member.staff_user_id) - update_shangjia_daily(merchant_id, Decimal(str(jiage)), STAT_ACTION_DISPATCH) - update_staff_daily( - merchant_id, member.staff_user_id, member.id, jiage, STAT_ACTION_DISPATCH, - ) - MerchantStaffMember.query.filter(id=member.id).update( - today_dispatch_count=F('today_dispatch_count') + 1, - today_dispatch_amount=F('today_dispatch_amount') + Decimal(str(jiage)), - ) shangjia.refresh_from_db() from merchant_ops.services.wallet import get_or_create_wallet diff --git a/merchant_ops/services/order_enrich.py b/merchant_ops/services/order_enrich.py index 491c057..fc9c63c 100644 --- a/merchant_ops/services/order_enrich.py +++ b/merchant_ops/services/order_enrich.py @@ -31,10 +31,12 @@ def get_dispatch_staff_payload(order_id): if not disp: return None if disp.dispatch_actor_type == ACTOR_MERCHANT: + u = User.query.filter(UserUID=disp.dispatch_actor_id).first() if disp.dispatch_actor_id else None return { 'dispatch_staff_type': ACTOR_MERCHANT, 'dispatch_staff_uid': disp.dispatch_actor_id or '', 'dispatch_staff_name': '商家老板', + 'dispatch_staff_avatar': user_avatar_relative(u), 'dispatch_role_code': '', 'dispatch_role_name': '商家老板', 'dispatch_member_id': None, @@ -42,10 +44,12 @@ def get_dispatch_staff_payload(order_id): if disp.staff_member_id: m = MerchantStaffMember.query.filter(id=disp.staff_member_id).select_related('role').first() if m: + u = User.query.filter(UserUID=m.staff_user_id).first() 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_staff_avatar': user_avatar_relative(u), '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, diff --git a/merchant_ops/services/stats.py b/merchant_ops/services/stats.py index c4112bc..55f7c48 100644 --- a/merchant_ops/services/stats.py +++ b/merchant_ops/services/stats.py @@ -1,4 +1,5 @@ """子客服日统计 + 商家日统计双写。""" +import logging from datetime import date from decimal import Decimal @@ -7,7 +8,9 @@ from django.db.models import F from backend.utils import update_shangjia_daily from merchant_ops.constants import STAT_ACTION_DISPATCH, STAT_ACTION_REFUND, STAT_ACTION_SETTLE -from merchant_ops.models import MerchantStaffDailyStats +from merchant_ops.models import MerchantOrderDispatch, MerchantStaffDailyStats, MerchantStaffMember + +logger = logging.getLogger(__name__) def _action_fields(action, amount): @@ -51,9 +54,40 @@ def update_staff_daily(merchant_id, staff_user_id, member_id, amount, action): MerchantStaffDailyStats.query.filter(id=stat.id).update(**update_kwargs) -def update_merchant_and_staff_stats(merchant_id, staff_member, amount, action): - """商家日统计必写;子客服则双写。""" - update_shangjia_daily(merchant_id, amount, action) +def sync_staff_stats_by_order_id(order_id, amount, action): + """按订单派单归属同步子客服日统计(商家侧由 update_shangjia_daily 负责)。""" + if not order_id: + return + amount = Decimal(str(amount)) if amount else Decimal('0.00') + if amount <= 0: + return + + disp = MerchantOrderDispatch.objects.filter(order_id=order_id).first() + if not disp or not disp.staff_member_id: + return + member = MerchantStaffMember.objects.filter(id=disp.staff_member_id).first() + if not member: + return + + update_staff_daily( + disp.merchant_id, + member.staff_user_id, + member.id, + amount, + action, + ) + if action == STAT_ACTION_DISPATCH: + MerchantStaffMember.objects.filter(id=member.id).update( + today_dispatch_count=F('today_dispatch_count') + 1, + today_dispatch_amount=F('today_dispatch_amount') + amount, + ) + + +def update_merchant_and_staff_stats(merchant_id, staff_member, amount, action, order_id=None): + """商家日统计必写;有 order_id 时按派单归属写客服统计,否则写操作人。""" + update_shangjia_daily(merchant_id, amount, action, order_id=order_id) + if order_id: + return if staff_member: update_staff_daily( merchant_id, diff --git a/merchant_ops/views_manage.py b/merchant_ops/views_manage.py index 0d21d1c..6ca5ae2 100644 --- a/merchant_ops/views_manage.py +++ b/merchant_ops/views_manage.py @@ -141,9 +141,11 @@ class StaffMemberListView(APIView): for m in members: total_stats = MerchantStaffDailyStats.query.filter(member_id=m.id).aggregate( dispatch_count=Sum('dispatch_count'), + dispatch_amount=Sum('dispatch_amount'), settle_count=Sum('settle_count'), settle_amount=Sum('settle_amount'), refund_count=Sum('refund_count'), + refund_amount=Sum('refund_amount'), ) today_stat = MerchantStaffDailyStats.query.filter( member_id=m.id, stat_date=today, @@ -161,11 +163,14 @@ class StaffMemberListView(APIView): '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), + 'todayDispatchAmount': str(today_stat.dispatch_amount if today_stat else (m.today_dispatch_amount or '0.00')), 'dispatchCount': total_stats['dispatch_count'] or 0, + 'dispatchAmount': str(total_stats['dispatch_amount'] or '0.00'), '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'), + 'refundCount': total_stats['refund_count'] or 0, + 'refundAmount': str(total_stats['refund_amount'] or '0.00'), 'quota_available': str(wallet.quota_available), 'quota_total': str(wallet.quota_total), 'quota_used': str(wallet.quota_used), diff --git a/merchant_ops/views_orders.py b/merchant_ops/views_orders.py index 6a9029a..1cf3742 100644 --- a/merchant_ops/views_orders.py +++ b/merchant_ops/views_orders.py @@ -17,8 +17,7 @@ from merchant_ops.services.order_query import ( 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 -from merchant_ops.constants import STAT_ACTION_REFUND, STAT_ACTION_SETTLE +from merchant_ops.services.stats import bump_cancel_count, bump_penalty_count from orders.models import MerchantOrderExt from orders.views import ( ShangjiaDingdanXiangqingView, ShangjiaJiesuanView, ShangjiaChexiaoView, @@ -155,11 +154,6 @@ class StaffSettleView(APIView): resp = proxy_merchant_order_view(request, member, ShangjiaJiesuanView, 'order_settle') if resp.data.get('code') in (0, 200): audit_staff(member, 'SETTLE', 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_SETTLE) return resp except Exception as e: return _err(e) @@ -189,11 +183,6 @@ 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) diff --git a/orders/views.py b/orders/views.py index 715b0c9..a19b5ee 100644 --- a/orders/views.py +++ b/orders/views.py @@ -1983,7 +1983,8 @@ class ShangjiaPaifaView(APIView): update_shangjia_daily( yonghuid=request.user.UserUID, amount=Decimal(str(jiage)), - action=1 + action=1, + order_id=dingdan.OrderID, ) # 事务提交,订单已持久化 except Exception as e: @@ -2487,7 +2488,8 @@ class ShangjiaJiesuanView(APIView): update_shangjia_daily( yonghuid=request.user.UserUID, amount=Decimal(str(jine)), - action=2 # 2 = 结算 + action=2, # 2 = 结算 + order_id=dingdan_id, ) # 商家订单管事分红(仅 Platform=2) @@ -2638,7 +2640,8 @@ class ShangjiaChexiaoView(APIView): update_shangjia_daily( yonghuid=request.user.UserUID, amount=Decimal(str(dingdan.Amount)), - action=3 # 3 = 退款 + action=3, # 3 = 退款 + order_id=dingdan_id, ) return Response({ @@ -5354,6 +5357,13 @@ class AdTongYiTuiKuanShangJia(APIView): shangjia_profile.save() logger.info(f"同意退款成功,商家{shangjia_id}退款订单+1,余额增加:{jine}") + update_shangjia_daily( + yonghuid=shangjia_id, + amount=Decimal(str(jine)), + action=3, + order_id=dingdan_id, + ) + except User.DoesNotExist: # 商家用户不存在,记录日志但不影响操作 logger.warning(f"同意退款:商家{shangjia_id}不存在,跳过商家更新") diff --git a/users/views.py b/users/views.py index f3934c9..4f61bd2 100644 --- a/users/views.py +++ b/users/views.py @@ -7955,7 +7955,8 @@ class KefuRejectRefundView(APIView): update_shangjia_daily( yonghuid=shangjia_id, amount=Decimal(str(order.Amount)), - action=2 # 2 = 结算 + action=2, # 2 = 结算 + order_id=dingdan_id, ) except (ObjectDoesNotExist, User.DoesNotExist, AttributeError): logger.warning(f"拒绝退款:商家信息不存在,跳过商家更新") @@ -8136,7 +8137,8 @@ class KefuMerchantRefundView(APIView): update_shangjia_daily( yonghuid=shangjia_id, amount=Decimal(str(order.Amount)), - action=3 # 3 = 退款 + action=3, # 3 = 退款 + order_id=dingdan_id, ) @@ -9531,7 +9533,8 @@ class KefuForceCompleteView(APIView): update_shangjia_daily( yonghuid=shangjia_ext.shangjia_id, amount=Decimal(str(order.Amount)), - action=2 # 2 = 结算 + action=2, # 2 = 结算 + order_id=dingdan_id, ) except ObjectDoesNotExist: