mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-05-12 16:16:50 +08:00
优化流程图
This commit is contained in:
@@ -20,18 +20,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-diagram-rest</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-common-rest</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-basic</artifactId>
|
||||
@@ -49,6 +37,7 @@
|
||||
<artifactId>batik-parser</artifactId>
|
||||
<version>${batik.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-transcoder</artifactId>
|
||||
@@ -60,6 +49,7 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-bridge</artifactId>
|
||||
@@ -125,16 +115,24 @@
|
||||
<artifactId>hsweb-system-organizational-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-system-organizational-authorization</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-system-dynamic-form-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.vavr</groupId>
|
||||
<artifactId>vavr</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-system-dynamic-form-starter</artifactId>
|
||||
@@ -142,11 +140,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.vavr</groupId>
|
||||
<artifactId>vavr</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-system-authorization-starter</artifactId>
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.workflow.web.diagram;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.activiti.engine.ActivitiException;
|
||||
import org.activiti.engine.ActivitiObjectNotFoundException;
|
||||
import org.activiti.engine.HistoryService;
|
||||
import org.activiti.engine.RepositoryService;
|
||||
import org.activiti.engine.RuntimeService;
|
||||
import org.activiti.engine.history.HistoricActivityInstance;
|
||||
import org.activiti.engine.impl.bpmn.behavior.BoundaryEventActivityBehavior;
|
||||
import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior;
|
||||
import org.activiti.engine.impl.bpmn.parser.BpmnParse;
|
||||
import org.activiti.engine.impl.bpmn.parser.ErrorEventDefinition;
|
||||
import org.activiti.engine.impl.bpmn.parser.EventSubscriptionDeclaration;
|
||||
import org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl;
|
||||
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
|
||||
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
|
||||
import org.activiti.engine.impl.pvm.PvmTransition;
|
||||
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
|
||||
import org.activiti.engine.impl.pvm.process.ActivityImpl;
|
||||
import org.activiti.engine.impl.pvm.process.Lane;
|
||||
import org.activiti.engine.impl.pvm.process.LaneSet;
|
||||
import org.activiti.engine.impl.pvm.process.ParticipantProcess;
|
||||
import org.activiti.engine.impl.pvm.process.TransitionImpl;
|
||||
import org.activiti.engine.repository.ProcessDefinition;
|
||||
import org.activiti.engine.runtime.Execution;
|
||||
import org.activiti.engine.runtime.ProcessInstance;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class BaseProcessDefinitionDiagramLayoutResource {
|
||||
|
||||
@Autowired
|
||||
private RuntimeService runtimeService;
|
||||
|
||||
@Autowired
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
@Autowired
|
||||
private HistoryService historyService;
|
||||
|
||||
public Map<String, Object> getDiagramNode(String processInstanceId, String processDefinitionId) {
|
||||
|
||||
List<String> highLightedFlows = Collections.<String>emptyList();
|
||||
List<String> highLightedActivities;
|
||||
|
||||
Map<String, Object> subProcessInstanceMap = new HashMap<>();
|
||||
|
||||
ProcessInstance processInstance = null;
|
||||
if (processInstanceId != null) {
|
||||
processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
|
||||
if (processInstance == null) {
|
||||
throw new ActivitiObjectNotFoundException("Process instance could not be found");
|
||||
}
|
||||
processDefinitionId = processInstance.getProcessDefinitionId();
|
||||
|
||||
List<ProcessInstance> subProcessInstances = runtimeService
|
||||
.createProcessInstanceQuery()
|
||||
.superProcessInstanceId(processInstanceId).list();
|
||||
|
||||
for (ProcessInstance subProcessInstance : subProcessInstances) {
|
||||
String subDefId = subProcessInstance.getProcessDefinitionId();
|
||||
|
||||
String superExecutionId = (subProcessInstance).getSuperExecutionId();
|
||||
ProcessDefinitionEntity subDef = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(subDefId);
|
||||
|
||||
JSONObject processInstanceJSON = new JSONObject();
|
||||
processInstanceJSON.put("processInstanceId", subProcessInstance.getId());
|
||||
processInstanceJSON.put("superExecutionId", superExecutionId);
|
||||
processInstanceJSON.put("processDefinitionId", subDef.getId());
|
||||
processInstanceJSON.put("processDefinitionKey", subDef.getKey());
|
||||
processInstanceJSON.put("processDefinitionName", subDef.getName());
|
||||
|
||||
subProcessInstanceMap.put(superExecutionId, processInstanceJSON);
|
||||
}
|
||||
}
|
||||
|
||||
if (processDefinitionId == null) {
|
||||
throw new ActivitiObjectNotFoundException("No process definition id provided");
|
||||
}
|
||||
|
||||
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
|
||||
|
||||
if (processDefinition == null) {
|
||||
throw new ActivitiException("Process definition " + processDefinitionId + " could not be found");
|
||||
}
|
||||
|
||||
JSONObject responseJSON = new JSONObject();
|
||||
|
||||
// Process definition
|
||||
Map<String, Object> pdrJSON = getProcessDefinitionResponse(processDefinition);
|
||||
|
||||
if (pdrJSON != null) {
|
||||
responseJSON.put("processDefinition", pdrJSON);
|
||||
}
|
||||
|
||||
// Highlighted activities
|
||||
if (processInstance != null) {
|
||||
JSONArray activityArray = new JSONArray();
|
||||
JSONArray flowsArray = new JSONArray();
|
||||
|
||||
highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
|
||||
highLightedFlows = getHighLightedFlows(processInstanceId, processDefinition);
|
||||
|
||||
for (String activityName : highLightedActivities) {
|
||||
activityArray.add(activityName);
|
||||
}
|
||||
|
||||
for (String flow : highLightedFlows)
|
||||
flowsArray.add(flow);
|
||||
|
||||
responseJSON.put("highLightedActivities", activityArray);
|
||||
responseJSON.put("highLightedFlows", flowsArray);
|
||||
}
|
||||
|
||||
// Pool shape, if process is participant in collaboration
|
||||
if (processDefinition.getParticipantProcess() != null) {
|
||||
ParticipantProcess pProc = processDefinition.getParticipantProcess();
|
||||
|
||||
JSONObject participantProcessJSON = new JSONObject();
|
||||
participantProcessJSON.put("id", pProc.getId());
|
||||
if (StringUtils.isNotEmpty(pProc.getName())) {
|
||||
participantProcessJSON.put("name", pProc.getName());
|
||||
} else {
|
||||
participantProcessJSON.put("name", "");
|
||||
}
|
||||
participantProcessJSON.put("x", pProc.getX());
|
||||
participantProcessJSON.put("y", pProc.getY());
|
||||
participantProcessJSON.put("width", pProc.getWidth());
|
||||
participantProcessJSON.put("height", pProc.getHeight());
|
||||
|
||||
responseJSON.put("participantProcess", participantProcessJSON);
|
||||
}
|
||||
|
||||
// Draw lanes
|
||||
|
||||
if (processDefinition.getLaneSets() != null && !processDefinition.getLaneSets().isEmpty()) {
|
||||
JSONArray laneSetArray = new JSONArray();
|
||||
for (LaneSet laneSet : processDefinition.getLaneSets()) {
|
||||
JSONArray laneArray = new JSONArray();
|
||||
if (laneSet.getLanes() != null && !laneSet.getLanes().isEmpty()) {
|
||||
for (Lane lane : laneSet.getLanes()) {
|
||||
JSONObject laneJSON = new JSONObject();
|
||||
laneJSON.put("id", lane.getId());
|
||||
if (StringUtils.isNotEmpty(lane.getName())) {
|
||||
laneJSON.put("name", lane.getName());
|
||||
} else {
|
||||
laneJSON.put("name", "");
|
||||
}
|
||||
laneJSON.put("x", lane.getX());
|
||||
laneJSON.put("y", lane.getY());
|
||||
laneJSON.put("width", lane.getWidth());
|
||||
laneJSON.put("height", lane.getHeight());
|
||||
|
||||
List<String> flowNodeIds = lane.getFlowNodeIds();
|
||||
JSONArray flowNodeIdsArray = new JSONArray();
|
||||
for (String flowNodeId : flowNodeIds) {
|
||||
flowNodeIdsArray.add(flowNodeId);
|
||||
}
|
||||
laneJSON.put("flowNodeIds", flowNodeIdsArray);
|
||||
|
||||
laneArray.add(laneJSON);
|
||||
}
|
||||
}
|
||||
JSONObject laneSetJSON = new JSONObject();
|
||||
laneSetJSON.put("id", laneSet.getId());
|
||||
if (StringUtils.isNotEmpty(laneSet.getName())) {
|
||||
laneSetJSON.put("name", laneSet.getName());
|
||||
} else {
|
||||
laneSetJSON.put("name", "");
|
||||
}
|
||||
laneSetJSON.put("lanes", laneArray);
|
||||
|
||||
laneSetArray.add(laneSetJSON);
|
||||
}
|
||||
|
||||
if (laneSetArray.size() > 0)
|
||||
responseJSON.put("laneSets", laneSetArray);
|
||||
}
|
||||
|
||||
JSONArray sequenceFlowArray = new JSONArray();
|
||||
JSONArray activityArray = new JSONArray();
|
||||
|
||||
// Activities and their sequence-flows
|
||||
|
||||
for (ActivityImpl activity : processDefinition.getActivities()) {
|
||||
getActivity(processInstanceId, activity, activityArray, sequenceFlowArray,
|
||||
processInstance, highLightedFlows, subProcessInstanceMap);
|
||||
}
|
||||
|
||||
responseJSON.put("activities", activityArray);
|
||||
responseJSON.put("sequenceFlows", sequenceFlowArray);
|
||||
|
||||
return responseJSON;
|
||||
}
|
||||
|
||||
private List<String> getHighLightedFlows(String processInstanceId, ProcessDefinitionEntity processDefinition) {
|
||||
|
||||
List<String> highLightedFlows = new ArrayList<String>();
|
||||
List<HistoricActivityInstance> historicActivityInstances = historyService
|
||||
.createHistoricActivityInstanceQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
.orderByHistoricActivityInstanceStartTime().asc().list();
|
||||
|
||||
List<String> historicActivityInstanceList = new ArrayList<String>();
|
||||
for (HistoricActivityInstance hai : historicActivityInstances) {
|
||||
historicActivityInstanceList.add(hai.getActivityId());
|
||||
}
|
||||
|
||||
// add current activities to list
|
||||
List<String> highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
|
||||
historicActivityInstanceList.addAll(highLightedActivities);
|
||||
|
||||
// activities and their sequence-flows
|
||||
for (ActivityImpl activity : processDefinition.getActivities()) {
|
||||
int index = historicActivityInstanceList.indexOf(activity.getId());
|
||||
|
||||
if (index >= 0 && index + 1 < historicActivityInstanceList.size()) {
|
||||
List<PvmTransition> pvmTransitionList = activity
|
||||
.getOutgoingTransitions();
|
||||
for (PvmTransition pvmTransition : pvmTransitionList) {
|
||||
String destinationFlowId = pvmTransition.getDestination().getId();
|
||||
if (destinationFlowId.equals(historicActivityInstanceList.get(index + 1))) {
|
||||
highLightedFlows.add(pvmTransition.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return highLightedFlows;
|
||||
}
|
||||
|
||||
private void getActivity(String processInstanceId, ActivityImpl activity, List activityArray,
|
||||
List sequenceFlowArray, ProcessInstance processInstance, List<String> highLightedFlows,
|
||||
Map<String, Object> subProcessInstanceMap) {
|
||||
|
||||
JSONObject activityJSON = new JSONObject();
|
||||
|
||||
// Gather info on the multi instance marker
|
||||
String multiInstance = (String) activity.getProperty("multiInstance");
|
||||
if (multiInstance != null) {
|
||||
if (!"sequential".equals(multiInstance)) {
|
||||
multiInstance = "parallel";
|
||||
}
|
||||
}
|
||||
|
||||
ActivityBehavior activityBehavior = activity.getActivityBehavior();
|
||||
// Gather info on the collapsed marker
|
||||
Boolean collapsed = (activityBehavior instanceof CallActivityBehavior);
|
||||
Boolean expanded = (Boolean) activity.getProperty(BpmnParse.PROPERTYNAME_ISEXPANDED);
|
||||
if (expanded != null) {
|
||||
collapsed = !expanded;
|
||||
}
|
||||
|
||||
Boolean isInterrupting = null;
|
||||
if (activityBehavior instanceof BoundaryEventActivityBehavior) {
|
||||
isInterrupting = ((BoundaryEventActivityBehavior) activityBehavior).isInterrupting();
|
||||
}
|
||||
|
||||
// Outgoing transitions of activity
|
||||
for (PvmTransition sequenceFlow : activity.getOutgoingTransitions()) {
|
||||
String flowName = (String) sequenceFlow.getProperty("name");
|
||||
boolean isHighLighted = (highLightedFlows.contains(sequenceFlow.getId()));
|
||||
boolean isConditional = sequenceFlow.getProperty(BpmnParse.PROPERTYNAME_CONDITION) != null &&
|
||||
!((String) activity.getProperty("type")).toLowerCase().contains("gateway");
|
||||
boolean isDefault = sequenceFlow.getId().equals(activity.getProperty("default"))
|
||||
&& ((String) activity.getProperty("type")).toLowerCase().contains("gateway");
|
||||
|
||||
List<Integer> waypoints = ((TransitionImpl) sequenceFlow).getWaypoints();
|
||||
JSONArray xPointArray = new JSONArray();
|
||||
JSONArray yPointArray = new JSONArray();
|
||||
for (int i = 0; i < waypoints.size(); i += 2) { // waypoints.size()
|
||||
// minimally 4: x1, y1,
|
||||
// x2, y2
|
||||
xPointArray.add(waypoints.get(i));
|
||||
yPointArray.add(waypoints.get(i + 1));
|
||||
}
|
||||
|
||||
JSONObject flowJSON = new JSONObject();
|
||||
flowJSON.put("id", sequenceFlow.getId());
|
||||
flowJSON.put("name", flowName);
|
||||
flowJSON.put("flow", "(" + sequenceFlow.getSource().getId() + ")--"
|
||||
+ sequenceFlow.getId() + "-->("
|
||||
+ sequenceFlow.getDestination().getId() + ")");
|
||||
|
||||
if (isConditional)
|
||||
flowJSON.put("isConditional", isConditional);
|
||||
if (isDefault)
|
||||
flowJSON.put("isDefault", isDefault);
|
||||
if (isHighLighted)
|
||||
flowJSON.put("isHighLighted", isHighLighted);
|
||||
|
||||
flowJSON.put("xPointArray", xPointArray);
|
||||
flowJSON.put("yPointArray", yPointArray);
|
||||
|
||||
sequenceFlowArray.add(flowJSON);
|
||||
}
|
||||
|
||||
// Nested activities (boundary events)
|
||||
JSONArray nestedActivityArray = new JSONArray();
|
||||
for (ActivityImpl nestedActivity : activity.getActivities()) {
|
||||
nestedActivityArray.add(nestedActivity.getId());
|
||||
}
|
||||
|
||||
Map<String, Object> properties = activity.getProperties();
|
||||
JSONObject propertiesJSON = new JSONObject();
|
||||
for (String key : properties.keySet()) {
|
||||
Object prop = properties.get(key);
|
||||
if (prop instanceof String)
|
||||
propertiesJSON.put(key, (String) properties.get(key));
|
||||
else if (prop instanceof Integer)
|
||||
propertiesJSON.put(key, (Integer) properties.get(key));
|
||||
else if (prop instanceof Boolean)
|
||||
propertiesJSON.put(key, (Boolean) properties.get(key));
|
||||
else if ("initial".equals(key)) {
|
||||
ActivityImpl act = (ActivityImpl) properties.get(key);
|
||||
propertiesJSON.put(key, act.getId());
|
||||
} else if ("timerDeclarations".equals(key)) {
|
||||
ArrayList<TimerDeclarationImpl> timerDeclarations = (ArrayList<TimerDeclarationImpl>) properties.get(key);
|
||||
JSONArray timerDeclarationArray = new JSONArray();
|
||||
|
||||
if (timerDeclarations != null)
|
||||
for (TimerDeclarationImpl timerDeclaration : timerDeclarations) {
|
||||
JSONObject timerDeclarationJSON = new JSONObject();
|
||||
|
||||
timerDeclarationJSON.put("isExclusive", timerDeclaration.isExclusive());
|
||||
if (timerDeclaration.getRepeat() != null)
|
||||
timerDeclarationJSON.put("repeat", timerDeclaration.getRepeat());
|
||||
|
||||
timerDeclarationJSON.put("retries", String.valueOf(timerDeclaration.getRetries()));
|
||||
timerDeclarationJSON.put("type", timerDeclaration.getJobHandlerType());
|
||||
timerDeclarationJSON.put("configuration", timerDeclaration.getJobHandlerConfiguration());
|
||||
//timerDeclarationJSON.put("expression", timerDeclaration.getDescription());
|
||||
|
||||
timerDeclarationArray.add(timerDeclarationJSON);
|
||||
}
|
||||
if (timerDeclarationArray.size() > 0)
|
||||
propertiesJSON.put(key, timerDeclarationArray);
|
||||
// TODO: implement getting description
|
||||
} else if ("eventDefinitions".equals(key)) {
|
||||
ArrayList<EventSubscriptionDeclaration> eventDefinitions = (ArrayList<EventSubscriptionDeclaration>) properties.get(key);
|
||||
JSONArray eventDefinitionsArray = new JSONArray();
|
||||
|
||||
if (eventDefinitions != null) {
|
||||
for (EventSubscriptionDeclaration eventDefinition : eventDefinitions) {
|
||||
JSONObject eventDefinitionJSON = new JSONObject();
|
||||
|
||||
if (eventDefinition.getActivityId() != null)
|
||||
eventDefinitionJSON.put("activityId", eventDefinition.getActivityId());
|
||||
|
||||
eventDefinitionJSON.put("eventName", eventDefinition.getEventName());
|
||||
eventDefinitionJSON.put("eventType", eventDefinition.getEventType());
|
||||
eventDefinitionJSON.put("isAsync", eventDefinition.isAsync());
|
||||
eventDefinitionJSON.put("isStartEvent", eventDefinition.isStartEvent());
|
||||
eventDefinitionsArray.add(eventDefinitionJSON);
|
||||
}
|
||||
}
|
||||
|
||||
if (eventDefinitionsArray.size() > 0)
|
||||
propertiesJSON.put(key, eventDefinitionsArray);
|
||||
|
||||
// TODO: implement it
|
||||
} else if ("errorEventDefinitions".equals(key)) {
|
||||
ArrayList<ErrorEventDefinition> errorEventDefinitions = (ArrayList<ErrorEventDefinition>) properties.get(key);
|
||||
JSONArray errorEventDefinitionsArray = new JSONArray();
|
||||
|
||||
if (errorEventDefinitions != null) {
|
||||
for (ErrorEventDefinition errorEventDefinition : errorEventDefinitions) {
|
||||
JSONObject errorEventDefinitionJSON = new JSONObject();
|
||||
|
||||
if (errorEventDefinition.getErrorCode() != null)
|
||||
errorEventDefinitionJSON.put("errorCode", errorEventDefinition.getErrorCode());
|
||||
else
|
||||
errorEventDefinitionJSON.put("errorCode",null);
|
||||
|
||||
errorEventDefinitionJSON.put("handlerActivityId",
|
||||
errorEventDefinition.getHandlerActivityId());
|
||||
|
||||
errorEventDefinitionsArray.add(errorEventDefinitionJSON);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorEventDefinitionsArray.size() > 0)
|
||||
propertiesJSON.put(key, errorEventDefinitionsArray);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ("callActivity".equals(properties.get("type"))) {
|
||||
CallActivityBehavior callActivityBehavior = null;
|
||||
|
||||
if (activityBehavior instanceof CallActivityBehavior) {
|
||||
callActivityBehavior = (CallActivityBehavior) activityBehavior;
|
||||
}
|
||||
|
||||
if (callActivityBehavior != null) {
|
||||
propertiesJSON.put("processDefinitonKey", callActivityBehavior.getProcessDefinitonKey());
|
||||
|
||||
// get processDefinitonId from execution or get last processDefinitonId
|
||||
// by key
|
||||
JSONArray processInstanceArray = new JSONArray();
|
||||
if (processInstance != null) {
|
||||
List<Execution> executionList = runtimeService.createExecutionQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
.activityId(activity.getId()).list();
|
||||
if (!executionList.isEmpty()) {
|
||||
for (Execution execution : executionList) {
|
||||
processInstanceArray.add(subProcessInstanceMap.get(execution.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If active activities nas no instance of this callActivity then add
|
||||
// last definition
|
||||
if (processInstanceArray.size() == 0 && StringUtils.isNotEmpty(callActivityBehavior.getProcessDefinitonKey())) {
|
||||
// Get last definition by key
|
||||
ProcessDefinition lastProcessDefinition = repositoryService
|
||||
.createProcessDefinitionQuery()
|
||||
.processDefinitionKey(callActivityBehavior.getProcessDefinitonKey())
|
||||
.latestVersion().singleResult();
|
||||
|
||||
// TODO: unuseful fields there are processDefinitionName, processDefinitionKey
|
||||
if (lastProcessDefinition != null) {
|
||||
JSONObject processInstanceJSON = new JSONObject();
|
||||
processInstanceJSON.put("processDefinitionId", lastProcessDefinition.getId());
|
||||
processInstanceJSON.put("processDefinitionKey", lastProcessDefinition.getKey());
|
||||
processInstanceJSON.put("processDefinitionName", lastProcessDefinition.getName());
|
||||
processInstanceArray.add(processInstanceJSON);
|
||||
}
|
||||
}
|
||||
|
||||
if (processInstanceArray.size() > 0) {
|
||||
propertiesJSON.put("processDefinitons", processInstanceArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activityJSON.put("activityId", activity.getId());
|
||||
activityJSON.put("properties", propertiesJSON);
|
||||
if (multiInstance != null)
|
||||
activityJSON.put("multiInstance", multiInstance);
|
||||
if (collapsed)
|
||||
activityJSON.put("collapsed", collapsed);
|
||||
if (nestedActivityArray.size() > 0)
|
||||
activityJSON.put("nestedActivities", nestedActivityArray);
|
||||
if (isInterrupting != null)
|
||||
activityJSON.put("isInterrupting", isInterrupting);
|
||||
|
||||
activityJSON.put("x", activity.getX());
|
||||
activityJSON.put("y", activity.getY());
|
||||
activityJSON.put("width", activity.getWidth());
|
||||
activityJSON.put("height", activity.getHeight());
|
||||
|
||||
activityArray.add(activityJSON);
|
||||
|
||||
// Nested activities (boundary events)
|
||||
for (ActivityImpl nestedActivity : activity.getActivities()) {
|
||||
getActivity(processInstanceId, nestedActivity, activityArray, sequenceFlowArray,
|
||||
processInstance, highLightedFlows, subProcessInstanceMap);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> getProcessDefinitionResponse(ProcessDefinitionEntity processDefinition) {
|
||||
JSONObject pdrJSON = new JSONObject();
|
||||
pdrJSON.put("id", processDefinition.getId());
|
||||
pdrJSON.put("name", processDefinition.getName());
|
||||
pdrJSON.put("key", processDefinition.getKey());
|
||||
pdrJSON.put("version", processDefinition.getVersion());
|
||||
pdrJSON.put("deploymentId", processDefinition.getDeploymentId());
|
||||
pdrJSON.put("isGraphicNotationDefined", isGraphicNotationDefined(processDefinition));
|
||||
return pdrJSON;
|
||||
}
|
||||
|
||||
private boolean isGraphicNotationDefined(ProcessDefinitionEntity processDefinition) {
|
||||
return ((ProcessDefinitionEntity) repositoryService
|
||||
.getProcessDefinition(processDefinition.getId()))
|
||||
.isGraphicalNotationDefined();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
package org.hswebframework.web.workflow.web.diagram;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/workflow/service/")
|
||||
public class ProcessDefinitionDiagramLayoutResource extends org.activiti.rest.diagram.services.ProcessDefinitionDiagramLayoutResource {
|
||||
public class ProcessDefinitionDiagramLayoutResource
|
||||
extends BaseProcessDefinitionDiagramLayoutResource {
|
||||
|
||||
@GetMapping(
|
||||
value = {"/process-definition/{processDefinitionId}/diagram-layout"},
|
||||
produces = {"application/json"}
|
||||
)
|
||||
public Object getDiagram(@PathVariable String processDefinitionId) {
|
||||
return this.getDiagramNode(null, processDefinitionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.hswebframework.web.workflow.web.diagram;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
@@ -9,6 +11,17 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/workflow/service/")
|
||||
public class ProcessInstanceDiagramLayoutResource extends org.activiti.rest.diagram.services.ProcessInstanceDiagramLayoutResource {
|
||||
public class ProcessInstanceDiagramLayoutResource
|
||||
extends BaseProcessDefinitionDiagramLayoutResource
|
||||
{
|
||||
|
||||
|
||||
@RequestMapping(
|
||||
value = {"/process-instance/{processInstanceId}/diagram-layout"},
|
||||
method = {RequestMethod.GET},
|
||||
produces = {"application/json"}
|
||||
)
|
||||
public Object getDiagram(@PathVariable String processInstanceId) {
|
||||
return this.getDiagramNode(processInstanceId, (String)null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,246 @@
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.workflow.web.diagram;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.activiti.engine.HistoryService;
|
||||
import org.activiti.engine.RepositoryService;
|
||||
import org.activiti.engine.RuntimeService;
|
||||
import org.activiti.engine.history.HistoricActivityInstance;
|
||||
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
|
||||
import org.activiti.engine.impl.pvm.PvmTransition;
|
||||
import org.activiti.engine.impl.pvm.process.ActivityImpl;
|
||||
import org.activiti.engine.runtime.ProcessInstance;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/workflow/service/")
|
||||
public class ProcessInstanceHighlightsResource extends org.activiti.rest.diagram.services.ProcessInstanceHighlightsResource {
|
||||
@RequestMapping("/workflow/service")
|
||||
public class ProcessInstanceHighlightsResource {
|
||||
|
||||
@Autowired
|
||||
private RuntimeService runtimeService;
|
||||
|
||||
@Autowired
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
@Autowired
|
||||
private HistoryService historyService;
|
||||
|
||||
|
||||
@GetMapping(value = "/process-instance/{processInstanceId}/highlights", produces = "application/json")
|
||||
public Object getHighlighted(@PathVariable String processInstanceId) {
|
||||
|
||||
JSONObject responseJSON = new JSONObject();
|
||||
|
||||
responseJSON.put("processInstanceId", processInstanceId);
|
||||
|
||||
JSONArray activitiesArray = new JSONArray();
|
||||
JSONArray flowsArray = new JSONArray();
|
||||
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
|
||||
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
|
||||
|
||||
responseJSON.put("processDefinitionId", processInstance.getProcessDefinitionId());
|
||||
|
||||
List<String> highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
|
||||
List<String> highLightedFlows = getHighLightedFlows(processDefinition, processInstanceId);
|
||||
|
||||
activitiesArray.addAll(highLightedActivities);
|
||||
|
||||
flowsArray.addAll(highLightedFlows);
|
||||
|
||||
|
||||
responseJSON.put("activities", activitiesArray);
|
||||
responseJSON.put("flows", flowsArray);
|
||||
|
||||
return responseJSON;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getHighLightedFlows
|
||||
*
|
||||
* @param processDefinition
|
||||
* @param processInstanceId
|
||||
* @return
|
||||
*/
|
||||
private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinition, String processInstanceId) {
|
||||
|
||||
List<String> highLightedFlows = new ArrayList<String>();
|
||||
|
||||
List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
//order by startime asc is not correct. use default order is correct.
|
||||
//.orderByHistoricActivityInstanceStartTime().asc()/*.orderByActivityId().asc()*/
|
||||
.list();
|
||||
|
||||
LinkedList<HistoricActivityInstance> hisActInstList = new LinkedList<HistoricActivityInstance>();
|
||||
hisActInstList.addAll(historicActivityInstances);
|
||||
|
||||
getHighlightedFlows(processDefinition.getActivities(), hisActInstList, highLightedFlows);
|
||||
|
||||
return highLightedFlows;
|
||||
}
|
||||
|
||||
/**
|
||||
* getHighlightedFlows
|
||||
* <p>
|
||||
* code logic:
|
||||
* 1. Loop all activities by id asc order;
|
||||
* 2. Check each activity's outgoing transitions and eventBoundery outgoing transitions, if outgoing transitions's destination.id is in other executed activityIds, add this transition to highLightedFlows List;
|
||||
* 3. But if activity is not a parallelGateway or inclusiveGateway, only choose the earliest flow.
|
||||
*
|
||||
* @param activityList
|
||||
* @param hisActInstList
|
||||
* @param highLightedFlows
|
||||
*/
|
||||
private void getHighlightedFlows(List<ActivityImpl> activityList, LinkedList<HistoricActivityInstance> hisActInstList, List<String> highLightedFlows) {
|
||||
|
||||
//check out startEvents in activityList
|
||||
List<ActivityImpl> startEventActList = new ArrayList<ActivityImpl>();
|
||||
Map<String, ActivityImpl> activityMap = new HashMap<String, ActivityImpl>(activityList.size());
|
||||
for (ActivityImpl activity : activityList) {
|
||||
|
||||
activityMap.put(activity.getId(), activity);
|
||||
|
||||
String actType = (String) activity.getProperty("type");
|
||||
if (actType != null && actType.toLowerCase().indexOf("startevent") >= 0) {
|
||||
startEventActList.add(activity);
|
||||
}
|
||||
}
|
||||
|
||||
//These codes is used to avoid a bug:
|
||||
//ACT-1728 If the process instance was started by a callActivity, it will be not have the startEvent activity in ACT_HI_ACTINST table
|
||||
//Code logic:
|
||||
//Check the first activity if it is a startEvent, if not check out the startEvent's highlight outgoing flow.
|
||||
HistoricActivityInstance firstHistActInst = hisActInstList.getFirst();
|
||||
String firstActType = (String) firstHistActInst.getActivityType();
|
||||
if (firstActType != null && firstActType.toLowerCase().indexOf("startevent") < 0) {
|
||||
PvmTransition startTrans = getStartTransaction(startEventActList, firstHistActInst);
|
||||
if (startTrans != null) {
|
||||
highLightedFlows.add(startTrans.getId());
|
||||
}
|
||||
}
|
||||
|
||||
while (!hisActInstList.isEmpty()) {
|
||||
HistoricActivityInstance histActInst = hisActInstList.removeFirst();
|
||||
ActivityImpl activity = activityMap.get(histActInst.getActivityId());
|
||||
if (activity != null) {
|
||||
boolean isParallel = false;
|
||||
String type = histActInst.getActivityType();
|
||||
if ("parallelGateway".equals(type) || "inclusiveGateway".equals(type)) {
|
||||
isParallel = true;
|
||||
} else if ("subProcess".equals(histActInst.getActivityType())) {
|
||||
getHighlightedFlows(activity.getActivities(), hisActInstList, highLightedFlows);
|
||||
}
|
||||
|
||||
List<PvmTransition> allOutgoingTrans = new ArrayList<PvmTransition>();
|
||||
allOutgoingTrans.addAll(activity.getOutgoingTransitions());
|
||||
allOutgoingTrans.addAll(getBoundaryEventOutgoingTransitions(activity));
|
||||
List<String> activityHighLightedFlowIds = getHighlightedFlows(allOutgoingTrans, hisActInstList, isParallel);
|
||||
highLightedFlows.addAll(activityHighLightedFlowIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check out the outgoing transition connected to firstActInst from startEventActList
|
||||
*
|
||||
* @param startEventActList
|
||||
* @param firstActInst
|
||||
* @return
|
||||
*/
|
||||
private PvmTransition getStartTransaction(List<ActivityImpl> startEventActList, HistoricActivityInstance firstActInst) {
|
||||
for (ActivityImpl startEventAct : startEventActList) {
|
||||
for (PvmTransition trans : startEventAct.getOutgoingTransitions()) {
|
||||
if (trans.getDestination().getId().equals(firstActInst.getActivityId())) {
|
||||
return trans;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* getBoundaryEventOutgoingTransitions
|
||||
*
|
||||
* @param activity
|
||||
* @return
|
||||
*/
|
||||
private List<PvmTransition> getBoundaryEventOutgoingTransitions(ActivityImpl activity) {
|
||||
List<PvmTransition> boundaryTrans = new ArrayList<PvmTransition>();
|
||||
for (ActivityImpl subActivity : activity.getActivities()) {
|
||||
String type = (String) subActivity.getProperty("type");
|
||||
if (type != null && type.toLowerCase().indexOf("boundary") >= 0) {
|
||||
boundaryTrans.addAll(subActivity.getOutgoingTransitions());
|
||||
}
|
||||
}
|
||||
return boundaryTrans;
|
||||
}
|
||||
|
||||
/**
|
||||
* find out single activity's highlighted flowIds
|
||||
*
|
||||
* @param activity
|
||||
* @param hisActInstList
|
||||
* @param isExclusive if true only return one flowId(Such as exclusiveGateway, BoundaryEvent On Task)
|
||||
* @return
|
||||
*/
|
||||
private List<String> getHighlightedFlows(List<PvmTransition> pvmTransitionList, LinkedList<HistoricActivityInstance> hisActInstList, boolean isParallel) {
|
||||
|
||||
List<String> highLightedFlowIds = new ArrayList<String>();
|
||||
|
||||
PvmTransition earliestTrans = null;
|
||||
HistoricActivityInstance earliestHisActInst = null;
|
||||
|
||||
for (PvmTransition pvmTransition : pvmTransitionList) {
|
||||
|
||||
String destActId = pvmTransition.getDestination().getId();
|
||||
HistoricActivityInstance destHisActInst = findHisActInst(hisActInstList, destActId);
|
||||
if (destHisActInst != null) {
|
||||
if (isParallel) {
|
||||
highLightedFlowIds.add(pvmTransition.getId());
|
||||
} else if (earliestHisActInst == null || (earliestHisActInst.getId().compareTo(destHisActInst.getId()) > 0)) {
|
||||
earliestTrans = pvmTransition;
|
||||
earliestHisActInst = destHisActInst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((!isParallel) && earliestTrans != null) {
|
||||
highLightedFlowIds.add(earliestTrans.getId());
|
||||
}
|
||||
|
||||
return highLightedFlowIds;
|
||||
}
|
||||
|
||||
private HistoricActivityInstance findHisActInst(LinkedList<HistoricActivityInstance> hisActInstList, String actId) {
|
||||
for (HistoricActivityInstance hisActInst : hisActInstList) {
|
||||
if (hisActInst.getActivityId().equals(actId)) {
|
||||
return hisActInst;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user