增加请求日志

This commit is contained in:
caoqianming 2022-04-08 09:31:17 +08:00
parent a6f0e386c0
commit 680a0dceeb
50 changed files with 1421 additions and 181 deletions

View File

@ -1,2 +1,2 @@
NAME_OR_PASSWORD_WRONG = '账户名或密码错误'
USERNAME_OR_PASSWORD_WRONG = {"code":"username_or_password_wrong", "detail":"账户名或密码错误"}

View File

@ -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
apps/hrm/__init__.py Normal file
View File

3
apps/hrm/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

9
apps/hrm/apps.py Normal file
View File

@ -0,0 +1,9 @@
from django.apps import AppConfig
class HrmConfig(AppConfig):
name = 'apps.hrm'
verbose_name = '人力资源管理'
def ready(self):
import apps.hrm.signals

37
apps/hrm/filters.py Normal file
View File

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

View File

@ -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': '员工补充信息',
},
),
]

View File

@ -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='在职状态'),
),
]

View File

@ -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='人脸识别数据'),
),
]

View File

@ -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,
},
),
]

View File

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

View File

@ -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='打卡时间'),
),
]

View File

@ -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,
},
),
]

View File

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

View File

@ -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='是否展示在岗状态'),
),
]

View File

63
apps/hrm/models.py Normal file
View File

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

37
apps/hrm/serializers.py Normal file
View File

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

50
apps/hrm/services.py Normal file
View File

@ -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, '人脸数据获取失败请重新上传图片'

13
apps/hrm/signals.py Normal file
View File

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

30
apps/hrm/tasks.py Normal file
View File

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

3
apps/hrm/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

15
apps/hrm/urls.py Normal file
View File

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

187
apps/hrm/views.py Normal file
View File

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

1
apps/monitor/errors.py Normal file
View File

@ -0,0 +1 @@
LOG_NOT_FONED = {"code":"log_not_found", "detail":"日志不存在"}

View File

@ -1 +0,0 @@
from django.utils.deprecation import MiddlewareMixin

View File

@ -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请求日志',
},
),
]

View File

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

View File

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

View File

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

View File

@ -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":"账户已存在"}

View File

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

View File

@ -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 = '部门'

View File

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

View File

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

View File

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

View File

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

4
apps/utils/errors.py Normal file
View File

@ -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":"未获取到主键列表"}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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