Activiti
Instructor: Bobo
Un flujo de trabajo (Workflow) es la automatización y gestión de procesos de negocio mediante una computadora. Su propósito principal es definir un conjunto de reglas predefinidas que permitan el traspaso automático de documentos, información o tareas entre múltiples participantes, con el objetivo de lograr una meta de negocio específica.
Introducción al contenido del curso
-
Conceptos básicos de Activiti
-
Conceptos avanzados de Activiti
-
Integración de Activiti
-
Conceptos básicos de Activiti
1.1 Introducción a los flujos de trabajo
Un flujo de trabajo (Workflow) es la automatización y gestión de procesos de negocio mediante una computadora. Su propósito principal es definir un conjunto de reglas predefinidas que permitan el traspaso automático de documentos, información o tareas entre múltiples participantes, con el objetivo de lograr una meta de negocio específica.
1.2 Sistemas de flujo de trabajo
Un software con funcionalidad de flujo de trabajo se denomina sistema de flujo de trabajo. Esta función permite la gestión automatizada de los procesos de negocio del sistema. Por lo tanto, el flujo de trabajo se basa en los procesos de negocio. El núcleo fundamental de un software es su proceso de negocio; el flujo de trabajo simplemente asiste en la gestión de dichos procesos. Incluso sin un sistema de flujo de trabajo, el software puede desarrollarse y ejecutarse, pero contar con uno mejora la gestión de los procesos y la escalabilidad del sistema.
1.3 Industrias aplicables
Bienes de consumo, manufactura, servicios de telecomunicaciones, servicios financieros (banco, seguros, valores), servicios logísticos, servicios inmobiliarios, grandes empresas comerciales de importación/exportación, instituciones gubernamentales, institutos de investigación y servicios educativos, especialmente en grandes corporaciones multinacionales y grupos empresariales.
1.4 Aplicaciones específicas
- Procesos de negocio clave: pedidos, gestión de cotizaciones, revisión de contratos, gestión de llamadas de clientes, gestión de la cadena de suministro, etc.
- Gestión administrativa: solicitudes de viaje, horas extras, permisos, uso de vehículos, compras, informes diarios y semanales, y cualquier formulario administrativo que antes se manejaba manualmente.
- Gestión de personal: capacitación de empleados, evaluaciones de desempeño, cambios de puesto, gestión de archivos de información de empleados, etc.
- Asuntos financieros: solicitudes de pago, gestión de cuentas por cobrar, gastos diarios, reembolsos de viaje, presupuestos y planes.
- Servicio al cliente: gestión de información del cliente, manejo de quejas y solicitudes, gestión de servicio postventa, etc.
- Servicios especiales: procesos relacionados con ISO, gestión de calidad, gestión de información de datos de productos, despacho aduanero para empresas comerciales, seguimiento de mercancías para empresas logísticas, y otras tareas que se completan mediante formularios manuales escalonados pueden implementarse de manera automatizada y estandarizada con software de flujo de trabajo.
1.5 Método de implementación
Antes de contar con un motor de flujo de trabajo dedicado, la implementación típica del control de procesos consistía en utilizar el valor de un campo de estado para rastrear los cambios en el flujo. De esta manera, usuarios con diferentes roles determinaban si un registro se mostraba según el valor del campo de estado.
Para los registros con permiso de visualización, el usuario actual, según su rol, decidía si la aprobación era válida. Si lo era, se establecía un valor en el campo de estado para indicar aprobación; si no, se establecía otro valor.
Este es el método más primitivo. Aunque controlaba el flujo mediante un campo de estado, cuando el proceso cambiaba, el código escrito de esta manera también debía ajustarse.
¿Existe una forma profesional de implementar la gestión de flujos de trabajo? Y que, además, permita que el programa no necesite cambios cuando los procesos de negocio se modifiquen. Lograr este efecto aumentaría enormemente la capacidad de adaptación de nuestro sistema de negocio.
2. Introducción a Activiti
El 17 de mayo de 2010, Alfresco Software anunció el inicio del proyecto de código abierto Activiti para la gestión de procesos de negocio (BPM). Su arquitecto principal fue Tom Baeyens, un experto en BPM y el anterior arquitecto de jBPM, un motor de flujo de trabajo muy conocido. Activiti también es un motor de flujo de trabajo.
Activiti es un motor de flujo de trabajo que puede extraer procesos de negocio complejos de un sistema, definirlos utilizando el lenguaje de modelado BPMN2.0 y ejecutar el proceso según una definición preestablecida. Esto permite que el flujo del sistema sea gestionado por Activiti, reduciendo el esfuerzo de actualización y mantenimiento del sistema debido a cambios en los procesos, mejorando la robustez del sistema y reduciendo los costos de desarrollo y mantenimiento.
Sitio web oficial: https://www.activiti.org/
2.1.1 BPM
BPM (Business Process Management), o Gestión de Procesos de Negocio, es una forma estandarizada de construir procesos de negocio de extremo a extremo para mejorar continuamente la eficiencia operativa de una organización. Está incluido en programas de educación gerencial comunes como EMBA, MBA, etc.
2.1.2 Software BPM
El software BPM son herramientas de TI que promueven la integración y el ajuste entre personas, y entre personas y sistemas, en respuesta a los cambios en el entorno empresarial.
Al modelar, automatizar, gestionar, monitorear y optimizar todo el ciclo de vida de los procesos de negocio internos y externos, el software BPM reduce los costos y aumenta significativamente los beneficios de la empresa.
El software BPM tiene una amplia gama de aplicaciones en la empresa. Dondequiera que existan procesos de negocio, se pueden gestionar con software BPM, como en la gestión administrativa de personal, gestión de compras, gestión de flujo de aprobación de documentos, gestión financiera, etc.
2.1.3 BPMN
BPMN (Business Process Model And Notation) - Modelo y Notación de Procesos de Negocio - es un conjunto de notaciones estándar para el modelado de procesos de negocio desarrollado por BPMI (Business Process Management Initiative). Utilizando los símbolos proporcionados por BPMN, se pueden crear procesos de negocio.
La especificación BPMN 1.0 se publicó en mayo de 2004. BPMI se fusionó con OMG (The Object Management Group) en septiembre de 2005. La versión final de BPMN 2.0 fue publicada por OMG en enero de 2011.
BPMN es el estándar BPM ampliamente aceptado por los principales proveedores de BPM. Activiti utiliza BPMN 2.0 para el modelado y la gestión de la ejecución de procesos, lo que incluye muchos símbolos de modelado, como:
- Evento (Event): Representado por un círculo, es un suceso que ocurre durante la ejecución de un proceso.
- Actividad (Activity): Representada por un rectángulo redondeado. Un proceso consiste en una o más actividades.
En realidad, el diagrama BPMN representa el proceso de negocio mediante XML. El archivo .bpmn anterior se puede abrir con un editor de texto:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="procesoPermiso" name="Proceso de Permiso" isExecutable="true">
<startEvent id="inicio1" name="Inicio"></startEvent>
<userTask id="tareaSolicitud" name="Crear Solicitud de Permiso"></userTask>
<sequenceFlow id="flujo1" sourceRef="inicio1" targetRef="tareaSolicitud"></sequenceFlow>
<userTask id="tareaGerente" name="Revisión del Gerente"></userTask>
<sequenceFlow id="flujo2" sourceRef="tareaSolicitud" targetRef="tareaGerente"></sequenceFlow>
<userTask id="tareaRRHH" name="Verificación de RRHH"></userTask>
<sequenceFlow id="flujo3" sourceRef="tareaGerente" targetRef="tareaRRHH"></sequenceFlow>
<endEvent id="fin1" name="Fin"></endEvent>
<sequenceFlow id="flujo4" sourceRef="tareaRRHH" targetRef="fin1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="DiagramaBPMN_procesoPermiso">
<bpmndi:BPMNPlane bpmnElement="procesoPermiso" id="PlanoBPMN_procesoPermiso">
<bpmndi:BPMNShape bpmnElement="inicio1" id="FormaBPMN_inicio1">
<omgdc:Bounds height="35.0" width="35.0" x="130.0" y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="tareaSolicitud" id="FormaBPMN_tareaSolicitud">
<omgdc:Bounds height="55.0" width="105.0" x="210.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="tareaGerente" id="FormaBPMN_tareaGerente">
<omgdc:Bounds height="55.0" width="105.0" x="360.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="tareaRRHH" id="FormaBPMN_tareaRRHH">
<omgdc:Bounds height="55.0" width="105.0" x="510.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="fin1" id="FormaBPMN_fin1">
<omgdc:Bounds height="35.0" width="35.0" x="660.0" y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flujo1" id="AristaBPMN_flujo1">
<omgdi:waypoint x="165.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="210.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flujo2" id="AristaBPMN_flujo2">
<omgdi:waypoint x="315.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="360.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flujo3" id="AristaBPMN_flujo3">
<omgdi:waypoint x="465.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="510.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flujo4" id="AristaBPMN_flujo4">
<omgdi:waypoint x="615.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="660.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
2.2 Pasos de uso
Desplegar Activiti
Activiti es un motor de flujo de trabajo (esencialmente un conjunto de APIs en formato JAR). Los sistemas de negocio acceden (u operan) a las interfaces de Activiti para manipular fácilmente los datos relacionados con el flujo, integrando así el entorno del flujo de trabajo con el entorno del sistema de negocio.
Definición del proceso
Utilizar la herramienta de modelado de procesos de Activiti (activity-designer) para definir el proceso de negocio (archivo .bpmn). El archivo .bpmn es el archivo de definición del proceso de negocio, que define el proceso mediante XML.
Despliegue de la definición del proceso
Activiti despliega la definición del proceso de negocio (archivo .bpmn). Utilizando las API proporcionadas por Activiti, se almacena el contenido de la definición del proceso, que se puede consultar durante la ejecución. Activiti ejecuta almacenando el contenido de la definición del proceso en la base de datos.
Iniciar una instancia de proceso
Una instancia de proceso (también llamada ProcessInstance) indica el inicio de la ejecución de un proceso de negocio. Una vez desplegada la definición del proceso de solicitud de permiso, si Zhang San desea solicitar un permiso, inicia una instancia de proceso. Si Li Si también desea solicitar un permiso, inicia otra instancia. La ejecución de los dos procesos no se afecta mutuamente.
Consulta de tareas pendientes por parte del usuario (Task)
Dado que el proceso de negocio del sistema ya está gestionado por Activiti, a través de Activiti se puede consultar en qué punto del proceso se encuentra actualmente y qué tarea necesita completar el usuario actual. Activiti gestiona esto, sin que el desarrollador necesite escribir sentencias SQL de consulta.
El usuario completa la tarea
Después de consultar las tareas pendientes, el usuario puede completar una tarea. Si después de completarla se requiere que otro usuario la procese, por ejemplo, después de crear una orden de compra, el gerente de departamento debe aprobarla, este proceso también lo gestiona Activiti.
Finalización del proceso
Cuando se completa una tarea y no hay un siguiente nodo de tarea, la instancia de proceso finaliza.
3. Aplicación de Activiti
3.1 Uso básico de Activiti
3.1.1 Crear un proyecto Maven
Cree un proyecto Maven ordinario y agregue las dependencias relevantes.
<propiedades>
<version.slf4j>1.6.6</version.slf4j>
<version.log4j>1.2.12</version.log4j>
<version.activiti>7.0.0.Beta1</version.activiti>
</propiedades>
<dependencias>
<dependencia>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${version.activiti}</version>
</dependencia>
<dependencia>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${version.activiti}</version>
</dependencia>
<!-- Procesamiento del modelo bpmn -->
<dependencia>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${version.activiti}</version>
</dependencia>
<!-- Conversión bpmn -->
<dependencia>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${version.activiti}</version>
</dependencia>
<!-- Conversión de datos json bpmn -->
<dependencia>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${version.activiti}</version>
</dependencia>
<!-- Diseño bpmn -->
<dependencia>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${version.activiti}</version>
<exclusiones>
<exclusion>
<groupId>com.github.jgraph</groupId>
<artifactId>jgraphx</artifactId>
</exclusion>
</exclusiones>
</dependencia>
<!-- Soporte en la nube de activiti -->
<dependencia>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>${version.activiti}</version>
</dependencia>
<!-- Controlador mysql -->
<dependencia>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependencia>
<!-- mybatis -->
<dependencia>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependencia>
<!-- Pool de conexiones -->
<dependencia>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.8.0</version>
</dependencia>
<dependencia>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependencia>
<!-- Inicio de log -->
<dependencia>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependencia>
<dependencia>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependencia>
<dependencia>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.14.1</version>
</dependencia>
</dependencias>
3.1.2 log4j
Agregue un archivo de registro log4j2.xml.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Consola" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<File name="Archivo" fileName="logs/activiti.log" append="false">
<PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Consola"/>
<AppenderRef ref="Archivo"/>
</Root>
</Loggers>
</Configuration>
3.1.3 Agregar archivo de configuración de Activiti
En este ejemplo se utiliza MySQL 8.0. El método predeterminado de Activiti requiere crear un archivo activiti.cfg.xml en la carpeta resources, con un nombre predeterminado que no se puede modificar. En el archivo de configuración, hay dos métodos: configurar un origen de datos por separado, o no configurar un origen de datos por separado.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" id="configMotorProceso">
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///mi_base_activiti?characterEncoding=utf-8&nullCatalogMeansCurrent=true&serverTimezone=UTC"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="contrasena"/>
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
3.1.4 Generar estructura de tablas mediante programa Java
Cree una clase de utilidad que invoque la clase de utilidad de Activiti para generar la estructura de tablas necesaria.
public class GeneradorTablasTest {
/**
* Generar la estructura de tablas relacionada con Activiti
*/
@Test
public void generarTablas() {
// Utilizar la configuración de activiti.cfg.xml en el classpath para crear el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
System.out.println("Motor de proceso creado: " + motor);
}
}
Al ejecutarlo, se crea la base de datos. Después de la ejecución, verifique la base de datos. Se han creado 25 tablas. El resultado es el siguiente:
3.2 Introducción a la estructura de tablas
3.2.1 Reglas de nomenclatura y función de las tablas
Observando las tablas creadas, notamos que todas las tablas de Activiti comienzan con ACT_. La segunda parte es un identificador de dos letras que indica el propósito de la tabla.
- ACT_RE: 'RE' significa repositorio. Las tablas con este prefijo contienen definiciones de procesos y recursos estáticos de procesos (imágenes, reglas, etc.).
- ACT_RU: 'RU' significa runtime (tiempo de ejecución). Estas tablas de tiempo de ejecución contienen datos en ejecución como instancias de proceso, tareas, variables, tareas asíncronas, etc. Activiti solo guarda estos datos durante la ejecución de la instancia de proceso y los elimina al finalizar el proceso, manteniendo las tablas pequeñas y rápidas.
- ACT_HI: 'HI' significa historia. Estas tablas contienen datos históricos, como instancias de proceso históricas, variables, tareas, etc.
- ACT_GE: GE significa general. Datos generales utilizados en diferentes escenarios.
3.2.2 Introducción a las tablas de datos de Activiti
| Categoría de tabla | Nombre de tabla | Explicación |
|---|---|---|
| Datos generales | ||
| [ACT_GE_BYTEARRAY] | Definición de proceso general y recursos | |
| [ACT_GE_PROPERTY] | Propiedades relacionadas con el sistema | |
| Registro histórico del proceso | ||
| [ACT_HI_ACTINST] | Instancia de proceso histórica | |
| [ACT_HI_ATTACHMENT] | Archivo adjunto del proceso histórico | |
| [ACT_HI_COMMENT] | Información descriptiva histórica | |
| [ACT_HI_DETAIL] | Detalles del proceso histórico en ejecución | |
| [ACT_HI_IDENTITYLINK] | Relación de usuarios del proceso histórico | |
| [ACT_HI_PROCINST] | Instancia de proceso histórico | |
| [ACT_HI_TASKINST] | Instancia de tarea histórica | |
| [ACT_HI_VARINST] | Variables del proceso histórico en ejecución | |
| Tabla de definición del proceso | ||
| [ACT_RE_DEPLOYMENT] | Información de la unidad de despliegue | |
| [ACT_RE_MODEL] | Información del modelo | |
| [ACT_RE_PROCDEF] | Definición de proceso desplegada | |
| Tablas de instancia en ejecución | ||
| [ACT_RU_EVENT_SUBSCR] | Evento en tiempo de ejecución | |
| [ACT_RU_EXECUTION] | Instancia de ejecución del proceso en tiempo de ejecución | |
| [ACT_RU_IDENTITYLINK] | Relación de usuarios en tiempo de ejecución, almacena información del nodo de tarea y participantes | |
| [ACT_RU_JOB] | Trabajo en tiempo de ejecución | |
| [ACT_RU_TASK] | Tarea en tiempo de ejecución | |
| [ACT_RU_VARIABLE] | Tabla de variables en tiempo de ejecución |
3.3 Método de creación de ProcessEngine
Anteriormente se utilizó el método getDefaultProcessEngine() para cargar el archivo activiti.cfg.xml del classpath. En algunos casos, es posible que no se haya procesado de manera predeterminada. ¿Qué debemos hacer entonces?
/**
* Cargar el archivo de configuración de forma personalizada
*/
@Test
public void crearMotorPersonalizado() {
// Primero, crear el objeto ProcessEngineConfiguration
ProcessEngineConfiguration configuracion =
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("mi-config-activiti.xml");
// A través del objeto ProcessEngineConfiguration, crear el objeto ProcessEngine
ProcessEngine motor = configuracion.buildProcessEngine();
}
3.4 Interfaces de servicio (Service)
Service son las interfaces de servicio proporcionadas por el motor de flujo de trabajo para el despliegue, ejecución y gestión de flujos de trabajo. Utilizamos estas interfaces para operar los datos de la tabla correspondiente.
3.4.1 Método de creación de Service
Crear Service a través de ProcessEngine. De la siguiente manera:
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
ServicioRepositorio servicioRepositorio = motor.getRepositoryService();
ServicioTareas servicioTareas = motor.getTaskService();
3.4.2 Resumen de Service
| Nombre del servicio | Función del servicio |
|---|---|
| RepositoryService | Clase de gestión de recursos de activiti |
| RuntimeService | Clase de gestión de ejecución de procesos de activiti |
| TaskService | Clase de gestión de tareas de activiti |
| HistoryService | Clase de gestión de historial de activiti |
| ManagementService | Clase de gestión del motor de activiti |
Breve introducción:
- RepositoryService: Es la clase de gestión de recursos de activiti, proporciona operaciones para gestionar y controlar paquetes de despliegue y definiciones de procesos. Las definiciones de procesos diseñadas con herramientas de modelado de flujo de trabajo deben desplegarse en la computadora utilizando este servicio. Además de desplegar definiciones de procesos, también puede: consultar paquetes de despliegue y definiciones de procesos en el motor; suspender o activar paquetes de despliegue (todos o específicos de una definición de proceso); obtener una versión POJO de la definición del proceso para análisis en Java sin necesidad de XML.
- RuntimeService: Clase de gestión de ejecución de procesos de Activiti. Desde esta clase de servicio se puede obtener mucha información relacionada con la ejecución del proceso.
- TaskService: Clase de gestión de tareas de Activiti. Desde esta clase se puede obtener información sobre las tareas.
- HistoryService: Clase de gestión del historial de Activiti, permite consultar información histórica. Durante la ejecución, el motor guarda muchos datos (según la configuración), como la hora de inicio de la instancia del proceso, los participantes de la tarea, la hora de finalización de la tarea, la ruta de ejecución de cada instancia del proceso, etc. Este servicio se utiliza principalmente a través de funciones de consulta para obtener estos datos.
- ManagementService: Clase de gestión del motor de Activiti, proporciona funciones de gestión y mantenimiento del motor de procesos de Activiti. Estas funciones no se utilizan en aplicaciones impulsadas por flujos de trabajo, sino principalmente para el mantenimiento diario del sistema Activiti.
3.5 Dibujo del proceso
3.5.1 Plugin de dibujo
Dado que IDEA no ha actualizado ni mantenido la herramienta de diseño de Activiti desde 2019, en versiones de IDEA más recientes no podemos usar el plugin actiBPM para dibujar. En este caso, podemos elegir reducir la versión o usar el Eclipse que les proporcionamos.
El Eclipse que proporcionamos ya tiene integrado el plugin de Activiti.
3.5.2 Dibujar el proceso
Utilice el panel deslizante para dibujar el proceso, arrastrando iconos desde el panel derecho al izquierdo. El resultado final es el siguiente:
Especifique la clave principal del proceso.
Especifique el responsable de la tarea. En la vista Propiedades, especifique el responsable de cada nodo de tarea:
Assignee: El responsable de la aprobación (quién realiza la aprobación)
- Aprobación del gerente: lisi
- Aprobación del gerente general: wangwu
- Aprobación financiera: xiaoming
Una vez configurado, guarde el archivo. Se generará simultáneamente una imagen PNG.
Luego, copie estos dos archivos al proyecto de IDEA.
3.5.3 Introducción a los iconos
Símbolos del proceso
BPMN 2.0 es la abreviatura de la notación de modelado de procesos de negocio 2.0. Fue creado y desarrollado continuamente por la asociación sin fines de lucro Business Process Management Initiative. Como identificador, BPMN 2.0 es un conjunto completo de símbolos estándar para definir diagramas de flujo de diseño de procesos de negocio, que mejora la eficiencia de la comunicación durante el modelado de negocios.
Actualmente, BPMN 2.0 es la versión más reciente, utilizada para la disposición y comunicación visual en el contexto de BPM.
A continuación, primero comprendamos los símbolos comunes en el diseño de procesos.
Eventos (Event): Representados por círculos. Son eventos que ocurren durante la ejecución del proceso.
Actividades (Activity): Representan trabajo o tareas de manera general. Una actividad puede ser una tarea o un subproceso del proceso actual; además, se pueden asignar diferentes tipos a la actividad. Las actividades comunes son:
Pasarela (GateWay): Se utiliza para tomar decisiones. Hay algunas pasarelas comunes que debemos conocer:
- Pasarela exclusiva (x): Solo se seleccionará una ruta. Cuando el proceso llega a esta pasarela, calcula en orden los flujos de salida; cuando el cálculo de la condición es verdadero, continúa ejecutando el flujo de salida actual. Si múltiples líneas son verdaderas, se ejecuta la primera línea cuyo valor sea verdadero. Si ninguna pasarela da verdadero, el motor lanza una excepción. La pasarela exclusiva debe usarse en combinación con flujos condicionales; el atributo default especifica el flujo predeterminado, que se ejecuta cuando todas las condiciones no se cumplen.
- Pasarela paralela (+): Todas las rutas se seleccionan simultáneamente. División: Ejecuta todos los flujos de salida en paralelo, creando una rama de ejecución paralela para cada flujo. Unión: Todas las líneas que se separan en la pasarela paralela y se completan se reúnen aquí, esperando a que todas las línicas se completen antes de continuar. Nota: Si la misma pasarela paralela tiene múltiples entradas y salidas, puede tener funciones de división y unión simultáneamente. En este caso, la pasarela primero convergerá todos los flujos de entrada y luego los dividirá en múltiples ramas paralelas.
- Pasarela inclusiva (+): Se pueden ejecutar múltiples líneas simultáneamente, también se pueden establecer condiciones en la pasarela. División: Calcula la expresión de cada línea; cuando el resultado del cálculo es verdadero, crea una rama paralela y continúa la ejecución. Unión: Todas las líneas que se separan en la pasarela paralela y se completan se reúnen aquí, esperando a que todas las líneas se completen antes de continuar.
- Pasarela de eventos (+): Especialmente para eventos intermedios de captura, permite establecer múltiples flujos de salida que apuntan a diferentes eventos intermedios de captura. Cuando el proceso llega a la pasarela de eventos, el proceso entra en estado de espera, necesitando esperar a que se lance un evento para convertir el estado de espera en estado activo.
Flujo (Flow): Es la línea que conecta dos nodos de proceso. Los flujos comunes incluyen:
Uso del diseñador de procesos
Palette (lienzo): Connection (conexión), Event (evento), Task (tarea), Gateway (pasarela), Container (contenedor), Boundary event (evento de límite), Intermediate event (evento intermedio).
4. Operaciones del proceso Activiti
4.1 Despliegue del proceso
Desplegar el proceso definido en el diseñador en la base de datos de Activiti se denomina despliegue del proceso.
Llamando a la API de Activiti, se puede desplegar uno por uno los archivos bpmn y png de la definición del proceso en Activiti, o se pueden comprimir en un paquete zip para el despliegue.
4.1.1 Despliegue de archivo único
Despliegue por separado del archivo bpmn y la imagen png.
/**
* Implementar el despliegue de un solo archivo
*/
@Test
public void desplegarArchivoIndividual() {
// 1. Obtener el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener RepositoryService para operaciones de despliegue
ServicioRepositorio servicio = motor.getRepositoryService();
// 3. Utilizar RepositoryService para realizar la operación de despliegue
Despliegue despliegue = servicio.createDeployment()
.addClasspathResource("bpmn/viaje.bpmn") // Agregar recurso bpmn
.addClasspathResource("bpmn/viaje.png") // Agregar recurso png
.name("Proceso de solicitud de viaje")
.deploy(); // Desplegar proceso
// 4. Salida de la información del despliegue del proceso
System.out.println("ID del despliegue del proceso:" + despliegue.getId());
System.out.println("Nombre del despliegue del proceso:" + despliegue.getName());
}
4.1.2 Despliegue de archivo zip
Empaquetar los archivos bpmn y png en un solo archivo zip para la carga unificada.
/**
* Operación de despliegue mediante un archivo zip
*/
@Test
public void desplegarArchivoZip() {
// Definir el flujo de entrada del archivo zip
InputStream flujoEntrada = this.getClass().getClassLoader().getResourceAsStream("bpmn/viaje.zip");
// Decorar inputStream
ZipInputStream flujoZipEntrada = new ZipInputStream(flujoEntrada);
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioRepositorio servicioRepositorio = motor.getRepositoryService();
Despliegue despliegue = servicioRepositorio.createDeployment()
.addZipInputStream(flujoZipEntrada)
.name("Proceso de solicitud de viaje").deploy();
// 4. Salida de la información del despliegue del proceso
System.out.println("ID del despliegue del proceso:" + despliegue.getId());
System.out.println("Nombre del despliegue del proceso:" + despliegue.getName());
}
Los datos en la base de datos después de la carga son esencialmente los mismos que para la carga de un solo archivo.
4.1.3 Operación en tablas de datos
Después del despliegue de la definición del proceso, operamos tres tablas en Activiti.
act_re_deployment: Tabla de despliegue de la definición del proceso. Cada despliegue agrega un registro.
act_re_procdef: Tabla de definición del proceso. Cada nueva definición de proceso desplegada agrega un registro en esta tabla.
act_ge_bytearray: Tabla de recursos del proceso. Los archivos bpmn y png desplegados se guardan en esta tabla.
4.2 Iniciar la instancia del proceso
Una vez desplegada la definición del proceso en Activiti, se puede gestionar el proceso de negocio a través del flujo de trabajo. Esto significa que el proceso de solicitud de viaje desplegado anteriormente puede usarse.
Iniciar una instancia de proceso para este proceso representa iniciar una nueva solicitud de viaje. Esto equivale a la relación entre una clase de Java y un objeto de Java. Una vez definida la clase, se necesita new para crear un objeto; por supuesto, se pueden crear múltiples objetos. Para el proceso de solicitud de viaje, Zhang San puede iniciar una solicitud, lo que requiere iniciar una instancia de proceso.
/**
* Iniciar una instancia de proceso
*/
@Test
public void iniciarInstanciaProceso() {
// 1. Crear el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener el objeto RuntimeService
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// 3. Iniciar el proceso según el id de la definición del proceso
String clave = "viaje";
InstanciaProceso instanciaProceso = servicioRuntime.startProcessInstanceByKey(clave);
// 4. Salida de información relacionada con la instancia del proceso
System.out.println("ID de la definición del proceso:" + instanciaProceso.getProcessDefinitionId());
System.out.println("ID de la instancia del proceso:" + instanciaProceso.getId());
System.out.println("ID de la actividad actual:" + instanciaProceso.getActivityId());
}
Tablas de la estructura involucradas en el inicio de la instancia del proceso:
- act_hi_actinst: Historial de ejecución de la instancia del proceso.
- act_hi_identitylink: Información histórica de los usuarios participantes del proceso.
- act_hi_procinst: Información histórica de la instancia del proceso.
- act_hi_taskinst: Información histórica de las tareas del proceso.
- act_ru_execution: Información de ejecución del proceso.
- act_ru_identitylink: Información de los usuarios participantes del proceso.
- act_ru_task: Información de la tarea.
4.3 Búsqueda de tareas
Después de iniciar el proceso, el responsable de la tarea puede consultar las tareas que puede manejar actualmente. Las tareas consultadas son las tareas pendientes del usuario actual.
4.4 Procesamiento de tareas del flujo
El responsable de la tarea consulta las tareas pendientes, elige una tarea y la completa.
/**
* Procesamiento de tareas del flujo
*/
@Test
public void completarTarea() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
Tarea tarea = servicioTareas.createTaskQuery()
// Buscar por clave la tarea del proceso
.processDefinitionKey("inicio")
// Responsable Zhang San
.taskAssignee("zhangsan").singleResult();
// Completar la tarea y presentar la aprobación
servicioTareas.complete(tarea.getId());
}
Una vez que zhangsan procesa esta operación, el flujo se transfiere a lisi. Luego, diferentes usuarios inician sesión, consultan las tareas y las procesan, hasta que el flujo de tareas se completa.
4.5 Consulta de la definición del proceso
Consultar información relacionada con el proceso, incluyendo la definición del proceso, el despliegue del proceso, la versión de la definición del proceso.
/**
* Consultar la definición del proceso
*/
@Test
public void consultarDefinicionProceso() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioRepositorio servicioRepositorio = motor.getRepositoryService();
// Obtener un objeto ProcessDefinitionQuery para operaciones de consulta
ConsultaDefinicionProceso consultaDefinicion = servicioRepositorio.createProcessDefinitionQuery();
List<DefinicionProceso> lista = consultaDefinicion.processDefinitionKey("inicio")
.orderByProcessDefinitionVersion() // Ordenar por versión
.desc() // Orden descendente
.list();
// Salida de información de la definición del proceso
for (DefinicionProceso definicion : lista) {
System.out.println("ID de la definición del proceso:" + definicion.getId());
System.out.println("Nombre de la definición del proceso:" + definicion.getName());
System.out.println("Clave de la definición del proceso:" + definicion.getKey());
System.out.println("Versión de la definición del proceso:" + definicion.getVersion());
System.out.println("ID del despliegue del proceso:" + definicion.getDeploymentId());
System.out.println("---------------------------------------------------------");
}
}
4.6 Eliminación del proceso
/**
* Eliminar proceso
*/
@Test
public void eliminarProceso() {
// Tablas consultadas: ACT_RE_DEPLOYMENT
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioRepositorio servicioRepositorio = motor.getRepositoryService();
// Eliminar la definición del proceso. Si ya se ha iniciado una instancia de proceso para esa definición, se produce un error al eliminar.
// servicioRepositorio.deleteDeployment("10001");
// Establecer TRUE para eliminación en cascada de la definición del proceso. Aunque se haya iniciado una instancia de proceso, se puede eliminar. Establecer FALSE para una operación de eliminación sin cascada.
servicioRepositorio.deleteDeployment("1", true);
}
Nota: En el desarrollo de proyectos, el permiso de eliminación en cascada generalmente se otorga solo al superadministrador.
4.7 Descarga de recursos del proceso
Ahora, los archivos de recursos de nuestro proceso ya están cargados en la base de datos. Si otros usuarios desean ver estos recursos, pueden descargarlos de la base de datos al local.
Solución:
- Usar JDBC para leer los datos de tipo blob o clob.
- Usar la API de activiti para implementar la operación.
Para usar la API de activiti, necesitamos agregar la dependencia de commons-io.
<dependencia>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependencia>
Código de implementación:
/**
* Leer archivos de recursos de la base de datos
*/
@Test
public void leerRecursosDeBD() throws Exception {
// 1. Obtener el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener el objeto RepositoryService
ServicioRepositorio servicioRepositorio = motor.getRepositoryService();
// 3. Obtener el consultor
DefinicionProceso definicion = servicioRepositorio.createProcessDefinitionQuery()
.processDefinitionKey("viaje")
.singleResult();
// 4. Obtener el id del despliegue del proceso
String despliegueId = definicion.getDeploymentId();
// 5. A través de los métodos relevantes del objeto repositoryService, obtener la información de la imagen y la información bpmn
// Imagen png
InputStream pngEntrada = servicioRepositorio
.getResourceAsStream(despliegueId, definicion.getDiagramResourceName());
// Flujo del archivo bpmn
InputStream bpmnEntrada = servicioRepositorio
.getResourceAsStream(despliegueId, definicion.getResourceName());
// 6. Guardar archivos
File archivoPng = new File("d:/viaje.png");
File archivoBpmn = new File("d:/viaje.bpmn");
OutputStream pngSalida = new FileOutputStream(archivoPng);
OutputStream bpmnSalida = new FileOutputStream(archivoBpmn);
IOUtils.copy(pngEntrada, pngSalida);
IOUtils.copy(bpmnEntrada, bpmnSalida);
pngEntrada.close();
pngSalida.close();
bpmnEntrada.close();
bpmnSalida.close();
}
4.8 Consulta de información histórica del proceso
Incluso si la definición del proceso ha sido eliminada, la información de la instancia de ejecución del proceso, según el análisis anterior, aún se almacena en las tablas relacionadas con act_hi_* de Activiti. Por lo tanto, aún podemos consultar la información histórica de la ejecución del proceso a través de HistoryService.
/**
* Consulta de información histórica del proceso
*/
@Test
public void consultarHistorico() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// Para ver la información histórica, necesitamos usar HistoryService
ServicioHistorial servicioHistorial = motor.getHistoryService();
// Obtener el objeto de consulta de la tabla actinst
ConsultaInstanciaActividadHistorica consultaInstancia = servicioHistorial.createHistoricActivityInstanceQuery();
consultaInstancia.processDefinitionId("viaje:1:12504");
consultaInstancia.orderByHistoricActivityInstanceStartTime().desc();
List<InstanciaActividadHistorica> lista = consultaInstancia.list();
// Salida de los resultados de la consulta
for (InstanciaActividadHistorica hi : lista) {
System.out.println(hi.getActivityId());
System.out.println(hi.getActivityName());
System.out.println(hi.getActivityType());
System.out.println(hi.getAssignee());
System.out.println(hi.getProcessDefinitionId());
System.out.println(hi.getProcessInstanceId());
System.out.println("-----------------------");
}
}
- Conceptos avanzados de Activiti
1. Instancia del proceso
1.1 ¿Qué es una instancia de proceso?
Una instancia de proceso (ProcessInstance) representa una instancia de ejecución de una definición de proceso. Una instancia de proceso incluye todos los nodos en ejecución; podemos utilizar este objeto para conocer el progreso actual de la instancia del proceso, entre otra información.
1.2 Gestión de negocio
Una vez desplegada la definición del proceso en Activiti, podemos gestionar la ejecución del proceso en el sistema a través de Activiti. Sin embargo, si deseamos asociar nuestra instancia de proceso con datos de negocio, necesitamos usar el BusinessKey (identificador de negocio) reservado en Activiti para la asociación.
Implementación del código:
/**
* Iniciar instancia de proceso, agregar businessKey
*/
@Test
public void iniciarConClaveNegocio() {
// 1. Obtener el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener el objeto RuntimeService
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// 3. Iniciar la instancia del proceso
InstanciaProceso instancia = servicioRuntime
.startProcessInstanceByKey("viaje", "1001");
// 4. Salida de los atributos relacionados de processInstance
System.out.println("businessKey = " + instancia.getBusinessKey());
}
1.3 Suspensión y activación de la instancia del proceso
En escenarios reales, puede ser necesario pausar la ejecución del proceso actual debido a cambios en el proceso, en lugar de eliminarlo. Después de pausar el proceso, no se podrá continuar con la ejecución.
1.3.1 Suspensión de todos los procesos
Operar la definición del proceso para que esté en estado suspendido. Todas las instancias de proceso bajo esa definición se pausarán.
Cuando la definición del proceso está en estado suspendido, no se permitirá iniciar nuevas instancias de proceso, y todas las instancias de proceso bajo esa definición se suspenderán y pausarán la ejecución.
/**
* Suspender y activar todas las instancias de proceso
*/
@Test
public void suspenderTodosLosProcesos() {
// 1. Obtener el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener el objeto RepositoryService
ServicioRepositorio servicioRepositorio = motor.getRepositoryService();
// 3. Consultar el objeto de la definición del proceso
DefinicionProceso definicionProceso = servicioRepositorio.createProcessDefinitionQuery()
.processDefinitionKey("viaje")
.singleResult();
// 4. Obtener el estado actual de la definición del proceso
boolean suspendido = definicionProceso.isSuspended();
String id = definicionProceso.getId();
// 5. Si está suspendido, activar; si está activo, suspender
if (suspendido) {
// Indica que el estado de la definición actual es SUSPENDIDO
servicioRepositorio.activateProcessDefinitionById(
id // ID de la definición del proceso
, true // ¿Activar?
, null // Tiempo de activación
);
System.out.println("Definición del proceso:" + id + ", ya activada");
} else {
// Estado no suspendido, estado activo. Entonces es necesario suspender la definición del proceso
servicioRepositorio.suspendProcessDefinitionById(
id // ID del proceso
, true // ¿Suspender?
, null // Tiempo de suspensión
);
System.out.println("Definición del proceso:" + id + ", ya suspendida");
}
}
Después de suspender la definición del proceso, el estado en el objeto de instancia correspondiente se modifica a 2. Luego, al operar la instancia de proceso correspondiente, se lanzará un mensaje de excepción. Al volver a activar el proceso suspendido, el valor de estado correspondiente se actualiza de 2 a 1, y el proceso de negocio puede procesarse normalmente.
1.3.2 Suspensión de una sola instancia
Operar el objeto de instancia de proceso para suspender la ejecución de una instancia de proceso individual. Si una instancia de proceso se suspende, esa instancia no continuará su ejecución. Las otras instancias de proceso de la misma definición de proceso no se ven afectadas.
/**
* Suspender y activar una sola instancia de proceso
*/
@Test
public void suspenderInstanciaIndividual() {
// 1. Obtener el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener RuntimeService
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// 3. Obtener el objeto de instancia de proceso
InstanciaProceso instanciaProceso = servicioRuntime.createProcessInstanceQuery()
.processInstanceId("25001")
.singleResult();
// 4. Obtener las operaciones de estado relevantes
boolean suspendido = instanciaProceso.isSuspended();
String id = instanciaProceso.getId();
if (suspendido) {
// Suspender --> Activar
servicioRuntime.activateProcessInstanceById(id);
System.out.println("Definición del proceso:" + id + ", ya activada");
} else {
// Activar --> Suspender
servicioRuntime.suspendProcessInstanceById(id);
System.out.println("Definición del proceso:" + id + ", ya suspendida");
}
}
Luego podemos ver la actualización del estado en la base de datos.
2. Tareas personales
2.1 Asignación de responsable de tarea
2.1.1 Asignación fija
Especificar un responsable fijo de la tarea al modelar el proceso de negocio.
2.1.2 Asignación mediante expresiones
Activiti admite el uso de expresiones UEL. UEL (Unified Expression Language) es parte de la especificación Java EE6. Activiti admite dos tipos de expresiones UEL: UEL-value y UEL-method.
UEL-value: Usar variables de proceso en assignee. Por ejemplo: ${assignee}. Activiti obtiene el valor de la expresión UEL, es decir, el valor de la variable de proceso assignee, y lo utiliza como responsable de la tarea para la asignación.
UEL-method: Por ejemplo: ${userBean.getUserId()}. userBean es un bean en el contenedor Spring, lo que indica la llamada al método getUserId() de dicho bean.
Combinación de UEL-method y UEL-value: Por ejemplo: ${ldapService.findManagerForEmployee(emp)}. ldapService es un bean del contenedor Spring, findManagerForEmployee es un método de dicho bean, emp es una variable de proceso de activiti que se pasa como parámetro al método ldapService.findManagerForEmployee.
Otros: Las expresiones admiten el análisis de tipos básicos, beans, listas, arrays y mapas, y también se pueden usar como condiciones. Ejemplo: ${order.price > 100 && order.price < 250}
2.1.3 Asignación mediante listener
Se pueden usar listeners para completar muchas operaciones de flujo de trabajo de Activiti. Aquí usamos un listener para especificar el responsable de la tarea, de modo que no necesitamos especificar assignee al diseñar el flujo.
Opciones de evento: create (después de la creación de la tarea), assignment (después de la asignación de la tarea), Delete (después de completar la tarea), All (todos los eventos).
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
public class MiListenerTarea implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
if ("Crear solicitud de permiso".equals(delegateTask.getName())
&& "create".equals(delegateTask.getEventName())) {
// Especificar el responsable de la tarea
delegateTask.setAssignee("Zhang San-Listener");
}
}
}
2.2 Consulta de tareas
Consultar tareas pendientes personales del responsable
// Consultar las tareas pendientes personales actuales para ejecución
@Test
public void buscarTareasPersonales() {
// Clave de la definición del proceso
String claveProceso = "miViaje1";
// Responsable de la tarea
String responsable = "zhangsan";
// Obtener TaskService
ServicioTareas servicioTareas = motor.getTaskService();
List<Tarea> listaTareas = servicioTareas.createTaskQuery()
.processDefinitionKey(claveProceso)
.includeProcessVariables()
.taskAssignee(responsable)
.list();
for (Tarea tarea : listaTareas) {
System.out.println("----------------------------");
System.out.println("ID de la instancia del proceso: " + tarea.getProcessInstanceId());
System.out.println("ID de la tarea: " + tarea.getId());
System.out.println("Responsable de la tarea: " + tarea.getAssignee());
System.out.println("Nombre de la tarea: " + tarea.getName());
}
}
Asociar businessKey
Requisito: En la aplicación real de activiti, al consultar tareas pendientes, puede ser necesario mostrar información relacionada del sistema de negocio.
Implementación: Al consultar tareas pendientes, asociar la consulta a la tabla de solicitud de viaje del sistema de negocio a través de businessKey (identificador de negocio) para consultar información como el número de días de viaje.
@Test
public void buscarConClaveNegocio() {
// Obtener processEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// Obtener TaskService
ServicioTareas servicioTareas = motor.getTaskService();
// Obtener RuntimeService
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// Consultar el objeto de la definición del proceso
Tarea tarea = servicioTareas.createTaskQuery()
.processDefinitionKey("miViaje1")
.taskAssignee("zhangsan")
.singleResult();
// Usar el objeto tarea para obtener el id de la instancia
String instanciaProcesoId = tarea.getProcessInstanceId();
// Usar el id de la instancia para obtener el objeto de instancia de proceso
InstanciaProceso instanciaProceso = servicioRuntime.createProcessInstanceQuery()
.processInstanceId(instanciaProcesoId)
.singleResult();
// Usar processInstance para obtener businessKey
String claveNegocio = instanciaProceso.getBusinessKey();
System.out.println("businessKey==" + claveNegocio);
}
2.3 Procesamiento de tareas
Nota: En aplicaciones reales, antes de completar una tarea, es necesario validar si el responsable de la tarea tiene los permisos necesarios para procesar dicha tarea.
/**
* Completar tarea, verificar si el usuario actual tiene permisos
*/
@Test
public void completarTareaConValidacion() {
// ID de la tarea
String tareaId = "15005";
// Responsable de la tarea
String responsable = "zhangsan";
// Obtener processEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// Crear TaskService
ServicioTareas servicioTareas = motor.getTaskService();
// Antes de completar la tarea, verificar que el responsable puede completar la tarea actual
// Método de verificación:
// Consultar la tarea actual por el id de la tarea y el responsable de la tarea. Si se encuentra que el usuario tiene permisos, completarla.
Tarea tarea = servicioTareas.createTaskQuery()
.taskId(tareaId)
.taskAssignee(responsable)
.singleResult();
if (tarea != null) {
servicioTareas.complete(tareaId);
System.out.println("Tarea completada");
}
}
3. Variables del proceso
3.1 ¿Qué son las variables del proceso?
Las variables del proceso son un papel muy importante en activiti. A veces, la operación del flujo depende de las variables del proceso, y la combinación del sistema de negocio con activiti no puede prescindir de las variables del proceso. Las variables del proceso son las variables establecidas por activiti según las necesidades de gestión al administrar el flujo de trabajo.
Nota: Aunque los datos de negocio se pueden almacenar en las variables del proceso y se pueden consultar a través de la API de activiti, no se recomienda su uso de esta manera, ya que la consulta de datos de negocio es responsabilidad del sistema de negocio. Las variables de proceso en activiti se crean para las necesidades de ejecución del proceso.
3.2 Tipos de variables del proceso
Si se almacena un POJO en una variable de proceso, debe implementar la interfaz serializable. Para evitar problemas de deserialización debido a campos nuevos, es necesario generar serialVersionUID.
3.3 Ámbito de las variables del proceso
El ámbito de una variable de proceso puede ser una instancia de proceso (processInstance), una tarea (task) o una instancia de ejecución (execution).
3.3.1 Variables globales
El ámbito predeterminado de una variable de proceso es la instancia de proceso. Cuando el ámbito de una variable de proceso es la instancia de proceso, se puede denominar variable global.
Nota: En las variables globales, no se permiten nombres de variables duplicados. Si se establece una variable con el mismo nombre, el valor posterior sobrescribirá el anterior.
3.3.2 Variables locales
Las tareas y las instancias de ejecución se aplican solo al ámbito de una tarea o instancia de ejecución. Como el ámbito es menor que el de la instancia de proceso, se denominan variables locales. Las variables locales no se afectan entre sí en diferentes tareas o instancias de ejecución, y los nombres de variables pueden ser iguales sin problemas. Los nombres de variables locales también pueden ser iguales a los de variables globales sin problemas.
3.4 Método de uso de variables del proceso
3.4.1 Usar expresiones UEL en propiedades
Se puede usar la expresión UEL en assignee, cuyo valor es el responsable de la tarea. Por ejemplo: ${assignee}. assignee es el nombre de una variable de proceso.
3.4.2 Usar expresiones UEL en enlaces
Se puede usar la expresión UEL en los enlaces para decidir la dirección del flujo. Por ejemplo: ${price < 10000}. price es el nombre de una variable de proceso. El resultado de la expresión UEL es de tipo booleano.
3.5 Uso de variables del proceso
3.5.1 Requisitos
El empleado crea una solicitud de viaje, el gerente de departamento revisa. Si la solicitud es aprobada por el gerente de departamento y es por 3 días o menos, el departamento financiero la aprueba directamente. Si es por más de 3 días, primero debe ser aprobada por el gerente general. Una vez aprobada por el gerente general, el departamento financiero la aprueba.
3.5.2 Definición del proceso
Primero, establecer el responsable mediante UEL-value. Luego, establecer las condiciones en los enlaces de bifurcación. También se puede usar la denominación de parámetros de objeto, como viaje.num.
Luego se pueden copiar los archivos de recursos relevantes al proyecto.
3.5.3 Usar variables globales
A continuación, usemos variables globales para controlar el flujo.
a. Creación de POJO
Primero, cree el objeto POJO.
/**
* Objeto POJO de solicitud de viaje
*/
@Data
public class Viaje {
private long id;
private String nombreViaje;
/**
* Número de días de viaje
*/
private double num;
private Date fechaInicio;
private Date fechaFin;
private String destino;
private String motivo;
}
b. Despliegue del proceso
/**
* Desplegar proceso
*/
@Test
public void desplegarProcesoVariables() {
// 1. Obtener el objeto ProcessEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// 2. Obtener RepositoryService para operaciones de despliegue
ServicioRepositorio servicio = motor.getRepositoryService();
// 3. Utilizar RepositoryService para realizar la operación de despliegue
Despliegue despliegue = servicio.createDeployment()
.addClasspathResource("bpmn/viaje-variable.bpmn") // Agregar recurso bpmn
.addClasspathResource("bpmn/viaje-variable.png") // Agregar recurso png
.name("Proceso de solicitud de viaje - Variables")
.deploy(); // Desplegar proceso
// 4. Salida de la información del despliegue del proceso
System.out.println("ID del despliegue del proceso:" + despliegue.getId());
System.out.println("Nombre del despliegue del proceso:" + despliegue.getName());
}
c. Establecer variables del proceso
c.1 Establecer variables del proceso al iniciar: Al iniciar el proceso, establecer las variables del proceso. El ámbito de la variable es toda la instancia del proceso.
/**
* Iniciar instancia del proceso, establecer variables del proceso
*/
@Test
public void iniciarConVariables() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// Clave de la definición del proceso
String clave = "viaje-variable";
// Crear colección de variables
Map<String, Object> variables = new HashMap<>();
// Crear objeto POJO de viaje
Viaje viaje = new Viaje();
// Establecer número de días de viaje
viaje.setNum(4d);
// Definir variables de proceso en la colección
variables.put("viaje", viaje);
// Establecer el valor de assignee
variables.put("responsable0", "zhangsan1");
variables.put("responsable1", "lisi1");
variables.put("responsable2", "wangwu1");
variables.put("responsable3", "caiwu1");
InstanciaProceso instanciaProceso = servicioRuntime.startProcessInstanceByKey(clave, variables);
// Información de salida
System.out.println("Nombre de la instancia del proceso obtenido:" + instanciaProceso.getName());
System.out.println("ID de la definición del proceso:" + instanciaProceso.getProcessDefinitionId());
}
Completar la tarea:
/**
* Completar tarea
*/
@Test
public void completarTareaConVariable() {
String clave = "viaje-variable";
String responsable = "lisi1";
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
Tarea tarea = servicioTareas.createTaskQuery()
.processDefinitionKey(clave)
.taskAssignee(responsable)
.singleResult();
if (tarea != null) {
servicioTareas.complete(tarea.getId());
System.out.println("Tarea completada...");
}
}
A través del método startProcessInstanceByKey, el ámbito de la variable de proceso establecida es una instancia de proceso. Las variables de proceso se almacenan en un Map. Si la misma clave existe en la instancia de proceso, la posterior sobrescribirá la anterior.
c.2 Establecer al procesar la tarea: Al completar la tarea, establecer la variable de proceso. Esta variable de proceso solo puede usarse por otros nodos después de que esta tarea se complete. Su ámbito es toda la instancia de proceso. Si la clave de la variable de proceso establecida ya existe con el mismo nombre en la instancia de proceso, la variable posterior reemplazará a la anterior.
Esto debe establecerse cuando se completa la tarea de creación de la solicitud de viaje.
/**
* Iniciar instancia del proceso
*/
@Test
public void iniciarSinVariables() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// Clave de la definición del proceso
String clave = "viaje-variable";
// Crear colección de variables
Map<String, Object> variables = new HashMap<>();
// Establecer el valor de assignee
variables.put("responsable0", "zhangsan1");
variables.put("responsable1", "lisi1");
variables.put("responsable2", "wangwu1");
variables.put("responsable3", "caiwu1");
InstanciaProceso instanciaProceso = servicioRuntime.startProcessInstanceByKey(clave, variables);
// Información de salida
System.out.println("Nombre de la instancia del proceso obtenido:" + instanciaProceso.getName());
System.out.println("ID de la definición del proceso:" + instanciaProceso.getProcessDefinitionId());
}
/**
* Completar tarea
*/
@Test
public void completarTareaEstableciendoVariable() {
String clave = "viaje-variable";
String responsable = "lisi1";
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
Tarea tarea = servicioTareas.createTaskQuery()
.processDefinitionKey(clave)
.taskAssignee(responsable)
.singleResult();
Map<String, Object> variables = new HashMap<>();
// Crear objeto POJO de viaje
Viaje viaje = new Viaje();
// Establecer número de días de viaje
viaje.setNum(4d);
// Definir variables de proceso en la colección
variables.put("viaje", viaje);
if (tarea != null) {
servicioTareas.complete(tarea.getId(), variables);
System.out.println("Tarea completada...");
}
}
d. Establecer mediante la instancia de proceso actual
Establecer la variable global a través del id de la instancia de proceso. La instancia de proceso no debe haber finalizado.
@Test
public void establecerVariableGlobalPorInstancia() {
// ID de ejecución de la instancia de proceso actual, normalmente se establece como el id de la instancia de proceso actual en ejecución
String ejecucionId = "2601";
// Obtener processEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
// Obtener RuntimeService
ServicioTiempoEjecucion servicioRuntime = motor.getRuntimeService();
// Crear objeto POJO de viaje
Viaje viaje = new Viaje();
// Establecer días
viaje.setNum(3d);
// Establecer la variable de proceso a través del id de la instancia de proceso
servicioRuntime.setVariable(ejecucionId, "viaje", viaje);
// Establecer múltiples valores a la vez
// servicioRuntime.setVariables(ejecucionId, variables)
}
e. Establecer mediante la tarea actual
@Test
public void establecerVariableGlobalPorTarea() {
// ID de la tarea pendiente actual
String tareaId = "1404";
// Obtener processEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
Viaje viaje = new Viaje();
viaje.setNum(3);
// Establecer la variable de proceso a través de la tarea
servicioTareas.setVariable(tareaId, "viaje", viaje);
// Establecer múltiples valores a la vez
// servicioTareas.setVariables(tareaId, variables)
}
Nota: El id de la tarea debe ser el id de la tarea pendiente actual, existente en act_ru_task. Si la tarea ya ha finalizado, se producirá un error. También se puede obtener la variable de proceso a través de servicioTareas.getVariable().
3.5.4 Establecer variables de proceso locales
a. Establecer al procesar la tarea
Establecer la variable de proceso local al procesar la tarea. La instancia de proceso actual en ejecución solo puede usar esta variable antes de que la tarea finalice. Después de finalizar la tarea, la variable no se puede usar en la instancia de proceso actual. Se puede consultar a través de la consulta de tareas históricas.
/**
* Establecer variables de proceso locales al procesar la tarea
*/
@Test
public void completarTareaConVariableLocal() {
// ID de la tarea
String tareaId = "1404";
// Obtener processEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
// Definir variables del proceso
Map<String, Object> variables = new HashMap<String, Object>();
Viaje viaje = new Viaje();
viaje.setNum(3d);
// Definir variables del proceso
variables = new HashMap<String, Object>();
// Nombre de la variable es "viaje", valor es el objeto viaje
variables.put("viaje", viaje);
// Establecer variable local, ámbito es esta tarea
servicioTareas.setVariablesLocal(tareaId, variables);
// Completar tarea
servicioTareas.complete(tareaId);
}
Nota: Al establecer una variable local con ámbito en la tarea, cada tarea puede establecer una variable con el mismo nombre, sin afectarse mutuamente.
b. Establecer mediante la tarea actual
@Test
public void establecerVariableLocalPorTarea() {
// ID de la tarea pendiente actual
String tareaId = "1404";
// Obtener processEngine
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
Viaje viaje = new Viaje();
viaje.setNum(3d);
// Establecer la variable de proceso a través de la tarea
servicioTareas.setVariableLocal(tareaId, "viaje", viaje);
// Establecer múltiples valores a la vez
// servicioTareas.setVariablesLocal(tareaId, variables)
}
c. Prueba 1 de variables locales
Si en el ejemplo anterior se cambian las variables globales por variables locales, ¿es factible? ¿Por qué?
Las variables locales no se pueden usar en la ejecución actual de la instancia de proceso después de que la tarea finaliza. Si el proceso posterior necesita usar esta variable, se producirá un error.
d. Prueba 2 de variables locales
Al establecer variables locales en las tareas de revisión del gerente de departamento, revisión del gerente general y revisión financiera, se puede consultar el valor de la variable de proceso al consultar cada tarea histórica a través de historyService.
// Crear objeto de consulta de tareas históricas
ConsultaInstanciaTareaHistorica consultaHistoricaTarea = servicioHistorial.createHistoricTaskInstanceQuery();
// La consulta incluye variables locales
consultaHistoricaTarea.includeTaskLocalVariables();
for (InstanciaTareaHistorica tareaHistorica : lista) {
System.out.println("==============================");
System.out.println("ID de la tarea:" + tareaHistorica.getId());
System.out.println("Nombre de la tarea:" + tareaHistorica.getName());
System.out.println("Responsable de la tarea:" + tareaHistorica.getAssignee());
System.out.println("Variables locales de la tarea:" + tareaHistorica.getTaskLocalVariables());
}
Nota: Consultar variables de proceso históricas, especialmente variables POJO, requiere deserialización. No se recomienda su uso.
4. Tareas grupales
4.1 Requisitos
En la definición del proceso, se establece un responsable fijo para la tarea en el nodo de tarea (assignee). Esto fija los participantes en el archivo bpmn durante la definición del proceso. Si el responsable de la tarea cambia temporalmente, es necesario modificar la definición del proceso, lo que resulta en una poca escalabilidad del sistema.
Para este caso, se pueden establecer múltiples candidatos para una tarea. De los candidatos, se puede seleccionar un participante para completar la tarea.
4.2 Establecer candidatos de tarea
En la configuración del nodo de tarea en el diagrama del proceso, estiblecer candidate-users (candidatos). Múltiples candidatos se separan por comas.
Al ver el archivo bpmn:
<userTask activiti:candidateUsers="lisi,wangwu" activiti:exclusive="true" id="_3" name="Revisión del Gerente"/>
Podemos ver que el revisor del gerente de departamento ya está configurado como un grupo de candidatos lisi,wangwu. Se puede usar activiti:candidateUsers="usuario1,usuario2,usuario3" para establecer un grupo de candidatos.
4.3 Tareas grupales
4.3.1 Flujo de procesamiento de tareas grupales
a. Consultar tareas grupales: Especificar candidatos y consultar las tareas pendientes grupales actuales de ese candidato. Los candidatos no pueden procesar la tarea de inmediato.
b. Tomar (reclamar) la tarea: Todos los candidatos de la tarea grupal pueden tomarla. La tarea grupal se convierte en una tarea personal. El candidato original se convierte en el responsable de la tarea. Si después de tomar la tarea no se desea procesarla, se debe devolver la tarea grupal tomada al grupo, convirtiendo la tarea personal nuevamente en una tarea grupal.
c. Consultar tareas personales: El método de consulta es el mismo que para las tareas personales, consultando las tareas personales a cargo del usuario según assignee.
d. Procesar tarea personal.
4.3.2 Consultar tareas grupales
/**
* Consultar tareas grupales
*/
@Test
public void buscarTareasGrupales() {
String clave = "viaje1";
String candidato = "lisi";
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
List<Tarea> lista = servicioTareas.createTaskQuery()
.processDefinitionKey(clave)
.taskCandidateUser(candidato)
.list();
for (Tarea tarea : lista) {
System.out.println("ID de la instancia del proceso:" + tarea.getProcessInstanceId());
System.out.println("ID de la tarea:" + tarea.getId());
System.out.println("Responsable:" + tarea.getAssignee());
System.out.println("Nombre de la tarea:" + tarea.getName());
}
}
4.3.3 Tomar tarea grupal
Después de que un candidato tome la tarea grupal, esta se convierte en su tarea personal.
/**
* Candidato toma la tarea
*/
@Test
public void tomarTareaGrupal() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
String tareaId = "72505";
// Candidato
String usuarioId = "lisi";
// Tomar la tarea
Tarea tarea = servicioTareas.createTaskQuery()
.taskId(tareaId)
.taskCandidateUser(usuarioId) // Consultar según candidato
.singleResult();
if (tarea != null) {
// Se puede tomar la tarea
servicioTareas.claim(tareaId, usuarioId);
System.out.println("Tarea tomada con éxito");
}
}
4.3.4 Consultar tareas pendientes personales
El método de consulta es el mismo que para la consulta de tareas personales.
4.3.5 Procesar tarea personal
Igual que el procesamiento de tareas personales.
/**
* Completar tarea personal
*/
@Test
public void completarTareaPersonal() {
String tareaId = "72505";
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
servicioTareas.complete(tareaId);
System.out.println("Tarea completada:" + tareaId);
}
4.3.6 Devolver tarea grupal
Si el usuario no desea procesar la tarea grupal, puede devolverla. Después de la devolución, el usuario ya no es el responsable de la tarea.
/**
* Devolver tarea
*/
@Test
public void devolverTareaGrupal() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
String tareaId = "75002";
String usuarioId = "zhangsan";
Tarea tarea = servicioTareas.createTaskQuery()
.taskId(tareaId)
.taskAssignee(usuarioId)
.singleResult();
if (tarea != null) {
// Si se establece en null, se devuelve la tarea grupal, la tarea no tiene responsable
servicioTareas.setAssignee(tareaId, null);
}
}
4.3.7 Transferencia de tarea
El responsable de la tarea transfiere la tarea a otro responsable para su procesamiento.
/**
* Transferencia de tarea
*/
@Test
public void transferirTarea() {
ProcessEngine motor = ProcessEngines.getDefaultProcessEngine();
ServicioTareas servicioTareas = motor.getTaskService();
String tareaId = "75002";
String usuarioId = "zhangsan";
Tarea tarea = servicioTareas.createTaskQuery()
.taskId(tareaId)
.taskAssignee(usuarioId)
.singleResult();
if (tarea != null) {
// Establecer el nuevo responsable de la tarea
servicioTareas.setAssignee(tareaId, "zhaoliu");
}
}
4.3.8 Operación en tablas de base de datos
Consultar la tabla de ejecución de tareas actual: SELECT * FROM act_ru_task. La tabla de ejecución de tareas registra la tarea actual en ejecución. Como la tarea actual es una tarea grupal, assignee está vacío. Cuando se toma la tarea, este campo se convierte en el id del usuario que la tomó.
Consultar participantes de la tarea: SELECT * FROM act_ru_identitylink. Los participantes de la tarea, registran los usuarios o grupos que participan en la tarea actual. Si la tarea tiene candidatos configurados, se insertarán registros de candidatos en esta tabla. Hay tantos registros como candidatos. Junto con act_ru_identitylink, también hay una tabla histórica act_hi_identitylink. Al insertar registros en act_ru_identitylink, también se insertan registros en la tabla histórica.
5. Pasarelas
Las pasarelas se utilizan para controlar el flujo del proceso.
5.1 Pasarela exclusiva (ExclusiveGateway)
5.1.1 ¿Qué es una pasarela exclusiva?
La pasarela exclusiva se utiliza para implementar decisiones en el flujo. Cuando el proceso llega a esta pasarela, todas las bifurcaciones evalúan si la condición es verdadera. Si es verdadera, ejecuta esa bifurcación.
Nota: La pasarela exclusiva solo seleccionará una bifurcación cuya condición sea verdadera. Si dos bifurcaciones tienen condiciones verdaderas, la pasarela exclusiva seleccionará la bifurcación con el valor de id menor.
¿Por qué usar una pasarela exclusiva? Sin la pasarela exclusiva, también se pueden implementar bifurcaciones, por ejemplo, estableciendo condiciones de bifurcación en el enlace. El inconveniente de establecer condiciones en el enlace es que si ninguna condición se cumple, el proceso finaliza (finalización anormal).
Si se utiliza una pasarela exclusiva para decidir la bifurcación, si ninguna condición se cumple, el sistema lanza una excepción.
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process
at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)
5.1.2 Definición del proceso
Icono de la pasarela exclusiva.
5.1.3 Prueba
Después de la revisión del gerente de departamento, el flujo pasa por la pasarela exclusiva. De la pasarela exclusiva salen dos bifurcaciones: una evalúa si el número de días de viaje es mayor que 3, la otra evalúa si es menor o igual a 3.
Al establecer las condiciones de bifurcación, si ninguna condición es verdadera, se produce un error.
5.2 Pasarela paralela (ParallelGateway)
5.2.1 ¿Qué es una pasarela paralela?
La pasarela paralela permite dividir el flujo en múltiples bifurcaciones, o unir múltiples bifurcaciones. La función de la pasarela paralela se basa en los flujos de entrada y salida:
- División (fork): Todos los flujos de salida se ejecutan en paralelo, creando una rama de ejecución paralela para cada flujo.
- Unión (join): Todas las bifurcaciones que llegan a la pasarela paralela esperan aquí, hasta que todas las bifurcaciones se completen, el flujo pasará a través de la pasarela de unión.
Si la misma pasarela paralela tiene múltiples entradas y múltiples salidas, puede tener funciones de división y unión simultáneamente. En este caso, la pasarela primero convergerá todos los flujos de entrada y luego los dividirá en múltiples ramas paralelas.
La principal diferencia con otras pasarelas es que la pasarela paralela no analiza condiciones. Incluso si se definen condiciones en el flujo, serán ignoradas.
Ejemplo: El gerente técnico y el gerente de proyecto son dos bifurcaciones de ejecución. En la tabla act_ru_execution, hay dos registros que representan al gerente técnico y al gerente de proyecto. act_ru_execution también tiene un registro que representa la instancia del proceso. Cuando ambas tareas del gerente técnico y del gerente de proyecto se completen, convergerán en el punto de unión, a través de la pasarela paralela parallelGateway.
La pasarela paralela se usa comúnmente en aplicaciones empresariales para tareas de firma múltiple (会签), que son tareas procesadas conjuntamente por múltiples participantes.
5.2.2 Definición del proceso
Icono de la pasarela paralela.
5.2.3 Prueba
Al ejecutar hasta la pasarela paralela, el seguimiento de la base de datos es el siguiente:
Tabla de tareas actual: SELECT * FROM act_ru_task. Hay dos tareas en ejecución actual.
Consulta de la tabla de ejecución de la instancia del proceso: SELECT * FROM act_ru_execution. Indica que la instancia de proceso actual tiene múltiples bifurcaciones (dos) en ejecución.
Ejecución de tareas paralelas: Las tareas paralelas no tienen un orden, el responsable de la tarea las ejecuta.
Después de ejecutar la tarea del gerente técnico, consulte la tabla de tareas actual: SELECT * FROM act_ru_task. La tarea completada del gerente técnico se ha eliminado de la tabla de tareas actual act_ru_task_.
En la tabla de ejecución de la instancia del proceso: SELECT * FROM act_ru_execution, existen múltiples bifurcaciones y un nodo de unión de la pasarela paralela.
Cuando todas las bifurcaciones se completen y lleguen al nodo de unión, la tabla de ejecución de la instancia del proceso: SELECT * FROM act_ru_execution, la instancia de ejecución del proceso se ha convertido en la revisión del gerente general, lo que indica que la ejecución del proceso ha pasado a través de la pasarela paralela.
5.3 Pasarela inclusiva (InclusiveGateway)
5.3.1 ¿Qué es una pasarela inclusiva?
La pasarela inclusiva se puede considerar una combinación de la pasarela exclusiva y la pasarela paralela. Al igual que la pasarela exclusiva, se pueden definir condiciones en los flujos de salida. La pasarela inclusiva las analizará. Sin embargo, la principal diferencia es que la pasarela inclusiva puede seleccionar más de un flujo de salida, similar a la pasarela paralela.
La función de la pasarela inclusiva se basa en los flujos de entrada y salida:
- Bifurcación: Se analizan todas las condiciones de los flujos de salida. Los flujos cuyo resultado sea verdadero se ejecutarán de manera paralela, creando una bifurcación para cada flujo.
- Convergencia: Todas las bifurcaciones paralelas que llegan a la pasarela inclusiva entran en estado de espera, hasta que las bifurcaciones de todos los flujos de entrada que contienen tokens lleguen. Esta es la mayor diferencia con la pasarela paralela. En otras palabras, la pasarela inclusiva solo espera a los flujos de entrada que fueron seleccionados para ejecución. Después de la convergencia, el flujo continuará a través de la pasarela inclusiva.
5.3.2 Definición del proceso
Solicitud de viaje mayor o igual a 3 días requiere la aprobación del gerente de proyecto, menos de 3 días requiere la aprobación del gerente técnico, la solicitud de viaje debe pasar por la aprobación del gerente de recursos humanos.
Icono de la pasarela inclusiva.
Definir el proceso: A través de la pasarela inclusiva, cada bifurcación tiene condiciones establecidas en los enlaces.
5.3.3 Prueba
Si las condiciones establecidas en la pasarela inclusiva no existen en las variables del proceso, se produce un error.
Al ejecutar la primera pasarela inclusiva, se evaluarán las condiciones para determinar qué bifurcaciones tomar.
Tabla de ejecución de la instancia del proceso: SELECT * FROM act_ru_execution.
Tabla de tareas actual: ACT_RU_TASK.
Si una bifurcación llega primero al nodo de unión, debe esperar a que las otras bifurcaciones lleguen.
Después de procesar la aprobación del gerente de proyecto, consulte la tabla de tareas actual: ACT_RU_TASK.
Tabla de ejecución de la instancia del proceso: SELECT * FROM act_ru_execution.
Resumen: En la bifurcación, se evalúan las condiciones. Las bifurcaciones que cumplan las condiciones se ejecutarán, y finalmente convergerán.
5.4 Pasarela de eventos (EventGateway)
La pasarela de eventos permite decidir el flujo según eventos. Cada flujo de salida de la pasarela debe conectarse a un evento intermedio de captura. Cuando el flujo llega a una pasarela basada en eventos, la pasarela entra en estado de espera: suspende la ejecución. Al mismo tiempo, crea suscripciones de eventos correspondientes para cada flujo de salida.
Los flujos de salida de la pasarela de eventos son diferentes de los flujos normales. Estos flujos no se "ejecutan" realmente; en cambio, le permiten al motor de flujo decidir a qué eventos debe suscribirse el proceso que llega a la pasarela de eventos. Se deben considerar las siguientes condiciones:
- La pasarela de eventos debe tener dos o más flujos de salida.
- Después de la pasarela de eventos, solo se puede usar el tipo intermediateCatchEvent (activiti no admite ReceiveTask después de la pasarela de eventos).
- Los eventos intermedios de captura conectados a la pasarela de eventos deben tener solo un flujo de entrada.
5.4.1 Definición del proceso
Icono de la pasarela de eventos.
intermediateCatchEvent: Tipos de eventos soportados: Evento de mensaje (Message Event), Evento de señal (Signal Event), Evento de temporizador (Timer Event).