Bläddra i källkod

仿钉钉流程设计- 增加流程进度接口第一版 (用于查询审批记录)

jason 10 månader sedan
förälder
incheckning
f03e26bc86
26 ändrade filer med 589 tillägg och 38 borttagningar
  1. 6 1
      sql/mysql/bpm_update.sql
  2. 67 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmProcessNodeProgressEnum.java
  3. 6 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
  4. 2 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmActivityController.java
  5. 11 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
  6. 60 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceProgressRespVO.java
  7. 8 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
  8. 5 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java
  9. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
  10. 13 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java
  11. 10 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategy.java
  12. 9 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java
  13. 11 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java
  14. 5 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategy.java
  15. 126 10
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
  16. 5 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
  17. 2 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
  18. 6 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
  19. 29 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityService.java
  20. 167 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java
  21. 9 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java
  22. 8 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
  23. 10 4
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
  24. 0 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
  25. 8 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
  26. 5 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

+ 6 - 1
sql/mysql/bpm_update.sql

@@ -3,4 +3,9 @@
 -- ----------------------------
 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`;
+    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`;

+ 67 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmProcessNodeProgressEnum.java

@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.bpm.enums.definition;
+
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 流程节点进度的枚举
+ *
+ * @author jason
+ */
+@Getter
+@AllArgsConstructor
+public enum BpmProcessNodeProgressEnum {
+    // 0 未开始
+    NOT_START(0,"未开始"),
+    // 1 ~ 20 进行中
+    RUNNING(1, "进行中"),  // 节点的进行
+    // 特殊的进行中状态
+    USER_TASK_DELEGATE(10, "委派中"), // 审批节点
+    USER_TASK_APPROVING(11, "向后加签审批通过中"), //向后加签 审批通过中.
+    USER_TASK_WAIT(12, "待审批"), // 一般用于先前加签
+
+    // 30 ~ 50 已经结束
+    // 30 ~ 40 审批节点的结束状态
+    USER_TASK_APPROVE(30, "审批通过"), // 审批节点
+    USER_TASK_REJECT(31, "审批不通过"), // 审批节点
+    USER_TASK_RETURN(32, "已退回"), // 审批节点
+    USER_TASK_CANCEL(34, "已取消"), // 审批节点
+    // 40 ~ 50 一般节点的接榫状态
+    FINISHED(41, "已结束"), // 一般节点的节点的结束状态
+    SKIP(42, "跳过"); // 未执行,跳过的节点
+
+    private final Integer status;
+    private final String name;
+
+    public static Integer convertBpmnTaskStatus(Integer taskStatus) {
+        Integer convertStatus = null;
+        if (BpmTaskStatusEnum.RUNNING.getStatus().equals(taskStatus)) {
+            convertStatus =  RUNNING.getStatus();
+        } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(taskStatus)) {
+            convertStatus = USER_TASK_REJECT.getStatus();
+        } else if( BpmTaskStatusEnum.APPROVE.getStatus().equals(taskStatus) ) {
+            convertStatus = USER_TASK_APPROVE.getStatus();
+        } else if (BpmTaskStatusEnum.DELEGATE.getStatus().equals(taskStatus)) {
+            convertStatus = USER_TASK_DELEGATE.getStatus();
+        } else if (BpmTaskStatusEnum.APPROVING.getStatus().equals(taskStatus)) {
+            convertStatus = USER_TASK_APPROVE.getStatus();
+        } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(taskStatus)) {
+            convertStatus = USER_TASK_CANCEL.getStatus();
+        } else if (BpmTaskStatusEnum.WAIT.getStatus().equals(taskStatus)) {
+            convertStatus = USER_TASK_WAIT.getStatus();
+        }
+        return convertStatus;
+    }
+
+    /**
+     * 判断用户节点是不是未通过
+     *
+     * @param status 状态
+     */
+    public static boolean isUserTaskNotApproved(Integer status) {
+        return ObjectUtils.equalsAny(status,
+                USER_TASK_REJECT.getStatus(), USER_TASK_RETURN.getStatus(), USER_TASK_CANCEL.getStatus());
+    }
+}

+ 6 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.bpm.enums.task;
 
 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -36,4 +37,9 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
         return ARRAYS;
     }
 
+    public static boolean isProcessEndStatus(Integer status) {
+        return ObjectUtils.equalsAny(status,
+                APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
+    }
+
 }

+ 2 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmActivityController.java

@@ -2,6 +2,7 @@ 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;
@@ -34,6 +35,6 @@ public class BpmActivityController {
     @PreAuthorize("@ss.hasPermission('bpm:task:query')")
     public CommonResult<List<BpmActivityRespVO>> getActivityList(
             @RequestParam("processInstanceId") String processInstanceId) {
-        return success(activityService.getActivityListByProcessInstanceId(processInstanceId));
+        return success(BpmActivityConvert.INSTANCE.convertList(activityService.getActivityListByProcessInstanceId(processInstanceId)));
     }
 }

+ 11 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java

@@ -47,6 +47,7 @@ public class BpmProcessInstanceController {
     private BpmProcessInstanceService processInstanceService;
     @Resource
     private BpmTaskService taskService;
+
     @Resource
     private BpmProcessDefinitionService processDefinitionService;
     @Resource
@@ -158,11 +159,19 @@ public class BpmProcessInstanceController {
     }
 
     @GetMapping("/get-form-fields-permission")
-    @Operation(summary = "获得流程实例表单字段权限", description = "在【我的流程】菜单中,进行调用")
+    @Operation(summary = "获得流程实例表单字段权限")
     @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
     public CommonResult<Map<String, String>> getProcessInstanceFormFieldsPermission(
-            @Valid BpmProcessInstanceFormFieldsPermissionReqVO reqVO){
+            @Valid BpmProcessInstanceFormFieldsPermissionReqVO reqVO) {
         return success(processInstanceService.getProcessInstanceFormFieldsPermission(reqVO));
     }
 
+    @GetMapping("/get-progress")
+    @Operation(summary = "获得流程实例的进度")
+    @Parameter(name = "id", description = "流程实例的编号", required = true)
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+    public CommonResult<BpmProcessInstanceProgressRespVO> getProcessInstanceProgress(@RequestParam("id") String id) {
+        return success(processInstanceService.getProcessInstanceProgress(id));
+    }
+
 }

+ 60 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceProgressRespVO.java

@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+
+@Schema(description = "管理后台 - 流程实例的进度 Response VO")
+@Data
+public class BpmProcessInstanceProgressRespVO {
+
+    @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
+
+    private List<ProcessNodeProgress> nodeProgressList;
+
+    @Schema(description = "节点进度信息")
+    @Data
+    public static class ProcessNodeProgress {
+
+        @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode")
+        private String id;  // Bpmn XML 节点 Id
+        @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人")
+        private String name;
+        private String displayText;
+        @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举
+        @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+        private Integer status; // 参见 BpmProcessNodeProgressEnum 枚举
+        @Schema(description = "节点的开始时间")
+        private LocalDateTime startTime;
+        @Schema(description = "节点的结束时间")
+        private LocalDateTime endTime;
+        @Schema(description = "用户列表")
+        private List<User> userList;
+        @Schema(description = "分支节点")
+        private List<ProcessNodeProgress> branchNodes;  // 有且仅有条件、并行、包容节点才会有分支节点
+
+        // TODO 用户意见,评论
+
+    }
+
+    @Schema(description = "用户信息")
+    @Data
+    public static class User {
+
+        @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        private Long id;
+        @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+        private String nickname;
+        @Schema(description = "用户头像", example = "芋艿")
+        private String avatar;
+        @Schema(description = "是否已处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+        private Boolean processed;
+        @Schema(description = "用户任务的处理状态", example = "1")
+        private Integer userTaskStatus;
+    }
+}

+ 8 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -48,7 +49,7 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
     /**
      * 流程模型的类型
      *
-     * 枚举 {@link BpmModelFormTypeEnum}
+     * 枚举 {@link BpmModelTypeEnum}
      */
     private Integer modelType;
 
@@ -105,6 +106,12 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
      */
     private String formCustomViewPath;
 
+    /**
+     * SIMPLE 设计器模型数据 json 格式
+     *
+     * 目的:当使用仿钉钉设计器时。流程模型发布的时候,需要保存流程模型设计器的快照数据。
+     */
+    private String simpleModel;
     /**
      * 是否可见
      *

+ 5 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java

@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInstanceCopyDO> {
 
@@ -18,4 +20,7 @@ public interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInst
                 .orderByDesc(BpmProcessInstanceCopyDO::getId));
     }
 
+    default List<BpmProcessInstanceCopyDO> selectListByProcInstIdAndActId(String processInstanceId, String activityId) {
+        return selectList(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId, BpmProcessInstanceCopyDO::getActivityId, activityId);
+    }
 }

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java

@@ -150,7 +150,7 @@ public class BpmTaskCandidateInvoker {
         assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
     }
 
-    private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
+    public BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
         BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
         Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
         BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);

+ 13 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import org.flowable.engine.delegate.DelegateExecution;
 
+import java.util.Collections;
 import java.util.Set;
 
 /**
@@ -36,6 +37,18 @@ public interface BpmTaskCandidateStrategy {
      */
     Set<Long> calculateUsers(DelegateExecution execution, String param);
 
+
+    /**
+     * 基于流程实例,获得任务的候选用户们。 用于获取未执行节点的候选用户们
+     *
+     * @param processInstanceId 流程实例
+     * @param param 节点的参数
+     * @return 用户编号集合
+     */
+    default Set<Long> calculateUsers(String processInstanceId, String param) {
+        return Collections.emptySet();
+    }
+
     /**
      * 是否一定要输入参数
      *

+ 10 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategy.java

@@ -41,6 +41,16 @@ public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrat
 
     @Override
     public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        return calculateUsersByParam(param);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(String processInstanceId, String param) {
+        return calculateUsersByParam(param);
+    }
+
+    private Set<Long> calculateUsersByParam(String param) {
+
         Set<Long> deptIds = StrUtils.splitToLongSet(param);
         List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds);
         return convertSet(users, AdminUserRespDTO::getId);

+ 9 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java

@@ -37,6 +37,15 @@ public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
 
     @Override
     public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        return calculateUsersByParam(param);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(String processInstanceId, String param) {
+       return calculateUsersByParam(param);
+    }
+
+    private  Set<Long> calculateUsersByParam(String param) {
         Set<Long> roleIds = StrUtils.splitToLongSet(param);
         return permissionApi.getUserRoleIdListByRoleIds(roleIds);
     }

+ 11 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java

@@ -35,8 +35,7 @@ public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrate
 
     @Override
     public Set<Long> calculateUsers(DelegateExecution execution, String param) {
-        String startUserId = processInstanceService.getProcessInstance(execution.getProcessInstanceId()).getStartUserId();
-        return SetUtils.asSet(Long.valueOf(startUserId));
+        return getStartUserOfProcessInstance(execution.getProcessInstanceId());
     }
 
     @Override
@@ -44,4 +43,14 @@ public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrate
         return false;
     }
 
+    @Override
+    public Set<Long> calculateUsers(String processInstanceId, String param) {
+        return getStartUserOfProcessInstance(processInstanceId);
+    }
+
+    private Set<Long> getStartUserOfProcessInstance(String processInstanceId) {
+        String startUserId = processInstanceService.getProcessInstance(processInstanceId).getStartUserId();
+        return SetUtils.asSet(Long.valueOf(startUserId));
+    }
+
 }

+ 5 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategy.java

@@ -36,4 +36,9 @@ public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
         return StrUtils.splitToLongSet(param);
     }
 
+    @Override
+    public Set<Long> calculateUsers(String processInstanceId, String param) {
+        return StrUtils.splitToLongSet(param);
+    }
+
 }

+ 126 - 10
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java

@@ -7,24 +7,30 @@ import cn.hutool.core.lang.TypeReference;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.*;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
+import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceProgressRespVO.ProcessNodeProgress;
 import cn.iocoder.yudao.module.bpm.enums.definition.*;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel.SimpleModelConditionGroups;
+import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
 import org.flowable.bpmn.BpmnAutoLayout;
 import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.*;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting;
 import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler;
 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
+import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum.RANDOM;
+import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum.RATIO;
+import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER;
 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP;
 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum.START_USER;
@@ -361,7 +367,7 @@ public class SimpleModelUtils {
     /**
      * 添加 UserTask 用户的审批超时 BoundaryEvent 事件
      *
-     * @param userTask 审批任务
+     * @param userTask       审批任务
      * @param timeoutHandler 超时处理器
      * @return BoundaryEvent 超时事件
      */
@@ -463,7 +469,7 @@ public class SimpleModelUtils {
 
         // 如果不是审批人节点,则直接返回
         addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, StrUtil.toStringOrNull(node.getApproveType()));
-        if (ObjectUtil.notEqual(node.getApproveType(), BpmUserTaskApproveTypeEnum.USER.getType())) {
+        if (ObjectUtil.notEqual(node.getApproveType(), USER.getType())) {
             return userTask;
         }
 
@@ -513,7 +519,7 @@ public class SimpleModelUtils {
 
     private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {
         BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod);
-        if (approveMethodEnum == null || approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) {
+        if (approveMethodEnum == null || approveMethodEnum == RANDOM) {
             return;
         }
         // 添加审批方式的扩展属性
@@ -531,7 +537,7 @@ public class SimpleModelUtils {
             multiInstanceCharacteristics.setSequential(true);
             multiInstanceCharacteristics.setLoopCardinality("1");
             userTask.setLoopCharacteristics(multiInstanceCharacteristics);
-        } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RATIO) {
+        } else if (approveMethodEnum == RATIO) {
             Assert.notNull(approveRatio, "通过比例不能为空");
             multiInstanceCharacteristics.setCompletionCondition(
                     String.format(APPROVE_BY_RATIO_COMPLETE_EXPRESSION, String.format("%.2f", approveRatio / (double) 100)));
@@ -607,9 +613,9 @@ public class SimpleModelUtils {
         userTask.setId(node.getId());
         userTask.setName(node.getName());
         // 人工审批
-        addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType().toString());
+        addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, USER.getType().toString());
         // 候选人策略为发起人自己
-        addCandidateElements(START_USER.getStrategy(),null, userTask);
+        addCandidateElements(START_USER.getStrategy(), null, userTask);
         // 添加表单字段权限属性元素
         addFormFieldsPermission(node.getFieldsPermission(), userTask);
         // 添加操作按钮配置属性元素
@@ -628,4 +634,114 @@ public class SimpleModelUtils {
         return endEvent;
     }
 
+    /**
+     *  遍历简单模型, 构建节点的进度。 TODO 回退节点暂未处理
+     *
+     * @param processInstance 流程实例
+     * @param simpleModel 简单模型
+     * @param historicActivityList 流程实例的活力列表
+     * @param activityInstanceMap 流程实例的活力 Map。 key: activityId
+     * @param nodeProgresses  节点的进度列表
+     * @param returnNodePosition 回退节点的位置。 TODO 处理回退节点,还未处理。还没想好
+     */
+    public static void traverseNodeToBuildNodeProgress(HistoricProcessInstance processInstance, BpmSimpleModelNodeVO simpleModel
+            , List<HistoricActivityInstance> historicActivityList, Map<String, HistoricActivityInstance> activityInstanceMap
+            , List<ProcessNodeProgress> nodeProgresses, List<Integer> returnNodePosition) {
+        // 判断是否有效节点
+        if (!isValidNode(simpleModel)) {
+            return;
+        }
+        buildNodeProgress(processInstance, simpleModel, nodeProgresses, historicActivityList, activityInstanceMap, returnNodePosition);
+        // 如果有“子”节点,则递归处理子节点
+        traverseNodeToBuildNodeProgress(processInstance, simpleModel.getChildNode(), historicActivityList, activityInstanceMap, nodeProgresses, returnNodePosition);
+    }
+
+
+    private static void buildNodeProgress(HistoricProcessInstance processInstance, BpmSimpleModelNodeVO node, List<ProcessNodeProgress> nodeProgresses,
+                                          List<HistoricActivityInstance> historicActivityList, Map<String, HistoricActivityInstance> activityInstanceMap, List<Integer> returnNodePosition) {
+        BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
+        Assert.notNull(nodeType, "模型节点类型不支持");
+
+        ProcessNodeProgress nodeProgress = new ProcessNodeProgress();
+        nodeProgress.setNodeType(nodeType.getType());
+        nodeProgress.setName(node.getName());
+        nodeProgress.setDisplayText(node.getShowText());
+        BpmActivityService activityService = SpringUtils.getBean(BpmActivityService.class);
+        if (!activityInstanceMap.containsKey(node.getId())) { // 说明这些节点没有执行过
+            // 1. 得到流程状态
+            Integer processInstanceStatus = FlowableUtils.getProcessInstanceStatus(processInstance);
+            // 2. 设置节点状态
+            nodeProgress.setStatus(activityService.getNotRunActivityProgressStatus(processInstanceStatus));
+            // 3. 抄送节点, 审批节点设置用户列表
+            if (COPY_NODE.getType().equals(node.getType()) ||
+                    (APPROVE_NODE.getType().equals(node.getType()) && USER.getType().equals(node.getApproveType()))) {
+                nodeProgress.setUserList(activityService.getNotRunActivityUserList(processInstance.getId()
+                        , processInstanceStatus, node.getCandidateStrategy(), node.getCandidateParam()));
+            }
+        } else {
+            nodeProgress.setStatus(BpmProcessNodeProgressEnum.FINISHED.getStatus()); // 默认设置成结束状态
+            HistoricActivityInstance historicActivity = activityInstanceMap.get(node.getId());
+            nodeProgress.setStartTime(DateUtils.of(historicActivity.getStartTime()));
+            nodeProgress.setEndTime(DateUtils.of(historicActivity.getEndTime()));
+            nodeProgress.setId(historicActivity.getId());
+            switch (nodeType) {
+                case START_USER_NODE: { // 发起人节点
+                    nodeProgress.setDisplayText(""); // 发起人节点不需要显示 displayText
+                    // 1. 设置节点的状态
+                    nodeProgress.setStatus(activityService.getHistoricActivityProgressStatus(historicActivity, false, historicActivityList));
+                    // 2. 设置用户信息
+                    nodeProgress.setUserList(activityService.getHistoricActivityUserList(historicActivity, false, historicActivityList));
+                    break;
+                }
+                case APPROVE_NODE: { // 审批节点
+                    if (USER.getType().equals(node.getApproveType())) { // 人工审批
+                        // 1. 判断是否多人审批
+                        boolean isMultiInstance = !RANDOM.getMethod().equals(node.getApproveMethod());
+                        // 2. 设置节点的状态
+                        nodeProgress.setStatus(activityService.getHistoricActivityProgressStatus(historicActivity, isMultiInstance, historicActivityList));
+                        // 3. 设置用户信息
+                        nodeProgress.setUserList(activityService.getHistoricActivityUserList(historicActivity, isMultiInstance, historicActivityList));
+                    } else {
+                        nodeProgress.setStatus(activityService.getHistoricActivityProgressStatus(historicActivity, false, historicActivityList));
+                    }
+                    break;
+                }
+                case COPY_NODE: { // 抄送节点
+                    // 1. 设置节点的状态
+                    nodeProgress.setStatus(activityService.getHistoricActivityProgressStatus(historicActivity, false, historicActivityList));
+                    // 2. 设置用户信息
+                    nodeProgress.setUserList(activityService.getHistoricActivityUserList(historicActivity, false, historicActivityList));
+                    break;
+                }
+
+                default: {
+                    // TODO 其它节点类型的实现
+                }
+            }
+        }
+        // 如果是“分支”节点,
+        if (BpmSimpleModelNodeType.isBranchNode(node.getType())
+                && ArrayUtil.isNotEmpty(node.getConditionNodes())) {
+            // 网关是否执行了, 执行了。只包含运行的分支。 未执行包含所有的分支
+            final boolean executed = activityInstanceMap.containsKey(node.getId());
+            LinkedList<ProcessNodeProgress> branchNodeList = new LinkedList<>();
+            node.getConditionNodes().forEach(item -> {
+                // 如果条件节点执行了。 ACT_HI_ACTINST 表会记录
+                if (executed) {
+                    if (activityInstanceMap.containsKey(item.getId())) {
+                        List<Integer> branchReturnNodePosition = new ArrayList<>();
+                        traverseNodeToBuildNodeProgress(processInstance, item, historicActivityList, activityInstanceMap, branchNodeList, branchReturnNodePosition);
+                        // TODO 处理回退节点
+                    }
+                } else {
+                    List<Integer> branchReturnNodePosition = new ArrayList<>();
+                    traverseNodeToBuildNodeProgress(processInstance, item, historicActivityList, activityInstanceMap, branchNodeList, branchReturnNodePosition);
+                    // TODO 处理回退节点
+                }
+            });
+            nodeProgress.setBranchNodes(branchNodeList);
+        }
+        nodeProgresses.add(nodeProgress);
+    }
+
 }

+ 5 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
@@ -17,7 +18,6 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
 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.SimpleModelUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
@@ -143,9 +143,11 @@ public class BpmModelServiceImpl implements BpmModelService {
         BpmFormDO form = validateFormConfig(metaInfo);
         // 1.4 校验任务分配规则已配置
         taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
+        // 1.5 获取仿钉钉流程设计器模型数据
+        byte[] simpleBytes = getModelSimpleJson(model.getId());
 
         // 2.1 创建流程定义
-        String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, form);
+        String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleBytes, form);
 
         // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
         updateProcessDefinitionSuspended(model.getDeploymentId());
@@ -276,7 +278,7 @@ public class BpmModelServiceImpl implements BpmModelService {
 
     /**
      * 挂起 deploymentId 对应的流程定义
-     *
+     * <p>
      * 注意:这里一个 deploymentId 只关联一个流程定义
      *
      * @param deploymentId 流程发布Id

+ 2 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java

@@ -48,10 +48,11 @@ public interface BpmProcessDefinitionService {
      * @param model 流程模型
      * @param modelMetaInfo 流程模型元信息
      * @param bpmnBytes BPMN XML 字节数组
+     * @param simpleBytes simple model json 字节数组
      * @param form 表单
      * @return 流程编号
      */
-    String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, byte[] bpmnBytes, BpmFormDO form);
+    String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, byte[] bpmnBytes, byte[] simpleBytes, BpmFormDO form);
 
     /**
      * 更新流程定义状态

+ 6 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java

@@ -5,13 +5,13 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
@@ -24,6 +24,7 @@ import org.flowable.engine.repository.ProcessDefinitionQuery;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -106,7 +107,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
 
     @Override
     public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
-                                          byte[] bpmnBytes, BpmFormDO form) {
+                                          byte[] bpmnBytes, byte[] simpleBytes, BpmFormDO form) {
         // 创建 Deployment 部署
         Deployment deploy = repositoryService.createDeployment()
                 .key(model.getKey()).name(model.getName()).category(model.getCategory())
@@ -131,7 +132,9 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
 
         // 插入拓展表
         BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class)
-                .setModelId(model.getId()).setProcessDefinitionId(definition.getId());
+                .setModelId(model.getId()).setProcessDefinitionId(definition.getId()).setModelType(modelMetaInfo.getType())
+                .setSimpleModel(StrUtil.str(simpleBytes, StandardCharsets.UTF_8));
+
         if (form != null) {
             definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());
         }

+ 29 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityService.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.bpm.service.task;
 
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceProgressRespVO;
 import org.flowable.engine.history.HistoricActivityInstance;
 
 import java.util.List;
@@ -18,7 +18,7 @@ public interface BpmActivityService {
      * @param processInstanceId 流程实例的编号
      * @return 活动实例列表
      */
-    List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId);
+    List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId);
 
     /**
      * 获得执行编号对应的活动实例
@@ -28,4 +28,31 @@ public interface BpmActivityService {
      */
     List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId);
 
+    /**
+     * 获取活动的用户列表。 例如:抄送人列表。 审批人列表
+     *
+     * @param historicActivity     活动
+     * @param isMultiInstance      是否多实例 (会签,或签 )
+     * @param historicActivityList 某个流程实例的所有活动列表
+     * @return 用户列表
+     */
+    List<BpmProcessInstanceProgressRespVO.User> getHistoricActivityUserList(HistoricActivityInstance historicActivity,
+                                                                            Boolean isMultiInstance, List<HistoricActivityInstance> historicActivityList);
+
+    /**
+     * 获取活动的进度状态。
+     *
+     * @param historicActivity     活动
+     * @param isMultiInstance      是否多实例 (会签,或签 )
+     * @param historicActivityList 某个流程实例的所有活动列表
+     * @return 活动的进度状态
+     */
+    Integer getHistoricActivityProgressStatus(HistoricActivityInstance historicActivity,
+                                              Boolean isMultiInstance, List<HistoricActivityInstance> historicActivityList);
+
+    Integer getNotRunActivityProgressStatus(Integer processInstanceStatus);
+
+    List<BpmProcessInstanceProgressRespVO.User> getNotRunActivityUserList(String processInstanceId, Integer processInstanceStatus
+            , Integer candidateStrategy, String candidateParam);
+
 }

+ 167 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java

@@ -1,15 +1,33 @@
 package cn.iocoder.yudao.module.bpm.service.task;
 
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
-import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.NumberUtil;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceProgressRespVO.User;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessNodeProgressEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.engine.HistoryService;
 import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import jakarta.annotation.Resource;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessNodeProgressEnum.*;
 
 
 /**
@@ -22,14 +40,31 @@ import java.util.List;
 @Validated
 public class BpmActivityServiceImpl implements BpmActivityService {
 
+    /**
+     * 抄送节点活动类型
+     */
+    private static final String COPY_NODE_ACTIVITY_TYPE = "serviceTask";
+    /**
+     * 审批节点活动类型
+     */
+    private static final String APPROVE_NODE_ACTIVITY_TYPE = "userTask";
+
     @Resource
     private HistoryService historyService;
+    @Resource
+    @Lazy
+    private BpmTaskService bpmTaskService;
+    @Resource
+    private BpmProcessInstanceCopyService bpmProcessInstanceCopyService;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private BpmTaskCandidateInvoker bpmTaskCandidateInvoker;
 
     @Override
-    public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId) {
-        List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
-                .processInstanceId(processInstanceId).list();
-        return BpmActivityConvert.INSTANCE.convertList(activityList);
+    public List<HistoricActivityInstance> getActivityListByProcessInstanceId(String processInstanceId) {
+        return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
+                .orderByHistoricActivityInstanceStartTime().asc().list();
     }
 
     @Override
@@ -37,4 +72,129 @@ public class BpmActivityServiceImpl implements BpmActivityService {
         return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list();
     }
 
+    @Override
+    public List<User> getHistoricActivityUserList(HistoricActivityInstance historicActivity
+            , Boolean isMultiInstance, List<HistoricActivityInstance> historicActivityList) {
+        Assert.notNull(historicActivity, "historicActivity 不能为 null ");
+        List<User> returnUserList = Collections.emptyList();
+        if (COPY_NODE_ACTIVITY_TYPE.equals(historicActivity.getActivityType())) {
+            Set<Long> copyUserIds = bpmProcessInstanceCopyService.getCopyUserIds(historicActivity.getProcessInstanceId(),
+                    historicActivity.getActivityId());
+            List<AdminUserRespDTO> userList = adminUserApi.getUserList(copyUserIds);
+            returnUserList = CollectionUtils.convertList(userList, item -> {
+                User user = BeanUtils.toBean(item, User.class);
+                user.setProcessed(Boolean.TRUE);
+                return user;
+            });
+        } else if (APPROVE_NODE_ACTIVITY_TYPE.equals(historicActivity.getActivityType())) {
+            if (isMultiInstance) {  // 多人 (会签 、 或签) // TODO 依次审批可能要特殊处理一下
+                // 多个任务列表
+                List<HistoricActivityInstance> taskList = CollectionUtils.filterList(historicActivityList,
+                        item -> historicActivity.getActivityId().equals(item.getActivityId()));
+                List<Long> userIds = CollectionUtils.convertList(taskList, item -> NumberUtil.parseLong(item.getAssignee(), null));
+                List<String> taskIds = CollectionUtils.convertList(taskList, HistoricActivityInstance::getTaskId);
+                Map<Long, AdminUserRespDTO> adminUserMap = CollectionUtils.convertMap(adminUserApi.getUserList(userIds), AdminUserRespDTO::getId);
+                Map<String, HistoricTaskInstance> historicTaskInstanceMap = CollectionUtils.convertMap(bpmTaskService.getHistoricTasks(taskIds), HistoricTaskInstance::getId);
+                returnUserList = CollectionUtils.convertList(taskList, item -> {
+                    AdminUserRespDTO adminUser = adminUserMap.get(NumberUtil.parseLong(item.getAssignee(), null));
+                    User user = BeanUtils.toBean(adminUser, User.class);
+                    if (user != null) {
+                        HistoricTaskInstance taskInstance = historicTaskInstanceMap.get(item.getTaskId());
+                        if (taskInstance != null) {
+                            user.setProcessed(taskInstance.getEndTime() != null);
+                            user.setUserTaskStatus(FlowableUtils.getTaskStatus(taskInstance));
+                        }
+                    }
+                    return user;
+                });
+            } else {
+                AdminUserRespDTO adminUserResp = adminUserApi.getUser(Long.valueOf(historicActivity.getAssignee()));
+                if (adminUserResp != null) {
+                    User user = BeanUtils.toBean(adminUserResp, User.class);
+                    // TODO 需要处理加签
+                    // 查询任务状态
+                    HistoricTaskInstance historicTask = bpmTaskService.getHistoricTask(historicActivity.getTaskId());
+                    if (historicTask != null) {
+                        Integer taskStatus = FlowableUtils.getTaskStatus(historicTask);
+                        user.setProcessed(historicTask.getEndTime() != null);
+                        user.setUserTaskStatus(taskStatus);
+                    }
+                    returnUserList = ListUtil.of(user);
+                }
+            }
+        }
+        return returnUserList;
+    }
+
+    @Override
+    public Integer getHistoricActivityProgressStatus(HistoricActivityInstance historicActivity
+            , Boolean isMultiInstance, List<HistoricActivityInstance> historicActivityList) {
+        Assert.notNull(historicActivity, "historicActivity 不能为 null ");
+        Integer progressStatus = null;
+        if (APPROVE_NODE_ACTIVITY_TYPE.equals(historicActivity.getActivityType())) {
+            if (isMultiInstance) { // 多人 (会签 、 或签)
+                // 多个任务列表
+                List<HistoricActivityInstance> taskList = CollectionUtils.filterList(historicActivityList,
+                        item -> historicActivity.getActivityId().equals(item.getActivityId()));
+                List<String> taskIds = CollectionUtils.convertList(taskList, HistoricActivityInstance::getTaskId);
+                Map<String, HistoricTaskInstance> historicTaskMap = CollectionUtils.convertMap(bpmTaskService.getHistoricTasks(taskIds), HistoricTaskInstance::getId);
+                for (HistoricActivityInstance activity : taskList) {
+                    if (activity.getEndTime() == null) {
+                        progressStatus = RUNNING.getStatus();
+                    } else {
+                        HistoricTaskInstance task = historicTaskMap.get(activity.getTaskId());
+                        if (task != null) {
+                            Integer taskStatus = FlowableUtils.getTaskStatus(task);
+                            progressStatus = BpmProcessNodeProgressEnum.convertBpmnTaskStatus(taskStatus);
+                        }
+                    }
+                    // 运行中或者未通过状态。退出循环 (会签可能需要多人通过)
+                    if (RUNNING.getStatus().equals(progressStatus) || isUserTaskNotApproved(progressStatus)) {
+                        break;
+                    }
+                }
+            } else {
+                HistoricTaskInstance historicTask = bpmTaskService.getHistoricTask(historicActivity.getTaskId());
+                if (historicTask != null) {
+                    Integer taskStatus = FlowableUtils.getTaskStatus(historicTask);
+                    progressStatus = BpmProcessNodeProgressEnum.convertBpmnTaskStatus(taskStatus);
+                }
+            }
+        } else {
+            if (historicActivity.getEndTime() == null) {
+                progressStatus = RUNNING.getStatus();
+            }else {
+                progressStatus = BpmProcessNodeProgressEnum.FINISHED.getStatus();
+            }
+
+        }
+        return progressStatus;
+    }
+
+    @Override
+    public Integer getNotRunActivityProgressStatus(Integer processInstanceStatus) {
+        if(BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)){
+            return SKIP.getStatus();
+        }else {
+            return NOT_START.getStatus();
+        }
+    }
+
+    @Override
+    public List<User> getNotRunActivityUserList(String processInstanceId, Integer processInstanceStatus, Integer candidateStrategy, String candidateParam) {
+        if(BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)){
+            // 跳过节点。返回空
+            return Collections.emptyList();
+        }else {
+            BpmTaskCandidateStrategy taskCandidateStrategy = bpmTaskCandidateInvoker.getCandidateStrategy(candidateStrategy);
+            Set<Long> userIds = taskCandidateStrategy.calculateUsers(processInstanceId, candidateParam);
+            List<AdminUserRespDTO> userList = adminUserApi.getUserList(userIds);
+            return CollectionUtils.convertList(userList, item -> {
+                User user = BeanUtils.toBean(item, User.class);
+                user.setProcessed(Boolean.FALSE);
+                return user;
+            });
+        }
+    }
+
 }

+ 9 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
 
 import java.util.Collection;
+import java.util.Set;
 
 /**
  * 流程抄送 Service 接口
@@ -42,5 +43,13 @@ public interface BpmProcessInstanceCopyService {
      */
     PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
                                                                     BpmProcessInstanceCopyPageReqVO pageReqVO);
+    /**
+     * 通过流程实例和流程活动编号获取抄送人的 Id
+     *
+     * @param processInstanceId 流程实例 Id
+     * @param activityId 流程活动编号 Id
+     * @return 抄送人 Ids
+     */
+    Set<Long> getCopyUserIds(String processInstanceId, String activityId);
 
 }

+ 8 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper;
@@ -18,6 +19,7 @@ import org.springframework.validation.annotation.Validated;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -85,4 +87,10 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
         return processInstanceCopyMapper.selectPage(userId, pageReqVO);
     }
 
+    @Override
+    public Set<Long> getCopyUserIds(String processInstanceId, String activityId) {
+        return CollectionUtils.convertSet(processInstanceCopyMapper.selectListByProcInstIdAndActId(processInstanceId, activityId),
+                BpmProcessInstanceCopyDO::getUserId);
+    }
+
 }

+ 10 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java

@@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceFormFieldsPermissionReqVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
 import jakarta.validation.Valid;
 import org.flowable.engine.history.HistoricProcessInstance;
 import org.flowable.engine.runtime.ProcessInstance;
@@ -95,6 +92,14 @@ public interface BpmProcessInstanceService {
      */
     Map<String, String> getProcessInstanceFormFieldsPermission(@Valid BpmProcessInstanceFormFieldsPermissionReqVO reqVO);
 
+    /**
+     * 获取流程实例的进度
+     *
+     * @param id 流程 Id
+     * @return 流程实例的进度
+     */
+    BpmProcessInstanceProgressRespVO getProcessInstanceProgress(String id);
+
     // ========== Update 写入相关方法 ==========
 
     /**
@@ -148,4 +153,5 @@ public interface BpmProcessInstanceService {
      */
     void processProcessInstanceCompleted(ProcessInstance instance);
 
+
 }

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java


+ 8 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java

@@ -93,6 +93,14 @@ public interface BpmTaskService {
      */
     HistoricTaskInstance getHistoricTask(String id);
 
+    /**
+     * 获取历史任务列表
+     *
+     * @param taskIds 任务编号集合
+     * @return 历史任务列表
+     */
+    List<HistoricTaskInstance> getHistoricTasks(Collection<String> taskIds);
+
     /**
      * 根据条件查询正在进行中的任务
      *

+ 5 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

@@ -217,6 +217,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult();
     }
 
+    @Override
+    public List<HistoricTaskInstance> getHistoricTasks(Collection<String> taskIds) {
+        return historyService.createHistoricTaskInstanceQuery().taskIds(taskIds).includeTaskLocalVariables().list();
+    }
+
     @Override
     public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) {
         Assert.notNull(processInstanceId, "processInstanceId 不能为空");

Vissa filer visades inte eftersom för många filer har ändrats