DeviceCustomUIDController.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import json
  2. import time
  3. from django.db import transaction
  4. from django.http import QueryDict
  5. from django.views import View
  6. from AgentModel.models import DeviceCustomUID, CustomUIDPool
  7. from Ansjer.config import LOGGER
  8. from Model.models import LogModel
  9. from Object.RedisObject import RedisObject
  10. from Object.ResponseObject import ResponseObject
  11. from Service.CommonService import CommonService
  12. class DeviceCustomUIDView(View):
  13. def get(self, request, *args, **kwargs):
  14. request.encoding = 'utf-8'
  15. operation = kwargs.get('operation')
  16. return self.validation(request.GET, request, operation)
  17. def post(self, request, *args, **kwargs):
  18. request.encoding = 'utf-8'
  19. operation = kwargs.get('operation')
  20. return self.validation(request.POST, request, operation)
  21. def delete(self, request, *args, **kwargs):
  22. request.encoding = 'utf-8'
  23. operation = kwargs.get('operation')
  24. delete = QueryDict(request.body)
  25. if not delete:
  26. delete = request.GET
  27. return self.validation(delete, request, operation)
  28. def put(self, request, *args, **kwargs):
  29. request.encoding = 'utf-8'
  30. operation = kwargs.get('operation')
  31. put = QueryDict(request.body)
  32. return self.validation(put, request, operation)
  33. def validation(self, request_dict, request, operation):
  34. response = ResponseObject('en')
  35. if operation == 'getUID': # 获取电池电量列表
  36. return self.get_custom_uid(request, request_dict, response)
  37. @staticmethod
  38. def get_custom_uid(request, request_dict, response):
  39. ip = CommonService.get_ip_address(request)
  40. LOGGER.info(f'获取定制客户UID:{request_dict},ip:{ip}')
  41. mac = request_dict.get('mac')
  42. token = request_dict.get('token')
  43. time_stamp = request_dict.get('time_stamp')
  44. # 参数校验
  45. if not all([mac, token, time_stamp]):
  46. LOGGER.error(f'{mac}请求绑定uid参数缺失')
  47. return response.json(444)
  48. # 时间戳token校验
  49. if not CommonService.check_time_stamp_token(token, time_stamp):
  50. LOGGER.error(f'{mac}时间戳校验失败time:{time_stamp},tk:{token}')
  51. return response.json(13)
  52. redis = RedisObject(db=3)
  53. lock_key = f"MAC:ASSIGN:{mac}"
  54. request_id = f"{mac}_{int(time.time())}"
  55. try:
  56. # 预检查绑定状态(缓存优化)
  57. cached_uid = redis.get_data(f"DEVICE:MAC:{mac}")
  58. if cached_uid:
  59. return response.json(0, data={'uid': cached_uid})
  60. # 分布式锁配置(增加锁持有检测)
  61. acquired = False
  62. for _ in range(3):
  63. if redis.try_lock(lock_key, request_id, expire=5, time_unit_second=1):
  64. acquired = True
  65. break
  66. time.sleep(0.3)
  67. if not acquired:
  68. LOGGER.warning(f'{mac}系统繁忙请稍后重试')
  69. return response.json(5)
  70. with transaction.atomic():
  71. # 二次检查(带数据库查询)
  72. existing = DeviceCustomUID.objects.filter(
  73. device_mac=mac,
  74. status=1
  75. ).only('uid').first()
  76. if existing:
  77. # 更新缓存
  78. redis.set_ex_data(f"DEVICE:MAC:{mac}", existing.uid, 10)
  79. return response.json(0, {'uid': existing.uid})
  80. count = 0
  81. while count < 5:
  82. available_uid = (
  83. CustomUIDPool.objects
  84. .filter(status=0)
  85. .order_by('updated_time')
  86. .first()
  87. )
  88. if not available_uid:
  89. LOGGER.error(f'{mac}UID池资源耗尽')
  90. return response.json(173)
  91. # 查询到未被绑定的UID进行redis加锁
  92. uid_lock = redis.try_lock(f"DEVICE:UID:{available_uid.uid}",
  93. request_id, expire=3, time_unit_second=1)
  94. if not uid_lock:
  95. count += 1
  96. continue
  97. # 原子操作更新
  98. rows_updated = CustomUIDPool.objects.filter(
  99. id=available_uid.id,
  100. status=0 # 乐观锁校验
  101. ).update(
  102. status=1,
  103. updated_time=int(time.time())
  104. )
  105. if int(rows_updated) <= 0: # 更新失败
  106. count += 1
  107. continue
  108. if not rows_updated:
  109. LOGGER.warning(f'{mac}UID并发竞争更新失败')
  110. return response.json(14)
  111. # 创建设备关联记录
  112. DeviceCustomUID.objects.create(
  113. device_mac=mac,
  114. uid=available_uid.uid,
  115. status=1,
  116. updated_time=int(time.time()),
  117. created_time=int(time.time())
  118. )
  119. content = json.loads(json.dumps(request_dict))
  120. log = {
  121. 'ip': ip,
  122. 'user_id': 1,
  123. 'status': 200,
  124. 'time': int(time.time()),
  125. 'content': json.dumps(content),
  126. 'url': 'api/device/custom/getUID',
  127. 'operation': '定制设备mac{}绑定uid: {}'.format(mac, available_uid.uid),
  128. }
  129. LogModel.objects.create(**log)
  130. # 设置缓存
  131. redis.set_ex_data(f"MAC_UID_CACHE:{mac}", available_uid.uid, 10)
  132. return response.json(0, {'uid': available_uid.uid})
  133. except Exception as e:
  134. LOGGER.error(f'mac:{mac}系统异常: {str(e)}', exc_info=True)
  135. return response.json(500)
  136. finally:
  137. redis.release_lock(lock_key, request_id)