Quellcode durchsuchen

消息推送俄罗斯切换对象存储位置、设备版本配置一键同步

zhangdongming vor 2 Monaten
Ursprung
Commit
4279fefa33

+ 130 - 1
AdminController/DeviceManagementController.py

@@ -14,7 +14,9 @@ from django.db.models import Q, F, Sum, OuterRef, Min, Subquery
 from django.forms.models import model_to_dict
 from django.views.generic.base import View
 
-from Ansjer.config import LOGGER, SERIAL_DOMAIN_NAME
+from Ansjer.cn_config.config_test import CONFIG_INFO
+from Ansjer.config import LOGGER, SERIAL_DOMAIN_NAME, \
+    CONFIG_TEST, CONFIG_CN, CONFIG_US, CONFIG_EUR
 from Ansjer.config import OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, \
     AWS_SES_ACCESS_REGION
 from Ansjer.config import SERVER_DOMAIN_TEST, SERVER_DOMAIN_CN, SERVER_DOMAIN_US, SERVER_DOMAIN_EUR
@@ -117,6 +119,8 @@ class DeviceManagement(View):
                 return self.edit_device_ver_info(request_dict, response)
             elif operation == 'delDeviceVerInfo':  # 删除设备型号版本
                 return self.del_device_ver_info(request_dict, response)
+            elif operation == 'syncDeviceVersion':  # 一键同步
+                return self.sync_device_version(request_dict, response)
 
             # 设备语音设置
             elif operation == 'getDeviceVoice':  # 获取设备音频
@@ -1686,6 +1690,131 @@ class DeviceManagement(View):
         device_version_info_qs.delete()
         return response.json(0)
 
+    @classmethod
+    def sync_device_version(cls, request_dict, response):
+        """
+        同步设备版本信息
+        @param user_id: 用户ID
+        @param request: 请求对象
+        @param request_dict: 请求参数
+        @param response: 响应对象
+        @return: 响应结果
+        """
+        try:
+            # 从请求字典中获取设备码和版本号
+            version = request_dict.get('softwareVer')
+            d_code = request_dict.get('dCode')
+
+            # 查询设备版本配置
+            version_config_qs = DeviceVersionInfo.objects.filter(
+                d_code=d_code,
+                software_ver=version
+            )
+
+            # 检查是否存在匹配的配置
+            if not version_config_qs.exists():
+                return response.json(173)  # 设备版本配置不存在
+
+            # 获取第一条匹配的记录(通常应该只有一条)
+            version_config = version_config_qs.first()
+
+            # 将数据库字段映射为驼峰命名的响应数据
+            req_data = {
+                'dCode': version_config.d_code,
+                'softwareVer': version_config.software_ver,
+                'firmwareVer': version_config.firmware_ver,
+                'videoCode': version_config.video_code,
+                'regionAlexa': version_config.region_alexa,
+                'supportsHumanTracking': version_config.supports_human_tracking,
+                'supportsCustomVoice': version_config.supports_custom_voice,
+                'supportsDualBandWifi': version_config.supports_dual_band_wifi,
+                'supportsFourPoint': version_config.supports_four_point,
+                'supports4g': version_config.supports_4g,
+                'supportsPTZ': version_config.supports_ptz,
+                'supportsAi': version_config.supports_ai,
+                'supportsCloudStorage': version_config.supports_cloud_storage,
+                'supportsAlexa': version_config.supports_alexa,
+                'deviceType': version_config.device_type,
+                'resolution': version_config.resolution,
+                'aiType': version_config.ai_type,
+                'supportsAlarm': version_config.supports_alarm,
+                'supportsNightVision': version_config.supports_night_vision,
+                'screenChannels': version_config.screen_channels,
+                'networkType': version_config.network_type,
+                'otherFeatures': version_config.other_features,
+                'electricityStatistics': version_config.electricity_statistics,
+                'supportsPetTracking': version_config.supports_pet_tracking
+            }
+            config_thread = threading.Thread(target=DeviceManagement.sync_device_version_config, kwargs=req_data)
+            config_thread.start()
+            # 返回成功响应,包含设备版本配置信息
+            return response.json(0)
+
+        except Exception as e:
+            # 通用异常处理(建议替换为具体异常类型)
+            LOGGER.exception("同步设备配置失败")
+            return response.json(500, f'Server error: {str(e)}')
+
+    @staticmethod
+    def sync_device_version_config(**req_data):
+        """
+           同步设备版本信息到本地数据库及外部服务器(含异常跳过逻辑)
+           """
+        try:
+            target_servers = []
+            if CONFIG_INFO == CONFIG_TEST:
+                target_servers = [
+                    "https://www.zositechc.cn/open/device/configuration/syncVerConfig",
+                    "https://www.dvema.com/open/device/configuration/syncVerConfig",
+                    "https://api.zositeche.com/open/device/configuration/syncVerConfig"
+                ]
+            elif CONFIG_INFO == CONFIG_CN:
+                target_servers = [
+                    "https://test.zositechc.cn/open/device/configuration/syncVerConfig",
+                    "https://www.dvema.com/open/device/configuration/syncVerConfig",
+                    "https://api.zositeche.com/open/device/configuration/syncVerConfig"
+                ]
+            elif CONFIG_INFO == CONFIG_US:
+                target_servers = [
+                    "https://test.zositechc.cn/open/device/configuration/syncVerConfig",
+                    "https://www.zositechc.cn/open/device/configuration/syncVerConfig",
+                    "https://api.zositeche.com/open/device/configuration/syncVerConfig"
+                ]
+            elif CONFIG_INFO == CONFIG_EUR:
+                target_servers = [
+                    "https://test.zositechc.cn/open/device/configuration/syncVerConfig",
+                    "https://www.zositechc.cn/open/device/configuration/syncVerConfig",
+                    "https://www.dvema.com/open/device/configuration/syncVerConfig"
+                ]
+
+            for server_url in target_servers:
+                try:
+                    # 发送请求并设置超时(10秒)
+                    headers = {'Content-Type': 'application/json'}
+                    resp = requests.post(
+                        server_url,
+                        json=req_data,
+                        headers=headers,
+                        timeout=10  # 超时时间
+                    )
+                    resp.raise_for_status()  # 抛出HTTP错误(如400/500)
+                    LOGGER.info(f"[{server_url}] 同步成功,响应:{resp.json()}")
+
+                except requests.exceptions.ConnectTimeout:
+                    LOGGER.error(f"[{server_url}] 连接超时,跳过")
+                    continue
+                except requests.exceptions.ReadTimeout:
+                    LOGGER.error(f"[{server_url}] 读取超时,跳过")
+                    continue
+                except requests.exceptions.RequestException as e:
+                    LOGGER.error(f"[{server_url}] 同步失败:{str(e)}")
+                    continue  # 跳过当前服务器
+            return True
+
+        except Exception as e:
+            LOGGER.exception("同步设备版本信息失败error{}".format(repr(e)))
+            return False
+
     @staticmethod
     def get_device_voice(request_dict, response):
         """

+ 11 - 1
Controller/DetectControllerV2.py

@@ -17,8 +17,9 @@ from Ansjer.config import DETECT_PUSH_DOMAIN, DETECT_PUSH_DOMAINS, DETECT_PUSH_D
 from Ansjer.config import PUSH_BUCKET, CONFIG_INFO, CONFIG_CN, CONFIG_EUR, CONFIG_US
 from Ansjer.config import PUSH_REDIS_ADDRESS
 from Model.models import Device_Info, Equipment_Info, UidSetModel, UidPushModel, CompanyModel, SysMsgModel, \
-    AiService, VodBucketModel
+    AiService, VodBucketModel, Device_User
 from Object.ETkObject import ETkObject
+from Object.Enums.RedisKeyConstant import RedisKeyConstant
 from Object.OCIObjectStorage import OCIObjectStorage
 from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
@@ -182,6 +183,7 @@ class DetectControllerViewV2(View):
             return response.json(444, 'appBundleId,app_type,token_val,uid,m_code')
 
         try:
+            LOGGER.info(f'v2消息推送uid:{uid},s:{status},tv:{token_val},ab{appBundleId},pt:{push_type}')
             # 判断用户是否拥有设备
             device_info_qs = Device_Info.objects.filter(userID_id=userID, UID=uid)
             if not device_info_qs.exists():
@@ -264,6 +266,14 @@ class DetectControllerViewV2(View):
                 uid_set_qs = UidSetModel.objects.create(**uid_set_data)
                 uid_set_id = uid_set_qs.id
 
+            if CONFIG_INFO == CONFIG_EUR:
+                user_qs = Device_User.objects.filter(userID=userID, region_country=91).values('userID')
+                if user_qs.exists():
+                    redisObj = RedisObject(host=PUSH_REDIS_ADDRESS)
+                    storage_key = RedisKeyConstant.PUSH_STORAGE_CONFIG_UID.value + uid
+                    redisObj.set_data(storage_key, 2, 3600 * 24 * 180)
+                    LOGGER.info(f'RU俄罗斯用户推送配置已更新UID:{uid}')
+
             # 初始化UidPushModel推送表
             if electricity_status:
                 if m_code != 0 and m_code != '0':

+ 82 - 0
Controller/UserDevice/DeviceVersionInfoController.py

@@ -7,6 +7,7 @@
 @Software: PyCharm
 """
 import json
+import time
 
 from django.http import QueryDict
 from django.views import View
@@ -47,6 +48,8 @@ class DeviceVersionInfoView(View):
 
     def validation(self, request_dict, request, operation):
         response = ResponseObject('cn')
+        if operation == 'syncVerConfig':
+            return self.sync_ver_config(request, request_dict, response)
         tko = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
         if tko.code != 0:
             return response.json(tko.code)
@@ -206,3 +209,82 @@ class DeviceVersionInfoView(View):
             error_line = e.__traceback__.tb_lineno
             LOGGER.error(f'验证用户uid异常 user:{user_id}, uid:{uid}, error_line:{error_line}, error_msg:{repr(e)}')
             return response.json(500, f'error_line:{error_line}, error_msg:{repr(e)}')
+
+    @classmethod
+    def sync_ver_config(cls, request, request_dict, response):
+        # 定义必要字段列表
+        required_fields = ['dCode', 'softwareVer', 'videoCode', 'deviceType', 'supportsAlarm', 'screenChannels',
+                           'networkType']
+
+        # 验证必要参数是否存在
+        if not all(request_dict.get(field) for field in required_fields):
+            LOGGER.error(f'缺少必要参数: {request_dict}')
+            return response.json(444)
+        LOGGER.info(f'同步设备版本信息 params: {request_dict}')
+        try:
+            d_code = request_dict.get('dCode', '')
+            ver = request_dict.get('softwareVer', '')
+            # 提取参数并设置默认值(添加必要字段的类型转换)
+            params = {
+                'd_code': d_code,  # 字符串类型,添加默认空字符串
+                'software_ver': ver,  # 字符串类型,添加默认空字符串
+                'firmware_ver': request_dict.get('firmwareVer', ''),  # 已有默认空字符串,保持不变
+                'video_code': int(request_dict.get('videoCode', 0)),  # 使用get()添加默认值0,避免KeyError
+                'region_alexa': request_dict.get('regionAlexa', 'ALL'),  # 匹配模型默认值'ALL'
+                'supports_human_tracking': bool(int(request_dict.get('supportsHumanTracking', 0))),  # 转为布尔值
+                'supports_custom_voice': bool(int(request_dict.get('supportsCustomVoice', 0))),  # 转为布尔值
+                'supports_dual_band_wifi': bool(int(request_dict.get('supportsDualBandWifi', 0))),  # 转为布尔值
+                'supports_four_point': bool(int(request_dict.get('supportsFourPoint', 0))),  # 转为布尔值
+                'supports_4g': bool(int(request_dict.get('supports4g', 0))),  # 转为布尔值
+                'supports_ptz': bool(int(request_dict.get('supportsPTZ', 0))),  # 转为布尔值
+                'supports_ai': bool(int(request_dict.get('supportsAi', 0))),  # 转为布尔值
+                'supports_cloud_storage': bool(int(request_dict.get('supportsCloudStorage', 0))),  # 转为布尔值
+                'supports_alexa': bool(int(request_dict.get('supportsAlexa', 0))),  # 转为布尔值
+                'device_type': int(request_dict.get('deviceType', 0)),  # 使用get()添加默认值0
+                'resolution': request_dict.get('resolution', ''),  # 字符串类型,添加默认空字符串
+                'ai_type': int(request_dict.get('aiType', 0)),  # 使用get()添加默认值0
+                'supports_alarm': int(request_dict.get('supportsAlarm', -1)),  # 使用get()添加默认值-1
+                'supports_night_vision': int(request_dict.get('supportsNightVision', -1)),  # 使用get()添加默认值-1
+                'screen_channels': int(request_dict.get('screenChannels', 1)),  # 匹配模型默认值1
+                'network_type': int(request_dict.get('networkType', 1)),  # 匹配模型默认值1
+                'other_features': json.loads(request_dict['otherFeatures']) if request_dict.get(
+                    'otherFeatures') else None,  # 保持不变
+                'electricity_statistics': int(request_dict.get('electricityStatistics', 0)),  # 使用get()添加默认值0
+                'supports_pet_tracking': int(request_dict.get('supportsPetTracking', 0)),  # 使用get()添加默认值0
+            }
+
+            now_time = int(time.time())
+
+            # 使用 update_or_create 合并更新和创建操作
+            version_config_qs = DeviceVersionInfo.objects.filter(
+                d_code=params['d_code'],
+                software_ver=params['software_ver']
+            )
+            if version_config_qs.exists():
+                version_config_qs.update(**params, updated_time=now_time)
+                # 创建Redis对象
+                redis = RedisObject()
+                # 构建Redis键
+                version_key = RedisKeyConstant.ZOSI_DEVICE_VERSION_INFO.value + ver + d_code
+                redis.del_data(version_key)
+            else:
+                DeviceVersionInfo.objects.create(**params, created_time=now_time, updated_time=now_time)
+
+        except json.JSONDecodeError as e:
+            # 专门捕获 JSON 解析错误
+            LOGGER.error(f"JSON 解析失败: {str(e)}")
+            return response.json(500, f'Invalid JSON format: {str(e)}')
+        except KeyError as e:
+            # 处理字段缺失(理论上 required_fields 已校验,此处作为备用)
+            LOGGER.error(f"参数缺失: {str(e)}")
+            return response.json(444, f'Missing parameter: {str(e)}')
+        except ValueError as e:
+            # 处理整型转换失败
+            LOGGER.error(f"参数类型错误: {str(e)}")
+            return response.json(400, f'Invalid parameter type: {str(e)}')
+        except Exception as e:
+            # 通用异常处理(建议替换为具体异常类型)
+            LOGGER.exception("同步设备配置失败")
+            return response.json(500, f'Server error: {str(e)}')
+
+        return response.json(0)

+ 2 - 0
Object/Enums/RedisKeyConstant.py

@@ -31,6 +31,8 @@ class RedisKeyConstant(Enum):
     APP_DOMAIN_NAME = "DOMAIN:NAME"
     # 活动信息
     ACTIVITY_INFO = 'ACTIVITY:INFO'
+    # 推送存储位置
+    PUSH_STORAGE_CONFIG_UID = 'push:storage:config:uid:'
 
     # Redis 过期时间常量 (秒)
     EXPIRE_TIME_60_SECONDS = 60    # 60秒