瀏覽代碼

更新分期结算、序列号绑定或解绑同步到代理设备

zhangdongming 1 年之前
父節點
當前提交
1d558c2559

+ 30 - 5
AdminController/CloudServiceManage/AgentDeviceController.py

@@ -150,7 +150,8 @@ class AgentDeviceView(View):
             print(e)
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
-    def calculate_profit_or_revenue(self, agent_device_orders, package_details, time_unit, metric_type, start_time, end_time):
+    def calculate_profit_or_revenue(self, agent_device_orders, package_details, time_unit, metric_type, start_time,
+                                    end_time):
         """
         计算利润或者营业额
         @param agent_device_orders: 代理设备订单
@@ -227,7 +228,7 @@ class AgentDeviceView(View):
             agent_device_orders = AgentDeviceOrder.objects.filter(
                 ac_id=agent_customer_info.id, created_time__gte=startTime, created_time__lte=endTime, status__in=[1, 2]
             )
-            
+
             # 获取代理套餐包id
             package_ids = agent_device_orders.values_list('csp_id', flat=True).distinct()
             package_details = {pkg.id: pkg for pkg in AgentCloudServicePackage.objects.filter(id__in=package_ids)}
@@ -244,9 +245,9 @@ class AgentDeviceView(View):
                 total_4G = item['4G'] + total_4G
                 total_cloud = item['云存'] + total_cloud
             response_data = {
-                    "list": result,
-                    "total_4G": total_4G,  # 4G的总和
-                    "total_云存": total_cloud,  # 云存的总和
+                "list": result,
+                "total_4G": total_4G,  # 4G的总和
+                "total_云存": total_cloud,  # 云存的总和
             }
 
             return response.json(0, response_data)
@@ -327,3 +328,27 @@ class AgentDeviceView(View):
         except Exception as e:
             print(e)
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def device_binding_or_unbinding(cls, serial_number, bind_type):
+        """
+        设备绑定或解绑
+        @param serial_number: 设备序列号
+        @param bind_type: 绑定类型 1:绑定 2:解绑
+        @return: 无返回值
+        """
+        try:
+            # 获取设备信息
+            device_info = AgentDevice.objects.filter(serial_number=serial_number)
+            if not device_info.exists():
+                return
+            n_time = int(time.time())
+            if bind_type == 1:
+                # 绑定设备
+                device_info.update(status=1, updated_time=n_time)
+            elif bind_type == 2:
+                # 解绑设备
+                device_info.update(status=0, updated_time=n_time)
+        except Exception as e:
+            LOGGER.info('*****AgentDeviceView.device_binding_or_unbinding:errLine:{}, errMsg:{}'
+                        .format(e.__traceback__.tb_lineno, repr(e)))

+ 171 - 6
AdminController/CloudServiceManage/AgentOrderController.py

@@ -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)))

+ 13 - 0
Controller/SerialNumberController.py

@@ -25,6 +25,8 @@ from Service.CommonService import CommonService
 from Service.EquipmentInfoService import EquipmentInfoService
 from Service.VodHlsService import SplitVodHlsObject
 
+from AdminController.CloudServiceManage.AgentDeviceController import AgentDeviceView
+
 
 class SerialNumberView(View):
 
@@ -229,6 +231,11 @@ class SerialNumberView(View):
                                                                                                 redisObj))
                         thread.start()
 
+                        # 异步调用客户代理平台同步序列号绑定UID
+                        agent_thread = threading.Thread(target=AgentDeviceView.device_binding_or_unbinding,
+                                                        args=(full_serial, 1))
+                        agent_thread.start()
+
                         return response.json(0, res)
                     return response.json(5)
             elif company_serial.status == 2:  # 返回uid
@@ -575,6 +582,12 @@ class SerialNumberView(View):
                     'operation': '序列号{}解绑uid: {}'.format(serial, uid),
                 }
                 LogModel.objects.create(**log)
+
+                # 异步调用客户代理平台同步序列号解绑UID
+                agent_thread = threading.Thread(target=AgentDeviceView.device_binding_or_unbinding,
+                                                args=(serial_number[0:9], 2))
+                agent_thread.start()
+
             return response.json(0)
         except Exception as e:
             # 记录操作日志