移动端适配
All checks were successful
Deploy to Server / deploy (push) Successful in 26s

This commit is contained in:
jeremygan2021
2026-02-23 18:01:56 +08:00
parent 52b16911b1
commit 3f363dbd8e
3 changed files with 190 additions and 162 deletions

177
backend/shop/services.py Normal file
View File

@@ -0,0 +1,177 @@
import logging
from django.db import models
from .models import Order, CommissionLog, Distributor
# To avoid circular imports, import other models inside function if needed
logger = logging.getLogger(__name__)
def handle_post_payment(order):
"""
处理订单支付成功后的业务逻辑
包括:
1. 更新活动报名状态
2. 发送活动报名短信
3. 计算分销佣金
4. 发送普通订单短信
"""
print(f"开始处理订单 {order.id} 支付后逻辑...")
# 1. Handle Activity Signup
if hasattr(order, 'activity') and order.activity:
try:
# Use apps.get_model to avoid circular dependency
from django.apps import apps
ActivitySignup = apps.get_model('community', 'ActivitySignup')
signup = ActivitySignup.objects.filter(order=order).first()
# Fallback: try to find by user and activity if not found by order
if not signup and order.wechat_user:
print(f"Warning: ActivitySignup not found by order {order.id}, trying by user/activity")
signup = ActivitySignup.objects.filter(
user=order.wechat_user,
activity=order.activity,
status='unpaid'
).first()
if signup:
print(f"Found signup {signup.id} by user/activity, linking order...")
signup.order = order
signup.save()
if signup:
# Determine status based on activity setting
# Use the model method if available, otherwise manual logic
if hasattr(signup, 'check_payment_status'):
signup.check_payment_status()
print(f"活动报名状态已更新(check_payment_status): {signup.id} -> {signup.status}")
else:
new_status = 'confirmed' if signup.activity.auto_confirm else 'pending'
signup.status = new_status
signup.save()
print(f"活动报名状态已更新: {signup.id} -> {new_status}")
# Send Activity SMS
try:
from .sms_utils import notify_user_activity_signup_success
notify_user_activity_signup_success(order, signup)
except Exception as sms_e:
print(f"发送活动报名短信失败: {str(sms_e)}")
else:
print(f"Error: No ActivitySignup found for paid order {order.id}")
except Exception as e:
print(f"更新活动报名状态失败: {str(e)}")
import traceback
traceback.print_exc()
# 2. 计算佣金 (旧版销售员系统 & 新版分销员系统)
try:
# 旧版销售员系统
salesperson = order.salesperson
if salesperson:
# 1. 计算直接佣金 (一级)
# 优先级: 产品独立分润比例 > 销售员个人分润比例
rate_1 = 0
if order.config:
rate_1 = order.config.commission_rate if order.config.commission_rate > 0 else salesperson.commission_rate
elif order.course:
# 课程暂时使用销售员默认比例
rate_1 = salesperson.commission_rate
amount_1 = order.total_price * rate_1
if amount_1 > 0:
CommissionLog.objects.create(
order=order,
salesperson=salesperson,
amount=amount_1,
level=1,
status='pending'
)
print(f"生成一级佣金(Salesperson): {salesperson.name} - {amount_1}")
# 2. 计算上级佣金 (二级)
parent = salesperson.parent
if parent:
rate_2 = parent.second_level_rate
amount_2 = order.total_price * rate_2
if amount_2 > 0:
CommissionLog.objects.create(
order=order,
salesperson=parent,
amount=amount_2,
level=2,
status='pending'
)
print(f"生成二级佣金(Salesperson): {parent.name} - {amount_2}")
# 新版分销员系统
distributor = order.distributor
if distributor:
# 1. 计算直接佣金 (一级)
# 优先级: 产品独立分润比例 > 分销员个人分润比例
rate_1 = 0
if order.config:
rate_1 = order.config.commission_rate if order.config.commission_rate > 0 else distributor.commission_rate
elif order.course:
# 课程暂时使用分销员默认比例
rate_1 = distributor.commission_rate
amount_1 = order.total_price * rate_1
if amount_1 > 0:
CommissionLog.objects.create(
order=order,
distributor=distributor,
amount=amount_1,
level=1,
status='settled' # 简化流程,直接结算到余额
)
# 更新余额
distributor.total_earnings += amount_1
distributor.withdrawable_balance += amount_1
distributor.save()
print(f"生成一级佣金(Distributor): {distributor.user.nickname} - {amount_1}")
# 2. 计算上级佣金 (二级)
parent = distributor.parent
if parent:
# 二级固定比例 2% (0.02)
rate_2 = 0.02
amount_2 = order.total_price * models.DecimalField(max_digits=5, decimal_places=4).to_python(rate_2)
if amount_2 > 0:
CommissionLog.objects.create(
order=order,
distributor=parent,
amount=amount_2,
level=2,
status='settled'
)
# 更新余额
parent.total_earnings += amount_2
parent.withdrawable_balance += amount_2
parent.save()
print(f"生成二级佣金(Distributor): {parent.user.nickname} - {amount_2}")
except Exception as e:
print(f"佣金计算失败: {str(e)}")
import traceback
traceback.print_exc()
# 3. 发送普通商品/课程购买的短信通知(排除活动报名,避免重复发送)
# 活动报名的短信已经在上面发送过了
if not (hasattr(order, 'activity') and order.activity):
try:
from .sms_utils import notify_admins_order_paid, notify_user_order_paid
notify_admins_order_paid(order)
notify_user_order_paid(order)
except Exception as e:
print(f"发送短信通知失败: {str(e)}")
else:
# 额外保险:如果是活动订单,手动标记不触发 signals 中的支付/发货通知
# 因为 signals 可能会在 save() 时触发
order._was_paid = False
order._was_shipped = False

View File

@@ -17,7 +17,7 @@ def send_sms(phone_number, template_code, template_params):
"phone_number": phone_number, "phone_number": phone_number,
"template_code": template_code, "template_code": template_code,
"sign_name": SIGN_NAME, "sign_name": SIGN_NAME,
"additionalProp1": template_params "template_params": template_params
} }
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -113,7 +113,7 @@ def notify_user_activity_signup_success(order, signup):
user_nick = order.wechat_user.nickname user_nick = order.wechat_user.nickname
# 2. unit_name (Activity Title) # 2. unit_name (Activity Title)
unit_name = signup.activity.title unit_name = f"{signup.activity.title}"
# 3. time # 3. time
start_time = signup.activity.start_time start_time = signup.activity.start_time

View File

@@ -11,6 +11,7 @@ from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiPara
from .models import ESP32Config, Order, WeChatPayConfig, Service, VCCourse, ServiceOrder, Salesperson, CommissionLog, WeChatUser, Distributor, Withdrawal, CourseEnrollment from .models import ESP32Config, Order, WeChatPayConfig, Service, VCCourse, ServiceOrder, Salesperson, CommissionLog, WeChatUser, Distributor, Withdrawal, CourseEnrollment
from .serializers import ESP32ConfigSerializer, OrderSerializer, ServiceSerializer, VCCourseSerializer, ServiceOrderSerializer, WeChatUserSerializer, DistributorSerializer, WithdrawalSerializer, CommissionLogSerializer, CourseEnrollmentSerializer from .serializers import ESP32ConfigSerializer, OrderSerializer, ServiceSerializer, VCCourseSerializer, ServiceOrderSerializer, WeChatUserSerializer, DistributorSerializer, WithdrawalSerializer, CommissionLogSerializer, CourseEnrollmentSerializer
from .utils import get_access_token from .utils import get_access_token
from .services import handle_post_payment
from django.db import transaction, models from django.db import transaction, models
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -507,166 +508,9 @@ def payment_finish(request):
order.wechat_trade_no = transaction_id order.wechat_trade_no = transaction_id
order.save() order.save()
print(f"订单 {order.id} 状态已更新") print(f"订单 {order.id} 状态已更新")
# Handle Activity Signup # 6. 处理支付后业务逻辑 (活动报名、佣金、短信通知)
if hasattr(order, 'activity') and order.activity: handle_post_payment(order)
try:
# Use apps.get_model to avoid circular dependency
from django.apps import apps
ActivitySignup = apps.get_model('community', 'ActivitySignup')
signup = ActivitySignup.objects.filter(order=order).first()
# Fallback: try to find by user and activity if not found by order
if not signup and order.wechat_user:
print(f"Warning: ActivitySignup not found by order {order.id}, trying by user/activity")
signup = ActivitySignup.objects.filter(
user=order.wechat_user,
activity=order.activity,
status='unpaid'
).first()
if signup:
print(f"Found signup {signup.id} by user/activity, linking order...")
signup.order = order
signup.save()
if signup:
# Determine status based on activity setting
# Use the model method if available, otherwise manual logic
if hasattr(signup, 'check_payment_status'):
signup.check_payment_status()
print(f"活动报名状态已更新(check_payment_status): {signup.id} -> {signup.status}")
else:
new_status = 'confirmed' if signup.activity.auto_confirm else 'pending'
signup.status = new_status
signup.save()
print(f"活动报名状态已更新: {signup.id} -> {new_status}")
# Send Activity SMS
try:
from .sms_utils import notify_user_activity_signup_success
notify_user_activity_signup_success(order, signup)
except Exception as sms_e:
print(f"发送活动报名短信失败: {str(sms_e)}")
else:
print(f"Error: No ActivitySignup found for paid order {order.id}")
except Exception as e:
print(f"更新活动报名状态失败: {str(e)}")
import traceback
traceback.print_exc()
# 计算佣金 (旧版销售员系统)
try:
salesperson = order.salesperson
if salesperson:
# 1. 计算直接佣金 (一级)
# 优先级: 产品独立分润比例 > 销售员个人分润比例
rate_1 = 0
if order.config:
rate_1 = order.config.commission_rate if order.config.commission_rate > 0 else salesperson.commission_rate
elif order.course:
# 课程暂时使用销售员默认比例
rate_1 = salesperson.commission_rate
amount_1 = order.total_price * rate_1
if amount_1 > 0:
CommissionLog.objects.create(
order=order,
salesperson=salesperson,
amount=amount_1,
level=1,
status='pending'
)
print(f"生成一级佣金(Salesperson): {salesperson.name} - {amount_1}")
# 2. 计算上级佣金 (二级)
parent = salesperson.parent
if parent:
rate_2 = parent.second_level_rate
amount_2 = order.total_price * rate_2
if amount_2 > 0:
CommissionLog.objects.create(
order=order,
salesperson=parent,
amount=amount_2,
level=2,
status='pending'
)
print(f"生成二级佣金(Salesperson): {parent.name} - {amount_2}")
# 计算佣金 (新版分销员系统)
distributor = order.distributor
if distributor:
# 1. 计算直接佣金 (一级)
# 优先级: 产品独立分润比例 > 分销员个人分润比例
rate_1 = 0
if order.config:
rate_1 = order.config.commission_rate if order.config.commission_rate > 0 else distributor.commission_rate
elif order.course:
# 课程暂时使用分销员默认比例
rate_1 = distributor.commission_rate
amount_1 = order.total_price * rate_1
if amount_1 > 0:
CommissionLog.objects.create(
order=order,
distributor=distributor,
amount=amount_1,
level=1,
status='settled' # 简化流程,直接结算到余额
)
# 更新余额
distributor.total_earnings += amount_1
distributor.withdrawable_balance += amount_1
distributor.save()
print(f"生成一级佣金(Distributor): {distributor.user.nickname} - {amount_1}")
# 2. 计算上级佣金 (二级)
parent = distributor.parent
if parent:
# 二级固定比例 2% (0.02)
rate_2 = 0.02
amount_2 = order.total_price * models.DecimalField(max_digits=5, decimal_places=4).to_python(rate_2)
if amount_2 > 0:
CommissionLog.objects.create(
order=order,
distributor=parent,
amount=amount_2,
level=2,
status='settled'
)
# 更新余额
parent.total_earnings += amount_2
parent.withdrawable_balance += amount_2
parent.save()
print(f"生成二级佣金(Distributor): {parent.user.nickname} - {amount_2}")
except Exception as e:
print(f"佣金计算失败: {str(e)}")
import traceback
traceback.print_exc()
# 发送普通商品/课程购买的短信通知(排除活动报名,避免重复发送)
# 活动报名的短信已经在上面发送过了
if not (hasattr(order, 'activity') and order.activity):
try:
from .sms_utils import notify_admins_order_paid, notify_user_order_paid
notify_admins_order_paid(order)
notify_user_order_paid(order)
except Exception as e:
print(f"发送短信通知失败: {str(e)}")
else:
# 额外保险:如果是活动订单,手动标记不触发 signals 中的支付/发货通知
# 因为 signals 可能会在 save() 时触发
order._was_paid = False
order._was_shipped = False
# order.save() # 不需要再 save因为已经是 post-save 或者不影响数据库的标记
except Exception as e: except Exception as e:
print(f"订单更新失败: {str(e)}") print(f"订单更新失败: {str(e)}")
@@ -1044,6 +888,10 @@ class OrderViewSet(viewsets.ModelViewSet):
order.status = 'paid' order.status = 'paid'
order.wechat_trade_no = result.get('transaction_id') order.wechat_trade_no = result.get('transaction_id')
order.save() order.save()
# 处理支付后逻辑
handle_post_payment(order)
return Response({'status': 'paid', 'message': '支付成功', 'detail': result}) return Response({'status': 'paid', 'message': '支付成功', 'detail': result})
return Response({'status': 'pending', 'trade_state': trade_state, 'message': result.get('trade_state_desc')}) return Response({'status': 'pending', 'trade_state': trade_state, 'message': result.get('trade_state_desc')})
@@ -1062,6 +910,9 @@ class OrderViewSet(viewsets.ModelViewSet):
order.status = 'paid' order.status = 'paid'
order.wechat_trade_no = f"WX_{str(uuid.uuid4())[:18]}" order.wechat_trade_no = f"WX_{str(uuid.uuid4())[:18]}"
order.save() order.save()
handle_post_payment(order)
return Response({'status': 'success', 'message': '支付成功'}) return Response({'status': 'success', 'message': '支付成功'})
def get_current_wechat_user(request): def get_current_wechat_user(request):