工序返回指导书嵌套字段
This commit is contained in:
parent
4bdf6382d5
commit
cd37d53459
|
@ -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:
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -49,6 +49,11 @@ class PTaskSerializer(serializers.ModelSerializer):
|
|||
return 'crontab'
|
||||
return 'interval'
|
||||
|
||||
class FileSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model =File
|
||||
fields = ['id', 'name', 'file', 'path']
|
||||
|
||||
class FileSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = 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='状态类型'),
|
||||
),
|
||||
]
|
|
@ -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):
|
||||
"""
|
||||
工单流转日志
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
"""
|
||||
到达结束状态
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue