This commit is contained in:
177
backend/shop/services.py
Normal file
177
backend/shop/services.py
Normal 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
|
||||
@@ -17,7 +17,7 @@ def send_sms(phone_number, template_code, template_params):
|
||||
"phone_number": phone_number,
|
||||
"template_code": template_code,
|
||||
"sign_name": SIGN_NAME,
|
||||
"additionalProp1": template_params
|
||||
"template_params": template_params
|
||||
}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -113,7 +113,7 @@ def notify_user_activity_signup_success(order, signup):
|
||||
user_nick = order.wechat_user.nickname
|
||||
|
||||
# 2. unit_name (Activity Title)
|
||||
unit_name = signup.activity.title
|
||||
unit_name = f"【{signup.activity.title}】"
|
||||
|
||||
# 3. time
|
||||
start_time = signup.activity.start_time
|
||||
|
||||
@@ -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 .serializers import ESP32ConfigSerializer, OrderSerializer, ServiceSerializer, VCCourseSerializer, ServiceOrderSerializer, WeChatUserSerializer, DistributorSerializer, WithdrawalSerializer, CommissionLogSerializer, CourseEnrollmentSerializer
|
||||
from .utils import get_access_token
|
||||
from .services import handle_post_payment
|
||||
from django.db import transaction, models
|
||||
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
|
||||
from django.contrib.auth.models import User
|
||||
@@ -507,166 +508,9 @@ def payment_finish(request):
|
||||
order.wechat_trade_no = transaction_id
|
||||
order.save()
|
||||
print(f"订单 {order.id} 状态已更新")
|
||||
|
||||
# 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()
|
||||
|
||||
# 计算佣金 (旧版销售员系统)
|
||||
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 或者不影响数据库的标记
|
||||
|
||||
# 6. 处理支付后业务逻辑 (活动报名、佣金、短信通知)
|
||||
handle_post_payment(order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"订单更新失败: {str(e)}")
|
||||
@@ -1044,6 +888,10 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
order.status = 'paid'
|
||||
order.wechat_trade_no = result.get('transaction_id')
|
||||
order.save()
|
||||
|
||||
# 处理支付后逻辑
|
||||
handle_post_payment(order)
|
||||
|
||||
return Response({'status': 'paid', 'message': '支付成功', 'detail': result})
|
||||
|
||||
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.wechat_trade_no = f"WX_{str(uuid.uuid4())[:18]}"
|
||||
order.save()
|
||||
|
||||
handle_post_payment(order)
|
||||
|
||||
return Response({'status': 'success', 'message': '支付成功'})
|
||||
|
||||
def get_current_wechat_user(request):
|
||||
|
||||
Reference in New Issue
Block a user