Pārlūkot izejas kodu

新增设备获取定制客户UID

zhangdongming 4 mēneši atpakaļ
vecāks
revīzija
05ccf7715a

+ 31 - 0
AgentModel/models.py

@@ -186,3 +186,34 @@ class ApplyAgent(models.Model):
         verbose_name = '申请代理表'
         verbose_name_plural = verbose_name
         app_label = 'AgentModel'
+
+
+class CustomUIDPool(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增标记ID')
+    uid = models.CharField(default='', db_index=True, max_length=32, unique=True, verbose_name='设备ID')
+    type = models.SmallIntegerField(default=1, verbose_name=u'外销客户UID')
+    customer_name = models.CharField(default='', max_length=32, verbose_name='客户id')
+    status = models.IntegerField(default=0, verbose_name='0:未绑,1:绑定,2:弃用不再使用')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'custom_uid_pool'
+        verbose_name = '定制UID池'
+        verbose_name_plural = verbose_name
+        app_label = 'AgentModel'
+
+
+class DeviceCustomUID(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增标记ID')
+    uid = models.CharField(default='', db_index=True, max_length=32, unique=True, verbose_name='设备ID')
+    device_mac = models.CharField(default='', db_index=True, max_length=64, unique=True, verbose_name='mac地址')
+    status = models.IntegerField(default=0, verbose_name='1:绑定,2:失败')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'device_custom_uid'
+        verbose_name = '设备关联定制UID'
+        verbose_name_plural = verbose_name
+        app_label = 'AgentModel'

+ 2 - 1
Ansjer/urls.py

@@ -33,7 +33,7 @@ from Controller.MessagePush import EquipmentMessagePush
 from Controller.SensorGateway import SensorGatewayController, EquipmentFamilyController
 from Controller.Surveys import CloudStorageController
 from Controller.UserDevice import UserDeviceShareController, UserSubscriptionController, DeviceVersionInfoController, \
-    APNConfigController, ActivityCenterController
+    APNConfigController, ActivityCenterController, DeviceCustomUIDController
 from Controller.CampaignController import AppCampaignController, AdDepartmentController
 from Controller.UserDevice import SerialNumberCheckController
 from Model import views  # 定时任务,不要删除该行代码
@@ -294,6 +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()),
 
 
 

+ 134 - 0
Controller/UserDevice/DeviceCustomUIDController.py

@@ -0,0 +1,134 @@
+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 Object.ResponseObject import ResponseObject
+from Service.CommonService import CommonService
+from Object.RedisObject import RedisObject
+
+
+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)
+
+    @classmethod
+    def get_custom_uid(cls, 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)
+
+        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()
+
+        lock_key = f"uid_assign:{mac}"
+        request_id = f"{mac}_{time_stamp}"
+
+        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}获取锁失败')
+                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,
+                    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})
+
+        except DatabaseError as e:
+            LOGGER.error(f'{mac}数据库操作异常: {str(e)}')
+            return response.json(18)
+        except Exception as e:
+            LOGGER.error(f'系统异常: {str(e)}')
+            return response.json(500)
+        finally:
+            # 确保释放锁
+            if redis.release_lock(lock_key, request_id):
+                LOGGER.debug(f'{mac}锁释放成功')
+            else:
+                LOGGER.warning(f'{mac}锁释放异常')