| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 | # -*- encoding: utf-8 -*-"""@File    : DeviceVersionInfoController.py@Time    : 2024/11/20 14:20@Author  : stephen@Email   : zhangdongming@asj6.wecom.work@Software: PyCharm"""import jsonimport timefrom django.http import QueryDictfrom django.views import Viewfrom Ansjer.config import LOGGERfrom Model.models import DeviceVersionInfo, Device_Infofrom Object.Enums.RedisKeyConstant import RedisKeyConstantfrom Object.RedisObject import RedisObjectfrom Object.ResponseObject import ResponseObjectfrom Object.TokenObject import TokenObjectfrom Service.CommonService import CommonServiceclass DeviceVersionInfoView(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('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)        response.lang = tko.lang        userID = tko.userID        if operation == 'getInfo':            return self.get_device_version_info(userID, request, request_dict, response)        elif operation == 'validateUserDevice':            return self.validateUserDevice(userID, request, request_dict, response)        elif operation == 'clearDeviceVersionCache':            return self.clear_device_version_cache(userID, request, request_dict, response)        elif operation == 'getDeviceVersion':            d_code = request_dict.get('d_code')            ver = request_dict.get('ver')            res = self.cache_device_version_info(d_code, ver)            return response.json(0, res)        else:            return response.json(414)    @classmethod    def clear_device_version_cache(cls, user_id, request, request_dict, response):        """        清除设备版本缓存        @param user_id: 用户ID        @param request: 请求对象        @param request_dict: 请求参数        @param response: 响应对象        @return: 响应结果        """        d_code = request_dict.get('d_code', None)        timestamp = request_dict.get('timestamp', None)        if not d_code and not timestamp:            return response.json(444)  # 错误代码:缺少必要参数        try:            LOGGER.info(f'清除缓存 user: {user_id}, d_code: {d_code}, timestamp: {timestamp}')            redis = RedisObject()            if d_code:                # 清除指定d_code的所有版本缓存                pattern = RedisKeyConstant.ZOSI_DEVICE_VERSION_INFO.value + '*' + d_code                matched_keys = redis.get_keys(pattern)  # 将结果赋值给 matched_keys                if matched_keys:                    for key in matched_keys:  # 使用正确的变量名                        redis.del_data(key)            if timestamp:                # 清除创建时间早于指定时间戳的缓存                devices = DeviceVersionInfo.objects.filter(created_time__lt=timestamp).values('d_code', 'software_ver')                for device in devices:                    key = RedisKeyConstant.ZOSI_DEVICE_VERSION_INFO.value + device['software_ver'] + device['d_code']                    redis.del_data(key)            return response.json(0, "缓存清除成功")        except Exception as e:            error_line = e.__traceback__.tb_lineno            LOGGER.error(f'清除缓存失败 user:{user_id}, error_line:{error_line}, error_msg:{repr(e)}')            return response.json(500, f'error_line:{error_line}, error_msg:{repr(e)}')    @classmethod    def get_device_version_info(cls, user_id, request, request_dict, response):        # 从请求字典中获取uid和version        uid = request_dict.get('uid')        version = request_dict.get('version')        # 检查version是否存在        if not version:            return response.json(444)  # 错误代码:没有提供version        try:            # 示例输入字符串            ip = CommonService.get_ip_address(request)            LOGGER.info(f'获取设备版本配置信息 user: {user_id}, uid: {uid}, version: {version}, ip: {ip} ')            # 从右侧分割字符串,获取版本和设备代码            ver, d_code = version.rsplit('.', 1)  # 使用从右到左分割            ver = ver.replace('V', '')  # 移除版本前的'V'            # 创建Redis对象            redis = RedisObject()            # 构建Redis键            version_key = RedisKeyConstant.ZOSI_DEVICE_VERSION_INFO.value + ver + d_code            # 尝试从Redis中获取数据            version_info = redis.get_data(version_key)            if version_info:                # 如果在Redis中找到了数据,直接返回                return response.json(0, json.loads(version_info))            # 从数据库查询设备版本信息            device_version_qs = DeviceVersionInfo.objects.filter(d_code=d_code, software_ver=ver).values()            if not device_version_qs.exists():                LOGGER.info(f'获取设备版本配置信息失败 user: {user_id}, uid: {uid}, version: {version}, ip: {ip} ')                return response.json(173)  # 错误代码:未找到设备版本信息            # 从QuerySet中获取设备信息            device_info = device_version_qs.first()            other_features_fields = [                'supports_lighting',                'is_support_multi_speed',                'supports_algorithm_switch',                'supports_playback_filtering'            ]            if not device_info.get("other_features"):                device_info["other_features"] = {field: -1 for field in other_features_fields}            else:                try:                    if isinstance(device_info["other_features"], str):                        device_info["other_features"] = json.loads(device_info["other_features"])                    for field in other_features_fields:                        if field not in device_info["other_features"] or device_info["other_features"][field] in (None,                                                                                                                  ''):                            device_info["other_features"][field] = -1                except json.JSONDecodeError:                    device_info["other_features"] = {field: -1 for field in other_features_fields}            # 将设备信息序列化为JSON            device_json = json.dumps(device_info)            # 将数据写入Redis,以便后续使用            redis.set_data(version_key, device_json, 60 * 60 * 24)  # 设置TTL为24小时            # 返回设备信息            return response.json(0, device_info)        except Exception as e:            LOGGER.error('uid:{}返回173,error_line:{}, error_msg:{}'.format(uid, e.__traceback__.tb_lineno, repr(e)))            return response.json(173)    @classmethod    def cache_device_version_info(cls, d_code, ver):        """        缓存设备版本信息        :param d_code: 设备规格码(软件方案)        :param ver: 设备版本号        :return: 设备版本信息字典或 None        """        redis = RedisObject()        version_key = f"{RedisKeyConstant.ZOSI_DEVICE_VERSION_INFO.value}{ver}{d_code}"        try:            version_info = redis.get_data(version_key)            if version_info:                return json.loads(version_info)        except Exception as e:            LOGGER.error(f"Redis get failed for key {version_key}: {e}")            return None        device_info = DeviceVersionInfo.objects.filter(            d_code=d_code, software_ver=ver        ).values().first()        if not device_info:            return None        try:            redis.set_data(version_key, json.dumps(device_info), 60 * 60 * 24)        except Exception as e:            LOGGER.error(f"Redis set failed for key {version_key}: {e}")            return None        return device_info    @classmethod    def validateUserDevice(cls, user_id, request, request_dict, response):        """        验证用户设备        @param user_id: 用户id        @param request: 请求        @param request_dict: 请求参数        @param response: 响应参数        @return: 成功 0 失败 173        """        uid = request_dict.get('uid')        serial_number = request_dict.get('serialNumber')        app_bundle_id = request_dict.get('appBundleId')        m_code = request_dict.get('mCode')        if not uid:            return response.json(444)  # 错误代码:没有提供uid        try:            LOGGER.info(f'直播验证用户uid:{uid},user:{user_id},m_code:{m_code}')            redis = RedisObject(3)            # 构建Redis键            device_key = f"{RedisKeyConstant.BASIC_USER.value}{user_id}:UID:{uid}"            # 尝试从Redis中获取数据            device_info = redis.get_data(device_key)            # 检查缓存命中和UID匹配            if device_info == uid:                return response.json(0, uid)  # 缓存命中返回 0            ip = CommonService.get_ip_address(request)            LOGGER.info(f'直播验证用户uid:{uid},ip:{ip},serial:{serial_number},app{app_bundle_id}')            # 从数据库查询设备版本信息            try:                device_qs = Device_Info.objects.filter(userID_id=user_id, UID=uid).values('UID')                if not device_qs.exists() or uid != device_qs.first().get('UID'):                    LOGGER.error(f'验证用户uid错误,未找到设备信息 uid:{uid}')                    return response.json(173)  # 错误代码:未找到设备信息                # 将数据写入Redis,以便后续使用,设置TTL为48小时                redis.set_data(device_key, uid, 60 * 60 * 24 * 2)                return response.json(0, uid)  # 返回设备信息            except Exception as db_exception:                LOGGER.error(f'数据库查询失败 user:{user_id}, uid:{uid}, error: {repr(db_exception)}')                return response.json(500, '数据库查询错误')        except Exception as e:            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                'has_4g_cloud': int(request_dict.get('has4gCloud', -1)),  # 使用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)
 |