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

@@ -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):