feat: 组队招募(/zudui)+ 结单/自动结算/罚款挂钩

新增 Team 表与接口;进8强制锁、组队分账优先且失败不回退全额队长。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-27 03:12:04 +08:00
parent e9e5ee85e2
commit 7002da6093
14 changed files with 1466 additions and 63 deletions

View File

@@ -583,8 +583,13 @@ class FineApplyView(APIView):
if not dashou_id:
return Response({'code': 400, 'msg': '该订单无接单打手,无法罚款'})
# 重复罚款检查
if Penalty.query.filter(PenalizedUserID=dashou_id, RelatedOrderID=OrderID).exists():
# 重复罚款检查(组队单按订单维度防重复)
from orders.services.team_recruit import get_active_recruit
_active_team = get_active_recruit(OrderID)
if _active_team:
if Penalty.query.filter(RelatedOrderID=OrderID).exclude(Status=4).exists():
return Response({'code': 400, 'msg': '该订单已有罚单'})
elif Penalty.query.filter(PenalizedUserID=dashou_id, RelatedOrderID=OrderID).exists():
return Response({'code': 400, 'msg': '该打手已被罚款过'})
# 检查权限(打手身份 shenfen=1
@@ -610,34 +615,57 @@ class FineApplyView(APIView):
return Response({'code': 500, 'msg': '图片上传失败'})
uploaded_urls.append(file_path)
# 创建罚单
# 创建罚单(组队均摊)
with transaction.atomic():
penalty = Penalty.query.create(
PenalizedUserID=dashou_id,
ApplicantID=request.user.Phone,
Identity=1, # 被处罚人身份1 打手
Reason=Reason,
FineAmount=FineAmount,
RelatedOrderID=OrderID,
Status=1, # 待缴纳
AffectsGrabbing=AffectsGrabbing,
ApplicantIdentity=1, # 申请人身份1 客服
ClubID=resolve_penalty_club_id(
order=order,
request=request,
user=request.user,
applicant_id=getattr(request.user, 'UserUID', None),
),
)
for relative_url in uploaded_urls:
PenaltyAppealImage.query.create(
Penalty=penalty,
PenalizedUserID=dashou_id,
ImageURL=relative_url,
Purpose=1
)
from orders.services.team_recruit import maybe_split_penalties
from users.fadan_fenhong_utils import lock_penalty_bonus
lock_penalty_bonus(penalty)
club_for_p = resolve_penalty_club_id(
order=order,
request=request,
user=request.user,
applicant_id=getattr(request.user, 'UserUID', None),
)
split = maybe_split_penalties(
order_id=OrderID,
total_amount=FineAmount,
reason=Reason,
applicant_id=request.user.Phone,
applicant_identity=1,
affects_grabbing=AffectsGrabbing,
club_id=club_for_p,
)
if split:
for p in split:
for relative_url in uploaded_urls:
PenaltyAppealImage.query.create(
Penalty=p,
PenalizedUserID=p.PenalizedUserID,
ImageURL=relative_url,
Purpose=1,
)
lock_penalty_bonus(p)
penalty = split[0]
else:
penalty = Penalty.query.create(
PenalizedUserID=dashou_id,
ApplicantID=request.user.Phone,
Identity=1,
Reason=Reason,
FineAmount=FineAmount,
RelatedOrderID=OrderID,
Status=1,
AffectsGrabbing=AffectsGrabbing,
ApplicantIdentity=1,
ClubID=club_for_p,
)
for relative_url in uploaded_urls:
PenaltyAppealImage.query.create(
Penalty=penalty,
PenalizedUserID=dashou_id,
ImageURL=relative_url,
Purpose=1
)
lock_penalty_bonus(penalty)
logger.info(f"客服罚款申请成功: 订单 {OrderID}, 打手 {dashou_id}, 金额 {FineAmount}")
return Response({'code': 0, 'msg': '罚款已生成', 'data': {'id': penalty.id}})