增加请求日志
This commit is contained in:
parent
a6f0e386c0
commit
680a0dceeb
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
NAME_OR_PASSWORD_WRONG = '账户名或密码错误'
|
||||
USERNAME_OR_PASSWORD_WRONG = {"code":"username_or_password_wrong", "detail":"账户名或密码错误"}
|
||||
|
|
@ -7,6 +7,7 @@ from rest_framework import status
|
|||
from django.contrib.auth import authenticate, login, logout
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from apps.auth1.errors import USERNAME_OR_PASSWORD_WRONG
|
||||
|
||||
|
||||
from apps.auth1.serializers import LoginSerializer
|
||||
|
|
@ -45,7 +46,7 @@ class LoginView(CreateAPIView):
|
|||
if user is not None:
|
||||
login(request, user)
|
||||
return Response()
|
||||
raise ParseError('账户名或密码错误', 'username_or_password_wrong')
|
||||
raise ParseError(**USERNAME_OR_PASSWORD_WRONG)
|
||||
|
||||
class LogoutView(APIView):
|
||||
authentication_classes = []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
class HrmConfig(AppConfig):
|
||||
name = 'apps.hrm'
|
||||
verbose_name = '人力资源管理'
|
||||
|
||||
def ready(self):
|
||||
import apps.hrm.signals
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
from django_filters import rest_framework as filters
|
||||
from apps.hrm.models import ClockRecord, Employee, NotWorkRemark
|
||||
|
||||
class ClockRecordFilterSet(filters.FilterSet):
|
||||
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
|
||||
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
|
||||
year = filters.NumberFilter(method='filter_year')
|
||||
month = filters.NumberFilter(method='filter_month')
|
||||
class Meta:
|
||||
model = ClockRecord
|
||||
fields = ['create_by', 'create_time_start', 'create_time_end', 'year', 'month']
|
||||
|
||||
def filter_year(self, queryset, name, value):
|
||||
return queryset.filter(create_time_date__year=value)
|
||||
|
||||
def filter_month(self, queryset, name, value):
|
||||
return queryset.filter(create_time_date__month=value)
|
||||
|
||||
class EmployeeFilterSet(filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = ['job_state', 'show_atwork']
|
||||
|
||||
|
||||
class NotWorkRemarkFilterSet(filters.FilterSet):
|
||||
year = filters.NumberFilter(method='filter_year')
|
||||
month = filters.NumberFilter(method='filter_month')
|
||||
class Meta:
|
||||
model = NotWorkRemark
|
||||
fields = ['year', 'month', 'user']
|
||||
|
||||
def filter_year(self, queryset, name, value):
|
||||
return queryset.filter(not_work_date__year=value)
|
||||
|
||||
def filter_month(self, queryset, name, value):
|
||||
return queryset.filter(not_work_date__month=value)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Generated by Django 3.2.6 on 2021-08-13 09:16
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Employee',
|
||||
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='删除标记')),
|
||||
('number', models.CharField(blank=True, max_length=50, null=True, unique=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='身份证号')),
|
||||
('gender', models.CharField(default='男', max_length=10, verbose_name='性别')),
|
||||
('signature', 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='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)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '员工补充信息',
|
||||
'verbose_name_plural': '员工补充信息',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# 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='在职状态'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# 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='人脸识别数据'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# 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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# 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',
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# 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='打卡时间'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# 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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# 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,
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# 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='是否展示在岗状态'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db.models.base import Model
|
||||
import django.utils.timezone as timezone
|
||||
from django.db.models.query import QuerySet
|
||||
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File,Position
|
||||
from utils.model import SoftModel, BaseModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
|
||||
|
||||
|
||||
class Employee(CommonAModel):
|
||||
"""
|
||||
员工信息
|
||||
"""
|
||||
JOB_ON = 1
|
||||
JOB_OFF = 2
|
||||
jobstate_choices = (
|
||||
(JOB_ON, '在职'),
|
||||
(JOB_OFF, '离职'),
|
||||
)
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee_user')
|
||||
number = models.CharField('人员编号', max_length=50,null=True, blank=True, unique=True)
|
||||
photo = models.CharField('证件照', max_length=1000, null=True, blank=True)
|
||||
id_number = models.CharField('身份证号', max_length=100, null=True, blank=True)
|
||||
gender = models.CharField('性别', max_length=10, default='男')
|
||||
signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
|
||||
birthday = models.DateField('出生年月', null=True, blank=True)
|
||||
qualification = models.CharField('学历', max_length=50, null=True, blank=True)
|
||||
job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
|
||||
face_data = models.JSONField('人脸识别数据', null=True, blank=True)
|
||||
is_atwork = models.BooleanField('当前在岗', default=False)
|
||||
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)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '员工补充信息'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class NotWorkRemark(CommonAModel):
|
||||
"""
|
||||
离岗说明
|
||||
"""
|
||||
not_work_date = models.DateField('未打卡日期')
|
||||
user = models.ForeignKey(User, verbose_name='用户', on_delete=models.CASCADE)
|
||||
remark = models.CharField('未打卡说明', null=True, blank=True, max_length=200)
|
||||
|
||||
class ClockRecord(CommonADModel):
|
||||
"""
|
||||
打卡记录
|
||||
"""
|
||||
ClOCK_WORK1 = 10
|
||||
type_choice = (
|
||||
(ClOCK_WORK1, '上班打卡'),
|
||||
)
|
||||
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
from apps.system.models import User
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework import serializers
|
||||
|
||||
from utils.mixins import DynamicFieldsSerializerMixin
|
||||
from .models import ClockRecord, Employee, NotWorkRemark
|
||||
from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
|
||||
|
||||
class EmployeeSerializer(DynamicFieldsSerializerMixin, ModelSerializer):
|
||||
name = serializers.CharField(source='user.name', read_only=True)
|
||||
dept_ = OrganizationSimpleSerializer(source='user.dept', read_only=True)
|
||||
class Meta:
|
||||
model = Employee
|
||||
exclude = ['face_data']
|
||||
|
||||
class EmployeeNotWorkRemarkSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = ['not_work_remark']
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
|
||||
|
||||
class FaceClockCreateSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
|
||||
class ClockRecordListSerializer(serializers.ModelSerializer):
|
||||
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
|
||||
class Meta:
|
||||
model = ClockRecord
|
||||
fields = '__all__'
|
||||
|
||||
class NotWorkRemarkListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = NotWorkRemark
|
||||
fields = '__all__'
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from django.conf import settings
|
||||
import uuid
|
||||
import face_recognition
|
||||
import os
|
||||
from apps.hrm.models import Employee
|
||||
from apps.hrm.tasks import update_all_user_facedata_cache
|
||||
from apps.system.models import User
|
||||
from django.core.cache import cache
|
||||
|
||||
class HRMService:
|
||||
|
||||
@classmethod
|
||||
def face_compare_from_base64(cls, base64_data):
|
||||
filename = str(uuid.uuid4())
|
||||
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(base64_data)
|
||||
try:
|
||||
unknown_picture = face_recognition.load_image_file(filepath)
|
||||
unknown_face_encoding = face_recognition.face_encodings(unknown_picture, num_jitters=2)[0]
|
||||
os.remove(filepath)
|
||||
except:
|
||||
os.remove(filepath)
|
||||
return None, '识别失败,请调整位置'
|
||||
|
||||
# 匹配人脸库
|
||||
face_datas = cache.get('face_datas')
|
||||
if face_datas is None:
|
||||
update_all_user_facedata_cache()
|
||||
face_datas = cache.get('face_datas')
|
||||
face_users = cache.get('face_users')
|
||||
results = face_recognition.compare_faces(face_datas,
|
||||
unknown_face_encoding, tolerance=0.45)
|
||||
for index, value in enumerate(results):
|
||||
if value:
|
||||
# 识别成功
|
||||
user = User.objects.get(id=face_users[index])
|
||||
return user, ''
|
||||
return None, '人脸未匹配,请调整位置'
|
||||
|
||||
@classmethod
|
||||
def get_facedata_from_img(cls, img_path):
|
||||
try:
|
||||
photo_path = settings.BASE_DIR + img_path
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me, num_jitters=2)[0]
|
||||
face_data_list = my_face_encoding.tolist()
|
||||
return face_data_list, ''
|
||||
except:
|
||||
return None, '人脸数据获取失败请重新上传图片'
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from django.db.models.signals import post_save
|
||||
from apps.system.models import User
|
||||
from django.dispatch import receiver
|
||||
from apps.hrm.models import Employee
|
||||
from django.conf import settings
|
||||
import face_recognition
|
||||
import logging
|
||||
logger = logging.getLogger('log')
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def createEmployee(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
Employee.objects.get_or_create(user=instance)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from celery import shared_task
|
||||
from apps.hrm.models import Employee
|
||||
from django.core.cache import cache
|
||||
|
||||
|
||||
@shared_task
|
||||
def update_all_employee_not_atwork():
|
||||
"""
|
||||
将所有员工设为非在岗状态
|
||||
"""
|
||||
Employee.objects.all().update(is_atwork=False, last_check_time = None, not_work_remark=None)
|
||||
|
||||
@shared_task
|
||||
def update_all_user_facedata_cache():
|
||||
"""
|
||||
更新人脸数据缓存
|
||||
"""
|
||||
facedata_queyset = Employee.objects.filter(face_data__isnull=False,
|
||||
user__is_active=True).values('user', 'face_data')
|
||||
face_users = []
|
||||
face_datas = []
|
||||
for i in facedata_queyset:
|
||||
face_users.append(i['user'])
|
||||
face_datas.append(i['face_data'])
|
||||
cache.set('face_users', face_users, timeout=None)
|
||||
cache.set('face_datas', face_datas, timeout=None)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
from rest_framework import urlpatterns
|
||||
from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin, NotWorkRemarkViewSet
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
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)),
|
||||
]
|
||||
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin
|
||||
from apps.hrm.filters import ClockRecordFilterSet, EmployeeFilterSet, NotWorkRemarkFilterSet
|
||||
from apps.hrm.services import HRMService
|
||||
from apps.hrm.tasks import update_all_user_facedata_cache
|
||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||
from apps.hrm.models import ClockRecord, Employee, NotWorkRemark
|
||||
from apps.hrm.serializers import ClockRecordListSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer, NotWorkRemarkListSerializer
|
||||
|
||||
|
||||
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework import status
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework import exceptions
|
||||
from apps.system.models import User
|
||||
from apps.system.serializers import UserSimpleSerializer
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.decorators import action
|
||||
|
||||
|
||||
# Create your views here.
|
||||
class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
"""
|
||||
员工详细信息
|
||||
"""
|
||||
perms_map = {'get': '*', 'put': 'employee_update'}
|
||||
queryset = Employee.objects.all()
|
||||
filterset_class = EmployeeFilterSet
|
||||
serializer_class = EmployeeSerializer
|
||||
search_fields = ['user__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:
|
||||
f_l, msg = HRMService.get_facedata_from_img(img_path=photo)
|
||||
if f_l:
|
||||
serializer.save(update_by=request.user, face_data = f_l)
|
||||
# 更新人脸缓存
|
||||
update_all_user_facedata_cache.delay()
|
||||
return Response()
|
||||
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
|
||||
serializer.save(update_by=request.user)
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post': 'employee_notworkremark'}
|
||||
, serializer_class=EmployeeNotWorkRemarkSerializer)
|
||||
def not_work_remark(self, request, pk=None):
|
||||
"""
|
||||
填写离岗说明
|
||||
"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
obj = self.get_object()
|
||||
if not obj.is_atwork:
|
||||
remark = request.data.get('not_work_remark', '')
|
||||
obj.not_work_remark = remark
|
||||
obj.save()
|
||||
now = timezone.now()
|
||||
instance, created = NotWorkRemark.objects.get_or_create(
|
||||
not_work_date = now.date(),
|
||||
user = obj.user,
|
||||
defaults={
|
||||
"not_work_date":now.date(),
|
||||
"user":obj.user,
|
||||
"remark":remark,
|
||||
"create_by":request.user,
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
instance.remark = remark
|
||||
instance.update_by = request.user
|
||||
instance.save()
|
||||
return Response()
|
||||
return Response('无需填写离岗说明', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
|
||||
"""
|
||||
打卡记录
|
||||
"""
|
||||
perms_map = {'get':'*', 'post':'*'}
|
||||
authentication_classes = []
|
||||
permission_classes = [AllowAny]
|
||||
queryset = ClockRecord.objects.select_related('create_by').all()
|
||||
serializer_class = ClockRecordListSerializer
|
||||
filterset_class = ClockRecordFilterSet
|
||||
ordering = ['-pk']
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'create':
|
||||
return FaceClockCreateSerializer
|
||||
return super().get_serializer_class()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
now = timezone.now()
|
||||
now_local = timezone.localtime()
|
||||
if 8<=now_local.hour<=17:
|
||||
base64_data = base64.urlsafe_b64decode(tran64(
|
||||
request.data.get('base64').replace(' ', '+')))
|
||||
user, msg = HRMService.face_compare_from_base64(base64_data)
|
||||
if user:
|
||||
ins, created = ClockRecord.objects.get_or_create(
|
||||
create_by = user, create_time__hour__range = [8,18],
|
||||
create_time__year=now_local.year, create_time__month=now_local.month,
|
||||
create_time__day=now_local.day,
|
||||
defaults={
|
||||
'type':ClockRecord.ClOCK_WORK1,
|
||||
'create_by':user,
|
||||
'create_time':now
|
||||
})
|
||||
if not created:
|
||||
ins.update_time = now
|
||||
ins.save()
|
||||
# 设为在岗
|
||||
Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now)
|
||||
return Response(UserSimpleSerializer(instance=user).data)
|
||||
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class NotWorkRemarkViewSet(ListModelMixin, GenericViewSet):
|
||||
"""
|
||||
离岗说明
|
||||
"""
|
||||
perms_map = {'get':'*'}
|
||||
queryset = NotWorkRemark.objects.select_related('user').all()
|
||||
serializer_class = NotWorkRemarkListSerializer
|
||||
filterset_class = NotWorkRemarkFilterSet
|
||||
ordering = ['-pk']
|
||||
|
||||
|
||||
import base64
|
||||
|
||||
def tran64(s):
|
||||
missing_padding = len(s) % 4
|
||||
if missing_padding != 0:
|
||||
s = s+'='* (4 - missing_padding)
|
||||
return s
|
||||
|
||||
class FaceLogin(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = FaceLoginSerializer
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
人脸识别登录
|
||||
"""
|
||||
base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+')))
|
||||
user, msg = HRMService.face_compare_from_base64(base64_data)
|
||||
if user:
|
||||
refresh = RefreshToken.for_user(user)
|
||||
# 可设为在岗
|
||||
now = timezone.now()
|
||||
now_local = timezone.localtime()
|
||||
if 8<=now_local.hour<=17:
|
||||
ins, created = ClockRecord.objects.get_or_create(
|
||||
create_by = user, create_time__hour__range = [8,18],
|
||||
create_time__year=now_local.year, create_time__month=now_local.month,
|
||||
create_time__day=now_local.day,
|
||||
defaults={
|
||||
'type':ClockRecord.ClOCK_WORK1,
|
||||
'create_by':user,
|
||||
'create_time':now
|
||||
})
|
||||
# 设为在岗
|
||||
if created:
|
||||
Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now)
|
||||
|
||||
return Response({
|
||||
'refresh': str(refresh),
|
||||
'access': str(refresh.access_token),
|
||||
'username':user.username,
|
||||
'name':user.name
|
||||
})
|
||||
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
|
@ -0,0 +1 @@
|
|||
LOG_NOT_FONED = {"code":"log_not_found", "detail":"日志不存在"}
|
||||
|
|
@ -1 +0,0 @@
|
|||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Generated by Django 3.2.12 on 2022-04-08 00:58
|
||||
|
||||
import apps.utils.snowflake
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DrfRequestLog',
|
||||
fields=[
|
||||
('id', models.CharField(default=apps.utils.snowflake.IdWorker.get_id, 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='删除标记')),
|
||||
('requested_at', models.DateTimeField(db_index=True)),
|
||||
('response_ms', models.PositiveIntegerField(default=0)),
|
||||
('path', models.CharField(db_index=True, help_text='请求地址', max_length=400)),
|
||||
('view', models.CharField(blank=True, db_index=True, help_text='执行视图', max_length=400, null=True)),
|
||||
('view_method', models.CharField(blank=True, db_index=True, max_length=6, null=True)),
|
||||
('remote_addr', models.GenericIPAddressField()),
|
||||
('host', models.URLField()),
|
||||
('method', models.CharField(max_length=10)),
|
||||
('query_params', models.TextField(blank=True, null=True)),
|
||||
('data', models.TextField(blank=True, null=True)),
|
||||
('response', models.TextField(blank=True, null=True)),
|
||||
('errors', models.TextField(blank=True, null=True)),
|
||||
('status_code', models.PositiveIntegerField(blank=True, db_index=True, null=True)),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'DRF请求日志',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,3 +1,47 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
from apps.utils.models import BaseModel
|
||||
|
||||
class DrfRequestLog(BaseModel):
|
||||
"""Logs Django rest framework API requests"""
|
||||
|
||||
user = models.ForeignKey(
|
||||
'system.user',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
requested_at = models.DateTimeField(db_index=True)
|
||||
response_ms = models.PositiveIntegerField(default=0)
|
||||
path = models.CharField(
|
||||
max_length=400,
|
||||
db_index=True,
|
||||
help_text="请求地址",
|
||||
)
|
||||
view = models.CharField(
|
||||
max_length=400,
|
||||
null=True,
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="执行视图",
|
||||
)
|
||||
view_method = models.CharField(
|
||||
max_length=6,
|
||||
null=True,
|
||||
blank=True,
|
||||
db_index=True,
|
||||
)
|
||||
remote_addr = models.GenericIPAddressField()
|
||||
host = models.URLField()
|
||||
method = models.CharField(max_length=10)
|
||||
query_params = models.TextField(null=True, blank=True)
|
||||
data = models.TextField(null=True, blank=True)
|
||||
response = models.TextField(null=True, blank=True)
|
||||
errors = models.TextField(null=True, blank=True)
|
||||
status_code = models.PositiveIntegerField(null=True, blank=True, db_index=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "DRF请求日志"
|
||||
|
||||
def __str__(self):
|
||||
return "{} {}".format(self.method, self.path)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework import routers
|
||||
from .views import ServerInfoView, LogView, LogDetailView, index, room, video
|
||||
from .views import DrfRequestLogViewSet, ServerInfoView, LogView, LogDetailView, index, room, video
|
||||
|
||||
API_BASE_URL = 'api/monitor/'
|
||||
HTML_BASE_URL = 'monitor/'
|
||||
|
|
@ -13,4 +13,5 @@ urlpatterns = [
|
|||
path(API_BASE_URL + 'log/', LogView.as_view()),
|
||||
path(API_BASE_URL + 'log/<str:name>/', LogDetailView.as_view()),
|
||||
path(API_BASE_URL + 'server/', ServerInfoView.as_view()),
|
||||
path(API_BASE_URL + 'request_log/', DrfRequestLogViewSet.as_view({'get':'list'}), name='requestlog_view')
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ from rest_framework import serializers, status
|
|||
from drf_yasg import openapi
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework.exceptions import NotFound
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from apps.monitor.models import DrfRequestLog
|
||||
|
||||
from apps.monitor.errors import LOG_NOT_FONED
|
||||
from apps.utils.viewsets import CustomGenericViewSet
|
||||
# Create your views here.
|
||||
|
||||
|
||||
|
|
@ -117,5 +122,21 @@ class LogDetailView(APIView):
|
|||
data = f.read()
|
||||
return Response(data)
|
||||
except:
|
||||
raise NotFound('不存在该日志')
|
||||
raise NotFound(**LOG_NOT_FONED)
|
||||
|
||||
class DrfRequestLogSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = DrfRequestLog
|
||||
fields = '__all__'
|
||||
|
||||
class DrfRequestLogViewSet(ListModelMixin, CustomGenericViewSet):
|
||||
"""请求日志
|
||||
|
||||
请求日志
|
||||
"""
|
||||
perms_map = {'get':'requestlog_view'}
|
||||
queryset = DrfRequestLog.objects.all()
|
||||
list_serializer_class = DrfRequestLogSerializer
|
||||
ordering = ['-requested_at']
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,6 @@
|
|||
# 自定义的错误码
|
||||
from rest_framework.exceptions import ValidationError
|
||||
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":"账户已存在"}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# Generated by Django 3.2.12 on 2022-04-06 06:58
|
||||
|
||||
import apps.utils.snowflake
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dept',
|
||||
name='third_info',
|
||||
field=models.JSONField(default=dict, verbose_name='三方系统信息'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dept',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dict',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dicttype',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='file',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='permission',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='post',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='role',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userpost',
|
||||
name='id',
|
||||
field=models.CharField(default=apps.utils.snowflake.IdWorker.get_id, editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID'),
|
||||
),
|
||||
]
|
||||
|
|
@ -46,6 +46,7 @@ class Dept(CommonAModel):
|
|||
parent = models.ForeignKey('self', null=True, blank=True,
|
||||
on_delete=models.SET_NULL, verbose_name='父')
|
||||
sort = models.PositiveSmallIntegerField('排序标记', default=1)
|
||||
third_info = models.JSONField('三方系统信息', default=dict)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '部门'
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ 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, USERNAME_EXIST
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from apps.utils.vars import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
|
||||
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
|
||||
from .models import (Dict, DictType, File, Dept, Permission, Post,
|
||||
Role, User, UserPost)
|
||||
|
||||
from rest_framework.exceptions import ParseError, APIException
|
||||
from django.db import transaction
|
||||
|
||||
class IntervalSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
|
|
@ -36,12 +38,6 @@ class PTaskSerializer(CustomModelSerializer):
|
|||
model = PeriodicTask
|
||||
fields = '__all__'
|
||||
|
||||
@staticmethod
|
||||
def setup_eager_loading(queryset):
|
||||
""" Perform necessary eager loading of data. """
|
||||
queryset = queryset.select_related('interval', 'crontab')
|
||||
return queryset
|
||||
|
||||
def get_schedule(self, obj):
|
||||
if obj.interval:
|
||||
return obj.interval.__str__()
|
||||
|
|
@ -188,7 +184,48 @@ class DeptCreateUpdateSerializer(CustomModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Dept
|
||||
exclude = EXCLUDE_FIELDS
|
||||
exclude = EXCLUDE_FIELDS + ['third_info']
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
from apps.utils.dahua import dhClient
|
||||
if dhClient:
|
||||
data = {
|
||||
"parentId":1,
|
||||
"name":validated_data['name'],
|
||||
"service":"ehs"
|
||||
}
|
||||
ok, res = dhClient.request('/evo-apigw/evo-brm/1.0.0/department/add',
|
||||
'post',json=data)
|
||||
if ok == 'success':
|
||||
third_info = {'dh_id':str(res['id'])}
|
||||
validated_data['third_info'] = third_info
|
||||
elif ok == 'fail':
|
||||
raise ParseError(**res)
|
||||
else:
|
||||
raise APIException(**res)
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def update(self, instance, validated_data):
|
||||
if instance.name != validated_data.get('name', ''):
|
||||
from apps.utils.dahua import dhClient
|
||||
if dhClient and instance.third_info.get('dh_id', False):
|
||||
data = {
|
||||
"id":instance.third_info['dh_id'],
|
||||
"parentId":1,
|
||||
"name":validated_data['name']
|
||||
}
|
||||
ok, res = dhClient.request('/evo-apigw/evo-brm/1.0.0/department/update',
|
||||
'put',json=data)
|
||||
if ok == 'success':
|
||||
pass
|
||||
elif ok == 'fail':
|
||||
raise ParseError(**res)
|
||||
else:
|
||||
raise APIException(**res)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
class UserSimpleSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
|
|
@ -214,50 +251,54 @@ class UserListSerializer(CustomModelSerializer):
|
|||
queryset = queryset.prefetch_related('posts')
|
||||
return queryset
|
||||
|
||||
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
|
||||
|
||||
def phone_exist(phone):
|
||||
if User.objects.filter(phone=phone):
|
||||
raise serializers.ValidationError(**PHONE_EXIST)
|
||||
|
||||
def user_exist(username):
|
||||
if User.objects.filter(username=username):
|
||||
raise serializers.ValidationError(**USERNAME_EXIST)
|
||||
return username
|
||||
|
||||
class UserUpdateSerializer(CustomModelSerializer):
|
||||
"""
|
||||
用户编辑序列化
|
||||
"""
|
||||
phone = serializers.CharField(max_length=11, required=False)
|
||||
phone = serializers.CharField(max_length=11,
|
||||
required=False, validators=[phone_check])
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'username', 'name', 'phone', 'email', 'belong_dept',
|
||||
'avatar', 'is_active', 'is_superuser']
|
||||
|
||||
def validate_phone(self, phone):
|
||||
re_phone = '^1[358]\d{9}$|^147\d{8}$|^176\d{8}$'
|
||||
if not re.match(re_phone, phone):
|
||||
raise serializers.ValidationError('手机号码不合法')
|
||||
return phone
|
||||
|
||||
|
||||
|
||||
class UserCreateSerializer(CustomModelSerializer):
|
||||
"""
|
||||
创建用户序列化
|
||||
"""
|
||||
username = serializers.CharField(required=True)
|
||||
phone = serializers.CharField(max_length=11, required=False)
|
||||
username = serializers.CharField(required=True, validators=[user_exist])
|
||||
phone = serializers.CharField(max_length=11,
|
||||
required=False, validators=[phone_check, phone_exist])
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'username', 'name', 'phone', 'email', 'belong_dept',
|
||||
'avatar', 'is_active']
|
||||
|
||||
def validate_username(self, username):
|
||||
if User.objects.filter(username=username):
|
||||
raise serializers.ValidationError(username + ' 账号已存在')
|
||||
return username
|
||||
|
||||
def validate_phone(self, phone):
|
||||
re_phone = '^1[358]\d{9}$|^147\d{8}$|^176\d{8}$'
|
||||
if not re.match(re_phone, phone):
|
||||
raise serializers.ValidationError('手机号码不合法')
|
||||
if User.objects.filter(phone=phone):
|
||||
raise serializers.ValidationError('手机号已经被注册')
|
||||
return phone
|
||||
|
||||
class PasswordChangeSerializer(serializers.Serializer):
|
||||
old_password = serializers.CharField(label="原密码")
|
||||
new_password1 = serializers.CharField(label="新密码1")
|
||||
new_password2 = serializers.CharField(label="新密码2")
|
||||
|
||||
class PTaskResultSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ from rest_framework.parsers import (JSONParser,
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from apps.system.errors import OLD_PASSWORD_WRONG, PASSWORD_NOT_SAME, SCHEDULE_WRONG
|
||||
|
||||
from apps.utils.mixins import (CustomCreateModelMixin)
|
||||
from django.conf import settings
|
||||
from apps.utils.permission import get_user_perms_map
|
||||
from apps.utils.permission import ALL_PERMS, get_user_perms_map
|
||||
from apps.utils.queryset import get_child_queryset2
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||
from server.celery import app as celery_app
|
||||
|
|
@ -23,7 +24,7 @@ from .filters import UserFilter
|
|||
from .models import (Dept, Dict, DictType, File, Permission, Post, Role, User,
|
||||
UserPost)
|
||||
from .serializers import (DeptCreateUpdateSerializer, DeptSerializer, DictCreateUpdateSerializer, DictSerializer, DictTypeCreateUpdateSerializer, DictTypeSerializer,
|
||||
FileSerializer, PermissionCreateUpdateSerializer, PermissionSerializer, PostCreateUpdateSerializer, PostSerializer,
|
||||
FileSerializer, PasswordChangeSerializer, PermissionCreateUpdateSerializer, PermissionSerializer, PostCreateUpdateSerializer, PostSerializer,
|
||||
PTaskCreateUpdateSerializer, PTaskResultSerializer,
|
||||
PTaskSerializer, RoleCreateUpdateSerializer, RoleSerializer,
|
||||
UserCreateSerializer, UserListSerializer, UserPostCreateSerializer,
|
||||
|
|
@ -62,8 +63,10 @@ class PTaskViewSet(CustomModelViewSet):
|
|||
serializer_class = PTaskSerializer
|
||||
create_serializer_class = PTaskCreateUpdateSerializer
|
||||
update_serializer_class = PTaskCreateUpdateSerializer
|
||||
partial_update_serializer_class = PTaskCreateUpdateSerializer
|
||||
search_fields = ['name', 'task']
|
||||
filterset_fields = ['enabled']
|
||||
select_related_fields = ['interval', 'crontab']
|
||||
ordering = ['-create_time']
|
||||
|
||||
@action(methods=['put'], detail=True, perms_map={'put': 'ptask_update'})
|
||||
|
|
@ -94,7 +97,7 @@ class PTaskViewSet(CustomModelViewSet):
|
|||
**interval_, defaults=interval_)
|
||||
data['interval'] = interval.id
|
||||
except:
|
||||
raise ParseError('时间策略有误', 'schedule_wrong')
|
||||
raise ParseError(**SCHEDULE_WRONG)
|
||||
if timetype == 'crontab' and crontab_:
|
||||
data['interval'] = None
|
||||
try:
|
||||
|
|
@ -103,7 +106,7 @@ class PTaskViewSet(CustomModelViewSet):
|
|||
**crontab_, defaults=crontab_)
|
||||
data['crontab'] = crontab.id
|
||||
except:
|
||||
raise ParseError('时间策略有误', 'schedule_wrong')
|
||||
raise ParseError(**SCHEDULE_WRONG)
|
||||
serializer = self.get_serializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
|
@ -128,7 +131,7 @@ class PTaskViewSet(CustomModelViewSet):
|
|||
**interval_, defaults=interval_)
|
||||
data['interval'] = interval.id
|
||||
except:
|
||||
raise ParseError('时间策略有误', 'schedule_wrong')
|
||||
raise ParseError(**SCHEDULE_WRONG)
|
||||
if timetype == 'crontab' and crontab_:
|
||||
data['interval'] = None
|
||||
try:
|
||||
|
|
@ -139,7 +142,7 @@ class PTaskViewSet(CustomModelViewSet):
|
|||
**crontab_, defaults=crontab_)
|
||||
data['crontab'] = crontab.id
|
||||
except:
|
||||
raise ParseError('时间策略有误', 'schedule_wrong')
|
||||
raise ParseError(**SCHEDULE_WRONG)
|
||||
instance = self.get_object()
|
||||
serializer = self.get_serializer(instance, data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
|
@ -173,6 +176,7 @@ class DictTypeViewSet(CustomModelViewSet):
|
|||
serializer_class = DictTypeSerializer
|
||||
create_serializer_class = DictTypeCreateUpdateSerializer
|
||||
update_serializer_class = DictTypeCreateUpdateSerializer
|
||||
partial_update_serializer_class = DictTypeCreateUpdateSerializer
|
||||
search_fields = ['name']
|
||||
|
||||
|
||||
|
|
@ -187,6 +191,7 @@ class DictViewSet(CustomModelViewSet):
|
|||
serializer_class = DictSerializer
|
||||
create_serializer_class = DictCreateUpdateSerializer
|
||||
update_serializer_class = DictCreateUpdateSerializer
|
||||
partial_update_serializer_class = DictCreateUpdateSerializer
|
||||
search_fields = ['name']
|
||||
|
||||
|
||||
|
|
@ -199,6 +204,7 @@ class PostViewSet(CustomModelViewSet):
|
|||
serializer_class = PostSerializer
|
||||
create_serializer_class = PostCreateUpdateSerializer
|
||||
update_serializer_class = PostCreateUpdateSerializer
|
||||
partial_update_serializer_class = PostCreateUpdateSerializer
|
||||
search_fields = ['name', 'description']
|
||||
|
||||
|
||||
|
|
@ -212,8 +218,18 @@ class PermissionViewSet(CustomModelViewSet):
|
|||
serializer_class = PermissionSerializer
|
||||
create_serializer_class = PermissionCreateUpdateSerializer
|
||||
update_serializer_class = PermissionCreateUpdateSerializer
|
||||
partial_update_serializer_class = PermissionCreateUpdateSerializer
|
||||
search_fields = ['name', 'code']
|
||||
|
||||
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
|
||||
def codes(self, request, pk=None):
|
||||
"""获取全部权限标识
|
||||
|
||||
需要先请求一次swagger
|
||||
"""
|
||||
ALL_PERMS.sort()
|
||||
return Response(ALL_PERMS)
|
||||
|
||||
|
||||
class DeptViewSet(CustomModelViewSet):
|
||||
"""部门-增删改查
|
||||
|
|
@ -224,6 +240,7 @@ class DeptViewSet(CustomModelViewSet):
|
|||
serializer_class = DeptSerializer
|
||||
create_serializer_class = DeptCreateUpdateSerializer
|
||||
update_serializer_class = DeptCreateUpdateSerializer
|
||||
partial_update_serializer_class = DeptCreateUpdateSerializer
|
||||
filterset_fields = ['type']
|
||||
search_fields = ['name']
|
||||
|
||||
|
|
@ -262,7 +279,7 @@ class UserPostViewSet(CreateModelMixin, DestroyModelMixin, ListModelMixin, Custo
|
|||
|
||||
def perform_destroy(self, instance):
|
||||
user = instance.user
|
||||
instance.delete()
|
||||
instance.delete(update_by = self.request.user)
|
||||
fdept = UserPost.objects.filter(user=user).order_by('sort', 'create_time').first()
|
||||
if fdept:
|
||||
user.belong_dept = fdept
|
||||
|
|
@ -279,18 +296,8 @@ class UserViewSet(CustomModelViewSet):
|
|||
update_serializer_class = UserUpdateSerializer
|
||||
filterset_class = UserFilter
|
||||
search_fields = ['username', 'name', 'phone', 'email']
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = self.queryset
|
||||
if hasattr(self.get_serializer_class(), 'setup_eager_loading'):
|
||||
queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化
|
||||
dept = self.request.query_params.get(
|
||||
'belong_dept', None) # 该部门及其子部门所有员工
|
||||
if dept:
|
||||
dept_queryset = get_child_queryset2(
|
||||
Dept.objects.get(pk=dept))
|
||||
queryset = queryset.filter(dept__in=dept_queryset)
|
||||
return queryset
|
||||
select_related_fields = ['superior', 'belong_dept']
|
||||
prefetch_related_fields = ['posts']
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""创建用户
|
||||
|
|
@ -307,7 +314,9 @@ class UserViewSet(CustomModelViewSet):
|
|||
serializer.save(password=password)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
@action(methods=['put'], detail=False, permission_classes=[IsAuthenticated])
|
||||
@action(methods=['put'], detail=False,
|
||||
permission_classes=[IsAuthenticated],
|
||||
serializer_class = PasswordChangeSerializer)
|
||||
def password(self, request, pk=None):
|
||||
"""修改密码
|
||||
|
||||
|
|
@ -323,9 +332,9 @@ class UserViewSet(CustomModelViewSet):
|
|||
user.save()
|
||||
return Response()
|
||||
else:
|
||||
raise ParseError('新密码两次输入不一致!', 'password_not_same')
|
||||
raise ParseError(**PASSWORD_NOT_SAME)
|
||||
else:
|
||||
raise ValidationError('旧密码错误!', 'old_password_wrong')
|
||||
raise ValidationError(**OLD_PASSWORD_WRONG)
|
||||
|
||||
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
|
||||
def info(self, request, pk=None):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
from rest_framework.exceptions import ParseError, APIException
|
||||
from apps.utils.dahua import dhClient
|
||||
from apps.utils.errors import XX_REQUEST_ERROR
|
||||
from apps.utils.xunxi import xxClient
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
|
@ -24,8 +26,10 @@ class DahuaTestView(APIView):
|
|||
"type": "hls"
|
||||
}
|
||||
}
|
||||
res = dhClient.request(
|
||||
url='/evo-apigw/admin/API/video/stream/realtime', method='post', json=data)
|
||||
# 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,
|
||||
|
|
@ -41,7 +45,12 @@ class DahuaTestView(APIView):
|
|||
# }
|
||||
# res = dhClient.request(
|
||||
# url='/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/channelControl/closeDoor', method='post', json=data)
|
||||
return Response(res)
|
||||
if ok == 'success':
|
||||
return Response(res)
|
||||
elif ok == 'fail':
|
||||
raise ParseError(**res)
|
||||
else:
|
||||
raise APIException(**res)
|
||||
|
||||
|
||||
class XxTestView(APIView):
|
||||
|
|
@ -51,9 +60,14 @@ class XxTestView(APIView):
|
|||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
res = xxClient.request(
|
||||
ok, res = xxClient.request(
|
||||
url='/api/application/build/buildListV2', json={})
|
||||
return Response(res)
|
||||
if ok == 'success':
|
||||
return Response(res)
|
||||
elif ok == 'fail':
|
||||
raise ParseError(**res)
|
||||
else:
|
||||
raise APIException(**res)
|
||||
|
||||
|
||||
class XxCommonViewSet(CreateModelMixin, CustomGenericViewSet):
|
||||
|
|
@ -67,12 +81,17 @@ class XxCommonViewSet(CreateModelMixin, CustomGenericViewSet):
|
|||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
res = xxClient.request(
|
||||
ok, res = xxClient.request(
|
||||
url=vdata['url'],
|
||||
method=vdata.get('method', 'post'),
|
||||
params=vdata.get('params', {}),
|
||||
json=vdata.get('data', {}))
|
||||
return Response(res)
|
||||
if ok == 'success':
|
||||
return Response(res)
|
||||
elif ok == 'fail':
|
||||
raise ParseError(**res)
|
||||
else:
|
||||
raise APIException(**res)
|
||||
|
||||
|
||||
class DhCommonViewSet(CreateModelMixin, CustomGenericViewSet):
|
||||
|
|
@ -86,9 +105,14 @@ class DhCommonViewSet(CreateModelMixin, CustomGenericViewSet):
|
|||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
res = dhClient.request(
|
||||
ok, res = dhClient.request(
|
||||
url=vdata['url'],
|
||||
method=vdata.get('method', 'post'),
|
||||
params=vdata.get('params', {}),
|
||||
json=vdata.get('data', {}))
|
||||
return Response(res)
|
||||
if ok == 'success':
|
||||
return Response(res)
|
||||
elif ok == 'fail':
|
||||
raise ParseError(**res)
|
||||
else:
|
||||
raise APIException(**res)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
from threading import Thread
|
||||
import traceback
|
||||
import requests
|
||||
from apps.utils.errors import DH_REQUEST_ERROR
|
||||
from apps.utils.tools import print_roundtrip
|
||||
from server import settings
|
||||
import json
|
||||
import time
|
||||
from rest_framework.exceptions import APIException, ParseError
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
class DhClient:
|
||||
|
|
@ -14,6 +16,8 @@ class DhClient:
|
|||
|
||||
def __init__(self, client_id= settings.DAHUA_CLIENTID
|
||||
, client_secret = settings.DAHUA_SECRET) -> None:
|
||||
if not settings.DAHUA_ENABLED:
|
||||
return None
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.headers = {}
|
||||
|
|
@ -74,8 +78,8 @@ class DhClient:
|
|||
else:
|
||||
r = getattr(requests, method)('{}{}'.format(settings.DAHUA_BASE_URL, url)
|
||||
, headers = self.headers, params=params, json=json, verify=False)
|
||||
if settings.DEBUG:
|
||||
print_roundtrip(r)
|
||||
# if settings.DEBUG:
|
||||
# print_roundtrip(r)
|
||||
if r.status_code == 200:
|
||||
"""
|
||||
请求成功
|
||||
|
|
@ -85,15 +89,10 @@ class DhClient:
|
|||
self.get_token() # 重新获取token
|
||||
self.request(url, method, params, json, timeout) # 重新请求
|
||||
else:
|
||||
|
||||
msg = '{}|{}{}'.format(str(ret['code']), ret.get('errMsg',''), ret.get('desc', ''))
|
||||
if ret['code'] not in ['0', '100', '00000', '1000', 0, 100, 1000]:
|
||||
detail = '{}|{}{}'.format(str(ret['code']), ret.get('errMsg',''), ret.get('desc', ''))
|
||||
return 'fail', dict(detail=detail, code='dh_'+str(ret['code']))
|
||||
return 'success', ret['data']
|
||||
return 'error', DH_REQUEST_ERROR
|
||||
|
||||
res = dict(success=True, code=200000, msg= msg, data=ret.get('data', None))
|
||||
if ret['code'] not in ['0', '100', '00000']:
|
||||
res['success'] = False
|
||||
res['code'] = 400000
|
||||
return res
|
||||
return dict(success=False, code=400901, msg='大华接口访问异常', data=None)
|
||||
|
||||
if settings.DAHUA_ENABLED:
|
||||
dhClient = DhClient()
|
||||
dhClient = DhClient()
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
XX_REQUEST_ERROR = {"code":"xx_request_error", "detail":"寻息接口访问异常"}
|
||||
DH_REQUEST_ERROR = {"code":"dh_request_error", "detail":"大华接口访问异常"}
|
||||
SIGN_MAKE_FAIL = {"code":"sign_make_fail", "detail":"签名照生成失败,请重新上传"}
|
||||
PKS_ERROR = {"code":"pks_error", "detail":"未获取到主键列表"}
|
||||
|
|
@ -1,21 +1,9 @@
|
|||
from typing import Tuple
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import Http404
|
||||
from server.settings import myLogger
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import exceptions
|
||||
from rest_framework.views import set_rollback
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import traceback
|
||||
|
||||
class MyError(exceptions.ParseError):
|
||||
"""自定义业务异常
|
||||
"""
|
||||
|
||||
def __init__(self, error:Tuple[str, str]=(), detail=None, code=None):
|
||||
if error:
|
||||
code, detail = error
|
||||
super().__init__(detail, code)
|
||||
|
||||
def custom_exception_hander(exc, context):
|
||||
"""
|
||||
|
|
@ -26,6 +14,7 @@ def custom_exception_hander(exc, context):
|
|||
elif isinstance(exc, PermissionDenied):
|
||||
exc = exceptions.PermissionDenied()
|
||||
|
||||
request_id = getattr(context['request'], 'request_id', None)
|
||||
if isinstance(exc, exceptions.APIException):
|
||||
headers = {}
|
||||
if getattr(exc, 'auth_header', None):
|
||||
|
|
@ -41,7 +30,7 @@ def custom_exception_hander(exc, context):
|
|||
data = {'err_msg': exc.detail, 'err_code':exc.get_codes()}
|
||||
|
||||
set_rollback()
|
||||
data['request_id'] = request_id
|
||||
return Response(data, status=exc.status_code, headers=headers)
|
||||
# 未处理的异常记录日志
|
||||
myLogger.error(traceback.format_exc())
|
||||
return Response(data={'err_code':'server_error', 'err_msg':'服务器错误'}, status=500)
|
||||
|
||||
return Response(data={'err_code':'server_error', 'err_msg':'服务器错误', 'request_id': request_id}, status=500)
|
||||
|
|
@ -1,5 +1,15 @@
|
|||
import uuid
|
||||
from django.db.models.query import QuerySet
|
||||
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, ListModelMixin, RetrieveModelMixin, DestroyModelMixin
|
||||
import ast
|
||||
import ipaddress
|
||||
import traceback
|
||||
from apps.monitor.models import DrfRequestLog
|
||||
from server.settings import myLogger
|
||||
from django.db import connection
|
||||
from django.utils.timezone import now
|
||||
from apps.utils.snowflake import idWorker
|
||||
|
||||
class CreateUpdateModelAMixin:
|
||||
"""
|
||||
业务用基本表A用
|
||||
|
|
@ -32,19 +42,6 @@ class CreateUpdateCustomMixin:
|
|||
def perform_update(self, serializer):
|
||||
serializer.save(update_by = self.request.user)
|
||||
|
||||
class OptimizationMixin:
|
||||
"""
|
||||
性能优化,需要在序列化器里定义setup_eager_loading,可在必要的View下继承
|
||||
"""
|
||||
def get_queryset(self):
|
||||
queryset = self.queryset
|
||||
if isinstance(queryset, QuerySet):
|
||||
# Ensure queryset is re-evaluated on each request.
|
||||
queryset = queryset.all()
|
||||
if hasattr(self.get_serializer_class(), 'setup_eager_loading'):
|
||||
queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化
|
||||
return queryset
|
||||
|
||||
class CustomCreateModelMixin(CreateModelMixin):
|
||||
|
||||
def perform_create(self, serializer):
|
||||
|
|
@ -63,3 +60,217 @@ class CustomDestoryModelMixin(DestroyModelMixin):
|
|||
def perform_destroy(self, instance):
|
||||
instance.delete(update_by = self.request.user)
|
||||
|
||||
|
||||
class MyLoggingMixin(object):
|
||||
"""Mixin to log requests"""
|
||||
|
||||
CLEANED_SUBSTITUTE = "********************"
|
||||
|
||||
# logging_methods = "__all__"
|
||||
logging_methods = ['POST', 'PUT', 'DELETE', 'PATCH']
|
||||
sensitive_fields = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
assert isinstance(
|
||||
self.CLEANED_SUBSTITUTE, str
|
||||
), "CLEANED_SUBSTITUTE must be a string."
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def initial(self, request, *args, **kwargs):
|
||||
request_id = idWorker.get_id()
|
||||
self.log = {"requested_at": now(), "id":request_id}
|
||||
setattr(request, 'request_id', request_id)
|
||||
if not getattr(self, "decode_request_body", False):
|
||||
self.log["data"] = ""
|
||||
else:
|
||||
self.log["data"] = self._clean_data(request.body)
|
||||
|
||||
super().initial(request, *args, **kwargs)
|
||||
|
||||
try:
|
||||
# Accessing request.data *for the first time* parses the request body, which may raise
|
||||
# ParseError and UnsupportedMediaType exceptions. It's important not to swallow these,
|
||||
# as (depending on implementation details) they may only get raised this once, and
|
||||
# DRF logic needs them to be raised by the view for error handling to work correctly.
|
||||
data = self.request.data.dict()
|
||||
except AttributeError:
|
||||
data = self.request.data
|
||||
self.log["data"] = self._clean_data(data)
|
||||
|
||||
def handle_exception(self, exc):
|
||||
response = super().handle_exception(exc)
|
||||
err_str = traceback.format_exc()
|
||||
self.log["errors"] = err_str
|
||||
myLogger.error('{}-{}'.format(self.log['request_id'], err_str))
|
||||
return response
|
||||
|
||||
def finalize_response(self, request, response, *args, **kwargs):
|
||||
response = super().finalize_response(
|
||||
request, response, *args, **kwargs
|
||||
)
|
||||
|
||||
# Ensure backward compatibility for those using _should_log hook
|
||||
should_log = (
|
||||
self._should_log if hasattr(self, "_should_log") else self.should_log
|
||||
)
|
||||
|
||||
if should_log(request, response):
|
||||
if (connection.settings_dict.get("ATOMIC_REQUESTS") and getattr(response, "exception", None) and connection.in_atomic_block):
|
||||
# response with exception (HTTP status like: 401, 404, etc)
|
||||
# pointwise disable atomic block for handle log (TransactionManagementError)
|
||||
connection.set_rollback(True)
|
||||
connection.set_rollback(False)
|
||||
if response.streaming:
|
||||
rendered_content = None
|
||||
elif hasattr(response, "rendered_content"):
|
||||
rendered_content = response.rendered_content
|
||||
else:
|
||||
rendered_content = response.getvalue()
|
||||
|
||||
self.log.update(
|
||||
{
|
||||
"remote_addr": self._get_ip_address(request),
|
||||
"view": self._get_view_name(request),
|
||||
"view_method": self._get_view_method(request),
|
||||
"path": self._get_path(request),
|
||||
"host": request.get_host(),
|
||||
"method": request.method,
|
||||
"query_params": self._clean_data(request.query_params.dict()),
|
||||
"user": self._get_user(request),
|
||||
"response_ms": self._get_response_ms(),
|
||||
"response": self._clean_data(rendered_content),
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
)
|
||||
try:
|
||||
self.handle_log()
|
||||
except Exception:
|
||||
# ensure that all exceptions raised by handle_log
|
||||
# doesn't prevent API call to continue as expected
|
||||
myLogger.exception("Logging API call raise exception!")
|
||||
return response
|
||||
|
||||
def handle_log(self):
|
||||
"""
|
||||
Hook to define what happens with the log.
|
||||
|
||||
Defaults on saving the data on the db.
|
||||
"""
|
||||
DrfRequestLog(**self.log).save()
|
||||
|
||||
def _get_path(self, request):
|
||||
"""Get the request path and truncate it"""
|
||||
return request.path
|
||||
|
||||
def _get_ip_address(self, request):
|
||||
"""Get the remote ip address the request was generated from."""
|
||||
ipaddr = request.META.get("HTTP_X_FORWARDED_FOR", None)
|
||||
if ipaddr:
|
||||
ipaddr = ipaddr.split(",")[0]
|
||||
else:
|
||||
ipaddr = request.META.get("REMOTE_ADDR", "")
|
||||
|
||||
# Account for IPv4 and IPv6 addresses, each possibly with port appended. Possibilities are:
|
||||
# <ipv4 address>
|
||||
# <ipv6 address>
|
||||
# <ipv4 address>:port
|
||||
# [<ipv6 address>]:port
|
||||
# Note that ipv6 addresses are colon separated hex numbers
|
||||
possibles = (ipaddr.lstrip("[").split("]")[0], ipaddr.split(":")[0])
|
||||
|
||||
for addr in possibles:
|
||||
try:
|
||||
return str(ipaddress.ip_address(addr))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return ipaddr
|
||||
|
||||
def _get_view_name(self, request):
|
||||
"""Get view name."""
|
||||
method = request.method.lower()
|
||||
try:
|
||||
attributes = getattr(self, method)
|
||||
return (
|
||||
type(attributes.__self__).__module__ + "." + type(attributes.__self__).__name__
|
||||
)
|
||||
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def _get_view_method(self, request):
|
||||
"""Get view method."""
|
||||
if hasattr(self, "action"):
|
||||
return self.action or None
|
||||
return request.method.lower()
|
||||
|
||||
def _get_user(self, request):
|
||||
"""Get user."""
|
||||
user = request.user
|
||||
if user.is_anonymous:
|
||||
return None
|
||||
return user
|
||||
|
||||
def _get_response_ms(self):
|
||||
"""
|
||||
Get the duration of the request response cycle is milliseconds.
|
||||
In case of negative duration 0 is returned.
|
||||
"""
|
||||
response_timedelta = now() - self.log["requested_at"]
|
||||
response_ms = int(response_timedelta.total_seconds() * 1000)
|
||||
return max(response_ms, 0)
|
||||
|
||||
def should_log(self, request, response):
|
||||
"""
|
||||
Method that should return a value that evaluated to True if the request should be logged.
|
||||
By default, check if the request method is in logging_methods.
|
||||
"""
|
||||
return (
|
||||
self.logging_methods == "__all__" or request.method in self.logging_methods
|
||||
)
|
||||
|
||||
def _clean_data(self, data):
|
||||
"""
|
||||
Clean a dictionary of data of potentially sensitive info before
|
||||
sending to the database.
|
||||
Function based on the "_clean_credentials" function of django
|
||||
(https://github.com/django/django/blob/stable/1.11.x/django/contrib/auth/__init__.py#L50)
|
||||
|
||||
Fields defined by django are by default cleaned with this function
|
||||
|
||||
You can define your own sensitive fields in your view by defining a set
|
||||
eg: sensitive_fields = {'field1', 'field2'}
|
||||
"""
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode(errors="replace")
|
||||
|
||||
if isinstance(data, list):
|
||||
return [self._clean_data(d) for d in data]
|
||||
|
||||
if isinstance(data, dict):
|
||||
SENSITIVE_FIELDS = {
|
||||
"api",
|
||||
"token",
|
||||
"key",
|
||||
"secret",
|
||||
"password",
|
||||
"signature",
|
||||
}
|
||||
|
||||
data = dict(data)
|
||||
if self.sensitive_fields:
|
||||
SENSITIVE_FIELDS = SENSITIVE_FIELDS | {
|
||||
field.lower() for field in self.sensitive_fields
|
||||
}
|
||||
|
||||
for key, value in data.items():
|
||||
try:
|
||||
value = ast.literal_eval(value)
|
||||
except (ValueError, SyntaxError):
|
||||
pass
|
||||
if isinstance(value, (list, dict)):
|
||||
data[key] = self._clean_data(value)
|
||||
if key.lower() in SENSITIVE_FIELDS:
|
||||
data[key] = self.CLEANED_SUBSTITUTE
|
||||
return data
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from copy import copy
|
||||
import django.utils.timezone as timezone
|
||||
from django.db import models
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from apps.utils.snowflake import idWorker
|
||||
|
||||
|
||||
|
|
@ -56,7 +57,8 @@ class BaseModel(models.Model):
|
|||
"""
|
||||
基本表
|
||||
"""
|
||||
id = models.CharField(max_length=20, primary_key=True, default=idWorker.get_id, editable=False, verbose_name='主键ID', help_text='主键ID')
|
||||
id = models.CharField(max_length=20, primary_key=True, default=idWorker.get_id,
|
||||
editable=False, verbose_name='主键ID', help_text='主键ID')
|
||||
create_time = models.DateTimeField(
|
||||
default=timezone.now, verbose_name='创建时间', help_text='创建时间')
|
||||
update_time = models.DateTimeField(
|
||||
|
|
@ -67,25 +69,7 @@ class BaseModel(models.Model):
|
|||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
# If self.pk is not None then it's an update.
|
||||
cls = self.__class__
|
||||
old = cls.objects.filter(pk=self.pk).first()
|
||||
if old:
|
||||
# This will get the current model state since super().save() isn't called yet.
|
||||
new = self # This gets the newly instantiated Mode object with the new values.
|
||||
changed_fields = []
|
||||
for field in cls._meta.get_fields():
|
||||
field_name = field.name
|
||||
try:
|
||||
if getattr(old, field_name) != getattr(new, field_name):
|
||||
changed_fields.append(field_name)
|
||||
except Exception as ex: # Catch field does not exist exception
|
||||
pass
|
||||
kwargs['update_fields'] = changed_fields
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class SoftModel(BaseModel):
|
||||
"""
|
||||
软删除基本表
|
||||
|
|
@ -113,51 +97,52 @@ class CommonAModel(SoftModel):
|
|||
业务用基本表A,包含create_by, update_by字段
|
||||
"""
|
||||
create_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name= '%(class)s_create_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='%(class)s_create_by')
|
||||
update_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name= '%(class)s_update_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='%(class)s_update_by')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
|
||||
class CommonBModel(SoftModel):
|
||||
"""
|
||||
业务用基本表B,包含create_by, update_by, belong_dept字段
|
||||
"""
|
||||
create_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name = '%(class)s_create_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='%(class)s_create_by')
|
||||
update_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name = '%(class)s_update_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='%(class)s_update_by')
|
||||
belong_dept = models.ForeignKey(
|
||||
'system.dept', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name= '%(class)s_belong_dept')
|
||||
'system.dept', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name='%(class)s_belong_dept')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class CommonADModel(BaseModel):
|
||||
"""
|
||||
业务用基本表A, 物理删除, 包含create_by, update_by字段
|
||||
"""
|
||||
create_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name= '%(class)s_create_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='%(class)s_create_by')
|
||||
update_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name= '%(class)s_update_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='%(class)s_update_by')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class CommonBDModel(BaseModel):
|
||||
"""
|
||||
业务用基本表B, 物理删除, 包含create_by, update_by, belong_dept字段
|
||||
"""
|
||||
create_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name = '%(class)s_create_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='%(class)s_create_by')
|
||||
update_by = models.ForeignKey(
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name = '%(class)s_update_by')
|
||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='%(class)s_update_by')
|
||||
belong_dept = models.ForeignKey(
|
||||
'system.organzation', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name= '%(class)s_belong_dept')
|
||||
'system.organzation', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name='%(class)s_belong_dept')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ from apps.system.models import Dept, Permission, Post, Role, UserPost
|
|||
from django.db.models import Q
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
ALL_PERMS = [
|
||||
|
||||
]
|
||||
|
||||
def get_user_perms_map(user):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
from rest_framework import serializers
|
||||
from django_restql.mixins import DynamicFieldsMixin
|
||||
|
||||
from rest_framework.fields import empty
|
||||
from rest_framework.request import Request
|
||||
class PkSerializer(serializers.Serializer):
|
||||
pks = serializers.ListField(child=serializers.CharField(max_length=20), label="主键ID列表")
|
||||
|
||||
|
|
@ -9,5 +10,21 @@ class GenSignatureSerializer(serializers.Serializer):
|
|||
path = serializers.CharField(label="图片地址")
|
||||
|
||||
class CustomModelSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
|
||||
pass
|
||||
|
||||
def __init__(self, instance=None, data=empty, request=None, **kwargs):
|
||||
super().__init__(instance, data, **kwargs)
|
||||
self.request: Request = request or self.context.get('request', None)
|
||||
|
||||
def create(self, validated_data):
|
||||
if self.request:
|
||||
if getattr(self.request, 'user', None):
|
||||
validated_data['create_by'] = self.request.user
|
||||
if getattr(self.request.user, 'belong_dept', None):
|
||||
validated_data['belong_dept'] = self.request.user.belong_dept
|
||||
return super().update(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
if self.request:
|
||||
if hasattr(instance, 'update_by'):
|
||||
validated_data['update_by'] = getattr(self.request, 'user', None)
|
||||
return super().update(instance, validated_data)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from rest_framework.views import APIView
|
||||
import os
|
||||
import cv2
|
||||
from apps.utils.errors import SIGN_MAKE_FAIL
|
||||
from server.settings import BASE_DIR
|
||||
import numpy as np
|
||||
from rest_framework.response import Response
|
||||
|
|
@ -45,4 +46,4 @@ class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet):
|
|||
cv2.imwrite(new_path, image)
|
||||
return Response({'path': new_path.replace(BASE_DIR, '')})
|
||||
except:
|
||||
raise ParseError('签名照处理失败,请重新上传')
|
||||
raise ParseError(**SIGN_MAKE_FAIL)
|
||||
|
|
|
|||
|
|
@ -1,29 +1,38 @@
|
|||
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
from rest_framework.decorators import action
|
||||
from apps.utils.mixins import CustomCreateModelMixin, CustomDestoryModelMixin, CustomUpdateModelMixin, OptimizationMixin
|
||||
from apps.utils.permission import RbacDataMixin, RbacPermission
|
||||
from apps.system.models import Dept, Post
|
||||
from apps.utils.errors import PKS_ERROR
|
||||
from apps.utils.mixins import CustomDestoryModelMixin, MyLoggingMixin
|
||||
from apps.utils.permission import ALL_PERMS, RbacPermission, get_user_perms_map
|
||||
from apps.utils.queryset import get_child_queryset2
|
||||
from apps.utils.serializers import PkSerializer
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, ListModelMixin
|
||||
from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, CreateModelMixin, UpdateModelMixin
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from django.core.cache import cache
|
||||
|
||||
|
||||
class CustomGenericViewSet(GenericViewSet):
|
||||
class CustomGenericViewSet(MyLoggingMixin, GenericViewSet):
|
||||
"""
|
||||
增强的GenericViewSet
|
||||
"""
|
||||
perms_map = {}
|
||||
perms_map = {} # 权限标识
|
||||
logging_methods = ['POST', 'PUT', 'PATCH', 'DELETE']
|
||||
ordering_fields = '__all__'
|
||||
filter_fields = '__all__'
|
||||
ordering = '-create_time'
|
||||
filterset_fields = '__all__'
|
||||
create_serializer_class = None
|
||||
update_serializer_class = None
|
||||
partial_update_serializer_class = None
|
||||
list_serializer_class = None
|
||||
retrieve_serializer_class = None
|
||||
select_related_fields = []
|
||||
prefetch_related_fields = []
|
||||
permission_classes = [IsAuthenticated & RbacPermission]
|
||||
data_filter = False # 数据权限过滤是否开启(需要RbacPermission)
|
||||
|
||||
def get_serializer_class(self):
|
||||
action_serializer_name = f"{self.action}_serializer_class"
|
||||
|
|
@ -32,13 +41,62 @@ class CustomGenericViewSet(GenericViewSet):
|
|||
return action_serializer_class
|
||||
return super().get_serializer_class()
|
||||
|
||||
class CustomDataGenericViewSet(RbacDataMixin, CustomGenericViewSet):
|
||||
"""
|
||||
增强的GenericViewSet, 带数据权限过滤
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.perms_map:
|
||||
for k, v in self.perms_map.items():
|
||||
if v not in ALL_PERMS and v!='*':
|
||||
ALL_PERMS.append(v)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
if self.select_related_fields:
|
||||
queryset = queryset.select_related(*self.select_related_fields)
|
||||
if self.prefetch_related_fields:
|
||||
queryset = queryset.prefetch_related(*self.prefetch_related_fields)
|
||||
if self.data_filter:
|
||||
if self.request.user.is_superuser:
|
||||
return queryset
|
||||
if hasattr(queryset.model, 'belong_dept'):
|
||||
user = self.request.user
|
||||
user_perms_map = cache.get('perms_' + user.id, None)
|
||||
if user_perms_map is None:
|
||||
user_perms_map = get_user_perms_map(self.request.user)
|
||||
if isinstance(user_perms_map, dict):
|
||||
if hasattr(self.view, 'perms_map'):
|
||||
perms_map = self.view.perms_map
|
||||
action_str = perms_map.get(self.request._request.method.lower(), None)
|
||||
if '*' in perms_map:
|
||||
return queryset
|
||||
elif action_str == '*':
|
||||
return queryset
|
||||
elif action_str in user_perms_map:
|
||||
new_queryset = queryset.none()
|
||||
for dept_id, data_range in user_perms_map[action_str].items:
|
||||
dept = Dept.objects.get(id=dept_id)
|
||||
if data_range == Post.POST_DATA_ALL:
|
||||
return queryset
|
||||
elif data_range == Post.POST_DATA_SAMELEVE_AND_BELOW:
|
||||
if dept.parent:
|
||||
belong_depts = get_child_queryset2(dept.parent)
|
||||
else:
|
||||
belong_depts = get_child_queryset2(dept)
|
||||
queryset = queryset.filter(belong_dept__in = belong_depts)
|
||||
elif data_range == Post.POST_DATA_THISLEVEL_AND_BELOW:
|
||||
belong_depts = get_child_queryset2(dept)
|
||||
queryset = queryset.filter(belong_dept__in = belong_depts)
|
||||
elif data_range == Post.POST_DATA_THISLEVEL:
|
||||
queryset = queryset.filter(belong_dept = dept)
|
||||
elif data_range == Post.POST_DATA_THISLEVEL:
|
||||
queryset = queryset.filter(create_by = user)
|
||||
new_queryset = new_queryset | queryset
|
||||
return new_queryset
|
||||
else:
|
||||
return queryset.none()
|
||||
return queryset
|
||||
|
||||
class CustomModelViewSet(OptimizationMixin, CustomCreateModelMixin
|
||||
, CustomUpdateModelMixin, ListModelMixin, RetrieveModelMixin
|
||||
class CustomModelViewSet(CreateModelMixin
|
||||
, UpdateModelMixin, ListModelMixin, RetrieveModelMixin
|
||||
, CustomDestoryModelMixin, CustomGenericViewSet):
|
||||
"""
|
||||
增强的ModelViewSet
|
||||
|
|
@ -53,6 +111,9 @@ class CustomModelViewSet(OptimizationMixin, CustomCreateModelMixin
|
|||
,'patch':'{}_update'.format(basename)
|
||||
,'delete':'{}_delete'.format(basename)
|
||||
,'deletes':'{}_delete'.format(basename)}
|
||||
for k, v in self.perms_map.items():
|
||||
if v not in ALL_PERMS and v!='*':
|
||||
ALL_PERMS.append(v)
|
||||
|
||||
@action(methods=['post'], detail=False, serializer_class=PkSerializer)
|
||||
def deletes(self,request,*args,**kwargs):
|
||||
|
|
@ -62,11 +123,4 @@ class CustomModelViewSet(OptimizationMixin, CustomCreateModelMixin
|
|||
self.get_queryset().filter(id__in=pks).delete(update_by=request.user)
|
||||
return Response()
|
||||
else:
|
||||
raise ValidationError("未获取到pks字段")
|
||||
|
||||
|
||||
|
||||
class CustomDataModelViewSet(RbacDataMixin, CustomModelViewSet):
|
||||
"""
|
||||
增强的ModelViewSet,带数据权限过滤
|
||||
"""
|
||||
raise ValidationError(**PKS_ERROR)
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
from threading import Thread
|
||||
import requests
|
||||
import json
|
||||
from apps.utils.errors import XX_REQUEST_ERROR
|
||||
from apps.utils.tools import print_roundtrip
|
||||
from server import settings
|
||||
import time
|
||||
from rest_framework.exceptions import APIException, ParseError
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
|
||||
|
|
@ -13,6 +15,8 @@ class XxClient:
|
|||
寻息
|
||||
"""
|
||||
def __init__(self, licence=settings.XX_LICENCE, username=settings.XX_USERNAME) -> None:
|
||||
if not settings.XX_ENABLED:
|
||||
return None
|
||||
self.licence = licence
|
||||
self.username = username
|
||||
self.isGetingToken = False
|
||||
|
|
@ -57,6 +61,7 @@ class XxClient:
|
|||
def request(self, url:str, method:str='post', params=dict(), json=dict(), timeout=20):
|
||||
params['accessToken'] = self.token
|
||||
json['username'] = self.username
|
||||
json['buildId'] = settings.XX_BUILDID
|
||||
if self.isGetingToken:
|
||||
req_num = 0
|
||||
while True:
|
||||
|
|
@ -69,20 +74,17 @@ class XxClient:
|
|||
else:
|
||||
r = getattr(requests, method)('{}{}'.format(settings.XX_BASE_URL, url)
|
||||
, params=params, json=json, verify=False)
|
||||
if settings.DEBUG:
|
||||
print_roundtrip(r)
|
||||
# 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) # 重新请求
|
||||
else:
|
||||
msg = '{}|{}'.format(str(ret['errorCode']), '|'.join(ret['errorMsg']))
|
||||
res = dict(success=True, code=200000, msg= msg, data=ret['data'])
|
||||
if ret['errorCode'] != 0:
|
||||
res['success'] = False
|
||||
res['code'] = 400000
|
||||
return res
|
||||
return dict(success=False, code=400900, msg='寻息接口访问异常', data=None)
|
||||
return 'fail', dict(detail='|'.join(ret['errorMsg']),
|
||||
code='xx_' + str(ret['errorCode']))
|
||||
return 'success', ret['data']
|
||||
return 'error', XX_REQUEST_ERROR
|
||||
|
||||
if settings.XX_ENABLED:
|
||||
xxClient = XxClient()
|
||||
xxClient = XxClient()
|
||||
|
|
@ -14,7 +14,7 @@ from django.shortcuts import get_object_or_404, render
|
|||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from rest_framework.decorators import action, api_view
|
||||
from apps.wf.models import CustomField, Ticket, Workflow, State, Transition, TicketFlow
|
||||
from apps.utils.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
|
||||
from apps.utils.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin
|
||||
from apps.wf.services import WfService
|
||||
from rest_framework.exceptions import APIException, PermissionDenied
|
||||
from rest_framework import status
|
||||
|
|
@ -119,7 +119,7 @@ class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin,
|
|||
return CustomFieldCreateUpdateSerializer
|
||||
return super().get_serializer_class()
|
||||
|
||||
class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
perms_map = {'get':'*', 'post':'ticket_create'}
|
||||
queryset = Ticket.objects.all()
|
||||
serializer_class = TicketSerializer
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ django-celery-beat==2.2.1
|
|||
django-celery-results==2.3.0
|
||||
django-cors-headers==3.11.0
|
||||
django-filter==21.1
|
||||
django-simple-history==3.0.0
|
||||
djangorestframework==3.13.1
|
||||
djangorestframework-simplejwt==5.1.0
|
||||
drf-yasg==1.20.0
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ INSTALLED_APPS = [
|
|||
'drf_yasg',
|
||||
'rest_framework',
|
||||
"django_filters",
|
||||
'simple_history',
|
||||
'apps.utils',
|
||||
'apps.third',
|
||||
'apps.system',
|
||||
|
|
@ -64,7 +63,6 @@ MIDDLEWARE = [
|
|||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'simple_history.middleware.HistoryRequestMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'server.urls'
|
||||
|
|
@ -303,7 +301,7 @@ LOGGING = {
|
|||
}
|
||||
}
|
||||
# 实例化myLogger
|
||||
myLogger = logging.getLogger('log')
|
||||
myLogger = logging.getLogger(__name__)
|
||||
|
||||
# 大华ICC平台
|
||||
DAHUA_ENABLED = conf.DAHUA_ENABLED
|
||||
|
|
@ -317,4 +315,5 @@ DAHUA_SECRET = conf.DAHUA_SECRET
|
|||
XX_ENABLED = conf.XX_ENABLED
|
||||
XX_BASE_URL = conf.XX_BASE_URL
|
||||
XX_LICENCE = conf.XX_LICENCE
|
||||
XX_USERNAME = conf.XX_USERNAME
|
||||
XX_USERNAME = conf.XX_USERNAME
|
||||
XX_BUILDID = conf.XX_BUILDID
|
||||
Loading…
Reference in New Issue