diff --git a/apps/ai/helmet/BeiHangGrpc.proto b/apps/ai/helmet/BeiHangGrpc.proto new file mode 100644 index 00000000..d3d96ef5 --- /dev/null +++ b/apps/ai/helmet/BeiHangGrpc.proto @@ -0,0 +1,38 @@ +//python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./BeiHangGrpc.proto +syntax = "proto3"; + +option java_generic_services = true; +option java_multiple_files = true; +option java_package = "com.lyzh.aiservice.grpc"; +option java_outer_classname = "BeiHangProto"; + +service Helmet_Reg { + rpc sendHelmet_Info(JinYu_Request) returns (BHhelmet_Response){} +} + +message JinYu_Request { + int64 zzid = 1; //业务唯一ID + string imgsbase64 = 2 ; //图像Base64字符串 + string imgUrl = 3 ; //图像oss链接Url +} + +message BHhelmet_Response { + int64 zzid = 1; //业务唯一ID + repeated HelmetInfo helmetinfoList = 2; //0:带安全帽 1:未带安全帽 +} + +message Point { + double x = 1; //x坐标 + double y = 2; //y坐标 +} + +message Rectangle { + Point uleft = 1; //左上 + Point lright = 2; //右下 +} + +message HelmetInfo { + int64 head_helmet = 1; //戴头盔 + double conf = 2; //置信度 + Rectangle coord = 3; //矩形坐标 +} \ No newline at end of file diff --git a/apps/ai/helmet/BeiHangGrpc_pb2.py b/apps/ai/helmet/BeiHangGrpc_pb2.py new file mode 100644 index 00000000..e662c3f6 --- /dev/null +++ b/apps/ai/helmet/BeiHangGrpc_pb2.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: BeiHangGrpc.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x42\x65iHangGrpc.proto\"A\n\rJinYu_Request\x12\x0c\n\x04zzid\x18\x01 \x01(\x03\x12\x12\n\nimgsbase64\x18\x02 \x01(\t\x12\x0e\n\x06imgUrl\x18\x03 \x01(\t\"F\n\x11\x42Hhelmet_Response\x12\x0c\n\x04zzid\x18\x01 \x01(\x03\x12#\n\x0ehelmetinfoList\x18\x02 \x03(\x0b\x32\x0b.HelmetInfo\"\x1d\n\x05Point\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\":\n\tRectangle\x12\x15\n\x05uleft\x18\x01 \x01(\x0b\x32\x06.Point\x12\x16\n\x06lright\x18\x02 \x01(\x0b\x32\x06.Point\"J\n\nHelmetInfo\x12\x13\n\x0bhead_helmet\x18\x01 \x01(\x03\x12\x0c\n\x04\x63onf\x18\x02 \x01(\x01\x12\x19\n\x05\x63oord\x18\x03 \x01(\x0b\x32\n.Rectangle2E\n\nHelmet_Reg\x12\x37\n\x0fsendHelmet_Info\x12\x0e.JinYu_Request\x1a\x12.BHhelmet_Response\"\x00\x42,\n\x17\x63om.lyzh.aiservice.grpcB\x0c\x42\x65iHangProtoP\x01\x88\x01\x01\x62\x06proto3') + + +_JINYU_REQUEST = DESCRIPTOR.message_types_by_name['JinYu_Request'] +_BHHELMET_RESPONSE = DESCRIPTOR.message_types_by_name['BHhelmet_Response'] +_POINT = DESCRIPTOR.message_types_by_name['Point'] +_RECTANGLE = DESCRIPTOR.message_types_by_name['Rectangle'] +_HELMETINFO = DESCRIPTOR.message_types_by_name['HelmetInfo'] +JinYu_Request = _reflection.GeneratedProtocolMessageType('JinYu_Request', (_message.Message,), { + 'DESCRIPTOR': _JINYU_REQUEST, + '__module__': 'BeiHangGrpc_pb2' + # @@protoc_insertion_point(class_scope:JinYu_Request) +}) +_sym_db.RegisterMessage(JinYu_Request) + +BHhelmet_Response = _reflection.GeneratedProtocolMessageType('BHhelmet_Response', (_message.Message,), { + 'DESCRIPTOR': _BHHELMET_RESPONSE, + '__module__': 'BeiHangGrpc_pb2' + # @@protoc_insertion_point(class_scope:BHhelmet_Response) +}) +_sym_db.RegisterMessage(BHhelmet_Response) + +Point = _reflection.GeneratedProtocolMessageType('Point', (_message.Message,), { + 'DESCRIPTOR': _POINT, + '__module__': 'BeiHangGrpc_pb2' + # @@protoc_insertion_point(class_scope:Point) +}) +_sym_db.RegisterMessage(Point) + +Rectangle = _reflection.GeneratedProtocolMessageType('Rectangle', (_message.Message,), { + 'DESCRIPTOR': _RECTANGLE, + '__module__': 'BeiHangGrpc_pb2' + # @@protoc_insertion_point(class_scope:Rectangle) +}) +_sym_db.RegisterMessage(Rectangle) + +HelmetInfo = _reflection.GeneratedProtocolMessageType('HelmetInfo', (_message.Message,), { + 'DESCRIPTOR': _HELMETINFO, + '__module__': 'BeiHangGrpc_pb2' + # @@protoc_insertion_point(class_scope:HelmetInfo) +}) +_sym_db.RegisterMessage(HelmetInfo) + +_HELMET_REG = DESCRIPTOR.services_by_name['Helmet_Reg'] +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\027com.lyzh.aiservice.grpcB\014BeiHangProtoP\001\210\001\001' + _JINYU_REQUEST._serialized_start = 21 + _JINYU_REQUEST._serialized_end = 86 + _BHHELMET_RESPONSE._serialized_start = 88 + _BHHELMET_RESPONSE._serialized_end = 158 + _POINT._serialized_start = 160 + _POINT._serialized_end = 189 + _RECTANGLE._serialized_start = 191 + _RECTANGLE._serialized_end = 249 + _HELMETINFO._serialized_start = 251 + _HELMETINFO._serialized_end = 325 + _HELMET_REG._serialized_start = 327 + _HELMET_REG._serialized_end = 396 +# @@protoc_insertion_point(module_scope) diff --git a/apps/ai/helmet/BeiHangGrpc_pb2_grpc.py b/apps/ai/helmet/BeiHangGrpc_pb2_grpc.py new file mode 100644 index 00000000..a9e672e4 --- /dev/null +++ b/apps/ai/helmet/BeiHangGrpc_pb2_grpc.py @@ -0,0 +1,67 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import BeiHangGrpc_pb2 as BeiHangGrpc__pb2 + + +class Helmet_RegStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.sendHelmet_Info = channel.unary_unary( + '/Helmet_Reg/sendHelmet_Info', + request_serializer=BeiHangGrpc__pb2.JinYu_Request.SerializeToString, + response_deserializer=BeiHangGrpc__pb2.BHhelmet_Response.FromString, + ) + + +class Helmet_RegServicer(object): + """Missing associated documentation comment in .proto file.""" + + def sendHelmet_Info(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_Helmet_RegServicer_to_server(servicer, server): + rpc_method_handlers = { + 'sendHelmet_Info': grpc.unary_unary_rpc_method_handler( + servicer.sendHelmet_Info, + request_deserializer=BeiHangGrpc__pb2.JinYu_Request.FromString, + response_serializer=BeiHangGrpc__pb2.BHhelmet_Response.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'Helmet_Reg', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + # This class is part of an EXPERIMENTAL API. + + +class Helmet_Reg(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def sendHelmet_Info(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/Helmet_Reg/sendHelmet_Info', + BeiHangGrpc__pb2.JinYu_Request.SerializeToString, + BeiHangGrpc__pb2.BHhelmet_Response.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/apps/ai/helmet/algo.py b/apps/ai/helmet/algo.py new file mode 100644 index 00000000..9ba6b769 --- /dev/null +++ b/apps/ai/helmet/algo.py @@ -0,0 +1,29 @@ +import grpc +import BeiHangGrpc_pb2_grpc, BeiHangGrpc_pb2 + + + +def helmet_test(ip, pic_url): + channel = grpc.insecure_channel(ip) # 监听频道 + stub = BeiHangGrpc_pb2_grpc.Helmet_RegStub(channel) # 客户端使用Stub类发送请求,参数为频道,为了绑定链接 + image_id = 3 + + # 本地图像输入 + # image_path = "./onnx/image/single3.jpg" + # with open(image_path, 'rb') as f: + # image_base64 = str(base64.b64encode(f.read()), encoding='utf-8') + # request = BeiHangGrpc_pb2.JinYu_Request(zzid=image_id,imgsbase64=image_base64) + + request = BeiHangGrpc_pb2.JinYu_Request(zzid=image_id, imgUrl=pic_url) + try: + response = stub.sendHelmet_Info(request) + print("network success! \n") + print(response.helmetinfoList[0].head_helmet) + except Exception: + print("network error! \n") + + +if __name__ == "__main__": + helmet_test( + ip='122.224.229.24:2000', + pic_url='http://1.203.161.103:2800/media/2022/07/06/dsf_bf6446cc-f9cd-11ec-bd2d-e4246c7d1635_34597240_34614457.jpg') diff --git a/apps/ai/main.py b/apps/ai/main.py new file mode 100644 index 00000000..1a650bc6 --- /dev/null +++ b/apps/ai/main.py @@ -0,0 +1,2 @@ +class AiService: + pass diff --git a/apps/system/views.py b/apps/system/views.py index 2fd61748..71b1402b 100755 --- a/apps/system/views.py +++ b/apps/system/views.py @@ -473,15 +473,15 @@ class FileViewSet(CustomCreateModelMixin, RetrieveModelMixin, ListModelMixin, Cu name = file_obj._name size = file_obj.size mime = file_obj.content_type - file_type = '其它' + file_type = File.FILE_TYPE_OTHER if 'image' in mime: - file_type = '图片' + file_type = File.FILE_TYPE_PIC elif 'video' in mime: - file_type = '视频' + file_type = File.FILE_TYPE_VIDEO elif 'audio' in mime: - file_type = '音频' + file_type = File.FILE_TYPE_AUDIO elif 'application' or 'text' in mime: - file_type = '文档' + file_type = File.FILE_TYPE_DOC instance = serializer.save( create_by=self.request.user, name=name, size=size, type=file_type, mime=mime) instance.path = settings.MEDIA_URL + instance.file.name diff --git a/apps/third/models.py b/apps/third/models.py index b457eef5..1b8a7d87 100755 --- a/apps/third/models.py +++ b/apps/third/models.py @@ -31,7 +31,7 @@ class TDevice(BaseModel): (DEVICE_PANEL, '面板机') ) type = models.PositiveSmallIntegerField('设备类型', choices=DEVICE_CHOICE) - code = models.CharField('设备唯一标识', max_length=50, db_index=True) + code = models.CharField('设备唯一标识', max_length=50, db_index=True, unique=True) name = models.CharField('名称', max_length=50, null=True, blank=True) location = models.JSONField('位置信息', default=dict, null=False, blank=True) @@ -40,14 +40,27 @@ class TDevice(BaseModel): areas = models.ManyToManyField(Area, verbose_name='覆盖区', related_name='tareas') obj_cate = models.CharField('绑定对象', max_length=20, help_text='people/...', null=True, blank=True) - employee = models.ForeignKey(Employee, verbose_name='当前绑定人员', on_delete=models.CASCADE, null=True, blank=True) + employee = models.ForeignKey(Employee, verbose_name='当前绑定人员', on_delete=models.CASCADE, + null=True, blank=True, related_name='td_employee') is_clock = models.BooleanField('是否打卡设备', default=False) third_info = models.JSONField('三方信息', default=dict, null=False, blank=True) -class BltRecord(BaseModel): - pass +class BltBind(BaseModel): + """标签绑定/解绑记录 + """ + BLT_BIND = 10 + BLT_UNBIND = 20 + BIND_TYPE_CHOICES = ( + (10, '绑定'), + (20, '解绑') + ) + type = models.PositiveSmallIntegerField('绑定类型', default=10, help_text='10(绑定)/20(解绑)', + choices=BIND_TYPE_CHOICES) + obj_cate = models.CharField('绑定对象', max_length=20, help_text='people/...', null=True, blank=True) + blt = models.ForeignKey(TDevice, verbose_name='关联标签', on_delete=models.CASCADE) + employee = models.ForeignKey(Employee, verbose_name='关联人员', on_delete=models.CASCADE, null=True, blank=True) class Tlog(BaseModel): diff --git a/apps/third/serializers.py b/apps/third/serializers.py index 85932dd2..6d5b9274 100755 --- a/apps/third/serializers.py +++ b/apps/third/serializers.py @@ -2,8 +2,9 @@ from rest_framework import serializers from apps.am.models import Area from apps.hrm.serializers import EmployeeBaseSerializer, EmployeeSimpleSerializer -from apps.third.models import TDevice, Tlog +from apps.third.models import BltBind, TDevice, Tlog from apps.utils.serializers import CustomModelSerializer +from django.db import transaction class PicSerializer(serializers.Serializer): @@ -11,6 +12,7 @@ class PicSerializer(serializers.Serializer): class TDeviceSerializer(CustomModelSerializer): + employee_ = EmployeeSimpleSerializer(source='employee', read_only=True) area_name = serializers.CharField(source='area.name', read_only=True) class Meta: @@ -69,10 +71,22 @@ class BltSerializer(serializers.ModelSerializer): class Meta: model = TDevice - fields = ['code', 'obj_cate', 'employee', 'employee_'] + fields = ['code', 'obj_cate', 'employee', 'employee_', 'id'] class BltQuerySerializer(serializers.ModelSerializer): class Meta: model = TDevice fields = ['area'] + + +class BltBindCreateSerializer(serializers.ModelSerializer): + blt = serializers.PrimaryKeyRelatedField(queryset=TDevice.objects.filter(type=TDevice.DEVICE_BLT)) + + class Meta: + model = BltBind + fields = ['type', 'blt', 'employee'] + + +class BltCreatesSerializer(serializers.Serializer): + macs = serializers.ListField(child=serializers.CharField(), label='标签mac列表') diff --git a/apps/third/speaker.py b/apps/third/speaker.py index dc3255a7..dabd1ca6 100644 --- a/apps/third/speaker.py +++ b/apps/third/speaker.py @@ -1,13 +1,16 @@ import time from threading import Thread +import uuid import requests from rest_framework.exceptions import APIException, ParseError from django.conf import settings from apps.third.errors import SP_REQUEST_ERROR +from apps.third.models import Tlog from apps.utils.tools import print_roundtrip from apps.third.tapis import spapis +from django.utils.timezone import now requests.packages.urllib3.disable_warnings() @@ -27,6 +30,7 @@ class SpClient: self.isGetingToken = False self.isRuning = True self.t = None # 线程 + self.log = {} self.setup() def _get_token_loop(self): @@ -72,47 +76,35 @@ class SpClient: file_path_rela=None, raise_exception=True): if not settings.SP_ENABLED: raise ParseError('音响对接未启用') - if self.isGetingToken: - req_num = 0 - while True: - time.sleep(0.5) - if not self.isGetingToken: - self.request(url, method, params, json, timeout, file_path_rela, raise_exception) - req_num = req_num + 1 - if req_num > 4: - break - else: - files = None - if file_path_rela: # 相对路径 - files = {'file': open(settings.BASE_DIR + file_path_rela, 'rb')} - try: - if params: - url = url.format(**params) - r = getattr(requests, method)('{}{}'.format(settings.SP_BASE_URL, url), - headers=self.headers, params=params, json=json, - timeout=timeout, files=files, verify=False) - except Exception: - if raise_exception: - raise APIException(**SP_REQUEST_ERROR) - return 'error', SP_REQUEST_ERROR - # if settings.DEBUG: - # print_roundtrip(r) - if r.status_code == 200: - ret = r.json() - if 'code' in ret and ret['code'] not in ['0', '100', '00000', '1000', 0, 100, 1000]: - detail = '音响错误:{}'.format(str(ret.get('msg', ''))) - err_detail = dict(detail=detail, code='sp_'+str(ret['code'])) - if raise_exception: - raise ParseError(**err_detail) - return 'fail', dict(detail=detail, code='sp_'+str(ret['code'])) - return 'success', ret - else: - ret = r.json() - detail = '音响错误:{}-{}'.format(str(ret.get('type', '')), str(ret.get('message', ''))) + self.log = {"requested_at": now(), "id": uuid.uuid4(), "path": url, "method": method, + "params": params, "body": json, "target": "dahua", "result": 10} + files = None + if file_path_rela: # 相对路径 + files = {'file': open(settings.BASE_DIR + file_path_rela, 'rb')} + try: + if params: + url = url.format(**params) + r = getattr(requests, method)('{}{}'.format(settings.SP_BASE_URL, url), + headers=self.headers, params=params, json=json, + timeout=timeout, files=files, verify=False) + except Exception: + if raise_exception: + raise APIException(**SP_REQUEST_ERROR) + return 'error', SP_REQUEST_ERROR + # if settings.DEBUG: + # print_roundtrip(r) + if r.status_code == 200: + ret = r.json() + if 'code' in ret and ret['code'] not in ['0', '100', '00000', '1000', 0, 100, 1000]: + detail = '音响错误:{}'.format(str(ret.get('msg', ''))) err_detail = dict(detail=detail, code='sp_'+str(ret['code'])) + self.handle_log(result='fail', response=ret) if raise_exception: raise ParseError(**err_detail) return 'fail', dict(detail=detail, code='sp_'+str(ret['code'])) + self.handle_log(result='success', response=ret) + return 'success', ret + self.handle_log(result='error', response=None) if raise_exception: raise APIException(**SP_REQUEST_ERROR) return 'error', SP_REQUEST_ERROR @@ -146,3 +138,20 @@ class SpClient: } self.request(**spapis['send_to_device'], json=json) return + + def _get_response_ms(self): + """ + Get the duration of the request response cycle is milliseconds. + In case of negative duration 0 is returned. + """ + response_timedelta = now() - self.log["requested_at"] + response_ms = int(response_timedelta.total_seconds() * 1000) + return max(response_ms, 0) + + def handle_log(self, result, response=None): + self.log.update({ + "result": result, + "response": response, + "response_ms": self._get_response_ms() + }) + Tlog(**self.log).save() diff --git a/apps/third/tapis.py b/apps/third/tapis.py index 7b86dd1a..7dccb43e 100755 --- a/apps/third/tapis.py +++ b/apps/third/tapis.py @@ -119,6 +119,14 @@ xxapis = { "rail_ibeacon_list": { "url": "/WebLocate/locateByRail", "method": "post" + }, + "blt_adds": { + "url": "/api/v4/device/blts/add", + "method": "post" + }, + "blt_info": { + "url": "/api/devicesV3/bltInfoByMac", + "method": "post" } } diff --git a/apps/third/views_d.py b/apps/third/views_d.py index e4075bd5..0662357d 100644 --- a/apps/third/views_d.py +++ b/apps/third/views_d.py @@ -1,5 +1,5 @@ -from apps.third.models import TDevice, Tlog -from apps.third.serializers import BindAreaSerializer, BltQuerySerializer, BltSerializer, LabelLocationSerializer, TDeviceSerializer, TlogSerializer +from apps.third.models import BltBind, TDevice, Tlog +from apps.third.serializers import BindAreaSerializer, BltBindCreateSerializer, BltQuerySerializer, BltSerializer, LabelLocationSerializer, TDeviceSerializer, TlogSerializer from apps.utils.viewsets import CustomGenericViewSet from rest_framework.mixins import ListModelMixin, CreateModelMixin from apps.third.clients import xxClient, dhClient, spClient @@ -10,6 +10,8 @@ from rest_framework.decorators import action from apps.am.models import Area from rest_framework.views import APIView from rest_framework.exceptions import ParseError +from django.db import transaction +from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin class BltViewSet(CustomGenericViewSet): @@ -36,9 +38,9 @@ class BltViewSet(CustomGenericViewSet): @action(methods=['get'], detail=False, perms_map={'get': '*'}) def count_now(self, request): - """统计在厂标签数 + """统计在厂且绑定标签数 - 统计在厂标签数 + 统计在厂且绑定标签数 """ json = { "pageNum": 1, @@ -108,14 +110,91 @@ class BltViewSet(CustomGenericViewSet): return Response(blt_list) -class TDeviceViewSet(ListModelMixin, CustomGenericViewSet): +class TDeviceViewSet(ListModelMixin, DestroyModelMixin, CustomGenericViewSet): """ 三方设备接口 """ queryset = TDevice.objects.all() serializer_class = TDeviceSerializer - ordering = ['code', '-create_time'] - filterset_fields = ['type'] + ordering = ['-create_time'] + filterset_fields = ['type', 'area', 'employee', 'obj_cate'] + select_related_fields = ['employee', 'area', 'employee__post', 'employee__belong_dept'] + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + else: + serializer = self.get_serializer(queryset, many=True) + data = serializer.data + typex = self.request.query_params.get('type', None) + if typex == str(TDevice.DEVICE_BLT): # 如果是定位标签加载三方信息 + macs = [] + for i in data: + macs.append(i['code']) + json = { + "macList": macs + } + _, res = xxClient.request(**xxapis['blt_info'], json=json) + macs_dict = {} + for i in res['success']: + macs_dict[i['mac']] = i + for i in res['error']: + macs_dict[i['mac']] = i + for i in data: + i['third_info'] = macs_dict.get(i['code'], {}) + if page is not None: + return self.get_paginated_response(data) + else: + return Response(data) + + @action(methods=['post'], detail=False, perms_map={'post': '*'}, + serializer_class=Serializer) + @transaction.atomic + def blt_sync(self, request): + """同步标签mac至平台 + + 同步标签mac至平台 + """ + json = { + "pageNum": 1, + "numPerPage": 1000 + } + _, res = xxClient.request(**xxapis['blt_list'], json=json) + blt_list = res['recordList'] + for i in blt_list: + TDevice.objects.get_or_create(code=i['mac'], default={"code": i['mac'], "type": TDevice.DEVICE_BLT}) + return Response() + + @action(methods=['post'], detail=False, perms_map={'post': '*'}, + serializer_class=BltBindCreateSerializer) + @transaction.atomic + def blt_bind(self, request): + """绑定/解绑定位卡 + + 绑定/解绑定位卡 + """ + serializer = BltBindCreateSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + blt = vdata['blt'] + if vdata['type'] == BltBind.BLT_BIND and vdata['employee']: + if blt.employee: + raise ParseError('该定位卡已绑定人员,请先解绑') + else: + blt.employee = vdata['employee'] + blt.obj_cate = 'people' + blt.save() + vdata['obj_cate'] = 'people' + BltBind.objects.create(**vdata) + elif vdata['type'] == BltBind.BLT_UNBIND: + if blt.employee: + blt.employee = None + blt.save() + vdata['obj_cate'] = 'people' + BltBind.objects.create(**vdata) + return Response() @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=Serializer) @@ -128,7 +207,7 @@ class TDeviceViewSet(ListModelMixin, CustomGenericViewSet): macs = [] for i in res['recordList']: macs.append(i['mac']) - qs = self.queryset.filter(code__in=macs).exclude(employee=None) + qs = self.queryset.filter(code__in=macs) qs_data = BltSerializer(instance=qs, many=True).data qs_dict = {} for i in qs_data: @@ -158,6 +237,7 @@ class TDeviceViewSet(ListModelMixin, CustomGenericViewSet): for i in tds_info: tds_dict[i['code']] = i for i in res['pageData']: + i['my_info'] = {} if i['channelCode'] in tds_dict: i['my_info'] = tds_dict[i['channelCode']] return Response(res) @@ -179,6 +259,7 @@ class TDeviceViewSet(ListModelMixin, CustomGenericViewSet): for i in tds_info: tds_dict[i['code']] = i for i in res['rows']: + i['my_info'] = {} if i['sn'] in tds_dict: i['my_info'] = tds_dict[i['sn']] return Response(res) @@ -202,6 +283,7 @@ class TDeviceViewSet(ListModelMixin, CustomGenericViewSet): for i in tds_info: tds_dict[i['code']] = i for i in res['pageData']: + i['my_info'] = {} if i['channelCode'] in tds_dict: i['my_info'] = tds_dict[i['channelCode']] return Response(res) @@ -246,9 +328,9 @@ class TDeviceViewSet(ListModelMixin, CustomGenericViewSet): serializer_class=BindAreaSerializer) def bind_area(self, request): """ - 绑定所在区域 + 批量绑定所在区域 - 绑定所在区域 + 批量绑定所在区域 """ serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/apps/utils/speech.py b/apps/utils/speech.py index f0a2c1cf..d48b3a3f 100644 --- a/apps/utils/speech.py +++ b/apps/utils/speech.py @@ -1,6 +1,8 @@ from aip import AipSpeech from django.conf import settings import uuid +import os +from django.utils import timezone def generate_voice(msg: str, per: int = 0): @@ -19,9 +21,12 @@ def generate_voice(msg: str, per: int = 0): result = client.synthesis(msg, 'zh', 1, {'vol': 5, 'spd': 5, 'per': per}) # 识别正确返回语音二进制 错误则返回dict 参照下面错误码 if not isinstance(result, dict): - # name = '{}.mp3'.format(uuid.uuid4()) - path = '/media/voice/{}.mp3'.format(uuid.uuid4()) - with open(settings.BASE_DIR + path, 'wb') as f: + file_name = '{}.mp3'.format(uuid.uuid4()) + path = '/media/' + timezone.now().strftime('%Y/%m/%d/') + full_path = settings.BASE_DIR + path + if not os.path.exists(full_path): + os.makedirs(full_path) + with open(full_path + file_name, 'wb') as f: f.write(result) - return True, path, None + return True, path + file_name, None return False, None, result diff --git a/apps/wf/models.py b/apps/wf/models.py index 4cc7e754..4c2ac1e3 100755 --- a/apps/wf/models.py +++ b/apps/wf/models.py @@ -22,6 +22,13 @@ class Workflow(CommonAModel): content_template = models.CharField( '内容模板', max_length=1000, default='标题:{title}, 创建时间:{create_time}', null=True, blank=True, help_text='工单字段的值可以作为参数写到模板中,格式如:标题:{title}, 创建时间:{create_time}') + class Meta: + verbose_name = '工作流' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + class State(CommonAModel): """ @@ -99,6 +106,13 @@ class State(CommonAModel): participant_cc = models.JSONField('抄送给', default=list, blank=True, help_text='抄送给(userid列表)') on_reach_func = models.CharField('到达时调用方法', max_length=100, null=True, blank=True) + class Meta: + verbose_name = '工作流节点' + verbose_name_plural = verbose_name + + def __str__(self): + return '{}-{}-{}'.format(self.id, self.workflow.name, self.name) + class Transition(CommonAModel): """ @@ -154,6 +168,13 @@ class Transition(CommonAModel): '是否校验必填项', default=True, help_text='默认在用户点击操作的时候需要校验工单表单的必填项,如果设置为否则不检查。用于如"退回"属性的操作,不需要填写表单内容') on_submit_func = models.CharField('提交操作调用方法', max_length=100, null=True, blank=True) + class Meta: + verbose_name = '工作流流转' + verbose_name_plural = verbose_name + + def __str__(self): + return '{}-{}-{}'.format(self.id, self.workflow.name, self.name) + class CustomField(CommonAModel): """自定义字段, 设定某个工作流有哪些自定义字段""" diff --git a/server/settings.py b/server/settings.py index 49d399a2..50561221 100755 --- a/server/settings.py +++ b/server/settings.py @@ -389,3 +389,7 @@ XNIU_SMS_SECRET = conf.XNIU_SMS_SECRET BD_SP_ID = conf.BD_SP_ID BD_SP_KEY = conf.BD_SP_KEY BD_SP_SECRET = conf.BD_SP_SECRET + + +# 算法层 +AI_IP = conf.AI_IP