""" 设备模块视图 - App端 """ from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated, AllowAny from utils.response import success, error from utils.exceptions import ErrorCode from apps.admins.authentication import AppJWTAuthentication from .models import Device, UserDevice, DeviceType from .serializers import ( DeviceSerializer, UserDeviceSerializer, BindDeviceSerializer, DeviceVerifySerializer, DeviceTypeSerializer ) class DeviceViewSet(viewsets.ViewSet): """设备视图集(App端)""" authentication_classes = [AppJWTAuthentication] permission_classes = [IsAuthenticated] @action(detail=False, methods=['get'], url_path='query-by-mac', authentication_classes=[], permission_classes=[AllowAny]) def query_by_mac(self, request): """ 通过MAC地址查询SN码(无需登录) GET /api/v1/devices/query-by-mac?mac=AA:BB:CC:DD:EE:FF """ mac = request.query_params.get('mac', '') if not mac: return error(message='MAC地址不能为空') # 统一格式 mac = mac.upper().replace('-', ':') try: device = Device.objects.select_related('device_type').get(mac_address=mac) return success(data={ 'sn': device.sn, 'mac_address': device.mac_address, 'device_type': DeviceTypeSerializer(device.device_type).data if device.device_type else None, 'status': device.status, 'is_bound': device.status == 'bound' }) except Device.DoesNotExist: return error( code=404, message='未找到对应的设备,请检查MAC地址是否正确或设备是否已完成入库', status_code=status.HTTP_404_NOT_FOUND ) @action(detail=False, methods=['post']) def verify(self, request): """ 验证设备SN POST /api/v1/devices/verify """ serializer = DeviceVerifySerializer(data=request.data) if not serializer.is_valid(): return error(message=str(serializer.errors)) sn = serializer.validated_data['sn'] try: device = Device.objects.select_related('device_type').get(sn=sn) except Device.DoesNotExist: return error(code=ErrorCode.DEVICE_NOT_FOUND, message='设备不存在') # 检查是否已被绑定 is_bindable = device.status != 'bound' return success(data={ 'sn': device.sn, 'is_bindable': is_bindable, 'device_type': DeviceTypeSerializer(device.device_type).data if device.device_type else None }) @action(detail=False, methods=['post']) def bind(self, request): """ 绑定设备 POST /api/v1/devices/bind """ serializer = BindDeviceSerializer(data=request.data) if not serializer.is_valid(): return error(message=str(serializer.errors)) sn = serializer.validated_data['sn'] spirit_id = serializer.validated_data.get('spirit_id') try: device = Device.objects.get(sn=sn) except Device.DoesNotExist: return error(code=ErrorCode.DEVICE_NOT_FOUND, message='设备不存在') # 检查是否已被绑定 if device.status == 'bound': # 检查是否是当前用户绑定的 existing = UserDevice.objects.filter(device=device, is_active=True).first() if existing and existing.user != request.user: return error(code=ErrorCode.DEVICE_ALREADY_BOUND, message='设备已被其他用户绑定') # 创建绑定关系 user_device, created = UserDevice.objects.update_or_create( user=request.user, device=device, defaults={ 'spirit_id': spirit_id, 'is_active': True } ) # 更新设备状态 device.status = 'bound' device.save() return success( data=UserDeviceSerializer(user_device).data, message='绑定成功' if created else '更新绑定成功' ) @action(detail=False, methods=['get']) def my_devices(self, request): """ 我的设备列表 GET /api/v1/devices/my_devices """ user_devices = UserDevice.objects.filter( user=request.user, is_active=True ).select_related('device', 'device__device_type', 'spirit') serializer = UserDeviceSerializer(user_devices, many=True) return success(data=serializer.data) @action(detail=True, methods=['delete']) def unbind(self, request, pk=None): """ 解绑设备 DELETE /api/v1/devices/{id}/unbind """ try: user_device = UserDevice.objects.get(id=pk, user=request.user) except UserDevice.DoesNotExist: return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在') # 更新绑定状态 user_device.is_active = False user_device.save() # 检查设备是否还有其他活跃绑定 active_bindings = UserDevice.objects.filter(device=user_device.device, is_active=True).count() if active_bindings == 0: user_device.device.status = 'out_stock' user_device.device.save() return success(message='解绑成功') @action(detail=True, methods=['put'], url_path='update-spirit') def update_spirit(self, request, pk=None): """ 更新设备绑定的智能体 PUT /api/v1/devices/{id}/update-spirit """ try: user_device = UserDevice.objects.get(id=pk, user=request.user, is_active=True) except UserDevice.DoesNotExist: return error(code=ErrorCode.DEVICE_NOT_FOUND, message='绑定记录不存在') spirit_id = request.data.get('spirit_id') user_device.spirit_id = spirit_id user_device.save() return success(data=UserDeviceSerializer(user_device).data, message='更新成功')