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:
@@ -1,4 +1,4 @@
|
||||
import io
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
@@ -394,40 +394,46 @@ class AdminUpdateConfigView(APIView):
|
||||
# 🔴 修复:更新QQ群配置 - 确保根据peizhiid正确更新对应的记录
|
||||
qq_groups = request.data.get('qq_groups')
|
||||
if qq_groups and isinstance(qq_groups, list):
|
||||
# 收集所有 peizhiid,批量预取已有记录(避免循环内 N+1)
|
||||
_peizhiids = [g.get('peizhiid') for g in qq_groups if g.get('peizhiid')]
|
||||
_existing_map = {}
|
||||
if _peizhiids:
|
||||
for q in Qunpeizhi.query.filter(GroupType__in=_peizhiids):
|
||||
# 如果存在多个相同 peizhiid 的记录,取第一个(应该只有一个)
|
||||
if q.GroupType not in _existing_map:
|
||||
_existing_map[q.GroupType] = q
|
||||
|
||||
_to_create = []
|
||||
_to_update = []
|
||||
updated_groups = []
|
||||
for group_data in qq_groups:
|
||||
peizhiid = group_data.get('peizhiid')
|
||||
if not peizhiid:
|
||||
continue
|
||||
|
||||
# 🔴 修复:使用filter()确保查询到正确的记录
|
||||
qun_configs = Qunpeizhi.query.filter(GroupType=peizhiid)
|
||||
|
||||
if qun_configs.exists():
|
||||
# 如果存在多个相同peizhiid的记录,取第一个(应该只有一个)
|
||||
qun_config = qun_configs.first()
|
||||
|
||||
# 更新字段
|
||||
neirong = group_data.get('neirong')
|
||||
qunid = group_data.get('qunid')
|
||||
jieshao = group_data.get('jieshao')
|
||||
neirong = group_data.get('neirong')
|
||||
qunid = group_data.get('qunid')
|
||||
jieshao = group_data.get('jieshao')
|
||||
|
||||
qun_config = _existing_map.get(peizhiid)
|
||||
if qun_config is not None:
|
||||
# 更新已有记录
|
||||
if neirong is not None:
|
||||
qun_config.GroupContent = neirong or ''
|
||||
if qunid is not None:
|
||||
qun_config.GroupID = qunid or ''
|
||||
if jieshao is not None:
|
||||
qun_config.Description = jieshao or ''
|
||||
|
||||
qun_config.save()
|
||||
_to_update.append(qun_config)
|
||||
else:
|
||||
# 如果不存在,创建新的(使用提供的peizhiid)
|
||||
qun_config = Qunpeizhi.query.create(
|
||||
# 不存在则创建新的
|
||||
qun_config = Qunpeizhi(
|
||||
GroupType=peizhiid,
|
||||
GroupContent=group_data.get('neirong', '') or '',
|
||||
GroupID=group_data.get('qunid', '') or '',
|
||||
Description=group_data.get('jieshao', '') or ''
|
||||
GroupContent=neirong or '',
|
||||
GroupID=qunid or '',
|
||||
Description=jieshao or '',
|
||||
)
|
||||
_to_create.append(qun_config)
|
||||
|
||||
updated_groups.append({
|
||||
'peizhiid': qun_config.GroupType,
|
||||
@@ -436,6 +442,13 @@ class AdminUpdateConfigView(APIView):
|
||||
'jieshao': qun_config.Description
|
||||
})
|
||||
|
||||
if _to_create:
|
||||
Qunpeizhi.objects.bulk_create(_to_create)
|
||||
if _to_update:
|
||||
Qunpeizhi.objects.bulk_update(
|
||||
_to_update, ['GroupContent', 'GroupID', 'Description']
|
||||
)
|
||||
|
||||
if updated_groups:
|
||||
response_data['qq_groups'] = updated_groups
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import io
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
@@ -270,6 +270,13 @@ class DashouPaihangView(APIView):
|
||||
# 第一名数量不足,不展示
|
||||
return [], False
|
||||
|
||||
# 批量预取老板扩展表昵称
|
||||
_boss_user_ids = [g.user_id for g in guanshi_list]
|
||||
_boss_nicheng_map = {
|
||||
b.user_id: b.nickname
|
||||
for b in UserBoss.query.filter(user_id__in=_boss_user_ids).only('user_id', 'nickname')
|
||||
}
|
||||
|
||||
# 构建返回数据
|
||||
paihang_list = []
|
||||
for index, guanshi in enumerate(guanshi_list):
|
||||
@@ -277,8 +284,7 @@ class DashouPaihangView(APIView):
|
||||
user_main = guanshi.user
|
||||
|
||||
# 管事的昵称需要查询老板扩展表
|
||||
boss_profile = UserBoss.query.filter(user=user_main).first()
|
||||
guanshi_nicheng = boss_profile.nickname if boss_profile else '未设置昵称'
|
||||
guanshi_nicheng = _boss_nicheng_map.get(guanshi.user_id, '未设置昵称')
|
||||
|
||||
# 获取头像(相对URL)
|
||||
touxiang_url = user_main.Avatar or ''
|
||||
@@ -337,6 +343,13 @@ class DashouPaihangView(APIView):
|
||||
# 第一名流水不足,不展示
|
||||
return [], False
|
||||
|
||||
# 批量预取老板扩展表昵称
|
||||
_boss_user_ids = [s.user_id for s in shangjia_list]
|
||||
_boss_nicheng_map = {
|
||||
b.user_id: b.nickname
|
||||
for b in UserBoss.query.filter(user_id__in=_boss_user_ids).only('user_id', 'nickname')
|
||||
}
|
||||
|
||||
# 构建返回数据
|
||||
paihang_list = []
|
||||
for index, shangjia in enumerate(shangjia_list):
|
||||
@@ -344,8 +357,7 @@ class DashouPaihangView(APIView):
|
||||
user_main = shangjia.user
|
||||
|
||||
# 商家的昵称需要查询老板扩展表
|
||||
boss_profile = UserBoss.query.filter(user=user_main).first()
|
||||
shangjia_nicheng = boss_profile.nickname if boss_profile else '未设置昵称'
|
||||
shangjia_nicheng = _boss_nicheng_map.get(shangjia.user_id, '未设置昵称')
|
||||
|
||||
# 获取头像(相对URL)
|
||||
touxiang_url = user_main.Avatar or ''
|
||||
@@ -604,21 +616,21 @@ class GetXiugaiJiluView(APIView):
|
||||
records = cursor.fetchall()
|
||||
|
||||
# 🔥 6. 处理查询结果(严格按照字段类型处理)
|
||||
# 预查询修改者账号信息(循环外执行一次,避免循环内重复查询同一 SQL)
|
||||
xiugaizhe_zhanghao = ''
|
||||
with connection.cursor() as cursor2:
|
||||
cursor2.execute(
|
||||
"SELECT phone, yonghuid FROM user_main WHERE phone = %s OR yonghuid = %s LIMIT 1",
|
||||
[xiugaizhe_zhanghaoid, xiugaizhe_zhanghaoid]
|
||||
)
|
||||
xiugaizhe_info = cursor2.fetchone()
|
||||
if xiugaizhe_info:
|
||||
xiugaizhe_zhanghao = str(xiugaizhe_info[0]) if xiugaizhe_info[0] else str(xiugaizhe_info[1]) if \
|
||||
xiugaizhe_info[1] else xiugaizhe_zhanghaoid
|
||||
|
||||
jilu_list = []
|
||||
for record in records:
|
||||
# 获取修改者账号信息(都是字符串)
|
||||
xiugaizhe_zhanghao = ''
|
||||
with connection.cursor() as cursor2:
|
||||
# 🔥 phone和yonghuid都是CharField,用字符串查询
|
||||
cursor2.execute(
|
||||
"SELECT phone, yonghuid FROM user_main WHERE phone = %s OR yonghuid = %s LIMIT 1",
|
||||
[xiugaizhe_zhanghaoid, xiugaizhe_zhanghaoid]
|
||||
)
|
||||
xiugaizhe_info = cursor2.fetchone()
|
||||
if xiugaizhe_info:
|
||||
# 🔥 都是字符串
|
||||
xiugaizhe_zhanghao = str(xiugaizhe_info[0]) if xiugaizhe_info[0] else str(xiugaizhe_info[1]) if \
|
||||
xiugaizhe_info[1] else xiugaizhe_zhanghaoid
|
||||
# 修改者账号信息已预查询,直接使用
|
||||
|
||||
# 获取会员名称(如果有)
|
||||
huiyuan_mingzi = ''
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import io
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
@@ -508,7 +508,7 @@ class KehuGetDingdanLianjieView(APIView):
|
||||
# 获取商家昵称
|
||||
shangjia_nicheng = ''
|
||||
try:
|
||||
user_main = User.query.get(UserUID=lianjie_obj.UserID)
|
||||
user_main = User.query.select_related('ShopProfile').get(UserUID=lianjie_obj.UserID)
|
||||
shangjia_obj = user_main.ShopProfile
|
||||
shangjia_nicheng = shangjia_obj.nicheng
|
||||
except (User.DoesNotExist, UserShangjia.DoesNotExist):
|
||||
@@ -816,7 +816,7 @@ class KehuTianxieDingdanView(APIView):
|
||||
# 验证打手是否存在且有效
|
||||
try:
|
||||
# 先查询用户主表
|
||||
dashou_user = User.query.get(UserUID=zhiding_dashou)
|
||||
dashou_user = User.query.select_related('DashouProfile').get(UserUID=zhiding_dashou)
|
||||
logger.info(f"找到打手用户 - 用户ID: {zhiding_dashou}")
|
||||
|
||||
# 通过反向关系获取打手扩展信息
|
||||
@@ -922,7 +922,7 @@ class KehuTianxieDingdanView(APIView):
|
||||
|
||||
# 获取商家信息用于返回
|
||||
try:
|
||||
user_main = User.query.get(UserUID=lianjie_obj.UserID)
|
||||
user_main = User.query.select_related('ShopProfile').get(UserUID=lianjie_obj.UserID)
|
||||
shangjia_obj = user_main.ShopProfile
|
||||
shangjia_mingcheng = shangjia_obj.nicheng
|
||||
except:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import io
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
@@ -142,17 +142,21 @@ class ShangjiaMobanListView(APIView):
|
||||
return Response({'code': 400, 'msg': '页码超出范围', 'data': {}}, status=400)
|
||||
|
||||
current_page = paginator.page(page)
|
||||
# 批量预取所有称号,避免循环内 N+1
|
||||
_mobans_list = list(current_page.object_list)
|
||||
_title_ids = [m.TitleID for m in _mobans_list if m.TitleID]
|
||||
_ch_map = {
|
||||
ch.id: ch
|
||||
for ch in Chenghao.query.filter(id__in=_title_ids).only('id', 'mingcheng', 'texiao_miaoshu')
|
||||
} if _title_ids else {}
|
||||
formatted = []
|
||||
for moban in current_page.object_list:
|
||||
for moban in _mobans_list:
|
||||
texiao_json = ''
|
||||
label_name = ''
|
||||
if moban.TitleID:
|
||||
try:
|
||||
ch = Chenghao.query.get(id=moban.TitleID)
|
||||
label_name = ch.mingcheng
|
||||
texiao_json = ch.texiao_miaoshu or ''
|
||||
except Chenghao.DoesNotExist:
|
||||
pass
|
||||
ch = _ch_map.get(moban.TitleID)
|
||||
if ch:
|
||||
label_name = ch.mingcheng
|
||||
texiao_json = ch.texiao_miaoshu or ''
|
||||
formatted.append({
|
||||
'mobanId': moban.id,
|
||||
'shangpinTypeId': moban.ProductTypeID,
|
||||
|
||||
Reference in New Issue
Block a user