diff --git a/.gitignore b/.gitignore index 429c26e6..c96a2f9f 100755 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ celerybeat-schedule.dat celerybeat-schedule.dir db.sqlite3 server/conf.py +server/conf.json sh/* nohup.out \ No newline at end of file diff --git a/apps/am/migrations/0001_initial.py b/apps/am/migrations/0001_initial.py new file mode 100644 index 00000000..49874c16 --- /dev/null +++ b/apps/am/migrations/0001_initial.py @@ -0,0 +1,76 @@ +# Generated by Django 3.2.12 on 2022-06-17 07:19 + +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), + ('system', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Access', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('type', models.PositiveSmallIntegerField(choices=[(10, '准入'), (20, '禁入')], verbose_name='准入类型')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Area', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('name', models.CharField(max_length=20, verbose_name='名称')), + ('type', models.PositiveSmallIntegerField(choices=[(10, '固定'), (20, '临时')], default=10, verbose_name='区域类型')), + ('level', models.PositiveSmallIntegerField(verbose_name='区域等级')), + ('number', models.CharField(blank=True, max_length=20, null=True, verbose_name='编号')), + ('visitor_yes', models.BooleanField(default=False, verbose_name='准许访客人员')), + ('remployee_yes', models.BooleanField(default=False, verbose_name='准许相关方人员')), + ('employee_yes', models.BooleanField(default=True, verbose_name='准许全部员工')), + ('third_info', models.JSONField(blank=True, default=dict, verbose_name='三方信息')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='area_belong_dept', to='system.dept', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='area_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('posts_access', models.ManyToManyField(through='am.Access', to='system.Post')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='area_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='access', + name='area', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='关联区域'), + ), + migrations.AddField( + model_name='access', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='access_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='access', + name='post', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='关联岗位'), + ), + migrations.AddField( + model_name='access', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='access_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + ] diff --git a/apps/am/migrations/__init__.py b/apps/am/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/am/models.py b/apps/am/models.py index 2b7faac9..319a6312 100755 --- a/apps/am/models.py +++ b/apps/am/models.py @@ -12,15 +12,22 @@ class Area(CommonBModel): AREA_LEVEL_2 = 20 AREA_LEVEL_3 = 30 AREA_LEVEL_4 = 40 - AREA_LEVEL_HOICES = ( + AREA_LEVEL_CHOICES = ( (AREA_LEVEL_1, '办公'), (AREA_LEVEL_2, '生产一般'), (AREA_LEVEL_3, '生产重点'), (AREA_LEVEL_4, '四级') ) + AREA_TYPE_FIX = 10 + AREA_TYPE_TEMP = 20 + AREA_TYPE_CHOICES = ( + (10, '固定'), + (20, '临时') + ) name = models.CharField('名称', max_length=20) + type = models.PositiveSmallIntegerField('区域类型', default=10, choices=AREA_TYPE_CHOICES) level = models.PositiveSmallIntegerField('区域等级') - sort_str = models.CharField('排序字符', max_length=12, default='1') + number = models.CharField('编号', max_length=20, null=True, blank=True) visitor_yes = models.BooleanField('准许访客人员', default=False) remployee_yes = models.BooleanField('准许相关方人员', default=False) employee_yes = models.BooleanField('准许全部员工', default=True) diff --git a/apps/am/serializers.py b/apps/am/serializers.py index bb058d37..c19c34f3 100644 --- a/apps/am/serializers.py +++ b/apps/am/serializers.py @@ -11,7 +11,7 @@ class AreaSimpleSerializer(CustomModelSerializer): class AreaCreateUpdateSerializer(CustomModelSerializer): class Meta: model = Area - fields = ['name', 'level', 'sort_srt', 'visitor_yes', 'remployee_yes', 'employee_yes', 'belong_dept'] + fields = ['name', 'level', 'number', 'visitor_yes', 'remployee_yes', 'employee_yes', 'belong_dept'] class AccessCreateSerializer(CustomModelSerializer): diff --git a/apps/ecm/migrations/0001_initial.py b/apps/ecm/migrations/0001_initial.py new file mode 100644 index 00000000..dce61cef --- /dev/null +++ b/apps/ecm/migrations/0001_initial.py @@ -0,0 +1,129 @@ +# Generated by Django 3.2.12 on 2022-06-17 07:19 + +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 = [ + ('am', '0001_initial'), + ('third', '0001_initial'), + ('hrm', '0002_auto_20220617_1124'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('system', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Event', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('imgs', models.JSONField(blank=True, default=list, verbose_name='事件图片')), + ('location', models.JSONField(blank=True, default=dict, verbose_name='事件点位坐标')), + ('peope_type', models.CharField(blank=True, choices=[('employee', '内部员工'), ('remployee', '相关方人员'), ('visitor', '访客')], max_length=20, null=True, verbose_name='当事人员类型')), + ('handle_time', models.DateTimeField(blank=True, null=True, verbose_name='处理时间')), + ('handle_desc', models.TextField(blank=True, null=True, verbose_name='处理描述')), + ('is_pushed', models.BooleanField(default=False, verbose_name='是否已推送')), + ('area', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='发生区域')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_belong_dept', to='system.dept', verbose_name='所属部门')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='EventCate', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('code', models.CharField(max_length=10, unique=True, verbose_name='标识')), + ('name', models.CharField(max_length=20, unique=True, verbose_name='名称')), + ('trigger', models.PositiveSmallIntegerField(choices=[(10, '监控'), (20, '定位')], default=10, verbose_name='触发方式')), + ('speaker_on', models.BooleanField(default=True, verbose_name='开启音响报警')), + ('filter_area_level', models.PositiveSmallIntegerField(choices=[(10, '办公'), (20, '生产一般'), (30, '生产重点'), (40, '四级')], default=10, verbose_name='固定音响区域级别过滤')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='eventcate_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('speakers', models.ManyToManyField(to='third.TDevice', verbose_name='固定音响')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='eventcate_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='NotifySetting', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('filter_sender', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='提醒人员过滤')), + ('filter_area_level', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='区域级别过滤')), + ('sms_enable', models.BooleanField(default=False, verbose_name='短信通知')), + ('wechat_enable', models.BooleanField(default=False, verbose_name='开启微信通知')), + ('can_handle', models.BooleanField(default=False, verbose_name='是否可处理')), + ('sort', models.PositiveIntegerField(default=1, verbose_name='排序')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notifysetting_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('event_cate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecm.eventcate', to_field='code', verbose_name='关联事件种类')), + ('post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='提醒岗位')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notifysetting_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Remind', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('msg', models.TextField(blank=True, null=True, verbose_name='推送文本')), + ('is_read', models.BooleanField(default=False, verbose_name='站内信已读')), + ('can_handle', models.BooleanField(default=False, verbose_name='是否可处理')), + ('dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='system.dept', verbose_name='部门')), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecm.event', verbose_name='关联事件')), + ('notify_setting', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ecm.notifysetting', verbose_name='通过哪个配置')), + ('post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='岗位')), + ('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='接收人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='event', + name='cate', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecm.eventcate', verbose_name='事件种类'), + ), + migrations.AddField( + model_name='event', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='event', + name='handle_user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='处理人'), + ), + migrations.AddField( + model_name='event', + name='people', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='当事人'), + ), + migrations.AddField( + model_name='event', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + ] diff --git a/apps/ecm/migrations/__init__.py b/apps/ecm/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/ecm/models.py b/apps/ecm/models.py index 198f294c..58ea3d2a 100644 --- a/apps/ecm/models.py +++ b/apps/ecm/models.py @@ -3,6 +3,7 @@ from apps.am.models import Area from apps.hrm.models import Employee from apps.utils.models import BaseModel, CommonAModel, CommonBModel from apps.system.models import Dept, Post, User +from apps.third.models import TDevice from django.utils import timezone # Create your models here. @@ -19,14 +20,14 @@ class EventCate(CommonAModel): name = models.CharField('名称', max_length=20, unique=True) trigger = models.PositiveSmallIntegerField('触发方式', default=10, choices=EVENT_TRIGGER_CHOICES) speaker_on = models.BooleanField('开启音响报警', default=True) - speakers = models.ManyToManyField('third.tdevice', verbose_name='固定音响') - filter_area_level = models.PositiveSmallIntegerField('固定音响区域级别过滤', choices=Area.AREA_LEVEL_HOICES, + speakers = models.ManyToManyField(TDevice, verbose_name='固定音响') + filter_area_level = models.PositiveSmallIntegerField('固定音响区域级别过滤', choices=Area.AREA_LEVEL_CHOICES, default=Area.AREA_LEVEL_1) -class PushSetting(CommonAModel): +class NotifySetting(CommonAModel): """ - 推送配置 + 提醒配置 """ PUSH_FILTER1_CHOICES = ( (10, '当事人部门'), @@ -36,11 +37,12 @@ class PushSetting(CommonAModel): ) event_cate = models.ForeignKey(EventCate, verbose_name='关联事件种类', to_field='code', on_delete=models.CASCADE) - post = models.ForeignKey(Post, verbose_name='推送岗位', + post = models.ForeignKey(Post, verbose_name='提醒岗位', on_delete=models.CASCADE, null=True, blank=True) - filter_to_user = models.PositiveSmallIntegerField('推送人员过滤', null=True, blank=True) + filter_sender = models.PositiveSmallIntegerField('提醒人员过滤', null=True, blank=True) filter_area_level = models.PositiveSmallIntegerField('区域级别过滤', null=True, blank=True) sms_enable = models.BooleanField('短信通知', default=False) + wechat_enable = models.BooleanField('开启微信通知', default=False) can_handle = models.BooleanField('是否可处理', default=False) sort = models.PositiveIntegerField('排序', default=1) @@ -49,7 +51,7 @@ class Event(CommonBModel): """ 事件 """ - cate = models.ForeignKey(EventCate, verbose_name='事件种类') + cate = models.ForeignKey(EventCate, verbose_name='事件种类', on_delete=models.CASCADE) imgs = models.JSONField('事件图片', default=list, null=False, blank=True) area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE) location = models.JSONField('事件点位坐标', default=dict, null=False, blank=True) @@ -64,22 +66,20 @@ class Event(CommonBModel): is_pushed = models.BooleanField('是否已推送', default=False) -class Push(BaseModel): +class Remind(BaseModel): """ - 推送情况 + 事件提醒表 """ event = models.ForeignKey(Event, verbose_name='关联事件', on_delete=models.CASCADE) - pusher = models.ForeignKey(User, verbose_name='推送人', - on_delete=models.CASCADE) - push_setting = models.ForeignKey(PushSetting, verbose_name='通过哪个配置', - on_delete=models.CASCADE, null=True, blank=True) + recipient = models.ForeignKey(User, verbose_name='接收人', + on_delete=models.CASCADE) + notify_setting = models.ForeignKey(NotifySetting, verbose_name='通过哪个配置', + on_delete=models.CASCADE, null=True, blank=True) post = models.ForeignKey(Post, verbose_name='岗位', on_delete=models.CASCADE, null=True, blank=True) dept = models.ForeignKey(Dept, verbose_name='部门', on_delete=models.CASCADE, null=True, blank=True) - msg = models.TextField('推送文本') + msg = models.TextField('推送文本', null=True, blank=True) is_read = models.BooleanField('站内信已读', default=False) - is_sms_send = models.BooleanField('短信已发送', default=False) can_handle = models.BooleanField('是否可处理', default=False) - last_push_time = models.DateTimeField('最后推送时间', default=timezone.now) diff --git a/apps/ecm/serializers.py b/apps/ecm/serializers.py index 94bb9b48..f41620bc 100644 --- a/apps/ecm/serializers.py +++ b/apps/ecm/serializers.py @@ -1,5 +1,5 @@ from apps.am.serializers import AreaSimpleSerializer -from apps.ecm.models import EventCate, Push, PushSetting, Event +from apps.ecm.models import EventCate, Remind, NotifySetting, Event from apps.utils.serializers import CustomModelSerializer from rest_framework import serializers from apps.system.serializers import UserSimpleSerializer @@ -23,9 +23,9 @@ class EventCateUpdateSerializer(CustomModelSerializer): fields = ['speaker_on', 'speakers', 'filter_area_level'] -class PushSettingsSerializer(CustomModelSerializer): +class NotifySettingsSerializer(CustomModelSerializer): class Meta: - model = PushSetting + model = NotifySetting fields = '__all__' @@ -46,9 +46,9 @@ class EventHandleSerializer(CustomModelSerializer): fields = ['handle_desc'] -class PushSerializer(CustomModelSerializer): - pusher_ = UserSimpleSerializer(source='pusher', read_only=True) - +class RemindSerializer(CustomModelSerializer): + recipient_ = UserSimpleSerializer(source='recipient', read_only=True) + class Meta: - model = Push + model = Remind fields = '__all__' diff --git a/apps/ecm/urls.py b/apps/ecm/urls.py index a5b4ffdf..6c72b27e 100644 --- a/apps/ecm/urls.py +++ b/apps/ecm/urls.py @@ -1,4 +1,4 @@ -from apps.ecm.views import EventCateViewSet, PushSettingViewSet, EventViewSet, PushViewSet +from apps.ecm.views import EventCateViewSet, NotifySettingViewSet, EventViewSet, RemindViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter @@ -8,8 +8,8 @@ HTML_BASE_URL = 'ecm/' router = DefaultRouter() router.register('event_cate', EventCateViewSet, basename='event_cate') router.register('event', EventViewSet, basename='event') -router.register('push_setting', PushSettingViewSet, basename='push_setting') -router.register('push', PushViewSet, basename='push') +router.register('notify_setting', NotifySettingViewSet, basename='notify_setting') +router.register('remind', RemindViewSet, basename='remind') urlpatterns = [ path(API_BASE_URL, include(router.urls)), ] diff --git a/apps/ecm/views.py b/apps/ecm/views.py index c1e786ae..f2961f9f 100644 --- a/apps/ecm/views.py +++ b/apps/ecm/views.py @@ -1,7 +1,7 @@ -from apps.ecm.models import Event, EventCate, Push, PushSetting +from apps.ecm.models import Event, EventCate, Remind, NotifySetting from apps.ecm.serializers import (EventCateListSerializer, EventCateUpdateSerializer, EventHandleSerializer, - EventSerializer, PushSerializer, PushSettingsSerializer) + EventSerializer, RemindSerializer, NotifySettingsSerializer) from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from rest_framework.mixins import UpdateModelMixin, ListModelMixin, RetrieveModelMixin from django.db import transaction @@ -19,9 +19,9 @@ class EventCateViewSet(UpdateModelMixin, ListModelMixin, CustomGenericViewSet): update_serializer_class = EventCateUpdateSerializer -class PushSettingViewSet(CustomModelViewSet): - queryset = PushSetting.objects.all() - serializer_class = PushSettingsSerializer +class NotifySettingViewSet(CustomModelViewSet): + queryset = NotifySetting.objects.all() + serializer_class = NotifySettingsSerializer class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): @@ -45,10 +45,10 @@ class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): return Response() -class PushViewSet(ListModelMixin, CustomGenericViewSet): - perms_map = {'get': 'push:view'} - queryset = Push.objects.all() - serializer_class = PushSerializer +class RemindViewSet(ListModelMixin, CustomGenericViewSet): + perms_map = {'get': 'envent:view'} + queryset = Remind.objects.all() + serializer_class = RemindSerializer @action(methods=['get'], detail=False, perms_map={'get': '*'}) def my(self, request, *args, **kwargs): @@ -56,7 +56,7 @@ class PushViewSet(ListModelMixin, CustomGenericViewSet): 推送给我的 """ user = self.request.user - queryset = self.filter_queryset(self.get_queryset().filter(pusher=user)) + queryset = self.filter_queryset(self.get_queryset().filter(recipient=user)) page = self.paginate_queryset(queryset) if page is not None: diff --git a/apps/hrm/migrations/0001_initial.py b/apps/hrm/migrations/0001_initial.py new file mode 100644 index 00000000..a8ea139f --- /dev/null +++ b/apps/hrm/migrations/0001_initial.py @@ -0,0 +1,108 @@ +# Generated by Django 3.2.12 on 2022-06-14 05:00 + +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 = [ + ('system', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='NotWorkRemark', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('not_work_date', models.DateField(verbose_name='未打卡日期')), + ('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='未打卡说明')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Employee', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('type', models.CharField(choices=[('employee', '内部员工'), ('remployee', '相关方人员'), ('visitor', '访客')], default='employee', max_length=10, verbose_name='人员类型')), + ('name', models.CharField(max_length=20, verbose_name='姓名')), + ('phone', models.CharField(blank=True, max_length=11, null=True, unique=True, verbose_name='手机号')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='邮箱号')), + ('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='人员编号')), + ('photo', models.CharField(blank=True, max_length=1000, null=True, verbose_name='证件照')), + ('id_number', models.CharField(blank=True, max_length=100, null=True, unique=True, verbose_name='身份证号')), + ('gender', models.CharField(default='男', max_length=10, verbose_name='性别')), + ('signature', models.CharField(blank=True, max_length=200, null=True, verbose_name='签名图片')), + ('birthday', models.DateField(blank=True, null=True, verbose_name='出生年月日')), + ('qualification', models.CharField(blank=True, max_length=50, null=True, verbose_name='学历')), + ('job_state', models.IntegerField(choices=[(10, '在职'), (20, '离职')], default=1, verbose_name='在职状态')), + ('face_data', models.JSONField(blank=True, null=True, verbose_name='人脸识别数据')), + ('is_atwork', models.BooleanField(default=False, verbose_name='当前在岗')), + ('show_atwork', models.BooleanField(default=True, verbose_name='是否展示在岗状态')), + ('last_check_time', models.DateTimeField(blank=True, null=True, verbose_name='打卡时间')), + ('not_work_remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='当前未打卡说明')), + ('third_info', models.JSONField(blank=True, default=dict, verbose_name='三方信息')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_belong_dept', to='system.dept', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='系统账号')), + ], + options={ + 'verbose_name': '员工补充信息', + 'verbose_name_plural': '员工补充信息', + }, + ), + migrations.CreateModel( + name='ClockRecord', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('type', models.PositiveSmallIntegerField(choices=[(10, '上班打卡')], default=10, verbose_name='打卡类型')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Certificate', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('name', models.CharField(max_length=20, verbose_name='证书名称')), + ('number', models.CharField(max_length=20, unique=True, verbose_name='证书编号')), + ('type', models.PositiveSmallIntegerField(default=10, verbose_name='证书类型')), + ('issue_date', models.DateField(verbose_name='发证日期')), + ('expiration_date', models.DateField(verbose_name='有效期')), + ('review_date', models.DateField(verbose_name='下一次复审日期')), + ('file', models.TextField(blank=True, null=True, verbose_name='文件地址')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='certificate_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='对应人员')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='certificate_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/hrm/migrations/0002_auto_20220617_1124.py b/apps/hrm/migrations/0002_auto_20220617_1124.py new file mode 100644 index 00000000..988b5db6 --- /dev/null +++ b/apps/hrm/migrations/0002_auto_20220617_1124.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.12 on 2022-06-17 03:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hrm', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='employee', + name='face_data', + ), + migrations.AlterField( + model_name='employee', + name='job_state', + field=models.IntegerField(choices=[(10, '在职'), (20, '离职')], default=10, verbose_name='在职状态'), + ), + ] diff --git a/apps/hrm/migrations/__init__.py b/apps/hrm/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/hrm/models.py b/apps/hrm/models.py index 1a9afe75..f7ccc3a2 100755 --- a/apps/hrm/models.py +++ b/apps/hrm/models.py @@ -24,17 +24,16 @@ class Employee(CommonBModel): verbose_name='系统账号', on_delete=models.PROTECT, null=True, blank=True) name = models.CharField('姓名', max_length=20) - phone = models.CharField('手机号', max_length=11, null=True, blank=True) + phone = models.CharField('手机号', max_length=11, null=True, blank=True, unique=True) email = models.EmailField('邮箱号', null=True, blank=True) number = models.CharField('人员编号', max_length=50, null=True, blank=True) photo = models.CharField('证件照', max_length=1000, null=True, blank=True) - id_number = models.CharField('身份证号', max_length=100, null=True, blank=True) + id_number = models.CharField('身份证号', max_length=100, null=True, blank=True, unique=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) + job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=10) is_atwork = models.BooleanField('当前在岗', default=False) show_atwork = models.BooleanField('是否展示在岗状态', default=True) last_check_time = models.DateTimeField('打卡时间', null=True, blank=True) @@ -88,9 +87,9 @@ class Certificate(CommonAModel): ) employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE) name = models.CharField('证书名称', max_length=20) - number = models.CharField('证书编号', max_length=20) + number = models.CharField('证书编号', max_length=20, unique=True) type = models.PositiveSmallIntegerField('证书类型', default=10) issue_date = models.DateField('发证日期') expiration_date = models.DateField('有效期') review_date = models.DateField('下一次复审日期') - file = models.CharField('文件地址', max_length=1000, null=True, blank=True) + file = models.TextField('文件地址', null=True, blank=True) diff --git a/apps/hrm/serializers.py b/apps/hrm/serializers.py index ea04020b..a2d0be79 100755 --- a/apps/hrm/serializers.py +++ b/apps/hrm/serializers.py @@ -12,6 +12,7 @@ from apps.third.clients import dhClient from apps.third.tapis import dhapis import re from datetime import datetime +from django.conf import settings class EmployeeBaseSerializer(CustomModelSerializer): @@ -35,8 +36,7 @@ class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer): class Meta: model = Employee - exclude = EXCLUDE_FIELDS + ['face_data', - 'is_atwork', 'last_check_time', + exclude = EXCLUDE_FIELDS + ['is_atwork', 'last_check_time', 'not_work_remark', 'third_info', 'type'] extra_kwargs = { 'phone': {'required': True}, @@ -48,7 +48,7 @@ class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer): @transaction.atomic def create(self, validated_data): instance = super().create(validated_data) - if dhClient: + if settings.DAHUA_ENABLED and dhClient: # 创建人员 _, res = dhClient.request(**dhapis['person_gen_id']) personId = res['id'] @@ -118,7 +118,7 @@ class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer): departmentId = instance.belong_dept.third_info['dh_id'] except Exception: pass - if dhClient: + if settings.DAHUA_ENABLED and dhClient: third_info = instance.third_info dh_id = instance.third_info['dh_id'] dh_photo = third_info['dh_photo'] @@ -192,7 +192,7 @@ class EmployeeSerializer(EmployeeBaseSerializer): class Meta: model = Employee - exclude = ['face_data'] + fields = '__all__' read_only_fields = ['is_atwork', 'last_check_time', 'not_work_remark'] diff --git a/apps/hrm/signals.py b/apps/hrm/signals.py index aa5d34c3..b3f078e5 100755 --- a/apps/hrm/signals.py +++ b/apps/hrm/signals.py @@ -8,9 +8,17 @@ from apps.hrm.models import Employee def updateEmployee(sender, instance, created, **kwargs): # if created: # 如果账号所属部门有变动, 更新关联人员的所属部门, 只限内部人员 - if not instance.is_superuser: + if not instance.is_superuser and instance.type == 'employee': ep = Employee.objects.filter(user=instance).first() - if ep and ep.type == 'employee': + if ep: if ep.belong_dept and ep.belong_dept != instance.belong_dept: ep.belong_dept = instance.belong_dept ep.save() + else: + Employee.objects.get_or_create(user=instance, + defaults={ + "user": instance, + "name": instance.name, + "phone": instance.phone, + "belong_dept": instance.belong_dept + }) diff --git a/apps/hrm/views.py b/apps/hrm/views.py index a87d093c..4903890c 100755 --- a/apps/hrm/views.py +++ b/apps/hrm/views.py @@ -30,7 +30,7 @@ class EmployeeViewSet(CustomModelViewSet): """ 人员管理 """ - queryset = Employee.objects.all() + queryset = Employee.objects.filter(type='employee') select_related_fields = ['user'] filterset_class = EmployeeFilterSet serializer_class = EmployeeSerializer diff --git a/apps/opm/models.py b/apps/opm/models.py index cfeef758..a808641c 100644 --- a/apps/opm/models.py +++ b/apps/opm/models.py @@ -1,34 +1,38 @@ from django.db import models -from apps.system.models import Dept, Dictionary, User -from apps.utils.models import BaseModel, CommonAModel, CommonBModel +from apps.hrm.models import Certificate +from apps.system.models import Dept, Dictionary, File, User +from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBDModel, CommonBModel from apps.am.models import Area from apps.wf.models import Ticket, Workflow # Create your models here. -class OptCate(CommonAModel): +class OplCate(CommonAModel): """ - 作业票种类 + 作业许可种类 """ code = models.CharField('标识', max_length=10, unique=True) name = models.CharField('名称', max_length=20, unique=True) + description = models.TextField('描述', null=True, blank=True) + workflow = models.ForeignKey(Workflow, verbose_name='使用的工作流') template_export = models.TextField('导出word模板', null=True, blank=True) - workflow = models.ForeignKey(Workflow, verbose_name='所用工作流', - on_delete=models.CASCADE, null=True, blank=True) + risk_options = models.ManyToManyField(Dictionary, verbose_name='风险分析') + measure_options = models.ManyToManyField(Dictionary, verbose_name='控制措施') + close_options = models.ManyToManyField(Dictionary, verbose_name='关闭工作') -class Operation(CommonBModel): +class Operation(CommonBDModel): + OP_CREATE = 10 + OP_AUDIT = 20 + OP_WAIT = 30 + OP_WORK = 40 + OP_CLOSE = 50 OP_STATE_CHOICES = ( - (10, '创建中'), - (20, '审批中'), - (30, '待作业'), - (40, '待关闭'), - (50, '已关闭') - ) - OP_WORK_CHOICES = ( - ('work', '运行'), - ('stop', '停机'), - ('repair', '检修') + (OP_CREATE, '创建中'), + (OP_AUDIT, '审批中'), + (OP_WAIT, '待作业'), + (OP_WORK, '作业中'), + (OP_CLOSE, '已关闭') ) number = models.CharField('作业编号', max_length=20, null=True, blank=True) name = models.CharField('作业简述', max_length=200) @@ -37,33 +41,109 @@ class Operation(CommonBModel): place = models.TextField('具体地点', null=True, blank=True) start_time = models.DateTimeField('作业开始时间') end_time = models.DateTimeField('作业结束时间') - dept_sd = models.ForeignKey(Dept, verbose_name='属地部门', - on_delete=models.CASCADE) - dept_yw = models.ForeignKey(Dept, verbose_name='业务部门', - on_delete=models.CASCADE) - user_xt = models.ForeignKey(User, verbose_name='业务部门协调员', - on_delete=models.CASCADE) - user_fz = models.ForeignKey(User, verbose_name='作业负责人', - on_delete=models.CASCADE) - user_jh = models.ForeignKey(User, verbose_name='作业监护人', - ) - state_work = models.CharField('生产状态', choices=OP_WORK_CHOICES) + dept_ter = models.ForeignKey(Dept, verbose_name='属地部门', + on_delete=models.CASCADE) + dept_bus = models.ForeignKey(Dept, verbose_name='业务部门', + on_delete=models.CASCADE) + coordinator = models.ForeignKey(User, verbose_name='业务部门协调员', + on_delete=models.CASCADE) + state_work = models.CharField('生产状态', max_length=20, help_text='运行/停机/检修') -class Opt(CommonBModel): +class Opl(CommonBDModel): """ - 作业票 + 作业许可证 """ + CLOSE_CHOICES1 = ( + (10, '作业正常结束'), + (20, '因计划改变停止作业'), + (30, '因发生异常终止作业'), + (40, '其他') + ) operation = models.ForeignKey(Operation, verbose_name='关联作业', - on_delete=models.CASCADE) - number = models.CharField('作业票编号', max_length=20, null=True, blank=True) - cate = models.ForeignKey(OptCate, verbose_name='作业票种类', + on_delete=models.CASCADE, related_name='opl_operation') + number = models.CharField('作业许可编号', max_length=20, null=True, blank=True) + level = models.CharField('作业级别', max_length=20, + help_text='特技/三级/二级/主干道/次干道', null=True, blank=True) + cate = models.ForeignKey(OplCate, verbose_name='作业许可种类', on_delete=models.CASCADE) - dept_zy = models.ForeignKey(Dept, verbose_name='作业部门', + start_time = models.DateTimeField('作业开始时间', null=True, blank=True) + end_time = models.DateTimeField('作业结束时间', null=True, blank=True) + dept_do = models.ForeignKey(Dept, verbose_name='作业部门', on_delete=models.CASCADE) - user_fz = models.ForeignKey(User, verbose_name='作业负责人', - on_delete=models.CASCADE) - user_jh = models.ForeignKey(User, verbose_name='作业监护人', - ) - ticket = models.ForeignKey(Ticket, verbose_name='关联工单', - on_delete=models.CASCADE, null=True, blank=True) + charger = models.ForeignKey(User, verbose_name='作业负责人', + on_delete=models.CASCADE, related_name='opl_charger') + monitor = models.ForeignKey(User, verbose_name='作业监护人', + on_delete=models.CASCADE, related_name='opl_monitor') + # electrician = models.ForeignKey(User, verbose_name='电工', + # on_delete=models.CASCADE, + # related_name='opl_electrician', null=True, blank=True) + work_time = models.DateTimeField('安装/拆除时间', null=True, blank=True) + accept_time = models.DateTimeField('验收时间', null=True, blank=True) + work_type = models.CharField('安装/拆除', max_length=20, null=True, blank=True) + power = models.PositiveIntegerField('用电功率', null=True, blank=True) + power_to = models.CharField('用电地点', max_length=20, null=True, blank=True) + power_from = models.CharField('电源接入点', max_length=20, null=True, blank=True) + power_end_time = models.DateTimeField('用电截至', null=True, blank=True) + power_start_time = models.DateTimeField('用电开始', null=True, blank=True) + power_days = models.PositiveSmallIntegerField('计划用电天数', null=True, blank=True) + other_risk = models.TextField('其他风险', null=True, blank=True) + other_emr = models.TextField('其他应急处置', null=True, blank=True) + escape_route = models.TextField('逃生路径', null=True, blank=True) + risks_checked = models.JSONField('风险选择', default=list, null=False, blank=True) + measures_checked = models.JSONField('措施选择', default=list, null=False, blank=True) + workers = models.ManyToManyField(User, verbose_name='作业人员', through='opm.oplworker') + create_imgs = models.ManyToManyField(File, verbose_name='作业审批照片') + close_imgs = models.ManyToManyField(File, verbose_name='作业关闭照片') + close_note = models.PositiveSmallIntegerField('作业关闭情况', + choices=CLOSE_CHOICES1, + help_text='正常结束/计划改变停止/发生异常停止/其他', + null=True, blank=True) + close_dos = models.JSONField('关闭工作', default=list, null=False, blank=True) + close_desc = models.TextField('作业关闭描述', null=True, blank=True) + ticket = models.OneToOneField(Ticket, verbose_name='关联工单', + on_delete=models.CASCADE, + null=True, blank=True) + + +class OplWorker(BaseModel): + """ + 作业许可人员 + """ + worker = models.ForeignKey(User, verbose_name='作业人员', + on_delete=models.CASCADE) + opl = models.ForeignKey(Opl, verbose_name='作业许可', + on_delete=models.CASCADE) + duty = models.CharField('工作职责', null=True, blank=True, help_text='作业人员/起重司机/司索人员/起重指挥/电工') + certificates = models.ManyToManyField(Certificate, verbose_name='关联证书', through='opt.optcert') + + +class OplCert(BaseModel): + """ + 作业许可人员证书 + """ + opl_worker = models.ForeignKey(OplWorker, verbose_name='关联作业人员', + on_delete=models.CASCADE) + certificate = models.ForeignKey(Certificate, verbose_name='关联证书', + on_delete=models.CASCADE) + name = models.CharField('证书名称', max_length=20) + number = models.CharField('证书编号', max_length=20) + type = models.PositiveSmallIntegerField('证书类型', default=10) + issue_date = models.DateField('发证日期') + expiration_date = models.DateField('有效期') + review_date = models.DateField('下一次复审日期') + file = models.TextField('文件地址', null=True, blank=True) + + +class GasCheck(CommonADModel): + """ + 气体检测记录 + """ + check_time = models.DateTimeField('检测时间') + check_place = models.CharField('检测部位') + o2 = models.DecimalField(decimal_places=1, max_digits=3) + co = models.PositiveSmallIntegerField() + lel = models.DecimalField(decimal_places=1, max_digits=3) + is_ok = models.BooleanField('是否正常', default=True) + checker = models.ForeignKey(User, verbose_name='检测人') + opl = models.ForeignKey(Opl, verbose_name='关联作业许可') diff --git a/apps/opm/serializers.py b/apps/opm/serializers.py index d6ec2071..ef56c486 100644 --- a/apps/opm/serializers.py +++ b/apps/opm/serializers.py @@ -1,30 +1,118 @@ -from apps.opm.models import Operation, OptCate +from apps.hrm.models import Certificate +from apps.opm.models import GasCheck, Operation, Opl, OplCate, OplCert, OplWorker from apps.utils.serializers import CustomModelSerializer -from apps.wf.serializers import WorkflowSimpleSerializer from apps.utils.constants import EXCLUDE_FIELDS +from rest_framework import serializers +from django.db import transaction +from rest_framework.exceptions import ParseError -class OptCateCreateUpdateSerializer(CustomModelSerializer): +class OplCateCreateUpdateSerializer(CustomModelSerializer): class Meta: - model = OptCate + model = OplCate fields = ['code', 'name', 'template_export', 'workflow'] -class OptCateSerializer(CustomModelSerializer): - workflow_ = WorkflowSimpleSerializer(source='workflow', read_only=True) +class OplCateSerializer(CustomModelSerializer): class Meta: - model = OptCate + model = OplCate fields = '__all__' class OperationCreateUpdateSerializer(CustomModelSerializer): class Meta: model = Operation - fields = EXCLUDE_FIELDS + ['number'] + exclude = EXCLUDE_FIELDS + ['number'] class OperationSerializer(CustomModelSerializer): class Meta: model = Operation fields = "__all__" + + +class OplWorkerCreateSerializer(CustomModelSerializer): + certificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True) + + class Meta: + model = OplWorker + fields = ['worker', 'duty', 'certificates', 'opl'] + + def create(self, validated_data): + certificates = validated_data.pop('certificates') + if OplWorker.objects.filter(worker=validated_data['worker'], opl=validated_data['opl']).exists(): + raise ParseError('该成员已加入') + with transaction.atomic(): + oplw = super().create(validated_data) + for x in certificates: + oplc, _ = OplCert.objects.get_or_create(opl_worker=oplw, certificate=x, + defaults={'opl_worker': oplw, 'certificate': x}) + for f in Certificate._meta.fields: + if f.name not in ['id']: + setattr(oplc, f.name, getattr(x, f.name, None)) + oplc.save() + return oplw + + +class OplWorkerUpdateSerializer(CustomModelSerializer): + certificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True) + + class Meta: + model = OplWorker + fields = ['duty', 'certificates'] + + def update(self, instance, validated_data): + certificates = validated_data.pop('certificates') + with transaction.atomic(): + oplw = super().update(instance, validated_data) + for x in certificates: + oplc, _ = OplCert.objects.get_or_create(opl_worker=oplw, certificate=x, + defaults={'opl_worker': oplw, 'certificate': x}) + for f in Certificate._meta.fields: + if f.name not in ['id']: + setattr(oplc, f.name, getattr(x, f.name, None)) + oplc.save() + return oplw + + +class OplWorkerSerializer(): + class Meta: + model = OplWorker + fields = '__all__' + + +class GasCheckCreateUpdateSerializer(CustomModelSerializer): + class Meta: + model = GasCheck + exclude = EXCLUDE_FIELDS + + +class GasCheckSerializer(CustomModelSerializer): + class Meta: + model = GasCheck + fields = '__all__' + + +class OplCreateUpdateSerializer(CustomModelSerializer): + + class Meta: + model = Opl + fields = ['operation', 'level', 'cate', 'start_time', 'end_time', + 'dept_do', 'charger', 'monitor', 'work_time', 'work_type', + 'accept_time', 'power', 'power_to', 'power_from', + 'power_end_time', 'power_start_time', 'power_days', + 'other_risk', 'other_emr', 'escape_route', + 'risks_checked', 'measures_checked', 'create_imgs'] + + +class OplSerializer(CustomModelSerializer): + class Meta: + model = Opl + fields = '__all__' + + +class OplCloseSerializer(CustomModelSerializer): + class Meta: + model = Opl + fields = ['close_imgs', 'close_note', 'close_dos', 'close_desc'] diff --git a/apps/opm/services.py b/apps/opm/services.py index 86defa84..fb154435 100644 --- a/apps/opm/services.py +++ b/apps/opm/services.py @@ -1,5 +1,28 @@ -from apps.wf.models import Ticket +from apps.opm.models import Operation, Opl +from apps.opm.serializers import OplCloseSerializer +from apps.wf.models import Ticket, Transition -def create_opt(ticket: Ticket): - pass \ No newline at end of file +def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict): + opl = Opl.objects.get(id=new_ticket_data['opl']) + ticket_data = ticket.ticket_data + ticket_data.update({'level': opl.level}) + ticket.ticket_data = ticket_data + ticket.save() + opl.ticket = opl + opl.number = ticket.sn + opl.save() + + +def close_opl_submit(ticket: Ticket, transition: Transition, new_ticket_data: dict): + opl = Opl.objects.get(ticket=ticket) + serializer = OplCloseSerializer(instance=opl, data=new_ticket_data, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + + +def give_perm_by_opl(): + """ + 根据许可证授予区域进入权限 + """ + pass diff --git a/apps/opm/tasks.py b/apps/opm/tasks.py new file mode 100644 index 00000000..c7abbb1c --- /dev/null +++ b/apps/opm/tasks.py @@ -0,0 +1,44 @@ +# Create your tasks here +from __future__ import absolute_import, unicode_literals +from apps.opm.models import Operation, Opl +from apps.opm.services import give_perm_by_opl +from apps.utils.task import CustomTask +from celery import shared_task + +from apps.wf.models import State, Ticket + + +@shared_task(base=CustomTask) +def opl_end(ticket_id): + """ + 作业关闭时执行 + """ + ticket = Ticket.objects.get(id=ticket_id) + operation = ticket.opl.operation + opls = Opl.objects.filter(operation=operation) + opls.filter(ticket=None).delete() # 删除无用许可证 + states = opls.values_list('ticket__state__type', flat=True) + if 0 in states or 1 in states: + pass + else: + operation.state = Operation.OP_CLOSE + operation.save() + + +@shared_task(base=CustomTask) +def opl_audit_start(ticket_id): + operation = Opl.objects.get(ticket__id=ticket_id).operation + if operation.state == Operation.OP_CREATE: + operation.state = Operation.OP_AUDIT + operation.save() + + +@shared_task(base=CustomTask) +def opl_audit_end(ticket_id): + opl = Opl.objects.get(ticket__id=ticket_id) + operation = opl.operation + if operation.state == Operation.OP_AUDIT: + operation.state = Operation.OP_WORK + operation.save() + # 授予区域或围栏权限 + give_perm_by_opl(opl) diff --git a/apps/opm/views.py b/apps/opm/views.py index 68467073..507eda15 100644 --- a/apps/opm/views.py +++ b/apps/opm/views.py @@ -1,19 +1,108 @@ from django.shortcuts import render -from apps.opm.models import Operation, OptCate -from apps.opm.serializers import OperationCreateUpdateSerializer, OptCateCreateUpdateSerializer, OptCateSerializer -from apps.utils.viewsets import CustomModelViewSet - +from rest_framework.response import Response +from apps.opm.models import GasCheck, Operation, Opl, OplCate, OplWorker +from apps.opm.serializers import GasCheckCreateUpdateSerializer, GasCheckSerializer, OperationCreateUpdateSerializer, OperationSerializer, OplCateCreateUpdateSerializer, OplCateSerializer, OplSerializer, OplWorkerCreateSerializer, OplWorkerUpdateSerializer +from apps.opm.services import create_opl_ticket +from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet +from rest_framework.exceptions import ParseError +from rest_framework.mixins import CreateModelMixin, ListModelMixin, DestroyModelMixin +from rest_framework.decorators import action +from rest_framework import serializers +from django.db import transaction # Create your views here. -class OptCateViewSet(CustomModelViewSet): - queryset = OptCate.objects.all() - create_serializer_class = OptCateCreateUpdateSerializer - update_serializer_class = OptCateCreateUpdateSerializer - serializer_class = OptCateSerializer +class OplCateViewSet(CustomModelViewSet): + queryset = OplCate.objects.all() + create_serializer_class = OplCateCreateUpdateSerializer + update_serializer_class = OplCateCreateUpdateSerializer + serializer_class = OplCateSerializer class OperationViewSet(CustomModelViewSet): queryset = Operation.objects.all() create_serializer_class = OperationCreateUpdateSerializer update_serializer_class = OperationCreateUpdateSerializer - \ No newline at end of file + serializer_class = OperationSerializer + + def update(self, request, *args, **kwargs): + obj = self.get_object() + if obj.state != Operation.OP_CREATE: + raise ParseError('非创建状态不可修改') + return super().update(request, *args, **kwargs) + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.state != Operation.OP_CREATE: + raise ParseError('非创建状态不可删除') + obj.delete(soft=False) + return Response(status=204) + + +class OplViewSet(CustomModelViewSet): + queryset = Opl.objects.all() + create_serializer_class = OplCateCreateUpdateSerializer + update_serializer_class = OplCateCreateUpdateSerializer + serializer_class = OplSerializer + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.ticket: + raise ParseError('许可证已处理不可删除') + return super().destroy(request, *args, **kwargs) + + def create(self, request, *args, **kwargs): + obj = self.get_object() + if obj.operation.state == Operation.OP_CLOSE: + raise ParseError('作业已关闭不可创建许可') + return super().create(request, *args, **kwargs) + + def update(self, request, *args, **kwargs): + obj = self.get_object() + if obj.ticket: + raise ParseError('许可证已处理不可编辑') + return super().update(request, *args, **kwargs) + + +class OplWorkerViewSet(CustomModelViewSet): + perms_map = {'get': '*', 'post': 'opl:update', 'put': 'opl:update', 'delete': 'opl:update'} + queryset = OplWorker.objects.all() + create_serializer_class = OplWorkerCreateSerializer + update_serializer_class = OplWorkerUpdateSerializer + serializer_class = OplSerializer + + def create(self, request, *args, **kwargs): + obj = self.get_object() + if obj.opl.ticket: + raise ParseError('许可证已处理不可新增') + return super().create(request, *args, **kwargs) + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.opl.ticket: + raise ParseError('许可证已处理不可删除') + return super().destroy(request, *args, **kwargs) + + def update(self, request, *args, **kwargs): + obj = self.get_object() + if obj.opl.ticket: + raise ParseError('许可证已处理不可编辑') + return super().update(request, *args, **kwargs) + + +class GasCheckViewSet(CreateModelMixin, ListModelMixin, DestroyModelMixin, CustomGenericViewSet): + perms_map = {'get': '*', 'post': 'opl:update', 'delete': 'opl:update'} + queryset = GasCheck.objects.all() + create_serializer_class = GasCheckCreateUpdateSerializer + serializer_class = GasCheckSerializer + + def create(self, request, *args, **kwargs): + obj = self.get_object() + if obj.opl.ticket: + raise ParseError('许可证已处理不可新增') + return super().create(request, *args, **kwargs) + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.opl.ticket: + raise ParseError('许可证已处理不可删除') + return super().destroy(request, *args, **kwargs) diff --git a/apps/rpm/models.py b/apps/rpm/models.py index a67c2e3d..f2639b5f 100644 --- a/apps/rpm/models.py +++ b/apps/rpm/models.py @@ -1,7 +1,8 @@ from django.db import models from apps.hrm.models import Certificate, Employee from apps.system.models import Dept, Dictionary, File, User -from apps.utils.models import CommonBModel, BaseModel +from apps.utils.models import CommonAModel, CommonBDModel, CommonBModel, BaseModel +from apps.wf.models import Ticket # Create your models here. @@ -20,7 +21,6 @@ class Rparty(CommonBModel): email = models.EmailField('邮箱', null=True, blank=True) addresss = models.CharField('企业地址', max_length=200, null=True, blank=True) description = models.TextField('概述', null=True, blank=True) - # belong_dept是归属部门 class RpartyFile(BaseModel): @@ -33,15 +33,15 @@ class RpartyFile(BaseModel): rparty = models.ForeignKey(Rparty, verbose_name='关联相关方', on_delete=models.CASCADE) -class Rproject(CommonBModel): +class Rpj(CommonBDModel): """ 相关方项目 """ - RP_START = 10 - RP_APPROVAL = 20 - RP_ENTER = 30 - RP_WORKING = 40 - RP_DONE = 50 + RPJ_CREATE = 10 + RPJ_AUDIT = 20 + RPJ_ENTER = 30 + RPJ_WORKING = 40 + RPJ_DONE = 50 RP_STATE_CHOICES = ( (10, '创建中'), (20, '审批中'), @@ -63,45 +63,72 @@ class Rproject(CommonBModel): state = models.PositiveSmallIntegerField('状态', default=10) rparty = models.ForeignKey(Rparty, verbose_name='关联相关方', on_delete=models.CASCADE) # belong_dept是业务部门可以带过来 + ticket = models.ForeignKey(Ticket, verbose_name='关联工单', + on_delete=models.CASCADE, null=True, blank=True) -class RprojectFile(BaseModel): +class RpjFile(BaseModel): """ 相关方项目文件库 """ file_cate = models.ForeignKey(Dictionary, verbose_name='文件种类', on_delete=models.CASCADE, null=True, blank=True) files = models.ManyToManyField(File, verbose_name='文件') - rproject = models.ForeignKey(Rproject, verbose_name='关联相关方项目', on_delete=models.CASCADE) + rpj = models.ForeignKey(Rpj, verbose_name='关联相关方项目', on_delete=models.CASCADE) -class Remployee(CommonBModel): +class Remployee(CommonAModel): """ 相关方成员 """ - employee = models.OneToOneField(Employee, verbose_name='成员信息', on_delete=models.CASCADE) - rparty = models.ForeignKey(Rparty, verbose_name='所属相关方', on_delete=models.CASCADE) - rproject = models.ForeignKey(Rproject, verbose_name='当前所属相关方项目', on_delete=models.CASCADE, - null=True, blank=True) - - -class Rpeople(BaseModel): - """ - 相关方项目人员 - """ - rproject = models.ForeignKey(Rproject, verbose_name='关联项目', on_delete=models.CASCADE) - employee = models.ForeignKey(Employee, verbose_name='关联人员', + employee = models.ForeignKey(Employee, verbose_name='成员信息', on_delete=models.CASCADE, null=True, blank=True) + name = models.CharField('姓名', max_length=20) + phone = models.CharField('手机号', max_length=11) + photo = models.CharField('证件照', max_length=1000) + id_number = models.CharField('身份证号', max_length=100) + rparty = models.ForeignKey(Rparty, verbose_name='所属相关方', on_delete=models.CASCADE) + rpj = models.ForeignKey(Rpj, verbose_name='最近所属相关方项目', on_delete=models.CASCADE, + null=True, blank=True) + + +class Rcertificate(CommonAModel): + """ + 相关方证书 + """ + certificate = models.ForeignKey(Certificate, verbose_name='关联认可证书', + on_delete=models.CASCADE, + null=True, blank=True) + remployee = models.ForeignKey(Remployee, verbose_name='关联相关方成员', + on_delete=models.CASCADE) + name = models.CharField('证书名称', max_length=20) + number = models.CharField('证书编号', max_length=20, unique=True) + type = models.PositiveSmallIntegerField('证书类型', default=10, choices=Certificate.CERTIFICATE_TYPE_CHOICES) + issue_date = models.DateField('发证日期') + expiration_date = models.DateField('有效期') + review_date = models.DateField('下一次复审日期') + file = models.CharField('文件地址', max_length=1000, null=True, blank=True) + + +class Rpjmember(BaseModel): + """ + 相关方项目成员 + """ + rpj = models.ForeignKey(Rpj, verbose_name='关联项目', on_delete=models.CASCADE) + remployee = models.ForeignKey(Remployee, verbose_name='关联人员', + on_delete=models.CASCADE, null=True, blank=True) + duty = models.CharField('职责', max_length=20, null=True, blank=True) is_manager = models.BooleanField('是否项目负责人', default=False) + rcertificates = models.ManyToManyField(Rcertificate, through='rpm.rpjcertificate') -class Rcertificate(BaseModel): +class Rpjcertificate(BaseModel): """ 相关方项目人员证书 """ - rproject = models.ForeignKey(Rproject, verbose_name='关联项目', on_delete=models.CASCADE) - employee = models.ForeignKey(Employee, verbose_name='关联人员', - on_delete=models.CASCADE, null=True, blank=True) + rpj_member = models.ForeignKey(Rpjmember, verbose_name='关联项目成员', on_delete=models.CASCADE) + rcertificate = models.ForeignKey(Rcertificate, verbose_name='关联相关方证书', on_delete=models.CASCADE, + null=True, blank=True) name = models.CharField('证书名称', max_length=20) number = models.CharField('证书编号', max_length=20) type = models.PositiveSmallIntegerField('证书类型', default=10, choices=Certificate.CERTIFICATE_TYPE_CHOICES) diff --git a/apps/rpm/serializers.py b/apps/rpm/serializers.py index 41f5405f..83eb072e 100644 --- a/apps/rpm/serializers.py +++ b/apps/rpm/serializers.py @@ -1,11 +1,13 @@ -from apps.hrm.models import Employee +from apps.hrm.models import Certificate, Employee from apps.hrm.serializers import phone_check -from apps.rpm.models import Rparty, Rpeople, Rproject +from apps.rpm.models import Rcertificate, Remployee, Rparty, Rpjcertificate, Rpjmember, Rpj from apps.system.models import Dept from apps.utils.constants import EXCLUDE_FIELDS from apps.utils.serializers import CustomModelSerializer from apps.system.serializers import DictSerializer, FileSerializer from rest_framework import serializers +from rest_framework.exceptions import ParseError +from django.db import transaction class RpartyCreateUpdateSerializer(CustomModelSerializer): @@ -16,7 +18,7 @@ class RpartyCreateUpdateSerializer(CustomModelSerializer): extra_kwargs = { 'belong_dept': {'required': True} } - + def create(self, validated_data): instance = super().create(validated_data) dept = Dept.objects.create(name=instance.name, @@ -56,41 +58,118 @@ class RpartyFileListSerializer(CustomModelSerializer): files_ = FileSerializer(source='files', many=True, read_only=True) -class RprojectCreateUpdateSerializer(CustomModelSerializer): +class RpjCreateUpdateSerializer(CustomModelSerializer): class Meta: - model = Rproject - fields = ['name', 'contract_number', 'type', 'come_time', 'leave_time', 'belong_dept'] + model = Rpj + fields = ['name', 'contract_number', 'type', 'come_time', 'leave_time', 'belong_dept', 'rparty'] -class RprojectListSerializer(CustomModelSerializer): +class RpjListSerializer(CustomModelSerializer): class Meta: - model = Rproject + model = Rpj fields = '__all__' -class RemployeeCreateUpdateSerializer(CustomModelSerializer): +class RemployeeCreateSerializer(CustomModelSerializer): phone = serializers.CharField(label="手机号", validators=[phone_check]) rparty = serializers.PrimaryKeyRelatedField(queryset=Rparty.objects.all(), label='相关方ID', required=False) class Meta: - model = Employee - exclude = EXCLUDE_FIELDS + ['face_data', - 'is_atwork', 'last_check_time', - 'not_work_remark', 'third_info', 'type'] - extra_kwargs = { - 'phone': {'required': True}, - 'number': {'required': True}, - 'photo': {'required': True}, - 'id_number': {'required': True}, - } + model = Remployee + fields = ['name', 'phone', 'photo', 'id_number'] + # model = Employee + # exclude = EXCLUDE_FIELDS + ['is_atwork', 'last_check_time', + # 'not_work_remark', 'third_info', 'type'] + # extra_kwargs = { + # 'phone': {'required': True}, + # 'number': {'required': True}, + # 'photo': {'required': True}, + # 'id_number': {'required': True}, + # } -class RpeopleCreatesSerializer(CustomModelSerializer): +class RemployeeSerializer(CustomModelSerializer): + class Meta: + model = Remployee + fields = '__all__' + + +class RcertificateCreateUpdateSerializer(CustomModelSerializer): + class Meta: + model = Rcertificate + exclude = EXCLUDE_FIELDS + ['certificate'] + + +class RcertificateSerializer(CustomModelSerializer): + class Meta: + model = Rcertificate + fields = '__all__' + + +class RpjmemberCreatesSerializer(CustomModelSerializer): employees = serializers.PrimaryKeyRelatedField( - queryset=Employee.objects.filter(type='remployee'), - many=True, label='员工ID列表' - ) + queryset=Employee.objects.filter(type='remployee'), + many=True, label='员工ID列表' + ) class Meta: - model = Rpeople - fields = ['employees', 'rproject'] + model = Rpjmember + fields = ['employees', 'Rpj'] + + +class RpjmemberCreateSerializer(CustomModelSerializer): + rcertificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True) + + class Meta: + model = Rpjmember + fields = ['employee', 'rpj', 'duty', 'is_manager'] + + def create(self, validated_data): + rpj = validated_data['rpj'] + if rpj.state != Rpj.RPJ_CREATE: + raise ParseError('成员非创建状态不可新增') + rcertificates = validated_data.pop('rcertificates') + if Rpjmember.objects.filter(remployee=validated_data['remployee'], rpj=validated_data['rpj']).exists(): + raise ParseError('该成员已选择') + with transaction.atomic(): + ins = super().create(validated_data) + for x in rcertificates: + rpjc, _ = Rpjcertificate.objects.get_or_create(rpj_member=ins, rcertificate=x, + defaults={'rpj_member': ins, 'rcertificate': x}) + for f in Rcertificate._meta.fields: + if f.name not in ['id']: + setattr(rpjc, f.name, getattr(x, f.name, None)) + rpjc.save() + if ins.is_manager: + Rpjmember.objects.exclude(id__in=ins.id).update(is_manager=False) + return ins + + +class RpjmemberUpdateSerializer(CustomModelSerializer): + rcertificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True) + + class Meta: + model = Rpjmember + fields = ['duty', 'is_manager', 'rcertificates'] + + def update(self, instance, validated_data): + rpj = validated_data['rpj'] + if rpj.state != Rpj.RPJ_CREATE: + raise ParseError('成员非创建状态不可修改') + rcertificates = validated_data.pop('rcertificates') + with transaction.atomic(): + instance = super().update(instance, validated_data) + for x in rcertificates: + rpjc, _ = Rpjcertificate.objects.get_or_create(rpj_member=instance, rcertificate=x, + defaults={'rpj_member': instance, 'rcertificate': x}) + for f in Rcertificate._meta.fields: + if f.name not in ['id']: + setattr(rpjc, f.name, getattr(x, f.name, None)) + rpjc.save() + return instance + + +class RpjmemberSerializer(CustomModelSerializer): + class Meta: + model = Rpjmember + fields = '__all__' diff --git a/apps/rpm/services.py b/apps/rpm/services.py new file mode 100644 index 00000000..a99a67c2 --- /dev/null +++ b/apps/rpm/services.py @@ -0,0 +1,13 @@ +from apps.wf.models import Ticket, Transition +from apps.rpm.models import Rpj + + +def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict): + rpj = Rpj.objects.get(id=new_ticket_data['rpj']) + # ticket_data = ticket.ticket_data + # ticket_data.update({'level': opl.level}) + # ticket.ticket_data = ticket_data + # ticket.save() + rpj.ticket = rpj + rpj.save() + diff --git a/apps/rpm/tasks.py b/apps/rpm/tasks.py new file mode 100644 index 00000000..8404af16 --- /dev/null +++ b/apps/rpm/tasks.py @@ -0,0 +1,54 @@ +# Create your tasks here +from __future__ import absolute_import, unicode_literals +from apps.hrm.models import Certificate, Employee +from apps.rpm.models import Rcertificate, Remployee, Rpj, Rpjcertificate, Rpjmember +from apps.utils.task import CustomTask +from celery import shared_task + + +@shared_task(base=CustomTask) +def rpj_audit_start(ticket_id): + rpj = Rpj.objects.get(ticket__id=ticket_id) + if rpj.state == Rpj.RPJ_CREATE: + rpj.state = Rpj.RPJ_AUDIT + rpj.save() + + +@shared_task(base=CustomTask) +def rpj_audit_end(ticket_id): + rpj = Rpj.objects.get(ticket__id=ticket_id) + if rpj.state == Rpj.RPJ_AUDIT: + rpj.state = Rpj.RPJ_ENTER + rpj.save() + # 更新企业相关方人员库和证书库 + for i in Rpjmember.objects.filter(rpj=rpj): + remployee = i.remployee + ep = Employee.objects.filter(id_number=remployee.id_number).first() + if ep: + pass + else: + ep = Employee() + ep.name = remployee.name + ep.phone = remployee.phone + ep.photo = remployee.photo + ep.id_number = remployee.id_number + ep.type = 'remployee' + ep.save() + remployee.employee = ep + remployee.save() + + for i in Rpjcertificate.objects.filter(rpj_member__rpj=rpj): + ct = Certificate.objects.filter(number=i.number).first() + if ct: + pass + else: + ct = Certificate() + ct.name = i.name + ct.number = i.number + ct.type = i.type + ct.issue_date = i.issue_date + ct.expiration_date = i.expiration_date + ct.review_date = i.review_date + ct.file = i.file + ct.employee = i.rpj_member.remployee.employee + ct.save() diff --git a/apps/rpm/urls.py b/apps/rpm/urls.py index e69de29b..68a7eacc 100644 --- a/apps/rpm/urls.py +++ b/apps/rpm/urls.py @@ -0,0 +1,18 @@ +from apps.rpm.models import RpjFile +from apps.rpm.views import RpartyViewSet, RemployeeViewSet, RpartyFileViewSet, RpjViewSet, RpjmemberViewSet +from django.urls import path, include +from rest_framework.routers import DefaultRouter + +API_BASE_URL = 'api/rpm/' +HTML_BASE_URL = 'rpm/' + +router = DefaultRouter() +router.register('rparty', RpartyViewSet, basename='rparty') +router.register('remployee', RemployeeViewSet, basename='remployee') +router.register('rparty_file', RpartyFileViewSet, basename='rparty_file') +router.register('rpj', RpjViewSet, basename='rpj') +router.register('rpj_member', RpjmemberViewSet, basename='rpj_member') +router.register('rpj_file', RpjFile, basename='rpj_file') +urlpatterns = [ + path(API_BASE_URL, include(router.urls)), +] \ No newline at end of file diff --git a/apps/rpm/views.py b/apps/rpm/views.py index 64c1cfaa..8bd11ae7 100644 --- a/apps/rpm/views.py +++ b/apps/rpm/views.py @@ -1,11 +1,12 @@ from django.shortcuts import render from apps.hrm.models import Certificate, Employee -from apps.hrm.serializers import CertificateCreateUpdateSerializer, CertificateSerializer -from apps.rpm.models import Remployee, Rparty, RpartyFile, Rpeople, Rproject -from apps.rpm.serializers import RemployeeCreateUpdateSerializer, RpartyAssignSerializer, RpartyCreateUpdateSerializer, RpartyFileListSerializer, RpartySerializer, RpeopleCreatesSerializer, RprojectCreateUpdateSerializer +from apps.hrm.serializers import CertificateCreateUpdateSerializer, CertificateSerializer, EmployeeSerializer +from apps.rpm.models import Rcertificate, Remployee, Rparty, RpartyFile, Rpjmember, Rpj +from apps.rpm.serializers import RcertificateCreateUpdateSerializer, RcertificateSerializer, RemployeeCreateSerializer, RemployeeSerializer, RpartyAssignSerializer, RpartyCreateUpdateSerializer, RpartyFileListSerializer, RpartySerializer, RpjListSerializer, RpjmemberCreateSerializer, RpjmemberCreatesSerializer, RpjCreateUpdateSerializer, RpjmemberSerializer, RpjmemberUpdateSerializer from apps.system.models import Post, User, UserPost from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet -from rest_framework.mixins import CreateModelMixin, ListModelMixin +from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin +from apps.utils.mixins import CustomDestoryModelMixin from rest_framework.decorators import action from rest_framework.response import Response from django.contrib.auth.hashers import check_password, make_password @@ -59,59 +60,61 @@ class RpartyFileViewSet(ListModelMixin, CustomGenericViewSet): return queryset -class RemployeeViewSet(CustomModelViewSet): - queryset = Employee.objects.filter(type='remployee') - create_serializer_class = RemployeeCreateUpdateSerializer - update_serializer_class = RemployeeCreateUpdateSerializer +class RemployeeViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, CustomDestoryModelMixin, + CustomGenericViewSet): + queryset = Remployee.objects.all() + create_serializer_class = RemployeeCreateSerializer + serializer_class = RemployeeSerializer def get_queryset(self): user = self.request.user queryset = super().get_queryset() if user.type == 'remployee': - queryset = queryset.filter(belong_dept=user.belong_dept) + queryset = queryset.filter(rparty=self.request.user.belong_dept) return queryset - @transaction.atomic - def create(self, request, *args, **kwargs): - """ - 添加人员 - """ - user = self.request.user - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - vdata = serializer.validated_data - if user.type == 'remployee': # 如果是相关方账号 - ep = serializer.save() - Remployee.objects.get_or_create(employee=ep, rparty=user.belong_dept.rparty, - defaults={ - "employee": ep, - "rparty": user.belong_dept.rparty - }) - else: - if 'rparty' not in vdata: - raise ParseError('未指定相关方') - serializer.save() - return Response(serializer.data, status=201) + # @transaction.atomic + # def create(self, request, *args, **kwargs): + # """ + # 添加人员 + # """ + # user = self.request.user + # serializer = self.get_serializer(data=request.data) + # serializer.is_valid(raise_exception=True) + # vdata = serializer.validated_data + # if user.type == 'remployee': # 如果是相关方账号 + # ep = serializer.save() + # Remployee.objects.get_or_create(employee=ep, rparty=user.belong_dept.rparty, + # defaults={ + # "employee": ep, + # "rparty": user.belong_dept.rparty + # }) + # else: + # if 'rparty' not in vdata: + # raise ParseError('未指定相关方') + # serializer.save() + # return Response(serializer.data, status=201) -class Rcertificate(CustomModelViewSet): - queryset = Certificate.objects.filter(employee__type='remployee') - create_serializer_class = CertificateCreateUpdateSerializer - update_serializer_class = CertificateCreateUpdateSerializer - serializer_class = CertificateSerializer +class RcertificateViewSet(CustomModelViewSet): + queryset = Rcertificate.objects.all() + create_serializer_class = RcertificateCreateUpdateSerializer + update_serializer_class = RcertificateCreateUpdateSerializer + serializer_class = RcertificateSerializer def get_queryset(self): user = self.request.user queryset = super().get_queryset() if user.type == 'remployee': - queryset = queryset.filter(belong_dept=user.belong_dept) + queryset = queryset.filter(remployee__rparty=user.belong_dept) return queryset -class RprojectViewSet(CustomModelViewSet): - queryset = Rproject.objects.all() - create_serializer_class = RprojectCreateUpdateSerializer - update_serializer_class = RprojectCreateUpdateSerializer +class RpjViewSet(CustomModelViewSet): + queryset = Rpj.objects.all() + create_serializer_class = RpjCreateUpdateSerializer + update_serializer_class = RpjCreateUpdateSerializer + serializer_class = RpjListSerializer def get_queryset(self): user = self.request.user @@ -135,42 +138,26 @@ class RprojectViewSet(CustomModelViewSet): def update(self, request, *args, **kwargs): obj = self.get_object() - if obj.state == Rproject.RP_START: - return super().update(request, *args, **kwargs) - raise ParseError('项目非创建状态不可更改') - - -class RpeopleViewSet(CustomGenericViewSet): - perms_map = {'get': '*'} - queryset = Rpeople.objects.all() - - @action(methods=['post'], detail=False, - perms_map={'post': 'rproject:update'}, serializer_class=RpeopleCreatesSerializer) - @transaction.atomic - def creates(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - vdata = serializer.validated_data - rp = vdata['rproject'] - if rp.state != Rproject.RP_START: + if obj.state != Rpj.RPJ_CREATE: raise ParseError('项目非创建状态不可更改') - for i in vdata['employees']: - Rpeople.objects.create(employee=i, rproject=rp) - # 有证书的添加人员证书 + return super().update(request, *args, **kwargs) - return Response() - - @action(methods=['put'], detail=True, - perms_map={'put': 'rproject:update'}, serializer_class=serializers.Serializer) - @transaction.atomic - def make_manager(self, request): - """ - 设为项目负责人 - """ + def destroy(self, request, *args, **kwargs): obj = self.get_object() - if obj.rproject.state != Rproject.RP_START: - raise ParseError('项目非创建状态不可更改') - Rpeople.objects.filter(rproject=obj.rproject).update(is_manager=False) - obj.is_manager = True - obj.save() - return Response() + if obj.state != Rpj.RPJ_CREATE: + raise ParseError('项目非创建状态不可删除') + return super().destroy(request, *args, **kwargs) + + +class RpjmemberViewSet(CustomModelViewSet): + perms_map = {'get': '*', 'post': 'rpj:update', 'put': 'rpj:update', 'delete': 'rpj:update'} + queryset = Rpjmember.objects.all() + create_serializer_class = RpjmemberCreateSerializer + update_serializer_class = RpjmemberUpdateSerializer + serializer_class = RpjmemberSerializer + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.rpj.state == Rpj.RPJ_CREATE: + raise ParseError('项目非创建状态不可删除') + return super().destroy(request, *args, **kwargs) diff --git a/apps/system/serializers.py b/apps/system/serializers.py index 762cb31a..c9bb16c1 100755 --- a/apps/system/serializers.py +++ b/apps/system/serializers.py @@ -11,6 +11,7 @@ from rest_framework.exceptions import ParseError from django.db import transaction from apps.third.tapis import dhapis from rest_framework.validators import UniqueValidator +from django.conf import settings # from django_q.models import Task as QTask, Schedule as QSchedule @@ -242,7 +243,7 @@ class DeptCreateUpdateSerializer(CustomModelSerializer): @transaction.atomic def create(self, validated_data): from apps.third.clients import dhClient - if dhClient: + if settings.DAHUA_ENABLED: data = { "parentId": 1, "name": validated_data['name'], @@ -257,7 +258,7 @@ class DeptCreateUpdateSerializer(CustomModelSerializer): def update(self, instance, validated_data): from apps.third.clients import dhClient third_info = instance.third_info - if dhClient and not third_info.get('dh_id', False): + if settings.DAHUA_ENABLED and not third_info.get('dh_id', False): # 如果dh_id 不存在 data = { "parentId": 1, @@ -269,7 +270,7 @@ class DeptCreateUpdateSerializer(CustomModelSerializer): instance.third_info = third_info instance.save() elif instance.name != validated_data.get('name', ''): - if dhClient and instance.third_info.get('dh_id', False): + if settings.DAHUA_ENABLED and instance.third_info.get('dh_id', False): data = { "id": instance.third_info['dh_id'], "parentId": 1, diff --git a/apps/third/migrations/0001_initial.py b/apps/third/migrations/0001_initial.py new file mode 100644 index 00000000..9d2a88f1 --- /dev/null +++ b/apps/third/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.12 on 2022-06-17 07:19 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('am', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='TDevice', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('type', models.PositiveSmallIntegerField(choices=[(10, '定位基站'), (20, '定位信标'), (30, '定位标签'), (40, 'aoa引擎'), (50, '音响'), (60, '视频通道'), (70, '闸机通道'), (80, '面板机')], verbose_name='设备类型')), + ('code', models.CharField(max_length=20, verbose_name='设备唯一标识')), + ('location', models.JSONField(blank=True, default=dict, verbose_name='位置信息')), + ('third_info', models.JSONField(blank=True, default=dict, verbose_name='三方信息')), + ('area', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='所在区')), + ('areas', models.ManyToManyField(related_name='tareas', to='am.Area', verbose_name='覆盖区')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/third/migrations/__init__.py b/apps/third/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/utils/dahua.py b/apps/utils/dahua.py index b8bbbba3..2c154358 100755 --- a/apps/utils/dahua.py +++ b/apps/utils/dahua.py @@ -18,15 +18,14 @@ 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 = {} - self.isGetingToken = False - self.isRuning = True - self.t = None # 线程 - self.setup() + if settings.DAHUA_ENABLED: + self.client_id = client_id + self.client_secret = client_secret + self.headers = {} + self.isGetingToken = False + self.isRuning = True + self.t = None # 线程 + self.setup() def _get_token_loop(self): while self.isRuning: @@ -70,7 +69,7 @@ class DhClient: def request(self, url: str, method: str, params=dict(), json=dict(), timeout=10, file_path_rela=None, raise_exception=True): - if self is None: + if not settings.DAHUA_ENABLED: raise ParseError('大华对接未启用') if self.isGetingToken: req_num = 0 diff --git a/apps/utils/models.py b/apps/utils/models.py index 36a61a6c..0beae17e 100755 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -152,7 +152,7 @@ class CommonBDModel(BaseModel): '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, + 'system.dept', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name='%(class)s_belong_dept') class Meta: diff --git a/apps/utils/viewsets.py b/apps/utils/viewsets.py index 7fb721a9..452f9931 100755 --- a/apps/utils/viewsets.py +++ b/apps/utils/viewsets.py @@ -3,14 +3,14 @@ from django.core.cache import cache from rest_framework.decorators import action from rest_framework.exceptions import ValidationError from rest_framework.mixins import (CreateModelMixin, ListModelMixin, - RetrieveModelMixin, UpdateModelMixin) + RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin) from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet from apps.system.models import DataFilter, Dept from apps.utils.errors import PKS_ERROR -from apps.utils.mixins import CustomDestoryModelMixin, MyLoggingMixin +from apps.utils.mixins import 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 @@ -101,7 +101,7 @@ class CustomGenericViewSet(MyLoggingMixin, GenericViewSet): class CustomModelViewSet(CreateModelMixin, UpdateModelMixin, ListModelMixin, - RetrieveModelMixin, CustomDestoryModelMixin, CustomGenericViewSet): + RetrieveModelMixin, DestroyModelMixin, CustomGenericViewSet): """ 增强的ModelViewSet """ @@ -126,6 +126,12 @@ class CustomModelViewSet(CreateModelMixin, UpdateModelMixin, ListModelMixin, pks = request_data.get('pks', None) if pks: self.get_queryset().filter(id__in=pks).delete(update_by=request.user) - return Response() + return Response(status=204) else: raise ValidationError(**PKS_ERROR) + + def perform_destroy(self, instance): + if self.delete_soft: + instance.delete(update_by=self.request.user) + else: + instance.delete(soft=False) diff --git a/apps/utils/xunxi.py b/apps/utils/xunxi.py index e1dd1a24..7cc71dd2 100755 --- a/apps/utils/xunxi.py +++ b/apps/utils/xunxi.py @@ -17,15 +17,14 @@ 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 - self.isRuning = True - self.token = '' - self.t = None - self.setup() + if settings.XX_ENABLED: + self.licence = licence + self.username = username + self.isGetingToken = False + self.isRuning = True + self.token = '' + self.t = None + self.setup() def _get_token_loop(self): while self.isRuning: @@ -58,10 +57,10 @@ class XxClient: 自定义销毁 """ self.isRuning = False - self.t.join() + # self.t.join() def request(self, url: str, method: str = 'post', params=dict(), json=dict(), timeout=4, raise_exception=True): - if self is None: + if not settings.XX_ENABLED: raise ParseError('寻息对接未启用') params['accessToken'] = self.token json['username'] = self.username diff --git a/apps/vm/models.py b/apps/vm/models.py index c1e241ed..0094ec96 100644 --- a/apps/vm/models.py +++ b/apps/vm/models.py @@ -15,17 +15,44 @@ class Visit(CommonBModel): (30, '面试'), (40, '开会') ) + V_CREATE = 10 + V_AUDIT = 20 + V_ENTER = 30 + V_WORKING = 40 + V_DONE = 50 + V_STATE_CHOICES = ( + (10, '创建中'), + (20, '审批中'), + (30, '待入厂'), + (40, '进行中'), + (50, '已完成') + ) purpose = models.PositiveSmallIntegerField('来访事由') - description = models.CharField('来访详述', max_length=200) + state = models.PositiveSmallIntegerField(choices=V_STATE_CHOICES, default=10) + name = models.CharField('来访概述', max_length=50) + description = models.TextField('来访详述', null=True, blank=True) visit_time = models.DateTimeField('来访时间') leave_time = models.DateTimeField('离开时间') receptionist = models.ForeignKey(User, verbose_name='接待人', on_delete=models.CASCADE) -class VisitPeople(BaseModel): +class Visitor(CommonAModel): + """ + 访客信息 + """ + employee = models.ForeignKey(Employee, verbose_name='成员信息', on_delete=models.CASCADE, null=True, blank=True) + name = models.CharField('姓名', max_length=20) + phone = models.CharField('手机号', max_length=11) + photo = models.CharField('证件照', max_length=1000) + id_number = models.CharField('身份证号', max_length=100) + visit = models.ForeignKey(Visit, verbose_name='最近所属访问项目', on_delete=models.CASCADE, + null=True, blank=True) + + +class Vpeople(BaseModel): """ 访客项目人员 """ visit = models.ForeignKey(Visit, verbose_name='关联访问项目', on_delete=models.CASCADE) - visitor = models.ForeignKey(Employee, verbose_name='访客', on_delete=models.CASCADE) - is_manager = models.BooleanField('是否主访人', default=False) + visitor = models.ForeignKey(Visitor, verbose_name='访客', on_delete=models.CASCADE) + is_main = models.BooleanField('是否主访人', default=False) diff --git a/apps/vm/serializers.py b/apps/vm/serializers.py new file mode 100644 index 00000000..2c9d3bb1 --- /dev/null +++ b/apps/vm/serializers.py @@ -0,0 +1,50 @@ +from apps.hrm.models import Employee +from apps.utils.constants import EXCLUDE_FIELDS +from apps.utils.serializers import CustomModelSerializer +from apps.vm.models import Visit, Visitor, Vpeople +from apps.hrm.serializers import phone_check +from rest_framework import serializers + + +class VisitCreateUpdateSerializer(CustomModelSerializer): + class Meta: + model = Visit + fields = ['purpose', 'name', 'description', 'visit_time', 'leave_time', 'receptionist'] + + +class VisitSerializer(CustomModelSerializer): + class Meta: + model = Visit + fields = '__all__' + + +class VisitorCreateSerializer(CustomModelSerializer): + phone = serializers.CharField(label="手机号", validators=[phone_check]) + + class Meta: + model = Visitor + exclude = ['name', 'phone', 'photo', 'id_number'] + + +class VisitorSerializer(CustomModelSerializer): + class Meta: + model = Visitor + fields = '__all__' + + +class VpeopleCreateSerializer(CustomModelSerializer): + class Meta: + model = Vpeople + fields = ['visitor', 'visitor', 'is_main'] + + def create(self, validated_data): + ins = super().create(validated_data) + if ins.is_main: + Vpeople.objects.filter(visit=validated_data['visit']).exclude(id__in=ins.id).update(is_main=False) + return ins + + +class VpeopleSerializer(CustomModelSerializer): + class Meta: + model = Vpeople + fields = '__all__' diff --git a/apps/vm/tasks.py b/apps/vm/tasks.py new file mode 100644 index 00000000..85c15189 --- /dev/null +++ b/apps/vm/tasks.py @@ -0,0 +1,30 @@ +# Create your tasks here +from __future__ import absolute_import, unicode_literals +from apps.hrm.models import Employee +from apps.utils.task import CustomTask +from apps.vm.models import Visit, Vpeople +from celery import shared_task + + +@shared_task(base=CustomTask) +def visit_audit_end(ticket_id): + visit = Visit.objects.get(ticket__id=ticket_id) + if visit.state == Visit.V_AUDIT: + visit.state = Visit.V_ENTER + visit.save() + # 更新企业访客人员库 + for i in Vpeople.objects.filter(visit=visit): + visitor = i.visitor + ep = Employee.objects.filter(id_number=visitor.id_number).first() + if ep: + pass + else: + ep = Employee() + ep.name = visitor.name + ep.phone = visitor.phone + ep.photo = visitor.photo + ep.id_number = visitor.id_number + ep.type = 'visitor' + ep.save() + visitor.employee = ep + visitor.save() diff --git a/apps/vm/views.py b/apps/vm/views.py index 91ea44a2..dee39dde 100644 --- a/apps/vm/views.py +++ b/apps/vm/views.py @@ -1,3 +1,62 @@ from django.shortcuts import render - +from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, GenericViewSet +from apps.vm.models import Visit, Visitor, Vpeople +from apps.vm.serializers import VisitCreateUpdateSerializer, VisitSerializer, VisitorCreateSerializer, VisitorSerializer, VpeopleCreateSerializer, VpeopleSerializer +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.exceptions import ParseError +from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin +from apps.utils.mixins import CustomDestoryModelMixin # Create your views here. + + +class VisitViewSet(CustomModelViewSet): + queryset = Visit.objects.all() + create_serializer_class = VisitCreateUpdateSerializer + update_serializer_class = VisitCreateUpdateSerializer + serializer_class = VisitSerializer + + def get_queryset(self): + user = self.request.user + queryset = super().get_queryset() + if user.type == 'visitor': + queryset = queryset.filter(create_by=user) + return queryset + + def update(self, request, *args, **kwargs): + obj = self.get_object() + if obj.state != Visit.V_CREATE: + raise ParseError('项目非创建状态不可更改') + return super().update(request, *args, **kwargs) + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.state != Visit.V_CREATE: + raise ParseError('项目非创建状态不可删除') + return super().destroy(request, *args, **kwargs) + + +class VisitorViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, CustomDestoryModelMixin, + CustomGenericViewSet): + queryset = Visitor.objects.all() + create_serializer_class = VisitorCreateSerializer + serializer_class = VisitorSerializer + + +class VpeopleView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, CustomGenericViewSet): + perms_map = {'get': '*', 'post': 'visit:update', 'put': 'visit:update', 'delete': 'visit:update'} + queryset = Vpeople.objects.all() + create_serializer_class = VpeopleCreateSerializer + serializer_class = VpeopleSerializer + + def create(self, request, *args, **kwargs): + obj = self.get_object() + if obj.visit.state != Visit.V_CREATE: + raise ParseError('项目非创建状态不可新增成员') + return super().create(request, *args, **kwargs) + + def destroy(self, request, *args, **kwargs): + obj = self.get_object() + if obj.visit.state != Visit.V_CREATE: + raise ParseError('项目非创建状态不可新增成员') + return super().destroy(request, *args, **kwargs) diff --git a/requirements.txt b/requirements.txt index 8f72ead3..4997e817 100755 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ pillow==9.0.1 opencv-python==4.5.5.62 daphne==3.0.2 redis==4.1.4 +django-redis==5.2.0 user-agents==2.2.0 daphne==3.0.2 channels==3.0.4 diff --git a/server/settings.py b/server/settings.py index af438faf..ae8228f7 100755 --- a/server/settings.py +++ b/server/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ """ from datetime import datetime, timedelta +import json import os from . import conf import logging @@ -54,6 +55,7 @@ INSTALLED_APPS = [ 'apps.auth1', 'apps.monitor', 'apps.wf', + 'apps.ecm', 'apps.hrm', 'apps.am', 'apps.vm', @@ -194,8 +196,8 @@ REST_FRAMEWORK = { # 'UNAUTHENTICATED_TOKEN': None, 'EXCEPTION_HANDLER': 'apps.utils.exceptions.custom_exception_hander', 'DEFAULT_THROTTLE_RATES': { - 'anon': '1/second', - 'user': '2/second' + 'anon': '100/second', + 'user': '200/second' } } # simplejwt配置 @@ -214,16 +216,15 @@ AUTHENTICATION_BACKENDS = ( ) # 缓存配置,有需要可更改为redis -# CACHES = { -# "default": { -# "BACKEND": "django_redis.cache.RedisCache", -# "LOCATION": "redis://redis:6379/1", -# "OPTIONS": { -# "CLIENT_CLASS": "django_redis.client.DefaultClient", -# "PICKLE_VERSION": -1 -# } -# } -# } +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://localhost:6379/2", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + } + } +} # celery配置,celery正常运行必须安装redis CELERY_BROKER_URL = "redis://localhost:6379/2" # 任务存储 @@ -257,8 +258,8 @@ CELERY_TASK_TRACK_STARTED = True # swagger配置 SWAGGER_SETTINGS = { - 'LOGIN_URL': '/django/login/', - 'LOGOUT_URL': '/django/logout/', + 'LOGIN_URL': '/django/admin/login/', + 'LOGOUT_URL': '/django/admin/logout/', } # 日志配置 diff --git a/server/urls.py b/server/urls.py index 2a787222..8ef62786 100755 --- a/server/urls.py +++ b/server/urls.py @@ -36,8 +36,8 @@ schema_view = get_schema_view( urlpatterns = [ # django后台 - path('django/doc/', include('django.contrib.admindocs.urls')), - path('django/', admin.site.urls), + path('django/admin/doc/', include('django.contrib.admindocs.urls')), + path('django/admin/', admin.site.urls), path('django/api-auth/', include('rest_framework.urls')), # api