diff --git a/common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java b/common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java index 47baa3c..7f84427 100644 --- a/common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java +++ b/common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java @@ -3,8 +3,10 @@ package apelet.common.flow.online.service.impl; import apelet.common.core.annotation.MyDataSource; import apelet.common.core.constant.ApplicationConstant; import apelet.common.flow.base.service.BaseFlowOnlineService; +import apelet.common.flow.model.FlowEntry; import apelet.common.flow.model.FlowWorkOrder; import apelet.common.flow.online.object.TransactionalFlowBusinessData; +import apelet.common.flow.service.FlowEntryService; import apelet.common.flow.util.FlowCustomExtFactory; import apelet.common.generator.model.OnlFormHead; import apelet.common.generator.service.IOnlFormHeadService; @@ -39,6 +41,8 @@ public class FlowOnlineBusinessServiceImpl implements BaseFlowOnlineService { @Autowired private FlowCustomExtFactory flowCustomExtFactory; @Autowired + private FlowEntryService flowEntryService; + @Autowired private OnlineTableService onlineTableService; @Autowired private OnlinePageService onlinePageService; @@ -113,15 +117,47 @@ public class FlowOnlineBusinessServiceImpl implements BaseFlowOnlineService { } @Override - public String getBusinessData(FlowWorkOrder workOrder) { - OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(workOrder.getOnlineTableId()); - if (onlineTable == null || workOrder.getBusinessKey() == null) { + public String getBusinessData(FlowWorkOrder workOrder, FlowEntry flowEntry) { + // 优先使用工单上的 onlineTableId;部分在线表单启动路径不会回填该字段,此时通过流程绑定的在线表单 pageId 解析主表。 + OnlineTable onlineTable = this.resolveMasterTable(workOrder, flowEntry); + if (onlineTable == null || workOrder == null || workOrder.getBusinessKey() == null) { return null; } Map dataMap = onlineOperationService.getMasterData(onlineTable, null, null, workOrder.getBusinessKey()); return dataMap == null ? null : JSON.toJSONString(dataMap); } + /** + * 解析在线表单工作流的主表。优先取工单上的 onlineTableId,为空时通过 FlowEntry.pageId → 页面关联数据源的主表Id 兜底。 + * 注意:OnlinePage.masterTableId 是表单元数据(OnlFormHead)的Id,不能直接作为 OnlineTable 的tableId 使用。 + * + * @param workOrder 流程工单对象。 + * @return 在线表单主表,无法解析返回null。 + */ + private OnlineTable resolveMasterTable(FlowWorkOrder workOrder, FlowEntry flowEntry) { + if (workOrder == null && flowEntry == null) { + return null; + } + if (workOrder != null) { + if (workOrder.getOnlineTableId() != null) { + return onlineTableService.getOnlineTableFromCache(workOrder.getOnlineTableId()); + } + if (StringUtils.isBlank(workOrder.getProcessDefinitionKey())) { + return null; + } + flowEntry = flowEntryService.getFlowEntryFromCache(workOrder.getProcessDefinitionKey()); + } + if (flowEntry == null || flowEntry.getPageId() == null) { + return null; + } + // 参考 OnlineOperationController#getEntryListByDatasourceId:通过页面关联的数据源解析主表。 + List datasourceList = onlineDatasourceService.getOnlineDatasourceListByPageId(flowEntry.getPageId(), null, null); + if (CollUtil.isEmpty(datasourceList) || datasourceList.get(0).getMasterTableId() == null) { + return null; + } + return onlineTableService.getOnlineTableFromCache(datasourceList.get(0).getMasterTableId()); + } + private void handleTransactionalFlowBusinessData(FlowWorkOrder workOrder, String desc) { TransactionalFlowBusinessData eventData = TransactionalFlowBusinessData.getFromRequestAttribute(); if (eventData != null) { diff --git a/common/common-flow/src/main/java/apelet/common/flow/base/service/BaseFlowOnlineService.java b/common/common-flow/src/main/java/apelet/common/flow/base/service/BaseFlowOnlineService.java index 2788ec2..8271f35 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/base/service/BaseFlowOnlineService.java +++ b/common/common-flow/src/main/java/apelet/common/flow/base/service/BaseFlowOnlineService.java @@ -1,5 +1,6 @@ package apelet.common.flow.base.service; +import apelet.common.flow.model.FlowEntry; import apelet.common.flow.model.FlowWorkOrder; /** @@ -38,9 +39,10 @@ public interface BaseFlowOnlineService { * 获取在线表单工作流的业务主表数据。 * * @param workOrder 工单对象。 + * @param flowEntry 流程节点对象。 * @return JSON格式的业务主表数据,如果无法获取返回null。 */ - default String getBusinessData(FlowWorkOrder workOrder) { + default String getBusinessData(FlowWorkOrder workOrder, FlowEntry flowEntry) { return null; } } diff --git a/common/common-flow/src/main/java/apelet/common/flow/controller/FlowOperationController.java b/common/common-flow/src/main/java/apelet/common/flow/controller/FlowOperationController.java index 3199fe4..5c3f344 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/controller/FlowOperationController.java +++ b/common/common-flow/src/main/java/apelet/common/flow/controller/FlowOperationController.java @@ -212,7 +212,8 @@ public class FlowOperationController { taskInfoVo.setVariableList(JSON.parseArray(flowTaskExt.getVariableListJson(), JSONObject.class)); } FlowWorkOrder workOrder = flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(processInstanceId); - taskInfoVo.setTitle(flowApiService.resolveTitle(flowTaskExt.getTitleTemplate(), processInstanceId, workOrder)); + FlowEntry flowEntry = workOrder == null ? null : flowEntryService.getFlowEntryFromCache(workOrder.getProcessDefinitionKey()); + taskInfoVo.setTitle(flowApiService.resolveTitle(flowTaskExt.getTitleTemplate(), processInstanceId, workOrder, flowEntry)); } return ResponseResult.success(taskInfoVo); } @@ -833,6 +834,18 @@ public class FlowOperationController { flowWorkOrderService.getInList(INSTANCE_ID, instanceIdSet); Map workOrderMap = workOrderList.stream().collect(Collectors.toMap(FlowWorkOrder::getProcessInstanceId, c -> c)); + Set processDefinitionIdSet = taskInstanceList.stream() + .map(HistoricTaskInstance::getProcessDefinitionId).collect(Collectors.toSet()); + List flowEntryPublishList = + flowEntryService.getFlowEntryPublishList(processDefinitionIdSet); + Map flowEntryPublishMap = + flowEntryPublishList.stream().collect(Collectors.toMap(FlowEntryPublish::getProcessDefinitionId, c -> c)); + Set entryIdSet = flowEntryPublishList.stream() + .map(FlowEntryPublish::getEntryId).filter(Objects::nonNull).collect(Collectors.toSet()); + Map flowEntryMap = flowEntryService.listByIds(entryIdSet).stream() + .collect(Collectors.toMap(FlowEntry::getEntryId, e -> e)); + Map flowTaskExtMap = flowTaskExtService.getByProcessDefinitionId(processDefinitionIdSet).stream() + .collect(Collectors.toMap(t -> t.getProcessDefinitionId() + ":" + t.getTaskId(), t -> t, (k1, k2) -> k1)); resultList.forEach(result -> { String instanceId = result.get(INSTANCE_ID).toString(); HistoricProcessInstance instance = instanceMap.get(instanceId); @@ -846,6 +859,17 @@ public class FlowOperationController { FlowWorkOrder flowWorkOrder = workOrderMap.get(instanceId); if (flowWorkOrder != null) { result.put("workOrderCode", flowWorkOrder.getWorkOrderCode()); + String processDefinitionId = result.get("processDefinitionId").toString(); + FlowEntryPublish flowEntryPublish = flowEntryPublishMap.get(processDefinitionId); + FlowTaskExt flowTaskExt = flowTaskExtMap.get( + processDefinitionId + ":" + result.get("taskDefinitionKey")); + if (flowEntryPublish != null && flowTaskExt != null) { + FlowEntry flowEntry = flowEntryMap.get(flowEntryPublish.getEntryId()); + if (flowEntry != null) { + result.put("title", flowApiService.resolveTitle( + flowTaskExt.getTitleTemplate(), instanceId, flowWorkOrder, flowEntry)); + } + } } }); Set taskIdSet = @@ -898,6 +922,34 @@ public class FlowOperationController { flowWorkOrderService.getInList(INSTANCE_ID, instanceIdSet); Map workOrderMap = workOrderList.stream().collect(Collectors.toMap(FlowWorkOrder::getProcessInstanceId, c -> c)); + Set processDefinitionIdSet = pageData.getDataList().stream() + .map(HistoricProcessInstance::getProcessDefinitionId).collect(Collectors.toSet()); + Map flowEntryPublishMap = new HashMap<>(); + Map flowEntryMap = new HashMap<>(); + Map flowTaskExtMap = new HashMap<>(); + // 流程实例列表使用起始节点的标题模板。 + Map initTaskKeyMap = new HashMap<>(); + if (CollUtil.isNotEmpty(processDefinitionIdSet)) { + List flowEntryPublishList = + flowEntryService.getFlowEntryPublishList(processDefinitionIdSet); + flowEntryPublishMap.putAll(flowEntryPublishList.stream() + .collect(Collectors.toMap(FlowEntryPublish::getProcessDefinitionId, c -> c))); + Set entryIdSet = flowEntryPublishList.stream() + .map(FlowEntryPublish::getEntryId).filter(Objects::nonNull).collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(entryIdSet)) { + flowEntryMap.putAll(flowEntryService.listByIds(entryIdSet).stream() + .collect(Collectors.toMap(FlowEntry::getEntryId, e -> e))); + } + for (FlowEntryPublish flowEntryPublish : flowEntryPublishList) { + TaskInfoVo initTaskInfo = StrUtil.isBlank(flowEntryPublish.getInitTaskInfo()) + ? null : JSON.parseObject(flowEntryPublish.getInitTaskInfo(), TaskInfoVo.class); + if (initTaskInfo != null && StrUtil.isNotBlank(initTaskInfo.getTaskKey())) { + initTaskKeyMap.put(flowEntryPublish.getProcessDefinitionId(), initTaskInfo.getTaskKey()); + } + } + flowTaskExtMap.putAll(flowTaskExtService.getByProcessDefinitionId(processDefinitionIdSet).stream() + .collect(Collectors.toMap(t -> t.getProcessDefinitionId() + ":" + t.getTaskId(), t -> t, (k1, k2) -> k1))); + } List> resultList = new LinkedList<>(); pageData.getDataList().forEach(instance -> { Map data = BeanUtil.beanToMap(instance); @@ -910,6 +962,16 @@ public class FlowOperationController { if (workOrder != null) { data.put("workOrderCode", workOrder.getWorkOrderCode()); data.put("flowStatus", workOrder.getFlowStatus()); + FlowEntryPublish flowEntryPublish = flowEntryPublishMap.get(instance.getProcessDefinitionId()); + FlowTaskExt flowTaskExt = flowTaskExtMap.get( + instance.getProcessDefinitionId() + ":" + initTaskKeyMap.get(instance.getProcessDefinitionId())); + if (flowEntryPublish != null && flowTaskExt != null) { + FlowEntry flowEntry = flowEntryMap.get(flowEntryPublish.getEntryId()); + if (flowEntry != null) { + data.put("title", flowApiService.resolveTitle( + flowTaskExt.getTitleTemplate(), instance.getId(), workOrder, flowEntry)); + } + } } resultList.add(data); }); diff --git a/common/common-flow/src/main/java/apelet/common/flow/model/FlowEntry.java b/common/common-flow/src/main/java/apelet/common/flow/model/FlowEntry.java index 71894c4..2b610dc 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/model/FlowEntry.java +++ b/common/common-flow/src/main/java/apelet/common/flow/model/FlowEntry.java @@ -2,6 +2,7 @@ package apelet.common.flow.model; import apelet.common.core.annotation.RelationOneToOne; import apelet.common.core.base.mapper.BaseModelMapper; +import apelet.common.flow.model.constant.FlowBindFormType; import apelet.common.flow.model.constant.FlowEntryStatus; import apelet.common.flow.vo.FlowEntryVo; import com.baomidou.mybatisplus.annotation.TableField; @@ -93,6 +94,7 @@ public class FlowEntry { /** * 绑定表单类型。 + * @see FlowBindFormType */ @TableField(value = "bind_form_type") private Integer bindFormType; diff --git a/common/common-flow/src/main/java/apelet/common/flow/service/FlowApiService.java b/common/common-flow/src/main/java/apelet/common/flow/service/FlowApiService.java index b57d856..a5bd813 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/service/FlowApiService.java +++ b/common/common-flow/src/main/java/apelet/common/flow/service/FlowApiService.java @@ -1,6 +1,7 @@ package apelet.common.flow.service; import apelet.common.core.object.*; +import apelet.common.flow.model.FlowEntry; import apelet.common.flow.model.FlowTaskComment; import apelet.common.flow.model.FlowTaskExt; import apelet.common.flow.model.FlowWorkOrder; @@ -643,7 +644,8 @@ public interface FlowApiService { * @param titleTemplate 节点配置的标题模板,为空时使用默认标题。 * @param processInstanceId 流程实例Id。 * @param workOrder 流程工单对象。 + * @param flowEntry 流程对象,用于判断是否为在线表单(bindFormType=0)。 * @return 解析后的标题,非在线表单工作流返回null。 */ - String resolveTitle(String titleTemplate, String processInstanceId, FlowWorkOrder workOrder); + String resolveTitle(String titleTemplate, String processInstanceId, FlowWorkOrder workOrder, FlowEntry flowEntry); } diff --git a/common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowApiServiceImpl.java b/common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowApiServiceImpl.java index b94b2f9..89af9d2 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowApiServiceImpl.java +++ b/common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowApiServiceImpl.java @@ -16,6 +16,7 @@ import apelet.common.flow.constant.FlowConstant; import apelet.common.flow.constant.FlowTaskStatus; import apelet.common.flow.exception.FlowOperationException; import apelet.common.flow.model.*; +import apelet.common.flow.model.constant.FlowBindFormType; import apelet.common.flow.object.*; import apelet.common.flow.service.*; import apelet.common.flow.util.BaseFlowIdentityExtHelper; @@ -789,6 +790,11 @@ public class FlowApiServiceImpl implements FlowApiService { flowEntryService.getFlowEntryPublishList(processDefinitionIdSet); Map flowEntryPublishMap = flowEntryPublishList.stream().collect(Collectors.toMap(FlowEntryPublish::getProcessDefinitionId, c -> c)); + // 批量加载流程对象,用于判断在线表单(bindFormType=0) + Set entryIdSet = flowEntryPublishList.stream() + .map(FlowEntryPublish::getEntryId).filter(Objects::nonNull).collect(Collectors.toSet()); + Map flowEntryMap = flowEntryService.listByIds(entryIdSet).stream() + .collect(Collectors.toMap(FlowEntry::getEntryId, e -> e)); List instanceList = this.getProcessInstanceList(procInstanceIdSet); Map instanceMap = instanceList.stream().collect(Collectors.toMap(ProcessInstance::getId, c -> c)); @@ -825,10 +831,11 @@ public class FlowApiServiceImpl implements FlowApiService { if (flowWorkOrder != null) { flowTaskVo.setIsDraft(flowWorkOrder.getFlowStatus().equals(FlowTaskStatus.DRAFT)); flowTaskVo.setWorkOrderCode(flowWorkOrder.getWorkOrderCode()); - FlowTaskExt flowTaskExt = flowTaskExtMap.get(task.getProcessDefinitionId() + ":" + task.getTaskDefinitionKey()); - if (flowTaskExt != null) { - flowTaskVo.setTitle(this.resolveTitle(flowTaskExt.getTitleTemplate(), task.getProcessInstanceId(), flowWorkOrder)); - } + } + FlowTaskExt flowTaskExt = flowTaskExtMap.get(task.getProcessDefinitionId() + ":" + task.getTaskDefinitionKey()); + FlowEntry flowEntry = flowEntryMap.get(flowEntryPublishMap.get(task.getProcessDefinitionId()).getEntryId()); + if (flowTaskExt != null && flowEntry != null) { + flowTaskVo.setTitle(this.resolveTitle(flowTaskExt.getTitleTemplate(), task.getProcessInstanceId(), flowWorkOrder, flowEntry)); } flowTaskVoList.add(flowTaskVo); } @@ -2456,13 +2463,13 @@ public class FlowApiServiceImpl implements FlowApiService { } @Override - public String resolveTitle(String titleTemplate, String processInstanceId, FlowWorkOrder workOrder) { - // 非在线表单(路由表单)工作流不支持自定义标题。 - if (workOrder == null || workOrder.getOnlineTableId() == null) { + public String resolveTitle(String titleTemplate, String processInstanceId, FlowWorkOrder workOrder, FlowEntry flowEntry) { + // 在线表单判断以流程绑定的表单类型为准(bindFormType=0),不能依赖工单上的 onlineTableId(部分在线表单启动路径不会回填该字段)。 + if (!ObjectUtil.equal(flowEntry.getBindFormType(), FlowBindFormType.ONLINE_FORM)) { return null; } if (StrUtil.isBlank(titleTemplate)) { - titleTemplate = this.buildDefaultTitle(workOrder); + titleTemplate = this.buildDefaultTitle(flowEntry); } String result = titleTemplate; // 1. Flowable 持久化变量: ${initiator}, ${startUserName}, ${submitUser} @@ -2470,14 +2477,24 @@ public class FlowApiServiceImpl implements FlowApiService { for (String varName : varNames) { String value = null; if (StrUtil.isNotBlank(processInstanceId)) { - Object v = runtimeService.getVariable(processInstanceId, varName); + Object v = null; + try { + v = runtimeService.getVariable(processInstanceId, varName); + } catch (Exception e) { + // 流程实例已结束,运行时变量已被清理,回退到历史变量查询。 + log.warn("查询流程实例运行时变量失败,回退到历史变量查询。processInstanceId [{}], varName [{}]", + processInstanceId, varName); + } + if (v == null) { + v = this.getHistoricProcessInstanceVariable(processInstanceId, varName); + } if (v != null) { value = v.toString(); } } - if (value == null && workOrder.getBusinessKey() != null) { + if (value == null) { // 2. 业务数据: ${number}, ${amount} 等在线表单字段。 - String bizData = flowCustomExtFactory.getOnlineBusinessDataExtHelper().getBusinessData(workOrder); + String bizData = flowCustomExtFactory.getOnlineBusinessDataExtHelper().getBusinessData(workOrder, flowEntry); if (StrUtil.isNotBlank(bizData)) { JSONObject biz = JSON.parseObject(bizData); value = biz.getString(varName); @@ -2490,14 +2507,11 @@ public class FlowApiServiceImpl implements FlowApiService { return result; } - private String buildDefaultTitle(FlowWorkOrder workOrder) { + private String buildDefaultTitle(FlowEntry flowEntry) { String tableName = null; - if (workOrder.getOnlineTableId() != null) { + if (flowEntry != null && flowEntry.getPageId() != null) { // 在线表单:FlowEntry.pageId → OnlinePage.masterTableName - FlowEntry flowEntry = flowEntryService.getFlowEntryFromCache(workOrder.getProcessDefinitionKey()); - if (flowEntry != null && flowEntry.getPageId() != null) { - tableName = flowCustomExtFactory.getOnlineBusinessDataExtHelper().getTableTxt(flowEntry.getPageId()); - } + tableName = flowCustomExtFactory.getOnlineBusinessDataExtHelper().getTableTxt(flowEntry.getPageId()); } if (StrUtil.isBlank(tableName)) { tableName = "业务"; diff --git a/common/common-flow/src/main/java/apelet/common/flow/util/BaseOnlineBusinessDataExtHelper.java b/common/common-flow/src/main/java/apelet/common/flow/util/BaseOnlineBusinessDataExtHelper.java index 0332516..14a4da3 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/util/BaseOnlineBusinessDataExtHelper.java +++ b/common/common-flow/src/main/java/apelet/common/flow/util/BaseOnlineBusinessDataExtHelper.java @@ -1,6 +1,7 @@ package apelet.common.flow.util; import apelet.common.flow.base.service.BaseFlowOnlineService; +import apelet.common.flow.model.FlowEntry; import apelet.common.flow.model.FlowWorkOrder; import cn.hutool.core.lang.Assert; import lombok.extern.slf4j.Slf4j; @@ -63,9 +64,10 @@ public class BaseOnlineBusinessDataExtHelper { * 获取在线表单工作流的业务主表数据。 * * @param workOrder 工单对象。 + * @param flowEntry 流程节点对象。 * @return JSON格式的业务主表数据,如果无法获取返回null。 */ - public String getBusinessData(FlowWorkOrder workOrder) { - return this.onlineBusinessService == null ? null : onlineBusinessService.getBusinessData(workOrder); + public String getBusinessData(FlowWorkOrder workOrder, FlowEntry flowEntry) { + return this.onlineBusinessService == null ? null : onlineBusinessService.getBusinessData(workOrder, flowEntry); } }