| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 | 
							- # -*- encoding: utf-8 -*-
 
- """
 
- @File    : AgentOrderController.py
 
- @Time    : 2024/3/14 10:53
 
- @Author  : stephen
 
- @Email   : zhangdongming@asj6.wecom.work
 
- @Software: PyCharm
 
- """
 
- import threading
 
- import time
 
- 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, \
 
-     AgentDeviceOrderInstallment, AgentAccount
 
- from Ansjer.config import LOGGER
 
- from Model.models import Order_Model, Store_Meal, UnicomCombo, TimeZoneInfo
 
- from Object.CeleryBeatObject import CeleryBeatObj
 
- from Object.ResponseObject import ResponseObject
 
- from Object.TokenObject import TokenObject
 
- from Service.CommonService import CommonService
 
- class AgentOrderView(View):
 
-     def get(self, request, *args, **kwargs):
 
-         request.encoding = 'utf-8'
 
-         operation = kwargs.get('operation')
 
-         return self.validation(request.GET, request, operation)
 
-     def post(self, request, *args, **kwargs):
 
-         request.encoding = 'utf-8'
 
-         operation = kwargs.get('operation')
 
-         return self.validation(request.POST, request, operation)
 
-     def delete(self, request, *args, **kwargs):
 
-         request.encoding = 'utf-8'
 
-         operation = kwargs.get('operation')
 
-         delete = QueryDict(request.body)
 
-         if not delete:
 
-             delete = request.GET
 
-         return self.validation(delete, request, operation)
 
-     def put(self, request, *args, **kwargs):
 
-         request.encoding = 'utf-8'
 
-         operation = kwargs.get('operation')
 
-         put = QueryDict(request.body)
 
-         return self.validation(put, request, operation)
 
-     def validation(self, request_dict, request, operation):
 
-         response = ResponseObject()
 
-         tko = TokenObject(
 
-             request.META.get('HTTP_AUTHORIZATION'),
 
-             returntpye='pc')
 
-         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)
 
-             package_id = request_dict.get('package_id', None)
 
-             self.check_agent_service_package(order_id, uid, int(package_id))
 
-             return response.json(0)
 
-         elif operation == 'addSettlementJob':
 
-             self.add_settlement_job()
 
-             return response.json(0)
 
-         elif operation == 'delSettlementJob':
 
-             self.del_settlement_job()
 
-             return response.json(0)
 
-         elif operation == 'updateSettlementJob':
 
-             self.update_settlement_job()
 
-             return response.json(0)
 
-     @classmethod
 
-     def update_settlement_job(cls):
 
-         celery_beat_obj = CeleryBeatObj()
 
-         job_name = 'Agent-updateSettlement'
 
-         time_zone_info_qs = TimeZoneInfo.objects.filter(tz=8).values('zone_info')
 
-         if time_zone_info_qs.exists():
 
-             time_zone = time_zone_info_qs[0]['zone_info']
 
-             cron_tuple = ('*/3', '*', '*', '*', '*', time_zone)
 
-             celery_beat_obj.update_task(name=job_name, crontab=cron_tuple)
 
-     @classmethod
 
-     def add_settlement_job(cls):
 
-         celery_beat_obj = CeleryBeatObj()
 
-         job_name = 'Agent-updateSettlement'
 
-         SMART_SCENE_TASK = 'Controller.CeleryTasks.tasks.update_installment_settlement_order'
 
-         celery_beat_obj.creat_crontab_task(
 
-             timezone_offset=8, name=job_name, task=SMART_SCENE_TASK, minute='*/2')
 
-     @classmethod
 
-     def del_settlement_job(cls):
 
-         celery_beat_obj = CeleryBeatObj()
 
-         job_name = 'Agent-updateSettlement'
 
-         celery_beat_obj.del_task(job_name)
 
-     @classmethod
 
-     def check_agent_service_package(cls, order_id, uid, package_id):
 
-         """
 
-         检查是否代理服务套餐
 
-         @param package_id: 套餐id
 
-         @param order_id: 订单ID
 
-         @param uid: UID
 
-         @return: True | False
 
-         """
 
-         try:
 
-             serial_number = CommonService.get_serial_number_by_uid(uid)
 
-             a_device_qs = AgentDevice.objects.filter(serial_number=serial_number) \
 
-                 .values('ac_id', 'type', 'status')
 
-             LOGGER.info(f'******check_agent_service_package检查是否代理*****orderId:{order_id}')
 
-             if not a_device_qs.exists():
 
-                 return False
 
-             LOGGER.info(f'******check_agent_service_package当前设备属于代理商*****serial_number:{serial_number}')
 
-             asy = threading.Thread(target=cls.save_agent_package,
 
-                                    args=(order_id, serial_number, a_device_qs[0]['ac_id'], package_id))
 
-             asy.start()
 
-             return True
 
-         except Exception as e:
 
-             LOGGER.info('*****AgentOrderView.check_agent_service_package:errLine:{}, errMsg:{}'
 
-                         .format(e.__traceback__.tb_lineno, repr(e)))
 
-         return False
 
-     @classmethod
 
-     def save_agent_package(cls, order_id, serial_number, ac_id, package_id):
 
-         """
 
-         保存代理套餐
 
-         """
 
-         try:
 
-             order_qs = Order_Model.objects.filter(orderID=order_id, status=1).values('price', 'payTime', 'order_type')
 
-             if not order_qs.exists():
 
-                 LOGGER.info(f'******save_agent_package当前代理客户未添加此套餐******ac_id:{ac_id},package_id:{package_id}')
 
-                 return
 
-             order_type = order_qs[0]['order_type']
 
-             package_type = 2 if order_type in [2, 3, 5] else 1  # 判断订单信息是云存还是4G
 
-             package_id = int(package_id)
 
-             agent_package_qs = AgentCloudServicePackage.objects.filter(type=package_type, package_id=package_id,
 
-                                                                        status=1)
 
-             if not agent_package_qs.exists():
 
-                 LOGGER.info(f'******save_agent_package当前套餐未设置代理******order_id:{order_id},serial_number:{serial_number}')
 
-                 return
 
-             agent_package = agent_package_qs.first()  # 代理云服务套餐
 
-             LOGGER.info(f'******save_agent_package代理套餐******service_name:{agent_package_qs.first().service_name}')
 
-             acp_qs = AgentCustomerPackage.objects.filter(ac_id=ac_id, cs_id=agent_package.id).values('id')
 
-             if not acp_qs.exists():
 
-                 LOGGER.info(f'******save_agent_package当前代理客户未添加此套餐******ac_id:{ac_id},package_id:{package_id}')
 
-                 return
 
-             # 组装数据
 
-             now_time = int(time.time())
 
-             pay_price = Decimal(order_qs[0]['price']).quantize(Decimal('0.00'))
 
-             profit = cls.calculate_order_profit(agent_package, pay_price)
 
-             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}
 
-             agent_order_obj = AgentDeviceOrder.objects.create(**dict_data)
 
-             # 保存分期结算记录
 
-             cls.save_order_installment(agent_order_obj.id, package_type, package_id, profit, ac_id)
 
-             LOGGER.info(f'******save_agent_package代理订单存表结束:{dict_data}')
 
-         except Exception as e:
 
-             LOGGER.info('*****AgentOrderView.save_agent_package:errLine:{}, errMsg:{}'
 
-                         .format(e.__traceback__.tb_lineno, repr(e)))
 
-     @classmethod
 
-     def calculate_order_profit(cls, agent_package, price):
 
-         """
 
-         计算利润
 
-         @param agent_package: 套餐配置
 
-         @param price: 支付价格
 
-         @return: 利润
 
-         """
 
-         profit = 0
 
-         price = Decimal(price).quantize(Decimal('0.00'))
 
-         if agent_package.profit_type == 1:
 
-             profit = agent_package.profit
 
-         elif agent_package.profit_type == 2:
 
-             profit_value = Decimal(agent_package.profit).quantize(Decimal('0.00'))
 
-             cost = Decimal(agent_package.cost).quantize(Decimal('0.00'))
 
-             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, ac_id=None):
 
-         """
 
-         保存代理订单分期信息
 
-         :param cls: 类方法的约定参数
 
-         :param agent_order_id: 代理订单ID
 
-         :param package_type: 套餐类型
 
-         :param package_id: 套餐ID
 
-         :param profit: 利润总额
 
-         :return: 无返回值
 
-         :param ac_id: 代理客户ID
 
-         """
 
-         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,
 
-                                                                     ac_id=ac_id,
 
-                                                                     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)))
 
-     @staticmethod
 
-     def update_periodic_settlement():
 
-         """
 
-         更新周期结算信息
 
-         返回值:
 
-         - 无返回值
 
-         """
 
-         try:
 
-             # 根据条件查询需要更新结算信息的订单分期记录
 
-             adoi_qs = AgentDeviceOrderInstallment.objects.filter(status=1, due_date__lte=int(time.time()))
 
-             if not adoi_qs:
 
-                 # 如果没有找到符合条件的记录,直接返回
 
-                 return
 
-             ids = []
 
-             a_account_list = []
 
-             adoi_set = set()
 
-             n_time = int(time.time())
 
-             for item in adoi_qs:
 
-                 # 准备分期记录的id列表和账户记录列表
 
-                 ids.append(item.id)
 
-                 adoi_set.add(item.ado_id)
 
-                 a_account_list.append(AgentAccount(ac_id=item.ac_id, amount=item.amount,
 
-                                                    remark=f'周期结算',
 
-                                                    status=1, created_time=n_time,
 
-                                                    updated_time=n_time))
 
-             batch_size = 100
 
-             # 分批更新分期记录状态
 
-             for i in range(0, len(ids), batch_size):
 
-                 AgentDeviceOrderInstallment.objects.filter(id__in=ids[i:i + batch_size]) \
 
-                     .update(status=2, settlement_time=n_time, updated_time=n_time)
 
-             # 分批创建账户记录
 
-             for i in range(0, len(a_account_list), batch_size):
 
-                 AgentAccount.objects.bulk_create(a_account_list[i:i + batch_size])
 
-             # 检查是否所有分期都已结算,如果是,则更新订单状态为已结算
 
-             for ado in adoi_set:
 
-                 adoi_qs = AgentDeviceOrderInstallment.objects.filter(ado_id=ado, status=1)
 
-                 if not adoi_qs.exists():
 
-                     AgentDeviceOrder.objects.filter(id=ado, status=1) \
 
-                         .update(status=2, settlement_time=n_time, updated_time=n_time)
 
-         except Exception as e:
 
-             # 记录异常信息
 
-             LOGGER.error(
 
-                 f'*****AgentOrderView.update_periodic_settlement:errLine:{e.__traceback__.tb_lineno}, errMsg:{str(e)}')
 
 
  |