Files
Django/orders/views/zudui.py
2026-07-27 05:04:42 +08:00

340 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""组队 API/zudui/*"""
from __future__ import annotations
import logging
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from jituan.services.club_context import resolve_club_id_from_request
from orders.models import Order, TeamRecruit, TeamMember
from orders.services import team_recruit as team_svc
logger = logging.getLogger(__name__)
def _club(request):
return (resolve_club_id_from_request(request) or '').strip()
def _uid(request):
return getattr(request.user, 'UserUID', None) or ''
def _require_club(request):
club = _club(request)
if not club:
return None, Response({'code': 400, 'msg': '缺少俱乐部'})
return club, None
def _assert_recruit_same_club(rec, club):
"""组队永不走跨店互通:帖/订单/当前俱乐部必须一致。"""
if not rec:
return Response({'code': 404, 'msg': '组队不存在'})
if (rec.club_id or '') != club:
return Response({'code': 403, 'msg': '仅本俱乐部可操作组队'})
order = Order.query.filter(OrderID=rec.order_id).first()
if not order:
return Response({'code': 404, 'msg': '订单不存在'})
if (order.ClubID or '') != club:
return Response({'code': 403, 'msg': '订单不属于本俱乐部'})
return None
class ZuduiKaqiView(APIView):
"""开启组队 POST /zudui/kaqi仅本俱乐部订单与跨店抢单无关"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
uid = _uid(request)
club, err = _require_club(request)
if err:
return err
oid = str(request.data.get('dingdan_id') or request.data.get('order_id') or '').strip()
if not oid:
return Response({'code': 400, 'msg': '缺少订单ID'})
order = Order.query.filter(OrderID=oid).first()
if not order:
return Response({'code': 404, 'msg': '订单不存在'})
if (order.ClubID or '') != club:
return Response({'code': 403, 'msg': '仅本俱乐部订单可开启组队'})
try:
rec = team_svc.open_recruit(
order=order,
leader_uid=uid,
need_count=int(request.data.get('need_count') or 1),
share_mode=request.data.get('share_mode') or 'fixed',
per_person_amount=request.data.get('per_person_amount'),
remark=request.data.get('remark') or '',
leader_game_id=request.data.get('leader_game_id') or request.data.get('game_id') or '',
min_deal_rate=request.data.get('min_deal_rate'),
max_fine_rate=request.data.get('max_fine_rate'),
)
return Response({
'code': 0,
'msg': '已开启组队',
'data': team_svc.serialize_recruit_public(rec, order, include_game_ids=True),
})
except ValueError as e:
return Response({'code': 400, 'msg': str(e)})
except Exception as e:
logger.exception('开启组队失败')
return Response({'code': 500, 'msg': '服务器错误'})
class ZuduiGaiyaoqiuView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
uid = _uid(request)
club, err = _require_club(request)
if err:
return err
rid = request.data.get('recruit_id')
try:
rid = int(rid)
except (TypeError, ValueError):
return Response({'code': 400, 'msg': '缺少 recruit_id'})
rec = TeamRecruit.query.filter(id=rid).first()
deny = _assert_recruit_same_club(rec, club)
if deny:
return deny
try:
kw = {'recruit_id': rid, 'leader_uid': uid}
if 'need_count' in request.data:
kw['need_count'] = request.data.get('need_count')
if 'remark' in request.data:
kw['remark'] = request.data.get('remark')
if 'min_deal_rate' in request.data:
kw['min_deal_rate'] = request.data.get('min_deal_rate')
if 'max_fine_rate' in request.data:
kw['max_fine_rate'] = request.data.get('max_fine_rate')
rec = team_svc.update_recruit(**kw)
order = Order.query.filter(OrderID=rec.order_id).first()
return Response({
'code': 0,
'msg': '已更新',
'data': team_svc.serialize_recruit_public(rec, order, include_game_ids=True),
})
except ValueError as e:
return Response({'code': 400, 'msg': str(e)})
class ZuduiGuantingView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
uid = _uid(request)
club, err = _require_club(request)
if err:
return err
rid = request.data.get('recruit_id')
try:
rid = int(rid)
except (TypeError, ValueError):
return Response({'code': 400, 'msg': '缺少 recruit_id'})
rec = TeamRecruit.query.filter(id=rid).first()
deny = _assert_recruit_same_club(rec, club)
if deny:
return deny
if rec.leader_uid != uid:
return Response({'code': 403, 'msg': '仅队长可关停'})
try:
rec = team_svc.lock_recruit(rid, actor_uid=uid, reason='manual')
order = Order.query.filter(OrderID=rec.order_id).first()
return Response({
'code': 0,
'msg': '已关停招募',
'data': team_svc.serialize_recruit_public(rec, order, include_game_ids=True),
})
except ValueError as e:
return Response({'code': 400, 'msg': str(e)})
class ZuduiDatingView(APIView):
"""组队大厅:仅本俱乐部招募中(不读跨店互通)。"""
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
club, err = _require_club(request)
if err:
return err
page = max(1, int(request.data.get('page') or 1))
page_size = min(50, max(1, int(request.data.get('page_size') or 10)))
leixing = request.data.get('leixing_id')
pingtai = request.data.get('fadanpingtai')
qs = TeamRecruit.query.filter(
club_id=club,
status=TeamRecruit.STATUS_RECRUITING,
).order_by('-opened_at')
# 商品类型必选过滤(与前端大厅一致)
if leixing not in (None, ''):
try:
lid = int(leixing)
except (TypeError, ValueError):
return Response({'code': 400, 'msg': '商品类型无效'})
oq = Order.query.filter(ClubID=club, Status=2, ProductTypeID=lid)
if pingtai not in (None, ''):
try:
oq = oq.filter(Platform=int(pingtai))
except (TypeError, ValueError):
pass
order_ids = list(oq.values_list('OrderID', flat=True)[:800])
qs = qs.filter(order_id__in=order_ids or ['__none__'])
elif pingtai not in (None, ''):
try:
oq = Order.query.filter(ClubID=club, Status=2, Platform=int(pingtai))
order_ids = list(oq.values_list('OrderID', flat=True)[:800])
qs = qs.filter(order_id__in=order_ids or ['__none__'])
except (TypeError, ValueError):
pass
total = qs.count()
start = (page - 1) * page_size
rows = list(qs[start:start + page_size])
oids = [r.order_id for r in rows]
orders = {o.OrderID: o for o in Order.query.filter(OrderID__in=oids)} if oids else {}
data_list = [
team_svc.serialize_recruit_public(r, orders.get(r.order_id), include_game_ids=False)
for r in rows
]
return Response({
'code': 0,
'msg': 'ok',
'data': {
'list': data_list,
'total': total,
'page': page,
'page_size': page_size,
'has_more': start + len(rows) < total,
},
})
class ZuduiXiangqingView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
uid = _uid(request)
club, err = _require_club(request)
if err:
return err
rid = request.data.get('recruit_id')
oid = str(request.data.get('dingdan_id') or request.data.get('order_id') or '').strip()
rec = None
if rid:
try:
rec = TeamRecruit.query.filter(id=int(rid)).first()
except (TypeError, ValueError):
pass
if not rec and oid:
rec = TeamRecruit.query.filter(order_id=oid, club_id=club).order_by('-id').first()
if not rec:
return Response({'code': 404, 'msg': '组队不存在'})
deny = _assert_recruit_same_club(rec, club)
if deny:
return deny
is_member = TeamMember.query.filter(
recruit_id=rec.id, user_uid=uid, join_status=TeamMember.JOIN_ACTIVE,
).exists()
include_game = is_member or rec.leader_uid == uid
order = Order.query.filter(OrderID=rec.order_id).first()
return Response({
'code': 0,
'msg': 'ok',
'data': team_svc.serialize_recruit_public(rec, order, include_game_ids=include_game),
})
class ZuduiRuiduiView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
uid = _uid(request)
club, err = _require_club(request)
if err:
return err
try:
rid = int(request.data.get('recruit_id'))
except (TypeError, ValueError):
return Response({'code': 400, 'msg': '缺少 recruit_id'})
rec0 = TeamRecruit.query.filter(id=rid).first()
deny = _assert_recruit_same_club(rec0, club)
if deny:
return deny
try:
rec = team_svc.join_recruit(
recruit_id=rid,
user_uid=uid,
game_id=request.data.get('game_id') or '',
request=request,
require_club_id=club,
)
order = Order.query.filter(OrderID=rec.order_id).first()
return Response({
'code': 0,
'msg': '加入成功',
'data': team_svc.serialize_recruit_public(rec, order, include_game_ids=True),
})
except ValueError as e:
return Response({'code': 400, 'msg': str(e)})
except Exception:
logger.exception('加入组队失败')
return Response({'code': 500, 'msg': '服务器错误'})
class ZuduiWodeView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [JSONParser]
def post(self, request):
uid = _uid(request)
club, err = _require_club(request)
if err:
return err
as_leader = TeamRecruit.query.filter(
club_id=club,
leader_uid=uid,
status__in=[TeamRecruit.STATUS_RECRUITING, TeamRecruit.STATUS_LOCKED],
).order_by('-opened_at')[:50]
member_rids = list(
TeamMember.query.filter(
user_uid=uid, join_status=TeamMember.JOIN_ACTIVE, role=TeamMember.ROLE_MEMBER,
).values_list('recruit_id', flat=True)[:50]
)
as_member_qs = TeamRecruit.query.filter(
id__in=member_rids or [0],
club_id=club,
status__in=[TeamRecruit.STATUS_RECRUITING, TeamRecruit.STATUS_LOCKED],
) if member_rids else []
as_member = list(as_member_qs) if as_member_qs else []
as_leader_list = list(as_leader)
oids = [r.order_id for r in as_leader_list + as_member]
orders = {o.OrderID: o for o in Order.query.filter(OrderID__in=oids)} if oids else {}
return Response({
'code': 0,
'msg': 'ok',
'data': {
'as_leader': [
team_svc.serialize_recruit_public(r, orders.get(r.order_id), include_game_ids=True)
for r in as_leader_list
],
'as_member': [
team_svc.serialize_recruit_public(r, orders.get(r.order_id), include_game_ids=True)
for r in as_member
],
},
})