@@ -3,9 +3,13 @@ package com.sdm.flowable.process;
import com.sdm.common.common.SdmResponse ;
import com.sdm.flowable.constants.FlowableConfig ;
import com.sdm.flowable.delegate.UniversalDelegate ;
import com.sdm.flowable.dto.NodeDetailInfo ;
import com.sdm.flowable.dto.NodeStructureInfo ;
import com.sdm.flowable.dto.ProcessDefinitionDTO ;
import com.sdm.flowable.dto.ProcessInstanceInfo ;
import com.sdm.flowable.dto.req.AsyncCallbackRequest ;
import com.sdm.flowable.dto.resp.DeployFlowableResp ;
import com.sdm.flowable.dto.resp.ProcessInstanceDetailResponse ;
import com.sdm.flowable.enums.FlowElementTypeEnums ;
import com.sdm.flowable.util.Dto2BpmnConverter ;
import com.sdm.flowable.dto.req.CompleteTaskReq ;
@@ -31,6 +35,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody ;
import java.util.* ;
import java.util.function.Function ;
import java.util.stream.Collectors ;
@@ -102,64 +107,34 @@ public class ProcessService {
/**
* 根据流程定义ID获取节点信息
*/
public List < Map < String , Object > > getNodesByProcessDefinitionId ( String processDefinitionId ) {
return getFlowStructureWithExtension s( processDefinitionId ) ;
public SdmResponse < List < NodeStructureInfo > > getNodesByProcessDefinitionId ( String processDefinitionId ) {
List < FlowNode > orderedNodes = getOrderedFlowNode s( processDefinitionId ) ;
return SdmResponse . success ( orderedNodes . stream ( )
. map ( this : : buildNodeStructureInfo )
. collect ( Collectors . toList ( ) ) ) ;
}
private NodeStructureInfo buildNodeStructureInfo ( FlowNode node ) {
NodeStructureInfo info = new NodeStructureInfo ( ) ;
info . setId ( node . getId ( ) ) ;
info . setName ( node . getName ( ) ! = null ? node . getName ( ) : " " ) ;
info . setType ( node . getClass ( ) . getSimpleName ( ) ) ;
/**
* 带 extensionElements 的流程结构解析
*
* @param processDefinitionId
* @return
*/
private List < Map < String , Object > > getFlowStructureWithExtensions ( String processDefinitionId ) {
// 后续节点
info . setNextNodeIds (
node . getOutgoingFlows ( ) . stream ( )
. map ( SequenceFlow : : getTargetRef )
. collect ( Collectors . toList ( ) )
) ;
BpmnModel bpmnModel = repositoryService . getBpmnModel ( processDefinitionId ) ;
if ( bpmnModel = = null ) return Collections . emptyList ( ) ;
Process process = bpmnModel . getMainProcess ( ) ;
// 找开始事件
StartEvent startEvent = process . findFlowElementsOfType ( StartEvent . class , false )
. stream ( ) . findFirst ( ) . orElse ( null ) ;
if ( startEvent = = null ) return Collections . emptyList ( ) ;
List < Map < String , Object > > result = new ArrayList < > ( ) ;
Queue < FlowNode > queue = new LinkedList < > ( ) ;
Set < String > visited = new HashSet < > ( ) ;
queue . add ( startEvent ) ;
while ( ! queue . isEmpty ( ) ) {
FlowNode node = queue . poll ( ) ;
if ( ! visited . add ( node . getId ( ) ) ) continue ;
Map < String , Object > info = new LinkedHashMap < > ( ) ;
info . put ( " id " , node . getId ( ) ) ;
info . put ( " name " , node . getName ( ) ) ;
info . put ( " type " , node . getClass ( ) . getSimpleName ( ) ) ;
// 后继节点
List < String > nextNodeIds = node . getOutgoingFlows ( )
. stream ( ) . map ( SequenceFlow : : getTargetRef ) . collect ( Collectors . toList ( ) ) ;
info . put ( " next " , nextNodeIds ) ;
// ⭐ 添加扩展属性 extensionElements ⭐
info . put ( " extensionElements " , parseExtensionElements ( node . getExtensionElements ( ) ) ) ;
result . add ( info ) ;
// BFS 的推进逻辑
for ( String nextId : nextNodeIds ) {
FlowElement nextElement = process . getFlowElement ( nextId ) ;
if ( nextElement instanceof FlowNode nextNode ) {
queue . add ( nextNode ) ;
}
// 扩展属性
if ( node . getExtensionElements ( ) ! = null ) {
List < ExtensionElement > extList = node . getExtensionElements ( ) . get ( FlowableConfig . EXECUTECONFIG ) ;
if ( extList ! = null & & ! extList . isEmpty ( ) ) {
info . setExecuteConfig ( extList . get ( 0 ) . getElementText ( ) ) ;
}
}
return result ;
return info ;
}
// 将 extensionElements 转成可阅读的 map
@@ -218,115 +193,181 @@ public class ProcessService {
}
/**
* 查询流程的 所有节点状态
*
* @param processInstanceId
* @return
* 查询流程实例及 所有节点的详细状态(返回结构化 DTO)
*/
public Map < String , Object > getProcessAndNodeDetailByInstanceId ( String processInstanceId ) {
return buildProcessNodeDetail ( processInstanceId ) ;
public SdmResponse < ProcessInstanceDetailResponse > getProcessAndNodeDetailByInstanceIdV2 ( String processInstanceId ) {
ProcessInstanceInfo processInfo = buildProcessInstanceInfo ( processInstanceId ) ;
List < NodeDetailInfo > nodes = buildNodeDetails ( processInstanceId , processInfo . getProcessDefinitionId ( ) ) ;
ProcessInstanceDetailResponse response = new ProcessInstanceDetailResponse ( ) ;
response . setProcessInfo ( processInfo ) ;
response . setNodes ( nodes ) ;
return SdmResponse . success ( response ) ;
}
/**
* 根据流程实例ID构建流程节点状态( 支持 UserTask 任务信息和扩展属性)
*/
private Map < String , Object > buildProcessNodeDetail ( String processInstanceId ) {
// 构建流程实例信息
private ProcessInstanceInfo buildProcessInstanceInfo ( String processInstanceId ) {
HistoricProcessInstance historicInstance = historyService . createHistoricProcessInstanceQuery ( )
. processInstanceId ( processInstanceId )
. singleResult ( ) ;
Map < String , Object > result = new HashMap < > ( ) ;
// 1. 查询流程实例历史信息
HistoricProcessInstance processInstance =
historyService . createHistoricProcessInstanceQuery ( )
. processInstanceId ( processInstanceId )
. singleResult ( ) ;
if ( processInstance = = null ) {
if ( historicInstance = = null ) {
throw new RuntimeException ( " 流程实例不存在: " + processInstanceId ) ;
}
String processDefinitionId = processInstance . getProcessDefinitionId ( ) ;
// 2. 获取 BPMN 模型
BpmnModel model = repositoryService . getBpmnModel ( processDefinitionId ) ;
Process process = model . getMainProcess ( ) ;
Collection < FlowElement > flowElements = process . getFlowElements ( ) ;
// 3. 查询正在执行的节点
// 判断流程是否正在运行中
boolean isRunning = runtimeService . createProcessInstanceQuery ( )
. processInstanceId ( processInstanceId )
. singleResul t( ) ! = null ;
. coun t( ) > 0 ;
List < String > activeIds ;
Map < String , org . flowable . task . api . Task > activeTaskMap = new HashMap < > ( ) ;
if ( isRunning ) {
activeIds = runtimeService . g etActiveActivityIds ( processInstanceId ) ;
ProcessInstanceInfo info = new ProcessInstanceInfo ( ) ;
info . setProcessInstanceId ( historicInstance . getId ( ) ) ;
info . setProcessDefinitionId ( h istoricInstance . getProcessDefinitionId ( ) ) ;
info . s etBusinessKey ( historicInstance . getBusinessKey ( ) ) ;
info . setStartTime ( historicInstance . getStartTime ( ) ) ;
info . setEndTime ( historicInstance . getEndTime ( ) ) ; // 可能为 null
// 查询当前活跃任务( UserTask)
List < org . flowable . task . api . Task > tasks = taskService . createTaskQuery ( )
. processInstanceId ( processInstanceId )
. list ( ) ;
activeTaskMap = tasks . stream ( ) . collect ( Collectors . toMap ( org . flowable . task . api . Task : : getTaskDefinitionKey , task - > task ) ) ;
} else {
activeIds = Collections . emptyList ( ) ;
Long duration = historicInstance . getDurationInMillis ( ) ;
if ( duration = = null & & historicInstance . getStartTime ( ) ! = null & & isRunning ) {
duration = System . currentTimeMillis ( ) - historicInstance . getStartTime ( ) . getTime ( ) ;
}
info . setDurationInMillis ( duration ) ;
info . setDurationFormatted ( duration ! = null ? formatDuration ( duration ) : null ) ;
info . setStatus ( isRunning ? " running " : " completed " ) ;
// 4. 查询已完成节点
List < HistoricActivityInstance > finished =
historyService . createHistoricActivityInstanceQuery ( )
. processInstanceId ( processInstanceId )
. finished ( )
. list ( ) ;
return info ;
}
Set < String > finishedIds = finished . stream ( )
. map ( H istoricActivityInstance : : getActivity Id)
. collect ( Collectors . toSet ( ) ) ;
//构建节点列表
private L ist< NodeDetailInfo > buildNodeDetails ( String processInstanceId , String processDefinition Id) {
List < FlowNode > orderedNodes = getOrderedFlowNodes ( processDefinitionId ) ;
// 5. 组装节点状态
List < Map < String , Object > > nodeList = new ArrayList < > ( ) ;
// 查询运行时 & 历史数据
List < HistoricActivityInstance > historicActivities = historyService . createHistoricActivityInstanceQuery ( )
. processInstanceId ( processInstanceId )
. list ( ) ;
for ( FlowElement element : flowElements ) {
if ( ! ( element instanceof FlowNode ) ) continue ;
Map < String , HistoricActivityInstance > activityMap = historicActivities . stream ( )
. collect ( Collectors . toMap (
HistoricActivityInstance : : getActivityId ,
Function . identity ( ) ,
( a , b ) - > a
) ) ;
FlowNode node = ( FlowNode ) element ;
List < String > activeActivityIds = isProcessRunning ( processInstanceId )
? runtimeService . getActiveActivityIds ( processInstanceId )
: Collections . emptyList ( ) ;
Map < String , Object > nodeInfo = new HashMap < > ( ) ;
nodeInfo . put ( " id " , n ode . getId ( ) ) ;
nodeInfo . put ( " name " , node . getName ( ) ) ;
nodeInfo . put ( " type " , node . getClass ( ) . getSimpleName ( ) ) ;
// 按有序节点构建详情
return orderedN odes . stream ( )
. map ( node - > buildNodeDetailInfo ( node , activityMap , activeActivityIds ) )
. collect ( Collectors . toList ( ) ) ;
}
// 节点状态
if ( activeIds . contains ( node . getId ( ) ) ) {
nodeInfo . put ( " status " , " active " ) ;
// --- 判断流程是否运行中 ---
private boolean isProcessRunning ( String processInstanceId ) {
return runtimeService . createProcessInstanceQuery ( )
. processInstanceId ( processInstanceId )
. count ( ) > 0 ;
}
// 如果是 UserTask, 返回任务信息
if ( node instanceof UserTask & & activeTaskMap . containsKey ( node . getId ( ) ) ) {
org . flowable . task . api . Task task = activeTaskMap . get ( node . getId ( ) ) ;
nodeInfo . put ( " taskId " , task . getId ( ) ) ;
nodeInfo . put ( " taskName " , task . getName ( ) ) ;
nodeInfo . put ( " assignee " , task . getAssignee ( ) ) ; // 可能为 null
}
} else if ( finishedIds . contains ( node . getId ( ) ) ) {
nodeInfo . put ( " status " , " finished " ) ;
} else {
nodeInfo . put ( " status " , " pending " ) ;
}
// --- 构建单个节点详情(返回 NodeDetailInfo) ---
private NodeDetailInfo buildNodeDetailInfo (
FlowNode node ,
Map < String , HistoricActivityInstance > activityMap ,
List < String > activeActivityIds ) {
// 读取扩展属性( executeConfig)
if ( n ode. getExtensionElements ( ) ! = null & & node . getExtensionElements ( ) . containsKey ( FlowableConfig . EXECUTECONFIG ) ) {
List < ExtensionElement > elements = node . getExtensionElements ( ) . get ( FlowableConfig . EXECUTECONFIG ) ;
if ( elements ! = null & & ! elements . isEmpty ( ) ) {
n odeInfo . put ( " executeConfig " , elements . get ( 0 ) . getElementText ( ) ) ;
// 先构建静态结构
N odeStructureInfo base = buildNodeStructureInfo ( node ) ;
// 再填充动态信息
N odeDetailInfo detail = new NodeDetailInfo ( ) ;
detail . setId ( base . getId ( ) ) ;
detail . setName ( base . getName ( ) ) ;
detail . setType ( base . getType ( ) ) ;
detail . setNextNodeIds ( base . getNextNodeIds ( ) ) ;
detail . setExecuteConfig ( base . getExecuteConfig ( ) ) ;
// 动态状态逻辑
String nodeId = node . getId ( ) ;
HistoricActivityInstance historicActivity = activityMap . get ( nodeId ) ;
boolean isActive = activeActivityIds . contains ( nodeId ) ;
boolean isFinished = historicActivity ! = null & & historicActivity . getEndTime ( ) ! = null ;
detail . setStatus ( isFinished ? " finished " : ( isActive ? " active " : " pending " ) ) ;
if ( historicActivity ! = null ) {
detail . setStartTime ( historicActivity . getStartTime ( ) ) ;
if ( isFinished ) {
detail . setEndTime ( historicActivity . getEndTime ( ) ) ;
detail . setDurationInMillis ( historicActivity . getDurationInMillis ( ) ) ;
if ( historicActivity . getDurationInMillis ( ) ! = null ) {
detail . setDurationFormatted ( formatDuration ( historicActivity . getDurationInMillis ( ) ) ) ;
}
}
nodeList . add ( nodeInfo ) ;
}
result . put ( " processInstanceId " , processInstanceId ) ;
result . put ( " nodes " , nodeList ) ;
return result ;
return detail ;
}
// --- 工具方法:格式化耗时(毫秒 → 可读字符串)---
private String formatDuration ( long millis ) {
long seconds = millis / 1000 ;
long mins = seconds / 60 ;
long hours = mins / 60 ;
long days = hours / 24 ;
if ( days > 0 ) return String . format ( " %dd %dh %dm %ds " , days , hours % 24 , mins % 60 , seconds % 60 ) ;
if ( hours > 0 ) return String . format ( " %dh %dm %ds " , hours , mins % 60 , seconds % 60 ) ;
if ( mins > 0 ) return String . format ( " %dm %ds " , mins , seconds % 60 ) ;
return String . format ( " %ds " , seconds ) ;
}
/**
* 根据流程定义 ID, 按流程执行顺序( BFS) 返回所有可达的 FlowNode
*/
private List < FlowNode > getOrderedFlowNodes ( String processDefinitionId ) {
BpmnModel bpmnModel = repositoryService . getBpmnModel ( processDefinitionId ) ;
if ( bpmnModel = = null | | bpmnModel . getMainProcess ( ) = = null ) {
return Collections . emptyList ( ) ;
}
Process process = bpmnModel . getMainProcess ( ) ;
// 找开始事件
StartEvent startEvent = process . findFlowElementsOfType ( StartEvent . class , false )
. stream ( )
. findFirst ( )
. orElse ( null ) ;
if ( startEvent = = null ) {
return Collections . emptyList ( ) ;
}
// BFS 遍历
List < FlowNode > orderedNodes = new ArrayList < > ( ) ;
Queue < FlowNode > queue = new LinkedList < > ( ) ;
Set < String > visited = new HashSet < > ( ) ;
queue . offer ( startEvent ) ;
visited . add ( startEvent . getId ( ) ) ;
while ( ! queue . isEmpty ( ) ) {
FlowNode current = queue . poll ( ) ;
orderedNodes . add ( current ) ;
// 对 outgoingFlows 排序可选(保证并行分支顺序稳定)
current . getOutgoingFlows ( ) . stream ( )
. sorted ( Comparator . comparing ( SequenceFlow : : getId ) ) // 可选:提升顺序稳定性
. forEach ( flow - > {
String targetRef = flow . getTargetRef ( ) ;
FlowElement element = process . getFlowElement ( targetRef ) ;
if ( element instanceof FlowNode & & visited . add ( targetRef ) ) {
queue . offer ( ( FlowNode ) element ) ;
}
} ) ;
}
return orderedNodes ;
}
public void continueServiceTask ( @RequestBody CompleteTaskReq req ) {