소스 검색

设备获取定制UID

zhangdongming 4 달 전
부모
커밋
c54a99864b
2개의 변경된 파일98개의 추가작업 그리고 75개의 파일을 삭제
  1. 1 1
      Ansjer/urls.py
  2. 97 74
      Controller/UserDevice/DeviceCustomUIDController.py

+ 1 - 1
Ansjer/urls.py

@@ -294,7 +294,7 @@ urlpatterns = [
     re_path('open/device/configuration/(?P<operation>.*)', DeviceVersionInfoController.DeviceVersionInfoView.as_view()),
     # 获取APN配置信息
     re_path('APNConfig/(?P<operation>.*)', APNConfigController.APNConfigView.as_view()),
-    re_path(r'^api/device/Custom/(?P<operation>.*)$', DeviceCustomUIDController.DeviceCustomUIDView.as_view()),
+    re_path(r'^api/device/custom/(?P<operation>.*)$', DeviceCustomUIDController.DeviceCustomUIDView.as_view()),
 
 
 

+ 97 - 74
Controller/UserDevice/DeviceCustomUIDController.py

@@ -1,14 +1,16 @@
+import json
 import time
 
-from MySQLdb import DatabaseError
 from django.db import transaction
 from django.http import QueryDict
 from django.views import View
-from Ansjer.config import LOGGER
+
 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
-from Object.RedisObject import RedisObject
 
 
 class DeviceCustomUIDView(View):
@@ -41,14 +43,15 @@ class DeviceCustomUIDView(View):
         if operation == 'getUID':  # 获取电池电量列表
             return self.get_custom_uid(request, request_dict, response)
 
-    @classmethod
-    def get_custom_uid(cls, request, request_dict, response):
+    @staticmethod
+    def get_custom_uid(request, request_dict, response):
         ip = CommonService.get_ip_address(request)
-        LOGGER.info('获取定制客户UID:{},ip:{}'.format(request_dict, ip))
-        mac = request_dict.get('mac', None)
-        token = request_dict.get('token', None)
-        time_stamp = request_dict.get('time_stamp', None)
+        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)
@@ -58,77 +61,97 @@ class DeviceCustomUIDView(View):
             LOGGER.error(f'{mac}时间戳校验失败time:{time_stamp},tk:{token}')
             return response.json(13)
 
-        # 分布式锁配置
-        redis = RedisObject()
-
-        lock_key = f"uid_assign:{mac}"
-        request_id = f"{mac}_{time_stamp}"
+        redis = RedisObject(db=3)
+        lock_key = f"MAC:ASSIGN:{mac}"
+        request_id = f"{mac}_{int(time.time())}"
 
         try:
-            # 第一次检查现有绑定
-            existing_binding = DeviceCustomUID.objects.filter(
-                device_mac=mac,
-                status=1
-            ).select_related('uid').first()
-
-            if existing_binding:
-                LOGGER.info(f'{mac}已存在有效绑定')
-                return response.json(0, data={'uid': existing_binding.uid.uid})
-
-            # 获取分布式锁(设置5秒过期时间)
-            if not redis.try_lock(lock_key, request_id, expire=5, time_unit_second=1):
-                LOGGER.error(f'{mac}获取锁失败')
+            # 预检查绑定状态(缓存优化)
+            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)
-
-            # 二次检查(防止在等待锁期间被其他请求处理)
-            existing_binding = DeviceCustomUID.objects.filter(
-                device_mac=mac,
-                status=1
-            ).select_related('uid').first()
-
-            if existing_binding:
-                LOGGER.info(f'{mac}二次检查存在绑定')
-                return response.json(0, data={'uid': existing_binding.uid.uid})
-
-            # 核心分配逻辑
             with transaction.atomic():
-                # 获取可用UID(使用SELECT FOR UPDATE锁定行)
-                available_uid = CustomUIDPool.objects.select_for_update() \
-                    .filter(status=0) \
-                    .order_by('id') \
-                    .first()
-
-                if not available_uid:
-                    LOGGER.error(f'{mac}UID池已耗尽')
-                    return response.json(173)
-
-                # 更新UID状态
-                n_time = int(time.time())
-                available_uid.status = 1
-                available_uid.updated_time = n_time
-                available_uid.save(update_fields=['status', 'updated_time'])
-
-                # 创建绑定记录
-                DeviceCustomUID.objects.create(
-                    uid=available_uid,
+                # 二次检查(带数据库查询)
+                existing = DeviceCustomUID.objects.filter(
                     device_mac=mac,
-                    status=1,
-                    created_time=n_time,
-                    updated_time=n_time
-                )
-
-                LOGGER.info(f'{mac}成功分配UID: {available_uid.uid}')
-                return response.json(0, data={'uid': available_uid.uid})
+                    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 DatabaseError as e:
-            LOGGER.error(f'{mac}数据库操作异常: {str(e)}')
-            return response.json(18)
         except Exception as e:
-            LOGGER.error(f'系统异常: {str(e)}')
+            LOGGER.error(f'mac:{mac}系统异常: {str(e)}', exc_info=True)
             return response.json(500)
         finally:
-            # 确保释放锁
-            if redis.release_lock(lock_key, request_id):
-                LOGGER.debug(f'{mac}锁释放成功')
-            else:
-                LOGGER.warning(f'{mac}锁释放异常')
+            redis.release_lock(lock_key, request_id)