1148 lines
50 KiB
Python
1148 lines
50 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 KefuGetDashouListView(APIView):
|
||
"""
|
||
客服获取打手列表接口(仅打手类型)
|
||
需要拥有以下任一权限:001aa,001bb,001cc,001dd,001ee,001ff,001gg
|
||
请求:POST /yonghu/kefuhqdslb
|
||
参数:
|
||
username: 客服手机号(用于权限校验)
|
||
keyword: 搜索关键词(可选,搜索用户ID或昵称)
|
||
page: 页码(从1开始)
|
||
page_size: 每页数量
|
||
返回:
|
||
code: 0
|
||
data: {
|
||
list: [打手对象],
|
||
has_more: bool,
|
||
total: int, // 总打手数
|
||
valid_dashou_count: int // 有效打手数(有有效会员)
|
||
}
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
# 1. 获取参数(前端字段为 username)
|
||
username = request.data.get('username', '').strip()
|
||
keyword = request.data.get('keyword', '').strip()
|
||
try:
|
||
page = int(request.data.get('page', 1))
|
||
page_size = int(request.data.get('page_size', 20))
|
||
except ValueError:
|
||
return Response({'code': 400, 'msg': '分页参数错误'})
|
||
|
||
if not username:
|
||
return Response({'code': 401, 'msg': '认证失败'})
|
||
|
||
# 2. 公共权限校验(验证客服身份,获取权限列表)
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions # 直接返回错误响应
|
||
|
||
# 3. 检查是否拥有打手管理相关权限(至少一个)
|
||
required_perms = {'001aa', '001bb', '001cc', '001dd', '001ee', '001ff', '001gg'}
|
||
if not any(p in permissions for p in required_perms):
|
||
return Response({'code': 403, 'msg': '您没有权限查看打手列表'})
|
||
|
||
# 4. 构建打手查询集(仅打手类型)
|
||
dashou_qs = filter_user_related_by_club(
|
||
UserDashou.query.select_related('user').all(), request,
|
||
)
|
||
|
||
# 关键词筛选(用户ID或昵称)
|
||
if keyword:
|
||
dashou_qs = dashou_qs.filter(
|
||
Q(user__UserUID__icontains=keyword) |
|
||
Q(nicheng__icontains=keyword)
|
||
)
|
||
|
||
# 5. 统计打手总数
|
||
total_dashou = dashou_qs.count()
|
||
|
||
# 6. 统计有效打手数(有会员且未过期)
|
||
dashou_user_ids = dashou_qs.values_list('user__UserUID', flat=True)
|
||
now = timezone.now()
|
||
hy_qs = filter_queryset_by_club(Huiyuangoumai.query.all(), request)
|
||
valid_dashou_count = hy_qs.filter(
|
||
yonghu_id__in=dashou_user_ids,
|
||
huiyuan_zhuangtai=1,
|
||
daoqi_time__gt=now,
|
||
).values('yonghu_id').distinct().count()
|
||
|
||
# 7. 分页
|
||
offset = (page - 1) * page_size
|
||
dashou_list = dashou_qs.order_by('-CreateTime')[offset:offset + page_size]
|
||
|
||
# 8. 构建返回数据
|
||
result = []
|
||
for dashou in dashou_list:
|
||
user = dashou.user
|
||
result.append({
|
||
'yonghuid': user.UserUID,
|
||
'avatar': user.Avatar or '',
|
||
'zaixianzhuangtai': dashou.zaixianzhuangtai,
|
||
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
|
||
'nicheng': dashou.nicheng or '',
|
||
'zhuangtai': dashou.zhuangtai,
|
||
})
|
||
|
||
has_more = (offset + len(dashou_list)) < total_dashou
|
||
|
||
meta = list_response_meta(request)
|
||
return Response({
|
||
'code': 0,
|
||
'club_id': meta['club_id'],
|
||
'scope': meta['scope'],
|
||
'data': {
|
||
'list': result,
|
||
'has_more': has_more,
|
||
'total': total_dashou,
|
||
'valid_dashou_count': valid_dashou_count,
|
||
}
|
||
})
|
||
|
||
|
||
|
||
class KefuGetDashouDetailView(APIView):
|
||
"""
|
||
客服获取打手详情接口
|
||
请求:POST /yonghu/kefuhqdsxq
|
||
参数:{
|
||
"username": "客服手机号",
|
||
"uid": "打手ID (yonghuid)"
|
||
}
|
||
认证:JWT
|
||
返回:code=0 + 打手信息 + 会员列表 + 所有会员类型 + 客服权限列表
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
# 1. 获取参数
|
||
username = request.data.get('phone', '').strip()
|
||
uid = request.data.get('uid', '').strip()
|
||
|
||
if not username or not uid:
|
||
return Response({'code': 400, 'msg': '缺少参数'})
|
||
|
||
# 2. 公共权限校验(验证客服身份,获取权限列表)
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
|
||
# 3. 检查是否拥有打手管理相关权限(至少一个)
|
||
required_perms = {'001aa', '001bb', '001cc', '001dd', '001ee', '001ff', '001gg'}
|
||
if not any(p in permissions for p in required_perms):
|
||
return Response({'code': 403, 'msg': '您没有权限查看打手详情'})
|
||
|
||
# 4. 查询打手主表和扩展表
|
||
try:
|
||
user_main = User.query.select_related('DashouProfile').get(
|
||
UserUID=uid
|
||
)
|
||
except User.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '打手不存在'})
|
||
|
||
denied = forbid_if_user_out_of_scope(request, user_main)
|
||
if denied:
|
||
return denied
|
||
|
||
try:
|
||
dashou = user_main.DashouProfile
|
||
except ObjectDoesNotExist:
|
||
# 用户存在但未注册打手身份,返回基本信息
|
||
user_info = {
|
||
'yonghuid': user_main.UserUID,
|
||
'avatar': user_main.Avatar or '',
|
||
'zaixianzhuangtai': 0,
|
||
'zhanghaozhuangtai': 0,
|
||
'zhuangtai': 0,
|
||
'nicheng': '',
|
||
'chenghao': '',
|
||
'dianhua': '',
|
||
'wechat': '',
|
||
'phone': user_main.Phone or '',
|
||
'CreateTime': user_main.UserCreateTime,
|
||
'create_time': user_main.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user_main, 'UserCreateTime', None) else '',
|
||
'ip': user_main.IP or '',
|
||
'jiedanzongliang': 0,
|
||
'chengjiaozongliang': 0,
|
||
'tuikuanliang': 0,
|
||
'jinrijiedan': 0,
|
||
'yue': 0.00,
|
||
'zonge': 0.00,
|
||
'jifen': 0,
|
||
'yajin': 0.00,
|
||
'jinrishouyi': 0.00,
|
||
'jinyueshouyi': 0.00,
|
||
'yaoqingren': '',
|
||
'jieshao': '',
|
||
}
|
||
member_list = []
|
||
# 仍然需要返回 all_members 和 permissions
|
||
all_members = list(Huiyuan.query.all().values('huiyuan_id', 'jieshao'))
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'user_info': user_info,
|
||
'member_list': member_list,
|
||
'all_members': all_members,
|
||
'permissions': permissions,
|
||
}
|
||
})
|
||
|
||
# 5. 构建打手信息
|
||
user_info = {
|
||
'yonghuid': user_main.UserUID,
|
||
'avatar': user_main.Avatar or '',
|
||
'zaixianzhuangtai': dashou.zaixianzhuangtai,
|
||
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
|
||
'zhuangtai': dashou.zhuangtai,
|
||
'nicheng': dashou.nicheng or '',
|
||
'chenghao': dashou.chenghao or '',
|
||
'dianhua': dashou.dianhua or '',
|
||
'wechat': dashou.wechat or '',
|
||
'phone': user_main.Phone or '',
|
||
'CreateTime': user_main.UserCreateTime,
|
||
'create_time': user_main.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user_main, 'UserCreateTime', None) else '',
|
||
'ip': user_main.IP or '',
|
||
'jiedanzongliang': dashou.jiedanzongliang,
|
||
'chengjiaozongliang': dashou.chengjiaozongliang,
|
||
'tuikuanliang': dashou.tuikuanliang,
|
||
'jinrijiedan': dashou.jinrijiedan,
|
||
'yue': float(dashou.yue) if dashou.yue else 0.00,
|
||
'zonge': float(dashou.zonge) if dashou.zonge else 0.00,
|
||
'jifen': dashou.jifen,
|
||
'yajin': float(dashou.yajin) if dashou.yajin else 0.00,
|
||
'jinrishouyi': float(dashou.jinrishouyi) if dashou.jinrishouyi else 0.00,
|
||
'jinyueshouyi': float(dashou.jinyueshouyi) if dashou.jinyueshouyi else 0.00,
|
||
'yaoqingren': dashou.yaoqingren or '',
|
||
'jieshao': dashou.jieshao or '',
|
||
}
|
||
|
||
# 6. 查询打手已拥有的会员(未过期)
|
||
member_records = Huiyuangoumai.query.filter(
|
||
yonghu_id=uid
|
||
).order_by('-CreateTime')
|
||
member_list = []
|
||
for record in member_records:
|
||
# 检查是否过期
|
||
if hasattr(record, 'jiance_shifou_daoqi'):
|
||
is_expired = record.jiance_shifou_daoqi()
|
||
else:
|
||
is_expired = record.daoqi_time < timezone.now() if record.daoqi_time else True
|
||
|
||
if not is_expired:
|
||
try:
|
||
huiyuan = Huiyuan.query.get(huiyuan_id=record.huiyuan_id)
|
||
huiyuan_name = huiyuan.jieshao
|
||
except Huiyuan.DoesNotExist:
|
||
huiyuan_name = '未知会员'
|
||
member_list.append({
|
||
'id': record.id,
|
||
'huiyuan_name': huiyuan_name,
|
||
'daoqi_time': record.daoqi_time,
|
||
'is_active': record.huiyuan_zhuangtai == 1,
|
||
'huiyuan_id': record.huiyuan_id, # 用于删除操作
|
||
})
|
||
|
||
# 7. 查询所有会员类型(用于前端添加会员)
|
||
all_members = list(Huiyuan.query.all().values('huiyuan_id', 'jieshao'))
|
||
|
||
return Response({
|
||
'code': 0,
|
||
'data': {
|
||
'user_info': user_info,
|
||
'member_list': member_list,
|
||
'all_members': all_members,
|
||
'permissions': permissions, # 返回当前客服的权限列表
|
||
}
|
||
})
|
||
|
||
|
||
|
||
class KefuUpdateDashouView(APIView):
|
||
"""
|
||
客服修改打手信息接口
|
||
支持操作:
|
||
- jian_yue / jia_yue : 减/加打手余额 (权限001aa/001bb)
|
||
- jian_yajin / jia_yajin : 减/加打手押金 (权限001aa/001bb)
|
||
- jian_jifen / jia_jifen : 减/加打手积分 (权限001cc/001dd)
|
||
- gai_zhanghaozhuangtai : 修改打手账号状态(封禁/解封) (权限001ee)
|
||
- gai_zaixianzhuangtai : 修改打手在线状态 (权限001ee)
|
||
- gai_zhuangtai : 修改打手接单状态(空闲/忙碌) (权限001ee)
|
||
- jia_huiyuan / jian_huiyuan : 加/减会员 (权限001ff/001gg)
|
||
- gai_chenghao : 修改打手称号(升级/降级金牌) (权限001hh)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
# 1. 获取前端参数
|
||
username = request.data.get('username', '').strip()
|
||
dashou_id = request.data.get('dashou_id', '').strip()
|
||
caozuo = request.data.get('caozuo', '').strip()
|
||
value = request.data.get('value') # 金额、积分、天数、称号等
|
||
huiyuan_id = request.data.get('huiyuan_id', '').strip()
|
||
|
||
if not username or not dashou_id or not caozuo:
|
||
return Response({'code': 400, 'msg': '参数不完整'})
|
||
|
||
# 2. 权限校验
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
|
||
# 3. 查询打手
|
||
try:
|
||
dashou_user = User.query.get(UserUID=dashou_id)
|
||
dashou_profile = dashou_user.DashouProfile
|
||
except User.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '打手不存在'})
|
||
except UserDashou.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '打手扩展信息缺失'})
|
||
|
||
with transaction.atomic():
|
||
try:
|
||
before = {
|
||
'yue': dashou_profile.yue,
|
||
'yajin': dashou_profile.yajin,
|
||
'jifen': dashou_profile.jifen,
|
||
'zhanghaozhuangtai': dashou_profile.zhanghaozhuangtai,
|
||
'zaixianzhuangtai': dashou_profile.zaixianzhuangtai,
|
||
'zhuangtai': dashou_profile.zhuangtai,
|
||
'chenghao': dashou_profile.chenghao,
|
||
}
|
||
|
||
# ---------- 余额操作 ----------
|
||
if caozuo == 'jian_yue':
|
||
if '001aa' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限减打手余额'})
|
||
amount = Decimal(str(value))
|
||
if dashou_profile.yue < amount:
|
||
return Response({'code': 400, 'msg': '余额不足'})
|
||
dashou_profile.yue -= amount
|
||
dashou_profile.save()
|
||
after = dashou_profile.yue
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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)}})
|
||
|
||
elif caozuo == 'jia_yue':
|
||
if '001bb' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限加打手余额'})
|
||
from jituan.services.group_finance_gate import deny_group_finance_exec
|
||
deny = deny_group_finance_exec(request.user, request)
|
||
if deny:
|
||
return deny
|
||
amount = Decimal(str(value))
|
||
dashou_profile.yue += amount
|
||
dashou_profile.save()
|
||
after = dashou_profile.yue
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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)}})
|
||
|
||
# ---------- 押金操作 ----------
|
||
elif caozuo == 'jian_yajin':
|
||
if '001aa' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限减打手押金'})
|
||
amount = Decimal(str(value))
|
||
if dashou_profile.yajin < amount:
|
||
return Response({'code': 400, 'msg': '押金不足'})
|
||
dashou_profile.yajin -= amount
|
||
dashou_profile.save()
|
||
after = dashou_profile.yajin
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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)}})
|
||
|
||
elif caozuo == 'jia_yajin':
|
||
if '001bb' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限加打手押金'})
|
||
from jituan.services.group_finance_gate import deny_group_finance_exec
|
||
deny = deny_group_finance_exec(request.user, request)
|
||
if deny:
|
||
return deny
|
||
amount = Decimal(str(value))
|
||
dashou_profile.yajin += amount
|
||
dashou_profile.save()
|
||
after = dashou_profile.yajin
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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)}})
|
||
|
||
# ---------- 积分操作 ----------
|
||
elif caozuo == 'jian_jifen':
|
||
if '001cc' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限减打手积分'})
|
||
jifen = int(value)
|
||
if dashou_profile.jifen < jifen:
|
||
return Response({'code': 400, 'msg': '积分不足'})
|
||
dashou_profile.jifen -= jifen
|
||
dashou_profile.save()
|
||
after = dashou_profile.jifen
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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}})
|
||
|
||
elif caozuo == 'jia_jifen':
|
||
if '001dd' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限加打手积分'})
|
||
jifen = int(value)
|
||
dashou_profile.jifen += jifen
|
||
dashou_profile.save()
|
||
after = dashou_profile.jifen
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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}})
|
||
|
||
# ---------- 状态修改 ----------
|
||
elif caozuo == 'gai_zhanghaozhuangtai':
|
||
if '001ee' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限修改打手账号状态'})
|
||
new_status = int(value)
|
||
if new_status not in [0, 1]:
|
||
return Response({'code': 400, 'msg': '状态值无效(0=封禁,1=正常)'})
|
||
dashou_profile.zhanghaozhuangtai = new_status
|
||
dashou_profile.save()
|
||
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=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}})
|
||
|
||
elif caozuo == 'gai_zaixianzhuangtai':
|
||
if '001ee' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限修改打手在线状态'})
|
||
new_status = int(value)
|
||
if new_status not in [0, 1]:
|
||
return Response({'code': 400, 'msg': '状态值无效(0=离线,1=在线)'})
|
||
dashou_profile.zaixianzhuangtai = new_status
|
||
dashou_profile.save()
|
||
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=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}})
|
||
|
||
elif caozuo == 'gai_zhuangtai':
|
||
if '001ee' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限修改打手接单状态'})
|
||
new_status = int(value)
|
||
if new_status not in [0, 1]:
|
||
return Response({'code': 400, 'msg': '状态值无效(0=忙碌,1=空闲)'})
|
||
dashou_profile.zhuangtai = new_status
|
||
dashou_profile.save()
|
||
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=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}})
|
||
|
||
# ---------- 会员操作 ----------
|
||
elif caozuo == 'jia_huiyuan':
|
||
if '001ff' not in permissions and '000001' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限给打手加会员'})
|
||
if not huiyuan_id:
|
||
return Response({'code': 400, 'msg': '缺少会员ID'})
|
||
try:
|
||
huiyuan = Huiyuan.query.get(huiyuan_id=huiyuan_id)
|
||
except Huiyuan.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '会员类型不存在'})
|
||
from jituan.services.club_user import get_user_club_id
|
||
from jituan.services.member_recharge import extend_member_entitlement_admin, get_club_huiyuan_row
|
||
player_club = get_user_club_id(dashou_user)
|
||
row = get_club_huiyuan_row(player_club, huiyuan_id, require_enabled=False)
|
||
days = int(value) if value else int(row.formal_days or 30) if row else 30
|
||
record, err = extend_member_entitlement_admin(
|
||
dashou_id, huiyuan_id, days, player_club,
|
||
)
|
||
if err:
|
||
return Response({'code': 400, 'msg': err})
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
leixing=XIUGAI_LEIXING_DASHOU,
|
||
huiyuants=days,
|
||
huiyuan_id=huiyuan_id,
|
||
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')}})
|
||
|
||
elif caozuo == 'jian_huiyuan':
|
||
if '001gg' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限给打手减会员'})
|
||
if not huiyuan_id:
|
||
return Response({'code': 400, 'msg': '缺少会员ID'})
|
||
try:
|
||
record = Huiyuangoumai.query.get(yonghu_id=dashou_id, huiyuan_id=huiyuan_id)
|
||
except Huiyuangoumai.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '该打手未购买此会员'})
|
||
record.delete()
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
leixing=XIUGAI_LEIXING_DASHOU,
|
||
huiyuan_id=huiyuan_id,
|
||
qitashuoming=(
|
||
f'【后台】移除打手会员:会员ID={huiyuan_id},'
|
||
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
|
||
),
|
||
)
|
||
return Response({'code': 0, 'msg': '减会员成功'})
|
||
|
||
# ---------- 称号修改(金牌打手) ----------
|
||
elif caozuo == 'gai_chenghao':
|
||
if '001hh' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限修改打手称号'})
|
||
new_chenghao = str(value).strip()
|
||
if not new_chenghao:
|
||
return Response({'code': 400, 'msg': '称号不能为空'})
|
||
# 可限制合法值,这里只记录日志
|
||
dashou_profile.chenghao = new_chenghao
|
||
dashou_profile.save()
|
||
write_xiugai_log(
|
||
yonghuid=dashou_id,
|
||
xiugaiid=kefu.user.Phone,
|
||
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}})
|
||
|
||
else:
|
||
return Response({'code': 400, 'msg': '未知操作类型'})
|
||
|
||
except Exception as e:
|
||
logger.error(f"修改打手信息异常: {str(e)}", exc_info=True)
|
||
return Response({'code': 500, 'msg': '服务器内部错误'})
|
||
|
||
|
||
|
||
|
||
|
||
|
||
class KefuGetShangjiaListView(APIView):
|
||
"""
|
||
客服/管理员获取商家列表接口
|
||
需要拥有以下任一权限:1100a, 1100b
|
||
请求:POST /houtai/hqsjgllb
|
||
参数:
|
||
username: 客服手机号(用于权限校验,必填)
|
||
keyword: 搜索关键词(可选,搜索商家用户ID或昵称)
|
||
balance_op: 余额比较符,gte(>=) / lte(<=)(可选)
|
||
balance_value: 余额数值(可选,需与balance_op同时存在)
|
||
order_count_op: 派单量比较符,gte(>=) / lte(<=)(可选)
|
||
order_count_value: 派单量数值(可选,需与order_count_op同时存在)
|
||
status: 商家状态,1=正常,2=禁用(可选)
|
||
page: 页码,从1开始,默认1
|
||
page_size: 每页数量,默认20
|
||
返回:
|
||
code: 0 成功,其他失败
|
||
data: {
|
||
list: [商家对象],
|
||
total: int, // 总商家数
|
||
}
|
||
"""
|
||
permission_classes = [] # 不依赖全局认证,手动校验
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
# 1. 获取参数
|
||
username = request.data.get('username', '').strip()
|
||
keyword = request.data.get('keyword', '').strip()
|
||
balance_op = request.data.get('balance_op', '').strip().lower()
|
||
balance_value_str = request.data.get('balance_value')
|
||
order_count_op = request.data.get('order_count_op', '').strip().lower()
|
||
order_count_value_str = request.data.get('order_count_value')
|
||
status_str = request.data.get('status')
|
||
|
||
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 ValueError:
|
||
return Response({'code': 400, 'msg': '分页参数错误'})
|
||
|
||
if not username:
|
||
return Response({'code': 401, 'msg': '认证失败,缺少username'})
|
||
|
||
# 2. 公共权限校验(复用现有函数 verify_kefu_permission)
|
||
# 假设 verify_kefu_permission 定义在某个公共模块,这里引入
|
||
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions # 直接返回错误响应
|
||
|
||
# 3. 检查商家管理权限(1100a 或 1100b)
|
||
required_perms = {'1100a', '1100b'}
|
||
if not any(p in permissions for p in required_perms):
|
||
return Response({'code': 403, 'msg': '您没有权限查看商家列表'})
|
||
|
||
# 4. 构建商家查询集(商家扩展表 + 关联用户主表)
|
||
shangjia_qs = filter_user_related_by_club(
|
||
UserShangjia.query.select_related('user').all(), request,
|
||
)
|
||
|
||
# 关键词筛选(用户ID或昵称)
|
||
if keyword:
|
||
shangjia_qs = shangjia_qs.filter(
|
||
Q(user__UserUID__icontains=keyword) |
|
||
Q(nicheng__icontains=keyword)
|
||
)
|
||
|
||
# 余额筛选
|
||
if balance_op and balance_value_str is not None and balance_value_str != '':
|
||
try:
|
||
balance_value = Decimal(str(balance_value_str))
|
||
if balance_op == 'gte':
|
||
shangjia_qs = shangjia_qs.filter(yue__gte=balance_value)
|
||
elif balance_op == 'lte':
|
||
shangjia_qs = shangjia_qs.filter(yue__lte=balance_value)
|
||
else:
|
||
return Response({'code': 400, 'msg': '余额比较符只支持gte或lte'})
|
||
except Exception:
|
||
return Response({'code': 400, 'msg': '余额数值格式错误'})
|
||
|
||
# 派单总量筛选(fabu字段)
|
||
if order_count_op and order_count_value_str is not None and order_count_value_str != '':
|
||
try:
|
||
order_count_value = int(order_count_value_str)
|
||
if order_count_op == 'gte':
|
||
shangjia_qs = shangjia_qs.filter(fabu__gte=order_count_value)
|
||
elif order_count_op == 'lte':
|
||
shangjia_qs = shangjia_qs.filter(fabu__lte=order_count_value)
|
||
else:
|
||
return Response({'code': 400, 'msg': '派单量比较符只支持gte或lte'})
|
||
except Exception:
|
||
return Response({'code': 400, 'msg': '派单量数值必须为整数'})
|
||
|
||
# 商家状态筛选
|
||
if status_str is not None and status_str != '':
|
||
try:
|
||
status_val = int(status_str)
|
||
if status_val not in [1, 2]:
|
||
return Response({'code': 400, 'msg': '商家状态值无效,应为1或2'})
|
||
shangjia_qs = shangjia_qs.filter(zhuangtai=status_val)
|
||
except ValueError:
|
||
return Response({'code': 400, 'msg': '商家状态格式错误'})
|
||
|
||
# 5. 统计总商家数(应用筛选后)
|
||
total = shangjia_qs.count()
|
||
valid_merchant_count = shangjia_qs.filter(zhuangtai=1).count()
|
||
|
||
# 6. 分页
|
||
offset = (page - 1) * page_size
|
||
shangjia_list = shangjia_qs.order_by('-CreateTime')[offset:offset + page_size]
|
||
|
||
# 7. 构建返回数据
|
||
result = []
|
||
for shangjia in shangjia_list:
|
||
user = shangjia.user
|
||
result.append({
|
||
'yonghuid': user.UserUID,
|
||
'avatar': user.Avatar or '',
|
||
'nicheng': shangjia.nicheng or '',
|
||
'yue': str(shangjia.yue), # 转换为字符串,避免前端精度问题
|
||
'fabu': shangjia.fabu,
|
||
'chengjiao': shangjia.chengjiao,
|
||
'zhuangtai': shangjia.zhuangtai, # 1=正常,2=禁用
|
||
})
|
||
|
||
meta = list_response_meta(request)
|
||
return Response({
|
||
'code': 0,
|
||
'club_id': meta['club_id'],
|
||
'scope': meta['scope'],
|
||
'data': {
|
||
'list': result,
|
||
'total': total,
|
||
'valid_merchant_count': valid_merchant_count,
|
||
}
|
||
})
|
||
|
||
|
||
|
||
|
||
|
||
|
||
# yonghu/views.py 中添加
|
||
|
||
class KefuGetShangjiaDetailView(APIView):
|
||
"""
|
||
客服/管理员获取商家详情接口
|
||
需要权限:1100a 或 1100b
|
||
请求:POST /houtai/hthqsjxq
|
||
参数:
|
||
username: 客服手机号(用于权限校验,必填)
|
||
yonghuid: 商家用户ID(必填)
|
||
返回:
|
||
code: 0 成功
|
||
data: 商家所有字段(含主表头像等)
|
||
"""
|
||
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 = {'1100a', '1100b'}
|
||
if not any(p in permissions for p in required_perms):
|
||
return Response({'code': 403, 'msg': '您没有权限查看商家详情'})
|
||
|
||
# 查询商家
|
||
try:
|
||
shangjia = UserShangjia.query.select_related('user').get(user__UserUID=yonghuid)
|
||
except UserShangjia.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '商家不存在'})
|
||
|
||
denied = forbid_if_user_out_of_scope(request, shangjia.user)
|
||
if denied:
|
||
return denied
|
||
|
||
user = shangjia.user
|
||
|
||
data = {
|
||
'yonghuid': user.UserUID,
|
||
'avatar': user.Avatar or '',
|
||
'nicheng': shangjia.nicheng,
|
||
'zhuangtai': shangjia.zhuangtai, # 1正常,2禁用
|
||
'dianhua': shangjia.dianhua,
|
||
'wechat': shangjia.wechat,
|
||
'yue': str(shangjia.yue),
|
||
'fabu': shangjia.fabu,
|
||
'chengjiao': shangjia.chengjiao,
|
||
'tuikuan': shangjia.tuikuan,
|
||
'jinridingdan': shangjia.jinridingdan,
|
||
'jinriliushui': str(shangjia.jinriliushui),
|
||
'jinyuedingdan': shangjia.jinyuedingdan,
|
||
'jinyueliushui': str(shangjia.jinyueliushui),
|
||
'is_youzhi_shangjia': bool(shangjia.is_youzhi_shangjia),
|
||
'CreateTime': shangjia.CreateTime.isoformat() if shangjia.CreateTime else None,
|
||
'create_time': user.UserCreateTime.strftime('%Y-%m-%d %H:%M:%S') if getattr(user, 'UserCreateTime', None) else '',
|
||
}
|
||
|
||
return Response({'code': 0, 'data': data})
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
class KefuUpdateShangjiaView(APIView):
|
||
"""
|
||
客服/管理员修改商家信息(余额、状态)
|
||
POST /houtai/xgsjxx
|
||
参数:
|
||
username, yonghuid, action
|
||
action可选:add_balance, sub_balance, ban, unban
|
||
amount(add_balance/sub_balance时必填)
|
||
"""
|
||
permission_classes = []
|
||
parser_classes = [JSONParser]
|
||
|
||
def post(self, request):
|
||
username = request.data.get('username', '').strip()
|
||
yonghuid = request.data.get('yonghuid', '').strip()
|
||
action = request.data.get('action', '').strip().lower()
|
||
|
||
if not username:
|
||
return Response({'code': 401, 'msg': '缺少username'})
|
||
if not yonghuid:
|
||
return Response({'code': 400, 'msg': '缺少商家ID'})
|
||
if action not in ('add_balance', 'sub_balance', 'ban', 'unban', 'set_youzhi'):
|
||
return Response({'code': 400, 'msg': 'action参数无效'})
|
||
|
||
# 权限校验
|
||
kefu, permissions = verify_kefu_permission(request, username)
|
||
if kefu is None:
|
||
return permissions
|
||
|
||
# 操作权限校验
|
||
if action in ('add_balance', 'ban', 'unban', 'set_youzhi') and '1100a' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限执行此操作(需要1100a)'})
|
||
if action == 'sub_balance' and '1100b' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限减少商家余额(需要1100b)'})
|
||
|
||
# 使用事务,select_for_update 必须在事务内
|
||
with transaction.atomic():
|
||
try:
|
||
shangjia = UserShangjia.objects.select_for_update().get(user__UserUID=yonghuid)
|
||
except UserShangjia.DoesNotExist:
|
||
return Response({'code': 404, 'msg': '商家不存在'})
|
||
|
||
# 增加余额
|
||
if action == 'add_balance':
|
||
from jituan.services.group_finance_gate import deny_group_finance_exec
|
||
deny = deny_group_finance_exec(request.user, request)
|
||
if deny:
|
||
return deny
|
||
amount_str = request.data.get('amount')
|
||
try:
|
||
amount = Decimal(str(amount_str))
|
||
if amount <= 0 or amount > 1000000:
|
||
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)}})
|
||
|
||
# 减少余额
|
||
if action == 'sub_balance':
|
||
amount_str = request.data.get('amount')
|
||
try:
|
||
amount = Decimal(str(amount_str))
|
||
if amount <= 0 or amount > 1000000:
|
||
return Response({'code': 400, 'msg': '金额需在0.01~1000000之间'})
|
||
except:
|
||
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)}})
|
||
|
||
# 封禁
|
||
if action == 'ban':
|
||
if shangjia.zhuangtai == 2:
|
||
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}})
|
||
|
||
# 解封
|
||
if action == 'unban':
|
||
if shangjia.zhuangtai == 1:
|
||
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}})
|
||
|
||
if action == 'set_youzhi':
|
||
if '1100a' not in permissions:
|
||
return Response({'code': 403, 'msg': '无权限设置优质商家(需要1100a)'})
|
||
is_youzhi = bool(request.data.get('is_youzhi_shangjia', False))
|
||
shangjia.is_youzhi_shangjia = is_youzhi
|
||
shangjia.save(update_fields=['is_youzhi_shangjia'])
|
||
write_xiugai_log(
|
||
yonghuid=yonghuid,
|
||
xiugaiid=kefu.user.Phone,
|
||
leixing=XIUGAI_LEIXING_SHANGJIA,
|
||
qitashuoming=(
|
||
f'【后台】设置优质商家:{is_youzhi},'
|
||
f'商家ID={yonghuid},昵称={shangjia.nicheng},操作人={kefu.user.Phone}'
|
||
),
|
||
)
|
||
return Response({
|
||
'code': 0,
|
||
'msg': '优质商家设置成功',
|
||
'data': {'is_youzhi_shangjia': is_youzhi},
|
||
})
|
||
|
||
return Response({'code': 400, 'msg': '未知操作'})
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
class KefuChatPermissionsView(APIView):
|
||
"""
|
||
获取客服聊天权限及GoEasy配置
|
||
POST /houtai/kfhqltqx
|
||
Body: { "phone": "客服手机号" }
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
# 1. 从前端获取传递的账号,用于防越权校验
|
||
username_from_frontend = request.data.get('phone', None)
|
||
|
||
# 2. 调用公共验证方法。
|
||
# 返回 (kefu_obj, permissions) 或 (None, Response)
|
||
kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
|
||
|
||
# 3. 如果第一个返回值为 None,说明验证失败,直接返回第二个值(错误Response)
|
||
if kefu_obj is None:
|
||
return permissions
|
||
|
||
# ---------- 验证通过,permissions 已经是权限码列表 ----------
|
||
# 4. 检查是否拥有至少一个聊天权限
|
||
has_chat_perm = any(p in CHAT_PERM_CODES for p in permissions)
|
||
|
||
# 5. 若有聊天权限,获取 GoEasy AppKey
|
||
goeasy_appkey = ''
|
||
if has_chat_perm:
|
||
goeasy_appkey = getattr(settings, 'GOEASY_APPKEY', '')
|
||
|
||
# 6. 返回数据
|
||
return Response({
|
||
'code': 0,
|
||
'msg': 'ok',
|
||
'data': {
|
||
'permissions': permissions,
|
||
'goeasy_appkey': goeasy_appkey
|
||
}
|
||
})
|
||
|
||
|
||
|
||
|
||
|