Преглед на файлове

Merge remote-tracking branch 'origin/master'

master
PoffyZhang преди 1 година
родител
ревизия
fd289b8128
променени са 14 файла, в които са добавени 820 реда и са изтрити 28 реда
  1. +27
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/common/model/FileBasicInfo.java
  2. +5
    -4
      pmapi/src/main/java/com/ningdatech/pmapi/sys/entity/Notice.java
  3. +2
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/sys/entity/req/NoticeListReq.java
  4. +0
    -1
      pmapi/src/main/java/com/ningdatech/pmapi/sys/entity/req/NoticeSaveReq.java
  5. +68
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProgressNode.java
  6. +70
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java
  7. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/constant/HisProInsEndActId.java
  8. +28
    -5
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java
  9. +1
    -1
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessStatusEnum.java
  10. +189
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/BackToHisApprovalNodeCmd.java
  11. +322
    -12
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java
  12. +82
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqProcessHandlerDTO.java
  13. +1
    -1
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProcessedDTO.java
  14. +0
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProjectListExportDTO.java

+ 27
- 0
pmapi/src/main/java/com/ningdatech/pmapi/common/model/FileBasicInfo.java Целия файл

@@ -0,0 +1,27 @@
package com.ningdatech.pmapi.common.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
* @author liuxinxin
* @date 2022/7/25 下午1:55
* 用于包装使用
*/
@Data
@ApiModel("文件信息基类")
public class FileBasicInfo {

@ApiModelProperty("文件id")
private Long fileId;

@ApiModelProperty("文件名")
private String fileName;

@ApiModelProperty("文件类型")
private Integer fileType;

@ApiModelProperty("文件路径")
private String filePath;
}

+ 5
- 4
pmapi/src/main/java/com/ningdatech/pmapi/sys/entity/Notice.java Целия файл

@@ -1,9 +1,6 @@
package com.ningdatech.pmapi.sys.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -46,15 +43,19 @@ public class Notice implements Serializable {
private String attachment;

@ApiModelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createOn;

@ApiModelProperty("创建人id")
@TableField(fill = FieldFill.INSERT)
private Long createBy;

@ApiModelProperty("最后修改时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateOn;

@ApiModelProperty("最后修改人")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;

@ApiModelProperty("是否删除")


+ 2
- 2
pmapi/src/main/java/com/ningdatech/pmapi/sys/entity/req/NoticeListReq.java Целия файл

@@ -8,14 +8,14 @@ import lombok.EqualsAndHashCode;

/**
* <p>
* DashboardNoticeListPo
* NoticeListReq
* </p>
*
* @author WendyYang
* @since 00:32 2022/7/23
*/
@Data
@ApiModel("工作台消息列表查询")
@ApiModel("公告查询参数类")
@EqualsAndHashCode(callSuper = true)
public class NoticeListReq extends PagePo {



+ 0
- 1
pmapi/src/main/java/com/ningdatech/pmapi/sys/entity/req/NoticeSaveReq.java Целия файл

@@ -4,7 +4,6 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;



+ 68
- 0
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/entity/ProgressNode.java Целия файл

@@ -0,0 +1,68 @@
package com.ningdatech.pmapi.todocenter.bean.entity;

import com.ningdatech.pmapi.todocenter.model.dto.req.ReqProcessHandlerDTO;
import com.wflow.workflow.bean.process.OrgUser;
import com.wflow.workflow.bean.process.enums.ApprovalModeEnum;
import com.wflow.workflow.bean.process.enums.NodeTypeEnum;
import com.wflow.workflow.bean.vo.ProcessHandlerParamsVo;
import com.wflow.workflow.bean.vo.TaskCommentVo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.List;

/**
* 流程节点实体
*
* @author CMM
* @since 2023/01/31 12:24
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProgressNode {
/**
* 节点ID
*/
private String nodeId;
/**
* 任务ID
*/
private String taskId;
/**
* 审批类型
*/
private ApprovalModeEnum approvalMode;
/**
* 节点类型
*/
private NodeTypeEnum nodeType;
/**
* 节点名称
*/
private String name;
/**
* 节点相关人员
*/
private OrgUser user;
/**
* 该节点动作操作类型
*/
private ReqProcessHandlerDTO.Action action;
/**
* 处理结果
*/
private ReqProcessHandlerDTO.Action result;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date finishTime;
}

+ 70
- 0
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java Целия файл

@@ -0,0 +1,70 @@
package com.ningdatech.pmapi.todocenter.bean.vo;

import com.ningdatech.pmapi.todocenter.bean.entity.ProgressNode;
import com.wflow.workflow.bean.process.OrgUser;
import com.wflow.workflow.bean.process.form.Form;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
* 流程进展详情实体
*
* @author CMM
* @since 2023/01/30 17:21
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProcessProgressDetailVo {
/**
* 审批实例ID
*/
private String instanceId;
/**
* 表单配置项
*/
private List<Form> formItems;
/**
* 表单值
*/
private Map<String, Object> formData;
/**
* 流程进度步骤
*/
private List<ProgressNode> progress;
/**
* 流程定义名称
*/
private String processDefName;
/**
* 版本
*/
private Integer version;
/**
* 流程状态
*/
private String status;
/**
* 流程结果
*/
private String result;
/**
* 发起人
*/
private OrgUser staterUser;
/**
* 发起人部门
*/
private String starterDept;
/**
* 发起时间
*/
private Date startTime;
}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/constant/HisProInsEndActId.java Целия файл

@@ -0,0 +1,25 @@
package com.ningdatech.pmapi.todocenter.constant;
/**
* 历史流程实例终点活跃ID状态
* @author CMM
* @since 2023/01/31 15:13
*/
public interface HisProInsEndActId {
/**
* 流程被驳回
*/
public static final String REJECT = "refuse-end";
/**
* 流程被退回
*/
public static final String BACK = "back-end";
/**
* 流程被撤回
*/
public static final String WITHDRAW = "cancel-end";
/**
* 流程结束
*/
public static final String END = "process-end";

}

+ 28
- 5
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java Целия файл

@@ -5,10 +5,11 @@ import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import com.ningdatech.pmapi.common.util.ExcelDownUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ningdatech.pmapi.todocenter.bean.vo.ProcessProgressDetailVo;
import com.ningdatech.pmapi.todocenter.model.dto.req.ReqProcessHandlerDTO;
import com.wflow.utils.R;
import com.wflow.workflow.bean.vo.ProcessHandlerParamsVo;
import org.springframework.web.bind.annotation.*;

import com.ningdatech.basic.model.ApiResponse;
import com.ningdatech.basic.model.PageVo;
@@ -40,7 +41,7 @@ public class TodoCenterController {
* @param param
* @return
*/
@GetMapping("/NotAppendProjectList")
@GetMapping("/query-project-list")
public ApiResponse<PageVo<ResToBeProcessedDTO>> queryProjectList(@Valid @ModelAttribute ReqToBeProcessedDTO param){
PageVo<ResToBeProcessedDTO> result = todoCenterManage.queryProjectList(param);
return ApiResponse.ofSuccess(result);
@@ -58,4 +59,26 @@ public class TodoCenterController {
ExcelDownUtil.downXlsx(response,param,todoCenterManage::exportProjectList);
}

/**
* 查询流程表单数据及审批的进度步骤
* @param instanceId 流程实例ID
* @param nodeId 当前获取流程人员关联的流程节点ID
* @return 流程进度及表单详情
*/
@GetMapping("progress/{instanceId}/{nodeId}")
public ApiResponse<ProcessProgressDetailVo> getProcessDetail(@PathVariable String instanceId,
@PathVariable(required = false) String nodeId) {
return ApiResponse.ofSuccess(todoCenterManage.getProcessDetail(nodeId, instanceId));
}

/**
* 审核通过,盖章并通过、退回、撤回、驳回等操作
* @param param 操作参数
* @return 操作结果
*/
@PostMapping("/handler")
public ApiResponse<Object> handler(@Valid @RequestBody ReqProcessHandlerDTO param) {
todoCenterManage.handler(param);
return ApiResponse.ofSuccess();
}
}

+ 1
- 1
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/enums/ProcessStatusEnum.java Целия файл

@@ -27,7 +27,7 @@ public enum ProcessStatusEnum {
/**
* 被退回
*/
BE_RETURNED(2, "被退回"),
BE_BACKED(2, "被退回"),

/**
* 被驳回


+ 189
- 0
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/BackToHisApprovalNodeCmd.java Целия файл

@@ -0,0 +1,189 @@
package com.ningdatech.pmapi.todocenter.extension.cmd;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.assertj.core.util.Sets;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ExecutionGraphUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;

import com.wflow.workflow.utils.FlowableUtils;

/**
* @author : willian fu
* @date : 2022/10/14
*/
public class BackToHisApprovalNodeCmd implements Command<String>, Serializable {

private static final long serialVersionUID = -80075781855060928L;

protected RuntimeService runtimeService;
protected String taskId;
protected String targetNodeId;

public BackToHisApprovalNodeCmd(RuntimeService runtimeService, String taskId, String targetNodeId) {
this.runtimeService = runtimeService;
this.taskId = taskId;
this.targetNodeId = targetNodeId;
}

@Override
public String execute(CommandContext commandContext) {

if (targetNodeId == null || targetNodeId.length() == 0) {
throw new FlowableException("退回的目标节点不能为空");
}
TaskEntity task = CommandContextUtil.getProcessEngineConfiguration().getTaskServiceConfiguration().getTaskService().getTask(taskId);
if (task == null) {
throw new FlowableObjectNotFoundException(taskId + " 任务不能为空", Task.class);
}
String sourceNodeId = task.getTaskDefinitionKey();
String processInstanceId = task.getProcessInstanceId();
String processDefinitionId = task.getProcessDefinitionId();
//获取流程定义
Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
FlowNode sourceFlowElement = (FlowNode) process.getFlowElement(sourceNodeId, true);
// 只支持从用户任务退回
if (!(sourceFlowElement instanceof UserTask)) {
throw new FlowableException("只能从审批节点进行回退");
}
FlowNode targetFlowElement = (FlowNode) process.getFlowElement(targetNodeId, true);
// 退回节点到当前节点不可达到,不允许退回
if (!ExecutionGraphUtil.isReachable(processDefinitionId, targetNodeId, sourceNodeId)) {
throw new FlowableException("无法回退到目标节点");
}
//目标节点如果相对当前节点是在子流程内部,则无法直接退回,目前处理是只能退回到子流程开始节点
String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement, targetFlowElement);
// 实际应操作的当前节点ID
String sourceRealActivityId = sourceAndTargetRealActivityId[0];
// 实际应操作的目标节点ID
String targetRealActivityId = sourceAndTargetRealActivityId[1];

Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
// 当前节点处在的并行网关list
List<String> sourceInSpecialGatewayList = new ArrayList<>();
// 目标节点处在的并行网关list
List<String> targetInSpecialGatewayList = new ArrayList<>();
setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes, sourceInSpecialGatewayList, targetInSpecialGatewayList);
// 实际应筛选的节点ID
Set<String> sourceRealAcitivtyIds = null;
// 若退回目标节点相对当前节点在并行网关中,则要找到相对当前节点最近的这个并行网关,后续做特殊处理
String targetRealSpecialGateway = null;

// 1.目标节点和当前节点都不在并行网关中
if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newLinkedHashSet(sourceRealActivityId);
}
// 2.目标节点不在并行网关中、当前节点在并行网关中
else if (targetInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
}
// 3.目标节点在并行网关中、当前节点不在并行网关中
else if (sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newLinkedHashSet(sourceRealActivityId);
targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
}
// 4.目标节点和当前节点都在并行网关中
else {
int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList, targetInSpecialGatewayList);
// 在并行网关同一层且在同一分支
if (diffSpecialGatewayLevel == -1) {
sourceRealAcitivtyIds = Sets.newLinkedHashSet(sourceRealActivityId);
} else {
// 当前节点最内层并行网关不被目标节点最内层并行网关包含
// 或理解为当前节点相对目标节点在并行网关外
// 只筛选当前节点的execution
if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
sourceRealAcitivtyIds = Sets.newLinkedHashSet(sourceRealActivityId);
}
// 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的execution
else {
sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
}
// 目标节点最内层并行网关包含当前节点最内层并行网关
// 或理解为目标节点相对当前节点在并行网关外
// 不做处理
if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
}
// 目标节点相对当前节点在并行网关内
else {
targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
}
}
}

// 筛选需要处理的execution
List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
task.getExecutionId(), sourceRealActivityId, sourceRealAcitivtyIds);
// 执行退回,直接跳转到实际的 targetRealActivityId
List<String> realExecutionIds = realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
// 目标节点相对当前节点处于并行网关内,需要特殊处理,需要手动生成并行网关汇聚节点(_end)的execution数据
if (targetRealSpecialGateway != null) {
createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process, targetInSpecialGatewayList, targetRealSpecialGateway);
}
return targetRealActivityId;
}

private void setSpecialGatewayList(String sourceNodeId, String targetNodeId, Map<String, Set<String>> specialGatewayNodes,
List<String> sourceInSpecialGatewayList, List<String> targetInSpecialGatewayList) {
for (Map.Entry<String, Set<String>> entry : specialGatewayNodes.entrySet()) {
if (entry.getValue().contains(sourceNodeId)) {
sourceInSpecialGatewayList.add(entry.getKey());
}
if (entry.getValue().contains(targetNodeId)) {
targetInSpecialGatewayList.add(entry.getKey());
}
}
}

private void createTargetInSpecialGatewayEndExecutions(CommandContext commandContext, List<ExecutionEntity> excutionEntitys, Process process,
List<String> targetInSpecialGatewayList, String targetRealSpecialGateway) {
// 目标节点相对当前节点处于并行网关,需要手动生成并行网关汇聚节点(_end)的execution数据
String parentExecutionId = excutionEntitys.iterator().next().getParentId();
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentExecutionId);

int index = targetInSpecialGatewayList.indexOf(targetRealSpecialGateway);
for (; index < targetInSpecialGatewayList.size(); index++) {
String targetInSpecialGateway = targetInSpecialGatewayList.get(index);
FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGateway + "_end", true);
int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
// 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
activityBehavior.execute(childExecution);
}
}
}

private List<ExecutionEntity> getRealExecutions(CommandContext commandContext, String processInstanceId,
String taskExecutionId, String sourceRealActivityId, Set<String> activityIds) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity taskExecution = executionEntityManager.findById(taskExecutionId);
List<ExecutionEntity> executions = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
Set<String> parentExecutionIds = FlowableUtils.getParentExecutionIdsByActivityId(executions, sourceRealActivityId);
String realParentExecutionId = FlowableUtils.getParentExecutionIdFromParentIds(taskExecution, parentExecutionIds);
return executionEntityManager.findExecutionsByParentExecutionAndActivityIds(realParentExecutionId, activityIds);
}
}

+ 322
- 12
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java Целия файл

@@ -1,28 +1,53 @@
package com.ningdatech.pmapi.todocenter.manage;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.pmapi.common.constant.ProjectDeclareConstants;
import com.ningdatech.pmapi.common.util.ExcelDownUtil;
import com.ningdatech.pmapi.common.util.ExcelExportStyle;
import com.ningdatech.pmapi.todocenter.bean.entity.ProgressNode;
import com.ningdatech.pmapi.todocenter.bean.vo.ProcessProgressDetailVo;
import com.ningdatech.pmapi.todocenter.constant.HisProInsEndActId;
import com.ningdatech.pmapi.todocenter.enums.ProcessStatusEnum;
import com.ningdatech.pmapi.todocenter.extension.cmd.BackToHisApprovalNodeCmd;
import com.ningdatech.pmapi.todocenter.model.dto.req.ReqProcessHandlerDTO;
import com.ningdatech.pmapi.todocenter.model.dto.res.ResToBeProjectListExportDTO;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import com.wflow.bean.do_.UserDo;
import com.wflow.bean.entity.WflowCcTasks;
import com.wflow.bean.entity.WflowModelHistorys;
import com.wflow.mapper.WflowCcTasksMapper;
import com.wflow.mapper.WflowModelHistorysMapper;
import com.wflow.service.OrgRepositoryService;
import com.wflow.workflow.bean.dto.ProcessInstanceOwnerDto;
import com.wflow.workflow.bean.process.OrgUser;
import com.wflow.workflow.bean.process.ProcessNode;
import com.wflow.workflow.bean.process.enums.ApprovalModeEnum;
import com.wflow.workflow.bean.process.enums.NodeTypeEnum;
import com.wflow.workflow.bean.process.form.Form;
import com.wflow.workflow.bean.process.props.ApprovalProps;
import com.wflow.workflow.bean.vo.ProcessProgressVo;
import com.wflow.workflow.bean.vo.ProcessTaskVo;
import com.wflow.workflow.service.ProcessInstanceService;
import com.wflow.workflow.service.UserDeptOrLeaderService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import com.wflow.workflow.config.WflowGlobalVarDef;
import com.wflow.workflow.service.*;
import com.wflow.workflow.service.FormService;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Maps;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;

import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Component;
import com.ningdatech.basic.model.PageVo;

@@ -33,10 +58,8 @@ import lombok.RequiredArgsConstructor;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
@@ -45,6 +68,7 @@ import java.util.stream.Collectors;
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class TodoCenterManage {

private final TaskService taskService;
@@ -52,6 +76,14 @@ public class TodoCenterManage {
private final RuntimeService runtimeService;
private final UserDeptOrLeaderService userDeptOrLeaderService;
private final ProcessInstanceService processService;
private final FormService formService;
private final ManagementService managementService;
private final HistoryService historyService;
private final WflowModelHistorysMapper modelHistorysMapper;
private final ProcessNodeCatchService nodeCatchService;
private final OrgRepositoryService orgRepositoryService;
private final ProcessTaskService processTaskService;
private final WflowCcTasksMapper ccTasksMapper;
public PageVo<ResToBeProcessedDTO> queryProjectList(ReqToBeProcessedDTO param) {
// 获取登录用户ID
Long userId = LoginUserUtil.getUserId();
@@ -99,7 +131,7 @@ public class TodoCenterManage {
.createTime(processInstance.getStartTime())
.taskCreateTime(task.getCreateTime())
.build();
res.setProcessTaskVo(processTaskVo);
res.setProcessTaskInfo(processTaskVo);
String projectName = (String) formData.get(ProjectDeclareConstants.BasicInformation.PROJECT_NAME);
res.setProjectName(projectName);
res.setReportUnitId(owner.getOwnerDeptId());
@@ -121,12 +153,11 @@ public class TodoCenterManage {
//取用户信息,减少数据库查询,一次构建
if (CollectionUtil.isNotEmpty(staterUsers)) {
Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers);
page.setRecords(result.stream().peek(v -> v.getProcessTaskVo().setOwner(userMap.get(v.getProcessTaskVo().getOwnerId()))).collect(Collectors.toList()));
page.setRecords(result.stream().peek(v -> v.getProcessTaskInfo().setOwner(userMap.get(v.getProcessTaskInfo().getOwnerId()))).collect(Collectors.toList()));
}
return PageVo.of(resVos,page.getTotal());
}


public void exportProjectList(HttpServletResponse response, ReqToBeProcessedDTO param) {
PageVo<ResToBeProcessedDTO> page =
queryProjectList(param);
@@ -149,4 +180,283 @@ public class TodoCenterManage {
throw new RuntimeException(e);
}
}

public void handler(ReqProcessHandlerDTO param) {
Long userId = LoginUserUtil.getUserId();
Task task = taskService.createTaskQuery().taskId(param.getTaskId()).active().singleResult();
HashMap<String, Object> formData = new HashMap<>(32);
String format = NdDateUtils.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm");
LocalDateTime auditTime = LocalDateTime.parse(format);
if (Objects.isNull(task)) {
throw new BizException("任务不存在");
}
switch (param.getAction()) {
// 通过
case pass:
formData.put("audit_pass_opinion",param.getAuditPassOpinion());
formData.put("audit_pass_appendix",param.getAuditPassAppendix());
formData.put("audit_pass_time", auditTime);
formService.updateInstanceFormData(param.getInstanceId(), formData);
doPass(task, param);
// 盖章并通过
case seal_pass:
formData.put("seal_pass_opinion",param.getSealPassOpinion());
formData.put("seal_pass_appendix",param.getSealPassAppendix());
Date sealPassTime = NdDateUtils.localDateTime2Date(LocalDateTime.now());
formData.put("seal_pass_time",sealPassTime);
formService.updateInstanceFormData(param.getInstanceId(), formData);
doSealPass(task, param);
// 驳回
case reject:
formData.put("audit_reject_opinion",param.getAuditRejectOpinion());
formData.put("audit_reject_appendix",param.getAuditRejectAppendix());
formData.put("audit_reject_time", auditTime);
formService.updateInstanceFormData(param.getInstanceId(), formData);
doReject(task, param);
break;
// 退回
case back:
formData.put("audit_back_opinion",param.getAuditBackOpinion());
formData.put("audit_back_appendix",param.getAuditBackAppendix());
formData.put("audit_back_time", auditTime);
formService.updateInstanceFormData(param.getInstanceId(), formData);
doBackTask(task.getTaskDefinitionKey(), userId, param);
break;
// 撤回
case withdraw:
doWithDrawProcess(task);
break;
default:
throw new IllegalStateException("Unexpected value: " + param.getAction());
}
}

/**
* 审批任务:驳回
*
* @param task 当前任务
* @param param 参数
*/
private void doReject(Task task, ReqProcessHandlerDTO param) {
Map<String, Object> var = new HashMap<>(16);
var.put("approve_" + task.getId(), param.getAction());
// TODO 中止流程并使项目进入对应状态
// TODO 给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被驳回,请及时处理。
taskService.complete(param.getTaskId(), var);
}

/**
* 审批任务:盖章并通过
*
* @param task 当前任务
* @param param 参数
*/
private void doSealPass(Task task, ReqProcessHandlerDTO param) {
Map<String, Object> var = new HashMap<>(16);
var.put("approve_" + task.getId(), param.getAction());
// TODO 判断项目申报单位级别,区县单位申报有上级主管单位意见栏,市级单位没有
// TODO 市级单位:为大数据局;区县单位:为大数据中心(根据附件区分?)
taskService.complete(param.getTaskId(), var);
}

/**
* 审批任务:通过
*
* @param task 当前任务
* @param param 参数
*/
private void doPass(Task task, ReqProcessHandlerDTO param) {
Map<String, Object> var = new HashMap<>(16);
var.put("approve_" + task.getId(), param.getAction());
// TODO 获取流程下一个节点的审核人

// TODO 若有下一个审核人,向其发送浙政钉工作通知:标题:审核任务 内容:【单位名称】的【项目名称】需要您审核。

// TODO 若没有,向发起人发送浙政钉工作通知:【项目名称】已通过【流程名称】,请及时开始下一步操作。
taskService.complete(param.getTaskId(), var);
}

/**
* 撤销流程处理
*
* @param task 当前任务
*/
private void doWithDrawProcess(Task task) {
// TODO 若是流程发起人点击撤回,项目回到上一个状态,并删除当前审核人对应的待办记录
// TODO 若是前一个审核人点击撤回,在审核记录中移除自己提交过的审核意见、待我处理中移除当前审核人的待办记录、待我处理中增加自己的待办记录、我已处理中去掉自己之前处理的记录
List<Execution> executions = runtimeService.createExecutionQuery()
.processInstanceId(task.getProcessInstanceId())
.onlyChildExecutions().list();
// 强制流程指向撤回
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdTo(task.getTaskDefinitionKey(), HisProInsEndActId.WITHDRAW)
.moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId)
.collect(Collectors.toList()), HisProInsEndActId.WITHDRAW)
.changeState();
}

/**
* 退回流程处理
* @param current 当前流程定义key
* @param userId 当前登录用户ID
* @param param 参数
*/
private void doBackTask(String current, Long userId, ReqProcessHandlerDTO param) {
// TODO 流程变成【被退回】状态
// TODO 待我处理中,为流程发起人增加一条待办记录
// TODO 给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被退回,请及时处理。
//执行自定义回退逻辑
managementService.executeCommand(new BackToHisApprovalNodeCmd(runtimeService, param.getTaskId(), param.getTargetNode()));
runtimeService.setVariables(param.getInstanceId(), Maps.newHashMap("approve_" + param.getTaskId(), param.getAction()));
log.info("用户[{}] 退回流程[{}] [{} -> {}]", userId, param.getInstanceId(), current, param.getTargetNode());
}

/**
* 查询流程表单数据及审批的进度步骤
* @param instanceId 流程实例ID
* @param nodeId 当前获取流程人员关联的流程节点ID
* @return 流程进度及表单详情
*/
public ProcessProgressDetailVo getProcessDetail(String nodeId, String instanceId) {
HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
// 取表单及表单数据
HistoricVariableInstance forms = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(instanceId).variableName(WflowGlobalVarDef.WFLOW_FORMS).singleResult();
List<HistoricVariableInstance> formDatas = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(instanceId).variableNameLike("field%").list();
// 取节点设置
HistoricVariableInstance nodeProps = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(instanceId).variableName(WflowGlobalVarDef.WFLOW_NODE_PROPS).singleResult();
Map<String, Object> nodePropsValue = (Map<String, Object>) nodeProps.getValue();
ProcessNode<?> currentNode = null;
if (StrUtil.isNotBlank(nodeId)) {
// 搜索当前版本流程的配置
WflowModelHistorys modelHistory = modelHistorysMapper.selectOne(new QueryWrapper<>(WflowModelHistorys.builder()
.processDefId(instance.getProcessDefinitionId()).version(instance.getProcessDefinitionVersion()).build()));
currentNode = nodeCatchService.reloadProcessByStr(modelHistory.getProcess()).get(nodeId);
}
UserDo users = orgRepositoryService.getUserById(instance.getStartUserId());
OrgUser startUser = OrgUser.builder().id(users.getUserId()).name(users.getUserName()).avatar(users.getAvatar()).build();
List<ProgressNode> taskRecords = getHisTaskRecords(instanceId, nodePropsValue);
// 获取添加抄送任务
taskRecords.addAll(getCcTaskRecords(instanceId));
if (ObjectUtil.isNull(instance.getEndTime())) {
// TODO 下版实现 获取等待中且还未开始的任务,如果存在条件则需要直接解析条件
taskRecords.addAll(getFutureTask(instanceId));
}
taskRecords = taskRecords.stream()
.sorted(Comparator.comparing(ProgressNode::getStartTime))
.collect(Collectors.toList());
taskRecords.add(0, ProgressNode.builder()
.nodeId("root")
.name("提交申请")
.user(startUser)
.nodeType(NodeTypeEnum.ROOT)
.startTime(instance.getStartTime())
.finishTime(instance.getStartTime())
.taskId("root")
.result(ReqProcessHandlerDTO.Action.pass)
.build());
// 提取全量表单数据
Map<String, Object> formData = formDatas.stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));
HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(instanceId).variableName("owner").singleResult();
ProcessInstanceOwnerDto owner = (ProcessInstanceOwnerDto) variableInstance.getValue();
ProcessProgressDetailVo res = ProcessProgressDetailVo.builder()
.instanceId(instanceId)
.version(instance.getProcessDefinitionVersion())
.formItems(formService.filterFormAndDataByPermConfig((List<Form>) forms.getValue(), formData, currentNode))
.formData(formData)
.processDefName(instance.getProcessDefinitionName())
.staterUser(startUser)
.starterDept(null == owner ? null : owner.getOwnerDeptName())
.result(instance.getEndActivityId())
.startTime(instance.getStartTime())
.progress(taskRecords)
.build();
if (Objects.isNull(instance.getEndActivityId())){
res.setStatus(ProcessStatusEnum.UNDER_REVIEW.name());
} else if (HisProInsEndActId.BACK.equals(instance.getEndActivityId())) {
res.setStatus(ProcessStatusEnum.BE_BACKED.name());
} else if (HisProInsEndActId.REJECT.equals(instance.getEndActivityId())) {
res.setStatus(ProcessStatusEnum.BE_REJECTED.name());
} else if (HisProInsEndActId.END.equals(instance.getEndActivityId())) {
res.setStatus(ProcessStatusEnum.APPROVED.name());
}
return res;
}

/**
* 获取抄送的流程实例信息
*
* @param instanceId 实例ID
* @return 抄送我的流程
*/
private List<ProgressNode> getCcTaskRecords(String instanceId) {
Set<String> ccUsers = new HashSet<>();
List<ProgressNode> ccList = ccTasksMapper.selectList(new QueryWrapper<WflowCcTasks>()
.eq("instance_id", instanceId)).stream().map(task -> {
ccUsers.add(task.getUserId());
return ProgressNode.builder()
.nodeId(task.getNodeId())
.nodeType(NodeTypeEnum.CC)
.name(task.getNodeName())
.user(OrgUser.builder().id(task.getUserId()).build())
.startTime(task.getCreateTime())
.finishTime(task.getCreateTime())
.build();
}).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(ccUsers)) {
Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(ccUsers);
ccList.stream().peek(v -> v.setUser(userMap.get(v.getUser().getId()))).collect(Collectors.toList());
}
return ccList;
}

/**
* 获取流程的审批历史记录
*
* @param instanceId 审批实例ID
* @param nodeProps 节点设置
* @return 历史记录列表
*/
private List<ProgressNode> getHisTaskRecords(String instanceId, Map<String, Object> nodeProps) {
List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId).orderByHistoricActivityInstanceStartTime().asc().list();
Set<String> userSet = new HashSet<>();
//获取节点处理结果
Map<String, ReqProcessHandlerDTO.Action> varMap = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(instanceId).variableNameLike("approve_%").list().stream()
.collect(Collectors.toMap(HistoricVariableInstance::getVariableName, v -> (ReqProcessHandlerDTO.Action) v.getValue()));
List<ProgressNode> progressNodes = list.stream().filter(his -> ObjectUtil.isNotNull(his.getTaskId())).map(his -> {
Object props = nodeProps.get(his.getActivityId());
ApprovalModeEnum approvalMode = null;
if (props instanceof ApprovalProps) {
approvalMode = ((ApprovalProps) props).getMode();
}
userSet.add(his.getAssignee());
return ProgressNode.builder()
.nodeId(his.getActivityId())
.name(his.getActivityName())
.nodeType(NodeTypeEnum.APPROVAL)
.user(OrgUser.builder().id(his.getAssignee()).build())
.startTime(his.getStartTime())
.finishTime(his.getEndTime())
.taskId(his.getTaskId())
.approvalMode(approvalMode)
.result(varMap.get("approve_" + his.getTaskId()))
.build();
}).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(userSet)) {
Map<String, OrgUser> map = userDeptOrLeaderService.getUserMapByIds(userSet);
progressNodes.forEach(n -> n.setUser(map.get(n.getUser().getId())));
}
return progressNodes;
}

private List<ProgressNode> getFutureTask(String instanceId) {
//根据流程遍历后续节点,期间要穿越后续包含并行网关和条件网关的节点
return Collections.emptyList();
}
}

+ 82
- 0
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ReqProcessHandlerDTO.java Целия файл

@@ -0,0 +1,82 @@
package com.ningdatech.pmapi.todocenter.model.dto.req;
import com.ningdatech.pmapi.common.model.FileBasicInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;

/**
* 流程处理操作参数实体
*
* @author CMM
* @since 2023/01/30 09:09
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ReqProcessHandlerDTO {

/**
* 实例ID
*/
private String instanceId;
/**
* 任务ID
*/
private String taskId;
/**
* 签名图片地址
*/
private String signature;
/**
* 操作类型
*/
private Action action;
/**
* 目标用户
*/
private String targetUser;
/**
* 目标节点
*/
private String targetNode;
/**
* 审核通过意见
*/
private String auditPassOpinion;

/**
* 审核通过附件
*/
private FileBasicInfo auditPassAppendix;
/**
* 盖章通过意见
*/
private String sealPassOpinion;

/**
* 盖章通过附件
*/
private FileBasicInfo sealPassAppendix;
/**
* 审核退回意见
*/
private String auditBackOpinion;
/**
* 审核退回附件
*/
private FileBasicInfo auditBackAppendix;
/**
* 审核驳回意见
*/
private String auditRejectOpinion;

/**
* 审核驳回附件
*/
private FileBasicInfo auditRejectAppendix;
public enum Action{
//通过、盖章并通过、退回、撤回、驳回,审核意见类型
pass, seal_pass ,back, withdraw, reject;
}
}

+ 1
- 1
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProcessedDTO.java Целия файл

@@ -49,5 +49,5 @@ public class ResToBeProcessedDTO implements Serializable {
private LocalDateTime processLaunchTime;

@ApiModelProperty("流程任务信息")
private ProcessTaskVo processTaskVo;
private ProcessTaskVo processTaskInfo;
}

+ 0
- 2
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/res/ResToBeProjectListExportDTO.java Целия файл

@@ -1,8 +1,6 @@
package com.ningdatech.pmapi.todocenter.model.dto.res;

import com.alibaba.excel.annotation.ExcelProperty;
import com.wflow.workflow.bean.vo.ProcessTaskVo;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


Loading…
Отказ
Запис