1 Django端
└─opwf_project
├─celery_task
│ └─__pycache__
├─db
├─logs
├─opwf
│ ├─apps
│ │ ├─inform
│ │ │ ├─migrations
│ │ │ │ └─__pycache__
│ │ │ └─__pycache__
│ │ ├─user
│ │ │ ├─migrations
│ │ │ │ └─__pycache__
│ │ │ └─__pycache__
│ │ ├─workerorder
│ │ │ ├─migrations
│ │ │ │ └─__pycache__
│ │ │ └─__pycache__
│ │ └─workflow
│ │ ├─migrations
│ │ │ └─__pycache__
│ │ └─__pycache__
│ ├─libs
│ ├─opwf
│ │ ├─apps
│ │ └─__pycache__
│ ├─static
│ │ ├─admin
│ │ │ ├─css
│ │ │ │ └─vendor
│ │ │ │ └─select2
│ │ │ ├─fonts
│ │ │ ├─img
│ │ │ │ └─gis
│ │ │ └─js
│ │ │ ├─admin
│ │ │ └─vendor
│ │ │ ├─jquery
│ │ │ ├─select2
│ │ │ │ └─i18n
│ │ │ └─xregexp
│ │ ├─dist
│ │ │ └─static
│ │ │ ├─css
│ │ │ └─js
│ │ └─rest_framework
│ │ ├─css
│ │ ├─docs
│ │ │ ├─css
│ │ │ ├─img
│ │ │ └─js
│ │ ├─fonts
│ │ ├─img
│ │ └─js
│ ├─templates
│ │ └─search
│ │ └─indexes
│ │ └─workerorder
│ ├─utils
│ │ └─__pycache__
│ ├─whoosh_index
│ └─__pycache__
├─packages
├─scrips
└─uwsgi_conf
1.1 全局配置文件
1.1.1 settings.py
import datetime
import os, sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
CELERY_DIR = '/home/worker/opwf_project/celery_task'
sys.path.insert(0, CELERY_DIR)
# 加入celery的路径
SECRET_KEY = 'uorj1ni^mnut@wo@c%)iv)%5=8dxlml4-j0!f3b%4#f*8a5)3t'
DEBUG = True
ALLOWED_HOSTS = ['*']
# 默认是所有网址
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'haystack', # haystack要放在应用的上面
'user.apps.UserConfig',
'workflow.apps.WorkflowConfig',
'workerorder.apps.WorkerorderConfig',
'inform.apps.InformConfig',
# 'jwt',
# 'rest_framework_jwt',
# 'rest_framework.authentication'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'corsheaders.middleware.CorsMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'opwf.urls'
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
'http://127.0.0.1:8080',
'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'opwf.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'opwf_db',
'USER': 'root',
'PASSWORD': '1',
'HOST': '127.0.0.1',
'PORT': '3306'
}
}
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
REST_FRAMEWORK = {
# 文档报错: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
# 用下面的设置可以解决
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
# 默认设置是:
# 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',
# 异常处理器
# 'EXCEPTION_HANDLER': 'user.utils.exception_handler',
# Base API policies 默认渲染器类
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
# 默认解析器类
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
# 1.认证器(全局)
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 在 DRF中配置JWT认证
# 'rest_framework.authentication.SessionAuthentication', # 使用session时的认证器
# 'rest_framework.authentication.BasicAuthentication' # 提交表单时的认证器
],
# 2.权限配置(全局): 顺序靠上的严格
'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAdminUser', # 管理员可以访问
# 'rest_framework.permissions.IsAuthenticated', # 认证用户可以访问
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 认证用户可以访问, 否则只能读取
'rest_framework.permissions.AllowAny', # 所有用户都可以访问
],
# 3.限流(防爬虫)
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
# 3.1限流策略
# 'DEFAULT_THROTTLE_RATES': {
# 'user': '100/hour', # 认证用户每小时100次
# 'anon': '300/day', # 未认证用户每天能访问3次
# },
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None,
# 4.分页(全局):全局分页器, 例如 省市区的数据自定义分页器, 不需要分页
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# # 每页返回数量
# 'PAGE_SIZE': 3,
# 5.过滤器后端
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
# 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路径有变化
],
# 5.1过滤排序(全局):Filtering 过滤排序
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering',
'NUM_PROXIES': None,
# 6.版本控制:Versioning 接口版本控制
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version',
# Authentication 认证
# 未认证用户使用的用户类型
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
# 未认证用户使用的Token值
'UNAUTHENTICATED_TOKEN': None,
# View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing
'TEST_REQUEST_RENDERER_CLASSES': [
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
],
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
# Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url',
# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True,
# Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
# Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
# 'Access-Control-Allow-Origin':'http://localhost:8080',
# 'Access-Control-Allow-Credentials': True
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/dist')
# STATICFILES_DIRS = [
# os.path.join(BASE_DIR, 'static/dist')
# ]
AUTH_USER_MODEL = 'user.User'
# jwt载荷中的有效期设置
JWT_AUTH = {
# 1.token前缀:headers中 Authorization 值的前缀
'JWT_AUTH_HEADER_PREFIX': 'JWT',
# 2.token有效期:一天有效
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
# 3.刷新token:允许使用旧的token换新token
'JWT_ALLOW_REFRESH': True,
# 4.token有效期:token在24小时内过期, 可续期token
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
# 5.自定义JWT载荷信息:自定义返回格式,需要手工创建
'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
}
# 邮箱配置
# 配置邮件发送
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com' # 如果为163邮箱,设置为smtp.163.com
EMAIL_PORT = 25 # 或者 465/587是设置了 SSL 加密方式
# 发送邮件的邮箱
EMAIL_HOST_USER = '742327252@qq.com'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'czwiakrihfmpbfej' # 第三方登陆使用的授权密码
EMAIL_USE_TLS = True # 这里必须是 True,否则发送不成功
# 收件人看到的发件人, 必须是一直且有效的
EMAIL_FROM = '姬如飞羽<742327252@qq.com>'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
# 缓存配置
CACHES = {
# django存缓默认位置,redis 0号库
# default: 连接名称
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
# django session存 reidis 1 号库(现在基本不需要使用)
"session": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
# 邮件,存redis 2号库
"email": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
# 配置session使用redis存储
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# 配置session存储的位置: 使用cache中的 session配置
SESSION_CACHE_ALIAS = "session"
# 全文检索配置
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 4 # 搜索出多条数据时需要分页
# HAYSTACK_CONNECTIONS = {
# 'default': {
# # 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
# 'ENGINE': 'workerorder.whoosh_cn_backend.MyWhooshEngine',
# 'PATH': os.path.join(BASE_DIR, 'whoosh_index') # 指定倒排索引 存放位置
# }
# }
# ES引擎
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://192.168.56.100:9200/', # Elasticsearch服务器ip地址,端口号固 定为9200
'INDEX_NAME': 'opwf_db', # Elasticsearch建立的反向索引库的名称
},
}
#
# 添加此项,当数据库改变时,会自动更新索引,非常方便
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
1.1.2 __init__
.py
import pymysql
pymysql.install_as_MySQLdb()
1.1.3 urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('user/', include('user.urls')),
path('workflow/', include('workflow.urls')),
path('workerorder/', include('workerorder.urls')),
path('inform/', include('inform.urls'))
]
1.1.4 wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "opwf.settings")
application = get_wsgi_application()
1.2 user模块
1.2.1 models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
'''
username:用户名
password:密码
mobile:手机号
email:邮箱
'''
username = models.CharField(max_length=30, unique=True)
# 写上 unique=True 就可以指定唯一,验证字段的时候自动验证
password = models.CharField(max_length=256)
mobile = models.CharField(max_length=11)
email = models.CharField(max_length=30)
token = models.CharField(max_length=256, default='')
weixin = models.CharField(max_length=30, null=True)
date_joined = models.DateField(auto_now_add=True)
class Meta:
db_table = 'user_user'
verbose_name = '用户表'
verbose_name_plural = verbose_name
class Role(models.Model):
'''
name:角色名称
description:描述
'''
zh_name = models.CharField(max_length=30)
en_name = models.CharField(max_length=30)
description = models.TextField()
class Meta:
db_table = 'user_role'
verbose_name = '角色表'
verbose_name_plural = verbose_name
class UserRole(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
class Meta:
db_table = 'user_userrole'
verbose_name = '用户角色表'
verbose_name_plural = verbose_name
1.2.2 serializers.py
# -*- coding: utf-8 -*-
from rest_framework import serializers
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.settings import api_settings
from user.models import User, UserRole, Role
class UserModelSerializer(serializers.ModelSerializer):
is_superuser = serializers.CharField(default=0)
roles = serializers.SerializerMethodField(required=False)
class Meta:
model = User
fields = '__all__'
def create(self, data):
username = data.get('username', '')
password = data.get('password', '')
mobile = data.get('mobile', '')
email = data.get('email', '')
weixin = data.get('weixin', '')
user = User(username=username, email=email, mobile=mobile, weixin=weixin)
user.set_password(password)
user.save()
return user
def get_roles(self, row):
roles_json = UserRole.objects.filter(user=row).values('role__id', "role__zh_name")
print('111', roles_json)
print('222', list(roles_json))
print(type(list(roles_json)))
return list(roles_json)
def get_serializer_class(self):
return UserModelSerializer
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
username = serializers.CharField()
password = serializers.CharField()
mobile = serializers.CharField()
email = serializers.EmailField()
weixin = serializers.CharField()
token = serializers.CharField(read_only=True)
def create(self, data):
username = data.get('username', '')
password = data.get('password', '')
mobile = data.get('mobile', '')
email = data.get('email', '')
weixin = data.get('weixin', '')
user = User(username=username, email=email, mobile=mobile, weixin=weixin)
user.set_password(password)
user.save()
# 补充生成记录登录状态的token
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user.token = token
return user
class UserRoleSerializer(serializers.ModelSerializer):
class Meta:
model = UserRole
fields = '__all__'
class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = "__all__"
1.2.3 urls.py
# -*- coding: utf-8 -*-
from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token
from user import views
router = DefaultRouter()
router.register(r'user', views.UserViewSet)
router.register(r'user_get', views.UserGetViewSet)
router.register(r'role', views.RoleViewSet)
router.register(r'role_get', views.RoleGetViewSet)
urlpatterns = [
path('login/', obtain_jwt_token),
path('register/', views.RegisterView.as_view()),
path('is_superuser/', views.IsSuperUser.as_view()),
]
urlpatterns += router.urls
1.2.4 views.py
import datetime
import random
from django.contrib.auth.hashers import make_password
from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.views import APIView
from utils.MyAuthorization import MyPermission, decodeToken
from user.models import User, Role, UserRole
from user.serializers import UserSerializer, UserModelSerializer, RoleSerializer, UserRoleSerializer
# 分页(局部):自定义分页器 局部
class PageNum(PageNumberPagination):
page_size = 4 # 每页显示多少条
page_size_query_param = 'page_size' # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
page_query_param = 'page' # 查询字符串中代表页码的参数名, 有默认值: page
max_page_size = None # 最大页码数限制
class UserViewSet(viewsets.ModelViewSet):
permission_classes = (MyPermission,)
queryset = User.objects.all()
serializer_class = UserModelSerializer
filter_fields = {"username", 'mobile', 'weixin', 'email'}
pagination_class = PageNum # 注意不是列表(只能有一个分页模式)
# permission_classes = (MyPermission,)
def perform_update(self, serializer):
user_obj = serializer.save()
# 原有的serializer反序列化添加
roles = self.request.data.get('roles')
user_obj.userrole_set.all().delete()
for role_id in roles:
userrole = UserRole.objects.create(user=user_obj, role_id=role_id)
def get_serializer_class(self):
print('action------->', self.action)
return UserModelSerializer
class UserGetViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (MyPermission,)
class RoleViewSet(viewsets.ModelViewSet):
queryset = Role.objects.all()
serializer_class = RoleSerializer
filter_fields = {"zh_name"}
pagination_class = PageNum # 注意不是列表(只能有一个分页模式)
permission_classes = (MyPermission,)
class RoleGetViewSet(viewsets.ModelViewSet):
# 用于角色查看接口(没有分页的那种)
queryset = Role.objects.all()
serializer_class = RoleSerializer
permission_classes = (MyPermission,)
class RegisterView(APIView):
permission_classes = (MyPermission,)
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
password_new = request.data.get('password_new')
role = request.data.get('roles')
print(role)
if not all([username, password, password_new]):
return Response(
{'msg': '信息不全', 'code': 400}
)
if password != password_new:
return Response(
{'code': 400, 'msg':'两次登录密码不一致'}
)
user_serializer = UserSerializer(data=request.data)
if user_serializer.is_valid():
user_serializer.save()
user_obj = User.objects.filter(username=username).first()
print(user_obj)
new_list = []
for i in role:
role_obj = Role.objects.filter(id=i).first()
# UserRole.objects.create(user=user_obj, role=role_obj)
# UserRole.objects.create(user_id=user_obj.id, role_id=role_obj.id)
new_list.append((UserRole(user=user_obj, role=role_obj)))
UserRole.objects.bulk_create(new_list)
return Response(
{'msg': '注册成功', 'code': 200, 'data':user_serializer.data}
)
return Response(
{'msg': '注册失败', 'error': user_serializer.errors }
)
class IsSuperUser(APIView):
def get(self, request):
user_list = decodeToken(request)
user_id = user_list.get('user_id')
obj = User.objects.filter(pk=user_id).first()
if obj.is_superuser == 1:
return Response({'type': 1, 'code': 200})
return Response({'type': 2, 'code': 200})
1.2.5 utils.py
# -*- coding: utf-8 -*-
# from rest_framework_jwt.views import obtain_jwt_token
# 为urls.py中的导包
def jwt_response_payload_handler(token, user=None, request=None, role=None):
"""
自定义jwt认证成功返回数据
:token 返回的jwt
:user 当前登录的用户信息[对象]
:request 当前本次客户端提交过来的数据
:role 角色
"""
if user.first_name:
name = user.first_name
else:
name = user.username
return {
'authenticated': 'true',
'id': user.id,
"role": role,
'name': name,
'username': user.username,
'email': user.email,
'token': token,
}
1.2.6 admin.py
from django.contrib import admin
from .models import *
admin.site.register(User)
admin.site.register(Role)
admin.site.register(UserRole)
1.3 workerorder模块
- 主要针对工单模板的管理以及动态生成工单模板效果的实现
1.3.1 models.py
from django.db import models
from utils.MyBaseModel import BaseModel
from user.models import User
from workflow.models import FlowConf
class WorkOrderModel(BaseModel):
'''
flowconf:工单名称(一对多,FlowConf)
create_user:工单创建用户(一对多,User表)
create_ts:创建时间
order_status:工单状态(审批中/被驳回/完成)
description:描述(text,存储用户工单描述)
'''
status_choices = (
('1', '审批中'),
('2', '被驳回'),
('3', '完成')
)
flowconf = models.ForeignKey(FlowConf, on_delete=models.CASCADE, null=True)
create_user = models.ForeignKey(User, on_delete=models.CASCADE)
order_status = models.CharField('工单状态', help_text='审批中/被驳回/完成', choices=status_choices, default='1', max_length=30)
parameter = models.TextField(default='{}')
# description = models.TextField('描述', help_text='存储用户工单描述')
class Meta:
db_table = 'workerorder_workerorder'
verbose_name = '实例化工单'
verbose_name_plural = verbose_name
class SubOrderModel(BaseModel):
'''
mainorder:一对多(WorkOrder)实例化工单相连
approve_user:一对多(内置User表)审批人
approbe_user_role:审批角色
approve_userrole_id:审批角色id
sequence_number:审批序号
approve_ts:审批时间
action_status:审批状态(待审批/通过/拒绝/退回)
suborder_status:子任务状态(待处理/已经处理/待上一节点处理)
approve_text:审批意见
approve_type_id: 审批类型
'''
action_status_choices = (
('1', '待审批'),
('2', '通过'),
('3', '拒绝'),
('4', '退回')
)
suborder_status_choices = (
('1', '待处理'),
('2', '已经处理'),
('3', '待上一节点处理')
)
mainorder = models.ForeignKey(WorkOrderModel, on_delete=models.CASCADE)
approve_user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, default='')
approbe_user_role = models.CharField('审批角色', max_length=30, null=True)
approve_userrole_id = models.IntegerField('审批角色id', null=True)
sequence_number = models.IntegerField('审批序号')
approve_ts = models.DateTimeField('审批时间', auto_now_add=True)
action_status = models.CharField('审批状态', help_text='待审批/通过/拒绝/退回', choices=action_status_choices, default='1', max_length=30)
suborder_status = models.CharField('子任务状态', help_text='待处理/已经处理/待上一节点处理', choices=suborder_status_choices, default='1', max_length=30)
approve_text = models.TextField('审批意见')
type_choice = (
('1', '角色审批'),
('2', '指定人员审批')
)
approve_type_id = models.CharField(max_length=30, choices=type_choice)
class Meta:
db_table = 'workerorder_suborder'
verbose_name = '实例化子工单'
verbose_name_plural = verbose_name
1.3.2 serializers.py
# -*- coding: utf-8 -*-
from rest_framework import serializers
from workerorder.models import WorkOrderModel, SubOrderModel
class SubOrderSerializer(serializers.ModelSerializer):
approve_user_name = serializers.SerializerMethodField(required=False)
action_status_name = serializers.SerializerMethodField(required=False)
suborder_status_name = serializers.SerializerMethodField(required=False)
flowconf_name = serializers.SerializerMethodField(required=False)
flowconf = serializers.SerializerMethodField(required=False)
mainorder = serializers.SerializerMethodField(required=False)
class Meta:
model = SubOrderModel
fields = "__all__"
# row中提供了大量的方法和字段,可以用dir(row)的方法来打印出内置的方法和属性
def get_action_status_name(self, row):
return dict(row.action_status_choices)[row.action_status]
def get_suborder_status_name(self, row):
return dict(row.suborder_status_choices)[row.suborder_status]
def get_approve_user_name(self, row):
if row.approve_user:
return row.approve_user.username
else:
return ''
def get_flowconf_name(self, row):
return row.mainorder.flowconf.name
def get_flowconf(self, row):
return row.mainorder.flowconf.id
def get_mainorder(self, row):
return row.mainorder_id
class SubOrderDeepSerializer(serializers.ModelSerializer):
class Meta:
model = SubOrderModel
fields = '__all__'
class WorkOrderSerializer(serializers.ModelSerializer):
subordermodel_set = SubOrderDeepSerializer(many=True)
order_status_name = serializers.SerializerMethodField(required=False)
create_user_name = serializers.SerializerMethodField(required=False)
flowconf_name = serializers.CharField(required=False, source='flowconf.name')
class Meta:
model = WorkOrderModel
fields = '__all__'
def get_order_status_name(self, row):
status_choices = dict(row.status_choices)
return status_choices[row.order_status]
def get_create_user_name(self, row):
# print(type(row.create_user.username))
# <str>
return row.create_user.username
class WorkOrderTextSerializer(serializers.ModelSerializer):
class Meta:
model = WorkOrderModel
fields = '__all__'
1.3.3 urls.py
# -*- coding: utf-8 -*-
from django.urls import path
from workerorder.views import *
urlpatterns = [
path('workorder/', WorkOrderView.as_view()),
path('suborder_get/', SubOrderView.as_view()),
path('suborder/', SubOrderGetView.as_view()),
path('workorder_search/', SearchWorkOrderView.as_view()),
path('suborder_search/', SearchSubOrderVIew.as_view()),
]
1.3.4 views.py
- 本模块中的views采用了初级封装,代码还是不够完美,但是第一次做项目,还是值得纪念!
import json
from django.conf import settings
from django.core.paginator import InvalidPage, Paginator
from django.http import JsonResponse, HttpResponse
from haystack.forms import ModelSearchForm
from rest_framework.response import Response
from rest_framework.views import APIView
from user.models import User
from utils.MyAuthorization import decodeToken, getAuthorization
from utils.MyPaginator import basePaginator
from workerorder.models import WorkOrderModel, SubOrderModel
from workerorder.serializers import WorkOrderSerializer, SubOrderSerializer
from workerorder.utils import getSuborder, getWorkerorder, addWorkerOrder, chooseSuborder, serFields, esSearchWorkOrder, esSearchSubOrder
class WorkOrderView(APIView):
serializer_class = WorkOrderSerializer
page_size = 4
def get(self, request):
id = request.query_params.get('id')
if id is None:
if request.user.is_superuser:
queryset = WorkOrderModel.objects.all()
ret = basePaginator(queryset, request, self.page_size, self.serializer_class)
else:
user_info = decodeToken(request)
queryset = getWorkerorder(user_info)
ret = basePaginator(queryset, request, self.page_size, self.serializer_class)
else:
ser = WorkOrderModel.objects.filter(id=id)
ret = WorkOrderSerializer(ser, many=True).data
return Response(ret)
def post(self, request):
res = addWorkerOrder(request)
return Response(res)
class SubOrderView(APIView):
serializer_class = SubOrderSerializer
page_size = 4
def get(self, request):
mainorder = request.query_params.get('mainorder')
if mainorder is None:
if request.user.is_superuser:
queryset = SubOrderModel.objects.all()
ret = basePaginator(queryset, request, self.page_size, self.serializer_class)
else:
user_info = decodeToken(request)
queryset = getSuborder(user_info)
ret = basePaginator(queryset, request, self.page_size, self.serializer_class)
else:
ser = SubOrderModel.objects.filter(mainorder_id=mainorder)
ret = SubOrderSerializer(ser, many=True).data
return Response(ret)
def post(self, request):
res = chooseSuborder(request)
return Response(res)
class SubOrderGetView(APIView):
serializer_class = SubOrderSerializer
page_size = 4
def get(self, request):
ret = getAuthorization(request)
val = serFields(ret)
return Response(val)
class SearchWorkOrderView(APIView):
def get(self, request):
data = esSearchWorkOrder(request)
return Response(data)
class SearchSubOrderVIew(APIView):
def get(self, request):
data = esSearchSubOrder(request)
return Response(data)
1.3.5 utils.py
# -*- coding: utf-8 -*-
import os, sys
from django.core.paginator import Paginator, InvalidPage
from haystack.forms import ModelSearchForm
from utils.MyAuthorization import decodeToken
from user.models import Role, User, UserRole
from workerorder.models import WorkOrderModel, SubOrderModel
from workflow.models import FlowConf, NewFlowUserRoleActionConf
def getSuborder(user_info):
user_id = user_info.get('user_id')
role_list = user_info.get('role_id')
queryset = SubOrderModel.objects.all()
results = []
for val in queryset:
# worker_id = val.mainorder_id
# create_user_id = WorkOrderModel.objects.filter(pk=worker_id).first().create_user_id
# print(1111, create_user_id)
# if create_user_id == user_id:
# results.append(val)
# else:
if val.approve_type_id == '2':
if val.approve_user_id == user_id:
print(val.approve_user_id)
results.append(val)
elif val.approve_type_id == '1':
if val.approve_userrole_id in role_list:
print(val.approve_userrole_id)
results.append(val)
return results
def getWorkerorder(user_info):
queryset = []
suborder_list = []
# suborder_queryset = getSuborder(user_info)
# for i in suborder_queryset:
# if i.mainorder_id not in suborder_list:
# suborder_list.append(i.mainorder_id)
workerorder_queryset = WorkOrderModel.objects.all()
user_id = user_info.get('user_id')
for i in workerorder_queryset:
if i.create_user_id == user_id:
queryset.append(i)
else:
if i.id in suborder_list:
queryset.append(i)
return queryset
def addWorkerOrder(request):
user_info = decodeToken(request)
user_id = user_info.get('user_id')
name = request.data.get('name')
form = request.data.get('form')
flowconf = FlowConf.objects.filter(name=name).first().pk
dic = {
'flowconf_id': flowconf,
'create_user_id': user_id,
'order_status': '1',
'parameter': form
}
work_obj = WorkOrderModel.objects.create(**dic)
approveconf_obj = list(NewFlowUserRoleActionConf.objects.filter(flowconf_id=work_obj.flowconf_id).values())
time = 0
for item in approveconf_obj:
if item['approvetype'] == '1':
if time == 0:
child_data = {
'mainorder_id': work_obj.id,
'approbe_user_role': Role.objects.filter(pk=item['approve_type_id']).first().zh_name,
'approve_user_id':None,
# 不加就报错
'approve_userrole_id': item['approve_type_id'],
'sequence_number': item['sequence'],
'approve_type_id': '1'
}
print('child_data', child_data)
SubOrderModel.objects.create(**child_data)
time += 1
else:
child_data = {
'mainorder_id': work_obj.id,
'approbe_user_role': Role.objects.filter(pk=item['approve_type_id']).first().zh_name,
'approve_user_id': None,
# 不加就报错
'approve_userrole_id': item['approve_type_id'],
'sequence_number': item['sequence'],
'approve_type_id': '1',
'suborder_status': '3'
}
SubOrderModel.objects.create(**child_data)
time += 1
if item['approvetype'] == '2':
if time == 0:
child_data = {
'mainorder_id': work_obj.id,
'approve_user_id': User.objects.filter(pk=item['approve_type_id']).first().id,
'approbe_user_role': '',
'approve_userrole_id': None,
'sequence_number': item['sequence'],
'approve_type_id': '2'
}
print('obj', child_data)
SubOrderModel.objects.create(**child_data)
time += 1
else:
child_data = {
'mainorder_id': work_obj.id,
'approve_user_id': User.objects.filter(pk=item['approve_type_id']).first().id,
'approbe_user_role': '',
'approve_userrole_id': None,
'sequence_number': item['sequence'],
'approve_type_id': '2',
'suborder_status': '3'
}
print('obj', child_data)
SubOrderModel.objects.create(**child_data)
user_list = []
user_id = SubOrderModel.objects.filter(mainorder_id=work_obj.id, sequence_number=1).first().approve_user_id
if user_id:
user_list.append(user_id)
else:
role_id = SubOrderModel.objects.filter(mainorder_id=work_obj.id, sequence_number=1).first().approve_userrole_id
user_get = UserRole.objects.filter(role_id=role_id).values('user_id')
for i in user_get:
user_list.append(i.get('user_id'))
sys.path.insert(0, r'/home/worker/opwf_project')
from celery_task import tasks
tasks.send_email.delay(user_list)
res = {'msg': '添加成功', 'code': 200, 'id': work_obj.pk, 'flowconf': work_obj.flowconf_id}
return res
def chooseSuborder(request):
# 子工单id
suborder_id = request.data.get('suborder_id')
# 子工单审批状态
action_status = request.data.get('action_status')
# 意见
decision = request.data.get('decision')
# 所属实例工单id
mainorder = SubOrderModel.objects.filter(pk=suborder_id).first().mainorder_id
# 子工单序号
sequence_number = SubOrderModel.objects.filter(pk=suborder_id).first().sequence_number
SubOrderModel.objects.filter(pk=suborder_id).update(action_status=action_status)
SubOrderModel.objects.filter(pk=suborder_id).update(suborder_status=2)
SubOrderModel.objects.filter(pk=suborder_id).update(approve_text=decision)
squenue_list = []
squenue_obj = SubOrderModel.objects.filter(mainorder_id=mainorder).values('sequence_number')
for i in squenue_obj:
squenue_list.append(i.get('sequence_number'))
print('list', squenue_list)
index = squenue_list.index(sequence_number) + 1
print('index', index)
if action_status == '4':
# 4 子工单退回--------主工单 驳回
WorkOrderModel.objects.filter(pk=mainorder).update(order_status=2) # 主工单一旦完成,子工单不变
text = '驳回'
sys.path.insert(0, r'/home/worker/opwf_project')
from celery_task import tasks_back
tasks_back.back_email(WorkOrderModel.objects.get(pk=mainorder).create_user.username, WorkOrderModel.objects.get(pk=mainorder).create_user.email, text)
# if squenue_list[index-1] != squenue_list[-1]:
# for m in squenue_list[index:]:
# SubOrderModel.objects.filter(sequence_number=m).update(suborder_status=2)
# SubOrderModel.objects.filter(sequence_number=m).update(action_status=action_status)
if action_status == '3':
# 3 子工单拒绝-------主工单 完成
WorkOrderModel.objects.filter(pk=mainorder).update(order_status=3) # 主工单一旦驳回,子工单不变
text = '拒绝'
sys.path.insert(0, r'/home/worker/opwf_project')
from celery_task import tasks_back
tasks_back.back_email(WorkOrderModel.objects.get(pk=mainorder).create_user.username,
WorkOrderModel.objects.get(pk=mainorder).create_user.email, text)
# if squenue_list[index-1] != squenue_list[-1]:
# for m in squenue_list[index:]:
# SubOrderModel.objects.filter(sequence_number=m).update(suborder_status=2)
# SubOrderModel.objects.filter(sequence_number=m).update(action_status=action_status)
if action_status == '2':
# suborder_status 1 待处理 2 已经处理 3 待上一节点处理
if squenue_list[index - 1] == squenue_list[-1]:
WorkOrderModel.objects.filter(pk=mainorder).update(order_status=3)
text = '通过'
sys.path.insert(0, r'/home/worker/opwf_project')
from celery_task import tasks_back
tasks_back.back_email(WorkOrderModel.objects.get(pk=mainorder).create_user.username,
WorkOrderModel.objects.get(pk=mainorder).create_user.email, text)
else:
# suborder_status 1 待处理 2 已经处理 3 待上一节点处理
SubOrderModel.objects.filter(mainorder_id=mainorder, sequence_number=index + 1).update(suborder_status=1)
user_list = []
user_id = SubOrderModel.objects.filter(sequence_number=index + 1, mainorder_id=mainorder).first().approve_user_id
if user_id:
user_list.append(user_id)
else:
role_id = SubOrderModel.objects.filter(sequence_number=index + 1, mainorder_id=mainorder).first().approve_userrole_id
user_get = UserRole.objects.filter(role_id=role_id).values('user_id')
for i in user_get:
user_list.append(i.get('user_id'))
# EmailInform(user_list)
# sys.path.insert(0, os.path.join('/home/worker/opwf_project', 'celery_task'))
sys.path.insert(0, r'/home/worker/opwf_project')
from celery_task import tasks
tasks.send_email.delay(user_list)
res = {'msg': 'ok', 'code': 200}
return res
def serFields(ret):
for i in ret:
if i['suborder_status'] == '1':
i['suborder_status_name'] = '待处理'
if i['action_status'] == '1':
i['action_status_name'] = '待审批'
elif i['action_status'] == '2':
i['action_status_name'] = '通过'
elif i['action_status'] == '3':
i['action_status_name'] = '拒绝'
elif i['action_status'] == '4':
i['action_status_name'] = '退回'
elif i['suborder_status'] == '2':
i['suborder_status_name'] = '已经处理'
if i['action_status'] == '1':
i['action_status_name'] = '待审批'
elif i['action_status'] == '2':
i['action_status_name'] = '通过'
elif i['action_status'] == '3':
i['action_status_name'] = '拒绝'
elif i['action_status'] == '4':
i['action_status_name'] = '退回'
elif i['suborder_status'] == '3':
i['suborder_status_name'] = '待上一节点处理'
if i['action_status'] == '1':
i['action_status_name'] = '待审批'
elif i['action_status'] == '2':
i['action_status_name'] = '通过'
elif i['action_status'] == '3':
i['action_status_name'] = '拒绝'
elif i['action_status'] == '4':
i['action_status_name'] = '退回'
return ret
# def elastaticSearch(request):
# RESULTS_PER_PAGE = getattr(settings, 'HAYSTACK_SEARCH_RESULTS_PER_PAGE', 4)
# # 1.获取前端传过来的关键字(查询数据)
# query = request.GET.get('q', None)
# page = int(request.GET.get('page', 1)) # 第几页
# page_size = int(request.GET.get('page_size', RESULTS_PER_PAGE)) # 每页多少条
# # 2.获取查询条件,进行查询
# if query:
# form = ModelSearchForm(request.GET, load_all=True) # 将查询条件传递给查询对象
# if form.is_valid():
# results = form.search() # 查询出来的最终数据
# else:
# results = []
# else:
# return {"msg": 'No file found!', "data": []}
# # 3.对结果集进行分页
# paginator = Paginator(results, page_size)
# try:
# page = paginator.page(page) # 从分好的页中拿第几页
# except InvalidPage: # 如果分页出错
# return {"msg": 'No file found!', "data": []}
#
# # 4.把查询的分页结果集对象转换成json格式
def esSearchWorkOrder(request):
user_info = decodeToken(request)
user_id = user_info.get('user_id')
form = ModelSearchForm(request.GET, load_all=True) # 将查询条件传递给查询对象
if form.is_valid():
results = form.search() # 查询出来的最终数据
else:
results = []
jsondata = []
for result in results: # 课程查询结果
if User.objects.filter(pk=user_id).first().is_superuser == False and result.create_user_id == user_id:
jsondata.append({
'id': result.object.id,
'flowconf': result.object.flowconf_id,
'flowconf_name': result.object.flowconf.name,
'create_user_id': result.object.create_user_id,
'create_user_name': result.object.create_user.username,
'order_status_name': result.object.get_order_status_display(),
'create_time': str(result.object.create_time)
})
elif User.objects.filter(pk=user_id).first().is_superuser:
jsondata.append({
'id': result.object.id,
'flowconf': result.object.flowconf_id,
'flowconf_name': result.object.flowconf.name,
'create_user_id': result.object.create_user_id,
'create_user_name': result.object.create_user.username,
'order_status_name': result.object.get_order_status_display(),
'create_time': str(result.object.create_time)
})
present_page = int(request.GET.get('page', 1))
paginator_new = Paginator(jsondata, 4)
one_page = paginator_new.page(present_page)
count = paginator_new.count
data = {
'count': count,
'results': list(one_page)
}
return data
def esSearchSubOrder(request):
user_info = decodeToken(request)
user_id = user_info.get('user_id')
role_id = user_info.get('role_id')
form = ModelSearchForm(request.GET, load_all=True) # 将查询条件传递给查询对象
if form.is_valid():
results = form.search() # 查询出来的最终数据
print(1234567,results)
else:
results = []
jsondata = []
for result in results: # 课程查询结果
if User.objects.filter(pk=user_id).first().is_superuser == False:
if result.object.approve_user_id == user_id:
jsondata.append({
'id': result.object.id,
'mainorder': result.object.mainorder_id,
'flowconf_name': result.object.mainorder.flowconf.name,
'flowconf': result.object.mainorder.flowconf.id,
'approve_user_id': result.object.approve_user_id,
'approve_user_name': result.object.approve_user.username,
'action_status_name': dict(result.object.action_status_choices)[result.object.action_status],
'action_status': result.object.action_status,
'suborder_status': result.object.suborder_status,
'approbe_user_role': '',
'approve_userrole_id': result.object.approve_userrole_id,
'type':'search'
})
if result.object.approve_user_id is None and result.object.approve_userrole_id in role_id:
jsondata.append({
'id': result.object.id,
'mainorder': result.object.mainorder_id,
'flowconf_name': result.object.mainorder.flowconf.name,
'flowconf': result.object.mainorder.flowconf.id,
'approve_user_id': result.object.approve_user_id,
'approve_user_name': '',
'action_status_name': dict(result.object.action_status_choices)[result.object.action_status],
'action_status': result.object.action_status,
'suborder_status': result.object.suborder_status,
'approbe_user_role': result.object.approbe_user_role,
'approve_userrole_id': result.object.approve_userrole_id,
'type': 'search'
})
else:
if result.object.approve_user_id is None:
jsondata.append({
'id': result.object.id,
'mainorder': result.object.mainorder_id,
'flowconf_name': result.object.mainorder.flowconf.name,
'flowconf': result.object.mainorder.flowconf.id,
'approve_user_id': result.object.approve_user_id,
'approve_user_name': '',
'action_status_name': dict(result.object.action_status_choices)[result.object.action_status],
'action_status': result.object.action_status,
'suborder_status': result.object.suborder_status,
'approbe_user_role': result.object.approbe_user_role,
'approve_userrole_id': result.object.approve_userrole_id,
'type': 'search'
})
else:
jsondata.append({
'id': result.object.id,
'mainorder': result.object.mainorder_id,
'flowconf_name': result.object.mainorder.flowconf.name,
'flowconf': result.object.mainorder.flowconf.id,
'approve_user_id': result.object.approve_user_id,
'approve_user_name': result.object.approve_user.username,
'action_status_name': dict(result.object.action_status_choices)[result.object.action_status],
'action_status': result.object.action_status,
'suborder_status': result.object.suborder_status,
'approbe_user_role': '',
'approve_userrole_id': result.object.approve_userrole_id,
'type': 'search'
})
present_page = int(request.GET.get('page', 1))
paginator_new = Paginator(jsondata, 4)
one_page = paginator_new.page(present_page)
count = paginator_new.count
data = {
'count': count,
'results': list(one_page)
}
return data
1.3.6 admin.py
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(WorkOrderModel)
admin.site.register(SubOrderModel)
1.3.3 search_indexes.py
# -*- coding: utf-8 -*-
# apps/course/search_indexes.py
# 文件名必须是 search_indexes.py
from haystack import indexes
from workerorder.models import WorkOrderModel, SubOrderModel
# 修改此处,类名为模型类的名称+Index,比如模型类为GoodsInfo,则这里类名为GoodsInfoIndex(其 实可以随便写)
class WorkOrderIndex(indexes.SearchIndex, indexes.Indexable):
"""
Course索引类
"""
# text为索引字段
# document = True,这代表haystack和搜索引擎将使用此字段的内容作为索引进行检索
# use_template=True 指定根据表中的那些字段建立索引文件的说明放在一个文件中
text = indexes.CharField(document=True, use_template=True)
# 对那张表进行查询
def get_model(self): # 重载get_model方法,必须要有
"""返回建立索引的模型类"""
return WorkOrderModel # 返回这个model
# 建立索引的数据
def index_queryset(self, using=None):
"""返回要建立索引的数据查询集"""
# 这个方法返回什么内容,最终就会对那些方法建立索引,这里是对所有字段建立索引
return self.get_model().objects.all()
class SubOrderIndex(indexes.SearchIndex, indexes.Indexable):
"""
Course索引类
"""
# text为索引字段
# document = True,这代表haystack和搜索引擎将使用此字段的内容作为索引进行检索
# use_template=True 指定根据表中的那些字段建立索引文件的说明放在一个文件中
text = indexes.CharField(document=True, use_template=True)
# 对那张表进行查询
def get_model(self): # 重载get_model方法,必须要有
"""返回建立索引的模型类"""
return SubOrderModel # 返回这个model
# 建立索引的数据
def index_queryset(self, using=None):
"""返回要建立索引的数据查询集"""
# 这个方法返回什么内容,最终就会对那些方法建立索引,这里是对所有字段建立索引
return self.get_model().objects.all()
1.3.4 whoosh_cn_backend.py
# -*- coding: utf-8 -*-
# 更换 text 字段的 分析方式, 变为jieba分词中的中文分析器
from haystack.backends.whoosh_backend import WhooshEngine, WhooshSearchBackend
from whoosh.fields import TEXT
from jieba.analyse import ChineseAnalyzer
class MyWhooshSearchBackend(WhooshSearchBackend):
def build_schema(self, fields):
(content_field_name, schema) = super().build_schema(fields)
# 指定whoosh使用jieba进行分词
schema._fields['text'] = TEXT(stored=True,
analyzer=ChineseAnalyzer(),
field_boost=fields.get('text').boost,
sortable=True)
return (content_field_name, schema)
class MyWhooshEngine(WhooshEngine):
backend = MyWhooshSearchBackend
1.4 workflow 模块
1.4.1 models.py
from django.db import models
# Create your models here.
from utils.MyBaseModel import BaseModelFlow
class FlowType(BaseModelFlow):
'''
name:工作流名称
description:描述
'''
class Meta:
db_table = 'workflow_flowtype'
verbose_name = '工单分类'
verbose_name_plural = verbose_name
class FlowConf(BaseModelFlow):
flowtype = models.ForeignKey(FlowType, on_delete=models.CASCADE)
customfield = models.TextField('自定义字段', help_text='{}')
class Meta:
db_table = 'workflow_flowconf'
verbose_name = '工单模板'
verbose_name_plural = verbose_name
class NewFlowUserRoleActionConf(models.Model):
'''
flowconf:流程审批名称(外键关联FlowConf,一对多)
sequence:审批序号(用于排序)
approvetype:审批类型
approve_type_id:审批类型id
'''
flowconf = models.ForeignKey(FlowConf, on_delete=models.CASCADE)
sequence = models.IntegerField('审批序号', help_text='用于排序')
type_choice = (
('1', '角色组审批'),
('2', '指定人员审批')
)
approvetype = models.CharField('审批类型', choices=type_choice, max_length=30)
approve_type_id = models.IntegerField('审批类型id')
class Meta:
db_table = 'workflow_approveconf'
verbose_name = '配置审批流'
verbose_name_plural = verbose_name
1.4.2 serializers.py
# -*- coding: utf-8 -*-
from rest_framework import serializers
from user.models import User, Role
from workflow.models import FlowConf, FlowType, NewFlowUserRoleActionConf
class FlowConfSerializer(serializers.ModelSerializer):
flowtype_name = serializers.ReadOnlyField(source='flowtype.name')
# 第一种序列化展示方法,新增字段flowtype_name储存名称
class Meta:
model = FlowConf
fields = "__all__"
class FlowTypeSerializer(serializers.ModelSerializer):
class Meta:
model = FlowType
fields = '__all__'
class ApproveConfGetSerializer(serializers.ModelSerializer):
flowconf_name = serializers.ReadOnlyField(source='flowconf.name')
approvetype_name = serializers.SerializerMethodField(required=False)
approve_type_id_name = serializers.SerializerMethodField(required=False)
class Meta:
model = NewFlowUserRoleActionConf
fields = '__all__'
def get_approvetype_name(self, row):
# print(row.approvetype)
if row.approvetype == '1':
return '角色组审批'
elif row.approvetype == '2':
return '指定人员审批'
else:
pass
# 更多暂未设定
def get_approve_type_id_name(self, row):
if row.approvetype == '2':
return User.objects.filter(pk=row.approve_type_id).first().username
elif row.approvetype == '1':
return Role.objects.filter(pk=row.approve_type_id).first().zh_name
else:
pass
class ApproveConfSerializer(serializers.ModelSerializer):
class Meta:
model = NewFlowUserRoleActionConf
fields = '__all__'
1.4.3 urls.py
# -*- coding: utf-8 -*-
from django.urls import path
from rest_framework.routers import DefaultRouter
from workflow import views
router = DefaultRouter()
router.register(r'flowtype', views.FlowTypeViewSet)
router.register(r'flowtype_get', views.FlowTypeGetViewSet)
router.register(r'flowconf', views.FlowConfViewSet)
router.register(r'flowconf_get', views.FlowConfGetViewSet)
router.register(r'approveconf', views.ApproveConfViewSet)
urlpatterns = [
]
urlpatterns += router.urls
1.4.4 views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.pagination import PageNumberPagination
from rest_framework.viewsets import ModelViewSet
from utils.MyAuthorization import MyPermission
from workflow.models import FlowConf, FlowType, NewFlowUserRoleActionConf
from workflow.serializers import FlowConfSerializer, FlowTypeSerializer, ApproveConfGetSerializer, ApproveConfSerializer
# 分页(局部):自定义分页器 局部
class PageNum(PageNumberPagination):
page_size = 4 # 每页显示多少条
page_size_query_param = 'page_size' # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
page_query_param = 'page' # 查询字符串中代表页码的参数名, 有默认值: page
max_page_size = None # 最大页码数限制
class FlowConfViewSet(ModelViewSet):
permission_classes = (MyPermission,)
queryset = FlowConf.objects.all()
serializer_class = FlowConfSerializer
pagination_class = PageNum
filter_fields = {'name'}
class FlowConfGetViewSet(ModelViewSet):
# permission_classes = (MyPermission,)
queryset = FlowConf.objects.all()
serializer_class = FlowConfSerializer
class FlowTypeViewSet(ModelViewSet):
permission_classes = (MyPermission,)
queryset = FlowType.objects.all()
serializer_class = FlowTypeSerializer
pagination_class = PageNum
filter_fields = {'name'}
class FlowTypeGetViewSet(ModelViewSet):
permission_classes = (MyPermission,)
queryset = FlowType.objects.all()
serializer_class = FlowTypeSerializer
class ApproveConfViewSet(ModelViewSet):
# permission_classes = (MyPermission,)
queryset = NewFlowUserRoleActionConf.objects.all()
serializer_class = ApproveConfGetSerializer
pagination_class = PageNum
filter_fields = {'flowconf'}
def get_serializer_class(self):
print(self.action)
if self.action == 'list':
return ApproveConfGetSerializer
else:
return ApproveConfSerializer
1.4.5 admin.py
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(NewFlowUserRoleActionConf)
admin.site.register(FlowConf)
admin.site.register(FlowType)
1.5 tempaltes文件夹
1.5.1 目录
- 目录一定不能乱写,txt文件名为
模型名_text.txt
模式,不得随意更改
![image-20201212151934042]()
1.5.2 subordermodel_text.txt
- 因为表中 subordermodel 是 workordermodel 的子工单,两个表通过外键连接,所以不可以建立相同模块的索引,否则 views 中会出现找不到指定字段的情况!
- 建立索引的命令为:
python.manage.py rebuild_index
{{object.create_time}}
{{object.update_time}}
{{object.approbe_user_role}}
{{object.approve_user.username}}
{{object.get_action_status_display}}
{{object.get_suborder_status_display}}
{{object.mainorder.flowconf.name}}
1.5.3 workordermodel_text.txt
{{object.get_order_status_display}}
1.6 utils 公用工具
1.6.1 MyAuthorization.py
# -*- coding: utf-8 -*-
from rest_framework.permissions import BasePermission
from rest_framework_jwt.utils import jwt_decode_handler
from user.models import UserRole
from workerorder.models import SubOrderModel
def decodeToken(request):
token = request.META.get('HTTP_AUTHORIZATION')
user_info = jwt_decode_handler(token[4:])
# user_info = jwt_decode_handler(token)
user_id = user_info.get('user_id')
role_info = UserRole.objects.filter(user_id=user_id).values('role_id')
role_list = []
for i in role_info:
role_list.append(i.get('role_id'))
user_info['role_id'] = role_list
return user_info
def getAuthorization(request):
user_info = decodeToken(request)
user_id = user_info.get('user_id')
role_list = user_info.get('role_id')
# 实例化工单id
workorder_id = request.query_params.get('mainorder')
suborder_list = []
for i in SubOrderModel.objects.filter(mainorder_id=workorder_id):
suborder_list.append(i.id)
suborder_list=sorted(suborder_list)
queryset = []
for suborder_id in suborder_list:
# 审批类型id,1角色审批,2指定人审批
obj = SubOrderModel.objects.filter(pk=suborder_id).values()[0]
approve_type_id = obj['approve_type_id']
if approve_type_id == '1':
approve_userrole_id = obj['approve_userrole_id']
if approve_userrole_id in role_list:
if obj['action_status'] == '1' and obj['suborder_status'] == '1':
obj['type'] = 1
else:
obj['type'] = 0
queryset.append(obj)
elif approve_type_id == '2':
approve_user = obj['approve_user_id']
if user_id == approve_user:
if obj['action_status'] == '1' and obj['suborder_status'] == '1':
obj['type'] = 1
else:
obj['type'] = 0
queryset.append(obj)
return queryset
class MyPermission(BasePermission):
# 继承的一定要是rest_framework里面的BasePermission,类的名字随意
# has_permission 是用户对这个视图有没有 GET POST PUT PATCH DELETE 权限的分别判断
def has_permission(self, request, view):
print(123,request.method)
# 可以用request.user来获取用户名
# 可以用request.method来查询访问的方式
# 可以用request.path_info来查询访问的路由
# 可以用 print(dir(request))来查看方法
# 任何用户对使用此权限类的视图都有访问权限
if request.method == 'POST' or request.method == 'DELETE' or request.method == 'PUT' or request.method == 'GET':
print(request.method)
if request.user.is_superuser:
return True
elif view.kwargs.get('pk') == str(request.user.id):
return False
# has_object_permission 是用户过了 has_permission 判断有权限以后,再判断这个用户有 没有对一个具体的对象有没有操作权限
# 这样设置以后,即使是django admin管理员也只能查询自己user标的信息,不能查询其他用户的 单条信息
# def has_object_permission(self, request, view,obj):
# # 判断当前用户是否有访问 /course/sections/1/ 接口权限
# course_detail_url = re.match('/course/sections/(\d+)/$', request.path_info)
# if course_detail_url:
# user = request.user
# course = obj.chapters.course
# has_video_rights = self.video_rights(user,course)
# return has_video_rights
# return True
1.6.2 MyBaseModel.py
# -*- coding: utf-8 -*-
from django.db import models
class BaseModel(models.Model):
is_delete = models.BooleanField('是否刪除', default=0)
create_time = models.DateField('创建时间', auto_now_add=True, null=True)
update_time = models.DateField('更新时间', auto_now=True, null=True)
class Meta:
abstract = True
class BaseModelFlow(models.Model):
name = models.CharField('名称', max_length=60)
description = models.TextField('描述')
class Meta:
abstract = True
1.6.3 MyEncoder.py
# -*- coding: utf-8 -*-
import json
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, bytes):
return str(obj, encoding='utf-8')
return json.JSONEncoder.default(self, obj)
1.6.4 MyPaginator.py
# -*- coding: utf-8 -*-
from django.core.paginator import Paginator
def basePaginator(queryset, request, page_size, serializer):
# 当前页
present_page = int(request.GET.get('page', 1))
# 自定义分页器
paginator_get = Paginator(queryset, page_size)
# 一页数据
one_page = serializer(paginator_get.page(present_page), many=True).data
# 总页数
count = paginator_get.count
data = {
'count': count,
'results': one_page
}
return data
1.6.5 MySendEmail.py
# -*- coding: utf-8 -*-
from django.conf import settings
from django.core.mail import send_mail
from workerorder.models import *
def EmailInform(user_list):
for user_id in user_list:
email = User.objects.filter(pk=user_id).first().email
username = User.objects.filter(pk=user_id).first().username
subject = '勤奋的工单系统'
message = ''
from_email = settings.EMAIL_FROM
recipient_list = [email]
html_message = 'dear{},您有新的工单审批信息了!可以点击以下链接进行工单审批:<a href="http://127.0.0.1:8080/login/">审批工单</a>'.format(username)
send_mail(subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
html_message=html_message)
def sayHello():
obj = SubOrderModel.objects.filter(suborder_status='1', action_status='1')
for i in obj:
if i.approve_type_id == '2':
username = User.objects.filter(pk=i.approve_user_id).first().username
email = User.objects.filter(pk=i.approve_user_id).first().eamil
subject = '守约的工单系统'
message = ''
from_email = settings.EMAIL_FROM
recipient_list = [email]
html_message = 'Dear{}, 您有新的指定工单需要审批,十万火急!请点击以下链接查看最新动态!<a href="http://127.0.0.1:8080/login/">宇宙无敌混世大魔王的订单系统</a>'.format(username)
send_mail(subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
html_message=html_message)
def timeOutEmail(username, email):
subject = '贱贱的工单'
message = ''
from_email = settings.EMAIL_FROM
recipient_list = [email]
html_message = 'Dear{},呀,一不小心,您的工单超时了呢,真可怜~请点击以下链接查看最新动态!<a href="http://127.0.0.1:8080/login/">宇宙无敌混世大魔王的订单系统</a>'.format(username)
send_mail(subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
html_message=html_message)
def backEmail(username, email, text):
subject = '美丽的的工单'
message = ''
from_email = settings.EMAIL_FROM
recipient_list = [email]
html_message = 'Dear{},您的您的工单审批结果已出,被{},请点击以下链接查看最新动态!<a href="http://127.0.0.1:8080/login/">宇宙无敌混世大魔王的订单系统</a>'.format(username, text)
send_mail(subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
html_message=html_message)
1.7 celery_task
celery -A celery_task.celery_lk worker -l INFO
celery -A celery_task.celery_lk beat -l INFO
- 其中出现 no model named celery_task
# 解决方案是导入项目根目录(就是opwf_object地址)
# 在启动celery失败的时候,记得更改celery.py为celery_lk.py启动文件
1.7.1 celery_lk.py
# -*- coding: utf-8 -*-
from celery import Celery
import os, sys
CELERY_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
print(sys.path)
# celery项目中的所有导包地址, 都是以CELERY_BASE_DIR为基准设定.
# 执行celery命令时, 也需要进入CELERY_BASE_DIR目录执行.
import django
sys.path.insert(0, os.path.join(CELERY_BASE_DIR, '../opwf'))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "opwf.settings")
# 定义celery实例, 需要的参数, 1, 实例名, 2, 任务发布位置, 3, 结果保存位置
app = Celery('celery',
broker='redis://127.0.0.1:6379/2', # 任务存放的地方
backend='redis://127.0.0.1:6379/3', # 结果存放的地方
include=['celery_task.tasks', 'celery_task.tasks_beat', 'celery_task.tasks_timeout', 'celery_task.tasks_back'])
# 由于上面配置了路径,所以导入时需要注意
app.conf.update(
result_expires=3600, #执行结果放到redis里,一个小时没人取就丢弃
)
from celery.schedules import crontab
# 配置定时任务
app.conf.beat_schedule = {
'add-every-180-seconds': {
'task': 'celery_task.tasks_beat.say_hello',
# 'schedule': 180.0,
# 每180秒发送一次任务
'schedule': crontab(hour=8, minute=22, day_of_week=2),
},
'check-every-24-hours': {
'task': 'celery_task.tasks_timeout.timeout_workorder',
'schedule': 180.0,
}
}
app.conf.timezone = 'Asia/Shanghai'
if __name__ == '__main__':
app.start()
1.7.2 tasks.py
![image-20201212155420891](imgs/image-20201212155420891.png)
# -*- coding: utf-8 -*-
# @app.task 指定将这个函数的执行交给celery异步执行
# 相当于装饰器,将下面函数打包给app
from celery_task.celery_lk import app
@app.task(bind=True)
def send_email(self, user_list):
# 在方法中导包
from utils.MySendEmail import EmailInform
# time.sleep(5)
try:
# 用 res 接收发送结果, 成功是:0, 失败是:-1
res = EmailInform(user_list)
except Exception as e:
res = '-1'
if res == '-1':
# 如果发送结果是 -1 就重试.
self.retry(countdown=5, max_retries=3, exc=Exception('邮箱发送失败'))
1.7.3 tasks_timeout.py
![image-20201212155445074](imgs/image-20201212155445074.png)
- celery超时任务,采用 celery 定时任务实施监控,开启一个beat调度worker对工单的状态进行监控,对72小时未审批的工单创建者进行通知,将工单状态改为超时
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
# @app.task 指定将这个函数的执行交给celery异步执行
from django.db.models import Q
from django.utils import timezone
# 相当于装饰器,将下面函数打包给app
from celery_task.celery_lk import app
from user.models import User
from workerorder.models import WorkOrderModel
now = timezone.now()
start_time = now - timezone.timedelta(hours=48)
end_time = now
@app.task(bind=True)
def timeout_workorder(self):
# 在方法中导包
from utils.MySendEmail import timeOutEmail
workorder_queryset = WorkOrderModel.objects.exclude(
Q(create_time__range=(start_time, end_time))).filter(Q(order_status='1'))
for query in workorder_queryset:
username = User.objects.filter(pk=query.create_user_id).first().username
email = User.objects.filter(pk=query.create_user_id).first().email
res = timeOutEmail(username, email)
WorkOrderModel.objects.filter(id=query.id).update(order_status=2)
1.7.4 tasks_beat.py
![image-20201212160033505](imgs/image-20201212160033505.png)
- celery定时任务,每周一八点半对所有在当前审批节点的用户进行工单未审批通知。
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
# @app.task 指定将这个函数的执行交给celery异步执行
# 相当于装饰器,将下面函数打包给app
from celery_task.celery_lk import app
@app.task(bind=True)
def say_hello(self):
# 在方法中导包
from utils.MySendEmail import sayHello
# time.sleep(5)
try:
# 用 res 接收发送结果, 成功是:0, 失败是:-1
res = sayHello()
except Exception as e:
res = '-1'
if res == '-1':
# 如果发送结果是 -1 就重试.
self.retry(countdown=5, max_retries=3, exc=Exception('邮箱发送失败'))
1.7.5 tasks_back.py
![image-20201212155540134](imgs/image-20201212155540134.png)
- 工单一旦完成就通知审批人,对当前工单的状态通过邮件的方式进行告知。
# -*- coding: utf-8 -*-
from celery_task.celery_lk import app
@app.task(bind=True)
def back_email(self, username, email, text):
# 在方法中导包
from utils.MySendEmail import backEmail
# time.sleep(5)
try:
# 用 res 接收发送结果, 成功是:0, 失败是:-1
res = backEmail(username, email, text)
except Exception as e:
res = '-1'
if res == '-1':
# 如果发送结果是 -1 就重试.
self.retry(countdown=5, max_retries=3, exc=Exception('邮箱发送失败'))
1.8 项目部署
1.8.1 nginx部署
- 写入etc/nginx/conf.d/django.conf
server {
listen 1594;
server_name 192.168.56.100;
location /static {
alias /home/worker/opwf_project/opwf/static;
}
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8000;
uwsgi_ignore_client_abort on;
}
}
server {
listen 8888;
server_name 192.168.56.100;
#access_log logs/access_example.log main;
root /home/worker/opwf_project/opwf/static/dist;
location / {
try_files $uri $uri/ @router;
}
location @router {
rewrite ^.*$ /index.html last;
}
}
1.8.2 uwsgi_conf
![image-20201212161242664](imgs/image-20201212161242664.png)
# vim /root/shiyanlou_project/uwsgi_conf/uwsgi.ini
# vim中,A键进入插入模式,ESC退出,:wq保存并退出
# 或者pycharm中直接粘贴也可以
[uwsgi]
# 使用Nginx连接时使用,Django程序所在服务器地址和端口号
socket=127.0.0.1:8000
# 项目目录绝对路径
chdir=/home/worker/opwf_project/opwf
# 项目中wsgi.py文件的目录,相对于项目目录
wsgi-file=opwf/wsgi.py
# 进程数(机器核数的1倍)
processes=4
# 线程数
threads=20
# uwsgi服务器的角色
master=True
# 存放进程编号的文件
pidfile=uwsgi.pid
# 日志文件
daemonize=uwsgi.log
# 指定依赖的虚拟环境
virtualenv=/root/.virtualenvs/syl