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:
2026-07-05 23:17:38 +08:00
parent 76ea5f8255
commit f1c8633345
54 changed files with 5332 additions and 4494 deletions

View File

@@ -1,4 +1,4 @@
import hmac
import hmac
import threading
import traceback
import uuid
@@ -241,11 +241,16 @@ class GetProductTypeZoneView(APIView):
return Response({'code': 400, 'msg': scope_err})
# 查询所有商品类型(含专区数量)
# 一次性分组聚合所有类型的专区数量,避免循环内 N+1 count
# 注ShangpinZhuanqu.leixing_id 是 PositiveIntegerField非 FK无法用反向 annotate
zone_count_rows = ShangpinZhuanqu.query.values('leixing_id').annotate(cnt=Count('id'))
zone_count_map = {row['leixing_id']: row['cnt'] for row in zone_count_rows}
types = ShangpinLeixing.query.all().order_by('-paixu') # paixu 越大越靠前
type_list = []
for t in types:
# 统计该类型下的专区数量
zone_count = ShangpinZhuanqu.query.filter(leixing_id=t.id).count()
# 从预聚合的 map 中取专区数量
zone_count = zone_count_map.get(t.id, 0)
type_list.append({
'id': t.id,
'jieshao': t.jieshao or '',
@@ -531,10 +536,11 @@ class ModifyProductTypeZoneView(APIView):
# 删除该类型下的所有专区
zones = ShangpinZhuanqu.query.filter(leixing_id=type_id)
if delete_products:
# 删除专区下的所有商品
for zone in zones:
Shangpin.query.filter(zhuanqu_id=zone.id).delete()
zones.delete()
# 收集 zone_ids 后批量删除,避免循环内 N+1 delete
zone_ids = list(zones.values_list('id', flat=True))
if zone_ids:
Shangpin.query.filter(zhuanqu_id__in=zone_ids).delete()
ShangpinZhuanqu.query.filter(id__in=zone_ids).delete()
# 删除直接关联该类型的商品
Shangpin.query.filter(leixing_id=type_id).delete()
else:
@@ -928,7 +934,8 @@ class PopupNoticeModifyAPIView(APIView):
if cascade:
# 级联删除:使用事务保证原子性
with transaction.atomic():
popups = page.popups.all()
# 预加载 images 关联,避免循环内 N+1 访问 related manager
popups = page.popups.prefetch_related('images')
for popup in popups:
# 删除弹窗下的所有图片文件
for img in popup.images.all():