diff --git a/apps/hrm/errors.py b/apps/hrm/errors.py index 0b4d1e27..22669f1b 100644 --- a/apps/hrm/errors.py +++ b/apps/hrm/errors.py @@ -1 +1,4 @@ -NO_NEED_LEVEL_REMARK = {"code":"no_need_level_remark", "detail":"无需填写离岗说明"} \ No newline at end of file +NO_NEED_LEVEL_REMARK = {"code":"no_need_level_remark", "detail":"无需填写离岗说明"} +PHONE_F_WRONG = {"code":"phone_f_wrong", "detail":"手机号格式错误"} +PHONE_EXIST = {"code":"phone_exist", "detail":"手机号已存在"} +DH_PHOTO_FALI = {"code":"dh_photo_fail", "detail":"大华照片人脸提取失败"} \ No newline at end of file diff --git a/apps/hrm/migrations/0001_initial.py b/apps/hrm/migrations/0001_initial.py index 496c0e6c..9471866d 100644 --- a/apps/hrm/migrations/0001_initial.py +++ b/apps/hrm/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.6 on 2021-08-13 09:16 +# Generated by Django 3.2.12 on 2022-04-13 06:34 from django.conf import settings from django.db import migrations, models @@ -11,29 +11,75 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('system', '0003_remove_user_phone'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Employee', + name='NotWorkRemark', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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='删除标记')), - ('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='人员编号')), + ('not_work_date', models.DateField(verbose_name='未打卡日期')), + ('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='未打卡说明')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Employee', + 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='删除标记')), + ('name', models.CharField(max_length=20, verbose_name='姓名')), + ('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='手机号')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='邮箱号')), + ('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='人员编号')), ('photo', models.CharField(blank=True, max_length=1000, null=True, verbose_name='证件照')), - ('ID_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='身份证号')), + ('id_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='身份证号')), ('gender', models.CharField(default='男', max_length=10, verbose_name='性别')), ('signature', models.CharField(blank=True, max_length=200, null=True, verbose_name='签名图片')), + ('birthday', models.DateField(blank=True, null=True, verbose_name='出生年月日')), + ('qualification', models.CharField(blank=True, max_length=50, null=True, verbose_name='学历')), + ('job_state', models.IntegerField(choices=[(10, '在职'), (20, '离职')], default=1, verbose_name='在职状态')), + ('face_data', models.JSONField(blank=True, null=True, verbose_name='人脸识别数据')), + ('is_atwork', models.BooleanField(default=False, verbose_name='当前在岗')), + ('show_atwork', models.BooleanField(default=True, verbose_name='是否展示在岗状态')), + ('last_check_time', models.DateTimeField(blank=True, null=True, verbose_name='打卡时间')), + ('not_work_remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='当前未打卡说明')), + ('third_info', models.JSONField(blank=True, default=dict, null=True, verbose_name='三方信息')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_belong_dept', to='system.dept', verbose_name='所属部门')), ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='employee_user', to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='employee_user', to=settings.AUTH_USER_MODEL, verbose_name='系统账号')), ], options={ 'verbose_name': '员工补充信息', 'verbose_name_plural': '员工补充信息', }, ), + migrations.CreateModel( + name='ClockRecord', + 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='删除标记')), + ('type', models.PositiveSmallIntegerField(choices=[(10, '上班打卡')], default=10, verbose_name='打卡类型')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), ] diff --git a/apps/hrm/migrations/0002_auto_20210924_1127.py b/apps/hrm/migrations/0002_auto_20210924_1127.py deleted file mode 100644 index 89485c70..00000000 --- a/apps/hrm/migrations/0002_auto_20210924_1127.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2.6 on 2021-09-24 03:27 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('system', '0003_auto_20210812_0909'), - ('hrm', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='employee', - name='academic', - field=models.CharField(blank=True, max_length=50, null=True, verbose_name='学历'), - ), - migrations.AddField( - model_name='employee', - name='birthdate', - field=models.DateField(blank=True, null=True, verbose_name='出生年月'), - ), - migrations.AddField( - model_name='employee', - name='job', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.position', verbose_name='岗位'), - ), - migrations.AddField( - model_name='employee', - name='jobstate', - field=models.IntegerField(choices=[(1, '在职'), (2, '离职')], default=1, verbose_name='在职状态'), - ), - ] diff --git a/apps/hrm/migrations/0003_employee_face_data.py b/apps/hrm/migrations/0003_employee_face_data.py deleted file mode 100644 index 1823ad20..00000000 --- a/apps/hrm/migrations/0003_employee_face_data.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.6 on 2021-10-18 05:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hrm', '0002_auto_20210924_1127'), - ] - - operations = [ - migrations.AddField( - model_name='employee', - name='face_data', - field=models.JSONField(blank=True, null=True, verbose_name='人脸识别数据'), - ), - ] diff --git a/apps/hrm/migrations/0004_clockrecord.py b/apps/hrm/migrations/0004_clockrecord.py deleted file mode 100644 index 97ae8896..00000000 --- a/apps/hrm/migrations/0004_clockrecord.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2.9 on 2022-01-21 06:45 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('hrm', '0003_employee_face_data'), - ] - - operations = [ - migrations.CreateModel( - name='ClockRecord', - fields=[ - ('id', models.BigAutoField(auto_created=True, 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='删除标记')), - ('type', models.PositiveSmallIntegerField(choices=[(10, '上班打卡')], default=10, verbose_name='打卡类型')), - ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), - ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/apps/hrm/migrations/0005_auto_20220126_1351.py b/apps/hrm/migrations/0005_auto_20220126_1351.py deleted file mode 100644 index d6bd2c30..00000000 --- a/apps/hrm/migrations/0005_auto_20220126_1351.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.2.9 on 2022-01-26 05:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('hrm', '0004_clockrecord'), - ] - - operations = [ - migrations.RenameField( - model_name='employee', - old_name='birthdate', - new_name='birthday', - ), - migrations.RenameField( - model_name='employee', - old_name='ID_number', - new_name='id_number', - ), - migrations.RenameField( - model_name='employee', - old_name='jobstate', - new_name='job_state', - ), - migrations.RenameField( - model_name='employee', - old_name='academic', - new_name='qualification', - ), - migrations.RemoveField( - model_name='employee', - name='job', - ), - ] diff --git a/apps/hrm/migrations/0006_auto_20220217_2155.py b/apps/hrm/migrations/0006_auto_20220217_2155.py deleted file mode 100644 index 15ed4781..00000000 --- a/apps/hrm/migrations/0006_auto_20220217_2155.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.9 on 2022-02-17 13:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hrm', '0005_auto_20220126_1351'), - ] - - operations = [ - migrations.AddField( - model_name='employee', - name='is_atwork', - field=models.BooleanField(default=False, verbose_name='当前在岗'), - ), - migrations.AddField( - model_name='employee', - name='last_check_time', - field=models.DateTimeField(blank=True, null=True, verbose_name='打卡时间'), - ), - ] diff --git a/apps/hrm/migrations/0007_auto_20220218_0843.py b/apps/hrm/migrations/0007_auto_20220218_0843.py deleted file mode 100644 index 36752f4a..00000000 --- a/apps/hrm/migrations/0007_auto_20220218_0843.py +++ /dev/null @@ -1,41 +0,0 @@ -# Generated by Django 3.2.9 on 2022-02-18 00:43 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('hrm', '0006_auto_20220217_2155'), - ] - - operations = [ - migrations.AddField( - model_name='employee', - name='not_work_remark', - field=models.CharField(blank=True, max_length=200, null=True, verbose_name='当前未打卡说明'), - ), - migrations.CreateModel( - name='NotWorkRemark', - fields=[ - ('id', models.BigAutoField(auto_created=True, 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='删除标记')), - ('year', models.PositiveSmallIntegerField(default=2022, verbose_name='年')), - ('month', models.PositiveSmallIntegerField(default=2, verbose_name='月')), - ('day', models.PositiveSmallIntegerField(default=1, verbose_name='日')), - ('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='未打卡说明')), - ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), - ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/apps/hrm/migrations/0008_auto_20220222_1112.py b/apps/hrm/migrations/0008_auto_20220222_1112.py deleted file mode 100644 index 406a088c..00000000 --- a/apps/hrm/migrations/0008_auto_20220222_1112.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2.9 on 2022-02-22 03:12 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('hrm', '0007_auto_20220218_0843'), - ] - - operations = [ - migrations.RemoveField( - model_name='notworkremark', - name='day', - ), - migrations.RemoveField( - model_name='notworkremark', - name='month', - ), - migrations.RemoveField( - model_name='notworkremark', - name='year', - ), - migrations.AddField( - model_name='notworkremark', - name='not_work_date', - field=models.DateField(default=django.utils.timezone.now, verbose_name='未打卡日期'), - preserve_default=False, - ), - ] diff --git a/apps/hrm/migrations/0009_employee_show_atwork.py b/apps/hrm/migrations/0009_employee_show_atwork.py deleted file mode 100644 index 83650ec0..00000000 --- a/apps/hrm/migrations/0009_employee_show_atwork.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.9 on 2022-03-17 03:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hrm', '0008_auto_20220222_1112'), - ] - - operations = [ - migrations.AddField( - model_name='employee', - name='show_atwork', - field=models.BooleanField(default=True, verbose_name='是否展示在岗状态'), - ), - ] diff --git a/apps/hrm/models.py b/apps/hrm/models.py index 23c55555..be204654 100644 --- a/apps/hrm/models.py +++ b/apps/hrm/models.py @@ -36,7 +36,7 @@ class Employee(CommonBModel): show_atwork = models.BooleanField('是否展示在岗状态', default=True) last_check_time = models.DateTimeField('打卡时间', null=True, blank=True) not_work_remark = models.CharField('当前未打卡说明', null=True, blank=True, max_length=200) - + third_info = models.JSONField('三方信息', default=dict, null=True, blank=True) class Meta: verbose_name = '员工补充信息' verbose_name_plural = verbose_name @@ -44,6 +44,15 @@ class Employee(CommonBModel): def __str__(self): return self.name +# class Card(CommonAModel): +# """ +# 卡 +# """ +# CARD_FACE = 10 +# CARD_LOCATION = 20 + + + class NotWorkRemark(CommonAModel): """ 离岗说明 diff --git a/apps/hrm/serializers.py b/apps/hrm/serializers.py index bee348bb..73a14b0c 100644 --- a/apps/hrm/serializers.py +++ b/apps/hrm/serializers.py @@ -1,18 +1,22 @@ -from apps.system.models import User +from apps.hrm.errors import DH_PHOTO_FALI, PHONE_EXIST, PHONE_F_WRONG +from apps.system.models import Dept, User from rest_framework.serializers import ModelSerializer from rest_framework import serializers from apps.utils.serializers import CustomModelSerializer from apps.utils.constants import EXCLUDE_FIELDS +from apps.utils.tools import rannum, ranstr from .models import ClockRecord, Employee, NotWorkRemark from apps.system.serializers import DeptSimpleSerializer,UserSimpleSerializer +from django.db import transaction +from apps.utils.dahua import dhClient +from apps.third.tapis import dhapis +import re +from server.settings import DEBUG +from rest_framework.exceptions import ParseError +from datetime import datetime, timedelta -class EmployeeSerializer(CustomModelSerializer): - belong_dept_ = DeptSimpleSerializer(source='user.belong_dept', read_only=True) - class Meta: - model = Employee - exclude = EXCLUDE_FIELDS + ['face_data'] - +class EmployeeBaseSerializer(CustomModelSerializer): def save(self, **kwargs): if self.validated_data.get('user', None): user = self.validated_data['user'] @@ -21,6 +25,174 @@ class EmployeeSerializer(CustomModelSerializer): return super().save(**kwargs) +def phone_check(phone): + re_phone = '^1[358]\d{9}$|^147\d{8}$|^176\d{8}$' + if not re.match(re_phone, phone): + raise serializers.ValidationError(**PHONE_F_WRONG) + return phone + +class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer): + phone = serializers.CharField(label="手机号", validators=[phone_check]) + class Meta: + model = Employee + exclude = EXCLUDE_FIELDS + ['face_data', + 'is_atwork', 'last_check_time', + 'not_work_remark', 'third_info'] + extra_kwargs = { + 'phone': {'required': True}, + 'number': {'required': True}, + 'photo': {'required': True}, + 'id_number': {'required': True}, + } + + @transaction.atomic + def create(self, validated_data): + instance = super().create(validated_data) + if dhClient: + # 创建人员 + _, res = dhClient.request(**dhapis['gen_person_id']) + personId = res['id'] + departmentId = 1 + if instance.belong_dept: + try: + departmentId = instance.belong_dept.third_info['dh_id'] + except: + pass + json_data = { + "service":"ehs", + "id": personId, + "name": instance.name, + "code": instance.number, + "paperType": 111, + "paperNumber": instance.id_number, + "paperAddress": "default", + "departmentId": departmentId, + "phone": instance.phone, + "email": instance.email, + "sex": 1 if instance.gender == '男' else 2 + } + _, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=instance.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) + # 开人脸卡 + _, res = dhClient.request(**dhapis['gen_card_id']) + cardId = res['id'] + cardNumber = instance.id[:8] + rannum(2) + now = datetime.now() + startDate = now.strftime("%Y-%m-%d %H:%M:%S") + endDate = (datetime(year=now.year+50, + month=now.month, day=1)).strftime("%Y-%m-%d %H:%M:%S") + json_data = { + "id": cardId, + "cardNumber": cardNumber, + "category": 0, + "cardType": 0, + "personId": personId, + "departmentId":departmentId, + "startDate": startDate, + "endDate": endDate + } + _, res = dhClient.request(**dhapis['card_add'], json=json_data) + instance.third_info = {'dh_id':personId, + 'dh_photo':dh_photo, 'dh_face_card':res['id']} + instance.save() + return instance + + @transaction.atomic + def update(self, instance, validated_data): + old_photo = instance.photo + instance = super().update(instance, validated_data) + departmentId = 1 + if instance.belong_dept: + try: + departmentId = instance.belong_dept.third_info['dh_id'] + except: + pass + if dhClient: + third_info = instance.third_info + dh_id = instance.third_info['dh_id'] + dh_photo = third_info['dh_photo'] + json_data = { + "service":"ehs", + "id": dh_id, + "name": instance.name, + "code": instance.number, + "paperType": 111, + "paperNumber": instance.id_number, + "paperAddress": "default", + "departmentId": departmentId, + "phone": instance.phone, + "email": instance.email, + "sex": 1 if instance.gender == '男' else 2, + "biosignatureTypeList":[3], + "personBiosignatures":[{ + "type":3, + "index":1, + "path": third_info['dh_photo'] + }] + } + if instance.photo != old_photo: + _, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=instance.photo) + dh_photo = res["fileUrl"] + json_data.update( + { + "biosignatureTypeList":[3], + "personBiosignatures":[{ + "type":3, + "index":1, + "path": dh_photo + }] + } + ) + third_info['dh_photo'] = dh_photo + dhClient.request(**dhapis['person_update'], json=json_data) + # 开人脸卡 + if instance.job_state in [Employee.JOB_ON]: + if not third_info.get('dh_face_card', None): + _, res = dhClient.request(**dhapis['gen_card_id']) + cardId = res['id'] + cardNumber = instance.id[:8] + rannum(2) + now = datetime.now() + startDate = now.strftime("%Y-%m-%d %H:%M:%S") + endDate = (datetime(year=now.year+50, + month=now.month, day=1)).strftime("%Y-%m-%d %H:%M:%S") + json_data = { + "id": cardId, + "cardNumber": cardNumber, + "category": 0, + "cardType": 0, + "personId": third_info['dh_id'], + "departmentId":departmentId, + "startDate": startDate, + "endDate": endDate + } + _, res = dhClient.request(**dhapis['card_add'], json=json_data) + third_info['dh_face_card'] = cardNumber + instance.save() + return instance + +class ChannelAuthoritySerializer(serializers.Serializer): + pks = serializers.ListField(child=serializers.CharField(max_length=20), label="员工ID列表") + channels = serializers.ListField(child=serializers.CharField(max_length=20), label="门通道ID列表") + +class EmployeeSerializer(EmployeeBaseSerializer): + belong_dept_ = DeptSimpleSerializer(source='belong_dept', read_only=True) + class Meta: + model = Employee + exclude = ['face_data'] + read_only_fields = ['is_atwork', 'last_check_time', 'not_work_remark'] + + class EmployeeNotWorkRemarkSerializer(ModelSerializer): diff --git a/apps/hrm/signals.py b/apps/hrm/signals.py index a49bdfd5..65a6b89b 100644 --- a/apps/hrm/signals.py +++ b/apps/hrm/signals.py @@ -4,7 +4,13 @@ from django.dispatch import receiver from apps.hrm.models import Employee from django.conf import settings -# @receiver(post_save, sender=User) -# def createEmployee(sender, instance, created, **kwargs): -# if created: -# Employee.objects.get_or_create(user=instance) +@receiver(post_save, sender=User) +def updateEmployee(sender, instance, created, **kwargs): + # if created: + # 如果账号所属部门有变动, 更新关联人员的所属部门 + ep = Employee.objects.filter(user=instance).first() + if ep: + if ep.belong_dept and ep.belong_dept != instance.belong_dept: + ep.belong_dept = instance.belong_dept + ep.save() + diff --git a/apps/hrm/urls.py b/apps/hrm/urls.py index b0ced0c8..d123fd41 100644 --- a/apps/hrm/urls.py +++ b/apps/hrm/urls.py @@ -1,15 +1,17 @@ from rest_framework import urlpatterns -from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin, NotWorkRemarkViewSet +from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, NotWorkRemarkViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter +API_BASE_URL = 'api/hrm/' +HTML_BASE_URL = 'hrm/' + router = DefaultRouter() router.register('employee', EmployeeViewSet, basename='employee') router.register('clock_record', ClockRecordViewSet, basename='clock_record') router.register('not_work_remark', NotWorkRemarkViewSet, basename='not_work_reamrk') urlpatterns = [ - path('facelogin/', FaceLogin.as_view()), - path('', include(router.urls)), + path(API_BASE_URL, include(router.urls)), ] diff --git a/apps/hrm/views.py b/apps/hrm/views.py index 0565d3dc..646e70d2 100644 --- a/apps/hrm/views.py +++ b/apps/hrm/views.py @@ -5,7 +5,7 @@ from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateMo from apps.hrm.errors import NO_NEED_LEVEL_REMARK from apps.hrm.filters import ClockRecordFilterSet, EmployeeFilterSet, NotWorkRemarkFilterSet from apps.hrm.models import ClockRecord, Employee, NotWorkRemark -from apps.hrm.serializers import ClockRecordListSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer, NotWorkRemarkListSerializer +from apps.hrm.serializers import ClockRecordListSerializer, ChannelAuthoritySerializer, EmployeeCreateUpdateSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, NotWorkRemarkListSerializer @@ -17,9 +17,14 @@ from apps.system.models import User from apps.system.serializers import UserSimpleSerializer from rest_framework.permissions import AllowAny from rest_framework.decorators import action +from apps.utils.serializers import PkSerializer from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet from rest_framework.exceptions import ParseError - +from django.db import transaction +from datetime import datetime +from rest_framework import serializers +from apps.utils.dahua import dhClient +from apps.third.tapis import dhapis # Create your views here. class EmployeeViewSet(CustomModelViewSet): @@ -30,22 +35,12 @@ class EmployeeViewSet(CustomModelViewSet): select_related_fields = ['user'] filterset_class = EmployeeFilterSet serializer_class = EmployeeSerializer + create_serializer_class = EmployeeCreateUpdateSerializer + update_serializer_class = EmployeeCreateUpdateSerializer + partial_update_serializer_class = EmployeeCreateUpdateSerializer search_fields = ['name', 'number', 'user__username'] ordering = ['-pk'] - def update(self, request, *args, **kwargs): - partial = kwargs.pop('partial', False) - instance = self.get_object() - data = request.data - serializer = self.get_serializer(instance, data=data, partial=partial) - serializer.is_valid(raise_exception=True) - photo = data.get('photo', None) - if instance.photo != photo: - # 调取大华接口 - return Response() - serializer.save() - return Response() - @action(methods=['post'], detail=True, perms_map={'post': 'employee_notworkremark'} , serializer_class=EmployeeNotWorkRemarkSerializer) def not_work_remark(self, request, pk=None): @@ -77,6 +72,37 @@ class EmployeeViewSet(CustomModelViewSet): return Response() raise ParseError(**NO_NEED_LEVEL_REMARK) + @transaction.atomic + @action(methods=['post'], detail=False, perms_map={'post': 'employee_channel_authority'} + , serializer_class=ChannelAuthoritySerializer) + def channel_authority(self, request, pk=None): + """门通道授权 + + 门通道授权 + """ + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + objs = Employee.objects.filter(pk__in=vdata['pks']) + infos = objs.values('third_info') + cards = [] + for i in infos: + if isinstance(i['third_info'], dict) and 'dh_face_card' in i['third_info']: + cards.append(i['third_info']['dh_face_card']) + details = [] + for i in vdata['channels']: + details.append({ + "privilegeType": 1, + "resouceCode": i + }) + json_data = { + "cardNumbers": cards, + "timeQuantumId": 1, + "cardPrivilegeDetails": details + } + dhClient.request(**dhapis['card_door_authority'], json=json_data) + objs.update(third_info__dh_channels = vdata['channels']) + return Response() class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): """ diff --git a/apps/monitor/migrations/0003_alter_drfrequestlog_view_method.py b/apps/monitor/migrations/0003_alter_drfrequestlog_view_method.py new file mode 100644 index 00000000..f8605082 --- /dev/null +++ b/apps/monitor/migrations/0003_alter_drfrequestlog_view_method.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2022-04-18 02:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('monitor', '0002_alter_drfrequestlog_id'), + ] + + operations = [ + migrations.AlterField( + model_name='drfrequestlog', + name='view_method', + field=models.CharField(blank=True, db_index=True, max_length=20, null=True), + ), + ] diff --git a/apps/monitor/models.py b/apps/monitor/models.py index e35ca0a8..057a1f8c 100644 --- a/apps/monitor/models.py +++ b/apps/monitor/models.py @@ -28,7 +28,7 @@ class DrfRequestLog(BaseModel): help_text="执行视图", ) view_method = models.CharField( - max_length=6, + max_length=20, null=True, blank=True, db_index=True, diff --git a/apps/system/errors.py b/apps/system/errors.py index 838c0b1b..bfac3206 100644 --- a/apps/system/errors.py +++ b/apps/system/errors.py @@ -1,8 +1,7 @@ SCHEDULE_WRONG = {"code":"schedule_wrong", "detail":"时间策略有误"} PASSWORD_NOT_SAME = {"code":"password_not_same", "detail":"新旧密码不一致"} OLD_PASSWORD_WRONG = {"code":"old_password_wrong", "detail":"旧密码错误"} -PHONE_F_WRONG = {"code":"phone_f_wrong", "detail":"手机号格式错误"} -PHONE_EXIST = {"code":"phone_exist", "detail":"手机号已存在"} + USERNAME_EXIST = {"code":"username_exist", "detail":"账户已存在"} ROLE_NAME_EXIST = {"code":"role_name_exist", "detail":"角色名已存在"} ROLE_CODE_EXIST = {"code":"role_code_exist", "detail":"角色标识已存在"} \ No newline at end of file diff --git a/apps/system/serializers.py b/apps/system/serializers.py index c5481af7..59437a3f 100644 --- a/apps/system/serializers.py +++ b/apps/system/serializers.py @@ -3,7 +3,7 @@ import re from django_celery_beat.models import PeriodicTask, CrontabSchedule, IntervalSchedule from rest_framework import serializers from django_celery_results.models import TaskResult -from apps.system.errors import PHONE_EXIST, PHONE_F_WRONG, ROLE_CODE_EXIST, ROLE_NAME_EXIST, USERNAME_EXIST +from apps.system.errors import ROLE_CODE_EXIST, ROLE_NAME_EXIST, USERNAME_EXIST from apps.utils.serializers import CustomModelSerializer from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE from .models import (Dict, DictType, File, Dept, Permission, Post, @@ -210,8 +210,20 @@ class DeptCreateUpdateSerializer(CustomModelSerializer): @transaction.atomic def update(self, instance, validated_data): - if instance.name != validated_data.get('name', ''): - from apps.utils.dahua import dhClient + from apps.utils.dahua import dhClient + third_info = instance.third_info + if dhClient and not third_info.get('dh_id', False): + # 如果dh_id 不存在 + data = { + "parentId":1, + "name":validated_data['name'], + "service":"ehs" + } + _, res = dhClient.request(**dhapis['dept_create'],json=data) + third_info['dh_id'] = res['id'] + instance.third_info = third_info + instance.save() + elif instance.name != validated_data.get('name', ''): if dhClient and instance.third_info.get('dh_id', False): data = { "id":instance.third_info['dh_id'], diff --git a/apps/third/tapis.py b/apps/third/tapis.py index 3da430df..e09ddc1b 100644 --- a/apps/third/tapis.py +++ b/apps/third/tapis.py @@ -11,7 +11,51 @@ dhapis = { "dept_update":{ "url":"/evo-apigw/evo-brm/1.0.0/department/update", "method":"put" - } + }, + "gen_person_id":{ + "url":"/evo-apigw/evo-brm/1.0.0/person/generate-id", + "method":"get" + }, + "person_add": { + "url":"/evo-apigw/evo-brm/1.2.0/person/subsystem/add", + "method":"post" + }, + "person_update": { + "url":"/evo-apigw/evo-brm/1.2.0/person/subsystem/update", + "method":"put" + }, + "person_img_upload": { + "url":"/evo-apigw/evo-brm/1.2.0/person/upload/img", + "method":"post" + }, + "person_detail": { + "url":"/evo-apigw/evo-brm/1.0.0/person/subsystem/{id}", + "method":"get" + }, + "device_list": { + "url":"/evo-apigw/evo-brm/1.2.0/device/subsystem/page", + "method":"get" + }, + "device_detail": { + "url":"/evo-apigw/evo-brm/1.0.0/device/{deviceCode}", + "method":"get" + }, + "channel_list": { + "url":"/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page", + "method":"post" + }, + "card_add": { + "url":"/evo-apigw/evo-brm/1.2.0/card/add", + "method":"post" + }, + "gen_card_id": { + "url":"/evo-apigw/evo-brm/1.0.0/card/generate-id", + "method":"get" + }, + "card_door_authority": { + "url":"/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/doorAuthority", + "method":"post" + }, } # 寻息API接口 diff --git a/apps/third/views.py b/apps/third/views.py index ec139e7a..2178ee09 100644 --- a/apps/third/views.py +++ b/apps/third/views.py @@ -3,6 +3,7 @@ from apps.third.tapis import dhapis, xxapis from apps.third.erros import TAPI_CODE_WRONG from apps.utils.dahua import dhClient from apps.utils.errors import XX_REQUEST_ERROR +from apps.utils.mixins import MyLoggingMixin from apps.utils.xunxi import xxClient from rest_framework.response import Response from rest_framework.views import APIView @@ -15,45 +16,19 @@ from apps.third.serializers import RequestCommonSerializer # Create your views here. -class DahuaTestView(APIView): +class DahuaTestView(MyLoggingMixin, APIView): """ 大华测试接口 """ permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): - data = { - "data":{ - "channelId": "1001339$1$0$0", - "streamType": "1", - "type": "hls" - } - } - # ok, res = dhClient.request( - # url='/evo-apigw/admin/API/video/stream/realtime', method='post', json=data) - ok, res = dhClient.request(url='/evo-apigw/evo-brm/1.2.0/department/tree', - method='get') - # data = { - # "pageNum":1, - # "pageSize":100, - # "isOnline":1, - # "showChildNodeData":1, - # "categorys":[8] - - # } - # res = dhClient.request( - # url='/evo-apigw/evo-brm/1.0.0/device/subsystem/page', method='post', json=data) - # data = { - # "channelCodeList": ["1001382$7$0$0"] - # } - # res = dhClient.request( - # url='/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/channelControl/closeDoor', method='post', json=data) - if ok == 'success': - return Response(res) - elif ok == 'fail': - raise ParseError(**res) - else: - raise APIException(**res) + # file_path_rela = '/media/default/avatar.png' + # _, res = dhClient.request(**dhapis['person_img_upload'], file_path_rela=file_path_rela) + _,res = dhClient.request( + url='/evo-apigw/evo-brm/1.0.0/person/subsystem/{}'.format(2059335), + method='get') + return Response(res) class XxTestView(APIView): diff --git a/apps/utils/dahua.py b/apps/utils/dahua.py index 95feff48..02f11ca9 100644 --- a/apps/utils/dahua.py +++ b/apps/utils/dahua.py @@ -7,6 +7,7 @@ from server import settings import json import time from rest_framework.exceptions import APIException, ParseError +import os requests.packages.urllib3.disable_warnings() class DhClient: @@ -63,21 +64,31 @@ class DhClient: 自定义销毁 """ self.isRuning = False - self.t.join() + # self.t.join() - def request(self, url:str, method:str, params=dict(), json=dict(), timeout=20, raise_exception=True): + def request(self, url:str, method:str, params=dict(), json=dict(), timeout=4, file_path_rela=None, raise_exception=True): if self.isGetingToken: req_num = 0 while True: time.sleep(0.5) if not self.isGetingToken: - self.request(url, method, params, json, timeout) + self.request(url, method, params, json, timeout, file_path_rela, raise_exception) req_num = req_num + 1 if req_num > 4: break else: - r = getattr(requests, method)('{}{}'.format(settings.DAHUA_BASE_URL, url) - , headers = self.headers, params=params, json=json, verify=False) + files = None + if file_path_rela: # 相对路径 + files = {'file': open(settings.BASE_DIR + file_path_rela, 'rb')} + try: + if params: + url = url.format(**params) + r = getattr(requests, method)('{}{}'.format(settings.DAHUA_BASE_URL, url) + , headers = self.headers, params=params, json=json, timeout=timeout, files=files, verify=False) + except: + if raise_exception: + raise APIException(**DH_REQUEST_ERROR) + return 'error', DH_REQUEST_ERROR # if settings.DEBUG: # print_roundtrip(r) if r.status_code == 200: @@ -87,7 +98,7 @@ class DhClient: ret = r.json() if ret.get('code') == '27001007': self.get_token() # 重新获取token - self.request(url, method, params, json, timeout) # 重新请求 + self.request(url, method, params, json, timeout, file_path_rela, raise_exception) else: if ret['code'] not in ['0', '100', '00000', '1000', 0, 100, 1000]: detail = '大华错误:' + '{}|{}{}'.format(str(ret['code']), ret.get('errMsg',''), ret.get('desc', '')) diff --git a/apps/utils/mixins.py b/apps/utils/mixins.py index 945367a4..eeeea018 100644 --- a/apps/utils/mixins.py +++ b/apps/utils/mixins.py @@ -68,7 +68,7 @@ class MyLoggingMixin(object): CLEANED_SUBSTITUTE = "********************" # logging_methods = "__all__" - logging_methods = ['POST', 'PUT', 'DELETE', 'PATCH'] + logging_methods = '__all__' sensitive_fields = {} def __init__(self, *args, **kwargs): @@ -100,6 +100,7 @@ class MyLoggingMixin(object): def handle_exception(self, exc): response = super().handle_exception(exc) + self.log["errors"] = traceback.format_exc() return response def finalize_response(self, request, response, *args, **kwargs): @@ -230,7 +231,7 @@ class MyLoggingMixin(object): """ return ( self.logging_methods == "__all__" or request.method in self.logging_methods - ) + ) and (response.status_code not in (401, 403)) def _clean_data(self, data): """ diff --git a/apps/utils/tools.py b/apps/utils/tools.py index e61f4867..18db1b17 100644 --- a/apps/utils/tools.py +++ b/apps/utils/tools.py @@ -1,5 +1,6 @@ import textwrap -import requests +import random +import string def print_roundtrip(response, *args, **kwargs): format_headers = lambda d: '\n'.join(f'{k}: {v}' for k, v in d.items()) @@ -19,4 +20,12 @@ def print_roundtrip(response, *args, **kwargs): res=response, reqhdrs=format_headers(response.request.headers), reshdrs=format_headers(response.headers), - )) \ No newline at end of file + )) + +def ranstr(num): + salt = ''.join(random.sample(string.ascii_lowercase + string.digits, num)) + return salt + +def rannum(num): + salt = ''.join(random.sample(string.digits, num)) + return salt \ No newline at end of file diff --git a/apps/utils/xunxi.py b/apps/utils/xunxi.py index 9f0fdfa1..a2827946 100644 --- a/apps/utils/xunxi.py +++ b/apps/utils/xunxi.py @@ -58,7 +58,7 @@ class XxClient: self.isRuning = False self.t.join() - def request(self, url:str, method:str='post', params=dict(), json=dict(), timeout=20, raise_exception=True): + def request(self, url:str, method:str='post', params=dict(), json=dict(), timeout=4, raise_exception=True): params['accessToken'] = self.token json['username'] = self.username json['buildId'] = settings.XX_BUILDID @@ -67,19 +67,19 @@ class XxClient: while True: time.sleep(0.5) if not self.isGetingToken: - self.request(url, method, params, json, timeout) + self.request(url, method, params, json, timeout, raise_exception) req_num = req_num + 1 if req_num > 4: break else: r = getattr(requests, method)('{}{}'.format(settings.XX_BASE_URL, url) - , params=params, json=json, verify=False) + , params=params, json=json, timeout=timeout, verify=False) # if settings.DEBUG: # print_roundtrip(r) ret = r.json() if ret.get('errorCode') == '1060000': self.get_token() # 重新获取token - self.request(url, method, params, json, timeout) # 重新请求 + self.request(url, method, params, json, timeout, raise_exception) # 重新请求 else: if ret['errorCode'] != 0: err_detail = dict(detail='寻息错误:' + '|'.join(ret['errorMsg']), diff --git a/media/default/test.png b/media/default/test.png new file mode 100644 index 00000000..84c16a87 Binary files /dev/null and b/media/default/test.png differ diff --git a/media/default/test2.jpg b/media/default/test2.jpg new file mode 100644 index 00000000..b40ddd78 Binary files /dev/null and b/media/default/test2.jpg differ diff --git a/server/settings.py b/server/settings.py index 8877fc43..fb773681 100644 --- a/server/settings.py +++ b/server/settings.py @@ -52,7 +52,8 @@ INSTALLED_APPS = [ 'apps.system', 'apps.auth1', 'apps.monitor', - 'apps.wf' + 'apps.wf', + 'apps.hrm' ] MIDDLEWARE = [ diff --git a/server/urls.py b/server/urls.py index 98748efe..d9660c97 100644 --- a/server/urls.py +++ b/server/urls.py @@ -47,6 +47,7 @@ urlpatterns = [ path('', include('apps.third.urls')), path('', include('apps.utils.urls')), path('', include('apps.develop.urls')), + path('', include('apps.hrm.urls')),