feat: 查看门禁权限并支持删除
This commit is contained in:
parent
b4cc887f79
commit
b479db9f89
|
|
@ -17,14 +17,25 @@ from rest_framework.exceptions import ParseError
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
class EmployeeShortSerializer(CustomModelSerializer):
|
||||
belong_dept_name = serializers.CharField(
|
||||
source='belong_dept.name', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = ['id', 'name', 'belong_dept_name', 'user', 'third_info']
|
||||
|
||||
|
||||
class EmployeeSimpleSerializer(CustomModelSerializer):
|
||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||
belong_dept_name = serializers.CharField(
|
||||
source='belong_dept.name', read_only=True)
|
||||
post_name = serializers.CharField(source='post.name', read_only=True)
|
||||
location = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = ['id', 'type', 'name', 'belong_dept', 'belong_dept_name', 'post', 'post_name', 'photo', 'third_info', 'number', 'id_number', 'location']
|
||||
fields = ['id', 'type', 'name', 'belong_dept', 'belong_dept_name', 'post',
|
||||
'post_name', 'photo', 'third_info', 'number', 'id_number', 'location']
|
||||
|
||||
def get_location(self, obj):
|
||||
key_str = 'ep_{}'.format(obj.id)
|
||||
|
|
@ -40,7 +51,8 @@ class EmployeeSimpleSerializer(CustomModelSerializer):
|
|||
|
||||
|
||||
class EmployeeCreateUpdateSerializer(CustomModelSerializer):
|
||||
id_number = serializers.CharField(label="身份证号", validators=[check_id_number_e])
|
||||
id_number = serializers.CharField(
|
||||
label="身份证号", validators=[check_id_number_e])
|
||||
|
||||
class Meta:
|
||||
model = Employee
|
||||
|
|
@ -72,7 +84,8 @@ class EmployeeCreateUpdateSerializer(CustomModelSerializer):
|
|||
instance = super().update(instance, validated_data)
|
||||
if old_photo != instance.photo: # 如果照片有变动,需要更新人脸库
|
||||
# 使用的是face_recongition
|
||||
face_data, msg = HrmService.get_facedata_from_img_x(settings.BASE_DIR + instance.photo)
|
||||
face_data, msg = HrmService.get_facedata_from_img_x(
|
||||
settings.BASE_DIR + instance.photo)
|
||||
in_face_data = instance.face_data
|
||||
if face_data:
|
||||
if isinstance(in_face_data, dict):
|
||||
|
|
@ -104,17 +117,19 @@ class EmployeeCreateUpdateSerializer(CustomModelSerializer):
|
|||
if instance.type in ['employee', 'remployee']:
|
||||
start_time = None
|
||||
end_time = None
|
||||
if instance.third_info.get('dh_face_card', None) is None and instance.type == 'employee': # 如果是正式员工,给长时间期限
|
||||
# 如果是正式员工,给长时间期限
|
||||
if instance.third_info.get('dh_face_card', None) is None and instance.type == 'employee':
|
||||
start_time = now
|
||||
end_time = now + timedelta(days=7300)
|
||||
if instance.job_state in [20, 30] and old_job_state == 10: # 离职或退休
|
||||
start_time = now
|
||||
end_time = now + timedelta(hours=8)
|
||||
elif instance.job_state == 10 and old_job_state in [20, 30] and instance.type == 'employee': # 正式员工重新在职
|
||||
# 正式员工重新在职
|
||||
elif instance.job_state == 10 and old_job_state in [20, 30] and instance.type == 'employee':
|
||||
start_time = now
|
||||
end_time = now + timedelta(days=7300)
|
||||
HrmService.sync_dahua_employee(ep=instance, old_photo=old_photo,
|
||||
start_time=start_time, end_time=end_time)
|
||||
start_time=start_time, end_time=end_time)
|
||||
return instance
|
||||
|
||||
|
||||
|
|
@ -124,7 +139,8 @@ class EmployeeImproveSerializer(CustomModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = ['photo', 'id_number', 'email', 'gender', 'signature', 'photo_f', 'signature_f', 'phone']
|
||||
fields = ['photo', 'id_number', 'email', 'gender',
|
||||
'signature', 'photo_f', 'signature_f', 'phone']
|
||||
extra_kwargs = {
|
||||
'number': {'required': True},
|
||||
'photo': {'required': True},
|
||||
|
|
@ -147,7 +163,8 @@ class ChannelAuthoritySerializer(serializers.Serializer):
|
|||
class EmployeeSerializer(CustomModelSerializer):
|
||||
belong_dept_ = DeptSimpleSerializer(source='belong_dept', read_only=True)
|
||||
user_ = UserSimpleSerializer(source='user', read_only=True)
|
||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||
belong_dept_name = serializers.CharField(
|
||||
source='belong_dept.name', read_only=True)
|
||||
post_name = serializers.CharField(source='post.name', read_only=True)
|
||||
blt_ = serializers.SerializerMethodField()
|
||||
photo_f = MyFilePathField(source='photo', read_only=True)
|
||||
|
|
@ -161,7 +178,7 @@ class EmployeeSerializer(CustomModelSerializer):
|
|||
def get_blt_(self, obj):
|
||||
if hasattr(obj, 'blt'):
|
||||
from apps.third.serializers import TDeviceSimpleSerializer
|
||||
return TDeviceSimpleSerializer(instance=obj.blt).data
|
||||
return TDeviceSimpleSerializer(instance=obj.blt).data
|
||||
# if hasattr(obj, 'tdevice'):
|
||||
# from apps.third.serializers import TDeviceSimpleSerializer
|
||||
# return TDeviceSimpleSerializer(instance=obj.tdevice).data
|
||||
|
|
@ -197,6 +214,7 @@ class ClockRecordListSerializer(serializers.ModelSerializer):
|
|||
model = ClockRecord
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class ClockRecordSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ClockRecord
|
||||
|
|
@ -218,7 +236,8 @@ class CertificateCreateUpdateSerializer(CustomModelSerializer):
|
|||
|
||||
|
||||
class CertificateSerializer(CustomModelSerializer):
|
||||
employee_name = serializers.CharField(source='employee.name', read_only=True)
|
||||
employee_name = serializers.CharField(
|
||||
source='employee.name', read_only=True)
|
||||
file_f = MyFilePathField(source='file', read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
|
@ -228,4 +247,4 @@ class CertificateSerializer(CustomModelSerializer):
|
|||
|
||||
class CorrectSerializer(serializers.Serializer):
|
||||
start_time = serializers.DateTimeField()
|
||||
end_time = serializers.DateTimeField()
|
||||
end_time = serializers.DateTimeField()
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class HrmService:
|
|||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
if not settings.DAHUA_ENABLED: # 如果大华没启用, 直接返回
|
||||
if not settings.DAHUA_ENABLED: # 如果大华没启用, 直接返回
|
||||
return
|
||||
dh_id = ep.third_info.get('dh_id', None)
|
||||
dh_photo = ep.third_info.get('dh_photo', None)
|
||||
|
|
@ -68,7 +68,8 @@ class HrmService:
|
|||
}]
|
||||
}
|
||||
if ep.photo != old_photo and ep.photo:
|
||||
_, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=ep.photo)
|
||||
_, res = dhClient.request(
|
||||
**dhapis['person_img_upload'], file_path_rela=ep.photo)
|
||||
dh_photo = res["fileUrl"]
|
||||
json_data.update(
|
||||
{
|
||||
|
|
@ -98,7 +99,8 @@ class HrmService:
|
|||
"email": ep.email,
|
||||
"sex": 1 if ep.gender == '男' else 2
|
||||
}
|
||||
_, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=ep.photo)
|
||||
_, res = dhClient.request(
|
||||
**dhapis['person_img_upload'], file_path_rela=ep.photo)
|
||||
dh_photo = res["fileUrl"]
|
||||
json_data.update(
|
||||
{
|
||||
|
|
@ -129,16 +131,20 @@ class HrmService:
|
|||
if start_time is None: # 如果时间段未提供,跳过更新操作
|
||||
pass
|
||||
else:
|
||||
startDate = timezone.localtime(start_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
endDate = timezone.localtime(end_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
startDate = timezone.localtime(
|
||||
start_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
endDate = timezone.localtime(
|
||||
end_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
json_data = {
|
||||
"cardNumber": cardNumber,
|
||||
"startDate": startDate,
|
||||
"endDate": endDate,
|
||||
"departmentId": departmentId,
|
||||
}
|
||||
_, res = dhClient.request(**dhapis['card_update'], json=json_data)
|
||||
cls.save(ep, data={'dh_face_card_start': startDate, 'dh_face_card_end': endDate})
|
||||
_, res = dhClient.request(
|
||||
**dhapis['card_update'], json=json_data)
|
||||
cls.save(
|
||||
ep, data={'dh_face_card_start': startDate, 'dh_face_card_end': endDate})
|
||||
return cardNumber
|
||||
else:
|
||||
_, res = dhClient.request(**dhapis['card_gen_id'])
|
||||
|
|
@ -147,10 +153,13 @@ class HrmService:
|
|||
if start_time is None: # 如果未规定时间范围, 默认1小时
|
||||
now = datetime.now()
|
||||
startDate = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
endDate = (now+timedelta(minutes=60)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
endDate = (now+timedelta(minutes=60)
|
||||
).strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
startDate = timezone.localtime(start_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
endDate = timezone.localtime(end_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
startDate = timezone.localtime(
|
||||
start_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
endDate = timezone.localtime(
|
||||
end_time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
json_data = {
|
||||
"id": cardId,
|
||||
"cardNumber": cardNumber,
|
||||
|
|
@ -180,7 +189,9 @@ class HrmService:
|
|||
from apps.third.models import TDevice
|
||||
dh_face_card = ep.third_info.get('dh_face_card', None)
|
||||
if dh_dchannels:
|
||||
pass
|
||||
# 只分配系统里有的
|
||||
dh_dchannels = list(TDevice.objects.filter(
|
||||
code__in=dh_dchannels).values_list('code', flat=True))
|
||||
else:
|
||||
# 查找可授予的门禁通道号
|
||||
dh_dchannels = list(TDevice.objects.filter(type=TDevice.DEVICE_DCHANNEL,
|
||||
|
|
@ -201,7 +212,8 @@ class HrmService:
|
|||
while ind < 8:
|
||||
time.sleep(6)
|
||||
try:
|
||||
dhClient.request(**dhapis['card_door_authority'], json=json_data)
|
||||
dhClient.request(
|
||||
**dhapis['card_door_authority'], json=json_data)
|
||||
break
|
||||
except Exception as e:
|
||||
ind = ind + 1
|
||||
|
|
@ -220,6 +232,13 @@ class HrmService:
|
|||
# elif is_ok == 'error':
|
||||
# raise APIException(**DH_REQUEST_ERROR)
|
||||
cls.save(ep, data={'dh_dchannels': dh_dchannels})
|
||||
from apps.third.models import DoorAuth
|
||||
DoorAuth.objects.filter(employee=ep).delete()
|
||||
drs = []
|
||||
for i in dh_dchannels:
|
||||
drs.append(
|
||||
DoorAuth(employee=ep, dchannel=TDevice.objects.get(code=i)))
|
||||
DoorAuth.objects.bulk_create(drs)
|
||||
return dh_dchannels
|
||||
|
||||
@classmethod
|
||||
|
|
@ -243,10 +262,11 @@ class HrmService:
|
|||
nodeCode = data['info']['extend']['acsChannelCode']
|
||||
swip_time = data['info']['extend']['swingTime']
|
||||
e_type = data['info']['extend']['enterOrExit']
|
||||
cls.swipe_next(nodeCode, id_number, swip_time, e_type, data['info']['extend'])
|
||||
|
||||
cls.swipe_next(nodeCode, id_number, swip_time,
|
||||
e_type, data['info']['extend'])
|
||||
|
||||
@classmethod
|
||||
def swipe_offline(cls, data:dict):
|
||||
def swipe_offline(cls, data: dict):
|
||||
"""离线刷卡推送记录处理
|
||||
"""
|
||||
for i in data['infoArray']:
|
||||
|
|
@ -256,9 +276,9 @@ class HrmService:
|
|||
swip_time = i['swingTime']
|
||||
e_type = i['enterOrExit']
|
||||
cls.swipe_next(nodeCode, id_number, swip_time, e_type, i)
|
||||
|
||||
|
||||
@classmethod
|
||||
def swipe_next(cls, nodeCode: str, id_number: str, swip_time: str, e_type:int, detail:dict):
|
||||
def swipe_next(cls, nodeCode: str, id_number: str, swip_time: str, e_type: int, detail: dict):
|
||||
from apps.rpm.models import Rpj
|
||||
from apps.vm.models import Visit
|
||||
|
||||
|
|
@ -267,15 +287,17 @@ class HrmService:
|
|||
state=Visit.V_WORKING)
|
||||
Rpj.objects.filter(state=Rpj.RPJ_ENTER, remployees__employee__id_number=id_number).update(
|
||||
state=Rpj.RPJ_WORKING)
|
||||
|
||||
|
||||
# 打卡处理
|
||||
device = TDevice.objects.filter(code=nodeCode).first()
|
||||
ep = Employee.objects.filter(id_number=id_number, type__in=["employee", "remployee"]).first()
|
||||
ep = Employee.objects.filter(id_number=id_number, type__in=[
|
||||
"employee", "remployee"]).first()
|
||||
if device and device.is_clock and ep:
|
||||
tzinfo = tz.gettz('Asia/Shanghai')
|
||||
s_time_f = datetime.strptime(swip_time, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzinfo)
|
||||
s_time_f = datetime.strptime(
|
||||
swip_time, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzinfo)
|
||||
first_time = datetime(year=s_time_f.year, month=s_time_f.month, day=s_time_f.day,
|
||||
hour=0, minute=0, second=0, tzinfo=tzinfo)
|
||||
hour=0, minute=0, second=0, tzinfo=tzinfo)
|
||||
end_time = datetime(year=s_time_f.year, month=s_time_f.month, day=s_time_f.day,
|
||||
hour=23, minute=59, second=59, tzinfo=tzinfo)
|
||||
|
||||
|
|
@ -288,13 +310,13 @@ class HrmService:
|
|||
card_type = 20
|
||||
elif e_type == 3:
|
||||
time_10_x = datetime(year=s_time_f.year, month=s_time_f.month,
|
||||
day=s_time_f.day, hour=7, minute=20, second=0, tzinfo=tzinfo)
|
||||
day=s_time_f.day, hour=7, minute=20, second=0, tzinfo=tzinfo)
|
||||
time_10_y = datetime(year=s_time_f.year, month=s_time_f.month,
|
||||
day=s_time_f.day, hour=11, minute=0, second=0, tzinfo=tzinfo)
|
||||
day=s_time_f.day, hour=11, minute=0, second=0, tzinfo=tzinfo)
|
||||
time_20_x = datetime(year=s_time_f.year, month=s_time_f.month,
|
||||
day=s_time_f.day, hour=16, minute=0, second=0, tzinfo=tzinfo)
|
||||
day=s_time_f.day, hour=16, minute=0, second=0, tzinfo=tzinfo)
|
||||
time_20_y = datetime(year=s_time_f.year, month=s_time_f.month,
|
||||
day=s_time_f.day, hour=18, minute=10, second=0, tzinfo=tzinfo)
|
||||
day=s_time_f.day, hour=18, minute=10, second=0, tzinfo=tzinfo)
|
||||
if time_10_x < s_time_f < time_10_y:
|
||||
card_type = 10
|
||||
elif time_20_x < s_time_f < time_20_y:
|
||||
|
|
@ -302,9 +324,10 @@ class HrmService:
|
|||
if card_type == 30:
|
||||
note = '非打卡时间范围'
|
||||
trigger = 'panel'
|
||||
|
||||
|
||||
# 先直接创建记录
|
||||
cr = ClockRecord.objects.filter(employee=ep, create_time=s_time_f).first()
|
||||
cr = ClockRecord.objects.filter(
|
||||
employee=ep, create_time=s_time_f).first()
|
||||
if cr:
|
||||
pass
|
||||
else:
|
||||
|
|
@ -312,7 +335,7 @@ class HrmService:
|
|||
cr.employee = ep
|
||||
cr.create_time = s_time_f
|
||||
cr.type = card_type
|
||||
cr.exception_type = None
|
||||
cr.exception_type = None
|
||||
cr.trigger = trigger
|
||||
cr.detail = detail
|
||||
cr.note = note
|
||||
|
|
@ -323,34 +346,38 @@ class HrmService:
|
|||
cr_10_q = ClockRecord.objects.filter(
|
||||
type=10, employee=ep, create_time__gte=first_time, create_time__lte=end_time)
|
||||
first_obj = cr_10_q.order_by('create_time').first()
|
||||
cr_10_q.exclude(id=first_obj.id).update(type=30, exception_type=None)
|
||||
cr_10_q.exclude(id=first_obj.id).update(
|
||||
type=30, exception_type=None)
|
||||
elif card_type == 20:
|
||||
# 查找当天的出门记录
|
||||
cr_20_q = ClockRecord.objects.filter(
|
||||
type=20, employee=ep, create_time__gte=first_time, create_time__lte=end_time)
|
||||
last_obj = cr_20_q.order_by('-create_time').first()
|
||||
cr_20_q.exclude(id=last_obj.id).update(type=30, exception_type=None)
|
||||
cr_20_q.exclude(id=last_obj.id).update(
|
||||
type=30, exception_type=None)
|
||||
# 判断是否有异常
|
||||
# 找到最近的上下打卡时间不一定是当天的
|
||||
cr_e = ClockRecord.objects.filter(create_time__lt=last_obj.create_time, employee=ep, type__in=[10, 20]).order_by('-create_time').first()
|
||||
cr_e = ClockRecord.objects.filter(create_time__lt=last_obj.create_time, employee=ep, type__in=[
|
||||
10, 20]).order_by('-create_time').first()
|
||||
if cr_e and last_obj:
|
||||
time_d = last_obj.create_time - cr_e.create_time
|
||||
if cr_e.type == 10:
|
||||
if time_d < timedelta(hours=7):
|
||||
if time_d < timedelta(hours=7):
|
||||
last_obj.exception_type = ClockRecord.E_TYPE_LESS
|
||||
last_obj.save()
|
||||
elif time_d > timedelta(hours=14):
|
||||
elif time_d > timedelta(hours=14):
|
||||
last_obj.exception_type = ClockRecord.E_TYPE_MORE
|
||||
last_obj.save()
|
||||
elif time_d > timedelta(hours=10):
|
||||
elif time_d > timedelta(hours=10):
|
||||
last_obj.exception_type = ClockRecord.E_TYPE_ADD
|
||||
last_obj.save()
|
||||
elif cr_e.type == 20:
|
||||
last_obj.exception_type = ClockRecord.E_TYPE_MISS
|
||||
last_obj.save()
|
||||
last_obj.exception_type = ClockRecord.E_TYPE_MISS
|
||||
last_obj.save()
|
||||
|
||||
# 记录在岗情况
|
||||
last_obj_t = ClockRecord.objects.filter(employee=ep, type__in=[10,20]).order_by('-create_time').first()
|
||||
last_obj_t = ClockRecord.objects.filter(
|
||||
employee=ep, type__in=[10, 20]).order_by('-create_time').first()
|
||||
if last_obj_t:
|
||||
update_fields = {'last_check_time': last_obj_t.create_time}
|
||||
if last_obj_t.type == 10:
|
||||
|
|
@ -358,48 +385,48 @@ class HrmService:
|
|||
else:
|
||||
update_fields['is_atwork'] = False
|
||||
Employee.objects.filter(id=ep.id).update(**update_fields)
|
||||
|
||||
|
||||
|
||||
|
||||
# 此处可触发安全帽事件逻辑
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_facedata_from_img_x(cls, img_path):
|
||||
import face_recognition
|
||||
try:
|
||||
picture_of_me = face_recognition.load_image_file(img_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me, num_jitters=2)[0]
|
||||
my_face_encoding = face_recognition.face_encodings(
|
||||
picture_of_me, num_jitters=2)[0]
|
||||
face_data_list = my_face_encoding.tolist()
|
||||
return face_data_list, ''
|
||||
except:
|
||||
return None, '人脸数据获取失败请重新上传图片'
|
||||
|
||||
|
||||
@classmethod
|
||||
def face_compare_from_base64(cls, base64_data):
|
||||
import face_recognition
|
||||
filename = str(uuid.uuid4())
|
||||
filepath = settings.BASE_DIR +'/temp/face_' + filename +'.png'
|
||||
filepath = settings.BASE_DIR + '/temp/face_' + filename + '.png'
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(base64_data)
|
||||
try:
|
||||
unknown_picture = face_recognition.load_image_file(filepath)
|
||||
unknown_face_encoding = face_recognition.face_encodings(unknown_picture, num_jitters=2)[0]
|
||||
unknown_face_encoding = face_recognition.face_encodings(
|
||||
unknown_picture, num_jitters=2)[0]
|
||||
os.remove(filepath)
|
||||
except:
|
||||
os.remove(filepath)
|
||||
return None, '识别失败,请调整位置'
|
||||
|
||||
# 匹配人脸库
|
||||
face_datas = cache.get('face_datas_dlib', None) # 使用的是face_datas_dlib作为key的
|
||||
# 使用的是face_datas_dlib作为key的
|
||||
face_datas = cache.get('face_datas_dlib', None)
|
||||
if face_datas is None:
|
||||
from apps.hrm.tasks import update_all_facedata_cache
|
||||
update_all_facedata_cache()
|
||||
face_datas = cache.get('face_datas_dlib')
|
||||
results = face_recognition.compare_faces(face_datas['datas'],
|
||||
unknown_face_encoding, tolerance=0.45)
|
||||
face_distances = face_recognition.face_distance(face_datas['datas'], unknown_face_encoding)
|
||||
results = face_recognition.compare_faces(face_datas['datas'],
|
||||
unknown_face_encoding, tolerance=0.45)
|
||||
face_distances = face_recognition.face_distance(
|
||||
face_datas['datas'], unknown_face_encoding)
|
||||
best_match_index = np.argmin(face_distances)
|
||||
if results[best_match_index]:
|
||||
epId = face_datas['eps'][best_match_index]
|
||||
|
|
@ -408,7 +435,7 @@ class HrmService:
|
|||
# first_match_index = results.index(True)
|
||||
# # 识别成功
|
||||
# ep = Employee.objects.get(id=face_datas['eps'][first_match_index])
|
||||
# return ep, ''
|
||||
# return ep, ''
|
||||
return None, '人脸未匹配,请调整位置'
|
||||
|
||||
@classmethod
|
||||
|
|
@ -416,7 +443,7 @@ class HrmService:
|
|||
from apps.utils.face import face_find
|
||||
# from deepface import DeepFace
|
||||
img_name = str(uuid.uuid4())
|
||||
img_path = settings.BASE_DIR +'/temp/face_' + img_name +'.jpg'
|
||||
img_path = settings.BASE_DIR + '/temp/face_' + img_name + '.jpg'
|
||||
with open(img_path, 'wb') as f:
|
||||
f.write(base64_data)
|
||||
# db_path = os.path.join(settings.BASE_DIR, 'media/face')
|
||||
|
|
@ -443,7 +470,8 @@ class HrmService:
|
|||
def get_facedata_from_img(cls, img_path):
|
||||
try:
|
||||
from deepface import DeepFace
|
||||
embedding_objs = DeepFace.represent(img_path=img_path, model_name='Facenet')
|
||||
embedding_objs = DeepFace.represent(
|
||||
img_path=img_path, model_name='Facenet')
|
||||
return embedding_objs[0]["embedding"], ''
|
||||
except Exception as e:
|
||||
return None, '人脸数据获取失败请重新上传图片'
|
||||
return None, '人脸数据获取失败请重新上传图片'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 3.2.12 on 2023-10-09 02:54
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0015_clockrecord_note'),
|
||||
('third', '0008_alter_tdevice_employee'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DoorAuth',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('dchannel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='third.tdevice', verbose_name='关联门通道')),
|
||||
('employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='关联用户')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -31,7 +31,8 @@ class TDevice(BaseModel):
|
|||
(DEVICE_PANEL, '面板机')
|
||||
)
|
||||
type = models.PositiveSmallIntegerField('设备类型', choices=DEVICE_CHOICE)
|
||||
code = models.CharField('设备唯一标识', max_length=50, db_index=True, unique=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)
|
||||
|
|
@ -39,13 +40,15 @@ class TDevice(BaseModel):
|
|||
verbose_name='所在区', null=True, blank=True)
|
||||
areas = models.ManyToManyField(Area, verbose_name='覆盖区',
|
||||
related_name='tareas', blank=True)
|
||||
obj_cate = models.CharField('绑定对象', max_length=20, help_text='people/...', null=True, blank=True)
|
||||
obj_cate = models.CharField(
|
||||
'绑定对象', max_length=20, help_text='people/...', null=True, blank=True)
|
||||
employee = models.OneToOneField(Employee, verbose_name='当前绑定人员', on_delete=models.SET_NULL,
|
||||
null=True, blank=True, related_name='blt')
|
||||
is_clock = models.BooleanField('是否打卡设备', default=False)
|
||||
access_list = models.JSONField('自动下发人员类型', default=list,
|
||||
null=False, blank=True, help_text='employee/remployee/visitor/driver')
|
||||
mtask_uid = models.CharField('监控任务ID', max_length=100, null=True, blank=True) # 废弃字段
|
||||
mtask_uid = models.CharField(
|
||||
'监控任务ID', max_length=100, null=True, blank=True) # 废弃字段
|
||||
# algos = models.ManyToManyField('ecm.eventcate', through='ecm.algochannel', blank=True)
|
||||
third_info = models.JSONField('三方信息', default=dict,
|
||||
null=False, blank=True)
|
||||
|
|
@ -62,9 +65,12 @@ class BltBind(BaseModel):
|
|||
)
|
||||
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)
|
||||
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):
|
||||
|
|
@ -83,3 +89,10 @@ class Tlog(BaseModel):
|
|||
errors = models.TextField(null=True, blank=True)
|
||||
params = models.JSONField(null=True, blank=True)
|
||||
body = models.JSONField(null=True, blank=True)
|
||||
|
||||
|
||||
class DoorAuth(BaseModel):
|
||||
employee = models.ForeignKey(
|
||||
Employee, verbose_name='关联用户', on_delete=models.CASCADE)
|
||||
dchannel = models.ForeignKey(
|
||||
TDevice, verbose_name='关联门通道', on_delete=models.CASCADE)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from apps.am.models import Area
|
||||
from apps.hrm.serializers import EmployeeSimpleSerializer
|
||||
from apps.third.models import BltBind, TDevice, Tlog
|
||||
from apps.hrm.serializers import EmployeeSimpleSerializer, EmployeeShortSerializer
|
||||
from apps.third.models import BltBind, TDevice, Tlog, DoorAuth
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from django.core.cache import cache
|
||||
|
||||
|
|
@ -23,6 +23,7 @@ class TDeviceSerializer(CustomModelSerializer):
|
|||
def get_mtask_(self, obj):
|
||||
return cache.get('vchannel_' + obj.code, {})
|
||||
|
||||
|
||||
class TDeviceUpdateSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = TDevice
|
||||
|
|
@ -30,7 +31,8 @@ class TDeviceUpdateSerializer(CustomModelSerializer):
|
|||
|
||||
def update(self, instance, validated_data):
|
||||
if validated_data.get('access_list', []):
|
||||
validated_data['access_list'] = ['employee', 'remployee', 'visitor']
|
||||
validated_data['access_list'] = [
|
||||
'employee', 'remployee', 'visitor']
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
|
|
@ -48,10 +50,14 @@ class RequestCommonSerializer(serializers.Serializer):
|
|||
('delete', 'delete')
|
||||
)
|
||||
url = serializers.CharField(label='请求地址', required=False)
|
||||
method = serializers.ChoiceField(label='请求方法', choices=method_choice, required=False)
|
||||
params = serializers.JSONField(label='请求参数', required=False, allow_null=True)
|
||||
json = serializers.JSONField(label='请求body(json格式)', required=False, allow_null=True)
|
||||
code = serializers.CharField(label='请求短标识', required=False, allow_null=True)
|
||||
method = serializers.ChoiceField(
|
||||
label='请求方法', choices=method_choice, required=False)
|
||||
params = serializers.JSONField(
|
||||
label='请求参数', required=False, allow_null=True)
|
||||
json = serializers.JSONField(
|
||||
label='请求body(json格式)', required=False, allow_null=True)
|
||||
code = serializers.CharField(
|
||||
label='请求短标识', required=False, allow_null=True)
|
||||
|
||||
|
||||
class BindAreaSerializer(serializers.Serializer):
|
||||
|
|
@ -90,7 +96,8 @@ class BltSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class BltQuerySerializer(serializers.ModelSerializer):
|
||||
depts = serializers.ListField(child=serializers.CharField(), label="部门ID列表", required=False)
|
||||
depts = serializers.ListField(
|
||||
child=serializers.CharField(), label="部门ID列表", required=False)
|
||||
|
||||
class Meta:
|
||||
model = TDevice
|
||||
|
|
@ -107,4 +114,14 @@ class BltBindCreateSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class BltCreatesSerializer(serializers.Serializer):
|
||||
macs = serializers.ListField(child=serializers.CharField(), label='标签mac列表')
|
||||
macs = serializers.ListField(
|
||||
child=serializers.CharField(), label='标签mac列表')
|
||||
|
||||
|
||||
class DoorAuthSerializer(CustomModelSerializer):
|
||||
employee_ = EmployeeShortSerializer(source='employee', read_only=True)
|
||||
dchannel_ = TDeviceSimpleSerializer(source='dchannel', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DoorAuth
|
||||
fields = '__all__'
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ dhapis = {
|
|||
"url": "/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/doorAuthority",
|
||||
"method": "post"
|
||||
},
|
||||
"card_door_authority_delete": {
|
||||
"url": "/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/doorAuthority/deleteSingleCardPrivilege",
|
||||
"method": "post"
|
||||
},
|
||||
"mq_subscribe": {
|
||||
"url": "/evo-apigw/evo-event/1.0.0/subscribe/mqinfo",
|
||||
"method": "post"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from email.mime import base
|
|||
from django.urls import path, include
|
||||
from rest_framework import routers
|
||||
from apps.third.views import DahuaTestView, DhCommonViewSet, SpTestView, SpeakerViewSet, XxCommonViewSet, XxTestView
|
||||
from apps.third.views_d import BltViewSet, TDeviceViewSet, TlogViewSet
|
||||
from apps.third.views_d import BltViewSet, TDeviceViewSet, TlogViewSet, DoorAuthViewSet
|
||||
|
||||
API_BASE_URL = 'api/third/'
|
||||
HTML_BASE_URL = 'third/'
|
||||
|
|
@ -14,6 +14,7 @@ router.register('speaker', SpeakerViewSet, basename='api_speaker')
|
|||
router.register('tdevice', TDeviceViewSet, basename='tdevice')
|
||||
router.register('tlog', TlogViewSet, basename='tlog')
|
||||
router.register('tdevice/blt', BltViewSet, basename='blt')
|
||||
router.register('doorauth', DoorAuthViewSet, basename='doorauth')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
path(API_BASE_URL + 'dahua/test/', DahuaTestView.as_view()),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from apps.third.filters import TDeviceFilterSet
|
||||
from apps.third.models import BltBind, TDevice, Tlog
|
||||
from apps.third.serializers import BindAreaSerializer, BltBindCreateSerializer, BltQuerySerializer, BltSerializer, LabelLocationSerializer, TDeviceSerializer, TDeviceUpdateSerializer, TlogSerializer
|
||||
from apps.third.models import BltBind, TDevice, Tlog, DoorAuth
|
||||
from apps.third.serializers import BindAreaSerializer, BltBindCreateSerializer, BltQuerySerializer, BltSerializer, LabelLocationSerializer, TDeviceSerializer, TDeviceUpdateSerializer, TlogSerializer, DoorAuthSerializer
|
||||
from apps.utils.viewsets import CustomGenericViewSet
|
||||
from rest_framework.mixins import ListModelMixin, CreateModelMixin
|
||||
from apps.third.dahua import dhClient
|
||||
|
|
@ -131,13 +131,15 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
"""
|
||||
三方设备接口
|
||||
"""
|
||||
perms_map = {'get': '*', 'put': 'tdevice.update', 'delete': 'tdevice.delete'}
|
||||
perms_map = {'get': '*', 'put': 'tdevice.update',
|
||||
'delete': 'tdevice.delete'}
|
||||
queryset = TDevice.objects.all()
|
||||
serializer_class = TDeviceSerializer
|
||||
update_serializer_class = TDeviceUpdateSerializer
|
||||
ordering = ['-create_time']
|
||||
filterset_class = TDeviceFilterSet
|
||||
select_related_fields = ['employee', 'area', 'employee__post', 'employee__belong_dept']
|
||||
select_related_fields = ['employee', 'area',
|
||||
'employee__post', 'employee__belong_dept']
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
|
|
@ -170,7 +172,7 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
'includeSubOwnerCodeFlag': True,
|
||||
'channelCodeList': codes,
|
||||
'pageSize': len(codes)
|
||||
}
|
||||
}
|
||||
_, res = dhClient.request(**dhapis['channel_list'], json=json)
|
||||
codes_dict = {}
|
||||
for i in res['pageData']:
|
||||
|
|
@ -211,7 +213,7 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
# ckey = 'vchannel_' + id
|
||||
# cache.delete(ckey)
|
||||
# return Response()
|
||||
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post': '*'},
|
||||
serializer_class=Serializer)
|
||||
@transaction.atomic
|
||||
|
|
@ -227,7 +229,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
_, 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'], defaults={"code": i['mac'], "type": TDevice.DEVICE_BLT})
|
||||
TDevice.objects.get_or_create(code=i['mac'], defaults={
|
||||
"code": i['mac'], "type": TDevice.DEVICE_BLT})
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post': '*'},
|
||||
|
|
@ -254,7 +257,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
td = TDevice(code=i['sn'], type=TDevice.DEVICE_SPEAKER)
|
||||
td.name = i['name']
|
||||
td.save()
|
||||
TDevice.objects.filter(type=TDevice.DEVICE_SPEAKER).exclude(code__in=t_l).delete() # 移除不需要的
|
||||
TDevice.objects.filter(type=TDevice.DEVICE_SPEAKER).exclude(
|
||||
code__in=t_l).delete() # 移除不需要的
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post': '*'},
|
||||
|
|
@ -280,10 +284,12 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
if td:
|
||||
pass
|
||||
else:
|
||||
td = TDevice(code=i['channelCode'], type=TDevice.DEVICE_VCHANNEL)
|
||||
td = TDevice(code=i['channelCode'],
|
||||
type=TDevice.DEVICE_VCHANNEL)
|
||||
td.name = i['channelName']
|
||||
td.save()
|
||||
TDevice.objects.filter(type=TDevice.DEVICE_VCHANNEL).exclude(code__in=t_l).delete()
|
||||
TDevice.objects.filter(type=TDevice.DEVICE_VCHANNEL).exclude(
|
||||
code__in=t_l).delete()
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post': '*'},
|
||||
|
|
@ -307,7 +313,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
if td:
|
||||
pass
|
||||
else:
|
||||
td = TDevice(code=i['channelCode'], type=TDevice.DEVICE_DCHANNEL)
|
||||
td = TDevice(code=i['channelCode'],
|
||||
type=TDevice.DEVICE_DCHANNEL)
|
||||
td.name = i['channelName']
|
||||
# td.access_list = ['employee', 'remployee', 'visitor']
|
||||
td.save()
|
||||
|
|
@ -325,7 +332,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
code = vdata['code']
|
||||
blt = TDevice.objects.filter(code=code, type=TDevice.DEVICE_BLT).first()
|
||||
blt = TDevice.objects.filter(
|
||||
code=code, type=TDevice.DEVICE_BLT).first()
|
||||
if blt:
|
||||
if vdata['type'] == BltBind.BLT_BIND and vdata['employee']:
|
||||
if blt.employee:
|
||||
|
|
@ -385,13 +393,14 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
request.data.update({
|
||||
'unitTypeList': ["1"],
|
||||
'includeSubOwnerCodeFlag': True
|
||||
})
|
||||
})
|
||||
_, res = dhClient.request(**dhapis['channel_list'], json=request.data)
|
||||
codes = []
|
||||
if res.get('pageData', None):
|
||||
for i in res['pageData']:
|
||||
codes.append(i['channelCode'])
|
||||
tds_info = TDeviceSerializer(instance=TDevice.objects.filter(code__in=codes), many=True).data
|
||||
tds_info = TDeviceSerializer(
|
||||
instance=TDevice.objects.filter(code__in=codes), many=True).data
|
||||
tds_dict = {}
|
||||
for i in tds_info:
|
||||
tds_dict[i['code']] = i
|
||||
|
|
@ -414,7 +423,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
codes = []
|
||||
for i in res['rows']:
|
||||
codes.append(i['sn'])
|
||||
tds_info = TDeviceSerializer(instance=TDevice.objects.filter(code__in=codes), many=True).data
|
||||
tds_info = TDeviceSerializer(
|
||||
instance=TDevice.objects.filter(code__in=codes), many=True).data
|
||||
tds_dict = {}
|
||||
for i in tds_info:
|
||||
tds_dict[i['code']] = i
|
||||
|
|
@ -435,13 +445,14 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
|
|||
request.data.update({
|
||||
'unitTypeList': ["7"],
|
||||
'includeSubOwnerCodeFlag': True
|
||||
})
|
||||
})
|
||||
_, res = dhClient.request(**dhapis['channel_list'], json=request.data)
|
||||
codes = []
|
||||
if res.get('pageData', None):
|
||||
for i in res['pageData']:
|
||||
codes.append(i['channelCode'])
|
||||
tds_info = TDeviceSerializer(instance=TDevice.objects.filter(code__in=codes), many=True).data
|
||||
tds_info = TDeviceSerializer(
|
||||
instance=TDevice.objects.filter(code__in=codes), many=True).data
|
||||
tds_dict = {}
|
||||
for i in tds_info:
|
||||
tds_dict[i['code']] = i
|
||||
|
|
@ -527,3 +538,29 @@ class TlogViewSet(ListModelMixin, CustomGenericViewSet):
|
|||
queryset = Tlog.objects.all()
|
||||
serializer_class = TlogSerializer
|
||||
filterset_fields = ['result', 'id']
|
||||
|
||||
|
||||
class DoorAuthViewSet(ListModelMixin, DestroyModelMixin, CustomGenericViewSet):
|
||||
"""
|
||||
list: 门禁权限
|
||||
|
||||
门禁权限
|
||||
"""
|
||||
perms_map = {'get': '*', 'delete': 'doorauth.delete'}
|
||||
queryset = DoorAuth.objects.all()
|
||||
serializer_class = DoorAuthSerializer
|
||||
filterset_fields = ['dchannel', 'employee']
|
||||
select_related_fields = ['dchannel', 'employee']
|
||||
search_fields = ['employee__name']
|
||||
|
||||
@transaction.atomic
|
||||
def perform_destroy(self, instance):
|
||||
dh_face_card = instance.employee.third_info['dh_face_card']
|
||||
rdict = {
|
||||
"cardNumber": dh_face_card,
|
||||
"cardPrivilegeDetails": [
|
||||
{"privilegeType": 1, "resouceCode": instance.dchannel.code},
|
||||
]
|
||||
}
|
||||
dhClient.request(**dhapis['card_door_authority_delete'], json=rdict)
|
||||
instance.delete()
|
||||
|
|
|
|||
Loading…
Reference in New Issue