From 2fe614d45c2580c77903943148c132c9bc100a0f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 16 Aug 2021 09:14:59 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0hrm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0002_alter_equipment_model.py | 18 +++++++++ hb_server/apps/em/views.py | 4 +- hb_server/apps/hrm/apps.py | 2 +- hb_server/apps/hrm/migrations/0001_initial.py | 39 +++++++++++++++++++ hb_server/apps/hrm/models.py | 19 ++++----- hb_server/apps/hrm/serializers.py | 22 +++++++++++ hb_server/apps/hrm/urls.py | 12 ++++++ hb_server/apps/hrm/views.py | 14 ++++++- hb_server/apps/pum/views.py | 4 +- hb_server/apps/system/serializers.py | 9 ++++- hb_server/requirements.txt | 3 +- hb_server/server/settings.py | 1 + hb_server/server/urls.py | 5 +++ 13 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 hb_server/apps/em/migrations/0002_alter_equipment_model.py create mode 100644 hb_server/apps/hrm/migrations/0001_initial.py create mode 100644 hb_server/apps/hrm/serializers.py create mode 100644 hb_server/apps/hrm/urls.py diff --git a/hb_server/apps/em/migrations/0002_alter_equipment_model.py b/hb_server/apps/em/migrations/0002_alter_equipment_model.py new file mode 100644 index 0000000..f72f26f --- /dev/null +++ b/hb_server/apps/em/migrations/0002_alter_equipment_model.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-08-16 01:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='equipment', + name='model', + field=models.CharField(blank=True, max_length=60, null=True, verbose_name='规格型号'), + ), + ] diff --git a/hb_server/apps/em/views.py b/hb_server/apps/em/views.py index ebf4943..912c5ee 100644 --- a/hb_server/apps/em/views.py +++ b/hb_server/apps/em/views.py @@ -3,11 +3,11 @@ from rest_framework.viewsets import ModelViewSet from apps.em.models import Equipment from apps.em.serializers import EquipmentSerializer -from apps.system.mixins import OptimizationMixin +from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin # Create your views here. -class EquipmentViewSet(ModelViewSet): +class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): """ 设备台账-增删改查 """ diff --git a/hb_server/apps/hrm/apps.py b/hb_server/apps/hrm/apps.py index d948875..6ed9897 100644 --- a/hb_server/apps/hrm/apps.py +++ b/hb_server/apps/hrm/apps.py @@ -1,7 +1,7 @@ from django.apps import AppConfig class SystemConfig(AppConfig): - name = 'apps.sam' + name = 'apps.hrm' verbose_name = '人力资源管理' diff --git a/hb_server/apps/hrm/migrations/0001_initial.py b/hb_server/apps/hrm/migrations/0001_initial.py new file mode 100644 index 0000000..496c0e6 --- /dev/null +++ b/hb_server/apps/hrm/migrations/0001_initial.py @@ -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': '员工补充信息', + }, + ), + ] diff --git a/hb_server/apps/hrm/models.py b/hb_server/apps/hrm/models.py index e868cd6..adb8b44 100644 --- a/hb_server/apps/hrm/models.py +++ b/hb_server/apps/hrm/models.py @@ -9,23 +9,18 @@ from simple_history.models import HistoricalRecords -class Employee(BaseModel): +class Employee(CommonAModel): """ 员工信息 """ - job_choices = ( - ('在职', '在职'), - ('离职', '离职'), - ) - name = models.CharField('姓名', max_length=50) + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee_user') number = models.CharField('人员编号', max_length=50,null=True, blank=True, unique=True) - gender = models.CharField('性别', max_length=10,null=True, blank=True) - phone = models.CharField('手机号', max_length=11,null=True, blank=True, unique=True) - jobstate = models.CharField('在职状态', max_length=11,choices=job_choices, default='在职') - dept = models.ForeignKey(Organization, verbose_name='关联部门', on_delete=models.CASCADE, related_name='employee_dept') - description = models.CharField('描述', max_length=200, blank=True, null=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) class Meta: - verbose_name = '客户信息' + verbose_name = '员工补充信息' verbose_name_plural = verbose_name def __str__(self): diff --git a/hb_server/apps/hrm/serializers.py b/hb_server/apps/hrm/serializers.py new file mode 100644 index 0000000..96ab4df --- /dev/null +++ b/hb_server/apps/hrm/serializers.py @@ -0,0 +1,22 @@ +from apps.system.models import User +from rest_framework.serializers import ModelSerializer + +from .models import Employee +from apps.system.serializers import UserListSerializer, UserSimpleSerializer +from django.db.models.query import Prefetch + +class EmployeeSerializer(ModelSerializer): + # user_ = UserListSerializer(source='user', read_only=True) + class Meta: + model = Employee + fields = '__all__' + # @staticmethod + # def setup_eager_loading(queryset): + # """ Perform necessary eager loading of data. """ + # queryset = queryset.select_related('user', 'user__dept') + # # queryset = queryset.prefetch_related('user','user__dept') + # queryset = queryset.prefetch_related( + # Prefetch('user_', + # queryset=User.objects.filter(employee_user__isnull=True)) + # ) + # return queryset diff --git a/hb_server/apps/hrm/urls.py b/hb_server/apps/hrm/urls.py new file mode 100644 index 0000000..bd532d0 --- /dev/null +++ b/hb_server/apps/hrm/urls.py @@ -0,0 +1,12 @@ +from django.db.models import base +from rest_framework import urlpatterns +from apps.hrm.views import EmployeeViewSet +from django.urls import path, include +from rest_framework.routers import DefaultRouter + +router = DefaultRouter() +router.register('employee', EmployeeViewSet, basename='employee') +urlpatterns = [ + path('', include(router.urls)), +] + diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index 91ea44a..843fa96 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -1,3 +1,15 @@ from django.shortcuts import render - +from rest_framework.viewsets import ModelViewSet, GenericViewSet +from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin +from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin +from apps.hrm.models import Employee +from apps.hrm.serializers import EmployeeSerializer # Create your views here. +class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet): + """ + 员工详细信息 + """ + perms_map = {'get': '*', 'put': 'employee_update'} + queryset = Employee.objects.all() + serializer_class = EmployeeSerializer + ordering = ['-pk'] \ No newline at end of file diff --git a/hb_server/apps/pum/views.py b/hb_server/apps/pum/views.py index 30f504b..1258ca9 100644 --- a/hb_server/apps/pum/views.py +++ b/hb_server/apps/pum/views.py @@ -3,11 +3,11 @@ from rest_framework.viewsets import ModelViewSet from apps.pum.models import Vendor from apps.pum.serializers import VendorSerializer -from apps.system.mixins import OptimizationMixin +from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin # Create your views here. -class VendorViewSet(ModelViewSet): +class VendorViewSet(CreateUpdateModelAMixin, ModelViewSet): """ 供应商-增删改查 """ diff --git a/hb_server/apps/system/serializers.py b/hb_server/apps/system/serializers.py index ef5d03b..b3feb82 100644 --- a/hb_server/apps/system/serializers.py +++ b/hb_server/apps/system/serializers.py @@ -119,7 +119,14 @@ class UserSimpleSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'username', 'name'] - + +# class UserStandardSerializer(serializers.ModelSerializer): +# dept_name = serializers.StringRelatedField(source='dept') +# class Meta: +# model = User +# fields = ['id', 'username', 'name', 'is_active', 'dept_name', 'dept'] + + class UserListSerializer(serializers.ModelSerializer): """ 用户列表序列化 diff --git a/hb_server/requirements.txt b/hb_server/requirements.txt index e7459d6..595fea2 100644 --- a/hb_server/requirements.txt +++ b/hb_server/requirements.txt @@ -7,5 +7,6 @@ django-simple-history==3.0.0 djangorestframework==3.12.4 djangorestframework-simplejwt==4.7.2 drf-yasg==1.20.0 -psycopg2==2.9.1 psutil==5.8.0 +pillow==8.3.1 +opencv-python==4.5.3.56 diff --git a/hb_server/server/settings.py b/hb_server/server/settings.py index d32bcf9..20ca66c 100644 --- a/hb_server/server/settings.py +++ b/hb_server/server/settings.py @@ -49,6 +49,7 @@ INSTALLED_APPS = [ 'apps.monitor', 'apps.pum', 'apps.em', + 'apps.hrm' ] MIDDLEWARE = [ diff --git a/hb_server/server/urls.py b/hb_server/server/urls.py index 959076c..1ee8d0c 100644 --- a/hb_server/server/urls.py +++ b/hb_server/server/urls.py @@ -25,6 +25,7 @@ from rest_framework.documentation import include_docs_urls from rest_framework_simplejwt.views import (TokenObtainPairView, TokenRefreshView) from django.views.generic import TemplateView +from utils.view import GenSignature router = routers.DefaultRouter() router.register('', FileViewSet, basename="file") @@ -58,6 +59,10 @@ urlpatterns = [ path('api/monitor/', include('apps.monitor.urls')), path('api/pum/', include('apps.pum.urls')), path('api/em/', include('apps.em.urls')), + path('api/hrm/', include('apps.hrm.urls')), + + # 工具 + path('api/utils/signature/', GenSignature.as_view()), # 前端页面入口 path('',TemplateView.as_view(template_name="index.html")) From 015f3d553712e01c33cb9ad6f0cac602436d03da Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 16 Aug 2021 10:34:36 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=88=9B=E5=BB=BAemploye?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/hrm/apps.py | 2 ++ hb_server/apps/hrm/signals.py | 9 +++++++++ hb_server/apps/system/serializers.py | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 hb_server/apps/hrm/signals.py diff --git a/hb_server/apps/hrm/apps.py b/hb_server/apps/hrm/apps.py index 6ed9897..d2dc3ce 100644 --- a/hb_server/apps/hrm/apps.py +++ b/hb_server/apps/hrm/apps.py @@ -4,4 +4,6 @@ class SystemConfig(AppConfig): name = 'apps.hrm' verbose_name = '人力资源管理' + def ready(self): + import apps.hrm.signals diff --git a/hb_server/apps/hrm/signals.py b/hb_server/apps/hrm/signals.py new file mode 100644 index 0000000..a2a7312 --- /dev/null +++ b/hb_server/apps/hrm/signals.py @@ -0,0 +1,9 @@ +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 + +@receiver(post_save, sender=User) +def createEmployee(sender, instance, created, **kwargs): + if created: + Employee.objects.get_or_create(user=instance) \ No newline at end of file diff --git a/hb_server/apps/system/serializers.py b/hb_server/apps/system/serializers.py index b3feb82..576be89 100644 --- a/hb_server/apps/system/serializers.py +++ b/hb_server/apps/system/serializers.py @@ -144,6 +144,11 @@ class UserListSerializer(serializers.ModelSerializer): queryset = queryset.select_related('superior','dept') queryset = queryset.prefetch_related('roles',) return queryset + + def get_field_names(self, declared_fields, info): + if hasattr(self.Meta.model, 'employee_user'): + self.Meta.fields.append('employee_user') + return super().get_field_names(declared_fields, info) class UserModifySerializer(serializers.ModelSerializer): """ From eda2c2365981ec5662a17e9f6c37cf69f71b82e0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 16 Aug 2021 17:25:40 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_client/.env.development | 2 +- hb_server/apps/em/apps.py | 2 +- hb_server/apps/hrm/apps.py | 2 +- hb_server/apps/monitor/views.py | 30 +++++++---- hb_server/apps/pum/apps.py | 2 +- hb_server/apps/sam/apps.py | 2 +- hb_server/apps/wf/__init__.py | 0 hb_server/apps/wf/admin.py | 3 ++ hb_server/apps/wf/apps.py | 7 +++ hb_server/apps/wf/models.py | 88 ++++++++++++++++++++++++++++++++ hb_server/apps/wf/serializers.py | 9 ++++ hb_server/apps/wf/tests.py | 3 ++ hb_server/apps/wf/urls.py | 12 +++++ hb_server/apps/wf/views.py | 18 +++++++ hb_server/server/settings.py | 5 ++ 15 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 hb_server/apps/wf/__init__.py create mode 100644 hb_server/apps/wf/admin.py create mode 100644 hb_server/apps/wf/apps.py create mode 100644 hb_server/apps/wf/models.py create mode 100644 hb_server/apps/wf/serializers.py create mode 100644 hb_server/apps/wf/tests.py create mode 100644 hb_server/apps/wf/urls.py create mode 100644 hb_server/apps/wf/views.py diff --git a/hb_client/.env.development b/hb_client/.env.development index f0aff47..21478c7 100644 --- a/hb_client/.env.development +++ b/hb_client/.env.development @@ -2,7 +2,7 @@ ENV = 'development' # base api -VUE_APP_BASE_API = 'http://localhost:8000/api' +VUE_APP_BASE_API = 'http://47.95.0.242:2222/api' # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, # to control whether the babel-plugin-dynamic-import-node plugin is enabled. diff --git a/hb_server/apps/em/apps.py b/hb_server/apps/em/apps.py index d05d748..0b795c8 100644 --- a/hb_server/apps/em/apps.py +++ b/hb_server/apps/em/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -class SystemConfig(AppConfig): +class EmConfig(AppConfig): name = 'apps.em' verbose_name = '设备仪器管理' diff --git a/hb_server/apps/hrm/apps.py b/hb_server/apps/hrm/apps.py index d2dc3ce..852f1e7 100644 --- a/hb_server/apps/hrm/apps.py +++ b/hb_server/apps/hrm/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -class SystemConfig(AppConfig): +class HrmConfig(AppConfig): name = 'apps.hrm' verbose_name = '人力资源管理' diff --git a/hb_server/apps/monitor/views.py b/hb_server/apps/monitor/views.py index 3fa290d..3e6cee4 100644 --- a/hb_server/apps/monitor/views.py +++ b/hb_server/apps/monitor/views.py @@ -30,23 +30,35 @@ class ServerInfoView(APIView): return Response(ret) class LogView(APIView): - + def get(self, request, *args, **kwargs): """ 查看最近的日志列表 + :query name """ - logs =[] + logs =[] + name = request.GET.get('name', None) for root, dirs, files in os.walk(settings.LOG_PATH): for file in files: if len(logs)>50:break filepath = os.path.join(root, file) - fsize = os.path.getsize(filepath) - if fsize: - logs.append({ - "name":file, - "filepath":filepath, - "size":round(fsize/1000,1) - }) + if name: + if filepath.contains(name): + fsize = os.path.getsize(filepath) + if fsize: + logs.append({ + "name":file, + "filepath":filepath, + "size":round(fsize/1000,1) + }) + else: + fsize = os.path.getsize(filepath) + if fsize: + logs.append({ + "name":file, + "filepath":filepath, + "size":round(fsize/1000,1) + }) return Response(logs) class LogDetailView(APIView): diff --git a/hb_server/apps/pum/apps.py b/hb_server/apps/pum/apps.py index 5032791..c48013e 100644 --- a/hb_server/apps/pum/apps.py +++ b/hb_server/apps/pum/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -class SystemConfig(AppConfig): +class PumConfig(AppConfig): name = 'apps.pum' verbose_name = '采购管理' diff --git a/hb_server/apps/sam/apps.py b/hb_server/apps/sam/apps.py index bc73763..44a0d59 100644 --- a/hb_server/apps/sam/apps.py +++ b/hb_server/apps/sam/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -class SystemConfig(AppConfig): +class SamConfig(AppConfig): name = 'apps.sam' verbose_name = '销售管理' diff --git a/hb_server/apps/wf/__init__.py b/hb_server/apps/wf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hb_server/apps/wf/admin.py b/hb_server/apps/wf/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/hb_server/apps/wf/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/hb_server/apps/wf/apps.py b/hb_server/apps/wf/apps.py new file mode 100644 index 0000000..f0709ed --- /dev/null +++ b/hb_server/apps/wf/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class WfConfig(AppConfig): + name = 'apps.wf' + verbose_name = '工作流管理' + + diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py new file mode 100644 index 0000000..eff4766 --- /dev/null +++ b/hb_server/apps/wf/models.py @@ -0,0 +1,88 @@ +from django.db import models +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 CommonAModel, CommonBModel, Organization, User, Dict, File +from utils.model import SoftModel, BaseModel +from simple_history.models import HistoricalRecords + + +class Workflow(CommonAModel): + """ + 工作流 + """ + name = models.CharField('名称', max_length=50) + description = models.CharField('描述', max_length=200) + view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后,只允许工单的关联人(创建人、曾经的处理人)有权限查看工单') + title_template = models.CharField('标题模板', max_length=50, default='你有一个待办工单:{title}', null=True, blank=True, help_text='工单字段的值可以作为参数写到模板中,格式如:你有一个待办工单:{title}') + +class State(BaseModel): + """ + 状态记录 + """ + type_choices = ( + (0, '普通类型'), + (1, '初始状态'), + (2, '结束状态') + ) + name = models.CharField('名称', max_length=50) + workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') + is_hidden = models.BooleanField('是否隐藏', default=False, help_text='设置为True时,获取工单步骤api中不显示此状态(当前处于此状态时除外)') + sort = models.IntegerField('状态顺序', default=0, help_text='用于工单步骤接口时,step上状态的顺序(因为存在网状情况,所以需要人为设定顺序),值越小越靠前') + type = models.IntegerField('状态类型', default=0, choices=type_choices, help_text='0.普通类型 1.初始状态(用于新建工单时,获取对应的字段必填及transition信息) 2.结束状态(此状态下的工单不得再处理,即没有对应的transition)') + enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态') + + participant_type = models.IntegerField('参与者类型', default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5,参与人填creator') + + +class Transition(BaseModel): + """ + 工作流流转,定时器,条件(允许跳过), 条件流转与定时器不可同时存在 + """ + attribute_type_choices = ( + (1, '同意'), + (2, '拒绝'), + (3, '其他') + ) + name = models.CharField('操作', max_length=50) + workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') + timer = models.IntegerField('定时器(单位秒)', default=0, help_text='单位秒。处于源状态X秒后如果状态都没有过变化则自动流转到目标状态。设置时间有效') + source_state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='源状态', related_name='sstate_transition') + destination_state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='目的状态', related_name='dstate_transition') + condition_expression = models.JSONField('条件表达式', max_length=1000, default='[]', help_text='流转条件表达式,根据表达式中的条件来确定流转的下个状态,格式为[{"expression":"{days} > 3 and {days}<10", "target_state":11}] 其中{}用于填充工单的字段key,运算时会换算成实际的值,当符合条件下个状态将变为target_state_id中的值,表达式只支持简单的运算或datetime/time运算.loonflow会以首次匹配成功的条件为准,所以多个条件不要有冲突' ) + attribute_type = models.IntegerField('属性类型', default=1, choices=attribute_type_choices, help_text='属性类型,1.同意,2.拒绝,3.其他') + field_require_check = models.BooleanField('是否校验必填项', default=True, help_text='默认在用户点击操作的时候需要校验工单表单的必填项,如果设置为否则不检查。用于如"退回"属性的操作,不需要填写表单内容') + + +class CustomField(BaseModel): + """自定义字段, 设定某个工作流有哪些自定义字段""" + field_type_choices = ( + ('string', '字符串'), + ('int', '整型'), + ('float', '浮点'), + ('bol', '布尔'), + ('date', '日期'), + ('datetime', '日期时间'), + ('radio', '单选'), + ('checkbox', '多选'), + ('select', '单选下拉'), + ('mutiselect', '多选下拉'), + ('textarea', '文本域'), + ('selectuser', '单选用户'), + ('selectusers', '多选用户'), + ('file', '附件') + ) + workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') + field_type = models.IntegerField('类型', help_text='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)') + field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突') + field_name = models.CharField('字段名称', max_length=50) + sort = models.IntegerField('排序', default=0, help_text='工单基础字段在表单中排序为:流水号0,标题20,状态id40,状态名41,创建人80,创建时间100,更新时间120.前端展示工单信息的表单可以根据这个id顺序排列') + default_value = models.CharField('默认值', null=True, blank=True, max_length=100, help_text='前端展示时,可以将此内容作为表单中的该字段的默认值') + description = models.CharField('描述', max_length=100, blank=True, default='', help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述') + placeholder = models.CharField('占位符', max_length=100, blank=True, default='', help_text='用户工单详情表单中作为字段的占位符显示') + field_template = models.TextField('文本域模板', default='', blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder') + boolean_field_display = models.JSONField('布尔类型显示名', default='{}', blank=True, + help_text='当为布尔类型时候,可以支持自定义显示形式。{"1":"是","0":"否"}或{"1":"需要","0":"不需要"},注意数字也需要引号') + field_choice = models.JSONField('radio、checkbox、select的选项', default='{}', blank=True, + help_text='radio,checkbox,select,multiselect类型可供选择的选项,格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号') + label = models.JSONField('标签', blank=True, default='{}', help_text='自定义标签,json格式,调用方可根据标签自行处理特殊场景逻辑,loonflow只保存文本内容') \ No newline at end of file diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py new file mode 100644 index 0000000..4fb91c4 --- /dev/null +++ b/hb_server/apps/wf/serializers.py @@ -0,0 +1,9 @@ +from rest_framework.serializers import ModelSerializer + +from .models import Workflow + + +class WorkflowSerializer(ModelSerializer): + class Meta: + model = Workflow + fields = '__all__' diff --git a/hb_server/apps/wf/tests.py b/hb_server/apps/wf/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/hb_server/apps/wf/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/hb_server/apps/wf/urls.py b/hb_server/apps/wf/urls.py new file mode 100644 index 0000000..a81ad24 --- /dev/null +++ b/hb_server/apps/wf/urls.py @@ -0,0 +1,12 @@ +from django.db.models import base +from rest_framework import urlpatterns +from apps.pum.views import VendorViewSet +from django.urls import path, include +from rest_framework.routers import DefaultRouter + +router = DefaultRouter() +router.register('vendor', VendorViewSet, basename='vendor') +urlpatterns = [ + path('', include(router.urls)), +] + diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py new file mode 100644 index 0000000..bbba501 --- /dev/null +++ b/hb_server/apps/wf/views.py @@ -0,0 +1,18 @@ +from apps.wf.serializers import WorkflowSerializer +from django.shortcuts import render +from rest_framework.viewsets import ModelViewSet + +from apps.wf.models import Workflow, State, Transition +from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin + + +# Create your views here. +class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet): + perms_map = {'get': '*', 'post': 'workflow_create', + 'put': 'workflow_update', 'delete': 'workflow_delete'} + queryset = Workflow.objects.all() + serializer_class = WorkflowSerializer + search_fields = ['name', 'description'] + filterset_fields = [] + ordering_fields = ['create_time'] + ordering = ['-create_time'] \ No newline at end of file diff --git a/hb_server/server/settings.py b/hb_server/server/settings.py index 20ca66c..a48a0e9 100644 --- a/hb_server/server/settings.py +++ b/hb_server/server/settings.py @@ -197,6 +197,11 @@ CELERYD_MAX_TASKS_PER_CHILD = 100 # 每个worker最多执行300个任务就会 CELERY_TIMEZONE = 'Asia/Shanghai' # 设置时区 CELERY_ENABLE_UTC = True # 启动时区设置 +# swagger配置 +SWAGGER_SETTINGS = { + 'LOGIN_URL':'/api/admin/login/', + 'LOGOUT_URL':'/api/admin/logout/' +} # 日志配置 # 创建日志的路径 From e040b1486002cd9e21939f80eb374a68dcf084ce Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 17 Aug 2021 09:47:14 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_client/src/router/index.js | 10 +++++++ hb_server/apps/em/models.py | 2 +- hb_server/apps/monitor/views.py | 48 +++++++++++++++++++++++---------- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/hb_client/src/router/index.js b/hb_client/src/router/index.js index 3991778..6999cb6 100644 --- a/hb_client/src/router/index.js +++ b/hb_client/src/router/index.js @@ -210,6 +210,16 @@ export const asyncRoutes = [ } ] }, + { + path: 'docs-link2', + component: Layout, + children: [ + { + path: process.env.VUE_APP_BASE_API + '/swagger/', + meta: { title: 'Swagger文档', icon: 'link', perms: ['dev_docs'] } + } + ] + }, { path: 'admin-link', component: Layout, diff --git a/hb_server/apps/em/models.py b/hb_server/apps/em/models.py index 70c558a..8083e89 100644 --- a/hb_server/apps/em/models.py +++ b/hb_server/apps/em/models.py @@ -35,5 +35,5 @@ class Equipment(CommonBModel): verbose_name_plural = verbose_name def __str__(self): - return self.name + '-' + self.name + return self.number + '-' + self.name \ No newline at end of file diff --git a/hb_server/apps/monitor/views.py b/hb_server/apps/monitor/views.py index 3e6cee4..ffd85fe 100644 --- a/hb_server/apps/monitor/views.py +++ b/hb_server/apps/monitor/views.py @@ -1,5 +1,6 @@ from django.shortcuts import render import psutil +from rest_framework import response from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated @@ -7,6 +8,8 @@ from rest_framework.viewsets import ViewSet from django.conf import settings import os from rest_framework import serializers, status +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema # Create your views here. class ServerInfoView(APIView): @@ -29,8 +32,23 @@ class ServerInfoView(APIView): ret['disk']['percent'] = disk.percent return Response(ret) +def get_file_list(file_path): + dir_list = os.listdir(file_path) + if not dir_list: + return + else: + # 注意,这里使用lambda表达式,将文件按照最后修改时间顺序升序排列 + # os.path.getmtime() 函数是获取文件最后修改时间 + # os.path.getctime() 函数是获取文件最后创建时间 + dir_list = sorted(dir_list,key=lambda x: os.path.getmtime(os.path.join(file_path, x)), reverse=True) + # print(dir_list) + return dir_list + class LogView(APIView): + @swagger_auto_schema(manual_parameters=[ + openapi.Parameter('name', openapi.IN_QUERY, description='日志文件名', type=openapi.TYPE_STRING) + ]) def get(self, request, *args, **kwargs): """ 查看最近的日志列表 @@ -38,20 +56,13 @@ class LogView(APIView): """ logs =[] name = request.GET.get('name', None) - for root, dirs, files in os.walk(settings.LOG_PATH): - for file in files: - if len(logs)>50:break - filepath = os.path.join(root, file) - if name: - if filepath.contains(name): - fsize = os.path.getsize(filepath) - if fsize: - logs.append({ - "name":file, - "filepath":filepath, - "size":round(fsize/1000,1) - }) - else: + # for root, dirs, files in os.walk(settings.LOG_PATH): + # files.reverse() + for file in get_file_list(settings.LOG_PATH): + if len(logs)>50:break + filepath = os.path.join(settings.LOG_PATH, file) + if name: + if name in filepath: fsize = os.path.getsize(filepath) if fsize: logs.append({ @@ -59,8 +70,17 @@ class LogView(APIView): "filepath":filepath, "size":round(fsize/1000,1) }) + else: + fsize = os.path.getsize(filepath) + if fsize: + logs.append({ + "name":file, + "filepath":filepath, + "size":round(fsize/1000,1) + }) return Response(logs) + class LogDetailView(APIView): def get(self, request, name):