feat: 打手列表支持会员押金统计与多维筛选

This commit is contained in:
XingQue
2026-07-29 18:53:18 +08:00
parent fd2163f96a
commit e2bfed75fa

View File

@@ -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,
} }
}) })