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
731 lines
28 KiB
Python
731 lines
28 KiB
Python
import io
|
||
import os
|
||
import re
|
||
import time
|
||
import random
|
||
import secrets
|
||
import hashlib
|
||
import threading
|
||
import urllib.parse
|
||
from decimal import Decimal, InvalidOperation
|
||
|
||
from django.conf import settings
|
||
from django.db import transaction, connection
|
||
from django.db.models import Q, F, Max, Prefetch
|
||
from gvsdsdk.fluent import db, func, FQ
|
||
from django.core.cache import cache
|
||
from django.core.paginator import Paginator
|
||
from django.utils import timezone
|
||
|
||
from rest_framework.views import APIView
|
||
from rest_framework.response import Response
|
||
from rest_framework import status, permissions
|
||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
|
||
from rest_framework.throttling import AnonRateThrottle, SimpleRateThrottle
|
||
|
||
from utils.oss_utils import upload_to_oss, delete_from_oss, validate_image
|
||
from utils.weixin_broadcast import WeixinBroadcastSender
|
||
from utils.weixin_token import get_weixin_mini_access_token, is_weixin_token_invalid
|
||
# from utils.ip_security import *
|
||
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
|
||
|
||
from backend.utils import update_shangjia_daily
|
||
from utils.chat_utils import subscribe_merchant_link_chat
|
||
from utils.pdd_order_validator import validate_pdd_order_id, validate_cn_mobile
|
||
|
||
from ..models import (
|
||
Gonggao, Lunbo, Tupianpeizhi, Qunpeizhi,
|
||
ShangjiaMoban, ShangjiaLianjie, PopupPage, PopupConfig, WithdrawConfig,
|
||
MiniappScriptScene, MiniappScriptAutoReply,
|
||
)
|
||
|
||
from users.models import (
|
||
AdminProfile, UserDashou, UserBoss,
|
||
UserShangjia, UserGuanshi, UserZuzhang
|
||
)
|
||
from users.business_models import User
|
||
|
||
from orders.models import CommissionRate, Order, MerchantOrderExt
|
||
from orders.notice_tasks import dingdan_guangbo
|
||
|
||
from rank.models import Chenghao, DingdanBiaoqian
|
||
|
||
from products.models import ShangpinLeixing, Huiyuangoumai
|
||
|
||
from ..serializers import PopupConfigSerializer
|
||
import traceback
|
||
import requests
|
||
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class DashouPaihangView(APIView):
|
||
"""
|
||
排行榜接口
|
||
路径:/peizhi/dsph
|
||
方法:POST
|
||
认证:JWT Token认证
|
||
|
||
请求参数:
|
||
- leixing: 1=今日收入,2=今月收入
|
||
- yonghu_leixing: 1=打手,2=管事,3=商家
|
||
|
||
返回数据:
|
||
- code: 0=成功,其他=失败
|
||
- msg: 提示信息
|
||
- data: {
|
||
xianshi: True/False, # 是否展示真实数据
|
||
paihang_list: [ # 排行榜列表
|
||
{
|
||
uid: '用户ID',
|
||
touxiang: '头像相对URL',
|
||
nicheng: '用户昵称',
|
||
jine: 收入金额,
|
||
mingci: 排名
|
||
},
|
||
...
|
||
]
|
||
}
|
||
"""
|
||
|
||
# =============== 可配置参数 ===============
|
||
# 每次查询返回的最大记录数(可修改)
|
||
ZUI_DAO_CHA_XUN_SHU_LIANG = 50
|
||
|
||
# 最少需要多少人才能展示(可修改)
|
||
ZUI_SHAO_REN_SHU_YAO_QIU = 1
|
||
|
||
# 第一名最低金额要求(单位:元)(可修改)
|
||
DI_YI_MING_ZUI_DI_JIN_E = Decimal('10.00')
|
||
|
||
# 管事第一名最低数量要求(可修改)
|
||
DI_YI_MING_ZUI_DI_SHU_LIANG = 1
|
||
# =============== 可配置参数结束 ===============
|
||
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 获取请求参数 - 修复参数类型问题
|
||
leixing_str = request.data.get('leixing', '1')
|
||
yonghu_leixing_str = request.data.get('yonghu_leixing', '1')
|
||
|
||
# 转换为整数
|
||
try:
|
||
leixing = int(leixing_str)
|
||
except (ValueError, TypeError):
|
||
leixing = 1
|
||
|
||
try:
|
||
yonghu_leixing = int(yonghu_leixing_str)
|
||
except (ValueError, TypeError):
|
||
yonghu_leixing = 1
|
||
|
||
# 参数验证
|
||
if leixing not in [1, 2]:
|
||
return Response({
|
||
'code': 400,
|
||
'msg': '参数错误:leixing必须为1(今日)或2(今月)',
|
||
'data': {'xianshi': False}
|
||
})
|
||
|
||
if yonghu_leixing not in [1, 2, 3]:
|
||
return Response({
|
||
'code': 400,
|
||
'msg': '参数错误:yonghu_leixing必须为1(打手)、2(管事)或3(商家)',
|
||
'data': {'xianshi': False}
|
||
})
|
||
|
||
# 根据用户类型调用不同的查询方法
|
||
if yonghu_leixing == 1:
|
||
# 打手排行榜
|
||
paihang_list, xianshi_flag = self.chaxun_dashou_paihang(leixing)
|
||
elif yonghu_leixing == 2:
|
||
# 管事排行榜
|
||
paihang_list, xianshi_flag = self.chaxun_guanshi_paihang(leixing)
|
||
else:
|
||
# 商家排行榜
|
||
paihang_list, xianshi_flag = self.chaxun_shangjia_paihang(leixing)
|
||
|
||
# 返回结果
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '获取成功',
|
||
'data': {
|
||
'xianshi': xianshi_flag,
|
||
'paihang_list': paihang_list
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"排行榜查询失败: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 500,
|
||
'msg': '服务器内部错误',
|
||
'data': {'xianshi': False}
|
||
})
|
||
|
||
def chaxun_dashou_paihang(self, leixing):
|
||
"""
|
||
查询打手排行榜
|
||
:param leixing: 1=今日收入,2=今月收入
|
||
:return: (排行榜列表, 是否展示标志)
|
||
"""
|
||
# 确定查询的字段
|
||
if leixing == 1:
|
||
# 今日收入
|
||
shouyi_ziduan = 'jinrishouyi'
|
||
else:
|
||
# 今月收入
|
||
shouyi_ziduan = 'jinyueshouyi'
|
||
|
||
# 查询条件:收入大于0,打手账号状态正常(1=正常)
|
||
# 注意:打手表中的zhanghaozhuangtai字段
|
||
chaxun_tiaojian = Q(**{f'{shouyi_ziduan}__gt': 0}) & Q(zhanghaozhuangtai=1)
|
||
|
||
# 查询打手扩展表,并关联用户主表
|
||
dashou_list = UserDashou.query.select_related('user').filter(
|
||
chaxun_tiaojian
|
||
).order_by(f'-{shouyi_ziduan}')[:self.ZUI_DAO_CHA_XUN_SHU_LIANG]
|
||
|
||
# 检查是否满足展示条件
|
||
if len(dashou_list) < self.ZUI_SHAO_REN_SHU_YAO_QIU:
|
||
# 人数不足,不展示
|
||
return [], False
|
||
|
||
# 检查第一名金额是否达到最低要求
|
||
if leixing == 1:
|
||
di_yi_ming_jine = dashou_list[0].jinrishouyi
|
||
else:
|
||
di_yi_ming_jine = dashou_list[0].jinyueshouyi
|
||
|
||
if di_yi_ming_jine < self.DI_YI_MING_ZUI_DI_JIN_E:
|
||
# 第一名金额不足,不展示
|
||
return [], False
|
||
|
||
# 构建返回数据
|
||
paihang_list = []
|
||
for index, dashou in enumerate(dashou_list):
|
||
# 获取用户主表信息
|
||
user_main = dashou.user
|
||
|
||
# 打手的昵称直接使用打手扩展表的nicheng字段
|
||
dashou_nicheng = dashou.nicheng or '未设置昵称'
|
||
|
||
# 获取头像(相对URL)
|
||
touxiang_url = user_main.Avatar or ''
|
||
|
||
# 获取收入金额
|
||
if leixing == 1:
|
||
shouyi_jine = dashou.jinrishouyi
|
||
else:
|
||
shouyi_jine = dashou.jinyueshouyi
|
||
|
||
paihang_list.append({
|
||
'uid': user_main.UserUID,
|
||
'touxiang': touxiang_url,
|
||
'nicheng': dashou_nicheng,
|
||
'jine': float(shouyi_jine), # 转换为float方便前端处理
|
||
'mingci': index + 1
|
||
})
|
||
|
||
return paihang_list, True
|
||
|
||
def chaxun_guanshi_paihang(self, leixing):
|
||
"""
|
||
查询管事排行榜
|
||
:param leixing: 1=今日,2=今月
|
||
:return: (排行榜列表, 是否展示标志)
|
||
"""
|
||
# 确定查询的字段
|
||
if leixing == 1:
|
||
# 今日充值打手数量
|
||
chaxun_ziduan = 'jinrichongzhi'
|
||
else:
|
||
# 今月充值打手数量
|
||
chaxun_ziduan = 'jinyuechongzhi'
|
||
|
||
# 查询条件:数量大于0,管事状态正常(状态1为正常)
|
||
# 注意:管手表中的zhuangtai字段
|
||
chaxun_tiaojian = Q(**{f'{chaxun_ziduan}__gt': 0}) & Q(zhuangtai=1)
|
||
|
||
# 查询管事扩展表,并关联用户主表
|
||
guanshi_list = UserGuanshi.query.select_related('user').filter(
|
||
chaxun_tiaojian
|
||
).order_by(f'-{chaxun_ziduan}')[:self.ZUI_DAO_CHA_XUN_SHU_LIANG]
|
||
|
||
# 检查是否满足展示条件
|
||
if len(guanshi_list) < self.ZUI_SHAO_REN_SHU_YAO_QIU:
|
||
# 人数不足,不展示
|
||
return [], False
|
||
|
||
# 检查第一名数量是否达到最低要求
|
||
if leixing == 1:
|
||
di_yi_ming_shuliang = guanshi_list[0].jinrichongzhi
|
||
else:
|
||
di_yi_ming_shuliang = guanshi_list[0].jinyuechongzhi
|
||
|
||
if di_yi_ming_shuliang < self.DI_YI_MING_ZUI_DI_SHU_LIANG:
|
||
# 第一名数量不足,不展示
|
||
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):
|
||
# 获取用户主表信息
|
||
user_main = guanshi.user
|
||
|
||
# 管事的昵称需要查询老板扩展表
|
||
guanshi_nicheng = _boss_nicheng_map.get(guanshi.user_id, '未设置昵称')
|
||
|
||
# 获取头像(相对URL)
|
||
touxiang_url = user_main.Avatar or ''
|
||
|
||
# 获取数量
|
||
if leixing == 1:
|
||
chongzhi_shuliang = guanshi.jinrichongzhi
|
||
else:
|
||
chongzhi_shuliang = guanshi.jinyuechongzhi
|
||
|
||
paihang_list.append({
|
||
'uid': user_main.UserUID,
|
||
'touxiang': touxiang_url,
|
||
'nicheng': guanshi_nicheng,
|
||
'jine': float(chongzhi_shuliang), # 这里jine字段实际存储的是数量
|
||
'mingci': index + 1
|
||
})
|
||
|
||
return paihang_list, True
|
||
|
||
def chaxun_shangjia_paihang(self, leixing):
|
||
"""
|
||
查询商家排行榜
|
||
:param leixing: 1=今日流水,2=今月流水
|
||
:return: (排行榜列表, 是否展示标志)
|
||
"""
|
||
# 确定查询的字段
|
||
if leixing == 1:
|
||
# 今日流水
|
||
liushui_ziduan = 'jinriliushui'
|
||
else:
|
||
# 今月流水
|
||
liushui_ziduan = 'jinyueliushui'
|
||
|
||
# 查询条件:流水大于0,商家状态正常(状态1为正常)
|
||
# 注意:商家表中的zhuangtai字段
|
||
chaxun_tiaojian = Q(**{f'{liushui_ziduan}__gt': 0}) & Q(zhuangtai=1)
|
||
|
||
# 查询商家扩展表,并关联用户主表
|
||
shangjia_list = UserShangjia.query.select_related('user').filter(
|
||
chaxun_tiaojian
|
||
).order_by(f'-{liushui_ziduan}')[:self.ZUI_DAO_CHA_XUN_SHU_LIANG]
|
||
|
||
# 检查是否满足展示条件
|
||
if len(shangjia_list) < self.ZUI_SHAO_REN_SHU_YAO_QIU:
|
||
# 人数不足,不展示
|
||
return [], False
|
||
|
||
# 检查第一名金额是否达到最低要求
|
||
if leixing == 1:
|
||
di_yi_ming_liushui = shangjia_list[0].jinriliushui
|
||
else:
|
||
di_yi_ming_liushui = shangjia_list[0].jinyueliushui
|
||
|
||
if di_yi_ming_liushui < self.DI_YI_MING_ZUI_DI_JIN_E:
|
||
# 第一名流水不足,不展示
|
||
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):
|
||
# 获取用户主表信息
|
||
user_main = shangjia.user
|
||
|
||
# 商家的昵称需要查询老板扩展表
|
||
shangjia_nicheng = _boss_nicheng_map.get(shangjia.user_id, '未设置昵称')
|
||
|
||
# 获取头像(相对URL)
|
||
touxiang_url = user_main.Avatar or ''
|
||
|
||
# 获取流水金额
|
||
if leixing == 1:
|
||
liushui_jine = shangjia.jinriliushui
|
||
else:
|
||
liushui_jine = shangjia.jinyueliushui
|
||
|
||
paihang_list.append({
|
||
'uid': user_main.UserUID,
|
||
'touxiang': touxiang_url,
|
||
'nicheng': shangjia_nicheng,
|
||
'jine': float(liushui_jine),
|
||
'mingci': index + 1
|
||
})
|
||
|
||
return paihang_list, True
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
class GetAdminZhanghaoView(APIView):
|
||
"""
|
||
获取管理员账号列表接口
|
||
路径:/peizhi/adzhhq
|
||
方法:POST
|
||
权限:JWT Token + 管理员验证
|
||
参数:
|
||
- zhanghao: 当前登录的管理员账号(字符串)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 🔥 1. 身份验证(100%按表模型)
|
||
user = request.user
|
||
|
||
# 获取当前用户的手机号(CharField,字符串类型)
|
||
current_phone = getattr(user, 'Phone', '')
|
||
if not current_phone:
|
||
return Response({
|
||
'code': 401,
|
||
'message': '用户手机号信息不完整',
|
||
'data': None
|
||
}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 检查前端传递的账号是否匹配(都是字符串)
|
||
frontend_zhanghao = request.data.get('zhanghao', '').strip()
|
||
if frontend_zhanghao != current_phone:
|
||
return Response({
|
||
'code': 403,
|
||
'message': '账号不匹配,无权限访问',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 🔥 2. 验证是否为管理员(user_type是CharField,值是'admin'字符串)
|
||
user_type = getattr(user, 'user_type', '')
|
||
if user_type != 'admin':
|
||
# 检查是否有管理员扩展表
|
||
if not hasattr(user, 'AdminProfile'):
|
||
return Response({
|
||
'code': 403,
|
||
'message': '无管理员权限',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 🔥 3. 查询管理员账号列表(全部按字符串查询)
|
||
with connection.cursor() as cursor:
|
||
# 🔥 查询所有管理员账号(user_type='admin' 字符串)
|
||
# 🔥 phone和yonghuid都是CharField,xiugaiid也是CharField
|
||
sql = """
|
||
SELECT
|
||
um.id,
|
||
um.phone,
|
||
um.yonghuid,
|
||
um.UserType,
|
||
COALESCE(COUNT(xjl.id), 0) as jilushuliang
|
||
FROM user_main um
|
||
LEFT JOIN xiugaijilu xjl ON um.phone = xjl.xiugaiid
|
||
WHERE um.UserType = 'admin'
|
||
GROUP BY um.id, um.phone, um.yonghuid, um.UserType
|
||
ORDER BY um.CreateTime DESC
|
||
"""
|
||
|
||
cursor.execute(sql)
|
||
results = cursor.fetchall()
|
||
|
||
# 🔥 4. 处理查询结果(全部保持字符串类型)
|
||
zhanghao_list = []
|
||
for row in results:
|
||
zhanghao_list.append({
|
||
#'id': row[0],
|
||
'id': str(row[1]) if row[1] else str(row[2]), # 🔥 保持字符串
|
||
'zhanghao':str(row[1]) if row[1] else str(row[2]), # 🔥 保持字符串
|
||
'yonghuid': str(row[2]) if row[2] else '', # 🔥 保持字符串
|
||
'user_type': str(row[3]) if row[3] else '', # 🔥 保持字符串
|
||
'jilushuliang': int(row[4]) # 只有这个是数字
|
||
})
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'message': '获取成功',
|
||
'data': zhanghao_list
|
||
}, status=status.HTTP_200_OK)
|
||
|
||
except Exception as e:
|
||
print(f"获取管理员账号列表失败: {str(e)}")
|
||
traceback.print_exc()
|
||
return Response({
|
||
'code': 500,
|
||
'message': '系统错误',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
|
||
class GetXiugaiJiluView(APIView):
|
||
"""
|
||
获取修改记录列表接口
|
||
路径:/peizhi/adhqxg
|
||
方法:POST
|
||
权限:JWT Token + 管理员验证
|
||
参数:
|
||
- zhanghao: 当前登录的管理员账号(字符串)
|
||
- xiugaizhe_zhanghaoid: 选中的管理员账号ID(字符串,对应phone或yonghuid)
|
||
- page: 页码(整数)
|
||
- pagesize: 每页条数(整数)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 🔥 1. 身份验证(100%按表模型)
|
||
user = request.user
|
||
|
||
# 获取当前用户的手机号(CharField,字符串)
|
||
current_phone = getattr(user, 'Phone', '')
|
||
if not current_phone:
|
||
return Response({
|
||
'code': 401,
|
||
'message': '用户手机号信息不完整',
|
||
'data': None
|
||
}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
# 检查前端传递的账号是否匹配(字符串比较)
|
||
frontend_zhanghao = request.data.get('zhanghao', '')
|
||
if not isinstance(frontend_zhanghao, str):
|
||
frontend_zhanghao = str(frontend_zhanghao)
|
||
frontend_zhanghao = frontend_zhanghao.strip()
|
||
|
||
if frontend_zhanghao != current_phone:
|
||
return Response({
|
||
'code': 403,
|
||
'message': '账号不匹配,无权限访问',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 🔥 2. 验证是否为管理员(user_type='admin'字符串)
|
||
user_type = getattr(user, 'user_type', '')
|
||
if user_type != 'admin':
|
||
if not hasattr(user, 'AdminProfile'):
|
||
return Response({
|
||
'code': 403,
|
||
'message': '无管理员权限',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 🔥 3. 获取参数(100%按实际类型)
|
||
# 🔥 xiugaizhe_zhanghaoid 是字符串(对应xiugaiid字段,CharField)
|
||
xiugaizhe_zhanghaoid_param = request.data.get('xiugaizhe_zhanghaoid', '')
|
||
|
||
|
||
# 🔥 强制转为字符串(因为数据库是CharField)
|
||
if xiugaizhe_zhanghaoid_param is None:
|
||
xiugaizhe_zhanghaoid = ''
|
||
else:
|
||
# 无论如何都转为字符串
|
||
xiugaizhe_zhanghaoid = str(xiugaizhe_zhanghaoid_param).strip()
|
||
|
||
|
||
# 🔥 分页参数转为整数
|
||
try:
|
||
page = int(request.data.get('page', 1))
|
||
except:
|
||
page = 1
|
||
|
||
try:
|
||
pagesize = int(request.data.get('pagesize', 10))
|
||
except:
|
||
pagesize = 10
|
||
|
||
# 🔥 4. 参数验证
|
||
if not xiugaizhe_zhanghaoid:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '修改者账号ID不能为空',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
if page < 1:
|
||
page = 1
|
||
if pagesize < 1 or pagesize > 50:
|
||
pagesize = 10
|
||
|
||
# 🔥 5. 高效查询(100%按表字段类型)
|
||
with connection.cursor() as cursor:
|
||
# 🔥 5.1 查询总记录数(xiugaiid是CharField,用字符串查询)
|
||
count_sql = """
|
||
SELECT COUNT(*)
|
||
FROM xiugaijilu
|
||
WHERE xiugaiid = %s
|
||
"""
|
||
cursor.execute(count_sql, [xiugaizhe_zhanghaoid])
|
||
total_count = cursor.fetchone()[0]
|
||
|
||
# 🔥 5.2 计算分页
|
||
offset = (page - 1) * pagesize
|
||
|
||
# 🔥 5.3 查询当前页数据(使用参数化查询,防止SQL注入)
|
||
data_sql = """
|
||
SELECT
|
||
xjl.id,
|
||
xjl.yonghuid, -- CharField
|
||
xjl.xiugaiid, -- CharField
|
||
xjl.leixing, -- IntegerField
|
||
xjl.xiugaitijiao, -- DecimalField
|
||
xjl.xiugaitijiaoq, -- DecimalField
|
||
xjl.jifen, -- IntegerField
|
||
xjl.yjifen, -- IntegerField
|
||
xjl.yajin, -- DecimalField
|
||
xjl.yyajin, -- DecimalField
|
||
xjl.shangjiayue, -- DecimalField
|
||
xjl.shangjiayueq, -- DecimalField
|
||
xjl.guanshiyue, -- DecimalField
|
||
xjl.guanshiyueq, -- DecimalField
|
||
xjl.huiyuants, -- IntegerField
|
||
xjl.huiyuan_id, -- CharField
|
||
xjl.qitashuoming, -- TextField
|
||
xjl.CreateTime, -- DateTimeField
|
||
um.avatar -- CharField
|
||
FROM xiugaijilu xjl
|
||
LEFT JOIN user_main um ON xjl.yonghuid = um.yonghuid
|
||
WHERE xjl.xiugaiid = %s
|
||
ORDER BY xjl.CreateTime DESC
|
||
LIMIT %s OFFSET %s
|
||
"""
|
||
|
||
cursor.execute(data_sql, [xiugaizhe_zhanghaoid, pagesize, offset])
|
||
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:
|
||
# 修改者账号信息已预查询,直接使用
|
||
|
||
# 获取会员名称(如果有)
|
||
huiyuan_mingzi = ''
|
||
if record[15]: # huiyuan_id是CharField
|
||
with connection.cursor() as cursor3:
|
||
cursor3.execute(
|
||
"SELECT jieshao FROM huiyuan WHERE huiyuan_id = %s LIMIT 1",
|
||
[str(record[15])] # 🔥 转为字符串查询
|
||
)
|
||
huiyuan_info = cursor3.fetchone()
|
||
if huiyuan_info:
|
||
huiyuan_mingzi = str(huiyuan_info[0]) if huiyuan_info[0] else ''
|
||
|
||
# 🔥 严格按照前端字段名映射,保持正确类型
|
||
jilu_data = {
|
||
# 基础字段(字符串)
|
||
'id': record[0],
|
||
'beixiugai_yonghuid': str(record[1]) if record[1] else '', # yonghuid
|
||
'xiugaizhe_id': str(record[2]) if record[2] else '', # xiugaiid
|
||
'xiugaizhe_zhanghao': xiugaizhe_zhanghao, # 字符串
|
||
'beixiugai_yonghuleixing': record[3], # leixing是整数
|
||
|
||
# 打手字段(Decimal转float,Integer转int)
|
||
'xiugaihou_dashoutixianjine': float(record[4]) if record[4] is not None else 0.00,
|
||
'xiugaiqian_dashoutixianjine': float(record[5]) if record[5] is not None else 0.00,
|
||
'xiugaihou_dashoujifen': int(record[6]) if record[6] is not None else 0,
|
||
'xiugaiqian_dashoujifen': int(record[7]) if record[7] is not None else 0,
|
||
'xiugaihou_dashouyajin': float(record[8]) if record[8] is not None else 0.00,
|
||
'xiugaiqian_dashouyajin': float(record[9]) if record[9] is not None else 0.00,
|
||
|
||
# 商家字段
|
||
'xiugaihou_shangjiayue': float(record[10]) if record[10] is not None else 0.00,
|
||
'xiugaiqian_shangjiayue': float(record[11]) if record[11] is not None else 0.00,
|
||
|
||
# 管事字段
|
||
'xiugaihou_guanshiyue': float(record[12]) if record[12] is not None else 0.00,
|
||
'xiugaiqian_guanshiyue': float(record[13]) if record[13] is not None else 0.00,
|
||
|
||
# 会员字段
|
||
'tianjia_huiyuantianshu': int(record[14]) if record[14] is not None else 0,
|
||
'huiyuan_id': str(record[15]) if record[15] else '', # 字符串
|
||
'huiyuan_mingzi': huiyuan_mingzi, # 字符串
|
||
|
||
# 其他字段
|
||
'qita_shuoming': str(record[16]) if record[16] else '', # 字符串
|
||
'xiugai_time': record[17].strftime('%Y-%m-%d %H:%M:%S') if record[17] else None,
|
||
'touxiang': str(record[18]) if record[18] else '' # avatar是字符串
|
||
}
|
||
|
||
jilu_list.append(jilu_data)
|
||
|
||
# 🔥 7. 分页控制
|
||
queried_records = len(jilu_list)
|
||
has_more = False
|
||
|
||
if queried_records == pagesize:
|
||
total_queried = (page - 1) * pagesize + queried_records
|
||
has_more = total_queried < total_count
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'message': '获取成功',
|
||
'data': {
|
||
'list': jilu_list,
|
||
'zongshuliang': total_count,
|
||
'dangqian_page': page,
|
||
'page_size': pagesize,
|
||
'has_more': has_more,
|
||
'shijichaxundediaoshuliang': queried_records
|
||
}
|
||
}, status=status.HTTP_200_OK)
|
||
|
||
except ValueError as e:
|
||
print(f"参数错误: {str(e)}")
|
||
return Response({
|
||
'code': 400,
|
||
'message': '参数错误',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
except Exception as e:
|
||
print(f"获取修改记录失败: {str(e)}")
|
||
traceback.print_exc()
|
||
return Response({
|
||
'code': 500,
|
||
'message': '系统错误',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|