diff --git a/backend/community/serializers.py b/backend/community/serializers.py index ef3abfd..2e6cb5c 100644 --- a/backend/community/serializers.py +++ b/backend/community/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers from .models import Activity, ActivitySignup, Topic, Reply, TopicMedia, Announcement -from shop.serializers import WeChatUserSerializer, ESP32ConfigSerializer, ServiceSerializer, VCCourseSerializer +from shop.serializers import WeChatUserSerializer, ESP32ConfigSerializer, ServiceSerializer, VCCourseSerializer, OrderSerializer from .utils import get_current_wechat_user class ActivitySerializer(serializers.ModelSerializer): @@ -47,10 +47,11 @@ class ActivitySerializer(serializers.ModelSerializer): class ActivitySignupSerializer(serializers.ModelSerializer): activity_info = serializers.SerializerMethodField() + order = OrderSerializer(read_only=True) class Meta: model = ActivitySignup - fields = ['id', 'activity', 'activity_info', 'user', 'signup_time', 'status', 'signup_info'] + fields = ['id', 'activity', 'activity_info', 'user', 'signup_time', 'status', 'signup_info', 'order'] read_only_fields = ['signup_time', 'status', 'user'] def get_activity_info(self, obj): diff --git a/backend/config/settings.py b/backend/config/settings.py index 0b5b667..1bb69af 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -100,8 +100,8 @@ DATABASES = { } # 从环境变量获取数据库配置 (Docker 环境会自动注入这些变量) -DB_HOST = os.environ.get('DB_HOST', '121.43.104.161') -# DB_HOST = os.environ.get('DB_HOST', '6.6.6.66') +# DB_HOST = os.environ.get('DB_HOST', '121.43.104.161') +DB_HOST = os.environ.get('DB_HOST', '6.6.6.66') if DB_HOST: DATABASES['default'] = { 'ENGINE': 'django.db.backends.postgresql', @@ -109,8 +109,8 @@ if DB_HOST: 'USER': os.environ.get('DB_USER', 'market'), 'PASSWORD': os.environ.get('DB_PASSWORD', '123market'), 'HOST': DB_HOST, - 'PORT': os.environ.get('DB_PORT', '6433'), - #'PORT': os.environ.get('DB_PORT', '5432'), + #'PORT': os.environ.get('DB_PORT', '6433'), + 'PORT': os.environ.get('DB_PORT', '5432'), } diff --git a/backend/shop/serializers.py b/backend/shop/serializers.py index 7afbebb..0e9d24a 100644 --- a/backend/shop/serializers.py +++ b/backend/shop/serializers.py @@ -207,6 +207,7 @@ class OrderSerializer(serializers.ModelSerializer): """ config_name = serializers.CharField(source='config.name', read_only=True) course_title = serializers.CharField(source='course.title', read_only=True) + activity_title = serializers.CharField(source='activity.title', read_only=True) config_image = serializers.SerializerMethodField() salesperson_name = serializers.CharField(source='salesperson.name', read_only=True) salesperson_code = serializers.CharField(source='salesperson.code', read_only=True) @@ -215,7 +216,7 @@ class OrderSerializer(serializers.ModelSerializer): class Meta: model = Order - fields = ['id', 'config', 'config_name', 'config_image', 'course', 'course_title', 'quantity', 'total_price', 'status', 'created_at', 'updated_at', 'wechat_trade_no', + fields = ['id', 'config', 'config_name', 'config_image', 'course', 'course_title', 'activity', 'activity_title', 'quantity', 'total_price', 'status', 'created_at', 'updated_at', 'wechat_trade_no', 'customer_name', 'phone_number', 'shipping_address', 'ref_code', 'salesperson_name', 'salesperson_code', 'courier_name', 'tracking_number'] read_only_fields = ['total_price', 'status', 'wechat_trade_no', 'created_at', 'updated_at'] extra_kwargs = { diff --git a/frontend/src/pages/MyOrders.jsx b/frontend/src/pages/MyOrders.jsx index 90722a4..8613255 100644 --- a/frontend/src/pages/MyOrders.jsx +++ b/frontend/src/pages/MyOrders.jsx @@ -1,8 +1,7 @@ import React, { useState, useEffect } from 'react'; import { Form, Input, Button, Card, List, Tag, Typography, message, Space, Statistic, Divider, Modal, Descriptions, Tabs } from 'antd'; import { MobileOutlined, LockOutlined, SearchOutlined, CarOutlined, InboxOutlined, SafetyCertificateOutlined, CheckCircleOutlined, ClockCircleOutlined, CloseCircleOutlined, UserOutlined, EnvironmentOutlined, PhoneOutlined, CalendarOutlined } from '@ant-design/icons'; -import { queryMyOrders, getMySignups } from '../api'; -import { motion } from 'framer-motion'; +import api, { getMySignups } from '../api'; import LoginModal from '../components/LoginModal'; import { useAuth } from '../context/AuthContext'; import { useNavigate } from 'react-router-dom'; @@ -41,8 +40,6 @@ const MyOrders = () => { const handleQueryData = async () => { setLoading(true); try { - const { default: api } = await import('../api'); - // Parallel fetch const [ordersRes, activitiesRes] = await Promise.allSettled([ api.get('/orders/'), @@ -344,7 +341,7 @@ const MyOrders = () => { onLoginSuccess={(userData) => { login(userData); if (userData.phone_number) { - handleQueryOrders(userData.phone_number); + handleQueryData(); } }} /> diff --git a/frontend/src/pages/activity/Detail.jsx b/frontend/src/pages/activity/Detail.jsx index 495c888..08959a1 100644 --- a/frontend/src/pages/activity/Detail.jsx +++ b/frontend/src/pages/activity/Detail.jsx @@ -28,6 +28,7 @@ const ActivityDetail = () => { const [signupFormVisible, setSignupFormVisible] = useState(false); const [paymentModalVisible, setPaymentModalVisible] = useState(false); const [paymentInfo, setPaymentInfo] = useState(null); + const [isPaidSuccess, setIsPaidSuccess] = useState(false); const [form] = Form.useForm(); // Header animation: transparent to white with shadow @@ -96,6 +97,7 @@ const ActivityDetail = () => { // 检查是否需要支付 if (data.payment_required) { setPaymentInfo(data); + setIsPaidSuccess(false); // 先关闭报名表单,确保层级正确 setSignupFormVisible(false); // 延迟一点点时间打开支付弹窗,避免状态更新冲突 @@ -125,14 +127,13 @@ const ActivityDetail = () => { // Polling for payment status useEffect(() => { let timer; - if (paymentModalVisible && paymentInfo?.order_id) { + if (paymentModalVisible && paymentInfo?.order_id && !isPaidSuccess) { timer = setInterval(async () => { try { const response = await queryOrderStatus(paymentInfo.order_id); if (response.data.status === 'paid') { + setIsPaidSuccess(true); message.success('支付成功,报名已确认!'); - setPaymentModalVisible(false); - setPaymentInfo(null); // Trigger success effects confetti({ @@ -152,7 +153,7 @@ const ActivityDetail = () => { }, 3000); } return () => clearInterval(timer); - }, [paymentModalVisible, paymentInfo, id, queryClient]); + }, [paymentModalVisible, paymentInfo, id, queryClient, isPaidSuccess]); const handleShare = async () => { const url = window.location.href; @@ -475,10 +476,27 @@ const ActivityDetail = () => { setPaymentModalVisible(false)} - footer={null} + onCancel={() => { + setPaymentModalVisible(false); + if (isPaidSuccess) { + setPaymentInfo(null); + } + }} + footer={[ + + ]} destroyOnHidden width={360} zIndex={1001} // 确保层级高于其他弹窗 @@ -486,20 +504,26 @@ const ActivityDetail = () => {
{paymentInfo?.code_url ? ( <> -
- -
-

¥{paymentInfo.price}

-

请使用微信扫一扫支付

+ {!isPaidSuccess ? ( + <> +
+ +
+

¥{paymentInfo.price}

+

请使用微信扫一扫支付

+ + ) : ( +
+
+ +
+

支付成功

+
+ )} ) : ( )} -
- -