|
@@ -8,17 +8,19 @@
|
|
|
"""
|
|
|
import threading
|
|
|
import time
|
|
|
-from decimal import Decimal
|
|
|
+from datetime import datetime, timedelta
|
|
|
+from decimal import Decimal, ROUND_DOWN
|
|
|
|
|
|
from django.http import QueryDict
|
|
|
from django.views import View
|
|
|
|
|
|
-from AgentModel.models import AgentDevice, AgentCloudServicePackage, AgentCustomerPackage, AgentDeviceOrder
|
|
|
-from Model.models import Order_Model
|
|
|
+from AgentModel.models import AgentDevice, AgentCloudServicePackage, AgentCustomerPackage, AgentDeviceOrder, \
|
|
|
+ AgentDeviceOrderInstallment
|
|
|
+from Ansjer.config import LOGGER
|
|
|
+from Model.models import Order_Model, Store_Meal, UnicomCombo
|
|
|
from Object.ResponseObject import ResponseObject
|
|
|
from Object.TokenObject import TokenObject
|
|
|
from Service.CommonService import CommonService
|
|
|
-from Ansjer.config import LOGGER
|
|
|
|
|
|
|
|
|
class AgentOrderView(View):
|
|
@@ -51,7 +53,7 @@ class AgentOrderView(View):
|
|
|
tko = TokenObject(
|
|
|
request.META.get('HTTP_AUTHORIZATION'),
|
|
|
returntpye='pc')
|
|
|
- if operation == 'addOrder':
|
|
|
+ if operation == 'addOrder': # 添加代理商订单
|
|
|
order_id = request_dict.get('order_id', None)
|
|
|
uid = request_dict.get('uid', None)
|
|
|
order_type = request_dict.get('order_type', None)
|
|
@@ -126,7 +128,11 @@ class AgentOrderView(View):
|
|
|
dict_data = {'ac_id': ac_id, 'serial_number': serial_number, 'csp_id': agent_package.id,
|
|
|
'order_id': order_id, 'status': 1, 'profit_amount': pay_price, 'profit': profit,
|
|
|
'pay_time': order_qs[0]['payTime'], 'created_time': now_time, 'updated_time': now_time}
|
|
|
- AgentDeviceOrder.objects.create(**dict_data)
|
|
|
+ agent_order_obj = AgentDeviceOrder.objects.create(**dict_data)
|
|
|
+
|
|
|
+ # 保存分期结算记录
|
|
|
+ cls.save_order_installment(agent_order_obj.id, package_type, package_id, profit)
|
|
|
+
|
|
|
LOGGER.info(f'******save_agent_package代理订单存表结束:{dict_data}')
|
|
|
except Exception as e:
|
|
|
LOGGER.info('*****AgentOrderView.save_agent_package:errLine:{}, errMsg:{}'
|
|
@@ -150,3 +156,162 @@ class AgentOrderView(View):
|
|
|
profit = (price - cost) * (profit_value / 100)
|
|
|
profit = profit.quantize(Decimal('0.00'))
|
|
|
return profit
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_settlement_interval(cls, package_type, package_id):
|
|
|
+ try:
|
|
|
+ if package_type == 1: # 云存
|
|
|
+ store_qs = Store_Meal.objects.filter(id=package_id).values('day', 'bucket_id', 'expire',
|
|
|
+ 'icloud_store_meal_id')
|
|
|
+ if not store_qs.exists():
|
|
|
+ return []
|
|
|
+
|
|
|
+ # 根据套餐周期计算往后每个月26号作为结算时间
|
|
|
+ return cls.get_future_timestamps(store_qs[0]['expire'])
|
|
|
+
|
|
|
+ elif package_type == 2: # 4G
|
|
|
+ combo4g_qs = UnicomCombo.objects.filter(id=package_id).values('expiration_days', 'expiration_type')
|
|
|
+ if not combo4g_qs.exists():
|
|
|
+ return []
|
|
|
+
|
|
|
+ # 目前4G套餐都是基于按天类型创建
|
|
|
+ if combo4g_qs[0]['expiration_type'] == 0 and combo4g_qs[0]['expiration_days'] > 0:
|
|
|
+ months = int(combo4g_qs[0]['expiration_days'] / 30)
|
|
|
+
|
|
|
+ # 根据套餐周期计算往后每个月26号作为结算时间
|
|
|
+ return cls.get_future_timestamps(months)
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('*****AgentOrderView.get_settlement_interval:errLine:{}, errMsg:{}'
|
|
|
+ .format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return []
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_future_timestamps(months):
|
|
|
+ """
|
|
|
+ 生成未来若干个月的第一个月的26号11点的timestamp列表。
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ months -- 未来需要生成timestamp的月份数量
|
|
|
+
|
|
|
+ 返回值:
|
|
|
+ timestamps -- 包含未来months个月第一个月的26号11点的timestamp的列表
|
|
|
+ """
|
|
|
+ current_time = datetime.now() # 获取当前时间,注意这会是系统当前时区的时间
|
|
|
+ current_month = current_time.month
|
|
|
+ current_year = current_time.year
|
|
|
+
|
|
|
+ timestamps = []
|
|
|
+ for _ in range(months):
|
|
|
+ # 如果当前月已经是需要生成的月份,则年份和月份不变
|
|
|
+ if current_month == 1 and _ == 0:
|
|
|
+ next_year = current_year
|
|
|
+ next_month = current_month + 1
|
|
|
+ else:
|
|
|
+ # 计算下一个月的年和月
|
|
|
+ if current_month == 12:
|
|
|
+ next_month = 1
|
|
|
+ next_year = current_year + 1
|
|
|
+ else:
|
|
|
+ next_month = current_month + 1
|
|
|
+ next_year = current_year
|
|
|
+
|
|
|
+ # 生成下个月的26号11点的时间点
|
|
|
+ next_date = datetime(next_year, next_month, 26, 11, 0, 0)
|
|
|
+
|
|
|
+ # 如果生成的日期超过了当月的实际天数(比如2月没有26号),则需要回退到当月的最后一天
|
|
|
+ last_day_of_month = (datetime(next_year, next_month, 1) + timedelta(days=31)).replace(day=1) - timedelta(
|
|
|
+ days=1)
|
|
|
+ if next_date > last_day_of_month:
|
|
|
+ next_date = last_day_of_month.replace(hour=11, minute=0, second=0, microsecond=0)
|
|
|
+
|
|
|
+ timestamps.append(int(next_date.timestamp()))
|
|
|
+
|
|
|
+ # 更新当前月份和年份为下一次循环使用
|
|
|
+ current_month = next_month
|
|
|
+ current_year = next_year
|
|
|
+
|
|
|
+ return timestamps
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def distribute_commission(commission, periods):
|
|
|
+ # 转换佣金和期数为Decimal类型,并设置精度
|
|
|
+ commission = Decimal(str(commission)).quantize(Decimal('0.01'))
|
|
|
+ periods = Decimal(periods)
|
|
|
+
|
|
|
+ # 每期基础金额(向下取整到最接近的0.01)
|
|
|
+ base_amount = (commission / periods).quantize(Decimal('0.01'), rounding=ROUND_DOWN)
|
|
|
+
|
|
|
+ # 初始化每期分配的金额列表
|
|
|
+ distributed_amounts = [base_amount] * int(periods)
|
|
|
+
|
|
|
+ # 计算按照基础金额分配后的总和
|
|
|
+ total_distributed = sum(distributed_amounts)
|
|
|
+
|
|
|
+ # 计算剩余需要分配的金额
|
|
|
+ remainder = commission - total_distributed
|
|
|
+
|
|
|
+ # 分配剩余金额
|
|
|
+ if remainder > Decimal('0'):
|
|
|
+ # 从第一期开始分配剩余金额
|
|
|
+ for i in range(len(distributed_amounts)):
|
|
|
+ if remainder >= Decimal('0.01'):
|
|
|
+ distributed_amounts[i] += Decimal('0.01')
|
|
|
+ remainder -= Decimal('0.01')
|
|
|
+ else:
|
|
|
+ # 如果剩余金额不足0.01,则将其全部加到当前期
|
|
|
+ distributed_amounts[i] += remainder
|
|
|
+ break
|
|
|
+
|
|
|
+ return distributed_amounts
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def save_order_installment(cls, agent_order_id, package_type, package_id, profit):
|
|
|
+ """
|
|
|
+ 保存代理订单分期信息
|
|
|
+
|
|
|
+ :param cls: 类方法的约定参数
|
|
|
+ :param agent_order_id: 代理订单ID
|
|
|
+ :param package_type: 套餐类型
|
|
|
+ :param package_id: 套餐ID
|
|
|
+ :param profit: 利润总额
|
|
|
+ :return: 无返回值
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 根据包裹类型和ID获取结算时间间隔列表
|
|
|
+ time_list = cls.get_settlement_interval(package_type, package_id)
|
|
|
+ period_number = len(time_list) # 计算分期总数
|
|
|
+
|
|
|
+ # 输入合理性检查
|
|
|
+ if period_number == 0 or profit <= 0:
|
|
|
+ LOGGER.info(f'Invalid input parameters: period_number={period_number}, profit={profit}')
|
|
|
+ return
|
|
|
+
|
|
|
+ n_time = int(datetime.now().timestamp()) # 获取当前时间戳
|
|
|
+ # 利润总额按分期数平均分配
|
|
|
+ amount_list = cls.distribute_commission(profit, period_number)
|
|
|
+
|
|
|
+ installment_list = []
|
|
|
+ # 遍历分期数,生成分期记录列表
|
|
|
+ for time_point in range(period_number):
|
|
|
+ installment_list.append(AgentDeviceOrderInstallment(ado_id=agent_order_id,
|
|
|
+ period_number=period_number,
|
|
|
+ amount=amount_list[time_point],
|
|
|
+ due_date=time_list[time_point],
|
|
|
+ status=1,
|
|
|
+ created_time=n_time,
|
|
|
+ updated_time=n_time))
|
|
|
+
|
|
|
+ # 分批处理大量数据,避免数据库压力过大
|
|
|
+ batch_size = 100
|
|
|
+ for i in range(0, len(installment_list), batch_size):
|
|
|
+ AgentDeviceOrderInstallment.objects.bulk_create(
|
|
|
+ installment_list[i:i + batch_size]
|
|
|
+ )
|
|
|
+
|
|
|
+ # 记录创建完成的日志
|
|
|
+ LOGGER.info(f'*****AgentOrderView.save_OrderInstallment分期结算记录创建完成:{len(installment_list)} records')
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ # 记录异常信息
|
|
|
+ LOGGER.error('*****AgentOrderView.save_OrderInstallment:errLine:{}, errMsg:{}'
|
|
|
+ .format(e.__traceback__.tb_lineno, repr(e)))
|