门禁和定位在线离线打卡记录

This commit is contained in:
曹前明 2022-08-10 15:13:21 +08:00
parent 9433459f24
commit 811f96fe83
18 changed files with 403 additions and 119 deletions

View File

@ -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

View File

@ -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()

View File

@ -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='标识'),
),
]

View File

@ -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(人员)/...')

View File

@ -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,10 +119,11 @@ 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()
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,
type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True))
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,
type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True))
# 查找固定喇叭
for m in event.cates.all():
for n in m.speakers.all():
@ -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:
# 在该固定区域停留时间(分钟)
stay_minute = int((ep_loc_dict['time2']-ep_loc_dict['time1'])/60)
# 判断停留时间是否合理
# 先通过自定义权限过滤(暂未做)
# 再经过通用设置过滤
if 0 < stay_minute < area_info['stay_minute_min']:
# 触发离岗事件
return
elif area_info['stay_minute_max'] < stay_minute:
# 触发超时滞留事件
return
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_fix['stay_minute_min']:
# 触发离岗事件
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:
# 触发超时滞留事件
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
cache.set(key_str, ep_loc_dict)
ep_loc_dict['time1'] = 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

View File

@ -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()

View File

@ -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)

View File

@ -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='系统账号'),
),
]

View File

@ -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):

View File

@ -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()

View File

@ -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']

View File

@ -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))
# 发送通知

View File

@ -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()
@ -139,4 +141,18 @@ class DhClient:
}
if '$' in code:
d_code = code.split('$')[0]
json_data['deviceCode'] = d_code
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

View File

@ -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"
}
},
}

View File

@ -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)

13
apps/utils/my_rsa.py Normal file
View File

@ -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')

View File

@ -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):
"""

View File

@ -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