import json import time from django.db import transaction from django.http import QueryDict from django.views import View from AgentModel.models import DeviceCustomUID, CustomUIDPool from Ansjer.config import LOGGER from Model.models import LogModel from Object.RedisObject import RedisObject from Object.ResponseObject import ResponseObject from Service.CommonService import CommonService class DeviceCustomUIDView(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('en') if operation == 'getUID': # 获取电池电量列表 return self.get_custom_uid(request, request_dict, response) @staticmethod def get_custom_uid(request, request_dict, response): ip = CommonService.get_ip_address(request) LOGGER.info(f'获取定制客户UID:{request_dict},ip:{ip}') mac = request_dict.get('mac') token = request_dict.get('token') time_stamp = request_dict.get('time_stamp') # 参数校验 if not all([mac, token, time_stamp]): LOGGER.error(f'{mac}请求绑定uid参数缺失') return response.json(444) # 时间戳token校验 if not CommonService.check_time_stamp_token(token, time_stamp): LOGGER.error(f'{mac}时间戳校验失败time:{time_stamp},tk:{token}') return response.json(13) redis = RedisObject(db=3) lock_key = f"MAC:ASSIGN:{mac}" request_id = f"{mac}_{int(time.time())}" try: # 预检查绑定状态(缓存优化) cached_uid = redis.get_data(f"DEVICE:MAC:{mac}") if cached_uid: return response.json(0, data={'uid': cached_uid}) # 分布式锁配置(增加锁持有检测) acquired = False for _ in range(3): if redis.try_lock(lock_key, request_id, expire=5, time_unit_second=1): acquired = True break time.sleep(0.3) if not acquired: LOGGER.warning(f'{mac}系统繁忙请稍后重试') return response.json(5) with transaction.atomic(): # 二次检查(带数据库查询) existing = DeviceCustomUID.objects.filter( device_mac=mac, status=1 ).only('uid').first() if existing: # 更新缓存 redis.set_ex_data(f"DEVICE:MAC:{mac}", existing.uid, 10) return response.json(0, {'uid': existing.uid}) count = 0 while count < 5: available_uid = ( CustomUIDPool.objects .filter(status=0) .order_by('updated_time') .first() ) if not available_uid: LOGGER.error(f'{mac}UID池资源耗尽') return response.json(173) # 查询到未被绑定的UID进行redis加锁 uid_lock = redis.try_lock(f"DEVICE:UID:{available_uid.uid}", request_id, expire=3, time_unit_second=1) if not uid_lock: count += 1 continue # 原子操作更新 rows_updated = CustomUIDPool.objects.filter( id=available_uid.id, status=0 # 乐观锁校验 ).update( status=1, updated_time=int(time.time()) ) if int(rows_updated) <= 0: # 更新失败 count += 1 continue if not rows_updated: LOGGER.warning(f'{mac}UID并发竞争更新失败') return response.json(14) # 创建设备关联记录 DeviceCustomUID.objects.create( device_mac=mac, uid=available_uid.uid, status=1, updated_time=int(time.time()), created_time=int(time.time()) ) content = json.loads(json.dumps(request_dict)) log = { 'ip': ip, 'user_id': 1, 'status': 200, 'time': int(time.time()), 'content': json.dumps(content), 'url': 'api/device/custom/getUID', 'operation': '定制设备mac{}绑定uid: {}'.format(mac, available_uid.uid), } LogModel.objects.create(**log) # 设置缓存 redis.set_ex_data(f"MAC_UID_CACHE:{mac}", available_uid.uid, 10) return response.json(0, {'uid': available_uid.uid}) except Exception as e: LOGGER.error(f'mac:{mac}系统异常: {str(e)}', exc_info=True) return response.json(500) finally: redis.release_lock(lock_key, request_id)