Commit 0a2599152d33aa965eeacb2e80b5d6f24dcdc950

Authored by YunaiV
1 parent ed28d6d5

暂时回退流程推断的能力

yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java
1 package cn.iocoder.yudao.module.bpm.convert.task; 1 package cn.iocoder.yudao.module.bpm.convert.task;
2 2
3 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO; 3 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
4 -import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmActivityDO;  
5 import org.flowable.engine.history.HistoricActivityInstance; 4 import org.flowable.engine.history.HistoricActivityInstance;
6 import org.mapstruct.Mapper; 5 import org.mapstruct.Mapper;
7 import org.mapstruct.Mapping; 6 import org.mapstruct.Mapping;
@@ -20,11 +19,11 @@ public interface BpmActivityConvert { @@ -20,11 +19,11 @@ public interface BpmActivityConvert {
20 19
21 BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class); 20 BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
22 21
23 - List<BpmActivityRespVO> convertList(List<BpmActivityDO> list); 22 + List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
24 23
25 @Mappings({ 24 @Mappings({
26 @Mapping(source = "activityId", target = "key"), 25 @Mapping(source = "activityId", target = "key"),
27 @Mapping(source = "activityType", target = "type") 26 @Mapping(source = "activityType", target = "type")
28 }) 27 })
29 - BpmActivityRespVO convert(BpmActivityDO bean); 28 + BpmActivityRespVO convert(HistoricActivityInstance bean);
30 } 29 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmActivityMapper.java
@@ -7,20 +7,9 @@ import org.apache.ibatis.annotations.Param; @@ -7,20 +7,9 @@ import org.apache.ibatis.annotations.Param;
7 7
8 import java.util.List; 8 import java.util.List;
9 9
10 -/**  
11 - * @author kemengkai  
12 - * @create 2022-05-09 09:26 TODO @ke:@date 哈。ps:一般这种类,可以不用谢类注释和方法注释。因为是 dao 是通用操作,所以都是通过方法名,可以指导它的用途  
13 - */  
14 @Mapper 10 @Mapper
15 public interface BpmActivityMapper extends BaseMapperX<BpmActivityDO> { 11 public interface BpmActivityMapper extends BaseMapperX<BpmActivityDO> {
16 12
17 - // TODO @ke:这个方法,是不是暂时用不到呀。如果是的话,可以删除哈  
18 - /**  
19 - * 获取所有历史任务  
20 - *  
21 - * @return 返回历史任务  
22 - */  
23 - List<BpmActivityDO> listAll();  
24 13
25 // TODO @ke:可以试试,把 activiti 的表,映射成对应的实体,然后读取下。我们尽量避免 xml 操作,因为要做多 db 类型的支持,例如说 oracle 等。通过 mybatis plus 帮助我们生成不同数据库的表操作 14 // TODO @ke:可以试试,把 activiti 的表,映射成对应的实体,然后读取下。我们尽量避免 xml 操作,因为要做多 db 类型的支持,例如说 oracle 等。通过 mybatis plus 帮助我们生成不同数据库的表操作
26 /** 15 /**
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/domain/vo/ApproveProcInstVO.java deleted
1 -package cn.iocoder.yudao.module.bpm.domain.vo;  
2 -  
3 -import io.swagger.annotations.ApiModelProperty;  
4 -import lombok.AllArgsConstructor;  
5 -import lombok.Data;  
6 -import lombok.NoArgsConstructor;  
7 -  
8 -import java.time.LocalDate;  
9 -  
10 -/**  
11 - * 审批流程VO  
12 - *  
13 - * @author kemengkai  
14 - */  
15 -@Data  
16 -@AllArgsConstructor  
17 -@NoArgsConstructor  
18 -// TODO @ke:这个类是不是没用呀  
19 -public class ApproveProcInstVO {  
20 -  
21 - @ApiModelProperty("任务id")  
22 - private String id;  
23 - @ApiModelProperty("任务部署key")  
24 - private String taskDefKey;  
25 - @ApiModelProperty("任务名称")  
26 - private String name;  
27 - @ApiModelProperty("审批人登录名")  
28 - private String assignee;  
29 - @ApiModelProperty("审批人姓名")  
30 - private String assigneeName;  
31 - @ApiModelProperty("审批回复")  
32 - private String taskComment;  
33 - @ApiModelProperty("审批完成时间")  
34 - private LocalDate endTime;  
35 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java
1 package cn.iocoder.yudao.module.bpm.service.task; 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2
3 -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;  
4 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO; 3 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
5 import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert; 4 import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert;
6 -import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmActivityDO;  
7 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmActivityMapper; 5 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmActivityMapper;
8 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
9 -  
10 import org.flowable.engine.HistoryService; 7 import org.flowable.engine.HistoryService;
11 import org.flowable.engine.history.HistoricActivityInstance; 8 import org.flowable.engine.history.HistoricActivityInstance;
12 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
@@ -32,13 +29,10 @@ public class BpmActivityServiceImpl implements BpmActivityService { @@ -32,13 +29,10 @@ public class BpmActivityServiceImpl implements BpmActivityService {
32 private BpmActivityMapper bpmActivityMapper; 29 private BpmActivityMapper bpmActivityMapper;
33 30
34 @Override 31 @Override
35 - @TenantIgnore  
36 - public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String procInstId) {  
37 -// List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()  
38 -// .processInstanceId(procInstId).list();  
39 - // TODO @ke:这个是想要过滤掉 aha.act_type_ != 'sequenceFlow' 类型么?  
40 - List<BpmActivityDO> bpmActivityDOList = bpmActivityMapper.listAllByProcInstIdAndDelete(procInstId);  
41 - return BpmActivityConvert.INSTANCE.convertList(bpmActivityDOList); 32 + public List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId) {
  33 + List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
  34 + .processInstanceId(processInstanceId).list();
  35 + return BpmActivityConvert.INSTANCE.convertList(activityList);
42 } 36 }
43 37
44 @Override 38 @Override
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1 -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.startProcessInstanceById(definition.getId(), businessKey, variables); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 //TODO startProcessInstance流程里面修改了 BpmProcessInstanceExt,没有提交,和下面的更新 锁持有冲突了,异步更新这个表 // processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()).setFormVariables(variables)); BpmProcessInstanceExtDO bpmProcessInstanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()).setFormVariables(variables); asyncUpdateProcesInstance(bpmProcessInstanceExtDO); return instance.getId(); } @Async public void asyncUpdateProcesInstance(BpmProcessInstanceExtDO bpmProcessInstanceExtDO){ log.info("asyncUpdateProcesInstance ,cause MySQL Dead Lock"); processInstanceExtMapper.updateByProcessInstanceId(bpmProcessInstanceExtDO); } }  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.startProcessInstanceById(definition.getId(), businessKey, variables); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 //TODO startProcessInstance流程里面修改了 BpmProcessInstanceExt,没有提交,和下面的更新 锁持有冲突了,异步更新这个表 // processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()).setFormVariables(variables)); BpmProcessInstanceExtDO bpmProcessInstanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()).setFormVariables(variables); asyncUpdateProcesInstance(bpmProcessInstanceExtDO); return instance.getId(); } @Async public void asyncUpdateProcesInstance(BpmProcessInstanceExtDO bpmProcessInstanceExtDO){ log.info("asyncUpdateProcesInstance ,cause MySQL Dead Lock"); processInstanceExtMapper.updateByProcessInstanceId(bpmProcessInstanceExtDO); } }
3 \ No newline at end of file 3 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -18,8 +18,8 @@ import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; @@ -18,8 +18,8 @@ import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
18 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; 18 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
19 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; 19 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
20 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; 20 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
21 -import cn.iocoder.yudao.module.business.hi.task.inst.service.HiTaskInstService;  
22 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 21 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  22 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
23 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 23 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
24 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 24 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
25 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
@@ -78,8 +78,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -78,8 +78,6 @@ public class BpmTaskServiceImpl implements BpmTaskService {
78 private BpmTaskAssignRuleMapper taskAssignRuleMapper; 78 private BpmTaskAssignRuleMapper taskAssignRuleMapper;
79 @Resource 79 @Resource
80 private BpmActivityMapper bpmActivityMapper; 80 private BpmActivityMapper bpmActivityMapper;
81 - @Resource  
82 - private HiTaskInstService hiTaskInstService;  
83 81
84 @Override 82 @Override
85 public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { 83 public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
@@ -161,22 +159,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -161,22 +159,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
161 @Override 159 @Override
162 public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) { 160 public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) {
163 // 获得任务列表 161 // 获得任务列表
164 - /* List<HistoricTaskInstance> tasks =  
165 - historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId) 162 + List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
  163 + .processInstanceId(processInstanceId)
166 .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序 164 .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
167 .list(); 165 .list();
168 if (CollUtil.isEmpty(tasks)) { 166 if (CollUtil.isEmpty(tasks)) {
169 return Collections.emptyList(); 167 return Collections.emptyList();
170 - }*/  
171 - // 获得 TaskExtDO Map  
172 - // List<BpmTaskExtDO> bpmTaskExtDOList =  
173 - // taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));  
174 -  
175 -// List<BpmTaskExtDO> bpmTaskExtDOList = taskExtMapper.listByProcInstId(processInstanceId);  
176 - // List<BpmTaskExtDO> bpmTaskExtDOList = BpmTaskConvert.INSTANCE.distinct(tmpBpmTaskExtDOList);  
177 - // bpmTaskExtDOList.forEach(var -> log.info("var = " + var)); 168 + }
178 169
179 - /*Map<String, BpmTaskExtDO> bpmTaskExtDoMap = convertMap(bpmTaskExtDOList, BpmTaskExtDO::getTaskId); 170 + // 获得 TaskExtDO Map
  171 + List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
  172 + Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
180 // 获得 ProcessInstance Map 173 // 获得 ProcessInstance Map
181 HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId); 174 HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
182 // 获得 User Map 175 // 获得 User Map
@@ -184,14 +177,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -184,14 +177,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
184 userIds.add(NumberUtils.parseLong(processInstance.getStartUserId())); 177 userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
185 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); 178 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
186 // 获得 Dept Map 179 // 获得 Dept Map
187 - Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));*/ 180 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
188 181
189 // 拼接数据 182 // 拼接数据
190 -// List<BpmTaskExtDO> tmpBpmTaskExtDOList = taskExtMapper.listByProcInstId(processInstanceId);  
191 - // TODO @ke:这个钉钉是咋处理的?得讨论下流程预测的需要程度哈。  
192 - List<BpmTaskExtDO> tmpBpmTaskExtDOList = taskExtMapper.selectListByProcessInstanceId(processInstanceId);  
193 - tmpBpmTaskExtDOList.sort(Comparator.comparing(BpmTaskExtDO::getCreateTime));  
194 - return hiTaskInstService.taskGetComment(tmpBpmTaskExtDOList, ""); 183 + return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap);
195 } 184 }
196 185
197 @Override 186 @Override
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/business/hi/task/inst/domain/HistoricApproveTaskDTO.java deleted
1 -package cn.iocoder.yudao.module.business.hi.task.inst.domain;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Data;  
5 -import lombok.NoArgsConstructor;  
6 -  
7 -/**  
8 - * @author kemengkai  
9 - * @create 2022-01-11 15:09  
10 - */  
11 -@Data  
12 -@AllArgsConstructor  
13 -@NoArgsConstructor  
14 -public class HistoricApproveTaskDTO {  
15 -  
16 - private String procInstId;  
17 - private String name;  
18 - private String assignee;  
19 -  
20 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/business/hi/task/inst/domain/vo/HiTaskinstVO.java deleted
1 -package cn.iocoder.yudao.module.business.hi.task.inst.domain.vo;  
2 -  
3 -import lombok.Data;  
4 -import java.util.Date;  
5 -import com.fasterxml.jackson.annotation.JsonFormat;  
6 -import io.swagger.annotations.ApiModelProperty;  
7 -  
8 -/**  
9 - * [ ]  
10 - *  
11 - * @author 孟凯  
12 - * @version 1.0  
13 - * @company 1024创新实验室( www.1024lab.net )  
14 - * @copyright (c) 1024创新实验室( www.1024lab.net )Inc. All rights reserved.  
15 - * @date 2022-01-17 15:14:27  
16 - * @since JDK1.8  
17 - */  
18 -@Data  
19 -public class HiTaskinstVO {  
20 - @ApiModelProperty("ID_")  
21 - private String id;  
22 -  
23 - @ApiModelProperty("TASK_DEF_KEY_")  
24 - private String taskDefKey;  
25 -  
26 - @ApiModelProperty("PROC_DEF_KEY_")  
27 - private String procDefKey;  
28 -  
29 - @ApiModelProperty("PROC_DEF_ID_")  
30 - private String procDefId;  
31 -  
32 - @ApiModelProperty("ROOT_PROC_INST_ID_")  
33 - private String rootProcInstId;  
34 -  
35 - @ApiModelProperty("PROC_INST_ID_")  
36 - private String procInstId;  
37 -  
38 - @ApiModelProperty("EXECUTION_ID_")  
39 - private String executionId;  
40 -  
41 - @ApiModelProperty("CASE_DEF_KEY_")  
42 - private String caseDefKey;  
43 -  
44 - @ApiModelProperty("CASE_DEF_ID_")  
45 - private String caseDefId;  
46 -  
47 - @ApiModelProperty("CASE_INST_ID_")  
48 - private String caseInstId;  
49 -  
50 - @ApiModelProperty("CASE_EXECUTION_ID_")  
51 - private String caseExecutionId;  
52 -  
53 - @ApiModelProperty("ACT_INST_ID_")  
54 - private String actInstId;  
55 -  
56 - @ApiModelProperty("NAME_")  
57 - private String name;  
58 -  
59 - @ApiModelProperty("PARENT_TASK_ID_")  
60 - private String parentTaskId;  
61 -  
62 - @ApiModelProperty("DESCRIPTION_")  
63 - private String description;  
64 -  
65 - @ApiModelProperty("OWNER_")  
66 - private String owner;  
67 -  
68 - @ApiModelProperty("ASSIGNEE_")  
69 - private String assignee;  
70 -  
71 - @ApiModelProperty("START_TIME_")  
72 - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")  
73 - private Date startTime;  
74 -  
75 - @ApiModelProperty("END_TIME_")  
76 - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")  
77 - private Date endTime;  
78 -  
79 - @ApiModelProperty("DURATION_")  
80 - private Long duration;  
81 -  
82 - @ApiModelProperty("DELETE_REASON_")  
83 - private String deleteReason;  
84 -  
85 - @ApiModelProperty("PRIORITY_")  
86 - private Integer priority;  
87 -  
88 - @ApiModelProperty("DUE_DATE_")  
89 - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")  
90 - private Date dueDate;  
91 -  
92 - @ApiModelProperty("FOLLOW_UP_DATE_")  
93 - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")  
94 - private Date followUpDate;  
95 -  
96 - @ApiModelProperty("TENANT_ID_")  
97 - private String tenantId;  
98 -  
99 - @ApiModelProperty("REMOVAL_TIME_")  
100 - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")  
101 - private Date removalTime;  
102 -  
103 -  
104 -  
105 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/business/hi/task/inst/service/HiTaskInstService.java deleted
1 -package cn.iocoder.yudao.module.business.hi.task.inst.service;  
2 -  
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.collection.CollUtil;  
5 -import cn.hutool.core.util.ObjectUtil;  
6 -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;  
7 -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;  
8 -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;  
9 -import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;  
10 -import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;  
11 -import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;  
12 -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;  
13 -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;  
14 -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;  
15 -import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;  
16 -import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;  
17 -import lombok.extern.slf4j.Slf4j;  
18 -import org.flowable.bpmn.model.*;  
19 -import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl;  
20 -import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext;  
21 -import org.flowable.common.engine.impl.javax.el.ExpressionFactory;  
22 -import org.flowable.common.engine.impl.javax.el.ValueExpression;  
23 -import org.flowable.engine.HistoryService;  
24 -import org.flowable.engine.RepositoryService;  
25 -import org.flowable.engine.history.HistoricProcessInstance;  
26 -import org.flowable.task.api.history.HistoricTaskInstance;  
27 -import org.flowable.variable.api.history.HistoricVariableInstance;  
28 -import org.springframework.stereotype.Service;  
29 -  
30 -import javax.annotation.Resource;  
31 -import java.util.HashMap;  
32 -import java.util.LinkedList;  
33 -import java.util.List;  
34 -import java.util.Map;  
35 -import java.util.function.Function;  
36 -import java.util.stream.Collectors;  
37 -  
38 -/**  
39 - * [ ]  
40 - *  
41 - * @author 孟凯  
42 - * @version 1.0  
43 - */  
44 -@Slf4j  
45 -@Service  
46 -public class HiTaskInstService {  
47 -  
48 - @Resource  
49 - private HistoryService historyService;  
50 - @Resource  
51 - private RepositoryService repositoryService;  
52 - @Resource  
53 - private BpmTaskAssignRuleMapper bpmTaskAssignRuleMapper;  
54 - @Resource  
55 - private BpmProcessInstanceService processInstanceService;  
56 - @Resource  
57 - private AdminUserMapper adminUserApi;  
58 - @Resource  
59 - private DeptMapper deptMapper;  
60 -  
61 - /**  
62 - * 获取任务具体流程信息  
63 - *  
64 - * @param taskList 任务信息  
65 - * @param approved 任意选择条件  
66 - *  
67 - * @return 返回流程信息  
68 - */  
69 - @TenantIgnore  
70 - public List<BpmTaskRespVO> taskGetComment(List<BpmTaskExtDO> taskList, Object approved) {  
71 - BpmTaskExtDO task = taskList.get(taskList.size() - 1);  
72 - Map<String, BpmTaskExtDO> bpmTaskMap =  
73 - taskList.stream().collect(Collectors.toMap(BpmTaskExtDO::getTaskId, Function.identity()));  
74 - // 获得 ProcessInstance Map  
75 - HistoricProcessInstance procInst =  
76 - processInstanceService.getHistoricProcessInstance(task.getProcessInstanceId());  
77 - Map<Long, AdminUserDO> userDoMap =  
78 - adminUserApi.selectList().stream().collect(Collectors.toMap(AdminUserDO::getId, Function.identity()));  
79 - Map<Long, DeptDO> deptMap =  
80 - deptMapper.selectList().stream().collect(Collectors.toMap(DeptDO::getId, Function.identity()));  
81 - List<HistoricTaskInstance> hisTaskInstList =  
82 - historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId())  
83 - .orderByHistoricTaskInstanceStartTime().desc().list();  
84 - LinkedList<BpmTaskRespVO> bpmTaskRespVOList =  
85 - nowTaskFormat(procInst, hisTaskInstList, bpmTaskMap, userDoMap, deptMap);  
86 -  
87 - //ProcessInstanceId流程实例  
88 - String procInstId = task.getProcessInstanceId();  
89 - List<BpmTaskAssignRuleDO> tmpBpmTaskAssignRuleDOList =  
90 - bpmTaskAssignRuleMapper.selectListByProcessDefinitionId(task.getProcessDefinitionId(), null);  
91 - List<HistoricVariableInstance> hisVarInstList =  
92 - historyService.createHistoricVariableInstanceQuery().processInstanceId(procInstId).list();  
93 - Map<String, Object> hisVarInstMap = new HashMap<>();  
94 - for (HistoricVariableInstance hisVarInst : hisVarInstList) {  
95 - hisVarInstMap.put(hisVarInst.getVariableName(), hisVarInst.getValue());  
96 - }  
97 - hisVarInstMap.put("approved", approved);  
98 - //获取bpm(模型)对象  
99 - BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());  
100 - getFlow(bpmnModel, task.getTaskDefKey(), tmpBpmTaskAssignRuleDOList, bpmTaskRespVOList, userDoMap, deptMap,  
101 - hisVarInstMap);  
102 - return bpmTaskRespVOList;  
103 - }  
104 -  
105 - /**  
106 - * 格式化任务信息  
107 - */  
108 - private LinkedList<BpmTaskRespVO> nowTaskFormat(HistoricProcessInstance procInst, List<HistoricTaskInstance> taskList,  
109 - Map<String, BpmTaskExtDO> bpmTaskExtDoMap, Map<Long, AdminUserDO> userMap, Map<Long, DeptDO> deptMap) {  
110 - LinkedList<BpmTaskRespVO> bpmTaskRespVOList = new LinkedList<>();  
111 - for (HistoricTaskInstance hisTaskInst : taskList) {  
112 - BpmTaskRespVO respVO = initRespVo(hisTaskInst, bpmTaskExtDoMap, procInst, userMap);  
113 - AdminUserDO user = userMap.get(Long.valueOf(hisTaskInst.getAssignee()));  
114 - if (BeanUtil.isNotEmpty(user)) {  
115 - respVO.setAssigneeUser(setUser(user));  
116 - DeptDO dept = deptMap.get(user.getDeptId());  
117 - if (BeanUtil.isNotEmpty(dept)) {  
118 - respVO.getAssigneeUser().setDeptName(dept.getName());  
119 - }  
120 - }  
121 - bpmTaskRespVOList.addFirst(respVO);  
122 - }  
123 - return bpmTaskRespVOList;  
124 - }  
125 -  
126 - private BpmTaskRespVO initRespVo(Object taskInst, Map<String, BpmTaskExtDO> bpmTaskExtDoMap,  
127 - HistoricProcessInstance procInst, Map<Long, AdminUserDO> userMap) {  
128 - BpmTaskRespVO respVO = new BpmTaskRespVO();  
129 - if (taskInst instanceof HistoricTaskInstance) {  
130 - respVO = setBpmnTaskRespVo((HistoricTaskInstance)taskInst);  
131 - BeanUtil.copyProperties(taskInst, respVO);  
132 - BpmTaskExtDO bpmTaskExtDO = bpmTaskExtDoMap.get(respVO.getId());  
133 - if (ObjectUtil.isNotEmpty(bpmTaskExtDO)) {  
134 - BeanUtil.copyProperties(bpmTaskExtDO, respVO);  
135 - respVO.setId(bpmTaskExtDO.getTaskId());  
136 - }  
137 - }  
138 - if (taskInst instanceof BpmTaskExtDO) {  
139 - respVO = setBpmnTaskRespVo((BpmTaskExtDO)taskInst);  
140 - BeanUtil.copyProperties(taskInst, respVO);  
141 - }  
142 - // copyTo(bpmTaskExtDO, respVO);  
143 - if (procInst != null) {  
144 - AdminUserDO startUser = userMap.get(Long.valueOf(procInst.getStartUserId()));  
145 - if (BeanUtil.isEmpty(startUser)) {  
146 - throw new RuntimeException("查找不到审批用户!!!");  
147 - }  
148 - respVO.setProcessInstance(setProcInst(procInst, startUser));  
149 - }  
150 - return respVO;  
151 - }  
152 -  
153 - private BpmTaskRespVO.User setUser(AdminUserDO bean) {  
154 - if (bean == null) {  
155 - return null;  
156 - }  
157 -  
158 - BpmTaskRespVO.User user = new BpmTaskRespVO.User();  
159 -  
160 - user.setId(bean.getId());  
161 - user.setNickname(bean.getNickname());  
162 - user.setDeptId(bean.getDeptId());  
163 -  
164 - return user;  
165 -  
166 - }  
167 -  
168 - private BpmTaskRespVO setBpmnTaskRespVo(BpmTaskExtDO bean) {  
169 - if (bean == null) {  
170 - return null;  
171 - }  
172 -  
173 - BpmTaskRespVO bpmTaskRespVO = new BpmTaskRespVO();  
174 -  
175 - bpmTaskRespVO.setDefinitionKey(bean.getTaskDefKey());  
176 - bpmTaskRespVO.setId(bean.getTaskId());  
177 - bpmTaskRespVO.setName(bean.getName());  
178 - bpmTaskRespVO.setCreateTime(bean.getCreateTime());  
179 -  
180 - return bpmTaskRespVO;  
181 - }  
182 -  
183 - public BpmTaskRespVO setBpmnTaskRespVo(HistoricTaskInstance bean) {  
184 - if (bean == null) {  
185 - return null;  
186 - }  
187 -  
188 - BpmTaskRespVO bpmTaskRespVO = new BpmTaskRespVO();  
189 -  
190 - bpmTaskRespVO.setDefinitionKey(bean.getTaskDefinitionKey());  
191 - bpmTaskRespVO.setId(bean.getId());  
192 - bpmTaskRespVO.setName(bean.getName());  
193 - bpmTaskRespVO.setClaimTime(bean.getClaimTime());  
194 - bpmTaskRespVO.setCreateTime(bean.getCreateTime());  
195 - bpmTaskRespVO.setEndTime(bean.getEndTime());  
196 - bpmTaskRespVO.setDurationInMillis(bean.getDurationInMillis());  
197 -  
198 - return bpmTaskRespVO;  
199 - }  
200 -  
201 - public BpmTaskTodoPageItemRespVO.ProcessInstance setProcInst(HistoricProcessInstance processInstance,  
202 - AdminUserDO startUser) {  
203 - if (processInstance == null && startUser == null) {  
204 - return null;  
205 - }  
206 -  
207 - BpmTaskTodoPageItemRespVO.ProcessInstance processInstanceResult =  
208 - new BpmTaskTodoPageItemRespVO.ProcessInstance();  
209 -  
210 - if (processInstance != null) {  
211 - processInstanceResult.setId(processInstance.getId());  
212 - processInstanceResult.setName(processInstance.getName());  
213 - if (processInstance.getStartUserId() != null) {  
214 - processInstanceResult.setStartUserId(Long.parseLong(processInstance.getStartUserId()));  
215 - }  
216 - processInstanceResult.setProcessDefinitionId(processInstance.getProcessDefinitionId());  
217 - }  
218 - if (startUser != null) {  
219 - processInstanceResult.setStartUserNickname(startUser.getNickname());  
220 - }  
221 -  
222 - return processInstanceResult;  
223 - }  
224 -  
225 - private void getFlow(BpmnModel bpmnModel, String taskDefKey, List<BpmTaskAssignRuleDO> tmpBpmTaskAssignRuleDOList,  
226 - LinkedList<BpmTaskRespVO> bpmTaskRespVOList, Map<Long, AdminUserDO> userDoMap, Map<Long, DeptDO> deptMap,  
227 - Map<String, Object> taskVarMap) {  
228 - //传节点定义key获取当前节点  
229 - FlowNode flowNode = (FlowNode)bpmnModel.getFlowElement(taskDefKey);  
230 - //输出连线  
231 - List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();  
232 - //遍历返回下一个节点信息  
233 - for (SequenceFlow outgoingFlow : outgoingFlows) {  
234 - //类型自己判断(获取下个节点是网关还是节点)  
235 - FlowElement targetFlowElement = outgoingFlow.getTargetFlowElement();  
236 - getFlow(bpmnModel, targetFlowElement, tmpBpmTaskAssignRuleDOList, bpmTaskRespVOList, userDoMap, deptMap,  
237 - taskVarMap);  
238 - }  
239 - }  
240 -  
241 - private void getFlow(BpmnModel bpmnModel, FlowElement targetFlowElement,  
242 - List<BpmTaskAssignRuleDO> tmpBpmTaskAssignRuleDOList, LinkedList<BpmTaskRespVO> bpmTaskRespVOList,  
243 - Map<Long, AdminUserDO> userDoMap, Map<Long, DeptDO> deptMap, Map<String, Object> taskVarMap) {  
244 - // 下一个taskDefKey  
245 - String nextTaskDefKey = null;  
246 - //下个是节点  
247 - if (targetFlowElement instanceof UserTask) {  
248 - // 判断是否是为并行任务  
249 - List<BpmTaskAssignRuleDO> bpmTaskAssignRuleDOList = tmpBpmTaskAssignRuleDOList.stream().filter(  
250 - bpmTaskAssignRuleDO -> bpmTaskAssignRuleDO.getTaskDefinitionKey().equals(targetFlowElement.getId()))  
251 - .collect(Collectors.toList());  
252 - if (CollUtil.isEmpty(bpmTaskAssignRuleDOList)) {  
253 - throw new RuntimeException("任务key不存在!!!");  
254 - }  
255 - for (BpmTaskAssignRuleDO bpmTaskAssignRuleDO : bpmTaskAssignRuleDOList) {  
256 - nextTaskDefKey = bpmTaskAssignRuleDO.getTaskDefinitionKey();  
257 - for (Long userId : bpmTaskAssignRuleDO.getOptions()) {  
258 - BpmTaskRespVO bpmTaskRespVO =  
259 - (BpmTaskRespVO)new BpmTaskRespVO().setName(targetFlowElement.getName());  
260 - bpmTaskRespVOList.addLast(bpmTaskRespVO);  
261 - AdminUserDO adminUserDO = userDoMap.get(userId);  
262 - DeptDO deptDO = deptMap.get(adminUserDO.getDeptId());  
263 - bpmTaskRespVO.setAssigneeUser(setUser(adminUserDO));  
264 - bpmTaskRespVO.getAssigneeUser().setDeptName(deptDO.getName());  
265 - // edit by 芋艿 TODO  
266 -// if (!bpmTaskAssignRuleDO.getType().equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType())  
267 -// && !bpmTaskAssignRuleDO.getType().equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType())) {  
268 -// break;  
269 -// }  
270 - }  
271 - }  
272 - getFlow(bpmnModel, nextTaskDefKey, tmpBpmTaskAssignRuleDOList, bpmTaskRespVOList, userDoMap, deptMap,  
273 - taskVarMap);  
274 - // 下个节点是网关(调用下面的方法)  
275 - } else if (targetFlowElement instanceof ExclusiveGateway) {  
276 - String defaultFlow = ((ExclusiveGateway)targetFlowElement).getDefaultFlow();  
277 - FlowElement nexFlowElement = getExclusiveGateway(targetFlowElement, taskVarMap, defaultFlow);  
278 - getFlow(bpmnModel, nexFlowElement, tmpBpmTaskAssignRuleDOList, bpmTaskRespVOList, userDoMap, deptMap,  
279 - taskVarMap);  
280 - }  
281 - }  
282 -  
283 - /**  
284 - * 获取排他网关分支名称、分支表达式、下一级任务节点  
285 - *  
286 - * @param flowElement 任务节点  
287 - * @param taskVarMap 审批数据  
288 - */  
289 - private FlowElement getExclusiveGateway(FlowElement flowElement, Map<String, Object> taskVarMap,  
290 - String defaultFlow) {  
291 - // 获取所有网关分支  
292 - List<SequenceFlow> targetFlows = ((ExclusiveGateway)flowElement).getOutgoingFlows();  
293 - Boolean elExpressionFlag = Boolean.FALSE;  
294 - FlowElement sequenceFlowResult = null;  
295 - FlowElement defaultSequenceFlow = null;  
296 - // 循环每个网关分支  
297 - for (SequenceFlow sequenceFlow : targetFlows) {  
298 - if (defaultFlow.equals(sequenceFlow.getId())) {  
299 - defaultSequenceFlow = sequenceFlow.getTargetFlowElement();  
300 - continue;  
301 - }  
302 - elExpressionFlag = elExpression(sequenceFlow.getConditionExpression(), taskVarMap);  
303 - if (elExpressionFlag) {  
304 - // 获取下一个网关和节点数据  
305 - FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();  
306 - // 网关的下个节点是用户节点  
307 - if (targetFlowElement instanceof UserTask) {  
308 - sequenceFlowResult = targetFlowElement;  
309 - break;  
310 - } else if (targetFlowElement instanceof EndEvent) {  
311 - log.info("排他网关的下一节点是EndEvent: 结束节点");  
312 - } else if (targetFlowElement instanceof ServiceTask) {  
313 - log.info("排他网关的下一节点是ServiceTask: 内部方法");  
314 - } else if (targetFlowElement instanceof ExclusiveGateway) {  
315 - defaultFlow = ((ExclusiveGateway)targetFlowElement).getDefaultFlow();  
316 - return getExclusiveGateway(targetFlowElement, taskVarMap, defaultFlow);  
317 - } else if (targetFlowElement instanceof SubProcess) {  
318 - log.info("排他网关的下一节点是SubProcess: 内部子流程");  
319 - }  
320 - }  
321 - }  
322 - if (!elExpressionFlag) {  
323 - if (defaultSequenceFlow instanceof UserTask) {  
324 - sequenceFlowResult = defaultSequenceFlow;  
325 - } else if (defaultSequenceFlow instanceof ExclusiveGateway) {  
326 - defaultFlow = ((ExclusiveGateway)defaultSequenceFlow).getDefaultFlow();  
327 - return getExclusiveGateway(defaultSequenceFlow, taskVarMap, defaultFlow);  
328 - }  
329 - }  
330 - return sequenceFlowResult;  
331 - }  
332 -  
333 - /**  
334 - * 网关分叉条件判断,网关分叉,必须要有默认出口  
335 - *  
336 - * @param elExpression el表达式  
337 - * @param variableMap 流程所有变量  
338 - *  
339 - * @return 返回true或false  
340 - */  
341 - private Boolean elExpression(String elExpression, Map<String, Object> variableMap) {  
342 - ExpressionFactory factory = new ExpressionFactoryImpl();  
343 - SimpleContext context = new SimpleContext();  
344 - for (String k : variableMap.keySet()) {  
345 - if (variableMap.get(k) != null) {  
346 - context.setVariable(k,  
347 - factory.createValueExpression(variableMap.get(k), variableMap.get(k).getClass()));  
348 - }  
349 - }  
350 - ValueExpression e = factory.createValueExpression(context, elExpression, Boolean.class);  
351 - //el表达式和variables得到的结果  
352 - return (Boolean)e.getValue(context);  
353 - }  
354 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/BpmActivityMapper.xml
@@ -17,11 +17,6 @@ @@ -17,11 +17,6 @@
17 </foreach> 17 </foreach>
18 </delete> 18 </delete>
19 19
20 - <select id="listAll" resultType="cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmActivityDO">  
21 - SELECT *  
22 - FROM act_hi_taskinst;  
23 - </select>  
24 -  
25 <select id="listAllByProcInstIdAndDelete" 20 <select id="listAllByProcInstIdAndDelete"
26 resultType="cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmActivityDO"> 21 resultType="cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmActivityDO">
27 SELECT id_ AS `id`, 22 SELECT id_ AS `id`,
yudao-ui-admin/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue
@@ -116,10 +116,11 @@ export default { @@ -116,10 +116,11 @@ export default {
116 let todoActivity = activityList.find(m => !m.endTime) // 找到待办的任务 116 let todoActivity = activityList.find(m => !m.endTime) // 找到待办的任务
117 let endActivity = activityList[activityList.length - 1] // 获得最后一个任务 117 let endActivity = activityList[activityList.length - 1] // 获得最后一个任务
118 // debugger 118 // debugger
119 - console.log(this.bpmnModeler.getDefinitions().rootElements[0].flowElements); 119 + // console.log(this.bpmnModeler.getDefinitions().rootElements[0].flowElements);
120 this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => { 120 this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
121 let activity = activityList.find(m => m.key === n.id) // 找到对应的活动 121 let activity = activityList.find(m => m.key === n.id) // 找到对应的活动
122 if (n.$type === 'bpmn:UserTask') { // 用户任务 122 if (n.$type === 'bpmn:UserTask') { // 用户任务
  123 + debugger
123 if (!activity) { 124 if (!activity) {
124 return; 125 return;
125 } 126 }
yudao-ui-admin/src/views/bpm/processInstance/detail.vue
@@ -232,21 +232,27 @@ export default { @@ -232,21 +232,27 @@ export default {
232 this.auditForms = []; 232 this.auditForms = [];
233 getTaskListByProcessInstanceId(this.id).then(response => { 233 getTaskListByProcessInstanceId(this.id).then(response => {
234 // 审批记录 234 // 审批记录
235 - this.tasks = response.data; 235 + this.tasks = [];
  236 + // 移除已取消的审批
  237 + response.data.forEach(task => {
  238 + if (task.result !== 4) {
  239 + this.tasks.push(task);
  240 + }
  241 + });
236 // 排序,将未完成的排在前面,已完成的排在后面; 242 // 排序,将未完成的排在前面,已完成的排在后面;
237 - // this.tasks.sort((a, b) => {  
238 - // // 有已完成的情况,按照完成时间倒序  
239 - // if (a.endTime && b.endTime) {  
240 - // return b.endTime - a.endTime;  
241 - // } else if (a.endTime) {  
242 - // return 1;  
243 - // } else if (b.endTime) {  
244 - // return -1;  
245 - // // 都是未完成,按照创建时间倒序  
246 - // } else {  
247 - // return b.createTime - a.createTime;  
248 - // }  
249 - // }); 243 + this.tasks.sort((a, b) => {
  244 + // 有已完成的情况,按照完成时间倒序
  245 + if (a.endTime && b.endTime) {
  246 + return b.endTime - a.endTime;
  247 + } else if (a.endTime) {
  248 + return 1;
  249 + } else if (b.endTime) {
  250 + return -1;
  251 + // 都是未完成,按照创建时间倒序
  252 + } else {
  253 + return b.createTime - a.createTime;
  254 + }
  255 + });
250 256
251 // 需要审核的记录 257 // 需要审核的记录
252 const userId = store.getters.userId; 258 const userId = store.getters.userId;