Преглед на файлове

更新算法小店客流统计&序列号出厂自带流量套餐

zhangdongming преди 1 година
родител
ревизия
999fa5825c

+ 43 - 1
AdminController/UnicomManageController.py

@@ -21,7 +21,7 @@ from Ansjer.config import CONFIG_INFO
 from Ansjer.config import LOGGER
 from Controller.UnicomCombo.UnicomComboController import UnicomComboView
 from Model.models import UnicomDeviceInfo, UnicomCombo, Pay_Type, UnicomComboOrderInfo, Device_User, Order_Model, \
-    ExchangeCode, UnicomFlowPush, SysMsgModel, UnicomComboExperienceHistory, LogModel
+    ExchangeCode, UnicomFlowPush, SysMsgModel, UnicomComboExperienceHistory, LogModel, SerialNumberPackage
 from Object.Enums.WXOperatorEnum import WXOperatorEnum
 from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
@@ -98,6 +98,8 @@ class UnicomManageControllerView(View):
                 return self.get_flow_combo_list(request_dict, response)
             elif operation == 'downloadCDK':  # 下载兑换码
                 return self.package_cdk_export_excel(response)
+            elif operation == 'uploadSerialNumberFile':  # 上传序列号文件绑定4G套餐
+                return self.upload_file(tko.user, request, request_dict, response)
             else:
                 return response.json(404)
 
@@ -968,3 +970,43 @@ class UnicomManageControllerView(View):
         combo_qs = combo_qs.values('id', 'combo_name').order_by('-created_time')
         flow_combo_list = list(combo_qs)
         return response.json(0, flow_combo_list)
+
+    @classmethod
+    def upload_file(cls, user, request, request_dict, response):
+        """
+        上传序列号文件绑定套餐id
+        @param user: 当前操作用户
+        @param request: file txt
+        @param request_dict: package_id 4G套餐id
+        @param response: 响应对象
+        @return: 成功数以及异常序列号列表
+        """
+        package_id = request_dict.get('packageId', None)
+        file = request.FILES['file']
+        serial_list = []
+        error_list = []
+        if not package_id:
+            return response.json(444)
+        n_time = int(time.time())
+        package_id = int(package_id)
+        sn_list = []
+        for line in file:
+            serial_number = line.decode().strip()[0:9]
+            try:
+                sn_qs = SerialNumberPackage.objects.filter(serial_number=serial_number)
+                if sn_qs:
+                    error_list.append({'serialNumber': serial_number, 'msg': '此序列号已绑定套餐'})
+                    continue
+                if serial_number in serial_list:
+                    error_list.append({'serialNumber': serial_number, 'msg': 'txt存在重复序列号'})
+                    continue
+                data = {'status': 1, 'serial_number': serial_number, 'package_id': package_id,
+                        'created_time': n_time, 'updated_time': n_time, 'created_by': user, 'updated_by': user}
+                serial_number_p = SerialNumberPackage(**data)
+                serial_list.append(serial_number)
+                sn_list.append(serial_number_p)
+            except Exception as e:
+                error_list.append({'serialNumber': serial_number, 'msg': repr(e)})
+        if sn_list:
+            SerialNumberPackage.objects.bulk_create(sn_list)
+        return response.json(0, {'total': len(serial_list), 'errData': error_list})

+ 245 - 4
Controller/AlgorithmShop/AlgorithmShopController.py

@@ -6,16 +6,24 @@
 @Email   : zhangdongming@asj6.wecom.work
 @Software: PyCharm
 """
+import json
 import logging
+import math
 import time
+from datetime import datetime, timedelta
 
-from django.db.models import F, Value, CharField
+from django.db.models import F, Value, CharField, Sum
 from django.views.generic.base import View
 
 from Model.models import DeviceAlgorithmExplain, DeviceAlgorithmBanner, DeviceUidAlgorithmType, \
-    DeviceTypeAlgorithmInfo, DeviceAppScenario, DeviceScenarioLangInfo, DeviceAlgorithmScenario
+    DeviceTypeAlgorithmInfo, DeviceAppScenario, DeviceScenarioLangInfo, DeviceAlgorithmScenario, \
+    DeviceAlgorithmPassengerFlow
+from Object.ETkObject import ETkObject
+from Object.Enums.TimeZone import TimeZone
+from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
 from Object.TokenObject import TokenObject
+from Object.utils import LocalDateTimeUtil
 
 LOGGER = logging.getLogger('info')
 
@@ -32,6 +40,8 @@ class AlgorithmShopView(View):
         return self.validation(request.POST, request, operation)
 
     def validation(self, request_dict, request, operation):
+        if operation == 'passengerFlowStatistical':
+            return self.passenger_flow_statistical(request_dict, ResponseObject('en'))
         token = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
         lang = request_dict.get('lang', token.lang)
         response = ResponseObject(lang)
@@ -49,9 +59,75 @@ class AlgorithmShopView(View):
             return self.get_scenario_list(request_dict, response)
         elif operation == 'getAlgorithmListByScenarioId':  # 根据应用场景id获取算法列表
             return self.get_scenario_algorithm_list(request_dict, response)
+        elif operation == 'getPassengerFlowList':  # 获取客流统计列表
+            return self.get_passenger_flow_list(request_dict, response)
         else:
             return response.json(0)
 
+    @classmethod
+    def passenger_flow_statistical(cls, request_dict, response):
+        """
+        添加客流统计
+        Args:
+            request_dict (dict): 请求参数字典
+            response: 响应对象
+        """
+        try:
+            LOGGER.info('*****AlgorithmShopView.passenger_flow_statistical:params{}'.format(json.dumps(request_dict)))
+            sign = request_dict.get('sign')
+            eto = ETkObject(sign)
+            uid = eto.uid
+            if not uid:
+                return response.json(444)
+
+            d_time = request_dict.get('deviceTime')
+            enter_count = request_dict.get('enterCount')
+            tz_value = request_dict.get('timeZone')
+            exit_count = request_dict.get('exitCount')
+
+            if not all([sign, enter_count, exit_count, tz_value]):
+                return response.json(444)
+            enter_count = int(enter_count)
+            exit_count = int(exit_count)
+            redis = RedisObject(5)
+            key = f'ASJ:PASSENGER:FLOW:{uid}:{d_time}'
+
+            # 检查Redis中是否已存在相同key的数据
+            r_data = redis.get_data(key)
+            if r_data:
+                return response.json(0)
+
+            now_time = int(time.time())
+            data = {
+                'uid': uid,
+                'updated_time': now_time,
+                'created_time': now_time,
+                'device_time': int(d_time)
+            }
+
+            tz = TimeZone.get_value(int(tz_value))
+            date_time = LocalDateTimeUtil.time_format_date(int(d_time), tz)
+            data['statistical_time'] = datetime.strptime(date_time, '%Y-%m-%d %H:%M:%S')
+
+            LOGGER.info(f'uid{uid}-DeviceTime:{d_time},tz:{tz},结果:{date_time}')
+            passenger_flow_list = []
+            data['count'] = enter_count
+            data['type'] = 1
+            passenger_flow_list.append(DeviceAlgorithmPassengerFlow(**data))
+            data['count'] = exit_count
+            data['type'] = 2
+            passenger_flow_list.append(DeviceAlgorithmPassengerFlow(**data))
+            DeviceAlgorithmPassengerFlow.objects.bulk_create(passenger_flow_list)
+            # 将数据存入Redis,并设置过期时间为10分钟
+            redis.CONN.setnx(key, d_time)
+            redis.CONN.expire(key, 600)
+
+            return response.json(0)
+        except Exception as e:
+            LOGGER.info('***get_algorithm_list_by_scenario_id,errLine:{}, errMsg:{}'
+                        .format(e.__traceback__.tb_lineno, repr(e)))
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
     @classmethod
     def get_algorithm_list_by_scenario_id(cls, scenario_id, lang):
         """
@@ -191,7 +267,7 @@ class AlgorithmShopView(View):
             return response.json(0, result_dto)
         except Exception as e:
             LOGGER.info('接口异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
-            return response.json(500, repr(e))
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
     @classmethod
     def get_scenario_algorithm_list(cls, request_dist, response):
@@ -269,7 +345,7 @@ class AlgorithmShopView(View):
             return response.json(0, algorithm_list)
         except Exception as e:
             print('查询算法小店列表异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
-            return response.json(500, repr(e))
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
     @classmethod
     def get_algorithm_details(cls, request_dict, response):
@@ -363,3 +439,168 @@ class AlgorithmShopView(View):
         except Exception as e:
             print('保存算法设置异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
             return response.json(177, repr(e))
+
+    @classmethod
+    def get_passenger_flow_list(cls, request_dict, response):
+        """
+        获取客流统计列表
+        """
+        try:
+            uid = request_dict.get('uid')
+            now_time = request_dict.get('nowTime')
+            flow_type = request_dict.get('flowType')
+            query_type = request_dict.get('queryType')
+            if not all([now_time, flow_type, query_type]):
+                return response.json(444)
+            query_type = int(query_type)
+            flow_type = int(flow_type)
+            passenger_flow_list = []
+            # 将需要查询的时间戳列表拼接成缓存的键名
+            cache_key = f"ASJ:PASSENGER_FLOW:{uid}_{flow_type}_{query_type}_{now_time}"
+            redis = RedisObject(5)
+            passenger_flow_json = redis.get_data(cache_key)
+            if passenger_flow_json:
+                return response.json(0, json.loads(passenger_flow_json))
+            if query_type == 1:
+                passenger_flow_list = cls.get_passenger_flow_by_day(uid, int(now_time), flow_type)
+            elif query_type == 2:
+                passenger_flow_list = cls.get_passenger_flow_by_month(uid, int(now_time), flow_type)
+            elif query_type == 3:
+                passenger_flow_list = cls.get_passenger_flow_by_year(uid, int(now_time), flow_type)
+            else:
+                return response.json(0, passenger_flow_list)
+            if passenger_flow_list:
+                redis.CONN.setnx(cache_key, json.dumps(passenger_flow_list))
+                redis.CONN.expire(cache_key, 3600 * 24)
+            return response.json(0, passenger_flow_list)
+        except Exception as e:
+            print('查询客流异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+            return response.json(500, repr(e))
+
+    @staticmethod
+    def get_passenger_flow_by_day(uid, now_time, flow_type):
+        """
+        按天获取客流统计
+        """
+        c_time = datetime.fromtimestamp(int(now_time))
+        # 一天周期24小时从当前小时整点往前查询近24小时,生成秒级时间戳
+        t_list = [math.floor((c_time - timedelta(hours=i)).timestamp()) for i in range(24)]
+        passenger_flow_list = []
+        pf_qs = DeviceAlgorithmPassengerFlow.objects.filter(
+            device_time__gte=t_list[23],
+            device_time__lte=t_list[0] + 60,
+            type=int(flow_type),
+            uid=uid
+        ).order_by('device_time')
+        if not pf_qs.exists():
+            return passenger_flow_list
+        # 循环查找最近24小时的记录(包含当前时间)
+        for i in range(24):
+            current_time = t_list[i]
+            next_time = -1 if i == 0 else t_list[i - 1]
+            is_data = False
+            count = 0
+            hour = 0
+            for item in pf_qs:
+                if next_time == -1 and next_time == item.device_time:
+                    hour = item.statistical_time.hour
+                    count += item.count
+                    is_data = True
+                elif current_time <= item.device_time < next_time:
+                    count += item.count
+                    hour = item.statistical_time.hour
+                    is_data = True
+                elif next_time == -1 and current_time <= item.device_time:
+                    hour = item.statistical_time.hour
+                    count += item.count
+                    is_data = True
+            if not is_data:
+                continue
+            passenger_flow_list.append({
+                'index': hour,
+                'count': count,
+                'time': current_time
+            })
+        return passenger_flow_list
+
+    @staticmethod
+    def get_passenger_flow_by_month(uid, now_time, flow_type):
+        """
+        按月获取客流统计
+        """
+
+        # 获取30天前的时间戳作为起始时间
+        s_time = LocalDateTimeUtil.get_before_days_timestamp(now_time, 30)
+        # 将起始时间设置为当天的开始时间
+        s_time = LocalDateTimeUtil.start_of_day_timestamp(s_time)
+
+        # 查询指定类型的客流数据,按设备时间升序排序
+        pf_qs = DeviceAlgorithmPassengerFlow.objects.filter(
+            device_time__range=(s_time, now_time),
+            type=int(flow_type),
+            uid=uid
+        ).order_by('device_time')
+
+        passenger_flow_list = []
+        if not pf_qs.exists():
+            return passenger_flow_list
+
+        for i in range(30):  # 查询月按30天为一个周期
+            if i == 0:
+                # 当天的开始时间作为查询的起始时间
+                s_time = LocalDateTimeUtil.start_of_day_timestamp(now_time)
+            else:
+                # 获取i天前的时间戳作为查询的起始时间
+                s_time = LocalDateTimeUtil.get_before_days_timestamp(now_time, i)
+                # 将起始时间设置为当天的开始时间
+                s_time = LocalDateTimeUtil.start_of_day_timestamp(s_time)
+
+            days_qs = []
+            day = 0
+            for item in pf_qs:
+                # 计算结束时间
+                end_time = now_time if i == 0 else s_time + (3600 * 24)
+
+                # 将在起始时间和结束时间范围内的客流数据放入列表中
+                if s_time < item.device_time <= end_time:
+                    days_qs.append(item.count)
+                    day = item.statistical_time.day if day == 0 else day
+
+            if days_qs:
+                # 将每天的客流数据统计结果添加到列表中
+                passenger_flow_list.append({
+                    'index': day,
+                    'count': sum(days_qs),
+                    'time': s_time
+                })
+
+        return passenger_flow_list
+
+    @staticmethod
+    def get_passenger_flow_by_year(uid, now_time, flow_type):
+        """
+        按年获取客流统计
+        """
+        month_time = 0
+        passenger_flow_list = []
+        for i in range(12):
+            if i == 0:
+                month_time = LocalDateTimeUtil.get_current_month_first_day(now_time)
+            df_qs = DeviceAlgorithmPassengerFlow.objects.filter(type=flow_type, uid=uid)
+            if i == 0:
+                df_qs = df_qs.filter(device_time__range=(month_time, now_time))
+                data_qs = df_qs.first()
+            else:
+                previous_month_time = LocalDateTimeUtil.get_previous_month_first_day(month_time)
+                df_qs = df_qs.filter(device_time__range=(previous_month_time, month_time))
+                data_qs = df_qs.first()
+                month_time = previous_month_time
+            df_qs = df_qs.aggregate(total=Sum('count'))
+            if not df_qs['total']:
+                continue
+            passenger_flow_list.append({
+                'index': data_qs.statistical_time.month,
+                'count': df_qs['total'],
+                'time': month_time
+            })
+        return passenger_flow_list

+ 94 - 1
Controller/UnicomCombo/WXTechController.py

@@ -14,7 +14,7 @@ from django.views import View
 from Ansjer.config import LOGGER
 from Controller.UnicomCombo.UnicomComboController import UnicomComboView
 from Model.models import UnicomDeviceInfo, Order_Model, UnicomComboExperienceHistory, UnicomCombo, ExchangeCode, \
-    LogModel
+    LogModel, SerialNumberPackage
 from Object.Enums.WXOperatorEnum import WXOperatorEnum
 from Object.Enums.WXStartTypeEnum import WXStartTypeEnum
 from Object.ResponseObject import ResponseObject
@@ -68,6 +68,10 @@ class WXTechController(View):
                 return self.create_order_package(user_id, request_dict, response)
             elif operation == 'exchangePackage':
                 return self.wx_exchange_package(request_dict, response, request, user_id)
+            elif operation == 'getPackageBySerialNumber':
+                return self.get_package_by_serial_number(request_dict, response, request, user_id)
+            elif operation == 'create4GFlowPackage':
+                return self.create_4G_flow_package(request_dict, response, request, user_id)
 
         @classmethod
         def wx_package_refund(cls, request_dict, response):
@@ -355,3 +359,92 @@ class WXTechController(View):
                 LOGGER.info('*****WXTechController.save_log:errLine:{}, errMsg:{}'
                             .format(e.__traceback__.tb_lineno, repr(e)))
                 return False
+
+        @classmethod
+        def get_package_by_serial_number(cls, request_dict, response, request, user_id):
+            """
+            根据序列号获取无限流量套餐信息
+            """
+            try:
+                serial_number = request_dict.get('serialNumber', None)
+                if not serial_number:
+                    return response.json(444)
+                serial_package_qs = SerialNumberPackage.objects.filter(serial_number=serial_number, status=1)
+                data = {}
+                if not serial_package_qs:
+                    return response.json(0, data)
+                serial_package = serial_package_qs.first()
+                package_id = serial_package.package_id
+                package_info_qs = UnicomCombo.objects.filter(id=package_id).values('combo_name')
+                if not package_info_qs.exists():
+                    return response.json(0, data)
+                result = {'comboName': package_info_qs[0]['combo_name']}
+                return response.json(110, result)
+            except Exception as e:
+                LOGGER.info('*****WXTechController.get_package_by_serial_number:errLine:{}, errMsg:{}'
+                            .format(e.__traceback__.tb_lineno, repr(e)))
+                return response.json(503)
+
+        @classmethod
+        def create_4G_flow_package(cls, request_dict, response, request, user_id):
+            """
+            创建4G流量套餐(出厂序列号绑定套餐)
+            """
+            ip = CommonService.get_ip_address(request)
+            serial_number = ''
+            try:
+                serial_number = request_dict.get('serialNumber')
+                if not serial_number:
+                    return response.json(0)
+                device_info_qs = UnicomDeviceInfo.objects.filter(serial_no=serial_number) \
+                    .values('iccid', 'card_type')
+                if not device_info_qs.exists():
+                    return response.json(173)  # 设备信息不存在
+
+                serial_package_qs = SerialNumberPackage.objects.filter(serial_number=serial_number, status=1)
+                if not serial_package_qs:
+                    return response.json(10064)
+
+                combo_id = serial_package_qs.first().package_id
+                # 获取餐套餐信息
+                package_info_qs = UnicomCombo.objects.filter(id=combo_id, is_del=False) \
+                    .values('package_id', 'combo_name')
+                if not package_info_qs.exists():
+                    return response.json(173)
+                # 得到五兴电信运营商套餐编码
+                package_code = package_info_qs[0]['package_id']
+                combo_name = package_info_qs[0]['combo_name']
+                iccid = device_info_qs[0]['iccid']
+
+                n_time = int(time.time())
+                # 兑换码套餐类型
+                card_type = device_info_qs[0]['card_type']
+                if card_type == 0:  # 订购联通流量套餐
+                    result = cls.exchange_unicom_package(serial_number, iccid,
+                                                         user_id, combo_id)
+                    serial_package_qs.update(status=2, updated_time=n_time, updated_by=user_id)
+                    if result:
+                        cls.save_log(ip, 10071, '{}领取{}成功{}'.format(serial_number, combo_name, user_id))
+                        return response.json(10071)
+                elif card_type == 1:  # 订购五兴电信流量套餐
+                    # 五兴订购流量包请求参数
+                    data = {'iccid': iccid, 'operator': WXOperatorEnum.TELECOM.value,
+                            'startType': str(WXStartTypeEnum.EFFECTIVE_IMMEDIATELY.value), 'packageCode': package_code}
+                    wx_tech = WXTechObject()
+                    # 请求五兴API订购套餐接口
+                    res = wx_tech.create_order_package(**data)
+                    LOGGER.info('*****五兴订购套餐结果:{}'.format(res))
+                    if res['code'] == '0':
+                        trade_no = res['data']['orderNumber']
+                        cls.created_order(serial_number, user_id, trade_no, combo_id, 11)
+                        serial_package_qs.update(status=2, updated_time=n_time, updated_by=user_id)
+                        cls.save_log(ip, 10071, '{}领取{}成功{}'.format(serial_number, combo_name, user_id))
+                        return response.json(10071)
+
+                cls.save_log(ip, 10064, '无效卡类型{}领取{}失败{}'.format(serial_number, combo_name, user_id))
+                return response.json(10064)
+            except Exception as e:
+                LOGGER.info('*****WXTechController.wx_exchange_package:errLine:{}, errMsg:{}'
+                            .format(e.__traceback__.tb_lineno, repr(e)))
+                cls.save_log(ip, 500, f'{serial_number}领取流量套餐异常')
+                return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 33 - 1
Model/models.py

@@ -3076,7 +3076,7 @@ class DeviceAlgorithmScenario(models.Model):
 class DeviceAlgorithmType(models.Model):
     id = models.AutoField(primary_key=True)
     # 0:移动侦测,1:人形检测,2:挥手识别,3:人脸检测,4:异声感知,5:车辆检测,7:宠物检测,6:哭声检测,8:徘徊检测
-    # 9:区域闯入,10:区域闯出,11:长时间无人检测,12:往来检测,13:云相册
+    # 9:区域闯入,10:区域闯出,11:长时间无人检测,12:往来检测,13:云相册,14:火焰检测
     type = models.SmallIntegerField(default=0, verbose_name='算法类型')
     memory = models.CharField(max_length=32, default='', verbose_name='所需内存')
     down_count = models.IntegerField(default=0, verbose_name='下载次数')
@@ -3167,6 +3167,22 @@ class DeviceTypeAlgorithmInfo(models.Model):
         verbose_name_plural = verbose_name
 
 
+class DeviceAlgorithmPassengerFlow(models.Model):
+    id = models.AutoField(primary_key=True)
+    uid = models.CharField(max_length=32, db_index=True, default='', verbose_name='设备uid')
+    type = models.SmallIntegerField(default=0, verbose_name='客流类型,1:进,2:离开')
+    count = models.IntegerField(default=0, verbose_name='当前时段统计数量')
+    statistical_time = models.DateTimeField(blank=True, null=True, verbose_name=u'统计日期时间')
+    device_time = models.IntegerField(default=0, verbose_name='设备上报时间戳')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'device_algorithm_passenger_flow'
+        verbose_name = '设备算法客流统计'
+        verbose_name_plural = verbose_name
+
+
 class VodHlsSummary(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='回放汇总列表主键')
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
@@ -3611,3 +3627,19 @@ class DeviceLiveRestrict(models.Model):
         db_table = 'device_live_restrict'
         verbose_name = '设备直播限制表'
         verbose_name_plural = verbose_name
+
+
+class SerialNumberPackage(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    serial_number = models.CharField(default='', db_index=True, max_length=32, verbose_name=u'设备序列号')
+    status = models.SmallIntegerField(default=1, verbose_name='状态,1:可激活,2:已激活')
+    package_id = models.IntegerField(default=0, verbose_name='关联4G套餐表id')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    created_by = models.CharField(blank=True, max_length=32, verbose_name='创建人')
+    updated_time = models.IntegerField(default=0, verbose_name='修改时间')
+    updated_by = models.CharField(blank=True, max_length=32, verbose_name='修改人')
+
+    class Meta:
+        db_table = 'd_serial_number_package'
+        verbose_name = '设备序列号关联套餐id表'
+        verbose_name_plural = verbose_name

+ 61 - 0
Object/Enums/TimeZone.py

@@ -0,0 +1,61 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : TimeZone.py
+@Time    : 2023/8/15 15:27
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+from enum import Enum
+
+
+class TimeZone(Enum):
+    INTERNATIONAL_DATE_LINE_WEST = (0, '-12:00')
+    MIDWAY_ISLAND_SAMOA = (1, '-11:00')
+    HAWAII = (2, '-10:00')
+    ALASKA = (3, '-09:00')
+    PACIFIC_TIME_US_CANADA = (4, '-08:00')
+    MOUNTAIN_TIME_US_CANADA = (5, '-07:00')
+    CENTRAL_TIME_US_CANADA = (6, '-06:00')
+    EASTERN_TIME_US_CANADA = (7, '-05:00')
+    CARACAS = (8, '-04:30')
+    ATLANTIC_TIME_CANADA = (9, '-04:00')
+    NEWFOUNDLAND = (10, '-03:50')
+    GEORGETOWN_BRASILIA = (11, '-03:00')
+    MID_ATLANTIC = (12, '-02:00')
+    CAPE_VERDE_ISLANDS_AZORES = (13, '-01:00')
+    DUBLIN_EDINBURGH_LONDON = (14, '+00:00')
+    AMSTERDAM_BERLIN_ROME_PARIS = (15, '+01:00')
+    ATHENS_JERUSALEM_ISTANBUL = (16, '+02:00')
+    BAGHDAD_KUWAIT_MOSCOW = (17, '+03:00')
+    TEHRAN = (18, '+03:30')
+    CAUCASUS_STANDARD_TIME = (19, '+04:00')
+    KABUL = (20, '+04:50')
+    ISLAMABAD_KARACHI_TASHKENT = (21, '+05:00')
+    MADRAS_BOMBAY_NEW_DELHI = (22, '+05:50')
+    KATHMANDU = (23, '+05:45')
+    ALMATY_NOVOSIBIRSK_DHAKA = (24, '+06:00')
+    YANGON = (25, '+06:30')
+    BANGKOK_HANOI_JAKARTA = (26, '+07:00')
+    BEIJING_URUMQI_SINGAPORE = (27, '+08:00')
+    SEOUL_TOKYO_OSAKA_SAPPORO = (28, '+09:00')
+    ADELAIDE_DARWIN = (29, '+09:30')
+    MELBOURNE_SYDNEY_CANBERRA = (30, '+10:00')
+    MAGADAN_SOLOMON_ISLANDS = (31, '+11:00')
+    AUCKLAND_WELLINGTON_FIJI = (32, '+12:00')
+    UTC_13 = (33, '+13:00')
+
+    def __str__(self):
+        return f"{self.value[1]}) {self.name.replace('_', ' ')}"
+
+    @classmethod
+    def get_value(cls, index):
+        for timezone in cls:
+            if timezone.value[0] == index:
+                return timezone.value[1]
+        return None
+
+
+if __name__ == '__main__':
+    result = TimeZone.get_value(30)
+    print(result)

+ 8 - 2
Object/ResponseObject.py

@@ -140,7 +140,10 @@ class ResponseObject(object):
             10066: 'This code has been redeemed and can be viewed in Settings-4G-My Package',
             10067: 'Invalid redemption code',
             10068: 'This device does not support redemption, please contact customer service',
-            10069: 'If the traffic is abnormal, check the data plan or contact customer service'
+            10069: 'If the traffic is abnormal, check the data plan or contact customer service',
+            10070: 'This device sharing limit has been reached',
+            503: 'The operation failed, please try again later',
+            10071: 'The successful collection can be viewed in Settings - 4G-My package'
         }
         data_cn = {
             0: '成功',
@@ -267,7 +270,10 @@ class ResponseObject(object):
             10066: '\t此码已兑换\n可在设置-4G-我的套餐中查看',
             10067: '无效兑换码',
             10068: '兑换失败,请联系客服',
-            10069: '流量异常,请查看流量套餐情况或联系客服'
+            10069: '流量异常,请查看流量套餐情况或联系客服',
+            10070: '此设备分享已达上限',
+            503: '操作失败,请稍后重试',
+            10071: '\t领取成功\n可在设置-4G-我的套餐中查看'
         }
 
         msg = data_cn if self.lang == 'cn' or self.lang == 'zh-Hans' or self.lang == 'zh-Hant' else data_en