356 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			356 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
from rest_framework.exceptions import ParseError, APIException
 | 
						|
import logging
 | 
						|
import time
 | 
						|
from apps.hrm.models import ClockRecord, Employee
 | 
						|
from apps.third.errors import DH_REQUEST_ERROR
 | 
						|
from apps.third.models import TDevice
 | 
						|
from apps.third.tapis import dhapis
 | 
						|
from apps.third.dahua import dhClient
 | 
						|
from apps.utils.tools import rannum, ranstr
 | 
						|
from datetime import datetime, timedelta
 | 
						|
from django.utils import timezone
 | 
						|
from dateutil import tz
 | 
						|
from threading import Thread
 | 
						|
from django.conf import settings
 | 
						|
myLogger = logging.getLogger('log')
 | 
						|
 | 
						|
 | 
						|
class HrmService:
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def sync_dahua_employee(cls, ep: Employee, old_photo='', start_time=None, end_time=None):
 | 
						|
        """同步大华信息(员工/卡片/门禁)
 | 
						|
 | 
						|
        Args:
 | 
						|
            ep (Employee): 人员实例
 | 
						|
            old_photo (str, optional): 原照片地址. Defaults to ''.
 | 
						|
            start_time (_type_, optional): 开人脸卡起始时间. Defaults to None.
 | 
						|
            end_time (_type_, optional): 开人脸卡结束时间. Defaults to None.
 | 
						|
        Returns:
 | 
						|
            _type_: _description_
 | 
						|
        """
 | 
						|
        if not settings.DAHUA_ENABLED: # 如果大华没启用, 直接返回
 | 
						|
            return
 | 
						|
        dh_id = ep.third_info.get('dh_id', None)
 | 
						|
        dh_photo = ep.third_info.get('dh_photo', None)
 | 
						|
        dh_face_card = ep.third_info.get('dh_face_card', None)
 | 
						|
        departmentId = 1
 | 
						|
        if ep.belong_dept:
 | 
						|
            try:
 | 
						|
                departmentId = ep.belong_dept.third_info['dh_id']
 | 
						|
            except Exception:
 | 
						|
                pass
 | 
						|
        if ep.third_info.get('dh_id', None):  # 如果有大华信息
 | 
						|
            dh_id = ep.third_info['dh_id']
 | 
						|
            dh_photo = ep.third_info['dh_photo']
 | 
						|
            json_data = {
 | 
						|
                "service": "ehs",
 | 
						|
                "id": dh_id,
 | 
						|
                "name": ep.name,
 | 
						|
                "code": ep.number if ep.number else ranstr(6),
 | 
						|
                "paperType": 111,
 | 
						|
                "paperNumber": ep.id_number,
 | 
						|
                "paperAddress": "default",
 | 
						|
                "departmentId": departmentId,
 | 
						|
                "phone": ep.phone,
 | 
						|
                "email": ep.email,
 | 
						|
                "sex": 1 if ep.gender == '男' else 2,
 | 
						|
                "biosignatureTypeList": [3],
 | 
						|
                "personBiosignatures": [{
 | 
						|
                    "type": 3,
 | 
						|
                    "index": 1,
 | 
						|
                    "path": dh_photo
 | 
						|
                }]
 | 
						|
            }
 | 
						|
            if ep.photo != old_photo and ep.photo:
 | 
						|
                _, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=ep.photo)
 | 
						|
                dh_photo = res["fileUrl"]
 | 
						|
                json_data.update(
 | 
						|
                    {
 | 
						|
                        "biosignatureTypeList": [3],
 | 
						|
                        "personBiosignatures": [{
 | 
						|
                            "type": 3,
 | 
						|
                            "index": 1,
 | 
						|
                            "path": dh_photo
 | 
						|
                        }]
 | 
						|
                    }
 | 
						|
                )
 | 
						|
            dhClient.request(**dhapis['person_update'], json=json_data)
 | 
						|
            ep = cls.save(ep, data={'dh_id': dh_id, 'dh_photo': dh_photo})
 | 
						|
        else:
 | 
						|
            _, res = dhClient.request(**dhapis['person_gen_id'])
 | 
						|
            dh_id = res['id']
 | 
						|
            json_data = {
 | 
						|
                "service": "ehs",
 | 
						|
                "id": dh_id,
 | 
						|
                "name": ep.name,
 | 
						|
                "code": ep.number if ep.number else ranstr(6),
 | 
						|
                "paperType": 111,
 | 
						|
                "paperNumber": ep.id_number,
 | 
						|
                "paperAddress": "default",
 | 
						|
                "departmentId": departmentId,
 | 
						|
                "phone": ep.phone,
 | 
						|
                "email": ep.email,
 | 
						|
                "sex": 1 if ep.gender == '男' else 2
 | 
						|
            }
 | 
						|
            _, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=ep.photo)
 | 
						|
            dh_photo = res["fileUrl"]
 | 
						|
            json_data.update(
 | 
						|
                {
 | 
						|
                    "biosignatureTypeList": [3],
 | 
						|
                    "personBiosignatures": [{
 | 
						|
                        "type": 3,
 | 
						|
                        "index": 1,
 | 
						|
                        "path": dh_photo
 | 
						|
                    }]
 | 
						|
                }
 | 
						|
            )
 | 
						|
            _, res = dhClient.request(**dhapis['person_add'], json=json_data)
 | 
						|
            ep = cls.save(ep, data={'dh_id': dh_id, 'dh_photo': dh_photo})
 | 
						|
        # 开人脸卡
 | 
						|
        dh_face_card = cls.open_face_card(
 | 
						|
            ep=ep, dh_id=dh_id, departmentId=departmentId, start_time=start_time, end_time=end_time)
 | 
						|
        # 授予门禁权限
 | 
						|
        dh_dchannels = cls.door_auth(ep=ep)
 | 
						|
        return {'dh_id': dh_id, 'dh_photo': dh_photo, 'dh_face_card': dh_face_card, 'dh_dchannels': dh_dchannels}
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def open_face_card(cls, ep, dh_id, departmentId, start_time, end_time):
 | 
						|
        """开人脸卡/有卡就更新卡时间
 | 
						|
        """
 | 
						|
        if ep.third_info.get('dh_face_card', None):
 | 
						|
            cardNumber = ep.third_info.get('dh_face_card')
 | 
						|
            # 如果有人脸卡就执行更新操作
 | 
						|
            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")
 | 
						|
                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})
 | 
						|
            return cardNumber
 | 
						|
        else:
 | 
						|
            _, res = dhClient.request(**dhapis['card_gen_id'])
 | 
						|
            cardId = res['id']
 | 
						|
            cardNumber = str(ep.id)[3:8] + rannum(5)
 | 
						|
            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")
 | 
						|
            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")
 | 
						|
            json_data = {
 | 
						|
                "id": cardId,
 | 
						|
                "cardNumber": cardNumber,
 | 
						|
                "category": 0,
 | 
						|
                "cardType": 0,
 | 
						|
                "personId": dh_id,
 | 
						|
                "departmentId": departmentId,
 | 
						|
                "startDate": startDate,
 | 
						|
                "endDate": endDate
 | 
						|
            }
 | 
						|
            dhClient.request(**dhapis['card_add'], json=json_data)
 | 
						|
            cls.save(ep, data={'dh_face_card': cardNumber,
 | 
						|
                     'dh_face_card_start': startDate, 'dh_face_card_end': endDate})
 | 
						|
            # anum = 0  # 尝试次数以确保卡片生成
 | 
						|
            # while anum < 10:
 | 
						|
            #     time.sleep(2)
 | 
						|
            #     is_ok, res = dhClient.request(**dhapis['card_detail'], params={'cardNumber': cardNumber}, raise_exception=False)
 | 
						|
            #     if is_ok == 'success':
 | 
						|
            #         break
 | 
						|
            #     anum = anum + 1
 | 
						|
            return cardNumber
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def door_auth(cls, ep: Employee, dh_dchannels: list = []):
 | 
						|
        """授予门禁权限
 | 
						|
        """
 | 
						|
        from apps.third.models import TDevice
 | 
						|
        dh_face_card = ep.third_info.get('dh_face_card', None)
 | 
						|
        if dh_dchannels:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            # 查找可授予的门禁通道号
 | 
						|
            dh_dchannels = list(TDevice.objects.filter(type=TDevice.DEVICE_DCHANNEL,
 | 
						|
                                                       access_list__contains=ep.type).values_list('code', flat=True))
 | 
						|
        if dh_dchannels:
 | 
						|
            details = []
 | 
						|
            for i in dh_dchannels:
 | 
						|
                details.append({
 | 
						|
                    "privilegeType": 1,
 | 
						|
                    "resouceCode": i
 | 
						|
                })
 | 
						|
            json_data = {
 | 
						|
                "cardNumbers": [dh_face_card],
 | 
						|
                "timeQuantumId": 1,
 | 
						|
                "cardPrivilegeDetails": details
 | 
						|
            }
 | 
						|
            ind = 0
 | 
						|
            while ind < 8:
 | 
						|
                time.sleep(6)
 | 
						|
                try:
 | 
						|
                    dhClient.request(**dhapis['card_door_authority'], json=json_data)
 | 
						|
                    break
 | 
						|
                except Exception as e:
 | 
						|
                    ind = ind + 1
 | 
						|
                    if ind >= 8:
 | 
						|
                        raise e
 | 
						|
            #     time.sleep(5)
 | 
						|
            #     is_ok, res = dhClient.request(**dhapis['card_door_authority'], json=json_data, raise_exception=False)
 | 
						|
            #     if is_ok == 'success':
 | 
						|
            #         break
 | 
						|
            #     elif is_ok == 'fail' and '44999999' in res['code']:
 | 
						|
            #         if ind
 | 
						|
            #         ind = ind + 1
 | 
						|
            #         continue
 | 
						|
            #     elif is_ok == 'fail':
 | 
						|
            #         raise ParseError(**res)
 | 
						|
            #     elif is_ok == 'error':
 | 
						|
            #         raise APIException(**DH_REQUEST_ERROR)
 | 
						|
            cls.save(ep, data={'dh_dchannels': dh_dchannels})
 | 
						|
        return dh_dchannels
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def save(cls, ep: Employee, data: dict):
 | 
						|
        """更新third_info
 | 
						|
        """
 | 
						|
        ti = ep.third_info
 | 
						|
        ti.update(data)
 | 
						|
        ep.third_info = ti
 | 
						|
        ep.save()
 | 
						|
        return ep
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def swipe(cls, data: dict):
 | 
						|
        """
 | 
						|
        实时刷卡事件/用于记录考勤
 | 
						|
        有身份证号的
 | 
						|
        """
 | 
						|
        id_number = data['info']['extend'].get('paperNumber', None)
 | 
						|
        if id_number:
 | 
						|
            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'])
 | 
						|
    
 | 
						|
    @classmethod
 | 
						|
    def swipe_offline(cls, data:dict):
 | 
						|
        """离线刷卡推送记录处理
 | 
						|
        """
 | 
						|
        for i in data['infoArray']:
 | 
						|
            id_number = i.get('paperNumber', None)
 | 
						|
            if id_number:
 | 
						|
                nodeCode = i['acsChannelCode']
 | 
						|
                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):
 | 
						|
        from apps.vm.models import Visit
 | 
						|
        from apps.rpm.models import Rpj
 | 
						|
 | 
						|
        # 进行相关方/访客项目更新
 | 
						|
        Visit.objects.filter(state=Visit.V_ENTER, visitors__employee__id_number=id_number).update(
 | 
						|
            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()
 | 
						|
        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)
 | 
						|
            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)
 | 
						|
            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)
 | 
						|
 | 
						|
            card_type = 30
 | 
						|
            trigger = 'door'
 | 
						|
            if e_type == 1:
 | 
						|
                card_type = 10
 | 
						|
            elif e_type == 2:
 | 
						|
                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=3, minute=0, 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)
 | 
						|
                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)
 | 
						|
                time_20_y = datetime(year=s_time_f.year, month=s_time_f.month,
 | 
						|
                                        day=s_time_f.day, hour=23, minute=0, 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:
 | 
						|
                    card_type = 20
 | 
						|
                trigger = 'panel'
 | 
						|
    
 | 
						|
            # 先直接创建记录
 | 
						|
            cr = ClockRecord.objects.filter(employee=ep, create_time=s_time_f).first()
 | 
						|
            if cr:
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                cr = ClockRecord()
 | 
						|
                cr.employee = ep
 | 
						|
                cr.create_time = s_time_f
 | 
						|
            cr.type = card_type
 | 
						|
            cr.exception_type = None 
 | 
						|
            cr.trigger = trigger
 | 
						|
            cr.detail = detail
 | 
						|
            cr.save()
 | 
						|
 | 
						|
            if card_type == 10:
 | 
						|
                # 查找当天的进门记录
 | 
						|
                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)
 | 
						|
            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_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):
 | 
						|
                            last_obj.exception_type = ClockRecord.E_TYPE_LESS
 | 
						|
                            last_obj.save()
 | 
						|
                        elif  time_d > timedelta(hours=14):
 | 
						|
                            last_obj.exception_type = ClockRecord.E_TYPE_MORE
 | 
						|
                            last_obj.save()
 | 
						|
                        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_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:
 | 
						|
                    update_fields['is_atwork'] = True
 | 
						|
                else:
 | 
						|
                    update_fields['is_atwork'] = False
 | 
						|
                Employee.objects.filter(id=ep.id).update(**update_fields)
 | 
						|
                
 | 
						|
 | 
						|
        
 | 
						|
 | 
						|
        # 此处可触发安全帽事件逻辑 |