【功能优化】工作流:BPMN 流程图高亮的计算,切换到后端为主

This commit is contained in:
YunaiV 2024-10-12 19:39:13 +08:00
parent f299bf8a36
commit 41eec7806d
15 changed files with 134 additions and 94 deletions

View File

@ -1,11 +0,0 @@
-- ----------------------------
-- 流程抄送表新加流程活动编号
-- ----------------------------
ALTER TABLE `pro-test`.`bpm_process_instance_copy`
ADD COLUMN `activity_id` varchar(64) NULL COMMENT '流程活动编号' AFTER `category`,
MODIFY COLUMN `task_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '任务编号' AFTER `category`;
ALTER TABLE `pro-test`.`bpm_process_definition_info`
ADD COLUMN `model_type` tinyint NOT NULL DEFAULT 10 COMMENT '流程模型的类型' AFTER `model_id`,
ADD COLUMN `simple_model` json NULL COMMENT 'SIMPLE 设计器模型数据' AFTER `form_custom_view_path`,
ADD COLUMN `visible` bit(1) NOT NULL DEFAULT 1 COMMENT '是否可见' AFTER `simple_model`;

View File

@ -38,6 +38,10 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
return ARRAYS; return ARRAYS;
} }
public static boolean isRejectStatus(Integer status) {
return REJECT.getStatus().equals(status);
}
public static boolean isProcessEndStatus(Integer status) { public static boolean isProcessEndStatus(Integer status) {
return ObjectUtils.equalsAny(status, return ObjectUtils.equalsAny(status,
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus()); APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());

View File

@ -45,6 +45,10 @@ public enum BpmTaskStatusEnum {
*/ */
private final String name; private final String name;
public static boolean isRejectStatus(Integer status) {
return REJECT.getStatus().equals(status);
}
/** /**
* 判断该状态是否已经处于 End 最终状态 * 判断该状态是否已经处于 End 最终状态
* <p> * <p>

View File

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert;
import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 流程活动实例")
@RestController
@RequestMapping("/bpm/activity")
@Validated
public class BpmActivityController {
@Resource
private BpmActivityService activityService;
@GetMapping("/list")
@Operation(summary = "生成指定流程实例的高亮流程图",
description = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
@Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<List<BpmActivityRespVO>> getActivityList(
@RequestParam("processInstanceId") String processInstanceId) {
return success(BpmActivityConvert.INSTANCE.convertList(activityService.getActivityListByProcessInstanceId(processInstanceId)));
}
}

View File

@ -0,0 +1,5 @@
### 请求 /bpm/process-instance/get-bpmn 接口 => 成功
GET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3
Content-Type: application/json
tenant-id: 1
Authorization: Bearer {{token}}

View File

@ -173,4 +173,11 @@ public class BpmProcessInstanceController {
return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO));
} }
@GetMapping("/get-bpmn-model-view")
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
@Parameter(name = "id", description = "流程实例的编号", required = true)
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) {
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
}
} }

View File

@ -121,7 +121,7 @@ public class BpmTaskController {
@PreAuthorize("@ss.hasPermission('bpm:task:query')") @PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId( public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
@RequestParam("processInstanceId") String processInstanceId) { @RequestParam("processInstanceId") String processInstanceId) {
List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId); List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true);
if (CollUtil.isEmpty(taskList)) { if (CollUtil.isEmpty(taskList)) {
return success(Collections.emptyList()); return success(Collections.emptyList());
} }

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
import java.util.Set;
@Schema(description = "管理后台 - 流程示例的 BPMN 视图 Response VO")
@Data
public class BpmProcessInstanceBpmnModelViewRespVO {
// ========== 基本信息 ==========
@Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED)
private BpmProcessInstanceRespVO processInstance;
@Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<BpmTaskRespVO> tasks;
@Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED)
private String bpmnXml;
// ========== 进度信息 ==========
@Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> unfinishedTaskActivityIds; // 只包括 UserTask
@Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> finishedTaskActivityIds; // 包括 UserTaskGateway 不包括 SequenceFlow
@Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> finishedSequenceFlowActivityIds; // 只包括 SequenceFlow
@Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> rejectedTaskActivityIds; // 只包括 UserTask
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.bpm.convert.task;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
import org.flowable.engine.history.HistoricActivityInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* BPM 活动 Convert
*
* @author 芋道源码
*/
@Mapper(uses = DateUtils.class)
public interface BpmActivityConvert {
BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
@Mappings({
@Mapping(source = "activityId", target = "key"),
@Mapping(source = "activityType", target = "type")
})
BpmActivityRespVO convert(HistoricActivityInstance bean);
}

View File

@ -1,23 +1,29 @@
package cn.iocoder.yudao.module.bpm.convert.task; package cn.iocoder.yudao.module.bpm.convert.task;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceStatusEvent; import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task; import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget; import org.mapstruct.MappingTarget;
@ -25,6 +31,9 @@ import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/** /**
* 流程实例 Convert * 流程实例 Convert
@ -109,4 +118,49 @@ public interface BpmProcessInstanceConvert {
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId())); .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
} }
default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance,
List<HistoricTaskInstance> taskInstances,
BpmnModel bpmnModel,
Set<String> unfinishedTaskActivityIds,
Set<String> finishedTaskActivityIds,
Set<String> finishedSequenceFlowActivityIds,
Set<String> rejectTaskActivityIds,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO();
// 基本信息
respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o
.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)))
.setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap)));
respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class)
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap))
.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap))));
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
// 进度信息
respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds)
.setFinishedTaskActivityIds(finishedTaskActivityIds)
.setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds)
.setRejectedTaskActivityIds(rejectTaskActivityIds);
return respVO;
}
default BpmProcessInstanceRespVO.User buildUser(String userId,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
if (StrUtil.isBlank(userId)) {
return null;
}
AdminUserRespDTO user = userMap.get(NumberUtils.parseLong(userId));
if (user == null) {
return null;
}
BpmProcessInstanceRespVO.User userVO = BeanUtils.toBean(user, BpmProcessInstanceRespVO.User.class);
DeptRespDTO dept = user.getDeptId() != null ? deptMap.get(user.getDeptId()) : null;
if (dept != null) {
userVO.setDeptName(dept.getName());
}
return userVO;
}
} }

View File

@ -9,7 +9,7 @@ import org.springframework.validation.annotation.Validated;
import java.util.List; import java.util.List;
// TODO 芋艿准备废弃
/** /**
* BPM 活动实例 Service 实现类 * BPM 活动实例 Service 实现类
* *

View File

@ -104,6 +104,14 @@ public interface BpmProcessInstanceService {
*/ */
BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);
/**
* 获取流程实例的 BPMN 模型视图
*
* @param id 流程实例的编号
* @return BPMN 模型视图
*/
BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id);
// ========== Update 写入相关方法 ========== // ========== Update 写入相关方法 ==========
/** /**
@ -157,5 +165,4 @@ public interface BpmProcessInstanceService {
*/ */
void processProcessInstanceCompleted(ProcessInstance instance); void processProcessInstanceCompleted(ProcessInstance instance);
} }

View File

@ -73,9 +73,10 @@ public interface BpmTaskService {
* 获得指定流程实例的流程任务列表包括所有状态的 * 获得指定流程实例的流程任务列表包括所有状态的
* *
* @param processInstanceId 流程实例的编号 * @param processInstanceId 流程实例的编号
* @param asc 是否升序
* @return 流程任务列表 * @return 流程任务列表
*/ */
List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId); List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc);
/** /**
* 获取任务 * 获取任务

View File

@ -170,16 +170,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
} }
@Override @Override
public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId) { public List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) {
List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery() HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
.includeTaskLocalVariables() .includeTaskLocalVariables()
.processInstanceId(processInstanceId) .processInstanceId(processInstanceId);
.orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序 if (Boolean.TRUE.equals(asc)) {
.list(); query.orderByHistoricTaskInstanceStartTime().asc();
if (CollUtil.isEmpty(tasks)) { } else {
return Collections.emptyList(); query.orderByHistoricTaskInstanceStartTime().desc();
} }
return tasks; return query.list();
} }
/** /**