feat: 更换邀请人、操作日志与组长列表搜索修复

复用 xiugaijilu 为后台关键改动补全详细日志,新增更换打手/管事邀请人接口与操作日志页面,并修复组长列表 BossProfile 关联名导致的搜索 500。
This commit is contained in:
XingQue
2026-06-21 12:12:08 +08:00
parent 24cd2ccea3
commit a1d5a2c3a3
3 changed files with 534 additions and 38 deletions

View File

@@ -6,8 +6,8 @@ from .view import (GetRolePermissionView,
ModifyAdminUserView, KefuGetDashouListView, KefuGetDashouDetailView, KefuUpdateDashouView, KefuGetShangjiaListView,
KefuGetShangjiaDetailView, KefuUpdateShangjiaView, GetProductBaseDataView, GetProductListView, SaveProductOrderView,
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView, UpdateMemberView, GetMemberListView,
AddMemberView, GetGuanliListView, GetGuanliDetailView, PromoteGuanshiToZuzhangView, UpdateGuanliView, GetWithdrawSettingsView,
UpdateWithdrawSettingsView, GetZuzhangListView, GetZuzhangDetailView, UpdateZuzhangView, GetProductTypeZoneView,
AddMemberView, GetGuanliListView, GetGuanliDetailView, PromoteGuanshiToZuzhangView, ChangeInviterView, UpdateGuanliView, GetWithdrawSettingsView,
UpdateWithdrawSettingsView, GetZuzhangListView, GetZuzhangDetailView, UpdateZuzhangView, GetOperationLogListView, GetProductTypeZoneView,
ModifyProductTypeZoneView, GetRateView, ModifyRateView, PopupNoticeListAPIView, PopupNoticeModifyAPIView,ShopListView,
ShopModifyView, ShopPublicTypeAndAuditView, ShopListForProductView, ShopProductModifyView,
ShopProductTypeMappingView, ProductListView, ProductModifyView, KefuChatPermissionsView,
@@ -47,7 +47,9 @@ urlpatterns = [
path('hthqgsxq', GetGuanliDetailView.as_view(), name='后台获取管事详情信息'),
path('htgssjzz', PromoteGuanshiToZuzhangView.as_view(), name='后台管事升级组长'),
path('htghyqr', ChangeInviterView.as_view(), name='后台更换邀请人'),
path('htxggsxx', UpdateGuanliView.as_view(), name='后台更新管事数据'),
path('hqczrz', GetOperationLogListView.as_view(), name='后台操作日志列表'),
path('hthqtxpz', GetWithdrawSettingsView.as_view(), name='后台获取提现配置'),
path('htxgtxsz', UpdateWithdrawSettingsView.as_view(), name='后台修改提现配置'),
path('hthqzzlb', GetZuzhangListView.as_view(), name='后台获取组长列表'),

View File

@@ -531,4 +531,39 @@ def pick_penalty_create_params(data):
affects = data.get('AffectsGrabbing')
if affects is None:
affects = data.get('yingxiang_qiangdan', 1)
return penalized_id, reason, fine_amount, affects
return penalized_id, reason, fine_amount, affects
# ---------- 修改记录Xiugaijilu ----------
XIUGAI_LEIXING_DASHOU = 2
XIUGAI_LEIXING_GUANSHI = 3
XIUGAI_LEIXING_SHANGJIA = 4
XIUGAI_LEIXING_ZUZHANG = 5
XIUGAI_LEIXING_LABEL = {
XIUGAI_LEIXING_DASHOU: '打手',
XIUGAI_LEIXING_GUANSHI: '管事',
XIUGAI_LEIXING_SHANGJIA: '商家',
XIUGAI_LEIXING_ZUZHANG: '组长',
}
def write_xiugai_log(*, yonghuid, xiugaiid, leixing, qitashuoming, **extra_fields):
"""
统一写入 xiugaijilu 修改记录。
qitashuoming 应详细描述本次操作(尤其是金额、身份、上下级变更等)。
extra_fields 可传入 xiugaitijiao、shangjiayue、guanshiyue 等模型字段。
"""
from users.models import Xiugaijilu
if not qitashuoming:
label = XIUGAI_LEIXING_LABEL.get(leixing, f'类型{leixing}')
qitashuoming = f'【后台】修改{label}信息,操作人{xiugaiid}'
Xiugaijilu.query.create(
yonghuid=str(yonghuid),
xiugaiid=str(xiugaiid),
leixing=leixing,
qitashuoming=qitashuoming,
**extra_fields,
)

View File

@@ -50,6 +50,12 @@ from .utils import (
check_fadan_permission,
pick_order_id,
pick_penalty_create_params,
write_xiugai_log,
XIUGAI_LEIXING_DASHOU,
XIUGAI_LEIXING_GUANSHI,
XIUGAI_LEIXING_SHANGJIA,
XIUGAI_LEIXING_ZUZHANG,
XIUGAI_LEIXING_LABEL,
)
# models 集中导入
@@ -1004,12 +1010,16 @@ class KefuUpdateDashouView(APIView):
dashou_profile.yue -= amount
dashou_profile.save()
after = dashou_profile.yue
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
xiugaitijiao=after,
xiugaitijiaoq=before['yue'],
qitashuoming=(
f'【后台】减少打手可提现余额:{before["yue"]}元 → {after}元,'
f'减少{amount}打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '减余额成功', 'data': {'new_yue': str(after)}})
@@ -1020,12 +1030,16 @@ class KefuUpdateDashouView(APIView):
dashou_profile.yue += amount
dashou_profile.save()
after = dashou_profile.yue
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
xiugaitijiao=after,
xiugaitijiaoq=before['yue'],
qitashuoming=(
f'【后台】增加打手可提现余额:{before["yue"]}元 → {after}元,'
f'增加{amount}打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '加余额成功', 'data': {'new_yue': str(after)}})
@@ -1039,12 +1053,16 @@ class KefuUpdateDashouView(APIView):
dashou_profile.yajin -= amount
dashou_profile.save()
after = dashou_profile.yajin
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
yajin=after,
yyajin=before['yajin'],
qitashuoming=(
f'【后台】减少打手押金:{before["yajin"]}元 → {after}元,'
f'减少{amount}打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '减押金成功', 'data': {'new_yajin': str(after)}})
@@ -1055,12 +1073,16 @@ class KefuUpdateDashouView(APIView):
dashou_profile.yajin += amount
dashou_profile.save()
after = dashou_profile.yajin
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
yajin=after,
yyajin=before['yajin'],
qitashuoming=(
f'【后台】增加打手押金:{before["yajin"]}元 → {after}元,'
f'增加{amount}打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '加押金成功', 'data': {'new_yajin': str(after)}})
@@ -1074,12 +1096,16 @@ class KefuUpdateDashouView(APIView):
dashou_profile.jifen -= jifen
dashou_profile.save()
after = dashou_profile.jifen
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
jifen=after,
yjifen=before['jifen'],
qitashuoming=(
f'【后台】减少打手积分:{before["jifen"]}{after}'
f'减少{jifen}打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '减积分成功', 'data': {'new_jifen': after}})
@@ -1090,12 +1116,16 @@ class KefuUpdateDashouView(APIView):
dashou_profile.jifen += jifen
dashou_profile.save()
after = dashou_profile.jifen
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
jifen=after,
yjifen=before['jifen'],
qitashuoming=(
f'【后台】增加打手积分:{before["jifen"]}{after}'
f'增加{jifen}打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '加积分成功', 'data': {'new_jifen': after}})
@@ -1108,11 +1138,16 @@ class KefuUpdateDashouView(APIView):
return Response({'code': 400, 'msg': '状态值无效0=封禁,1=正常)'})
dashou_profile.zhanghaozhuangtai = new_status
dashou_profile.save()
Xiugaijilu.query.create(
status_label = '正常' if new_status == 1 else '封禁'
old_label = '正常' if before['zhanghaozhuangtai'] == 1 else '封禁'
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改账号状态为{new_status}',
leixing=XIUGAI_LEIXING_DASHOU,
qitashuoming=(
f'【后台】修改打手账号状态:{old_label}{status_label}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '修改账号状态成功', 'data': {'new_zhanghaozhuangtai': new_status}})
@@ -1124,11 +1159,16 @@ class KefuUpdateDashouView(APIView):
return Response({'code': 400, 'msg': '状态值无效0=离线,1=在线)'})
dashou_profile.zaixianzhuangtai = new_status
dashou_profile.save()
Xiugaijilu.query.create(
status_label = '在线' if new_status == 1 else '离线'
old_label = '在线' if before['zaixianzhuangtai'] == 1 else '离线'
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改在线状态为{new_status}',
leixing=XIUGAI_LEIXING_DASHOU,
qitashuoming=(
f'【后台】修改打手在线状态:{old_label}{status_label}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '修改在线状态成功', 'data': {'new_zaixianzhuangtai': new_status}})
@@ -1140,11 +1180,16 @@ class KefuUpdateDashouView(APIView):
return Response({'code': 400, 'msg': '状态值无效0=忙碌,1=空闲)'})
dashou_profile.zhuangtai = new_status
dashou_profile.save()
Xiugaijilu.query.create(
status_label = '空闲' if new_status == 1 else '忙碌'
old_label = '空闲' if before['zhuangtai'] == 1 else '忙碌'
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改接单状态为{new_status}',
leixing=XIUGAI_LEIXING_DASHOU,
qitashuoming=(
f'【后台】修改打手接单状态:{old_label}{status_label}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '修改接单状态成功', 'data': {'new_zhuangtai': new_status}})
@@ -1173,13 +1218,17 @@ class KefuUpdateDashouView(APIView):
record.daoqi_time = new_daoqi
record.huiyuan_zhuangtai = 1
record.save()
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
huiyuants=days,
huiyuan_id=huiyuan_id,
qitashuoming=f'加会员 {huiyuan_id},增加{days}',
qitashuoming=(
f'【后台】给打手添加会员会员ID={huiyuan_id}{huiyuan.jieshao}'
f'增加{days}天,到期时间={record.daoqi_time.strftime("%Y-%m-%d %H:%M:%S")}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '加会员成功', 'data': {'new_daoqi': record.daoqi_time.strftime('%Y-%m-%d %H:%M:%S')}})
@@ -1193,12 +1242,15 @@ class KefuUpdateDashouView(APIView):
except Huiyuangoumai.DoesNotExist:
return Response({'code': 404, 'msg': '该打手未购买此会员'})
record.delete()
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
leixing=XIUGAI_LEIXING_DASHOU,
huiyuan_id=huiyuan_id,
qitashuoming=f'移除会员 {huiyuan_id}',
qitashuoming=(
f'【后台】移除打手会员会员ID={huiyuan_id}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '减会员成功'})
@@ -1212,11 +1264,14 @@ class KefuUpdateDashouView(APIView):
# 可限制合法值,这里只记录日志
dashou_profile.chenghao = new_chenghao
dashou_profile.save()
Xiugaijilu.query.create(
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
leixing=2,
qitashuoming=f'修改称号:{before["chenghao"] or ""}{new_chenghao}',
leixing=XIUGAI_LEIXING_DASHOU,
qitashuoming=(
f'【后台】修改打手称号:{before["chenghao"] or ""}{new_chenghao}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '修改称号成功', 'data': {'new_chenghao': new_chenghao}})
@@ -1490,11 +1545,23 @@ class KefuUpdateShangjiaView(APIView):
return Response({'code': 400, 'msg': '金额需在0.01~1000000之间'})
except:
return Response({'code': 400, 'msg': '金额格式错误'})
old_yue = shangjia.yue
new_yue = shangjia.yue + amount
if new_yue > 999999999.99:
return Response({'code': 400, 'msg': '增加后余额超出上限'})
shangjia.yue = new_yue
shangjia.save(update_fields=['yue'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=kefu.user.Phone,
leixing=XIUGAI_LEIXING_SHANGJIA,
shangjiayue=new_yue,
shangjiayueq=old_yue,
qitashuoming=(
f'【后台】增加商家余额:{old_yue}元 → {new_yue}元,增加{amount}元,'
f'商家ID={yonghuid},昵称={shangjia.nicheng},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '余额增加成功', 'data': {'new_balance': str(shangjia.yue)}})
# 减少余额
@@ -1508,8 +1575,20 @@ class KefuUpdateShangjiaView(APIView):
return Response({'code': 400, 'msg': '金额格式错误'})
if shangjia.yue < amount:
return Response({'code': 400, 'msg': '余额不足'})
old_yue = shangjia.yue
shangjia.yue = shangjia.yue - amount
shangjia.save(update_fields=['yue'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=kefu.user.Phone,
leixing=XIUGAI_LEIXING_SHANGJIA,
shangjiayue=shangjia.yue,
shangjiayueq=old_yue,
qitashuoming=(
f'【后台】减少商家余额:{old_yue}元 → {shangjia.yue}元,减少{amount}元,'
f'商家ID={yonghuid},昵称={shangjia.nicheng},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '余额减少成功', 'data': {'new_balance': str(shangjia.yue)}})
# 封禁
@@ -1518,6 +1597,15 @@ class KefuUpdateShangjiaView(APIView):
return Response({'code': 400, 'msg': '商家已是封禁状态'})
shangjia.zhuangtai = 2
shangjia.save(update_fields=['zhuangtai'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=kefu.user.Phone,
leixing=XIUGAI_LEIXING_SHANGJIA,
qitashuoming=(
f'【后台】封禁商家账号:正常 → 禁用,'
f'商家ID={yonghuid},昵称={shangjia.nicheng},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '商家已封禁', 'data': {'zhuangtai': 2}})
# 解封
@@ -1526,6 +1614,15 @@ class KefuUpdateShangjiaView(APIView):
return Response({'code': 400, 'msg': '商家已是正常状态'})
shangjia.zhuangtai = 1
shangjia.save(update_fields=['zhuangtai'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=kefu.user.Phone,
leixing=XIUGAI_LEIXING_SHANGJIA,
qitashuoming=(
f'【后台】解封商家账号:禁用 → 正常,'
f'商家ID={yonghuid},昵称={shangjia.nicheng},操作人={kefu.user.Phone}'
),
)
return Response({'code': 0, 'msg': '商家已解封', 'data': {'zhuangtai': 1}})
@@ -3059,6 +3156,16 @@ class PromoteGuanshiToZuzhangView(APIView):
except IntegrityError:
return Response({'code': 400, 'msg': '升级失败,请稍后重试'})
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=kefu.user.Phone,
leixing=XIUGAI_LEIXING_GUANSHI,
qitashuoming=(
f'【后台】管事升级组长用户ID={yonghuid}'
f'生成组长邀请码={zuzhang.yaoqingma},操作人={kefu.user.Phone}'
),
)
logger.info(f"管事升级组长成功: yonghuid={yonghuid}, yaoqingma={zuzhang.yaoqingma}")
return Response({
'code': 0,
@@ -3119,6 +3226,7 @@ class UpdateGuanliView(APIView):
user = User.objects.select_for_update().get(UserUID=yonghuid)
guanshi = user.GuanshiProfile
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
operator = kefu.user.Phone
# ---------- 1. 基础信息修改需要4400a----------
nickname = request.data.get('nickname')
@@ -3130,6 +3238,11 @@ class UpdateGuanliView(APIView):
if any(v is not None for v in [nickname, dianhua, wechat, zhuangtai, avatar]):
if '4400a' not in permissions:
return Response({'code': 403, 'msg': '无权限修改管事基础信息需要4400a'})
base_changes = []
old_nickname = boss.nickname if boss else ''
old_dianhua = guanshi.dianhua
old_wechat = guanshi.wechat
old_zhuangtai = guanshi.zhuangtai
# 处理昵称(在老板扩展表中)
if nickname is not None:
if not boss:
@@ -3137,22 +3250,42 @@ class UpdateGuanliView(APIView):
else:
boss.nickname = nickname
boss.save()
if str(nickname) != str(old_nickname):
base_changes.append(f'昵称:{old_nickname or ""}{nickname}')
# 处理其他字段
if dianhua is not None:
guanshi.dianhua = dianhua
if str(dianhua) != str(old_dianhua):
base_changes.append(f'电话:{old_dianhua or ""}{dianhua}')
if wechat is not None:
guanshi.wechat = wechat
if str(wechat) != str(old_wechat):
base_changes.append(f'微信:{old_wechat or ""}{wechat}')
if zhuangtai is not None:
try:
zt = int(zhuangtai)
if zt in (0, 1):
guanshi.zhuangtai = zt
if zt != old_zhuangtai:
zt_map = {0: '禁用', 1: '正常'}
base_changes.append(f'状态:{zt_map.get(old_zhuangtai, old_zhuangtai)}{zt_map.get(zt, zt)}')
except:
pass
if avatar is not None:
user.Avatar = avatar
user.save()
base_changes.append('更新了头像')
guanshi.save()
if base_changes:
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_GUANSHI,
qitashuoming=(
f'【后台】修改管事基础信息:{"".join(base_changes)}'
f'管事ID={yonghuid},操作人={operator}'
),
)
# ---------- 2. 余额修改4400c4400b----------
new_yue = request.data.get('yue')
@@ -3168,11 +3301,27 @@ class UpdateGuanliView(APIView):
if new_yue > old_yue:
if '4400c' not in permissions:
return Response({'code': 403, 'msg': '无权限增加余额需要4400c'})
action_desc = f'增加{new_yue - old_yue}'
elif new_yue < old_yue:
if '4400b' not in permissions:
return Response({'code': 403, 'msg': '无权限减少余额需要4400b'})
guanshi.yue = new_yue
guanshi.save()
action_desc = f'减少{old_yue - new_yue}'
else:
action_desc = '无变动'
if new_yue != old_yue:
guanshi.yue = new_yue
guanshi.save()
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_GUANSHI,
guanshiyue=new_yue,
guanshiyueq=old_yue,
qitashuoming=(
f'【后台】修改管事可提现余额:{old_yue}元 → {new_yue}元({action_desc}'
f'管事ID={yonghuid},操作人={operator}'
),
)
# ---------- 3. 提现限额修改需要4400e----------
kaioi_ewai = request.data.get('kaioi_ewai_xiane')
@@ -3180,14 +3329,31 @@ class UpdateGuanliView(APIView):
if kaioi_ewai is not None or ewai_val is not None:
if '4400e' not in permissions:
return Response({'code': 403, 'msg': '无权限修改提现限额需要4400e'})
limit_changes = []
old_kaioi = guanshi.kaioi_ewai_xiane
old_ewai = guanshi.ewai_xiane
if kaioi_ewai is not None:
guanshi.kaioi_ewai_xiane = bool(kaioi_ewai)
if bool(kaioi_ewai) != old_kaioi:
limit_changes.append(f'额外限额开关:{"" if old_kaioi else ""}{"" if kaioi_ewai else ""}')
if ewai_val is not None:
try:
guanshi.ewai_xiane = Decimal(str(ewai_val))
if guanshi.ewai_xiane != old_ewai:
limit_changes.append(f'额外限额金额:{old_ewai}{guanshi.ewai_xiane}')
except:
return Response({'code': 400, 'msg': '额外限额金额格式错误'})
guanshi.save()
if limit_changes:
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_GUANSHI,
qitashuoming=(
f'【后台】修改管事提现限额:{"".join(limit_changes)}'
f'管事ID={yonghuid},操作人={operator}'
),
)
# ---------- 4. 分红相关修改需要4400f----------
perm_enabled = request.data.get('permanent_enabled')
@@ -3277,6 +3443,25 @@ class UpdateGuanliView(APIView):
if (record.cishu, record.huiyuan) not in keep_set:
record.delete()
dividend_parts = []
if perm_enabled is not None:
dividend_parts.append(f'永久分红开关→{"" if perm_enabled else ""}')
if perm_amount is not None:
dividend_parts.append(f'永久分红金额→{perm_amount}')
if custom_first is not None:
dividend_parts.append(f'更新首次定制分红({len(custom_first)}项)')
if extra_map is not None:
dividend_parts.append(f'更新额外次数分红({len(extra_map)}组)')
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_GUANSHI,
qitashuoming=(
f'【后台】修改管事分红策略:{"".join(dividend_parts) or "配置已更新"}'
f'管事ID={yonghuid},操作人={operator}'
),
)
return Response({'code': 0, 'msg': '修改成功'})
@@ -3559,7 +3744,7 @@ class GetZuzhangListView(APIView):
if keyword:
queryset = queryset.filter(
Q(user__UserUID__icontains=keyword) |
Q(user__boss_profile__nickname__icontains=keyword)
Q(user__BossProfile__nickname__icontains=keyword)
)
if status is not None and status != '':
@@ -3812,12 +3997,16 @@ class UpdateZuzhangView(APIView):
# 开始事务
with transaction.atomic():
operator = kefu.user.Phone
old_nickname = boss.nickname if boss else ''
old_zhuangtai = zuzhang.zhuangtai
# ---------- 1. 基础信息修改需要6600c按需求基础信息修改通常用6600c但为了安全暂不校验如有需要可加----------
nickname = request.data.get('nickname', '').strip()
avatar = request.data.get('avatar', '').strip()
dianhua = request.data.get('dianhua', '').strip()
wechat = request.data.get('wechat', '').strip()
zhuangtai = request.data.get('zhuangtai')
base_changes = []
# 状态修改需要6600c
if zhuangtai is not None:
if '6600c' not in permissions:
@@ -3826,6 +4015,9 @@ class UpdateZuzhangView(APIView):
zt = int(zhuangtai)
if zt in (0, 1):
zuzhang.zhuangtai = zt
if zt != old_zhuangtai:
zt_map = {0: '禁用', 1: '正常'}
base_changes.append(f'状态:{zt_map.get(old_zhuangtai, old_zhuangtai)}{zt_map.get(zt, zt)}')
except:
pass
if nickname:
@@ -3834,9 +4026,23 @@ class UpdateZuzhangView(APIView):
else:
boss.nickname = nickname
boss.save()
if nickname != old_nickname:
base_changes.append(f'昵称:{old_nickname or ""}{nickname}')
if avatar:
user.Avatar = avatar
user.save()
base_changes.append('更新了头像')
if base_changes:
zuzhang.save()
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_ZUZHANG,
qitashuoming=(
f'【后台】修改组长基础信息:{"".join(base_changes)}'
f'组长ID={yonghuid},操作人={operator}'
),
)
# 组长表没有电话/微信字段如需存储可暂存到User或忽略
# 这里不处理
@@ -3852,11 +4058,27 @@ class UpdateZuzhangView(APIView):
if new_val > old_ketixian_jine:
if '6600a' not in permissions:
return Response({'code': 403, 'msg': '无权限增加可提现金额需要6600a'})
action_desc = f'增加{new_val - old_ketixian_jine}'
elif new_val < old_ketixian_jine:
if '6600b' not in permissions:
return Response({'code': 403, 'msg': '无权限减少可提现金额需要6600b'})
zuzhang.ketixian_jine = new_val
zuzhang.save(update_fields=['ketixian_jine'])
action_desc = f'减少{old_ketixian_jine - new_val}'
else:
action_desc = '无变动'
if new_val != old_ketixian_jine:
zuzhang.ketixian_jine = new_val
zuzhang.save(update_fields=['ketixian_jine'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_ZUZHANG,
xiugaitijiao=new_val,
xiugaitijiaoq=old_ketixian_jine,
qitashuoming=(
f'【后台】修改组长可提现金额:{old_ketixian_jine}元 → {new_val}元({action_desc}'
f'组长ID={yonghuid},操作人={operator}'
),
)
# ---------- 3. 提现限额修改需要6600d----------
kaioi_ewai = request.data.get('kaioi_ewai_tixian')
@@ -3864,14 +4086,31 @@ class UpdateZuzhangView(APIView):
if kaioi_ewai is not None or ewai_val is not None:
if '6600d' not in permissions:
return Response({'code': 403, 'msg': '无权限修改提现限额需要6600d'})
limit_changes = []
old_kaioi = zuzhang.kaioi_ewai_tixian
old_ewai = zuzhang.ewai_tixian_xiane
if kaioi_ewai is not None:
zuzhang.kaioi_ewai_tixian = bool(kaioi_ewai)
if bool(kaioi_ewai) != old_kaioi:
limit_changes.append(f'额外限额开关:{"" if old_kaioi else ""}{"" if kaioi_ewai else ""}')
if ewai_val is not None:
try:
zuzhang.ewai_tixian_xiane = Decimal(str(ewai_val))
if zuzhang.ewai_tixian_xiane != old_ewai:
limit_changes.append(f'额外限额金额:{old_ewai}{zuzhang.ewai_tixian_xiane}')
except:
return Response({'code': 400, 'msg': '额外限额金额格式错误'})
zuzhang.save()
if limit_changes:
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_ZUZHANG,
qitashuoming=(
f'【后台】修改组长提现限额:{"".join(limit_changes)}'
f'组长ID={yonghuid},操作人={operator}'
),
)
# ---------- 4. 分红相关修改需要6600e----------
perm_enabled = request.data.get('permanent_enabled')
@@ -3957,11 +4196,231 @@ class UpdateZuzhangView(APIView):
if (record.cishu, record.huiyuan) not in keep_set:
record.delete()
dividend_parts = []
if perm_enabled is not None:
dividend_parts.append(f'永久分红开关→{"" if perm_enabled else ""}')
if perm_amount is not None:
dividend_parts.append(f'永久分红金额→{perm_amount}')
if custom_first is not None:
dividend_parts.append(f'更新首次定制分红({len(custom_first)}项)')
if extra_map is not None:
dividend_parts.append(f'更新额外次数分红({len(extra_map)}组)')
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_ZUZHANG,
qitashuoming=(
f'【后台】修改组长分红策略:{"".join(dividend_parts) or "配置已更新"}'
f'组长ID={yonghuid},操作人={operator}'
),
)
# 保存组长其他可能修改的字段(例如电话、微信没有,忽略)
return Response({'code': 0, 'msg': '修改成功'})
class ChangeInviterView(APIView):
"""
后台更换邀请人/上级
POST /houtai/htghyqr
参数:
username: 操作人账号
yonghuid: 被修改用户ID
target_type: dashou打手| guanshi管事
new_yaoqingren: 新邀请人用户ID须在 User 主表存在)
权限:
dashou → 001ee
guanshi → 4400a
校验:
打手新上级须为管事;管事新上级须为组长;不可设为自己
"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
yonghuid = request.data.get('yonghuid', '').strip()
target_type = request.data.get('target_type', '').strip().lower()
new_yaoqingren = request.data.get('new_yaoqingren', '').strip()
if not username:
return Response({'code': 401, 'msg': '缺少username'})
if not yonghuid:
return Response({'code': 400, 'msg': '缺少用户ID'})
if target_type not in ('dashou', 'guanshi'):
return Response({'code': 400, 'msg': 'target_type 须为 dashou 或 guanshi'})
if not new_yaoqingren:
return Response({'code': 400, 'msg': '缺少新邀请人ID'})
if new_yaoqingren == yonghuid:
return Response({'code': 400, 'msg': '不能将自己设为邀请人'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if target_type == 'dashou':
if '001ee' not in permissions:
return Response({'code': 403, 'msg': '无权限更换打手邀请人需要001ee'})
else:
if '4400a' not in permissions:
return Response({'code': 403, 'msg': '无权限更换管事邀请人需要4400a'})
try:
target_user = User.query.get(UserUID=yonghuid)
except User.DoesNotExist:
return Response({'code': 404, 'msg': '被修改用户不存在'})
try:
inviter_user = User.query.get(UserUID=new_yaoqingren)
except User.DoesNotExist:
return Response({'code': 404, 'msg': '新邀请人用户ID不存在'})
operator = kefu.user.Phone
with transaction.atomic():
if target_type == 'dashou':
try:
dashou = target_user.DashouProfile
except UserDashou.DoesNotExist:
return Response({'code': 404, 'msg': '用户不是打手'})
try:
guanshi_profile = inviter_user.GuanshiProfile
except UserGuanshi.DoesNotExist:
return Response({'code': 400, 'msg': '新邀请人不是管事,无法作为打手上级'})
if guanshi_profile.zhuangtai != 1:
return Response({'code': 400, 'msg': '新邀请人管事账号已禁用'})
old_inviter = dashou.yaoqingren or ''
dashou.yaoqingren = new_yaoqingren
dashou.save(update_fields=['yaoqingren'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_DASHOU,
qitashuoming=(
f'【后台】更换打手邀请人:{old_inviter}{new_yaoqingren}'
f'打手ID={yonghuid},新上级须为管事(已校验),操作人={operator}'
),
)
else:
try:
guanshi = target_user.GuanshiProfile
except UserGuanshi.DoesNotExist:
return Response({'code': 404, 'msg': '用户不是管事'})
try:
zuzhang_profile = inviter_user.ZuzhangProfile
except UserZuzhang.DoesNotExist:
return Response({'code': 400, 'msg': '新邀请人不是组长,无法作为管事上级'})
if zuzhang_profile.zhuangtai != 1:
return Response({'code': 400, 'msg': '新邀请人组长账号已禁用'})
old_inviter = guanshi.yaoqingren or ''
guanshi.yaoqingren = new_yaoqingren
guanshi.save(update_fields=['yaoqingren'])
write_xiugai_log(
yonghuid=yonghuid,
xiugaiid=operator,
leixing=XIUGAI_LEIXING_GUANSHI,
qitashuoming=(
f'【后台】更换管事邀请人:{old_inviter}{new_yaoqingren}'
f'管事ID={yonghuid},新上级须为组长(已校验),操作人={operator}'
),
)
return Response({
'code': 0,
'msg': '邀请人更换成功',
'data': {'yonghuid': yonghuid, 'new_yaoqingren': new_yaoqingren},
})
class GetOperationLogListView(APIView):
"""
后台操作日志列表xiugaijilu
POST /houtai/hqczrz
权限000001 超级权限 或 czrz666
"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
if not username:
return Response({'code': 401, 'msg': '缺少username'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if '000001' not in permissions and 'czrz666' not in permissions:
return Response({'code': 403, 'msg': '无权限查看操作日志需要000001或czrz666'})
yonghuid = request.data.get('yonghuid', '').strip()
xiugaiid = request.data.get('xiugaiid', '').strip()
leixing = request.data.get('leixing')
keyword = request.data.get('keyword', '').strip()
try:
page = max(1, int(request.data.get('page', 1)))
except (ValueError, TypeError):
page = 1
try:
page_size = min(100, max(1, int(request.data.get('page_size', 20))))
except (ValueError, TypeError):
page_size = 20
qs = Xiugaijilu.query.all().order_by('-CreateTime')
if yonghuid:
qs = qs.filter(yonghuid=yonghuid)
if xiugaiid:
qs = qs.filter(xiugaiid__icontains=xiugaiid)
if leixing not in (None, ''):
try:
qs = qs.filter(leixing=int(leixing))
except (ValueError, TypeError):
pass
if keyword:
qs = qs.filter(qitashuoming__icontains=keyword)
total = qs.count()
start = (page - 1) * page_size
records = qs[start:start + page_size]
def _fmt_decimal(val):
if val is None:
return None
return str(val)
data_list = []
for r in records:
data_list.append({
'id': r.id,
'yonghuid': r.yonghuid,
'xiugaiid': r.xiugaiid,
'leixing': r.leixing,
'leixing_label': XIUGAI_LEIXING_LABEL.get(r.leixing, f'类型{r.leixing}'),
'xiugaitijiao': _fmt_decimal(r.xiugaitijiao),
'xiugaitijiaoq': _fmt_decimal(r.xiugaitijiaoq),
'jifen': r.jifen,
'yjifen': r.yjifen,
'yajin': _fmt_decimal(r.yajin),
'yyajin': _fmt_decimal(r.yyajin),
'shangjiayue': _fmt_decimal(r.shangjiayue),
'shangjiayueq': _fmt_decimal(r.shangjiayueq),
'guanshiyue': _fmt_decimal(r.guanshiyue),
'guanshiyueq': _fmt_decimal(r.guanshiyueq),
'huiyuants': r.huiyuants,
'huiyuan_id': r.huiyuan_id,
'qitashuoming': r.qitashuoming or '',
'create_time': r.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if r.CreateTime else '',
})
return Response({
'code': 0,
'data': {
'list': data_list,
'total': total,
'page': page,
'page_size': page_size,
},
})
class GetProductTypeZoneView(APIView):