perf: P1 性能优化批量修复 + kefu 视图拆分
P1 性能优化(避免循环内 N+1 / N 次 DB 查询): - shop_order_views._batch_images: 移除 ThreadPoolExecutor 掩盖的 N 次查询,改单次批量 + Python 侧分组 - product_query.DashouHuiyuanList: 5N 查询 -> 3 次批量预取 + 本地闭包判断 - roles.GetRolePermissionView / 用户列表: 循环内 RolePermission/Permission/UserRole/Role -> 批量 __in 预取 - guanli / zuzhang 杜次分红 4.2/4.3: 循环内 update_or_create + 单条 delete -> bulk_create + bulk_update + 批量 delete - admin_config QQ 群配置: 循环内 exists/first/save/create -> 批量预取 + bulk_create + bulk_update - jituan 服务层多处合并统计查询、批量预取 map - rank 多处循环 N+1 改批量预取 - backend 多处循环内 count/create 改批量 - config/orders/merchant_ops 多处循环 N+1 改批量预取 其他改动: - users/views/kefu.py 拆分为 kefu_base/kefu_dashou/kefu_orders/kefu_punishment/kefu_withdraw 5 个文件 - 删除遗留脚本 check_prod_uid.py / create_rbac_tables.sql
This commit is contained in:
@@ -109,7 +109,7 @@ def staff_dispatch_order(member, staff_user, data):
|
||||
zhiding_dashou = None
|
||||
if zhiding_uid:
|
||||
try:
|
||||
zhiding_user = User.query.get(UserUID=zhiding_uid)
|
||||
zhiding_user = User.query.select_related('DashouProfile').get(UserUID=zhiding_uid)
|
||||
zhiding_dashou = zhiding_user.DashouProfile
|
||||
if zhiding_dashou.zhanghaozhuangtai != 1:
|
||||
return False, {'code': 400, 'msg': '指定打手状态异常', 'data': None}
|
||||
|
||||
@@ -119,6 +119,7 @@ def staff_generate_link(member, staff_user, data):
|
||||
final_huiyuan_id = shangpin_leixing.huiyuan_id or ''
|
||||
|
||||
label_name = ''
|
||||
ch = None
|
||||
if chenghao_id:
|
||||
ch = Chenghao.query.filter(id=chenghao_id).first()
|
||||
if ch:
|
||||
@@ -155,10 +156,9 @@ def staff_generate_link(member, staff_user, data):
|
||||
MerchantNickname=shangjia.nicheng or f"商家{merchant_id}",
|
||||
)
|
||||
|
||||
if chenghao_id:
|
||||
ch = Chenghao.query.filter(id=chenghao_id).first()
|
||||
if ch:
|
||||
DingdanBiaoqian.query.create(dingdan=dingdan, chenghao=ch)
|
||||
# 复用已查询的 ch 对象,避免重复查询
|
||||
if ch:
|
||||
DingdanBiaoqian.query.create(dingdan=dingdan, chenghao=ch)
|
||||
|
||||
UserShangjia.query.filter(id=shangjia.id).update(
|
||||
fabu=F('fabu') + 1,
|
||||
|
||||
@@ -128,8 +128,16 @@ class StaffInviteListView(APIView):
|
||||
qs = MerchantStaffInvite.query.filter(merchant_id=request.user.UserUID).select_related('role')
|
||||
if status_filter is not None:
|
||||
qs = qs.filter(status=int(status_filter))
|
||||
invites = list(qs.order_by('-CreateTime')[:200])
|
||||
# 批量预取已使用邀请码对应的用户,避免循环内 N+1
|
||||
from users.business_models import User
|
||||
_used_uids = [inv.used_by for inv in invites if inv.used_by]
|
||||
_user_map = {
|
||||
u.UserUID: u
|
||||
for u in User.query.filter(UserUID__in=_used_uids).only('UserUID', 'Nickname')
|
||||
} if _used_uids else {}
|
||||
items = []
|
||||
for inv in qs.order_by('-CreateTime')[:200]:
|
||||
for inv in invites:
|
||||
item = {
|
||||
'id': inv.id,
|
||||
'code': inv.invite_code,
|
||||
@@ -140,8 +148,7 @@ class StaffInviteListView(APIView):
|
||||
'user': None,
|
||||
}
|
||||
if inv.used_by:
|
||||
from users.business_models import User
|
||||
u = User.query.filter(UserUID=inv.used_by).first()
|
||||
u = _user_map.get(inv.used_by)
|
||||
item['user'] = {'nickname': getattr(u, 'Nickname', '') or inv.used_by, 'uid': inv.used_by}
|
||||
items.append(item)
|
||||
return Response({'code': 0, 'msg': '成功', 'data': {'list': items, 'gudingCode': 'XQKF2026'}})
|
||||
@@ -181,21 +188,51 @@ class StaffMemberListView(APIView):
|
||||
merchant_id=mid,
|
||||
status__in=[MEMBER_STATUS_ACTIVE, MEMBER_STATUS_DISABLED],
|
||||
).select_related('role')
|
||||
result = []
|
||||
for m in members:
|
||||
total_stats = MerchantStaffDailyStats.query.filter(member_id=m.id).aggregate(
|
||||
members_list = list(members)
|
||||
_member_ids = [m.id for m in members_list]
|
||||
_staff_uids = [m.staff_user_id for m in members_list if m.staff_user_id]
|
||||
|
||||
# 批量预取聚合统计,避免循环内 N+1
|
||||
_total_stats_map = {}
|
||||
_today_stat_map = {}
|
||||
if _member_ids:
|
||||
from django.db.models import Count
|
||||
for item in MerchantStaffDailyStats.query.filter(
|
||||
member_id__in=_member_ids
|
||||
).values('member_id').annotate(
|
||||
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,
|
||||
).first()
|
||||
wallet = get_or_create_wallet(m)
|
||||
u = User.query.filter(UserUID=m.staff_user_id).first()
|
||||
):
|
||||
_total_stats_map[item['member_id']] = item
|
||||
for s in MerchantStaffDailyStats.query.filter(
|
||||
member_id__in=_member_ids, stat_date=today,
|
||||
):
|
||||
_today_stat_map[s.member_id] = s
|
||||
|
||||
# 批量预取用户头像
|
||||
_user_map = {
|
||||
u.UserUID: u
|
||||
for u in User.query.filter(UserUID__in=_staff_uids).only('UserUID', 'Avatar')
|
||||
} if _staff_uids else {}
|
||||
|
||||
# 批量预取钱包
|
||||
_wallet_map = {
|
||||
w.member_id: w
|
||||
for w in MerchantStaffWallet.objects.filter(member_id__in=_member_ids)
|
||||
} if _member_ids else {}
|
||||
|
||||
result = []
|
||||
for m in members_list:
|
||||
total_stats = _total_stats_map.get(m.id, {})
|
||||
today_stat = _today_stat_map.get(m.id)
|
||||
wallet = _wallet_map.get(m.id)
|
||||
if wallet is None:
|
||||
wallet = get_or_create_wallet(m)
|
||||
u = _user_map.get(m.staff_user_id)
|
||||
result.append({
|
||||
'id': m.id,
|
||||
'uid': m.staff_user_id,
|
||||
@@ -210,13 +247,13 @@ class StaffMemberListView(APIView):
|
||||
'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,
|
||||
'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'),
|
||||
'dispatchCount': total_stats.get('dispatch_count') or 0,
|
||||
'dispatchAmount': str(total_stats.get('dispatch_amount') or '0.00'),
|
||||
'orderCount': total_stats.get('dispatch_count') or 0,
|
||||
'settleCount': total_stats.get('settle_count') or 0,
|
||||
'settleAmount': str(total_stats.get('settle_amount') or '0.00'),
|
||||
'refundCount': total_stats.get('refund_count') or 0,
|
||||
'refundAmount': str(total_stats.get('refund_amount') or '0.00'),
|
||||
'quota_available': str(wallet.quota_available),
|
||||
'quota_total': str(wallet.quota_total),
|
||||
'quota_used': str(wallet.quota_used),
|
||||
@@ -618,9 +655,15 @@ class StaffRankView(APIView):
|
||||
sort_field = sort_by if sort_by in ('dispatch_count', 'dispatch_amount', 'settle_count', 'settle_amount') else 'dispatch_count'
|
||||
agg = sorted(agg, key=lambda x: x[sort_field] or 0, reverse=True)[:50]
|
||||
|
||||
# 批量预取 member 信息,避免循环内 N+1
|
||||
_member_ids = [row['member_id'] for row in agg]
|
||||
_members_map = {
|
||||
m.id: m
|
||||
for m in MerchantStaffMember.query.filter(id__in=_member_ids).only('id', 'display_name')
|
||||
} if _member_ids else {}
|
||||
result = []
|
||||
for idx, row in enumerate(agg, 1):
|
||||
m = MerchantStaffMember.query.filter(id=row['member_id']).first()
|
||||
m = _members_map.get(row['member_id'])
|
||||
result.append({
|
||||
'rank': idx,
|
||||
'member_id': row['member_id'],
|
||||
|
||||
Reference in New Issue
Block a user