Selaa lähdekoodia

新增APP设备图标功能配置字段、修复4G批量重置问题

zhangdongming 1 vuosi sitten
vanhempi
commit
1303bf01bc

+ 3 - 1
AdminController/DeviceManagementController.py

@@ -796,8 +796,9 @@ class DeviceManagement(View):
                     model=F('app_device_type__model'), type=F('app_device_type__type'), icon=F('app_device_type__icon'),
                     name=F('app_device_type__devicenamelanguage__name'),
                     sort=F('app_device_type__devicenamelanguage__sort'),
+                    config=F('app_device_type__config'),
                     app_version_number_id=F('app_device_type__app_version_number_id')).values('model', 'type', 'icon',
-                                                                                              'name', 'sort',
+                                                                                              'name', 'sort', 'config',
                                                                                               'app_device_type__app_version_number_id')
                 for app_bundle in app_bundle_qs:
                     app_bundle_list.append({
@@ -806,6 +807,7 @@ class DeviceManagement(View):
                         'icon': app_bundle['icon'],
                         'name': app_bundle['name'],
                         'sort': app_bundle['sort'],
+                        'config': app_bundle['config'],
                         'app_device_type__app_version_number_id': app_bundle['app_device_type__app_version_number_id'],
                     })
             dvr_list = [app_bundle for app_bundle in app_bundle_list if app_bundle['model'] == 1]

+ 197 - 40
AdminController/UnicomManageController.py

@@ -12,6 +12,7 @@ import threading
 import time
 import uuid
 from decimal import Decimal
+from math import ceil
 
 import openpyxl
 import requests
@@ -36,6 +37,8 @@ from Object.WXTechObject import WXTechObject
 from Service.CommonService import CommonService
 from Service.TelecomService import TelecomService
 
+UNICOM_MANAGE_LOCK = 'BASIC:UNICOM:MANAGE:OPERATION:LOCK'
+
 
 class UnicomManageControllerView(View):
 
@@ -51,6 +54,7 @@ class UnicomManageControllerView(View):
 
     def validation(self, request_dict, request, operation):
         response = ResponseObject()
+
         # 获取支付类型
         if operation == 'get/pay':
             return self.get_pay_type(response)
@@ -105,7 +109,7 @@ class UnicomManageControllerView(View):
             elif operation == 'downloadCDK':  # 下载兑换码
                 return self.package_cdk_export_excel(response)
             elif operation == 'uploadSerialNumberFile':  # 上传序列号文件绑定4G套餐
-                return self.upload_file(userID, tko.user, request, request_dict, response)
+                return self.serial_number_bind_package_bulk(userID, tko.user, request, request_dict, response)
             elif operation == 'iccidBatchReset':  # 批量重置流量
                 return self.iccid_batch_reset(tko.user, request, request_dict, response)
             elif operation == 'orderFlowPackage':  # 订购流量套餐包
@@ -116,6 +120,10 @@ class UnicomManageControllerView(View):
                 return self.serial_number_package_update(request_dict, response, tko.user)
             elif operation == 'transferDevicePackage':  # 转移设备套餐
                 return self.transfer_device_package(request_dict, response, tko.user)
+            elif operation == 'updateExpirationDate':  # 修改套餐过期时间
+                return self.update_expiration_date(request_dict, response)
+            if operation == 'verifyPackageExport':
+                return self.verify_package_export_excel(request, request_dict, response)
             else:
                 return response.json(404)
 
@@ -828,7 +836,7 @@ class UnicomManageControllerView(View):
         card_type = ud_qs[0]['card_type']
         if card_type == 0 or card_type == 3:
             o_qs = UnicomComboOrderInfo.objects.filter(iccid=iccid) \
-                .values('status', 'flow_total_usage', 'flow_exceed', 'activation_time', 'expire_time',
+                .values('id', 'status', 'flow_total_usage', 'flow_exceed', 'activation_time', 'expire_time',
                         'combo__combo_name', 'combo__flow_total', 'updated_time') \
                 .order_by('created_time')
             if not o_qs:
@@ -865,6 +873,7 @@ class UnicomManageControllerView(View):
             status_dict = {0: "待使用", 1: "使用中", 2: "已失效"}
             status = status_dict.get(item['status'])
             package_list.append({
+                'id': item['id'],
                 'packageName': item['combo__combo_name'],
                 'status': status,
                 'flowTotal': flow_total,
@@ -1066,7 +1075,7 @@ class UnicomManageControllerView(View):
         return response.json(0, flow_combo_list)
 
     @classmethod
-    def upload_file(cls, user_id, user, request, request_dict, response):
+    def serial_number_bind_package_bulk(cls, user_id, user, request, request_dict, response):
         """
         上传序列号文件绑定套餐id
         @param user_id: user_id
@@ -1076,46 +1085,68 @@ class UnicomManageControllerView(View):
         @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 = []
-
-        uc_qs = UnicomCombo.objects.filter(id=package_id)
-        if not uc_qs.exists():
-            return response.json(173)
+        redis_obj = RedisObject(5)
+        try:
+            lock = redis_obj.try_lock(UNICOM_MANAGE_LOCK, 'iccid', 15, 60)
+            if not lock:
+                return response.json(10074)
 
-        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)
+            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 = []
 
-            # 终身免流量时只保留一个有效套餐并自动激活
-            if uc_qs.first().remark == 'LIFETIME_FREE':
-                asy = threading.Thread(target=cls.activate_4G_lifetime_free, args=(sn_list, user_id))
-                asy.start()
+            uc_qs = UnicomCombo.objects.filter(id=package_id)
+            if not uc_qs.exists():
+                return response.json(173)
 
-        return response.json(0, {'total': len(serial_list), 'errData': error_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:
+                # 计算总数量和页数,仅计算一次
+                total_count = len(sn_list)
+                pages = ceil(total_count / 100)
+
+                # 遍历每一页,使用列表切片处理批次
+                for page in range(pages):
+                    # 计算本次循环实际处理的结束索引,考虑最后一页可能不足100的情况
+                    end = min((page + 1) * 100, total_count)
+                    # 切片获取当前批次的sn_list
+                    batch_sn_list = sn_list[page * 100:end]
+
+                    # 执行批量创建
+                    SerialNumberPackage.objects.bulk_create(batch_sn_list)
+
+                # 终身免流量时只保留一个有效套餐并自动激活
+                if uc_qs.first().remark == 'LIFETIME_FREE':
+                    asy = threading.Thread(target=cls.activate_4G_lifetime_free, args=(sn_list, user_id))
+                    asy.start()
+            # 释放锁
+            redis_obj.release_lock(UNICOM_MANAGE_LOCK, 'iccid')
+            return response.json(0, {'total': len(serial_list), 'errData': error_list})
+        except Exception as e:
+            LOGGER.info('批量绑定异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
     @classmethod
     def activate_4G_lifetime_free(cls, sn_list, user_id):
@@ -1154,6 +1185,10 @@ class UnicomManageControllerView(View):
         """
         iccid批量重置流量
         """
+        redis_obj = RedisObject(5)
+        lock = redis_obj.try_lock(UNICOM_MANAGE_LOCK, 'iccid', 15, 60)
+        if not lock:
+            return response.json(10074)
         file = request.FILES['file']
         binding_type = int(request_dict.get('type', 0))
         if not file:
@@ -1167,6 +1202,9 @@ class UnicomManageControllerView(View):
     @staticmethod
     def async_bulk_reset_flow_package(file, user, ip, binding_type):
         err_data = []
+        date_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+
+        num = 0
         for line in file:
             serial_number = line.decode().strip()[0:9]
             try:
@@ -1193,6 +1231,7 @@ class UnicomManageControllerView(View):
                     res = wx_tech.delete_card_package(**data)
                     if res['code'] == '0':
                         UnicomComboExperienceHistory.objects.filter(iccid=iccid).delete()
+                        num += 1
                         continue
                     err_data.append({'serialNumber': serial_number, 'msg': '电信重置流量异常:{}'.format(res['code'])})
                     continue
@@ -1217,6 +1256,7 @@ class UnicomManageControllerView(View):
                 result = UnicomComboView().activate_test_flow_package(serial_number)  # 订购新的100M测试流量
                 describe = '批量重置4G流量序列号{},iccid:{},{}'.format(serial_number, iccid, result)
 
+                num += 1
                 # 当前缓存是客户首次扫序列号,激活赠送测试流量生成的,重置流量所以要清除
                 key = f'ASJ:UNICOM:CARD:ACTIVATE:{serial_number}'
                 redis = RedisObject()
@@ -1231,6 +1271,15 @@ class UnicomManageControllerView(View):
             describe = json.loads(json.dumps(err_data))
             UnicomManageControllerView().create_operation_log('unicom/manage/iccidBatchReset', ip, binding_type,
                                                               describe)
+        try:
+            redis_obj = RedisObject(5)
+            lock = redis_obj.release_lock(UNICOM_MANAGE_LOCK, 'iccid')
+            describe = f'{date_str}批量重置流量释放锁{lock},数量:{num}'
+            UnicomManageControllerView().create_operation_log('unicom/manage/iccidBatchReset', ip, binding_type,
+                                                              describe)
+        except Exception as e:
+            LOGGER.info('释放锁异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
         LOGGER.info(f'批量重置流量type={binding_type},err_data={err_data}')
 
     @classmethod
@@ -1394,3 +1443,111 @@ class UnicomManageControllerView(View):
             }
             package_list.append(new_package)
         return package_list
+
+    @classmethod
+    def update_expiration_date(cls, request_dict, response):
+        """
+        更改套餐过期时间
+        @param request_dict: 请求参数
+        @request_dict id: 套餐id
+        @request_dict expireTime: 过期时间
+        @request_dict serialNumber: 序列号
+        @param response: 响应对象
+        @return:
+        """
+        id = request_dict.get("id", None)
+        expire_time = request_dict.get("expireTime", None)
+        serial_number = request_dict.get("serialNumber", None)
+
+        if not all([id, serial_number, expire_time]):
+            return response.json(444)
+        try:
+            ud_qs = UnicomDeviceInfo.objects.filter(serial_no=serial_number).values('iccid', 'card_type')
+            package_list = []
+            if not ud_qs.exists():
+                return response.json(0, {'packageList': package_list})
+
+            card_type = ud_qs[0]['card_type']
+            if card_type == 0 or card_type == 3:
+                UnicomComboOrderInfo.objects.filter(pk=id).update(expire_time=expire_time)
+                return response.json(0)
+            else:
+                return response.json(177, "这个类型的卡不能更改过期时间")
+
+        except Exception as e:
+            LOGGER.info('UnicomManageControllerView.update_expiration_date, errLine:{}, errMsg:{}'.format(
+                e.__traceback__.tb_lineno, repr(e)))
+            return response.json(500)
+
+    @classmethod
+    def verify_package_export_excel(cls, request, request_dict, response):
+        """
+        验证套餐导出excel
+        @param request_dict:
+        @param request:
+        @param response: 响应对象
+        @return:
+        """
+        try:
+            serial_list = []
+            file = request.FILES['serialFile']
+            for line in file:
+                serial_number = line.decode().strip()[0:9]
+                serial_list.append(serial_number)
+            total_count = len(serial_list)
+            pages = ceil(total_count / 100)
+
+            package_list = []
+            # 遍历每一页,使用列表切片处理批次
+            for page in range(pages):
+                start = page * 100
+                end = (page + 1) * 100
+                serial_list_batch = serial_list[start:end]
+                # 获取设备信息
+                pck_qs = SerialNumberPackage.objects.filter(serial_number__in=serial_list_batch) \
+                    .values('serial_number', 'package_id')
+                if not pck_qs.exists():
+                    return response.json(0)
+                for item in pck_qs:
+                    package_list.append(item)
+
+            combo_list = list(UnicomCombo.objects.filter(is_del=False).values('id', 'combo_name'))
+
+            # 创建一个新的excel文档
+            wb = openpyxl.Workbook()
+            # 获取默认的工作表
+            sheet = wb.active
+            sheet.title = '套餐核对'
+            sheet.column_dimensions['A'].width = 20
+            sheet.column_dimensions['B'].width = 20
+            sheet.column_dimensions['C'].width = 20
+            sheet.column_dimensions['D'].width = 20
+
+            for i, item in enumerate(package_list):
+                combo_name = next((combo['combo_name'] for combo in combo_list if combo['id'] == item['package_id']),
+                                  None)
+                u_device_qs = UnicomDeviceInfo.objects.filter(serial_no=item['serial_number']).values('card_type',
+                                                                                                      'iccid')
+                sheet.cell(row=i + 1, column=1, value=item['serial_number'])
+                if u_device_qs.exists():
+                    card_name = ''
+                    if u_device_qs[0]['card_type'] == 0:
+                        card_name = '珠海联通'
+                    elif u_device_qs[0]['card_type'] == 1:
+                        card_name = '五兴电信'
+                    sheet.cell(row=i + 1, column=2, value=card_name)
+                    sheet.cell(row=i + 1, column=3, value=u_device_qs[0]['iccid'])
+                sheet.cell(row=i + 1, column=4, value=combo_name)
+
+            filename = '序列号流量套餐核对-{}.xlsx'.format(len(package_list))
+            # 创建一个http响应
+            res = HttpResponse(content_type='application/vnd.ms-excel')
+            # 设置响应头,告诉浏览器文件要下载而不是直接打开
+            res['Content-Disposition'] = 'attachment; filename={}'.format(filename)
+            # 将excel文档保存到http响应中
+            wb.save(res)
+            return res
+        except Exception as e:
+            LOGGER.info('*****UnicomManageController.package_cdk_export_excel:errLine:{}, errMsg:{}'
+                        .format(e.__traceback__.tb_lineno, repr(e)))
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 1 - 0
Model/models.py

@@ -2865,6 +2865,7 @@ class AppDeviceType(models.Model):
     model = models.SmallIntegerField(default=0, verbose_name='设备类型')
     type = models.IntegerField(default=0, verbose_name='设备型号')
     icon = models.CharField(default='', max_length=200, verbose_name='图标文件路径')
+    config = models.JSONField(null=True, verbose_name='关联配网方式、网络频段配置json')
     app_version_number_id = models.CharField(default='', max_length=32, verbose_name='关联APP版本号表id')
 
     class Meta:

+ 43 - 1
Object/RedisObject.py

@@ -107,4 +107,46 @@ class RedisObject:
             return False
 
     def get_all_hash_data(self, key):
-        return self.CONN.hgetall(key)
+        return self.CONN.hgetall(key)
+
+    def set_persist(self, key):
+        return self.CONN.persist(key)
+
+    def try_lock(self, lock_key, request_id, expire, time_unit_second):
+        """
+        获取分布式锁 原子性
+        :param lock_key: 锁的key
+        :param request_id: 请求id
+        :param expire: 锁过期时间
+        :param time_unit_second: 时间单位(秒)
+        :return: True or False
+        """
+        try:
+            # 使用set命令尝试获取锁,并设置nx=True
+            result = self.CONN.set(lock_key, request_id, ex=time_unit_second * expire, nx=True)
+            return result is not None
+        except Exception as e:
+            print("redis lock error.", e)
+            return False
+
+    def release_lock(self, lock_key, request_id):
+        """
+        释放锁
+        :param lock_key: 锁的key
+        :param request_id: 请求id
+        :return: True or False
+        """
+        try:
+            # 使用Lua脚本来释放锁,避免在执行脚本时发生异常导致锁无法释放
+            lua_script = """
+            if redis.call('get', KEYS[1]) == ARGV[1] then
+                return redis.call('del', KEYS[1])
+            else
+                return 0
+            end
+            """
+            result = self.CONN.eval(lua_script, 1, lock_key, request_id)
+            return result == 1
+        except Exception as e:
+            print("redis unlock error.", e)
+            return False

+ 6 - 2
Object/ResponseObject.py

@@ -146,7 +146,9 @@ class ResponseObject(object):
             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',
-            10072: 'This function has been disabled. Please contact the administrator'
+            10072: 'This function has been disabled. Please contact the administrator',
+            10073: 'The verification code has been sent, please pay attention to check',
+            10074: 'Task queue processing, please try again later'
         }
         data_cn = {
             0: '成功',
@@ -279,7 +281,9 @@ class ResponseObject(object):
             10070: '此设备分享已达上限',
             503: '操作失败,请稍后重试',
             10071: '\t领取成功\n可在设置-4G-我的套餐中查看',
-            10072: '该功能已停用,请联系管理员'
+            10072: '该功能已停用,请联系管理员',
+            10073: '验证码已发送,请注意查收',
+            10074: '任务队列处理中,请稍后再试'
         }
 
         msg = data_cn if self.lang == 'cn' or self.lang == 'zh-Hans' or self.lang == 'zh-Hant' else data_en