feat: 打手列表支持会员押金统计与多维筛选
This commit is contained in:
@@ -124,90 +124,182 @@ class KefuGetDashouListView(APIView):
|
|||||||
"""
|
"""
|
||||||
客服获取打手列表接口(仅打手类型)
|
客服获取打手列表接口(仅打手类型)
|
||||||
需要拥有以下任一权限:001aa,001bb,001cc,001dd,001ee,001ff,001gg
|
需要拥有以下任一权限:001aa,001bb,001cc,001dd,001ee,001ff,001gg
|
||||||
请求:POST /yonghu/kefuhqdslb
|
请求:POST /jituan/houtai/kefuhqdslb
|
||||||
参数:
|
参数:
|
||||||
username: 客服手机号(用于权限校验)
|
username: 客服手机号(用于权限校验)
|
||||||
keyword: 搜索关键词(可选,搜索用户ID或昵称)
|
keyword: 用户ID / 昵称 / 手机号(可选)
|
||||||
page: 页码(从1开始)
|
zhanghaozhuangtai: 1正常 / 0封禁(可选)
|
||||||
page_size: 每页数量
|
has_member: 1有有效会员 / 0无(可选)
|
||||||
返回:
|
huiyuan_id: 限定有效会员含该类型(可选)
|
||||||
code: 0
|
yajin_op + yajin_value: gte/lte/eq + 数值(可选)
|
||||||
data: {
|
zaixianzhuangtai / zhuangtai: 在线 / 空闲接单(可选)
|
||||||
list: [打手对象],
|
page / page_size: 分页
|
||||||
has_more: bool,
|
|
||||||
total: int, // 总打手数
|
|
||||||
valid_dashou_count: int // 有效打手数(有有效会员)
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
parser_classes = [JSONParser]
|
parser_classes = [JSONParser]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
# 1. 获取参数(前端字段为 username)
|
username = (request.data.get('username') or '').strip()
|
||||||
username = request.data.get('username', '').strip()
|
keyword = (request.data.get('keyword') or '').strip()
|
||||||
keyword = request.data.get('keyword', '').strip()
|
huiyuan_id = (request.data.get('huiyuan_id') or '').strip()
|
||||||
|
yajin_op = (request.data.get('yajin_op') or '').strip().lower()
|
||||||
|
yajin_value_raw = request.data.get('yajin_value', None)
|
||||||
try:
|
try:
|
||||||
page = int(request.data.get('page', 1))
|
page = int(request.data.get('page', 1))
|
||||||
page_size = int(request.data.get('page_size', 20))
|
page_size = int(request.data.get('page_size', 20))
|
||||||
except ValueError:
|
except (TypeError, ValueError):
|
||||||
return Response({'code': 400, 'msg': '分页参数错误'})
|
return Response({'code': 400, 'msg': '分页参数错误'})
|
||||||
|
|
||||||
if not username:
|
if not username:
|
||||||
return Response({'code': 401, 'msg': '认证失败'})
|
return Response({'code': 401, 'msg': '认证失败'})
|
||||||
|
|
||||||
# 2. 公共权限校验(验证客服身份,获取权限列表)
|
|
||||||
kefu, permissions = verify_kefu_permission(request, username)
|
kefu, permissions = verify_kefu_permission(request, username)
|
||||||
if kefu is None:
|
if kefu is None:
|
||||||
return permissions # 直接返回错误响应
|
return permissions
|
||||||
|
|
||||||
# 3. 检查是否拥有打手管理相关权限(至少一个)
|
required_perms = ('001aa', '001bb', '001cc', '001dd', '001ee', '001ff', '001gg')
|
||||||
required_perms = {'001aa', '001bb', '001cc', '001dd', '001ee', '001ff', '001gg'}
|
if not has_perm_code(permissions, *required_perms):
|
||||||
if not any(p in permissions for p in required_perms):
|
|
||||||
return Response({'code': 403, 'msg': '您没有权限查看打手列表'})
|
return Response({'code': 403, 'msg': '您没有权限查看打手列表'})
|
||||||
|
|
||||||
# 4. 构建打手查询集(仅打手类型)
|
|
||||||
dashou_qs = filter_user_related_by_club(
|
dashou_qs = filter_user_related_by_club(
|
||||||
UserDashou.query.select_related('user').all(), request,
|
UserDashou.query.select_related('user').all(), request,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 关键词筛选(用户ID或昵称)
|
|
||||||
if keyword:
|
if keyword:
|
||||||
dashou_qs = dashou_qs.filter(
|
dashou_qs = dashou_qs.filter(
|
||||||
Q(user__UserUID__icontains=keyword) |
|
Q(user__UserUID__icontains=keyword) |
|
||||||
Q(nicheng__icontains=keyword)
|
Q(nicheng__icontains=keyword) |
|
||||||
|
Q(user__Phone__icontains=keyword)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. 统计打手总数
|
zhanghao_raw = request.data.get('zhanghaozhuangtai', None)
|
||||||
total_dashou = dashou_qs.count()
|
if zhanghao_raw is not None and zhanghao_raw != '':
|
||||||
|
try:
|
||||||
|
zhanghao_val = int(zhanghao_raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return Response({'code': 400, 'msg': '账号状态格式错误'})
|
||||||
|
if zhanghao_val not in (0, 1):
|
||||||
|
return Response({'code': 400, 'msg': '账号状态值无效'})
|
||||||
|
dashou_qs = dashou_qs.filter(zhanghaozhuangtai=zhanghao_val)
|
||||||
|
|
||||||
|
zaixian_raw = request.data.get('zaixianzhuangtai', None)
|
||||||
|
if zaixian_raw is not None and zaixian_raw != '':
|
||||||
|
try:
|
||||||
|
zaixian_val = int(zaixian_raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return Response({'code': 400, 'msg': '在线状态格式错误'})
|
||||||
|
if zaixian_val not in (0, 1):
|
||||||
|
return Response({'code': 400, 'msg': '在线状态值无效'})
|
||||||
|
dashou_qs = dashou_qs.filter(zaixianzhuangtai=zaixian_val)
|
||||||
|
|
||||||
|
zhuangtai_raw = request.data.get('zhuangtai', None)
|
||||||
|
if zhuangtai_raw is not None and zhuangtai_raw != '':
|
||||||
|
try:
|
||||||
|
zhuangtai_val = int(zhuangtai_raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return Response({'code': 400, 'msg': '接单状态格式错误'})
|
||||||
|
if zhuangtai_val not in (0, 1):
|
||||||
|
return Response({'code': 400, 'msg': '接单状态值无效'})
|
||||||
|
dashou_qs = dashou_qs.filter(zhuangtai=zhuangtai_val)
|
||||||
|
|
||||||
# 6. 统计有效打手数(有会员且未过期)
|
|
||||||
dashou_user_ids = dashou_qs.values_list('user__UserUID', flat=True)
|
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
hy_qs = filter_queryset_by_club(Huiyuangoumai.query.all(), request)
|
active_hy_base = filter_queryset_by_club(Huiyuangoumai.query.all(), request).filter(
|
||||||
valid_dashou_count = hy_qs.filter(
|
|
||||||
yonghu_id__in=dashou_user_ids,
|
|
||||||
huiyuan_zhuangtai=1,
|
huiyuan_zhuangtai=1,
|
||||||
daoqi_time__gt=now,
|
daoqi_time__gt=now,
|
||||||
|
)
|
||||||
|
active_hy_for_filter = active_hy_base
|
||||||
|
if huiyuan_id:
|
||||||
|
active_hy_for_filter = active_hy_for_filter.filter(huiyuan_id=huiyuan_id)
|
||||||
|
active_member_uids = active_hy_for_filter.values('yonghu_id').distinct()
|
||||||
|
|
||||||
|
has_member_raw = request.data.get('has_member', None)
|
||||||
|
if has_member_raw is not None and has_member_raw != '':
|
||||||
|
try:
|
||||||
|
has_member_val = int(has_member_raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return Response({'code': 400, 'msg': '会员筛选格式错误'})
|
||||||
|
if has_member_val == 1:
|
||||||
|
dashou_qs = dashou_qs.filter(user__UserUID__in=active_member_uids)
|
||||||
|
elif has_member_val == 0:
|
||||||
|
any_member_uids = active_hy_base.values('yonghu_id').distinct()
|
||||||
|
dashou_qs = dashou_qs.exclude(user__UserUID__in=any_member_uids)
|
||||||
|
else:
|
||||||
|
return Response({'code': 400, 'msg': '会员筛选值无效'})
|
||||||
|
elif huiyuan_id:
|
||||||
|
dashou_qs = dashou_qs.filter(user__UserUID__in=active_member_uids)
|
||||||
|
|
||||||
|
# 押金≥阈值人数:在应用押金比较筛之前统计(相对其它已选条件)
|
||||||
|
yajin_gte_count = None
|
||||||
|
yajin_value = None
|
||||||
|
if yajin_value_raw is not None and yajin_value_raw != '':
|
||||||
|
try:
|
||||||
|
yajin_value = Decimal(str(yajin_value_raw))
|
||||||
|
except Exception:
|
||||||
|
return Response({'code': 400, 'msg': '押金数值格式错误'})
|
||||||
|
yajin_gte_count = dashou_qs.filter(yajin__gte=yajin_value).count()
|
||||||
|
op = yajin_op if yajin_op in ('gte', 'lte', 'eq') else 'gte'
|
||||||
|
if op == 'gte':
|
||||||
|
dashou_qs = dashou_qs.filter(yajin__gte=yajin_value)
|
||||||
|
elif op == 'lte':
|
||||||
|
dashou_qs = dashou_qs.filter(yajin__lte=yajin_value)
|
||||||
|
else:
|
||||||
|
dashou_qs = dashou_qs.filter(yajin=yajin_value)
|
||||||
|
|
||||||
|
filtered_uids_subq = dashou_qs.values('user__UserUID')
|
||||||
|
total_dashou = dashou_qs.count()
|
||||||
|
_status_agg = dashou_qs.aggregate(
|
||||||
|
normal_count=Count('id', filter=Q(zhanghaozhuangtai=1)),
|
||||||
|
banned_count=Count('id', filter=Q(zhanghaozhuangtai=0)),
|
||||||
|
)
|
||||||
|
valid_dashou_count = active_hy_base.filter(
|
||||||
|
yonghu_id__in=filtered_uids_subq,
|
||||||
).values('yonghu_id').distinct().count()
|
).values('yonghu_id').distinct().count()
|
||||||
|
no_member_count = max(total_dashou - valid_dashou_count, 0)
|
||||||
|
|
||||||
# 7. 分页
|
|
||||||
offset = (page - 1) * page_size
|
offset = (page - 1) * page_size
|
||||||
dashou_list = dashou_qs.order_by('-CreateTime')[offset:offset + page_size]
|
dashou_list = list(dashou_qs.order_by('-CreateTime')[offset:offset + page_size])
|
||||||
|
page_uids = [d.user.UserUID for d in dashou_list if getattr(d, 'user', None)]
|
||||||
|
|
||||||
|
hy_by_uid = {}
|
||||||
|
if page_uids:
|
||||||
|
page_hy = active_hy_base.filter(yonghu_id__in=page_uids).values(
|
||||||
|
'yonghu_id', 'huiyuan_id', 'jieshao',
|
||||||
|
)
|
||||||
|
name_map = {
|
||||||
|
h['huiyuan_id']: (h['jieshao'] or '')
|
||||||
|
for h in Huiyuan.query.filter(
|
||||||
|
huiyuan_id__in={r['huiyuan_id'] for r in page_hy}
|
||||||
|
).values('huiyuan_id', 'jieshao')
|
||||||
|
}
|
||||||
|
for row in page_hy:
|
||||||
|
uid = row['yonghu_id']
|
||||||
|
name = (row.get('jieshao') or '').strip() or name_map.get(row['huiyuan_id'], '')
|
||||||
|
hy_by_uid.setdefault(uid, []).append({
|
||||||
|
'huiyuan_id': row['huiyuan_id'],
|
||||||
|
'name': name or row['huiyuan_id'],
|
||||||
|
})
|
||||||
|
|
||||||
# 8. 构建返回数据
|
|
||||||
result = []
|
result = []
|
||||||
for dashou in dashou_list:
|
for dashou in dashou_list:
|
||||||
user = dashou.user
|
user = dashou.user
|
||||||
|
uid = user.UserUID
|
||||||
result.append({
|
result.append({
|
||||||
'yonghuid': user.UserUID,
|
'yonghuid': uid,
|
||||||
'avatar': user.Avatar or '',
|
'avatar': user.Avatar or '',
|
||||||
|
'phone': user.Phone or '',
|
||||||
|
'club_id': getattr(user, 'ClubID', None) or '',
|
||||||
'zaixianzhuangtai': dashou.zaixianzhuangtai,
|
'zaixianzhuangtai': dashou.zaixianzhuangtai,
|
||||||
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
|
'zhanghaozhuangtai': dashou.zhanghaozhuangtai,
|
||||||
'nicheng': dashou.nicheng or '',
|
'nicheng': dashou.nicheng or '',
|
||||||
'zhuangtai': dashou.zhuangtai,
|
'zhuangtai': dashou.zhuangtai,
|
||||||
|
'yajin': str(dashou.yajin if dashou.yajin is not None else '0'),
|
||||||
|
'huiyuan_list': hy_by_uid.get(uid, []),
|
||||||
})
|
})
|
||||||
|
|
||||||
has_more = (offset + len(dashou_list)) < total_dashou
|
has_more = (offset + len(dashou_list)) < total_dashou
|
||||||
|
huiyuan_options = list(
|
||||||
|
Huiyuan.query.all().order_by('huiyuan_id').values('huiyuan_id', 'jieshao')
|
||||||
|
)
|
||||||
|
|
||||||
meta = list_response_meta(request)
|
meta = list_response_meta(request)
|
||||||
return Response({
|
return Response({
|
||||||
@@ -219,6 +311,11 @@ class KefuGetDashouListView(APIView):
|
|||||||
'has_more': has_more,
|
'has_more': has_more,
|
||||||
'total': total_dashou,
|
'total': total_dashou,
|
||||||
'valid_dashou_count': valid_dashou_count,
|
'valid_dashou_count': valid_dashou_count,
|
||||||
|
'no_member_count': no_member_count,
|
||||||
|
'normal_count': _status_agg['normal_count'] or 0,
|
||||||
|
'banned_count': _status_agg['banned_count'] or 0,
|
||||||
|
'yajin_gte_count': yajin_gte_count,
|
||||||
|
'huiyuan_options': huiyuan_options,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user