import json import logging import threading import time from datetime import datetime import requests from django.db import transaction from django.db.models import Q from django.views import View from Ansjer.config import CRCKey, CONFIG_INFO, CONFIG_US, CONFIG_EUR, \ CONFIG_CN, USED_SERIAL_REDIS_LIST, UNUSED_SERIAL_REDIS_LIST, SERVER_DOMAIN_US, REGION_ID_LIST, SERVER_DOMAIN_TEST, \ SERVER_DOMAIN_LIST, SERVER_DOMAIN_CN, SERVER_DOMAIN_EUR, RESET_REGION_ID_SERIAL_REDIS_LIST, LOGGER, CONFIG_TEST, \ SERVER_DOMAIN, HUAWEICLOUD_OBS_SERVER, HUAWEICLOUD_BAIDU_BIG_MODEL_LICENSE_BUKET from Controller.CheckUserData import DataValid from Controller.UnicomCombo.UnicomComboController import UnicomComboView from Model.models import SerialNumberModel, CompanySerialModel, UIDCompanySerialModel, UIDModel, Device_Info, \ iotdeviceInfoModel, LogModel, UidSetModel, UID_Bucket, \ Unused_Uid_Meal, Order_Model, StsCrdModel, VodHlsModel, ExperienceContextModel, UidUserModel, ExperienceAiModel, \ AiService, DeviceDomainRegionModel, RegionModel, UidPushModel, AppScannedSerial, Device_User, SerialUnbindUID, \ DeviceNetInfo, BaiduBigModelLicense, Instavision from Object.AWS.S3Email import S3Email from Object.RedisObject import RedisObject from Object.TokenObject import TokenObject from Object.uidManageResponseObject import uidManageResponseObject from Service.AlgorithmService import AlgorithmBaseOn35 from Service.CommonService import CommonService from AdminController.CloudServiceManage.AgentDeviceController import AgentDeviceView from django.conf import settings HUAWEICLOUD_AK = settings.HUAWEICLOUD_AK HUAWEICLOUD_SK = settings.HUAWEICLOUD_SK class SerialNumberView(View): def get(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation', None) request_dict = request.GET return self.validate(request_dict, operation, request) def post(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation', None) request_dict = request.POST return self.validate(request_dict, operation, request) def validate(self, request_dict, operation, request): response = uidManageResponseObject() if operation == 'attachUID': # 绑定uid return self.do_attach_uid(request_dict, response, request) elif operation == 'detachUID': # 解绑uid return self.do_detach_uid(request, request_dict, response) elif operation == 'get-uid': # app获取uid token_code, user_id, res = CommonService.verify_token_get_user_id(request_dict, request) return self.get_uid(request_dict, response, request, user_id) elif operation == 'create': # 创建序列号 return self.do_create(request_dict, response) elif operation == 'getUID': # 根据序列号获取uid return self.do_get_uid(request_dict, response) elif operation == 'getRegionInfo': # 根据序列号状态确认uid地区(PC工具使用) return self.get_region_info(request_dict, response) elif operation == 'saveRegion': # 保存设备地区信息(app使用) return self.save_region(request, request_dict, response) elif operation == 'getDomain': # 获取域名(设备使用) return self.get_domain(request_dict, response) elif operation == 'resetRegionId': # 重置地区id return self.reset_region_id(request_dict, response) elif operation == 'get-status': # 获取序列号绑定信息 return self.check_serial_status(request_dict, response) elif operation == 'getUidRegion': # 根据序列号获取uid地区 return self.get_uid_region(request_dict, response) elif operation == 'getGlobalUidRegion': # 获取序列号在全球服绑定uid的地区 return self.get_global_uid_region(request_dict, response) elif operation == 'getIoTCoreBySerialNumber': # 根据序列号获取iot core return self.get_iot_core_by_serial_number(request_dict, response) elif operation == 'saveUserNetInfo': return self.save_user_net_info(request_dict, response) elif operation == 'getBaiduBigModelLicenseUrl': return self.get_baidu_big_model_license_url(request_dict, response) elif operation == 'getInstavision': return self.get_instavision(request_dict, response) else: return response.json(414) @staticmethod def do_create(request_dict, response): quantity = int(request_dict.get('quantity', 0)) if not quantity: return response.json(444) try: try: number = SerialNumberModel.objects.last().id except: number = 0 serial_number_bulk = [] now_time = int(time.time()) algorithm = AlgorithmBaseOn35() for i in range(quantity): serial_number = algorithm.getLetter(number) number += 1 # sum每次递增1 # 前面补0至六位 serial_number = (6 - len(serial_number)) * '0' + serial_number serial_number_bulk.append(SerialNumberModel(serial_number=serial_number, add_time=now_time)) # 开启事务写入 with transaction.atomic(): SerialNumberModel.objects.bulk_create(serial_number_bulk) return response.json(0) except Exception as e: print(e) return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) def do_attach_uid(self, request_dict, response, request): serial_number = request_dict.get('serial_number', None) token = request_dict.get('token', None) time_stamp = request_dict.get('time_stamp', None) is_verify = request_dict.get('is_verify', None) if not all([serial_number, token, time_stamp]): return response.json(444) # 时间戳token校验 if not CommonService.check_time_stamp_token(token, time_stamp): LOGGER.error(f'{serial_number}时间戳校验失败time:{time_stamp},tk:{token}') return response.json(13) now_time = int(time.time()) serial = serial_number[0:6] company_identity = serial_number[6:9] full_serial = serial_number[0:9] # 根据企业标识确认企业秘钥 company_secret = '' if company_identity == '11A': company_secret = 'MTEyMTNB' elif company_identity == '11L': company_secret = 'VmXEWnBR' elif company_identity == '11Z': company_secret = 'ZsKWcxdD' try: redisObj = RedisObject() # 查询app是否已扫码,未扫码不能进行绑定 # 如果没扫码设置24小时的缓存,扫码后删除缓存 scanned_serial_key = serial + 'scanned_serial' if redisObj.get_data(scanned_serial_key): return response.json(5) app_scanned_serial_qs = AppScannedSerial.objects.filter(serial=serial).values('region_country') if not app_scanned_serial_qs.exists() and is_verify != '1': redisObj.set_data(scanned_serial_key, 1, 24*60*60) LOGGER.info(f'{serial}当前序列号未进行APP扫码') return response.json(5) # 判断序列号是否刚解绑,刚解绑1分钟内不能进行绑定 unused_serial_redis_list = redisObj.lrange(UNUSED_SERIAL_REDIS_LIST, 0, -1) unused_serial_redis_list = [str(i, 'utf-8') for i in unused_serial_redis_list] if serial in unused_serial_redis_list: return response.json(5) # 判断序列号是否未清除uid数据 serial_unbind_uid_qs = SerialUnbindUID.objects.filter(serial=serial, status=0) if serial_unbind_uid_qs.exists(): return response.json(5) # redis加锁,防止同一个序列号重复绑定 key = serial + 'do_attach_uid' is_lock = redisObj.CONN.setnx(key, 1) if not is_lock: return response.json(5) redisObj.CONN.expire(key, 60) # 判断序列号是否已和企业关联 company_serial_qs = CompanySerialModel.objects.filter(company__secret=company_secret, serial_number=serial) if not company_serial_qs.exists(): return response.json(173) company_serial = company_serial_qs[0] if company_serial.status == 0: # 该序列号未绑定企业 return response.json(173) elif company_serial.status == 1: # 绑定uid # is_verify: '1',跳过查询扫码记录 if is_verify == '1': region_country = 0 else: region_country = app_scanned_serial_qs[0]['region_country'] # 获取并判断region_id region_id = CommonService.confirm_region_id(region_country) if region_id not in REGION_ID_LIST: return response.json(444, {'invalid region_id': region_id}) # 获取p2p类型 p2p_type = request_dict.get('p2ptype', None) if serial_number[9:10]: p2p_type = serial_number[9:10] elif not p2p_type: return response.json(444, {'param': 'p2ptype'}) p2p_type = int(p2p_type) with transaction.atomic(): count = 0 while count < 3: # 查询是否存在未绑定序列号的uid uid_qs = UIDModel.objects.filter(vpg__company_id=company_serial.company.id, vpg__region_id=region_id, status=0, p2p_type=p2p_type). \ order_by('update_time') if not uid_qs.exists(): return response.json(375) uid = uid_qs[0] # 判断uid是否已绑定过序列号 uid_company_serial_qs = UIDCompanySerialModel.objects.filter(uid_id=uid.id) if uid_company_serial_qs.exists(): return response.json(377) result = UIDModel.objects.filter(id=uid.id, status=0).update(status=2, update_time=now_time) if int(result) <= 0: # 更新失败 count += 1 continue # 判断序列号是否已绑定过uid company_serial_qs = UIDCompanySerialModel.objects.filter(company_serial_id=company_serial.id) if company_serial_qs.exists(): return response.json(380) # UID关联【企业关联序列号】表创建数据 UIDCompanySerialModel.objects.create(uid_id=uid.id, company_serial_id=company_serial.id, add_time=now_time, update_time=now_time) company_serial.status = 2 company_serial.update_time = now_time company_serial.save() full_uid_code = uid.full_uid_code if uid.platform in CRCKey.keys(): full_uid_code += ':' + CRCKey[uid.platform] res = { 'full_uid_code': CommonService.encode_data(full_uid_code), 'uid': CommonService.encode_data(uid.uid), 'extra': uid.uid_extra, 'platform': uid.platform, 'initString': uid.init_string, 'initStringApp': uid.init_string_app, } # 记录操作日志 ip = CommonService.get_ip_address(request) content = json.loads(json.dumps(request_dict)) log = { 'ip': ip, 'user_id': 1, 'status': 200, 'time': int(time.time()), 'content': json.dumps(content), 'url': 'serialNumber/attachUID', 'operation': '序列号{}绑定uid: {}'.format(serial, uid.uid), } LogModel.objects.create(**log) redisObj.del_data(key=key) # 处理序列号状态和计算剩余uid数量线程 thread = threading.Thread(target=self.rpush_serial_and_count_uid, args=(serial, p2p_type, redisObj)) thread.start() return response.json(0, res) return response.json(5) elif company_serial.status == 2: # 返回uid res = self.get_uid_info_by_serial(company_serial.id) # 记录操作日志 ip = CommonService.get_ip_address(request) content = json.loads(json.dumps(request_dict)) log = { 'ip': ip, 'user_id': 1, 'status': 200, 'time': now_time, 'content': json.dumps(content), 'url': 'serialNumber/attachUID', 'operation': '序列号{}获取uid: {}'.format(serial, CommonService.decode_data(res['uid'])), } LogModel.objects.create(**log) redisObj.del_data(key=key) return response.json(0, res) elif company_serial.status == 3: # 已占用 redisObj.del_data(key=key) sync_success = self.sync_serial_data_and_log(request, company_serial.id, serial_number, now_time) if not sync_success: return response.json(10042) return response.json(0, self.get_uid_info_by_serial(company_serial.id)) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @classmethod def get_uid_info_by_serial(cls, company_serial_id): """ 根据企业关联序列号ID返回UID信息 """ uid_qs = UIDCompanySerialModel.objects.filter(company_serial_id=company_serial_id) if not uid_qs.exists(): return {} uid = uid_qs.values('uid__uid', 'uid__mac', 'uid__uid_extra', 'uid__full_uid_code', 'uid__platform', 'uid__init_string', 'uid__init_string_app')[0] full_uid_code = uid['uid__full_uid_code'] if uid['uid__platform'] in CRCKey.keys(): full_uid_code += ':' + CRCKey[uid['uid__platform']] res = { 'full_uid_code': CommonService.encode_data(full_uid_code), 'uid': CommonService.encode_data(uid['uid__uid']), 'extra': uid['uid__uid_extra'], 'platform': uid['uid__platform'], 'initString': uid['uid__init_string'], 'initStringApp': uid['uid__init_string_app'], } return res @classmethod def check_serial_status(cls, request_dict, response): serial_number = request_dict.get('serial_number', None) if not serial_number: return response.json(444) serial = serial_number[0:6] company_identity = serial_number[6:9] # 根据企业标识确认企业秘钥 company_secret = '' if company_identity == '11A': company_secret = 'MTEyMTNB' elif company_identity == '11L': company_secret = 'VmXEWnBR' elif company_identity == '11Z': company_secret = 'ZsKWcxdD' try: # 判断序列号是否已和企业关联 company_serial_qs = CompanySerialModel.objects.filter(company__secret=company_secret, serial_number=serial) if not company_serial_qs.exists(): return response.json(173) company_serial = company_serial_qs[0] # 获取企业序列号是否绑定UID uid_company_qs = UIDCompanySerialModel.objects.filter(company_serial_id=company_serial.id) \ .values('uid__uid', 'uid__mac', 'uid__uid_extra', 'uid__status', 'uid__area', 'uid__vpg_id', 'uid__p2p_type', 'uid__full_uid_code', 'uid__platform', 'uid__init_string', 'uid__init_string_app') uid_info = {} if uid_company_qs.exists(): # 更新被同步数据的uid的状态为3(数据被同步) uid = uid_company_qs[0]['uid__uid'] UIDModel.objects.filter(uid=uid).update(status=3) uid_info['uid'] = uid uid_info['mac'] = uid_company_qs[0]['uid__mac'] uid_info['uid_extra'] = uid_company_qs[0]['uid__uid_extra'] uid_info['status'] = uid_company_qs[0]['uid__status'] uid_info['area'] = uid_company_qs[0]['uid__area'] uid_info['vpg_id'] = uid_company_qs[0]['uid__vpg_id'] uid_info['p2p_type'] = uid_company_qs[0]['uid__p2p_type'] uid_info['full_uid_code'] = uid_company_qs[0]['uid__full_uid_code'] uid_info['platform'] = uid_company_qs[0]['uid__platform'] uid_info['init_string'] = uid_company_qs[0]['uid__init_string'] uid_info['init_string_app'] = uid_company_qs[0]['uid__init_string_app'] results = {'serial': company_serial.serial_number, 'status': company_serial.status, 'uidInfo': uid_info} return response.json(0, results) except Exception as e: LOGGER.info('查询序列号异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e))) return response.json(176, str(e)) @staticmethod def rpush_serial_and_count_uid(serial, p2p_type, redis_obj): """ 处理序列号状态和计算剩余uid数量线程 @param serial: 序列号 @param p2p_type: p2p类型 @param redis_obj: redis对象 @return: """ # 写入已使用序列号redis列表 redis_obj.rpush(USED_SERIAL_REDIS_LIST, serial) p2p_type = int(p2p_type) platform = '尚云' if p2p_type == 1 else 'tutk' redis_key = 'uid_count_warning_time_limit_' + platform redis_obj = RedisObject() time_limit = redis_obj.get_data(redis_key) if not time_limit: vpg_id = 1 if CONFIG_INFO == CONFIG_US: vpg_id = 3 elif CONFIG_INFO == CONFIG_EUR: vpg_id = 4 try: # 尚云剩余uid数量少于1000,尚云剩余uid数量少于5000,发送邮件提醒 unused_uid_count = UIDModel.objects.filter(vpg_id=vpg_id, p2p_type=p2p_type, status=0).count() warning_count = 1000 if p2p_type == 1 else 5000 if unused_uid_count < warning_count: # 限制一天提醒一次 redis_obj.set_data(redis_key, 1, 60 * 60 * 24) email_content = '{}服{}的uid数量少于{}个,剩余数量:{},请及时处理'.format( CONFIG_INFO, platform, warning_count, unused_uid_count) S3Email().faEmail(email_content, 'servers@ansjer.com') except Exception as e: LOGGER.info('发送提醒邮件异常: error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) try: # 异步调用客户代理平台同步序列号绑定UID serial_number = serial + '11A' agent_thread = threading.Thread(target=AgentDeviceView.device_binding_or_unbinding, args=(serial_number, 1)) agent_thread.start() except Exception as e: LOGGER.error('异步更新定制客户序列号状态一次: error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def sync_serial_data_and_log(request, company_serial_id, serial, now_time): """ 同步序列号数据和记录日志 @param company_serial_id: 企业关联序列号 ID @param request: 请求 @param serial: 序列号 @param now_time: 当前时间 @return : bool """ redis = RedisObject() key = f'BIND:UID:LIMIT:{serial}' serial_obj = redis.get_data(key) # 序列号已占用一天写一次日志记录 if serial_obj: return False ip = CommonService.get_ip_address(request) operation = '{}序列号占用,APP扫码已停用同步UID'.format(serial) log = { 'ip': ip, 'user_id': 1, 'status': 200, 'time': now_time, 'url': 'serialNumber/attachUID', 'operation': operation } LogModel.objects.create(**log) redis.set_data(key, '1', 86400) return False # sync_result = False # # 测试服和国内服不同步 # if CONFIG_INFO == 'test' or CONFIG_INFO == 'cn': # return sync_result # try: # # 判断当前序列号是否绑定UID # uid_serial_qs = UIDCompanySerialModel.objects.filter(company_serial_id=company_serial_id) # if uid_serial_qs.exists(): # operation += ',已绑定UID,不同步数据' # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result # # region = 'us' # # 欧洲服同步美洲服,美洲服同步国内服数据 # if CONFIG_INFO == 'eur': # domain_name = 'https://www.dvema.com/' # else: # region = 'cn' # domain_name = 'https://www.zositechc.cn/' # # url = domain_name + 'serialNumber/get-status' # response = requests.get(url=url, params={'serial_number': serial}, timeout=15) # # if response.status_code != 200: # operation += '查询{}服UID数据响应状态码异常:{}'.format(region, response.status_code) # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result # # results = json.loads(response.text) # if results['result_code'] != 0: # operation += '查询{}服UID数据result_code异常:{}'.format(region, results['result_code']) # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result # # # 其它服没有绑定uid # uid_info = results['result']['uidInfo'] # if not uid_info: # operation += '{}服没有绑定UID'.format(region) # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result # # 同步uid数据 # if uid_info['p2p_type'] == 1: # operation += '尚云UID不同步数据' # else: # uid_qs = UIDModel.objects.filter(uid=uid_info['uid']) # if not uid_qs.exists(): # uid_info['add_time'] = now_time # uid_info['update_time'] = now_time # uid_id = UIDModel.objects.create(**uid_info).id # else: # if uid_qs.first().status == 2: # 判断uid是否被使用 # operation += 'uid{}已被使用'.format(uid_info['uid']) # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result # else: # uid_qs.update(status=2, update_time=now_time) # uid_id = uid_qs.first().id # # # 企业序列号关联 uid # UIDCompanySerialModel.objects.create( # uid_id=uid_id, company_serial_id=company_serial_id, add_time=now_time, update_time=now_time) # # 修改企业序列号状态为2(绑定uid) # CompanySerialModel.objects.filter(id=company_serial_id) \ # .update(status=2, update_time=now_time) # sync_iot_result = SerialNumberView.sync_iot_core_data(domain_name, serial) # # operation += '同步{}服uid数据成功,同步iot数据结果:{}'.format(region, sync_iot_result) # sync_result = True # # # 记录日志 # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result # # except Exception as e: # operation += '同步数据异常,error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)) # log['operation'] = operation # LogModel.objects.create(**log) # return sync_result def do_get_uid(self, request_dict, response): serial_number = request_dict.get('serial_number', None) token = request_dict.get('token', None) time_stamp = request_dict.get('time_stamp', None) if token and time_stamp and serial_number: # 时间戳token校验 if not CommonService.check_time_stamp_token(token, time_stamp): return response.json(13) mark = serial_number[6:9] serial = serial_number[0:6] uid_company_serial_qs = UIDCompanySerialModel.objects.filter(company_serial__company__mark=mark, company_serial__serial_number__serial_number=serial) if uid_company_serial_qs.exists(): uid = uid_company_serial_qs.values('uid__uid', 'uid__mac', 'uid__uid_extra')[0] res = { 'uid': CommonService.encode_data(uid['uid__uid']), 'mac': CommonService.encode_data(uid['uid__mac']), 'extra': uid['uid__uid_extra'] } return response.json(0, res) else: return response.json(173) else: return response.json(444) @staticmethod def do_detach_uid(request, request_dict, response): token = request_dict.get('token', None) time_stamp = request_dict.get('time_stamp', None) serial_number = request_dict.get('serial_number', None) if not all([token, time_stamp, serial_number]): return response.json(444) # 时间戳token校验 if not CommonService.check_time_stamp_token(token, time_stamp): return response.json(13) now_time = int(time.time()) # 记录操作日志 ip = CommonService.get_ip_address(request) content = json.loads(json.dumps(request_dict)) serial = serial_number[0:6] company_serial_qs = CompanySerialModel.objects.filter(serial_number=serial).values('status') if not company_serial_qs.exists(): return response.json(379) status = company_serial_qs[0]['status'] if status == 1: return response.json(0, {'success': '序列号未绑定uid'}) elif status == 3: return response.json(10043) uid_serial_qs = UIDCompanySerialModel.objects.filter(company_serial__serial_number=serial) if not uid_serial_qs.exists(): return response.json(173) uid_serial = uid_serial_qs[0] # 判断序列号是否刚绑定uid,刚绑定需要过1分钟才能解绑 redisObj = RedisObject() used_serial_redis_list = redisObj.lrange(USED_SERIAL_REDIS_LIST, 0, -1) used_serial_redis_list = [str(i, 'utf-8') for i in used_serial_redis_list] if serial in used_serial_redis_list: return response.json(5) # 判断序列号是否未清除uid数据 serial_unbind_uid_qs = SerialUnbindUID.objects.filter(serial=serial, status=0) if serial_unbind_uid_qs.exists(): return response.json(5) try: uid = uid_serial.uid.uid # 写入序列号解绑uid表 SerialUnbindUID.objects.create(serial=serial, uid=uid, created_time=now_time, updated_time=now_time) end_time = int(time.time()) # 记录操作日志 log = { 'ip': ip, 'user_id': 1, 'status': 200, 'time': now_time, 'content': json.dumps(content), 'url': 'serialNumber/detachUID', 'operation': '序列号{}加入解绑队列uid:{}:{}秒'.format(serial, uid, end_time-now_time), } LogModel.objects.create(**log) return response.json(0) except Exception as e: log = { 'ip': ip, 'user_id': 1, 'status': 200, 'time': now_time, 'content': json.dumps(content) + '异常:{}'.format(repr(e)), 'url': 'serialNumber/detachUID', 'operation': '序列号{}解绑uid异常'.format(serial), } LogModel.objects.create(**log) return response.json(176, str(e)) def get_uid(self, request_dict, response, request, user_id): """ app获取序列号 @param request_dict: @param response: @param request: @param user_id: @return: """ token = request_dict.get('token', None) time_stamp = request_dict.get('time_stamp', None) company_secret = request_dict.get('company_id', None) serial_number = request_dict.get('serial_number', None) if not all([token, time_stamp, company_secret, serial_number]): return response.json(444) # 时间戳token校验 if not CommonService.check_time_stamp_token(token, time_stamp): return response.json(13) now_time = int(time.time()) serial = serial_number[0:6] # 判断序列号是否已和企业关联 company_serial_qs = CompanySerialModel.objects.filter(company__secret=company_secret, serial_number=serial) if not company_serial_qs.exists(): return response.json(173) company_serial = company_serial_qs[0] try: # 添加或更新扫码记录 ip = CommonService.get_ip_address(request) LOGGER.info('请求获取UID{},userId:{},ip:{}'.format(serial_number, user_id, ip)) # 查询用户国家id region_country = 0 # ios请求头没传token,user_id为None if user_id: device_user_qs = Device_User.objects.filter(userID=user_id).values('region_country') if device_user_qs.exists(): region_country = device_user_qs[0]['region_country'] data = { 'ip': ip, 'update_time': now_time, 'region_country': region_country } app_scanned_serial_qs = AppScannedSerial.objects.filter(serial=serial) if app_scanned_serial_qs.exists(): app_scanned_serial_qs.update(**data) else: data['serial'] = serial data['add_time'] = now_time AppScannedSerial.objects.create(**data) # 扫码后删除缓存 redis_obj = RedisObject() scanned_serial_key = serial + 'scanned_serial' redis_obj.del_data(scanned_serial_key) if company_serial.status == 0 or company_serial.status == 1: # 未使用 UnicomComboView().is_4g_device(serial_number, request_dict, request) return response.json(173) elif company_serial.status == 2: # 返回uid res = self.get_uid_info_by_serial(company_serial.id) return response.json(0, res) elif company_serial.status == 3: # 已占用 sync_success = self.sync_serial_data_and_log(request, company_serial.id, serial_number, now_time) if not sync_success: return response.json(20001) return response.json(0, self.get_uid_info_by_serial(company_serial.id)) except Exception as e: error_logger = logging.getLogger('django') error_logger.exception(repr(e)) return response.json(176, str(e)) @staticmethod def get_region_info(request_dict, response): """ 根据序列号状态确认uid地区 @param request_dict: 请求参数 @param response: 响应对象 @request_dict serial_number: 序列号 @return: response """ serial_number = request_dict.get('serial_number', None) if not serial_number: return response(444) company_serial_qs = CompanySerialModel.objects.filter(serial_number=serial_number[:6]).values('status') if not company_serial_qs.exists(): return response.json(173) status = company_serial_qs[0]['status'] if status == 2: if CONFIG_INFO == CONFIG_CN: return response.json(0, {'region': 1}) elif CONFIG_INFO == CONFIG_US: return response.json(0, {'region': 3}) elif status == 3: if CONFIG_INFO == CONFIG_CN: return response.json(0, {'region': 3}) elif CONFIG_INFO == CONFIG_US: return response.json(0, {'region': 1}) return response.json(0) @staticmethod def save_region(request, request_dict, response): """ 保存设备地区信息 @param request: 请求体 @param request_dict: 请求参数 @param response: 响应对象 @request_dict token: 令牌 @request_dict time_stamp: 时间戳 @return: response """ token = request_dict.get('token', None) serial_number = request_dict.get('serial_number', None) if not all([serial_number, token]): return response(444) try: serial_number = serial_number[:6] # 测试和国内服不保存区域信息 if CONFIG_INFO == CONFIG_CN or CONFIG_INFO == CONFIG_TEST: return response.json(0) # 不是美洲服,请求美洲域名保存数据 if CONFIG_INFO != CONFIG_US: # token认证 token_obj = TokenObject(token) if token_obj.code != 0: return response.json(token_obj.code) response.lang = token_obj.lang url = SERVER_DOMAIN_US + 'serialNumber/saveRegion' data = request_dict.dict() data['ip'] = CommonService.get_ip_address(request) data['region_id'] = CommonService.confirm_region_id() try: r = requests.post(url=url, data=data, timeout=10) assert r.status_code == 200 res = r.json() assert res['result_code'] == 0 except Exception as e: pass # 美洲服,处理请求和保存数据 else: data = {} # 处理其他服务器发起请求的情况 region_id = request_dict.get('region_id', None) if region_id is not None: region_id = int(region_id) data['region_id'] = region_id data['ip'] = request_dict.get('ip', '') else: # token认证 token_obj = TokenObject(token) if token_obj.code != 0: return response.json(token_obj.code) response.lang = token_obj.lang region_id = CommonService.confirm_region_id() data['region_id'] = region_id data['ip'] = CommonService.get_ip_address(request) # 写入数据 device_domain_region_qs = DeviceDomainRegionModel.objects.filter(serial_number=serial_number). \ values('region_id') if not device_domain_region_qs.exists(): data['serial_number'] = serial_number DeviceDomainRegionModel.objects.create(**data) else: # 设备解绑过uid,更新region_id if device_domain_region_qs[0]['region_id'] == 0: device_domain_region_qs.update(**data) return response.json(0) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def get_domain(request_dict, response): """ 设备获取域名 @param request_dict: 请求参数 @param response: 响应对象 @request_dict serial_number: 序列号 @request_dict time_stamp_token: 时间戳token @request_dict time_stamp: 时间戳 @return: response """ serial_number = request_dict.get('serial_number', None) time_stamp_token = request_dict.get('time_stamp_token', None) time_stamp = request_dict.get('time_stamp', None) if not all([serial_number, time_stamp_token, time_stamp]): return response.json(444) # 时间戳token校验 if not CommonService.check_time_stamp_token(time_stamp_token, time_stamp): return response.json(13) try: serial_number = serial_number[:6] device_domain_region_qs = DeviceDomainRegionModel.objects.filter(serial_number=serial_number).values( 'region_id') if not device_domain_region_qs.exists(): return response.json(173) region_id = device_domain_region_qs[0]['region_id'] region_qs = RegionModel.objects.filter(id=region_id).values('api', 'push_api') if not region_qs.exists(): return response.json(173) # 国内和测试的推送存储地区为国内 push_region = 2 if region_id in [1, 5] else 1 res = { 'api': region_qs[0]['api'], 'push_api': region_qs[0]['push_api'], 'push_region': push_region } return response.json(0, res) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def reset_region_id(request_dict, response): """ 重置地区id @param request_dict: 请求参数 @param response: 响应对象 @request_dict serial_number: 序列号 @return: response """ serial_number = request_dict.get('serial_number', None) if not serial_number: return response(444) try: serial_number = serial_number[:6] DeviceDomainRegionModel.objects.filter(serial_number=serial_number).update(region_id=0) return response.json(0) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def get_uid_region(request_dict, response): """ 根据序列号获取uid地区 @param request_dict: 请求参数 @param response: 响应对象 @request_dict serial_number: 序列号 @return: response """ serial_number = request_dict.get('serial_number', None) if not serial_number: return response(444) try: serial_number = serial_number[:6] uid_company_serial_qs = UIDCompanySerialModel.objects.filter( company_serial__serial_number=serial_number).values('uid__uid') res = [] for item in uid_company_serial_qs: res.append({'uid': item['uid__uid']}) return response.json(0, res) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def get_global_uid_region(request_dict, response): """ 获取序列号在全球服绑定uid的地区 @param request_dict: 请求参数 @param response: 响应对象 @request_dict serial_number: 序列号 @return: response """ orders_domain_name_list = SERVER_DOMAIN_LIST if SERVER_DOMAIN_TEST in orders_domain_name_list: orders_domain_name_list.remove(SERVER_DOMAIN_TEST) uid_list = [] try: for orders_domain_name in orders_domain_name_list: url = orders_domain_name + 'serialNumber/getUidRegion' res = requests.post(url=url, data=request_dict) result = res.json() if result['result_code'] != 0: return response.json(result['result_code']) if orders_domain_name == SERVER_DOMAIN_CN: for item in result['result']: item['region'] = 1 uid_list.append(item) elif orders_domain_name == SERVER_DOMAIN_US: for item in result['result']: item['region'] = 3 uid_list.append(item) elif orders_domain_name == SERVER_DOMAIN_EUR: for item in result['result']: item['region'] = 4 uid_list.append(item) return response.json(0, uid_list) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @classmethod def get_iot_core_by_serial_number(cls, request_dict, response): """ 根据序列号查询iot core """ try: serial_number = request_dict.get('serialNumber', None) if not serial_number: return response.json(444) serial = serial_number[0:6] LOGGER.info('根据序列号查询IoTC:{}'.format(serial_number)) device_iot_qs = iotdeviceInfoModel.objects.filter(serial_number=serial) if not device_iot_qs.exists(): return response.json(173) iot_vo = device_iot_qs.first() iot_dto = { 'serial_number': iot_vo.serial_number, 'uid': iot_vo.uid, 'certificate_id': iot_vo.certificate_id, 'certificate_pem': iot_vo.certificate_pem, 'public_key': iot_vo.public_key, 'private_key': iot_vo.private_key, 'thing_name': iot_vo.thing_name, 'thing_groups': iot_vo.thing_groups, 'endpoint': iot_vo.endpoint, 'token_iot_number': iot_vo.token_iot_number, 'add_time': iot_vo.add_time.strftime("%Y-%m-%d %H:%M:%S"), 'update_time': iot_vo.update_time.strftime("%Y-%m-%d %H:%M:%S") } return response.json(0, {'iotInfo': iot_dto}) except Exception as e: LOGGER.info('查询序列号异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e))) return response.json(176, str(e)) @staticmethod def sync_iot_core_data(domain_name, serial_number): """ 同步iot core 数据 @param domain_name: 域名 @param serial_number: 序列号 """ try: serial = serial_number[0:6] device_iot_qs = iotdeviceInfoModel.objects.filter(serial_number=serial) if device_iot_qs.exists(): return False url = domain_name + 'serialNumber/getIoTCoreBySerialNumber' response = requests.get(url=url, params={'serialNumber': serial_number}, timeout=15) if response.status_code != 200: return False results = json.loads(response.text) if results['result_code'] != 0: return False iot_vo = results['result']['iotInfo'] iot_vo['add_time'] = datetime.strptime(iot_vo['add_time'], "%Y-%m-%d %H:%M:%S") iot_vo['update_time'] = datetime.strptime(iot_vo['update_time'], "%Y-%m-%d %H:%M:%S") iotdeviceInfoModel.objects.create(**iot_vo) return True except Exception as e: LOGGER.info('{}同步iot异常,errLine:{}, errMsg:{}'.format(serial_number, e.__traceback__.tb_lineno, repr(e))) return False @classmethod def save_user_net_info(cls, request_dict, response): """ 保存用户网络信息 @return: """ serial = request_dict.get('serial', None) username = request_dict.get('username', None) wifi_name = request_dict.get('wifi_name', None) wifi_password = request_dict.get('wifi_password', None) time_stamp = request_dict.get('time_stamp', None) token = request_dict.get('time_stamp_token', None) if not all([serial, username, wifi_name, wifi_password, time_stamp, token]): return response.json(444) if len(serial) < 14: return response.json(444, '序列号长度小于14') # 根据序列号获取p2p类型,设备类型信息 serial_number = serial[:9] p2p_type = serial[9:10] device_type = int(serial[10:14], 16) # 请求绑定uid或查询uid data = { 'serial_number': serial_number, 'p2ptype': p2p_type, 'time_stamp': time_stamp, 'token': token, 'is_verify': '1' } url = SERVER_DOMAIN + 'serialNumber/attachUID' try: r = requests.post(url=url, data=data, timeout=30) assert r.status_code == 200 r = r.json() if r['result_code'] != 0: return response.json(r['result_code']) uid = CommonService.decode_data(r['result']['uid']) # 查询用户是否存在,不存在则创建 device_user_qs = Device_User.objects.filter(username=username).values('userID') if device_user_qs.exists(): user_id = device_user_qs[0]['userID'] # 判断设备是否已被其他账号添加 device_info_qs = Device_Info.objects.filter(~Q(userID_id=user_id), UID=uid).values('id') if device_info_qs.exists(): return response.json(174) # 已保存过,更新网络信息 device_info_qs = Device_Info.objects.filter(userID_id=user_id, UID=uid).values('id') if device_info_qs.exists(): device_id = device_info_qs[0]['id'] device_net_info_qs = DeviceNetInfo.objects.filter(device_id=device_id) if device_net_info_qs.exists(): device_net_info_qs.update(wifi_name=wifi_name, wifi_password=wifi_password) else: # 创建用户设备和设备网络数据 cls.creat_device_data(user_id, serial, uid, device_type, username, wifi_name, wifi_password) else: # 判断设备是否已被其他账号添加 device_info_qs = Device_Info.objects.filter(UID=uid).values('id') if device_info_qs.exists(): return response.json(174) password = '' user_id = CommonService.getUserID(μs=False, setOTAID=True) user_data = { 'userID': user_id, 'username': username, 'NickName': username, 'password': password, 'is_active': True, 'user_isValid': True } # 判断用户名是手机号还是邮箱 if DataValid().mobile_validate(username): user_data['phone'] = username elif DataValid().email_validate(username): user_data['userEmail'] = username else: return response.json(444) Device_User.objects.create(**user_data) # 创建用户设备和设备网络数据 cls.creat_device_data(user_id, serial, uid, device_type, username, wifi_name, wifi_password) # 生成或更新扫码记录 serial = serial[:6] now_time = int(time.time()) app_scanned_serial_qs = AppScannedSerial.objects.filter(serial=serial) if app_scanned_serial_qs.exists(): app_scanned_serial_qs.update(update_time=now_time) else: AppScannedSerial.objects.create(serial=serial, add_time=now_time, update_time=now_time) return response.json(0) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def creat_device_data(user_id, serial, uid, device_type, username, wifi_name, wifi_password): """ @param user_id: @param serial: @param uid: @param device_type: @param username: @param wifi_name: @param wifi_password: @return: """ nickname = serial[:6] device_id = CommonService.getUserID(getUser=False) Device_Info.objects.create( id=device_id, userID_id=user_id, serial_number=serial[:9], UID=uid, NickName=nickname, Type=device_type, View_Account='admin', View_Password='admin', vodPrimaryMaster=username, vodPrimaryUserID=user_id ) DeviceNetInfo.objects.create( device_id=device_id, wifi_name=wifi_name, wifi_password=wifi_password ) # 创建或更新设备影子昵称 uid_set_qs = UidSetModel.objects.filter(uid=uid) if uid_set_qs.exists(): uid_set_qs.update(nickname=nickname) else: UidSetModel.objects.create(uid=uid, nickname=nickname) @staticmethod def get_baidu_big_model_license_url(request_dict, response): """ 获取百度大模型许可证下载链接 @param request_dict: 请求参数 @param response: 响应对象 @request_dict serial_number: 序列号 @return: response """ serial = request_dict.get('serial', None) if not serial: return response.json(444) try: # 同个序列号返回相同license baidu_big_model_license_qs = BaiduBigModelLicense.objects.filter(serial=serial).values('license_name') if baidu_big_model_license_qs.exists(): license_name = baidu_big_model_license_qs[0]['license_name'] else: # 查询未被使用的license,使用事务和行锁避免并发问题 with transaction.atomic(): baidu_big_model_license_qs = BaiduBigModelLicense.objects.filter( serial='', use_status=0 ).select_for_update().order_by('id').first() if not baidu_big_model_license_qs: return response.json(173) license_name = baidu_big_model_license_qs.license_name # 更新数据为已使用 now_time = int(time.time()) baidu_big_model_license_qs.serial = serial baidu_big_model_license_qs.use_status = 1 baidu_big_model_license_qs.updated_time = now_time baidu_big_model_license_qs.save() # 生成华为云OBS临时下载链接 from obs import ObsClient obs_client = ObsClient( access_key_id=HUAWEICLOUD_AK, secret_access_key=HUAWEICLOUD_SK, server=HUAWEICLOUD_OBS_SERVER ) download_url = obs_client.createSignedUrl( method='GET', bucketName=HUAWEICLOUD_BAIDU_BIG_MODEL_LICENSE_BUKET, objectKey=license_name, expires=3600 # 有效期1小时 ) res = { 'license_url': download_url.signedUrl } return response.json(0, res) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) @staticmethod def get_instavision(request_dict, response): """ 获取instavision数据 @param request_dict: 请求参数 @param response: 响应对象 @request_dict mac: 设备mac地址 @return: response """ mac = request_dict.get('mac', None) if not mac: return response.json(444) try: # 查询mac是否已存在数据 instavision_qs = Instavision.objects.filter(mac=mac, use_status=1).values('device_id', 'access_key') if instavision_qs.exists(): res = { 'device_id': instavision_qs[0]['device_id'], 'access_key': instavision_qs[0]['access_key'], } else: # 使用事务和select_for_update来处理并发问题 with transaction.atomic(): # 使用select_for_update加锁,防止并发时多个请求获取到同一条记录 instavision_qs = Instavision.objects.select_for_update().filter(use_status=0).first() if not instavision_qs: return response.json(444, '没有可用的即时视觉数据') device_id = instavision_qs.device_id access_key = instavision_qs.access_key # 更新该条数据的mac、use_status和updated_time instavision_qs.mac = mac instavision_qs.use_status = 1 # 标记为已使用 instavision_qs.updated_time = int(time.time()) instavision_qs.save() res = { 'device_id': device_id, 'access_key': access_key, } return response.json(0, res) except Exception as e: return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))