1. /**
    2. * 通过任务id跳转到上一步任务
    3. * @param taskId 任务id
    4. * @return
    5. */
    6. @Override
    7. public String executeAndJumpOneTask(String taskId) {
    8. try {
    9. /** 取得当前需要撤回的任务 ***/
    10. HistoricTaskInstance currTask = historyService
    11. .createHistoricTaskInstanceQuery().taskId(taskId)
    12. .singleResult();
    13. /** 获取当前任务所属流程定义,进而获取流程定义key **/
    14. ProcessDefinition processDefinition = repositoryService.getProcessDefinition(currTask.getProcessDefinitionId());
    15. /** 获取当前任务的前一个任务id **/
    16. String taskHisId = getPrevTaskByCurrentTaskId(processDefinition.getKey(),currTask.getProcessInstanceId(),taskId);
    17. /** 取回流程节点 taskId当前任务id,taskHisId需要取回的任务的id***/
    18. callBackProcess(taskId,taskHisId);
    19. //删除历史流程走向记录
    20. historyService.deleteHistoricTaskInstance(currTask.getId());
    21. historyService.deleteHistoricTaskInstance(taskId);
    22. } catch (Exception e) {
    23. return "撤回失败 :" + e.getMessage();
    24. }
    25. return "success";
    26. }
    27. /**
    28. * 取回流程中当前任务的上一个任务
    29. * @param taskId 当前任务ID
    30. * @param activityId 取回节点ID (这里不一定是上一个流程节点id)
    31. * @throws Exception
    32. */
    33. public void callBackProcess(String taskId, String activityId) throws Exception {
    34. if (StringUtils.isEmpty(activityId)) {
    35. throw new Exception("目标节点ID为空!");
    36. }
    37. //通过任务id获取流程实例id
    38. String processInstanceId = findProcessInstanceByTaskId(taskId).getId();
    39. //通过任务id获取任务定义key
    40. String taskDefinitionKey = findTaskById(taskId).getTaskDefinitionKey();
    41. // 查找所有并行任务节点,同时取回
    42. List<Task> taskList = findTaskListByKey(processInstanceId, taskDefinitionKey);
    43. for (Task task : taskList) {
    44. commitProcess(task.getId(), null, activityId);
    45. }
    46. }
    47. /**
    48. * 提交流程/流程转向
    49. * @param taskId 当前任务ID
    50. * @param variables 流程变量
    51. * @param activityId 流程转向执行任务节点ID,此参数为空,默认为提交操作
    52. * @throws Exception
    53. */
    54. private void commitProcess(String taskId, Map<String, Object> variables,
    55. String activityId) throws Exception {
    56. if (variables == null) {
    57. variables = new HashMap<String, Object>();
    58. }
    59. // 跳转节点为空,默认提交操作
    60. if (StringUtils.isEmpty(activityId)) {
    61. taskService.complete(taskId, variables);
    62. } else {
    63. // 流程转向操作
    64. turnTransition(taskId, activityId, variables);
    65. }
    66. }
    67. /**
    68. * 流程转向操作
    69. * @param taskId 当前任务ID
    70. * @param activityId 目标节点任务ID
    71. * @param variables 流程变量
    72. * @throws Exception
    73. */
    74. private void turnTransition(String taskId, String activityId, Map<String, Object> variables) throws Exception {
    75. // 当前节点
    76. ActivityImpl currActivity = findActivitiImpl(taskId, null);
    77. // 清空当前流向
    78. List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);
    79. // 创建新流向,即将当前节点的流向指向需要跳转的节点流向
    80. TransitionImpl newTransition = currActivity.createOutgoingTransition();
    81. // 目标任务的活动节点
    82. ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
    83. // 设置新流向的目标节点,将当前节点指向需要跳转的节点
    84. newTransition.setDestination(pointActivity);
    85. /** 执行转向任务 这种方法转向执行任务获取到的任务id将不再是原来的任务id ***/
    86. // todo 这里需要将撤回操作的操作人定义为当前登录操作的用户(即撤回用户)???? ***/
    87. //fixme 算法待修改
    88. taskService.complete(taskId, variables);
    89. /** 任务执行完需要将任务还原 ***/
    90. // 删除目标节点新流入
    91. pointActivity.getIncomingTransitions().remove(newTransition);
    92. // 还原以前流向
    93. restoreTransition(currActivity, oriPvmTransitionList);
    94. }
    95. /**
    96. * 还原指定活动节点流向
    97. * @param activityImpl 活动节点
    98. * @param oriPvmTransitionList 原有节点流向集合
    99. */
    100. private void restoreTransition(ActivityImpl activityImpl, List<PvmTransition> oriPvmTransitionList) {
    101. // 清空现有流向
    102. List<PvmTransition> pvmTransitionList = activityImpl .getOutgoingTransitions();
    103. pvmTransitionList.clear();
    104. // 还原以前流向
    105. for (PvmTransition pvmTransition : oriPvmTransitionList) {
    106. pvmTransitionList.add(pvmTransition);
    107. }
    108. }
    109. /**
    110. * 根据任务ID和目标节点ID获取活动节点
    111. * @param taskId 任务ID
    112. * @param activityId 活动节点ID,如果为null或"",则默认查询当前活动节点 如果为"end",则查询结束节点
    113. * @return
    114. * @throws Exception
    115. */
    116. private ActivityImpl findActivitiImpl(String taskId, String activityId) throws Exception {
    117. /** 通过当前任务id,取得流程定义 **/
    118. ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);
    119. /** 获取当前活动节点ID,如果目标节点为空,获取当前活动节点 ,不为空,获取目标节点**/
    120. if (StringUtils.isEmpty(activityId)) {
    121. activityId = findTaskById(taskId).getTaskDefinitionKey();
    122. }else{
    123. HistoricTaskInstance currTask = historyService.createHistoricTaskInstanceQuery().taskId(activityId) .singleResult();
    124. activityId = currTask.getTaskDefinitionKey();
    125. }
    126. /** 根据流程定义,获取该流程实例的结束节点 **/
    127. if (activityId.toUpperCase().equals("END")) {
    128. for (ActivityImpl activityImpl : processDefinition.getActivities()) {
    129. /** 获取所有当前活动节点的输出节点 **/
    130. List<PvmTransition> pvmTransitionList = activityImpl .getOutgoingTransitions();
    131. /** 如果当前节点没有输出路径了,那么当前节点为结束节点 **/
    132. if (pvmTransitionList.isEmpty()) {
    133. return activityImpl;
    134. }
    135. }
    136. }
    137. /** 根据节点ID,获取对应的活动节点 **/
    138. ActivityImpl activityImpl = ((ProcessDefinitionImpl)processDefinition).findActivity(activityId);
    139. return activityImpl;
    140. }
    141. /**
    142. * 清空指定活动节点流向
    143. * @param activityImpl 活动节点
    144. * @return 节点流向集合
    145. */
    146. private List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
    147. // 存储当前节点所有流向临时变量
    148. List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
    149. // 获取当前节点所有流向,存储到临时变量,然后清空
    150. List<PvmTransition> pvmTransitionList = activityImpl .getOutgoingTransitions();
    151. for (PvmTransition pvmTransition : pvmTransitionList) {
    152. oriPvmTransitionList.add(pvmTransition);
    153. }
    154. pvmTransitionList.clear();
    155. return oriPvmTransitionList;
    156. }
    157. /**
    158. * 根据任务ID获取流程定义
    159. * @param taskId 任务ID
    160. * @return 流程定义实体
    161. * @throws Exception
    162. */
    163. public ProcessDefinitionEntity findProcessDefinitionEntityByTaskId( String taskId) throws Exception {
    164. // 取得流程定义
    165. ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService) .getDeployedProcessDefinition(findTaskById(taskId) .getProcessDefinitionId());
    166. if (processDefinition == null) {
    167. throw new Exception("流程定义未找到!");
    168. }
    169. return processDefinition;
    170. }
    171. /**
    172. * 根据任务ID获得任务实例
    173. * @param taskId 任务ID
    174. * @return 任务实例
    175. * @throws Exception
    176. */
    177. private TaskEntity findTaskById(String taskId) throws Exception {
    178. TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
    179. if (task == null) {
    180. throw new Exception("任务实例未找到!");
    181. }
    182. return task;
    183. }
    184. /**
    185. * 根据流程实例ID和任务key值查询所有同级任务集合
    186. * @param processInstanceId 流程实例id
    187. * @param key 任务定义key
    188. * @return
    189. * */
    190. private List<Task> findTaskListByKey(String processInstanceId, String key) {
    191. return taskService.createTaskQuery().processInstanceId(processInstanceId).taskDefinitionKey(key).list();
    192. }
    193. /**
    194. * 根据任务ID获取对应的流程实例
    195. * @param taskId 任务ID
    196. * @return
    197. * @throws Exception
    198. */
    199. public ProcessInstance findProcessInstanceByTaskId(String taskId) throws Exception {
    200. // 找到流程实例
    201. ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId( findTaskById(taskId).getProcessInstanceId()) .singleResult();
    202. if (processInstance == null) {
    203. throw new Exception("流程实例未找到!");
    204. }
    205. return processInstance;
    206. }
    207. /**
    208. * 通过流程定义key和当前任务id来获取流程图中,当前任务所处节点的上一个节点信息
    209. * @param dkey
    210. * @param currentTaskId
    211. * 1.通过taskId获得executionid
    212. * 2.通过executionId获得currentActivityId
    213. * 3.通过currentActivityId获得ActivityImpl对象
    214. * 4.通过ActivityImpl对象获得incomingTransitions()
    215. * 5.通过transitionImpl对象获得source(ActivityImpl类型)
    216. * 6.通过ActivityImpl获得activityId
    217. * 7.通过activityId(这个activityId就是当前任务所在节点的前一个节点了)在流程历史里查询对应的task。
    218. * @return
    219. */
    220. public List<String> getPrevActivitiByCurrentTaskId(String dkey,String currentTaskId){
    221. /** 用于存储当前节点的所有上一个节点id **/
    222. List<String> preTaskIds = new ArrayList<>();
    223. //任务查询条件
    224. TaskQuery taskQuery = taskService.createTaskQuery();
    225. //流程实力查询条件
    226. ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
    227. //获取当前任务
    228. Task task = taskQuery.taskId(currentTaskId).singleResult();
    229. if(task != null){
    230. //获取当前流程任务的执行路线id
    231. String executionId = task.getExecutionId();
    232. //获取运行时流程执行实例
    233. Execution execution = executionQuery.executionId(executionId).singleResult();
    234. 当前流程节点Id信息
    235. String activityId = execution.getActivityId();
    236. /** 根据流程定义的key获取流程定义所有的activity模型时期对象,即获取待所有的流程组件对象信息 ***/
    237. List<ActivityImpl> allActivities = getAllActivities(dkey);
    238. for(ActivityImpl activity : allActivities){
    239. /** 当获取到当前节点的时候,才执行之后的操作,主要需要通过当前节点来获取指向该节点的上一个节点 **/
    240. String id = activity.getId();
    241. if(!id.equals(activityId)){
    242. continue;
    243. }
    244. /** PVM
    245. PVM:Process Virtal Machine,流程虚拟机API暴露了流程虚拟机的POJO核心,
    246. 流程虚拟机API描述了一个工作流流程必备的组件,这些组件包括:
    247. PvmProcessDefinition:流程的定义,形象点说就是用户画的那个图。静态含义。
    248. PvmProcessInstance:流程实例,用户发起的某个PvmProcessDefinition的一个实例,动态含义。
    249. PvmActivity:流程中的一个节点
    250. PvmTransition:衔接各个节点之间的路径,形象点说就是图中各个节点之间的连接线。
    251. PvmEvent:流程执行过程中触发的事件 **/
    252. /** 这里获取 当前活动的所有入口 ,首先获取指向该节点的所有路径**/
    253. List<PvmTransition> incomingTransitions = activity.getIncomingTransitions();
    254. /** 遍历路径,获取路径的源节点, 然后通过远点获取源节点的id,保存到集合中**/
    255. for(PvmTransition transition : incomingTransitions){
    256. //获取路径源节点
    257. PvmActivity source = transition.getSource();
    258. //源节点id
    259. String preTaskId = source.getId();
    260. //加入集合
    261. preTaskIds.add(preTaskId);
    262. }
    263. }
    264. }
    265. return preTaskIds;
    266. }
    267. /**
    268. * 通过当前任务id获取前一个任务
    269. * @param dkey 流程定义key
    270. * @param processInstanceId 流程实例id
    271. * @param taskId 当前任务id
    272. * @return 离当当前任务最近的上一个任务
    273. */
    274. public String getPrevTaskByCurrentTaskId(String dkey,String processInstanceId,String taskId){
    275. HistoricTaskInstance historicTaskInstance = null;
    276. /** 这里是获取当前流程定义所对应流程图中指向当前节点的所有上一个节点id **/
    277. List<String> prevTaskIds = getPrevActivitiByCurrentTaskId(dkey, taskId);
    278. if(prevTaskIds != null && prevTaskIds.size() > 0){
    279. /** 用于存放当前任务的历史节点信息,在后面进行时间比较,获取最新的上一个节点 **/
    280. Map<String,HistoricActivityInstance> instanceMap = new HashMap<>();
    281. /** 遍历当前节点的所有指向当前节点的源节点。判断最新的节点是哪个 **/
    282. for(String preact : prevTaskIds){
    283. /** 根据当前流程实例id获取 HistoricActivityInstance ,HistoricActivityInstance包含一个活动(流程上的节点)的执行信息 **/
    284. HistoricActivityInstance activityInstance = historyService.createHistoricActivityInstanceQuery()
    285. .processInstanceId(processInstanceId).activityId(preact).singleResult();
    286. if(activityInstance == null){
    287. continue;
    288. }
    289. /** 获取当前节点实例的结束时间 ***/
    290. Date endTime = activityInstance.getEndTime();
    291. /** 这里首先获取上一个遍历存放的历史节点信息**/
    292. HistoricActivityInstance activityInstance1 = instanceMap.get(taskId);
    293. /** 如果已存放上一个节点信息,那么,进行时间比较,放入最近的时间的历史节点信息,反之加入集合,用于下一个循环比较 **/
    294. if(activityInstance1 != null){
    295. Date endTime1 = activityInstance1.getEndTime();
    296. if(endTime1.after(endTime)){
    297. continue;
    298. }
    299. instanceMap.put(taskId,activityInstance1);
    300. }else{
    301. instanceMap.put(taskId,activityInstance);
    302. }
    303. }
    304. /** 获取前一步通过时间比较获取的结束时间最近的当前任务节点的上一个节点 **/
    305. HistoricActivityInstance activityInstance = instanceMap.get(taskId);
    306. /** 获取挂接在当前历史节点下的当前任务的上一个任务 **/
    307. if(activityInstance != null){
    308. String hisTaskId = activityInstance.getTaskId();
    309. historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).taskId(hisTaskId).singleResult();
    310. }
    311. }
    312. return historicTaskInstance.getId();
    313. }
    314. /**
    315. * activity 是模型时期对象
    316. *
    317. * @params: [dkey 流程定义的key]
    318. * @return:
    319. * @描述: 根据流程定义的key获取流程定义所有的activity
    320. *
    321. * 释义:在设计流程时每一个组件在Activiti中都可以称之为——Activity,
    322. * 部署流程时引擎把XML文件保存到数据库;
    323. * 当启动流程、完成任务时会从数据库读取XML并转换为Java对象,
    324. * 如果想在处理任务时获取任务的一些配置,
    325. * 例如某个任务配置了哪些监听器或者条件Flow配置了什么条件表达式,
    326. * 可以获取具体的Activity。
    327. */
    328. public List<ActivityImpl> getAllActivities(String dkey) {
    329. List<ActivityImpl> activities = new ArrayList<>();
    330. /** 根据流程定义的key获取流程定义对象 **/
    331. ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(dkey).singleResult();
    332. ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
    333. .getDeployedProcessDefinition(definition.getId());
    334. /** 获取所有的activity -- 即获取所有节点信息**/
    335. activities = processDefinition.getActivities();
    336. return activities;
    337. }