工序返回指导书嵌套字段

This commit is contained in:
caoqianming 2021-08-26 10:01:16 +08:00
parent 4bdf6382d5
commit 50e7182f6d
8 changed files with 175 additions and 47 deletions

View File

@ -1,6 +1,7 @@
from rest_framework.serializers import ModelSerializer
from .models import Material, Process, Step
from apps.system.serializers import FileSimpleSerializer
class MaterialSerializer(ModelSerializer):
@ -9,9 +10,11 @@ class MaterialSerializer(ModelSerializer):
fields = '__all__'
class ProcessSerializer(ModelSerializer):
instruction_ = FileSimpleSerializer(source='instruction', read_only=True)
class Meta:
model = Process
fields = '__all__'
class StepSerializer(ModelSerializer):
class Meta:

View File

@ -29,7 +29,7 @@ class ProcessViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
perms_map = {'get': '*', 'post': 'process_create',
'put': 'process_update', 'delete': 'process_delete'}
queryset = Process.objects.all()
queryset = Process.objects.select_related('instruction').all()
serializer_class = ProcessSerializer
search_fields = ['name', 'number']
filterset_fields = ['number']

View File

@ -49,6 +49,10 @@ class PTaskSerializer(serializers.ModelSerializer):
return 'crontab'
return 'interval'
class FileSimpleSerializer(serializers.ModelSerializer):
class Meta:
fields = ['id', 'name', 'file', 'path']
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.6 on 2021-08-25 07:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0005_auto_20210823_1548'),
]
operations = [
migrations.AddField(
model_name='state',
name='state_fields',
field=models.JSONField(default=dict, help_text='json格式字典存储,包括读写属性1只读2必填3可选. 示例:{"created_at":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称', verbose_name='表单字段'),
),
migrations.AlterField(
model_name='state',
name='type',
field=models.IntegerField(choices=[(0, '普通类型'), (1, 1), (2, 2)], default=0, help_text='0.普通类型 1.初始状态(用于新建工单时,获取对应的字段必填及transition信息) 2.结束状态(此状态下的工单不得再处理即没有对应的transition)', verbose_name='状态类型'),
),
]

View File

@ -26,20 +26,28 @@ class State(CommonAModel):
STATE_TYPE_START = 1
STATE_TYPE_END = 2
type_choices = (
(0, '普通类型'),
(1, STATE_TYPE_START),
(2, STATE_TYPE_END)
(0, '普通'),
(STATE_TYPE_START, '开始'),
(STATE_TYPE_END, '结束')
)
PARTICIPANT_TYPE_PERSONAL = 1
PARTICIPANT_TYPE_MULTI = 2
PARTICIPANT_TYPE_DEPT = 3
PARTICIPANT_TYPE_ROLE = 4
PARTICIPANT_TYPE_VARIABLE = 5
PARTICIPANT_TYPE_ROBOT = 6
PARTICIPANT_TYPE_FIELD = 7
PARTICIPANT_TYPE_PARENT_FIELD = 8
type2_choices = (
(0, '无处理人'),
(1, '个人'),
(2, '多人'),
(3, '部门'),
(4, '角色'),
(5, '变量'),
(6, '脚本'),
(7, '工单的字段'),
(8, '父工单的字段')
(PARTICIPANT_TYPE_PERSONAL, '个人'),
(PARTICIPANT_TYPE_MULTI, '多人'),
(PARTICIPANT_TYPE_DEPT, '部门'),
(PARTICIPANT_TYPE_ROLE, '角色'),
(PARTICIPANT_TYPE_VARIABLE, '变量'),
(PARTICIPANT_TYPE_ROBOT, '脚本'),
(PARTICIPANT_TYPE_FIELD, '工单的字段'),
(PARTICIPANT_TYPE_PARENT_FIELD, '父工单的字段')
)
name = models.CharField('名称', max_length=50)
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
@ -48,7 +56,7 @@ class State(CommonAModel):
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('参与者类型', choices=type2_choices, default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5参与人填creator')
state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1只读2必填3可选. 示例:{"created_at":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称)state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1只读2必填3可选4不显示, 字典的字典
class Transition(CommonAModel):
"""
@ -116,6 +124,9 @@ class Ticket(CommonAModel):
in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下')
add_node_man = models.CharField('加签人', max_length=50, default='', blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效')
participant_type = models.IntegerField('当前处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色', choices=State.type2_choices)
participant = models.CharField('当前处理人', max_length=1000, default='', blank=True, help_text='可以为空(无处理人的情况,如结束状态)、username\多个username(以,隔开)\部门id\角色id\脚本文件名等')
class TicketFlow(BaseModel):
"""
工单流转日志

View File

@ -1,29 +1,30 @@
from rest_framework.serializers import ModelSerializer, SerializerMethodField, CharField
import rest_framework
from rest_framework import serializers
from .models import State, Ticket, Workflow, Transition, CustomField
class WorkflowSerializer(ModelSerializer):
class WorkflowSerializer(serializers.ModelSerializer):
class Meta:
model = Workflow
fields = '__all__'
class StateSerializer(ModelSerializer):
class StateSerializer(serializers.ModelSerializer):
class Meta:
model = State
fields = '__all__'
class WorkflowSimpleSerializer(ModelSerializer):
class WorkflowSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Workflow
fields = ['id', 'name']
class StateSimpleSerializer(ModelSerializer):
class StateSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = State
fields = ['id', 'name']
class TransitionSerializer(ModelSerializer):
class TransitionSerializer(serializers.ModelSerializer):
source_state_ = StateSimpleSerializer(source='source_state', read_only=True)
destination_state_ = StateSimpleSerializer(source='destination_state', read_only=True)
class Meta:
@ -36,18 +37,19 @@ class TransitionSerializer(ModelSerializer):
return queryset
class CustomFieldSerializer(ModelSerializer):
class CustomFieldSerializer(serializers.ModelSerializer):
class Meta:
model = CustomField
fields = '__all__'
class TicketCreateSerializer(ModelSerializer):
class TicketCreateSerializer(serializers.ModelSerializer):
transition = serializers.IntegerField(label='流转ID')
class Meta:
model=Ticket
fields=['title','workflow','ticket_data']
class TicketSerializer(ModelSerializer):
fields=['title','workflow','ticket_data', 'transition']
class TicketSerializer(serializers.ModelSerializer):
workflow_ = WorkflowSimpleSerializer(source='workflow', read_only=True)
state_ = StateSimpleSerializer(source='state', read_only=True)

View File

@ -1,31 +1,38 @@
from apps.wf.models import State, Ticket, Transition, Workflow
from apps.wf.models import CustomField, State, Ticket, Transition, Workflow
from rest_framework.exceptions import APIException
class WfService(object):
@staticmethod
def get_wf_states(wf:Workflow):
def get_worlflow_states(workflow:Workflow):
"""
获取工作流状态列表
"""
return State.objects.filter(workflow=wf, is_deleted=False).order_by('sort')
return State.objects.filter(workflow=workflow, is_deleted=False).order_by('sort')
@staticmethod
def get_wf_transitions(wf:Workflow):
def get_workflow_transitions(workflow:Workflow):
"""
获取工作流流转列表
"""
return Transition.objects.filter(workflow=wf, is_deleted=False)
return Transition.objects.filter(workflow=workflow, is_deleted=False)
@staticmethod
def get_wf_start_state(wf:Workflow):
def get_workflow_start_state(workflow:Workflow):
"""
获取工作流初始状态
"""
try:
wf_state_obj = State.objects.get(workflow=wf, type=State.STATE_TYPE_START, is_deleted=False)
wf_state_obj = State.objects.get(workflow=workflow, type=State.STATE_TYPE_START, is_deleted=False)
return wf_state_obj
except:
raise Exception('工作流初始状态配置错误')
raise Exception('工作流状态配置错误')
@staticmethod
def get_workflow_custom_fields(workflow:Workflow):
"""
获取工单字段
"""
return CustomField.objects.filter(is_deleted=False, workflow=workflow).order_by('sort')
@classmethod
def get_ticket_transitions(cls, ticket:Ticket):
"""
@ -41,14 +48,57 @@ class WfService(object):
return Transition.objects.filter(is_deleted=False, source_state=state).all()
@classmethod
def get_ticket_next_state(cls, ticket:Ticket)->object:
transitions = Transition.objects.filter(source_state=ticket.state, is_deleted=False)
count = transitions.count()
if count == 0:
raise Exception('未配置流转条件')
elif count == 1:
return transitions.first()
else:
for i in transitions:
pass
def get_ticket_steps(cls, ticket:Ticket):
steps = cls.get_worlflow_states(ticket.workflow)
for i in steps:
if ticket.state.is_hidden and ticket.state != i:
steps.remove(i)
return steps
@classmethod
def get_ticket_transitions(cls, ticket:Ticket):
"""
获取工单可执行的操作
"""
return cls.get_state_transitions(ticket.state)
@classmethod
def get_transition_by_args(cls, kwargs:dict):
"""
查询并获取流转
"""
kwargs['is_deleted'] = False
return Transition.objects.filter(**kwargs).all()
@classmethod
def get_next_state_id_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, workflow:Workflow = None)->object:
"""
获取下个节点状态
"""
if ticket: # 如果是新建工单
source_state = ticket.state
else:
source_state = cls.get_workflow_start_state(workflow)
if transition.source_state != source_state:
raise APIException('流转错误')
destination_state = transition.destination_state
if transition.condition_expression:
pass
return destination_state
@classmethod
def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, ticket_data:dict):
"""
获取工单目标状态实际的处理人
"""
if state.type == State.STATE_TYPE_START:
"""
回到初始状态
"""
elif state.type == State.STATE_TYPE_END:
"""
到达结束状态
"""

View File

@ -4,10 +4,11 @@ from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModel
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer
from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action
from rest_framework.decorators import action, api_view
from apps.wf.models import CustomField, Ticket, Workflow, State, Transition
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
from apps.wf.services import WfService
from rest_framework.exceptions import APIException
# Create your views here.
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
@ -26,7 +27,7 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
工作流下的状态节点
"""
wf = self.get_object()
serializer = self.serializer_class(instance=WfService.get_wf_states(wf), many=True)
serializer = self.serializer_class(instance=WfService.get_worlflow_states(wf), many=True)
return Response(serializer.data)
@action(methods=['get'], detail=True, perms_map={'get':'workflow_update'}, pagination_class=None, serializer_class=TransitionSerializer)
@ -35,7 +36,7 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
工作流下的流转规则
"""
wf = self.get_object()
serializer = self.serializer_class(instance=WfService.get_wf_transitions(wf), many=True)
serializer = self.serializer_class(instance=WfService.get_workflow_transitions(wf), many=True)
return Response(serializer.data)
@action(methods=['get'], detail=True, perms_map={'get':'workflow_update'}, pagination_class=None, serializer_class=CustomFieldSerializer)
@ -54,9 +55,12 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
ret={}
wf = self.get_object()
start_state = WfService.get_wf_start_state(wf)
start_state = WfService.get_workflow_start_state(wf)
transitions = WfService.get_state_transitions(start_state)
ret['workflow'] = pk
ret['transitions'] = TransitionSerializer(instance=transitions, many=True).data
field_list = CustomFieldSerializer(instance=WfService.get_workflow_custom_fields(wf), many=True).data
ret['field_list'] = field_list
return Response(ret)
class StateViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
@ -97,4 +101,35 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
return super().get_serializer_class()
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
"""
新建工单
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
start_state = WfService.get_workflow_start_state(serializer.data['workflow'])
transition = Transition.objects.get(pk=serializer.data['transition'])
ticket_data = serializer.data['ticket_data']
if transition.field_require_check:
for key, value in start_state.state_fields.items(): #校验必填项
if value == 2:
if key not in ticket_data or not ticket_data[key]:
raise APIException('字段{}必填'.format(key))
next_state = WfService.get_next_state_id_by_transition_and_ticket_info(ticket=None, transition=transition, workflow=serializer.data['workflow'])
@action(methods=['get'], detail=True, perms_map={'get':'*'})
def flowsteps(self, request, pk=None):
"""
工单流转step, 用于显示当前状态的step图(线性结构)
"""
ticket = self.get_object()
steps = WfService.get_ticket_steps(ticket)
return Response(StateSerializer(instance=steps, many=True).data)
@action(methods=['get'], detail=True, perms_map={'get':'*'})
def transitions(self, request, pk=None):
"""
获取工单可执行的操作
"""
ticket = self.get_object()
transitions = WfService.get_ticket_transitions(ticket)
return Response(TransitionSerializer(instance=transitions, many=True).data)