门禁和定位在线离线打卡记录
This commit is contained in:
parent
9433459f24
commit
811f96fe83
|
@ -11,19 +11,19 @@ def cache_areas_info():
|
|||
"""
|
||||
缓存区域信息
|
||||
"""
|
||||
area_fix_list = []
|
||||
for i in Area.objects.filter(type=Area.AREA_TYPE_FIX).exclude(third_info__xx_rail=None):
|
||||
area_list = []
|
||||
for i in Area.objects.filter(is_hidden=False).exclude(third_info__xx_rail=None).order_by('number'):
|
||||
points = []
|
||||
for item in i.third_info['xx_rail']['detail']['polygon']['points']:
|
||||
points.append((item['x'], item['y']))
|
||||
|
||||
area_dict = {
|
||||
'id': i.id,
|
||||
'type': i.type,
|
||||
'floor_no': i.third_info['xx_rail']['detail']['floorNo'],
|
||||
'polygon': Polygon(points),
|
||||
'stay_minute_min': i.stay_minute_min,
|
||||
'stay_minute_max': i.stay_minute_max
|
||||
}
|
||||
area_fix_list.append(area_dict)
|
||||
cache.set('area_fix_list', area_fix_list, timeout=None)
|
||||
return area_fix_list
|
||||
area_list.append(area_dict)
|
||||
cache.set('area_list', area_list, timeout=None)
|
||||
return area_list
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from apps.am.models import Access, Area
|
||||
from apps.am.serializers import AccessCreateSerializer, AccessSerializer, AreaCreateUpdateSerializer, AreaSerializer
|
||||
from apps.am.tasks import cache_areas_info
|
||||
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
||||
from django.db import transaction
|
||||
from rest_framework.decorators import action
|
||||
|
@ -12,7 +13,7 @@ from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModel
|
|||
|
||||
# Create your views here.
|
||||
class AreaViewSet(CustomModelViewSet):
|
||||
queryset = Area.objects.all()
|
||||
queryset = Area.objects.all().order_by('number')
|
||||
create_serializer_class = AreaCreateUpdateSerializer
|
||||
update_serializer_class = AreaCreateUpdateSerializer
|
||||
serializer_class = AreaSerializer
|
||||
|
@ -40,6 +41,7 @@ class AreaViewSet(CustomModelViewSet):
|
|||
third_info['xx_rail'] = rail_info
|
||||
obj.third_info = third_info
|
||||
obj.save()
|
||||
cache_areas_info.delay()
|
||||
return Response()
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# Generated by Django 3.2.12 on 2022-08-10 07:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('am', '0004_auto_20220713_1408'),
|
||||
('ecm', '0012_alter_eventdo_cate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='area',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='发生区域'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eventcate',
|
||||
name='code',
|
||||
field=models.CharField(max_length=20, unique=True, verbose_name='标识'),
|
||||
),
|
||||
]
|
|
@ -15,7 +15,7 @@ class EventCate(CommonAModel):
|
|||
(10, '监控'),
|
||||
(20, '定位')
|
||||
)
|
||||
code = models.CharField('标识', max_length=10, unique=True)
|
||||
code = models.CharField('标识', max_length=20, unique=True)
|
||||
name = models.CharField('名称', max_length=20, unique=True)
|
||||
priority = models.PositiveSmallIntegerField('优先级', default=1, help_text='1-99')
|
||||
trigger = models.PositiveSmallIntegerField('触发方式', default=10, choices=EVENT_TRIGGER_CHOICES)
|
||||
|
@ -80,7 +80,7 @@ class Event(CommonBModel):
|
|||
cates = models.ManyToManyField(EventCate, verbose_name='关联事件种类', through='ecm.eventdo')
|
||||
face_img = models.CharField('人脸照', max_length=1000, null=True, blank=True)
|
||||
global_img = models.CharField('全景照', max_length=1000, null=True, blank=True)
|
||||
area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE)
|
||||
area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE, null=True, blank=True)
|
||||
vchannel = models.ForeignKey(TDevice, verbose_name='抓拍设备', on_delete=models.CASCADE, null=True, blank=True)
|
||||
location = models.JSONField('事件点位坐标', default=dict, null=False, blank=True)
|
||||
obj_cate = models.CharField('发生对象', max_length=20, help_text='people(人员)/...')
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
|
||||
from apps.opm.models import Operation, Opl, OplWorker
|
||||
from apps.utils.sms import send_sms
|
||||
import requests
|
||||
from apps.am.models import Access, Area
|
||||
from apps.am.tasks import cache_areas_info
|
||||
from apps.ecm.models import AlgoChannel, Event, EventCate, Eventdo, NotifySetting, Remind
|
||||
from apps.hrm.models import Employee
|
||||
from apps.hrm.models import ClockRecord, Employee
|
||||
from apps.system.models import User
|
||||
from apps.third.models import TDevice
|
||||
from apps.utils.queryset import get_child_queryset2, get_parent_queryset
|
||||
|
@ -58,6 +59,8 @@ def get_ep_default():
|
|||
"""
|
||||
return {
|
||||
'area_fix_id': None, # 当前所在固定区域ID
|
||||
'area_temp_id': None, # 当前所在临时区域ID
|
||||
'xx_detail': {}, # 寻息定位的详细信息
|
||||
'time0': None, # 定位首次出现时间戳
|
||||
"time1": None, # 首次在该区域时间戳
|
||||
"time2": int(time.time()), # 当前时间戳
|
||||
|
@ -82,8 +85,9 @@ def notify_event(event: Event):
|
|||
"""
|
||||
# 生成通知文本
|
||||
ep = event.employee
|
||||
obj_cate = event.obj_cate
|
||||
params = {'area': event.area.name, 'employee': '', 'event': ''}
|
||||
if ep:
|
||||
if obj_cate == 'people' and ep:
|
||||
ep_name = ep.name
|
||||
ep_type = '员工'
|
||||
if ep.type == 'rempoyee':
|
||||
|
@ -91,8 +95,10 @@ def notify_event(event: Event):
|
|||
elif ep.type == 'visitor':
|
||||
ep_type = '访客'
|
||||
params['employee'] = ep_type + ep_name
|
||||
else:
|
||||
elif obj_cate == 'people':
|
||||
params['employee'] = '未知人员'
|
||||
elif obj_cate == 'area':
|
||||
params['employee'] = '区域'
|
||||
for i in event.cates.all():
|
||||
params['event'] = params['event'] + i.name + ','
|
||||
event.voice_msg = '位于{}的{},{}请及时处理'.format(params['area'], params['employee'], params['event'])
|
||||
|
@ -113,6 +119,7 @@ def save_voice_and_speak(event: Event):
|
|||
v_p, v_num = main_cate.voice_person, main_cate.voice_num
|
||||
_, event.voice, _ = generate_voice(event.voice_msg, v_p)
|
||||
event.save()
|
||||
if event.area: # 如果事件存在发生区域
|
||||
sps = list(TDevice.objects.filter(area=event.area, type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True))
|
||||
if len(sps) == 0: # 如果当前区域没有喇叭就找覆盖区的喇叭
|
||||
sps = list(TDevice.objects.filter(areas=event.area,
|
||||
|
@ -212,7 +219,6 @@ def dispatch_dahua_event(data: dict):
|
|||
alarm_type = data['info']['alarmType']
|
||||
vchannel = TDevice.objects.filter(code=vchannel_code).first()
|
||||
event = None
|
||||
print(data)
|
||||
if alarm_type in [1001003, 1001000] and vchannel: # 内部人员/或陌生人报警
|
||||
# 查看加载的算法
|
||||
algo_codes = list(AlgoChannel.objects.filter(vchannel=vchannel).exclude(
|
||||
|
@ -260,47 +266,37 @@ def dispatch_xunxi_event(data: dict):
|
|||
rail_out(data=data.data)
|
||||
elif data.type == 'onKeyAlarm':
|
||||
# 一键呼救
|
||||
one_key_alarm(data=data)
|
||||
handle_xx_event(name='one_key_alarm', data=data)
|
||||
elif data.type == 'location':
|
||||
# 定位信息
|
||||
loc_change(data=data)
|
||||
elif data.type == 'onOffLine':
|
||||
if data.data.online:
|
||||
# 标签定位在线
|
||||
pass
|
||||
blt_online(data=data.data)
|
||||
else:
|
||||
# 标签定位离线
|
||||
pass
|
||||
blt_offline(data=data.data)
|
||||
elif data.type == 'lowpower':
|
||||
# 低电量
|
||||
low_power(data=data.data)
|
||||
handle_xx_event(name='low_power', data=data)
|
||||
elif data.type == 'bltOnOffLineV2':
|
||||
if data.data.online:
|
||||
# 标签通信在线
|
||||
blt_online(data=data.data)
|
||||
pass
|
||||
else:
|
||||
# 标签通信离线
|
||||
blt_offline(data=data.data)
|
||||
pass
|
||||
|
||||
|
||||
def rail_in(data):
|
||||
"""围栏进入事件
|
||||
"""围栏进入
|
||||
"""
|
||||
# 找到所在围栏
|
||||
area = Area.objects.filter(third_info__xx_rail__id=data['railId']).first()
|
||||
# 找到进入对象
|
||||
blts = TDevice.objects.filter(code=data['userId']).first()
|
||||
if area and blts and blts.employee: # 如果是人
|
||||
if area.type == Area.AREA_TYPE_FIX: # 如果是固定区域
|
||||
# 更新人员位置信息缓存
|
||||
key_str = 'ep_{}'.format(blts.employee.id)
|
||||
ep_loc_dict = cache.get_or_set(
|
||||
key_str, get_ep_default(), timeout=None
|
||||
)
|
||||
if ep_loc_dict['area_fix_id'] != area.id: # 如果区域未变化则不动
|
||||
ep_loc_dict['time1'] = ep_loc_dict['time2']
|
||||
ep_loc_dict['area_fix_id'] = area.id
|
||||
cache.set(key_str, ep_loc_dict)
|
||||
ep_blt = blts.employee # 标签绑定人员
|
||||
if ep_blt:
|
||||
for i in Access.objects.filter(area=area).order_by('sort'):
|
||||
|
@ -310,28 +306,28 @@ def rail_in(data):
|
|||
if ep_blt in eps_access and i.type == Access.ACCESS_IN_YES:
|
||||
return
|
||||
elif ep_blt in eps_access and i.type == Access.ACCESS_IN_NO:
|
||||
# 触发非法进入事件
|
||||
pass
|
||||
# 触发违规进入事件
|
||||
handle_xx_event_2('i_enter', ep=ep_blt, area=area)
|
||||
elif i.dept: # 如果是按部门设定的
|
||||
if i.dept.type == 'dept': # 如果是内部部门
|
||||
depts = get_child_queryset2(i.dept)
|
||||
if ep_blt.belong_dept in depts and i.type == Access.ACCESS_IN_YES:
|
||||
return
|
||||
elif ep_blt.belong_dept in depts and i.type == Access.ACCESS_IN_NO:
|
||||
# 触发非法进入事件
|
||||
pass
|
||||
# 触发违规进入事件
|
||||
handle_xx_event_2('i_enter', ep=ep_blt, area=area)
|
||||
elif i.dept.type == 'rparty': # 如果是相关方
|
||||
if ep_blt.belong_dept == i.dept and i.type == Access.ACCESS_IN_YES:
|
||||
return
|
||||
elif ep_blt.belong_dept == i.dept and i.type == Access.ACCESS_IN_NO:
|
||||
# 触发非法进入事件
|
||||
pass
|
||||
# 触发违规进入事件
|
||||
handle_xx_event_2('i_enter', ep=ep_blt, area=area)
|
||||
elif i.employee: # 如果是按人设定的
|
||||
if ep_blt == i.employee and i.type == Access.ACCESS_IN_YES:
|
||||
return
|
||||
elif ep_blt == i.employee and i.type == Access.ACCESS_IN_NO:
|
||||
# 触发非法进入事件
|
||||
pass
|
||||
# 触发违规进入事件
|
||||
handle_xx_event_2('i_enter', ep=ep_blt, area=area)
|
||||
# 通用权限设置过滤
|
||||
if ep_blt.type == 'employee' and area.employee_yes:
|
||||
return
|
||||
|
@ -340,72 +336,185 @@ def rail_in(data):
|
|||
elif ep_blt.type == 'visitor' and area.visitor_yes:
|
||||
return
|
||||
else:
|
||||
# 触发非法进入事件
|
||||
pass
|
||||
elif area and (not blts):
|
||||
# 触发未知标签进入事件
|
||||
pass
|
||||
# 触发违规进入事件
|
||||
handle_xx_event_2('i_enter', ep=ep_blt, area=area)
|
||||
# elif area and (not blts):
|
||||
# # 触发未知标签进入事件
|
||||
# e_i_enter(ep=ep_blt, area=area)
|
||||
|
||||
|
||||
def rail_out(data):
|
||||
pass
|
||||
|
||||
|
||||
def low_power(data):
|
||||
# 有绑定对象再提示低电量
|
||||
blts = TDevice.objects.filter(code=data['userId']).first()
|
||||
if blts.employee:
|
||||
# 触发低电量提醒事件
|
||||
pass
|
||||
|
||||
|
||||
def one_key_alarm(data):
|
||||
pass
|
||||
|
||||
|
||||
def loc_change(data):
|
||||
blts = TDevice.objects.filter(code=data['userId']).first()
|
||||
if blts.employee:
|
||||
# 从缓存查询人员位置信息
|
||||
time2 = int(time.time())
|
||||
key_str = 'ep_{}'.format(blts.employee.id)
|
||||
ep_loc_dict = cache.get_or_set(
|
||||
key_str, get_ep_default(), timeout=None
|
||||
)
|
||||
ep_loc_dict['time2'] = int(time.time())
|
||||
ep_loc_dict['xx_detail'] = data
|
||||
|
||||
area_fix, area_temp = get_area_from_point(data['xMillimeter'], data['yMillimeter'], data['floorNo'])
|
||||
time2 = int(time.time())
|
||||
ep_loc_dict['area_temp_id'] = area_temp['id'] if area_temp else None
|
||||
ep_loc_dict['time2'] = time2
|
||||
# 从缓存里获取固定区域列表信息
|
||||
area_fix_list = cache.get('area_fix_list', None)
|
||||
if not area_fix_list:
|
||||
area_fix_list = cache_areas_info()
|
||||
if ep_loc_dict.get('area_fix_id', None):
|
||||
# 如果存在所在固定区域
|
||||
area_info = get_area_info_from_cache(ep_loc_dict['area_fix_id'], area_fix_list)
|
||||
if area_info:
|
||||
if area_fix and ep_loc_dict['area_fix_id'] == area_fix['id']:
|
||||
# 如果停留在该区域
|
||||
cache.set(key_str, ep_loc_dict)
|
||||
# 在该固定区域停留时间(分钟)
|
||||
stay_minute = int((ep_loc_dict['time2']-ep_loc_dict['time1'])/60)
|
||||
# 判断停留时间是否合理
|
||||
# 先通过自定义权限过滤(暂未做)
|
||||
# 再经过通用设置过滤
|
||||
if 0 < stay_minute < area_info['stay_minute_min']:
|
||||
if 0 < stay_minute < area_fix['stay_minute_min']:
|
||||
# 触发离岗事件
|
||||
return
|
||||
elif area_info['stay_minute_max'] < stay_minute:
|
||||
handle_xx_event_2('leave_area', ep=blts.employee, area=Area.objects.get(id=area_fix['id']))
|
||||
elif area_fix['stay_minute_max'] < stay_minute:
|
||||
# 触发超时滞留事件
|
||||
return
|
||||
handle_xx_event_2('stand_area', ep=blts.employee, area=Area.objects.get(id=area_fix['id']))
|
||||
else:
|
||||
for i in area_fix_list:
|
||||
if data['floorNo'] == i['floor_no']:
|
||||
point = Point(data['xMillimeter'], data['yMillimeter'])
|
||||
if i['polygon'].intersects(point): # 如果点在多边形中
|
||||
ep_loc_dict['time1'] = time2
|
||||
ep_loc_dict['are_id'] = i['area_fix_id']
|
||||
ep_loc_dict['time2'] = time2
|
||||
ep_loc_dict['area_fix_id'] = area_fix['id'] if area_fix else None
|
||||
cache.set(key_str, ep_loc_dict)
|
||||
|
||||
|
||||
def handle_xx_event(name: str, data: dict):
|
||||
# 有绑定对象再提示事件
|
||||
blts = TDevice.objects.filter(code=data['userId']).first()
|
||||
if blts.employee:
|
||||
# 触发事件
|
||||
cate = EventCate.objects.filter(code=name).first()
|
||||
if cate:
|
||||
event = Event()
|
||||
# 查询定位信息
|
||||
key_str = 'ep_{}'.format(blts.employee.id)
|
||||
ep_loc_dict = cache.get(key_str, None)
|
||||
if ep_loc_dict:
|
||||
if ep_loc_dict['area_fix_id']:
|
||||
event.area = Area.objects.get(id=ep_loc_dict['area_fix_id'])
|
||||
event.location = ep_loc_dict['xx_detail']
|
||||
event.obj_cate = 'people'
|
||||
event.employee = blts.employee
|
||||
event.happen_time = timezone.now()
|
||||
event.save()
|
||||
Eventdo.objects.get_or_create(cate=cate, event=event, defaults={
|
||||
'cate': cate,
|
||||
'event': event
|
||||
})
|
||||
notify_event(event)
|
||||
|
||||
|
||||
def handle_xx_event_2(name: str, ep: Employee, area: Area):
|
||||
# 违规进入事件特殊处理
|
||||
# 找寻该区域下审批和进行的作业, 本厂或相关方人员, 如是就不触发
|
||||
if name == 'i_enter' and ep.type in ['employee', 'remployee']:
|
||||
ops = Operation.objects.filter(area=area, state__in=[Operation.OP_AUDIT, Operation.OP_WAIT, Operation.OP_WORK])
|
||||
if OplWorker.objects.filter(opl__operation__in=ops, worker__employee=ep).exists():
|
||||
# 如果是作业人员
|
||||
return
|
||||
elif ops.filter(coordinator__employee=ep).exists():
|
||||
# 如果是协调员
|
||||
return
|
||||
elif Opl.objects.filter(operation=ops, charger__employee=ep).exists():
|
||||
# 如果是作业负责人
|
||||
return
|
||||
elif Opl.objects.filter(operation=ops, monitor__employee=ep).exists():
|
||||
# 如果是作业监护人
|
||||
return
|
||||
cate = EventCate.objects.filter(code=name).first()
|
||||
if cate:
|
||||
event = Event()
|
||||
event.area = area
|
||||
# 查询定位信息
|
||||
key_str = 'ep_{}'.format(ep.id)
|
||||
ep_loc_dict = cache.get(key_str, None)
|
||||
if ep_loc_dict:
|
||||
event.location = ep_loc_dict['xx_detail']
|
||||
event.obj_cate = 'people'
|
||||
event.employee = ep
|
||||
event.happen_time = timezone.now()
|
||||
event.save()
|
||||
Eventdo.objects.get_or_create(cate=cate, event=event, defaults={
|
||||
'cate': cate,
|
||||
'event': event
|
||||
})
|
||||
notify_event(event)
|
||||
|
||||
|
||||
def blt_online(data):
|
||||
pass
|
||||
# 定位在线
|
||||
blts = TDevice.objects.filter(code=data['userId']).first()
|
||||
if blts.employee:
|
||||
ep = blts.employee
|
||||
if ep.type == 'employee' and ep.is_at_work is False:
|
||||
# 上班打卡
|
||||
now = timezone.now()
|
||||
cr_10 = ClockRecord.objects.filter(type=10, employee=ep, create_time__year=now.year,
|
||||
create_time__month=now.month, create_time__day=now.day).first()
|
||||
if cr_10:
|
||||
if now < cr_10.create_by:
|
||||
cr_10.create_by = now
|
||||
cr_10.trigger = 'location'
|
||||
cr_10.detail = data
|
||||
cr_10.save()
|
||||
else:
|
||||
cr_10 = ClockRecord()
|
||||
cr_10.type = 10
|
||||
cr_10.employee = ep
|
||||
cr_10.trigger = 'location'
|
||||
cr_10.detail = data
|
||||
cr_10.save()
|
||||
ep.is_at_work = True
|
||||
ep.save()
|
||||
|
||||
|
||||
def blt_offline(data):
|
||||
pass
|
||||
blts = TDevice.objects.filter(code=data['userId']).first()
|
||||
# 定位离线
|
||||
if blts.employee:
|
||||
ep = blts.employee
|
||||
if ep.type == 'employee' and ep.is_at_work:
|
||||
# 下班打卡
|
||||
now = timezone.now()
|
||||
cr_20 = ClockRecord.objects.filter(type=20, employee=ep, create_time__year=now.year,
|
||||
create_time__month=now.month, create_time__day=now.day).first()
|
||||
if cr_20:
|
||||
if now > cr_20.create_by:
|
||||
cr_20.create_by = now
|
||||
cr_20.trigger = 'location'
|
||||
cr_20.detail = data
|
||||
cr_20.save()
|
||||
else:
|
||||
cr_20 = ClockRecord()
|
||||
cr_20.type = 20
|
||||
cr_20.employee = ep
|
||||
cr_20.trigger = 'location'
|
||||
cr_20.detail = data
|
||||
cr_20.save()
|
||||
ep.is_at_work = False
|
||||
ep.save()
|
||||
|
||||
|
||||
def get_area_from_point(x: int, y: int, floorNo: str):
|
||||
"""
|
||||
从位置信息获取所在固定区域
|
||||
返回一个固定区域, 一个临时区域
|
||||
"""
|
||||
area_fix = None
|
||||
area_temp = None
|
||||
area_list = cache.get('area_list', None)
|
||||
if not area_list:
|
||||
area_list = cache_areas_info()
|
||||
point = Point(x, y)
|
||||
for i in area_list:
|
||||
if floorNo == i['floor_no']:
|
||||
if i['polygon'].intersects(point): # 如果点在多边形中
|
||||
if i['type'] == Area.AREA_TYPE_FIX:
|
||||
area_fix = i
|
||||
elif i['type'] == Area.AREA_TYPE_TEMP:
|
||||
area_temp = i
|
||||
return area_fix, area_temp
|
||||
|
|
|
@ -5,6 +5,8 @@ from threading import Thread
|
|||
from celery import shared_task
|
||||
|
||||
from apps.am.models import Area
|
||||
from apps.ecm.models import EventCate, Eventdo
|
||||
from apps.ecm.service import notify_event
|
||||
from apps.third.clients import xxClient
|
||||
from apps.third.models import TDevice
|
||||
from apps.third.tapis import xxapis
|
||||
|
@ -26,10 +28,25 @@ def update_count_people(i: Area):
|
|||
i.save()
|
||||
if i.count_people >= i.count_people_max:
|
||||
# 触发超员事件
|
||||
pass
|
||||
handle_xx_event_3('over_man', i)
|
||||
elif i.count_people < i.count_people_min:
|
||||
# 触发缺员事件
|
||||
pass
|
||||
handle_xx_event_3('lack_man', i)
|
||||
|
||||
|
||||
def handle_xx_event_3(name: str, area: Area):
|
||||
cate = EventCate.objects.filter(code=name).first()
|
||||
if cate:
|
||||
event = Event()
|
||||
event.area = area
|
||||
event.obj_cate = 'area'
|
||||
event.happen_time = timezone.now()
|
||||
event.save()
|
||||
Eventdo.objects.get_or_create(cate=cate, event=event, defaults={
|
||||
'cate': cate,
|
||||
'event': event
|
||||
})
|
||||
notify_event(event)
|
||||
|
||||
|
||||
@shared_task
|
||||
|
@ -50,5 +67,3 @@ def check_event_timeout():
|
|||
if cate.hanle_minute > 0 and (timezone.now()-i.create_time).seconds > cate.hanle_minute * 60:
|
||||
i.is_timeout = True
|
||||
i.save()
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ class ClockRecordFilterSet(filters.FilterSet):
|
|||
|
||||
class Meta:
|
||||
model = ClockRecord
|
||||
fields = ['create_by', 'create_time_start', 'create_time_end', 'year', 'month']
|
||||
fields = ['employee', 'create_time_start', 'create_time_end', 'year', 'month']
|
||||
|
||||
def filter_year(self, queryset, name, value):
|
||||
return queryset.filter(create_time_date__year=value)
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# Generated by Django 3.2.12 on 2022-08-10 07:12
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('hrm', '0005_alter_employee_phone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='clockrecord',
|
||||
name='create_by',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='clockrecord',
|
||||
name='update_by',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='clockrecord',
|
||||
name='detail',
|
||||
field=models.JSONField(blank=True, default=dict, verbose_name='相关记录'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='clockrecord',
|
||||
name='employee',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='对应人员'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='clockrecord',
|
||||
name='trigger',
|
||||
field=models.CharField(default='door', max_length=20, verbose_name='触发'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clockrecord',
|
||||
name='type',
|
||||
field=models.PositiveSmallIntegerField(choices=[(10, '上班打卡'), (20, '下班打卡')], default=10, verbose_name='打卡类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='user',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='employee', to=settings.AUTH_USER_MODEL, verbose_name='系统账号'),
|
||||
),
|
||||
]
|
|
@ -1,7 +1,7 @@
|
|||
from django.db import models
|
||||
from apps.system.models import Post, User
|
||||
|
||||
from apps.utils.models import CommonADModel, CommonAModel, CommonBModel
|
||||
from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel
|
||||
|
||||
|
||||
class Employee(CommonBModel):
|
||||
|
@ -22,6 +22,7 @@ class Employee(CommonBModel):
|
|||
type = models.CharField('人员类型', default='employee', max_length=10, choices=PEOPLE_TYPE_CHOICES)
|
||||
user = models.OneToOneField(User,
|
||||
verbose_name='系统账号',
|
||||
related_name='employee',
|
||||
on_delete=models.PROTECT, null=True, blank=True)
|
||||
name = models.CharField('姓名', max_length=20)
|
||||
phone = models.CharField('手机号', max_length=11, null=True, blank=True, unique=True)
|
||||
|
@ -69,15 +70,20 @@ class NotWorkRemark(CommonAModel):
|
|||
remark = models.CharField('未打卡说明', null=True, blank=True, max_length=200)
|
||||
|
||||
|
||||
class ClockRecord(CommonADModel):
|
||||
class ClockRecord(BaseModel):
|
||||
"""
|
||||
打卡记录
|
||||
"""
|
||||
ClOCK_WORK1 = 10
|
||||
ClOCK_ON = 10
|
||||
CLOCK_OFF = 20
|
||||
type_choice = (
|
||||
(ClOCK_WORK1, '上班打卡'),
|
||||
(ClOCK_ON, '上班打卡'),
|
||||
(CLOCK_OFF, '下班打卡'),
|
||||
)
|
||||
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1)
|
||||
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_ON)
|
||||
employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE)
|
||||
trigger = models.CharField('触发', max_length=20)
|
||||
detail = models.JSONField('相关记录', default=dict, null=False, blank=True)
|
||||
|
||||
|
||||
class Certificate(CommonAModel):
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from apps.hrm.models import Employee
|
||||
from apps.hrm.models import ClockRecord, Employee
|
||||
from apps.third.models import TDevice
|
||||
from apps.third.tapis import dhapis
|
||||
from apps.third.clients import dhClient
|
||||
from apps.utils.tools import rannum, ranstr
|
||||
from datetime import datetime
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
class HrmService:
|
||||
|
||||
|
@ -193,7 +193,53 @@ class HrmService:
|
|||
"""
|
||||
deviceCode = data['infoArray']['deviceCode']
|
||||
device = TDevice.objects.filter(code=deviceCode).first()
|
||||
if not device:
|
||||
device = TDevice.objects.create(type=TDevice.DEVICE_DCHANNEL, code=deviceCode)
|
||||
if device.is_clock:
|
||||
pass
|
||||
if device and device.is_clock:
|
||||
# 如果设置为关联考勤
|
||||
if data['info']['extend']['enterOrExit'] == "1":
|
||||
# 如果是进门
|
||||
id_number = data['info']['extend']['paperNumber']
|
||||
if id_number:
|
||||
ep = Employee.objects.filter(id_number=id_number, type="employee").first()
|
||||
# 如果是内部员工创建上班打卡记录(更新)
|
||||
if ep and ep.is_at_work is False:
|
||||
now = timezone.now()
|
||||
cr_10 = ClockRecord.objects.filter(type=10, employee=ep, create_time__year=now.year, create_time__month=now.month, create_time__day=now.day).first()
|
||||
if cr_10:
|
||||
if now < cr_10.create_by:
|
||||
cr_10.create_by = now
|
||||
cr_10.trigger = 'door'
|
||||
cr_10.detail = data['info']['extend']
|
||||
cr_10.save()
|
||||
else:
|
||||
cr_10 = ClockRecord()
|
||||
cr_10.type = 10
|
||||
cr_10.employee = ep
|
||||
cr_10.trigger = 'door'
|
||||
cr_10.detail = data['info']['extend']
|
||||
cr_10.save()
|
||||
ep.is_at_work = True
|
||||
ep.save()
|
||||
elif data['info']['extend']['enterOrExit'] == "2":
|
||||
# 如果是出门
|
||||
id_number = data['info']['extend']['paperNumber']
|
||||
if id_number:
|
||||
ep = Employee.objects.filter(id_number=id_number, type="employee").first()
|
||||
# 如果是内部员工创建下班打卡记录(更新)
|
||||
if ep and ep.is_at_work:
|
||||
now = timezone.now()
|
||||
cr_20 = ClockRecord.objects.filter(type=20, employee=ep, create_time__year=now.year, create_time__month=now.month, create_time__day=now.day).first()
|
||||
if cr_20:
|
||||
if now > cr_20.create_by:
|
||||
cr_20.create_by = now
|
||||
cr_20.trigger = 'door'
|
||||
cr_20.detail = data['info']['extend']
|
||||
cr_20.save()
|
||||
else:
|
||||
cr_20 = ClockRecord()
|
||||
cr_20.type = 20
|
||||
cr_20.employee = ep
|
||||
cr_20.trigger = 'door'
|
||||
cr_20.detail = data['info']['extend']
|
||||
cr_20.save()
|
||||
ep.is_at_work = False
|
||||
ep.save()
|
||||
|
|
|
@ -207,7 +207,7 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
|
|||
authentication_classes = []
|
||||
permission_classes = [AllowAny]
|
||||
queryset = ClockRecord.objects.all()
|
||||
select_related_fields = ['create_by']
|
||||
select_related_fields = ['employee']
|
||||
serializer_class = ClockRecordListSerializer
|
||||
filterset_class = ClockRecordFilterSet
|
||||
ordering = ['-pk']
|
||||
|
|
|
@ -61,4 +61,5 @@ def opl_audit_end(ticket: Ticket):
|
|||
op.save()
|
||||
|
||||
# 授予相关工作人员区域进入权限
|
||||
worker_ep_ids = list(OplWorker.objects.filter(opl=opl).values_list('worker__id', flat=True))
|
||||
# worker_ep_ids = list(OplWorker.objects.filter(opl=opl).values_list('worker__id', flat=True))
|
||||
# 发送通知
|
||||
|
|
|
@ -9,8 +9,10 @@ from rest_framework.exceptions import APIException, ParseError
|
|||
|
||||
from apps.third.errors import DH_REQUEST_ERROR
|
||||
from apps.third.models import Tlog
|
||||
from apps.utils.my_rsa import encrypt_data
|
||||
from apps.utils.tools import print_roundtrip
|
||||
from django.utils.timezone import now
|
||||
from apps.third.tapis import dhapis
|
||||
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
|
@ -140,3 +142,17 @@ class DhClient:
|
|||
if '$' in code:
|
||||
d_code = code.split('$')[0]
|
||||
json_data['deviceCode'] = d_code
|
||||
|
||||
def get_password_token(self):
|
||||
_, res = self.request(**dhapis['oauth_key'])
|
||||
e_pwd = encrypt_data(settings.DAHUA_PASSWORD, res['publicKey'])
|
||||
res['public_key'] = res['publicKey']
|
||||
res['password'] = e_pwd
|
||||
res.update({
|
||||
"grant_type": "password",
|
||||
"username": settings.DAHUA_USERNAME,
|
||||
"client_id": settings.DAHUA_CLIENTID,
|
||||
"client_secret": settings.DAHUA_SECRET,
|
||||
})
|
||||
_, res2 = self.request(**dhapis['oauth_token'], json=res)
|
||||
return res2
|
||||
|
|
|
@ -107,6 +107,14 @@ dhapis = {
|
|||
"close_door": {
|
||||
"url": "/evo-apigw/evo-accesscontrol/1.2.0/card/accessControl/channelControl/closeDoor",
|
||||
"method": "post"
|
||||
},
|
||||
"oauth_key": {
|
||||
"url": "/evo-apigw/evo-oauth/1.0.0/oauth/public-key",
|
||||
"method": "get"
|
||||
},
|
||||
"oauth_token": {
|
||||
"url": "/evo-apigw/evo-oauth/1.0.0/oauth/extend/token",
|
||||
"method": "post"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +151,7 @@ xxapis = {
|
|||
"blt_info": {
|
||||
"url": "/api/devicesV3/bltInfoByMac",
|
||||
"method": "post"
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,9 +27,7 @@ class DahuaTestView(MyLoggingMixin, APIView):
|
|||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
_, res = dhClient.request(
|
||||
url='/evo-apigw/evo-brm/1.0.0/person/subsystem/{}'.format(2059335),
|
||||
method='get')
|
||||
res = dhClient.get_password_token()
|
||||
return Response(res)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import base64
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Hash import SHA
|
||||
from Crypto.Signature import PKCS1_v1_5 as PKCS1_signature
|
||||
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
|
||||
|
||||
|
||||
def encrypt_data(msg, pub_key):
|
||||
pub_key = '-----BEGIN RSA PUBLIC KEY-----\n'+pub_key+'\n-----END RSA PUBLIC KEY-----'
|
||||
public_key = RSA.importKey(pub_key)
|
||||
cipher = PKCS1_cipher.new(public_key)
|
||||
encrypt_text = base64.b64encode(cipher.encrypt(bytes(msg.encode("utf8"))))
|
||||
return encrypt_text.decode('utf-8')
|
|
@ -86,13 +86,6 @@ class WfService(object):
|
|||
nsteps_list.append(i)
|
||||
return nsteps_list
|
||||
|
||||
@classmethod
|
||||
def get_ticket_transitions(cls, ticket: Ticket):
|
||||
"""
|
||||
获取工单可执行的操作
|
||||
"""
|
||||
return cls.get_state_transitions(ticket.state)
|
||||
|
||||
@classmethod
|
||||
def get_transition_by_args(cls, kwargs: dict):
|
||||
"""
|
||||
|
|
|
@ -27,3 +27,4 @@ requests==2.28.1
|
|||
grpcio==1.47.0
|
||||
grpcio-tools==1.47.0
|
||||
protobuf==3.20.1
|
||||
pycryptodome==3.15.0
|
||||
|
|
Loading…
Reference in New Issue