This commit is contained in:
@@ -38,14 +38,19 @@ class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
|
|||||||
|
|
||||||
activity = self.get_object()
|
activity = self.get_object()
|
||||||
|
|
||||||
# Check if already signed up (and not cancelled)
|
# 1. Check confirmed signup
|
||||||
existing_signup = ActivitySignup.objects.filter(activity=activity, user=user).exclude(status='cancelled').first()
|
if ActivitySignup.objects.filter(activity=activity, user=user, status='confirmed').exists():
|
||||||
if existing_signup:
|
|
||||||
return Response({'error': '您已报名该活动'}, status=400)
|
return Response({'error': '您已报名该活动'}, status=400)
|
||||||
|
|
||||||
# Check limit (exclude cancelled)
|
# 2. Get pending signup (for retry)
|
||||||
current_count = activity.signups.exclude(status='cancelled').count()
|
pending_signup = ActivitySignup.objects.filter(activity=activity, user=user, status='pending').first()
|
||||||
if current_count >= activity.max_participants:
|
|
||||||
|
# 3. Check limit (exclude cancelled, exclude current pending)
|
||||||
|
query = activity.signups.exclude(status='cancelled')
|
||||||
|
if pending_signup:
|
||||||
|
query = query.exclude(id=pending_signup.id)
|
||||||
|
|
||||||
|
if query.count() >= activity.max_participants:
|
||||||
return Response({'error': '活动名额已满'}, status=400)
|
return Response({'error': '活动名额已满'}, status=400)
|
||||||
|
|
||||||
# Get signup info
|
# Get signup info
|
||||||
@@ -77,8 +82,21 @@ class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
|
|||||||
import time
|
import time
|
||||||
from wechatpayv3 import WeChatPayType
|
from wechatpayv3 import WeChatPayType
|
||||||
|
|
||||||
# Create Order
|
# Create or Get Order
|
||||||
# Check if there is a pending order
|
order = None
|
||||||
|
if pending_signup and pending_signup.order:
|
||||||
|
# Reuse existing order if it's pending
|
||||||
|
if pending_signup.order.status == 'pending':
|
||||||
|
order = pending_signup.order
|
||||||
|
# Update contact info if needed
|
||||||
|
contact_name = signup_info.get('name') or signup_info.get('participant_name') or user.nickname or 'Activity User'
|
||||||
|
contact_phone = signup_info.get('phone') or user.phone_number or ''
|
||||||
|
if contact_name: order.customer_name = contact_name
|
||||||
|
if contact_phone: order.phone_number = contact_phone
|
||||||
|
order.save()
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
# Check independent pending order
|
||||||
pending_order = Order.objects.filter(
|
pending_order = Order.objects.filter(
|
||||||
wechat_user=user,
|
wechat_user=user,
|
||||||
activity=activity,
|
activity=activity,
|
||||||
@@ -87,9 +105,7 @@ class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
|
|||||||
|
|
||||||
if pending_order:
|
if pending_order:
|
||||||
order = pending_order
|
order = pending_order
|
||||||
# Update info if needed? Maybe not.
|
|
||||||
else:
|
else:
|
||||||
# 优先从报名信息获取联系方式
|
|
||||||
contact_name = signup_info.get('name') or signup_info.get('participant_name') or user.nickname or 'Activity User'
|
contact_name = signup_info.get('name') or signup_info.get('participant_name') or user.nickname or 'Activity User'
|
||||||
contact_phone = signup_info.get('phone') or user.phone_number or ''
|
contact_phone = signup_info.get('phone') or user.phone_number or ''
|
||||||
|
|
||||||
@@ -101,7 +117,7 @@ class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
|
|||||||
quantity=1,
|
quantity=1,
|
||||||
customer_name=contact_name,
|
customer_name=contact_name,
|
||||||
phone_number=contact_phone,
|
phone_number=contact_phone,
|
||||||
shipping_address=activity.location or '线下活动', # 使用活动地点作为发货地址
|
shipping_address=activity.location or '线下活动',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate Pay Code
|
# Generate Pay Code
|
||||||
@@ -128,7 +144,11 @@ class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
|
|||||||
if code in range(200, 300):
|
if code in range(200, 300):
|
||||||
code_url = result.get('code_url')
|
code_url = result.get('code_url')
|
||||||
|
|
||||||
# Create a pending signup record so we can update it later
|
if pending_signup:
|
||||||
|
pending_signup.signup_info = signup_info
|
||||||
|
pending_signup.order = order
|
||||||
|
pending_signup.save()
|
||||||
|
else:
|
||||||
ActivitySignup.objects.create(
|
ActivitySignup.objects.create(
|
||||||
activity=activity,
|
activity=activity,
|
||||||
user=user,
|
user=user,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { motion, useScroll, useTransform } from 'framer-motion';
|
import { motion, useScroll, useTransform } from 'framer-motion';
|
||||||
import { ArrowLeftOutlined, ShareAltOutlined, CalendarOutlined, ClockCircleOutlined, EnvironmentOutlined, UserOutlined, UploadOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, ShareAltOutlined, CalendarOutlined, ClockCircleOutlined, EnvironmentOutlined, UserOutlined, UploadOutlined, PayCircleOutlined } from '@ant-design/icons';
|
||||||
import confetti from 'canvas-confetti';
|
import confetti from 'canvas-confetti';
|
||||||
import { message, Spin, Button, Result, Modal, Form, Input, Select, Radio, Checkbox, Upload } from 'antd';
|
import { message, Spin, Button, Result, Modal, Form, Input, Select, Radio, Checkbox, Upload } from 'antd';
|
||||||
import { getActivityDetail, signUpActivity, queryOrderStatus } from '../../api';
|
import { getActivityDetail, signUpActivity, queryOrderStatus } from '../../api';
|
||||||
@@ -316,6 +316,10 @@ const ActivityDetail = () => {
|
|||||||
<UserOutlined />
|
<UserOutlined />
|
||||||
<span>{activity.current_signups || 0} / {activity.max_participants} 已报名</span>
|
<span>{activity.current_signups || 0} / {activity.max_participants} 已报名</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
|
<PayCircleOutlined />
|
||||||
|
<span>{activity.is_paid ? `¥${activity.price}` : '免费'}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 10 }}>
|
<div style={{ display: 'flex', gap: 10 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user