818 lines
34 KiB
Python
818 lines
34 KiB
Python
import hmac
|
||
import threading
|
||
import traceback
|
||
import uuid
|
||
import hashlib
|
||
import xmltodict
|
||
import time
|
||
import logging
|
||
import requests
|
||
import os
|
||
import secrets
|
||
import random
|
||
import string
|
||
from calendar import monthrange
|
||
from decimal import Decimal
|
||
from datetime import date, datetime, timedelta
|
||
from django.utils import timezone
|
||
from django.conf import settings
|
||
from django.core.exceptions import ObjectDoesNotExist
|
||
from django.core.paginator import Paginator
|
||
from django.db import models, transaction, IntegrityError
|
||
from django.db.models import Q, Count, Sum, F, Exists, OuterRef
|
||
from gvsdsdk.fluent import db, func, FQ
|
||
from django.db.models.functions import TruncDate, TruncMonth, TruncYear
|
||
from django.contrib.auth.hashers import make_password
|
||
from django.views.decorators.csrf import csrf_exempt
|
||
from django.utils.decorators import method_decorator
|
||
|
||
from rest_framework.views import APIView
|
||
from rest_framework.response import Response
|
||
from rest_framework import status
|
||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
|
||
|
||
# 工具类导入
|
||
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
|
||
from backend.utils import (
|
||
update_dashou_daily_by_action,
|
||
update_shangjia_daily
|
||
)
|
||
from jituan.services.club_context import filter_club_char_field, filter_queryset_by_club, filter_user_related_by_club, filter_user_qs_by_club, orders_for_request
|
||
from jituan.services.catalog_scope import block_non_group_catalog_scope
|
||
from jituan.services.club_penalty import (
|
||
filter_penalty_qs, resolve_penalty_club_id, filter_gsfenhong_qs,
|
||
forbid_penalty_out_of_scope, resolve_penalty_record_club_id,
|
||
)
|
||
from jituan.services.club_user_access import forbid_if_user_out_of_scope, list_response_meta
|
||
from shop.utils import update_dianpu_daily_stat
|
||
from products.utils import update_shangpin_daily_stat
|
||
from orders.notice_tasks import dingdan_guangbo
|
||
from ..utils import (
|
||
verify_kefu_permission,
|
||
PERMISSION_TO_SHENFEN,
|
||
SHENFEN_PROFILE_MAP,
|
||
has_fadan_view_permission,
|
||
has_merchant_order_permission,
|
||
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 集中导入
|
||
## gvsdsdk RBAC 模型(使用 PascalCase 字段名,匹配实际数据库表结构)
|
||
from gvsdsdk.models import Role, Permission, RolePermission, UserRole
|
||
## backend
|
||
from backend.models import WithdrawalDailyStats
|
||
|
||
## users
|
||
from users.models import (
|
||
UserGuanshi, UserBoss, UserZuzhang,
|
||
UserKefu, UserDashou, Xiugaijilu, UserShangjia, UserShenheguan
|
||
)
|
||
from users.business_models import User
|
||
|
||
## orders
|
||
from orders.models import (
|
||
CommissionRate, PlayerDeliveryImage, PenaltyRecord,
|
||
OrderPlayerHistory, Order, RefundRecord,
|
||
MerchantOrderExt, Penalty, PenaltyAppealImage, PenaltyBonus
|
||
)
|
||
|
||
## shop
|
||
from shop.models import Dianpu, DianpuMorenPeizhi, YonghuPingzheng, ShangpinLeixingDianpu, DianpuShangpinShenheShezhi
|
||
|
||
## products
|
||
from products.models import (
|
||
Shangpin, ShangpinLeixing, ShangpinZhuanqu, Huiyuan,
|
||
DuociFenhong, Czjilu, Huiyuangoumai, Gsfenhong
|
||
)
|
||
|
||
## config
|
||
from config.models import (
|
||
WithdrawConfig, ShangjiaLianjie, AccountPermission,
|
||
TixianQuotaDefault,
|
||
DailyIncomeStat, DailyPayoutStat, PopupPage, PopupConfig, PopupImage
|
||
)
|
||
|
||
## rank
|
||
from rank.models import (
|
||
KaoheguanBankuai, Bankuai, Chenghao, KaoheCishuFeiyong,
|
||
YonghuChenghao, ShenheJilu, KaoheJiluFeiyong, KaoheJujueJilu
|
||
)
|
||
|
||
# 序列化器
|
||
from ..serializers import PopupPageSerializer
|
||
|
||
# 全局常量、日志对象
|
||
logger = logging.getLogger('houtai')
|
||
SHOP_PRODUCT_PERMS = ['1199ab', '1199abc', '1199abd']
|
||
|
||
class GetGuanliListView(APIView):
|
||
"""
|
||
获取管事列表
|
||
权限:4400a,4400b,4400c,4400d,4400e,4400f 任一
|
||
POST /houtai/hthqgslb
|
||
参数:
|
||
username, keyword(可选), balance_op/balance_value, commission_op/commission_value,
|
||
invite_count_op/invite_count_value, status, page, page_size
|
||
返回:
|
||
code:0, data: { list: [...], total }
|
||
"""
|
||
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
|
||
required_perms = {'4400a', '4400b', '4400c', '4400d', '4400e', '4400f'}
|
||
if not any(p in permissions for p in required_perms):
|
||
return Response({'code': 403, 'msg': '您没有权限查看管事列表'})
|
||
|
||
# 分页参数
|
||
try:
|
||
page = int(request.data.get('page', 1))
|
||
page_size = int(request.data.get('page_size', 20))
|
||
if page < 1 or page_size < 1:
|
||
raise ValueError
|
||
except:
|
||
return Response({'code': 400, 'msg': '分页参数错误'})
|
||
|
||
# 筛选参数
|
||
keyword = request.data.get('keyword', '').strip()
|
||
balance_op = request.data.get('balance_op', '').strip().lower()
|
||
balance_value = request.data.get('balance_value')
|
||
commission_op = request.data.get('commission_op', '').strip().lower()
|
||
commission_value = request.data.get('commission_value')
|
||
invite_count_op = request.data.get('invite_count_op', '').strip().lower()
|
||
invite_count_value = request.data.get('invite_count_value')
|
||
status = request.data.get('status')
|
||
|
||
# 构建查询:从 User 开始,关联 UserGuanshi 和 UserBoss(左连接获取昵称)
|
||
qs = filter_user_qs_by_club(
|
||
User.query.filter(
|
||
GuanshiProfile__isnull=False # 确保是管事
|
||
).select_related('GuanshiProfile').select_related('BossProfile'),
|
||
request,
|
||
)
|
||
|
||
# 关键词搜索(用户ID 或 昵称来自 boss_profile.nickname)
|
||
if keyword:
|
||
qs = qs.filter(
|
||
Q(UserUID__icontains=keyword) |
|
||
Q(BossProfile__nickname__icontains=keyword)
|
||
)
|
||
|
||
# 余额筛选(使用 guanshi_profile.yue)
|
||
if balance_op and balance_value is not None and balance_value != '':
|
||
try:
|
||
balance_val = Decimal(str(balance_value))
|
||
if balance_op == 'gte':
|
||
qs = qs.filter(GuanshiProfile__yue__gte=balance_val)
|
||
elif balance_op == 'lte':
|
||
qs = qs.filter(GuanshiProfile__yue__lte=balance_val)
|
||
else:
|
||
return Response({'code': 400, 'msg': '余额比较符无效'})
|
||
except:
|
||
return Response({'code': 400, 'msg': '余额数值格式错误'})
|
||
|
||
# 分佣总额筛选(guanshi_profile.chongzhifenrun)
|
||
if commission_op and commission_value is not None and commission_value != '':
|
||
try:
|
||
commission_val = Decimal(str(commission_value))
|
||
if commission_op == 'gte':
|
||
qs = qs.filter(GuanshiProfile__chongzhifenrun__gte=commission_val)
|
||
elif commission_op == 'lte':
|
||
qs = qs.filter(GuanshiProfile__chongzhifenrun__lte=commission_val)
|
||
else:
|
||
return Response({'code': 400, 'msg': '分佣比较符无效'})
|
||
except:
|
||
return Response({'code': 400, 'msg': '分佣数值格式错误'})
|
||
|
||
# 邀请打手总数筛选(guanshi_profile.yaogingshuliang)
|
||
if invite_count_op and invite_count_value is not None and invite_count_value != '':
|
||
try:
|
||
invite_val = int(invite_count_value)
|
||
if invite_count_op == 'gte':
|
||
qs = qs.filter(GuanshiProfile__yaogingshuliang__gte=invite_val)
|
||
elif invite_count_op == 'lte':
|
||
qs = qs.filter(GuanshiProfile__yaogingshuliang__lte=invite_val)
|
||
else:
|
||
return Response({'code': 400, 'msg': '邀请数量比较符无效'})
|
||
except:
|
||
return Response({'code': 400, 'msg': '邀请数量格式错误'})
|
||
|
||
# 状态筛选
|
||
if status is not None and status != '':
|
||
try:
|
||
status_val = int(status)
|
||
if status_val not in (0, 1):
|
||
raise ValueError
|
||
qs = qs.filter(GuanshiProfile__zhuangtai=status_val)
|
||
except:
|
||
return Response({'code': 400, 'msg': '状态值无效'})
|
||
|
||
# 计算总数
|
||
total = qs.count()
|
||
|
||
# 分页
|
||
offset = (page - 1) * page_size
|
||
users = qs.order_by('-UserCreateTime')[offset:offset + page_size]
|
||
|
||
# 构建返回数据
|
||
result = []
|
||
for user in users:
|
||
guanshi = user.GuanshiProfile
|
||
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
|
||
result.append({
|
||
'yonghuid': user.UserUID,
|
||
'avatar': user.Avatar or '',
|
||
'nickname': boss.nickname if boss else '',
|
||
'zhuangtai': guanshi.zhuangtai,
|
||
'yaogingshuliang': guanshi.yaogingshuliang,
|
||
'yue': str(guanshi.yue),
|
||
'chongzhifenrun': str(guanshi.chongzhifenrun),
|
||
})
|
||
|
||
return Response({
|
||
'code': 0,
|
||
**list_response_meta(request),
|
||
'data': {'list': result, 'total': total},
|
||
})
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
class GetGuanliDetailView(APIView):
|
||
"""
|
||
获取管事详情
|
||
权限:4400a,4400b,4400c,4400d,4400e,4400f 任一
|
||
POST /houtai/hthqgsxq
|
||
参数:username, yonghuid
|
||
返回:
|
||
code:0, data: {
|
||
base: {...},
|
||
balance: {...},
|
||
permanent_enabled, permanent_amount,
|
||
custom_first_dividend: {},
|
||
extra_dividend_map: {},
|
||
all_members: []
|
||
}
|
||
"""
|
||
permission_classes = []
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
username = request.data.get('username', '').strip()
|
||
yonghuid = request.data.get('yonghuid', '').strip()
|
||
|
||
if not username:
|
||
return Response({'code': 401, 'msg': '缺少username'})
|
||
if not yonghuid:
|
||
return Response({'code': 400, 'msg': '缺少用户ID'})
|
||
|
||
# 权限校验(需要任一权限)
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
required_perms = {'4400a', '4400b', '4400c', '4400d', '4400e', '4400f'}
|
||
if not any(p in permissions for p in required_perms):
|
||
return Response({'code': 403, 'msg': '您没有权限查看管事详情'})
|
||
|
||
# 查询用户主表和管事扩展表
|
||
try:
|
||
user = User.query.select_related('GuanshiProfile', 'BossProfile').get(UserUID=yonghuid)
|
||
guanshi = user.GuanshiProfile
|
||
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
|
||
except User.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '管事不存在'})
|
||
except UserGuanshi.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '用户不是管事'})
|
||
|
||
denied = forbid_if_user_out_of_scope(request, user)
|
||
if denied:
|
||
return denied
|
||
|
||
# 基础信息
|
||
is_zuzhang = UserZuzhang.query.filter(user=user).exists()
|
||
base_data = {
|
||
'yonghuid': user.UserUID,
|
||
'nickname': boss.nickname if boss else '',
|
||
'avatar': user.Avatar or '',
|
||
'dianhua': guanshi.dianhua or '',
|
||
'wechat': guanshi.wechat or '',
|
||
'zhuangtai': guanshi.zhuangtai,
|
||
'yaoqingma': guanshi.yaoqingma,
|
||
'yaoqingren': guanshi.yaoqingren,
|
||
'invite_qrcode_url': guanshi.invite_qrcode_url or '',
|
||
'yaogingshuliang': guanshi.yaogingshuliang,
|
||
'jinrichongzhi': guanshi.jinrichongzhi,
|
||
'jinyuechongzhi': guanshi.jinyuechongzhi,
|
||
'chongzhifenrun': str(guanshi.chongzhifenrun),
|
||
'create_time': guanshi.CreateTime.isoformat() if guanshi.CreateTime else None,
|
||
'CreateTime': guanshi.CreateTime.isoformat() if guanshi.CreateTime else None,
|
||
}
|
||
|
||
# 余额提现相关
|
||
balance_data = {
|
||
'yue': str(guanshi.yue),
|
||
'jinritixian_jine': str(guanshi.jinritixian_jine),
|
||
'last_tixian_date': guanshi.last_tixian_date.isoformat() if guanshi.last_tixian_date else None,
|
||
'kaioi_ewai_xiane': guanshi.kaioi_ewai_xiane,
|
||
'ewai_xiane': str(guanshi.ewai_xiane),
|
||
}
|
||
|
||
# 永久分红(二次分红)
|
||
permanent_enabled = guanshi.fenghong_erci_enabled
|
||
permanent_amount = str(guanshi.fenghong_erci) if guanshi.fenghong_erci else '0'
|
||
|
||
# 获取所有会员(用于前端选择)
|
||
all_members = list(Huiyuan.query.all().values('huiyuan_id', 'jieshao', 'jiage', 'guanshifc'))
|
||
for m in all_members:
|
||
m['jiage'] = str(m['jiage'])
|
||
m['guanshifc'] = str(m['guanshifc'])
|
||
|
||
# 获取多次分红配置(按次数和会员组织,按俱乐部隔离)
|
||
from jituan.services.club_penalty import resolve_gsfenhong_club_id
|
||
guanshi_club = resolve_gsfenhong_club_id(dashouid=yonghuid)
|
||
duoci_records = DuociFenhong.query.filter(
|
||
yonghuid=yonghuid, club_id=guanshi_club,
|
||
).order_by('cishu')
|
||
custom_first = {} # 首次分红定制(cishu=1)
|
||
extra_map = {} # 其他次数 { cishu: { huiyuan_id: { guanshi_fenhong, jieshao, jiage } } }
|
||
|
||
for record in duoci_records:
|
||
# 获取会员信息
|
||
member = Huiyuan.query.filter(huiyuan_id=record.huiyuan).first()
|
||
if not member:
|
||
continue
|
||
member_info = {
|
||
'jieshao': member.jieshao,
|
||
'jiage': str(member.jiage),
|
||
}
|
||
if record.cishu == 1:
|
||
custom_first[record.huiyuan] = float(record.guanshi_fenhong)
|
||
else:
|
||
times = record.cishu
|
||
if times not in extra_map:
|
||
extra_map[times] = {}
|
||
extra_map[times][record.huiyuan] = {
|
||
'guanshi_fenhong': float(record.guanshi_fenhong),
|
||
**member_info
|
||
}
|
||
|
||
# 注意:首次分红定制只存储有定制记录的会员,没有定制的会员使用默认值(前端通过会员表展示)
|
||
# 额外次数分红按次数返回
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'base': base_data,
|
||
'balance': balance_data,
|
||
'permanent_enabled': permanent_enabled,
|
||
'permanent_amount': permanent_amount,
|
||
'custom_first_dividend': custom_first,
|
||
'extra_dividend_map': extra_map,
|
||
'all_members': all_members,
|
||
'is_zuzhang': is_zuzhang,
|
||
}
|
||
})
|
||
|
||
|
||
|
||
|
||
def _generate_unique_zuzhang_yaoqingma(yonghuid):
|
||
"""生成唯一组长邀请码(与小程序组长注册逻辑一致)"""
|
||
timestamp = str(int(time.time()))[-6:]
|
||
uid_part = yonghuid[-6:] if len(yonghuid) >= 6 else yonghuid.rjust(6, '0')
|
||
rand_str = ''.join(random.choices(string.ascii_uppercase + string.digits, k=2))
|
||
base = f"Z{timestamp}{uid_part}{rand_str}"
|
||
for _ in range(5):
|
||
if not UserZuzhang.query.filter(yaoqingma=base).exists():
|
||
return base
|
||
base = base + random.choice(string.digits)
|
||
return base + str(int(time.time() * 1000))[-4:]
|
||
|
||
|
||
def _create_zuzhang_profile(user):
|
||
"""为已有用户创建组长扩展记录(默认字段与小程序注册一致)"""
|
||
invite_guanshi_count = UserGuanshi.query.filter(yaoqingren=user.UserUID).count()
|
||
yaoqingma = _generate_unique_zuzhang_yaoqingma(user.UserUID)
|
||
return UserZuzhang.query.create(
|
||
user=user,
|
||
yaoqingma=yaoqingma,
|
||
fenyong_zonge=Decimal('0.00'),
|
||
ketixian_jine=Decimal('0.00'),
|
||
yaoqing_zongshu=invite_guanshi_count,
|
||
jinri_fenyong=Decimal('0.00'),
|
||
jinyue_fenyong=Decimal('0.00'),
|
||
zhuangtai=1,
|
||
jinri_tixian=Decimal('0.00'),
|
||
ewai_tixian_xiane=Decimal('0.00'),
|
||
kaioi_ewai_tixian=False,
|
||
kaioi_ewai_fenhong=False,
|
||
ewai_fenhong_jine=Decimal('0.00'),
|
||
)
|
||
|
||
|
||
class PromoteGuanshiToZuzhangView(APIView):
|
||
"""
|
||
后台将管事用户升级为组长(写入 user_zuzhang 扩展表)
|
||
POST /houtai/htgssjzz
|
||
权限:sjzz666
|
||
参数:username, yonghuid
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
username = request.data.get('username', '').strip()
|
||
yonghuid = request.data.get('yonghuid', '').strip()
|
||
|
||
if not username:
|
||
return Response({'code': 401, 'msg': '缺少username'})
|
||
if not yonghuid:
|
||
return Response({'code': 400, 'msg': '缺少用户ID'})
|
||
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
if 'sjzz666' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限升级组长(需要 sjzz666)'})
|
||
|
||
try:
|
||
user = User.query.select_related('GuanshiProfile').get(UserUID=yonghuid)
|
||
guanshi = user.GuanshiProfile
|
||
except User.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '用户不存在'})
|
||
except UserGuanshi.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '用户不是管事,无法升级组长'})
|
||
|
||
if UserZuzhang.query.filter(user=user).exists():
|
||
zuzhang = user.ZuzhangProfile
|
||
return Response({
|
||
'code': 400,
|
||
'msg': '该用户已是组长',
|
||
'data': {
|
||
'yonghuid': user.UserUID,
|
||
'yaoqingma': zuzhang.yaoqingma,
|
||
},
|
||
})
|
||
|
||
if guanshi.zhuangtai != 1:
|
||
return Response({'code': 400, 'msg': '管事账号已禁用,无法升级组长'})
|
||
|
||
try:
|
||
with transaction.atomic():
|
||
zuzhang = _create_zuzhang_profile(user)
|
||
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,
|
||
'msg': '已升级为组长',
|
||
'data': {
|
||
'yonghuid': user.UserUID,
|
||
'nickname': getattr(user.BossProfile, 'nickname', '') if hasattr(user, 'BossProfile') else '',
|
||
'yaoqingma': zuzhang.yaoqingma,
|
||
'yaoqing_zongshu': zuzhang.yaoqing_zongshu,
|
||
'ketixian_jine': str(zuzhang.ketixian_jine),
|
||
},
|
||
})
|
||
|
||
|
||
|
||
|
||
class UpdateGuanliView(APIView):
|
||
"""
|
||
修改管事信息(基础信息、余额、提现限额、分红策略)
|
||
权限细分:
|
||
- 修改状态(封禁/解封)及昵称/电话/微信:需要4400a
|
||
- 增加余额:需要4400c
|
||
- 减少余额:需要4400b
|
||
- 修改提现限额(kaioi_ewai_xiane/ewai_xiane):需要4400e
|
||
- 修改分红相关(永久分红、定制首次、额外次数):需要4400f
|
||
POST /houtai/htxggsxx
|
||
"""
|
||
permission_classes = []
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
username = request.data.get('username', '').strip()
|
||
yonghuid = request.data.get('yonghuid', '').strip()
|
||
|
||
if not username:
|
||
return Response({'code': 401, 'msg': '缺少username'})
|
||
if not yonghuid:
|
||
return Response({'code': 400, 'msg': '缺少用户ID'})
|
||
|
||
# 权限校验(先获取权限列表)
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
|
||
# 查询管事对象(不在事务外加锁,事务内加锁)
|
||
try:
|
||
user = User.query.get(UserUID=yonghuid)
|
||
guanshi = user.GuanshiProfile
|
||
boss = user.BossProfile if hasattr(user, 'BossProfile') else None
|
||
except User.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '用户不存在'})
|
||
except UserGuanshi.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '用户不是管事'})
|
||
|
||
# 开始事务,内部使用 select_for_update
|
||
with transaction.atomic():
|
||
# 重新获取加锁对象
|
||
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')
|
||
dianhua = request.data.get('dianhua')
|
||
wechat = request.data.get('wechat')
|
||
zhuangtai = request.data.get('zhuangtai')
|
||
avatar = request.data.get('avatar')
|
||
|
||
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:
|
||
boss = UserBoss.query.create(user=user, nickname=nickname)
|
||
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. 余额修改(加:4400c;减:4400b)----------
|
||
new_yue = request.data.get('yue')
|
||
if new_yue is not None:
|
||
try:
|
||
new_yue = Decimal(str(new_yue))
|
||
if new_yue < 0:
|
||
return Response({'code': 400, 'msg': '余额不能为负数'})
|
||
except:
|
||
return Response({'code': 400, 'msg': '余额格式错误'})
|
||
|
||
old_yue = guanshi.yue
|
||
if new_yue > old_yue:
|
||
if '4400c' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限增加余额(需要4400c)'})
|
||
from jituan.services.group_finance_gate import deny_group_finance_exec
|
||
deny = deny_group_finance_exec(request.user, request)
|
||
if deny:
|
||
return deny
|
||
action_desc = f'增加{new_yue - old_yue}元'
|
||
elif new_yue < old_yue:
|
||
if '4400b' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限减少余额(需要4400b)'})
|
||
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')
|
||
ewai_val = request.data.get('ewai_xiane')
|
||
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')
|
||
perm_amount = request.data.get('permanent_amount')
|
||
custom_first = request.data.get('custom_first_dividend')
|
||
extra_map = request.data.get('extra_dividend_map')
|
||
|
||
if any(v is not None for v in [perm_enabled, perm_amount, custom_first, extra_map]):
|
||
if '4400f' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限修改分红策略(需要4400f)'})
|
||
|
||
# 4.1 永久分红
|
||
if perm_enabled is not None:
|
||
guanshi.fenghong_erci_enabled = bool(perm_enabled)
|
||
if perm_amount is not None:
|
||
try:
|
||
guanshi.fenghong_erci = Decimal(str(perm_amount))
|
||
except:
|
||
return Response({'code': 400, 'msg': '永久分红金额格式错误'})
|
||
guanshi.save()
|
||
|
||
# 4.2 首次定制分红(cishu=1)
|
||
if custom_first is not None and isinstance(custom_first, dict):
|
||
from jituan.services.club_penalty import resolve_gsfenhong_club_id
|
||
guanshi_club = resolve_gsfenhong_club_id(dashouid=yonghuid)
|
||
# 获取当前所有首次定制记录
|
||
existing_first = DuociFenhong.query.filter(
|
||
club_id=guanshi_club, yonghuid=yonghuid, cishu=1,
|
||
)
|
||
# 前端传来的定制字典 {会员ID: 金额}
|
||
for huiyuan_id, amount in custom_first.items():
|
||
try:
|
||
amount = Decimal(str(amount))
|
||
except:
|
||
continue
|
||
if amount <= 0:
|
||
DuociFenhong.query.filter(
|
||
club_id=guanshi_club, yonghuid=yonghuid, huiyuan=huiyuan_id, cishu=1,
|
||
).delete()
|
||
else:
|
||
DuociFenhong.query.update_or_create(
|
||
club_id=guanshi_club,
|
||
yonghuid=yonghuid,
|
||
huiyuan=huiyuan_id,
|
||
cishu=1,
|
||
defaults={
|
||
'guanshi_fenhong': amount,
|
||
'zuzhang_fenhong': Decimal('0'),
|
||
},
|
||
)
|
||
# 删除那些在前端字典中不存在的首次定制记录(即已取消定制)
|
||
for record in existing_first:
|
||
if record.huiyuan not in custom_first:
|
||
record.delete()
|
||
|
||
# 4.3 额外次数分红(cishu >= 2)
|
||
if extra_map is not None and isinstance(extra_map, dict):
|
||
from jituan.services.club_penalty import resolve_gsfenhong_club_id
|
||
guanshi_club = resolve_gsfenhong_club_id(dashouid=yonghuid)
|
||
# 收集需要保留的记录
|
||
keep_set = set()
|
||
for times_str, members in extra_map.items():
|
||
try:
|
||
cishu = int(times_str)
|
||
if cishu < 2:
|
||
continue
|
||
except:
|
||
continue
|
||
if not isinstance(members, dict):
|
||
continue
|
||
for huiyuan_id, data in members.items():
|
||
if not isinstance(data, dict):
|
||
continue
|
||
amount = data.get('guanshi_fenhong')
|
||
if amount is None:
|
||
continue
|
||
try:
|
||
amount = Decimal(str(amount))
|
||
except:
|
||
continue
|
||
keep_set.add((cishu, huiyuan_id))
|
||
# 更新或创建
|
||
DuociFenhong.query.update_or_create(
|
||
club_id=guanshi_club,
|
||
yonghuid=yonghuid,
|
||
huiyuan=huiyuan_id,
|
||
cishu=cishu,
|
||
defaults={
|
||
'guanshi_fenhong': amount,
|
||
'zuzhang_fenhong': Decimal('0')
|
||
}
|
||
)
|
||
# 删除不在 keep_set 中的额外次数记录
|
||
existing_extra = DuociFenhong.query.filter(
|
||
club_id=guanshi_club, yonghuid=yonghuid, cishu__gte=2,
|
||
)
|
||
for record in existing_extra:
|
||
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': '修改成功'})
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|