關於Activiti工作流委托代理人方案實現
作者:Jesai
生活就要像寫代碼,一行行的去寫。認認真真,整整齊齊,漂漂亮亮,干干凈凈,簡潔明了!
夢想還是要有的,萬一就實現了呢
前言:
現實生活里面,我們上司可能工作繁忙,經常不在公司或者出差,這個時候,上司就會委托一個得力助手在他不在公司的這段時間處理一些事務。包括文件審批,戰略決策,財務審批等工作。這個時候,被委托人擁有委托人部分或者全部的權力。權力的大小取決於委托人授予你多少。那么,這個委托人是擁有絕對主動權的,也就是說委托人可以隨時回收被委托人的全力。當委托人不授予(回收)權力的時候,被委托人就沒有決策的能力了。還有一種情況就是,委托人在某一段時間內進行委托操作。一旦超過了或者不在這個委托時間段里面,被委托也是沒有委托權限來決策的。委托的方式也可能有幾種,第一種:委托具體的某一個或者具體某一項決策。第二種:委托某一部分或者某一個領域決策。第三種:全權委托。通俗的講,第一種就是一次性行為,整個周期就是這件事情的開始到結束。第二種就是某一個方面,這個方面你來管理決策。第三種就是管理委托人所擁有的一切決策和能力。
要素:
(1)委托的范圍(某一項、某一類、全部)
(2)委托的時間(無限期、某一時間段)
(3)委托的絕對主動權(委托人擁有絕對主動權,隨時回收或者委托)
(4)存據(委托有效的依據、不能越權辦事,也不能先辦事后授權)
當前已有方案:
當前已有工作流可以實現的方案是基於某一個task(任務)的委托。比如編號為10001的任務,我沒有時間去看具體的事項,那么。我需要委托一個人來幫我辦理這個任務。這個方案目前只能很不靈活。也就是說很不適用。因為假如有10000個這類任務,那么我需要委托10000次。顯然這個方案是笨拙的。然后回到我們問題的本身。我們是要實現某一個時間段里面可以實行委托操作的。比如,上司出差。委托甲來全權負責公司的請假審批。請乙負責公司的人力資源審批。請丙丁兩個人共同來審核公司的財務管理。
舊方案實現
1 //直接委托任務 2 @RequestMapping("/taskEntrust") 3 @ResponseBody 4 public Json taskEntrust(@RequestParam String taskId,@RequestParam String taskAssignee){ 5 Json j=new Json(); 6 try{ 7 Task task=taskService.createTaskQuery().taskId(taskId).active().singleResult(); 8 taskService.delegateTask(task.getId(), taskAssignee); 9 j.setMsg("任務委托成功!"); 10 j.setSuccess(true); 11 }catch(Exception ex){ 12 ex.printStackTrace(); 13 j.setMsg("任務委托失敗"); 14 } 15 return j; 16 }
你可以看到,其實這個實現是很笨拙的,他就是流程轉發的一種,流程轉發是這么來實現的
1 //指派任務 2 @RequestMapping("/assignProcessInstance") 3 @ResponseBody 4 public Json assignProcessInstance(@RequestParam String taskId,@RequestParam String taskAssignee){ 5 Json j=new Json(); 6 try{ 7 taskService.createTaskQuery().taskId(taskId).singleResult().setAssignee(taskAssignee); 8 j.setMsg("任務指派成功!"); 9 j.setSuccess(true); 10 }catch(Exception ex){ 11 ex.printStackTrace(); 12 j.setMsg("任務指派失敗"); 13 } 14 return j; 15 }
那么我們要實現靈活控制工作流,在我們的OA系統就需要考慮那幾個方面的因素:
考慮因素:
(1)不同任務委托給不同人
(2)全盤委托
(3)委托給多個人共同決策(一票否決,一票通過)
(4)委托時間
案例:
原來正常的流程:
現在部門主管需要出差,那么我們原先的計划是,部門主管超時未處理的任務會直接提交給總經理。當然這個時候是部門主管不作為,就是部門主管不干事。那么部門主管需要委托人來辦理他的任務,那么他需要怎么做呢。這就是我們需要做的任務了。
(1)委托到某一個人
(2)委托到多個人共同決策
一般是第一種情況出現的最多,那么我們先考慮第一種情況,第二種情況后續再考慮。
設計表:
表act_ru_delegate
字段 |
類型 |
長度 |
是否必填 |
屬性 |
描述 |
ID |
BIGINT |
20 |
M |
Pk |
|
ASSIGNEE |
VARCHAR |
200 |
O |
|
辦理人 |
ATTORNEY |
VARCHAR |
200 |
O |
|
代理人 |
START_TIME |
TIMESTAMP |
|
O |
|
CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
END_TIME |
TIMESTAMP |
|
O |
|
'0000-00-00 00:00:00' |
PROCESS_DEFINITION_ID |
VARCHAR |
100 |
O |
Fk |
|
STATUS |
INT |
11 |
O |
|
表act_hi_delegate
字段 |
類型 |
長度 |
是否必填 |
屬性 |
描述 |
ID |
BIGINT |
20 |
M |
Pk |
|
ASSIGNEE |
VARCHAR |
200 |
O |
|
辦理人 |
ATTORNEY |
VARCHAR |
200 |
O |
|
代理人 |
DELEGATE_TIME |
TIMESTAMP |
|
O |
|
CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
TASK_ID |
VARCHAR |
100 |
O |
Fk |
任務ID |
STATUS |
INT |
11 |
O |
|
Model實現:
(1)DelegateHistory類
1 /** 2 * 3 */ 4 package light.mvc.workflow.model; 5 6 import java.sql.Date; 7 8 import javax.persistence.Column; 9 import javax.persistence.Entity; 10 import javax.persistence.Table; 11 import javax.persistence.Temporal; 12 import javax.persistence.TemporalType; 13 14 import org.hibernate.annotations.DynamicInsert; 15 import org.hibernate.annotations.DynamicUpdate; 16 import org.hibernate.validator.constraints.NotBlank; 17 18 /** 19 * 20 * 項目名稱:lightmvc 21 * 類名稱:DelegateHistory 22 * 類描述: 23 * 創建人:鄧家海 24 * 創建時間:2017年6月3日 下午2:37:13 25 * 修改人:deng 26 * 修改時間:2017年6月3日 下午2:37:13 27 * 修改備注: 28 * @version 29 * 30 */ 31 @Entity 32 @Table(name = "act_hi_delegate") 33 @DynamicInsert(true) 34 @DynamicUpdate(true) 35 public class DelegateHistory { 36 private String Assignee; 37 private String Attorney; 38 private Date Delegate_Time; 39 private String TaskId; 40 private Integer Status; 41 public DelegateHistory(){ 42 super(); 43 } 44 public DelegateHistory(String assignee,String attorney, Date delegate_time, String taskId,Integer status){ 45 super(); 46 this.Assignee=assignee; 47 this.Attorney=attorney; 48 this.Delegate_Time=delegate_time; 49 this.TaskId=taskId; 50 this.Status=status; 51 } 52 /** 53 * @return the assignee 54 */ 55 @NotBlank 56 public String getAssignee() { 57 return Assignee; 58 } 59 /** 60 * @param assignee the assignee to set 61 */ 62 public void setAssignee(String assignee) { 63 Assignee = assignee; 64 } 65 /** 66 * @return the attorney 67 */ 68 @NotBlank 69 public String getAttorney() { 70 return Attorney; 71 } 72 /** 73 * @param attorney the attorney to set 74 */ 75 public void setAttorney(String attorney) { 76 Attorney = attorney; 77 } 78 /** 79 * @return the delegate_Time 80 */ 81 public Date getDelegate_Time() { 82 return Delegate_Time; 83 } 84 /** 85 * @param delegate_Time the delegate_Time to set 86 */ 87 @Temporal(TemporalType.TIMESTAMP) 88 @Column(name = "delegate_Time", length = 19) 89 public void setDelegate_Time(Date delegate_Time) { 90 Delegate_Time = delegate_Time; 91 } 92 /** 93 * @return the taskId 94 */ 95 @NotBlank 96 public String getTaskId() { 97 return TaskId; 98 } 99 /** 100 * @param taskId the taskId to set 101 */ 102 public void setTaskId(String taskId) { 103 TaskId = taskId; 104 } 105 /** 106 * @return the status 107 */ 108 public Integer getStatus() { 109 return Status; 110 } 111 /** 112 * @param status the status to set 113 */ 114 public void setStatus(Integer status) { 115 Status = status; 116 } 117 }
(2)DelegateInfo類
1 /** 2 * 3 */ 4 package light.mvc.workflow.model; 5 6 import java.sql.Date; 7 8 import javax.persistence.Column; 9 import javax.persistence.Entity; 10 import javax.persistence.Table; 11 import javax.persistence.Temporal; 12 import javax.persistence.TemporalType; 13 14 import light.mvc.model.base.IdEntity; 15 16 import org.hibernate.annotations.DynamicInsert; 17 import org.hibernate.annotations.DynamicUpdate; 18 import org.hibernate.validator.constraints.NotBlank; 19 20 /** 21 * 22 * 項目名稱:lightmvc 23 * 類名稱:DelegateInfo 24 * 類描述: 25 * 創建人:鄧家海 26 * 創建時間:2017年6月3日 下午2:36:55 27 * 修改人:deng 28 * 修改時間:2017年6月3日 下午2:36:55 29 * 修改備注: 30 * @version 31 * 32 */ 33 34 @Entity 35 @Table(name = "act_ru_delegate") 36 @DynamicInsert(true) 37 @DynamicUpdate(true) 38 public class DelegateInfo extends IdEntity implements java.io.Serializable{ 39 private String Assignee; 40 private String Attorney; 41 private Date Start_Time; 42 private Date End_Time; 43 private String Process_Definition_Id; 44 private Integer Status; 45 public DelegateInfo(){ 46 super(); 47 } 48 public DelegateInfo(String assignee,String attorney, Date start_time, Date end_time,String process_definition_id,Integer status){ 49 super(); 50 this.Assignee=assignee; 51 this.Attorney=attorney; 52 this.Start_Time=start_time; 53 this.End_Time=end_time; 54 this.Process_Definition_Id=process_definition_id; 55 this.Status=status; 56 } 57 /** 58 * @return the assignee 59 */ 60 public String getAssignee() { 61 return Assignee; 62 } 63 /** 64 * @param assignee the assignee to set 65 */ 66 public void setAssignee(String assignee) { 67 Assignee = assignee; 68 } 69 /** 70 * @return the attorney 71 */ 72 public String getAttorney() { 73 return Attorney; 74 } 75 /** 76 * @param attorney the attorney to set 77 */ 78 public void setAttorney(String attorney) { 79 Attorney = attorney; 80 } 81 /** 82 * @return the start_Time 83 */ 84 public Date getStart_Time() { 85 return Start_Time; 86 } 87 /** 88 * @param start_Time the start_Time to set 89 */ 90 public void setStart_Time(Date start_Time) { 91 Start_Time = start_Time; 92 } 93 /** 94 * @return the end_Time 95 */ 96 public Date getEnd_Time() { 97 return End_Time; 98 } 99 /** 100 * @param end_Time the end_Time to set 101 */ 102 public void setEnd_Time(Date end_Time) { 103 End_Time = end_Time; 104 } 105 /** 106 * @return the process_Definition_Id 107 */ 108 public String getProcess_Definition_Id() { 109 return Process_Definition_Id; 110 } 111 /** 112 * @param process_Definition_Id the process_Definition_Id to set 113 */ 114 public void setProcess_Definition_Id(String process_Definition_Id) { 115 Process_Definition_Id = process_Definition_Id; 116 } 117 /** 118 * @return the status 119 */ 120 public Integer getStatus() { 121 return Status; 122 } 123 /** 124 * @param status the status to set 125 */ 126 public void setStatus(Integer status) { 127 Status = status; 128 } 129 }
(3)DelegateServiceI接口
1 /** 2 * 3 */ 4 package light.mvc.service.workflow; 5 6 import java.sql.Date; 7 8 import light.mvc.workflow.model.DelegateInfo; 9 10 /** 11 * 12 * 項目名稱:lightmvc 13 * 類名稱:DelegateServiceI 14 * 類描述: 15 * 創建人:鄧家海 16 * 創建時間:2017年6月3日 下午5:18:53 17 * 修改人:deng 18 * 修改時間:2017年6月3日 下午5:18:53 19 * 修改備注: 20 * @version 21 * 22 */ 23 //接口 24 public interface DelegateServiceI { 25 26 DelegateInfo getDelegateInfo(String targetAssignee,String targetProcessDefinitionId); 27 28 void saveRecord(String assignee, String attorney, String taskId); 29 30 void removeRecord(Long id); 31 32 void addDelegateInfo(String assignee, String attorney,Date startTime, Date endTime, String processDefinitionId); 33 34 }
(4)DelegateServiceImpl類
1 /** 2 * 3 */ 4 package light.mvc.service.workflow.impl; 5 6 import java.sql.Date; 7 import java.util.HashMap; 8 import java.util.List; 9 import java.util.Map; 10 11 import org.springframework.beans.factory.annotation.Autowired; 12 13 import light.mvc.dao.BaseDaoI; 14 import light.mvc.model.sys.Tuser; 15 import light.mvc.service.workflow.DelegateServiceI; 16 import light.mvc.workflow.model.DelegateHistory; 17 import light.mvc.workflow.model.DelegateInfo; 18 19 /** 20 * 21 * 項目名稱:lightmvc 22 * 類名稱:DelegateServiceImpl 23 * 類描述: 24 * 創建人:鄧家海 25 * 創建時間:2017年6月3日 下午5:20:30 26 * 修改人:deng 27 * 修改時間:2017年6月3日 下午5:20:30 28 * 修改備注: 29 * @version 30 * 31 */ 32 33 public class DelegateServiceImpl implements DelegateServiceI { 34 @Autowired 35 private BaseDaoI<DelegateInfo> delegateInfoDao; 36 @Autowired 37 private BaseDaoI<DelegateHistory> delegateHistoryDao; 38 39 40 public DelegateInfo getDelegateInfo(String targetAssignee,String targetProcessDefinitionId) { 41 42 Map<String,Object> map=new HashMap<String, Object>(); 43 map.put("assignee", targetAssignee); 44 List<DelegateInfo> list=delegateInfoDao.find("from DelegateInfo d where d.status=1 and d.assignee=:assignee order by id desc",map); 45 for (DelegateInfo delegateInfo : list) { 46 47 String processDefinitionId = (String) delegateInfo.getProcess_Definition_Id(); 48 Date startTime = (Date) delegateInfo.getStart_Time(); 49 Date endTime = (Date) delegateInfo.getEnd_Time(); 50 51 52 if (timeNotBetweenNow(startTime, endTime)) { 53 54 continue; 55 } 56 57 if ((processDefinitionId == null)|| processDefinitionId.equals(targetProcessDefinitionId)) { 58 return delegateInfo; 59 } 60 } 61 62 return null; 63 } 64 65 public void saveRecord(String assignee, String attorney, String taskId) { 66 DelegateHistory delegateHistory=new DelegateHistory(); 67 delegateHistory.setAttorney(assignee); 68 delegateHistory.setAttorney(attorney); 69 delegateHistory.setTaskId(taskId); 70 delegateHistory.setDelegate_Time(new java.sql.Date(System.currentTimeMillis())); 71 delegateHistory.setStatus(1); 72 delegateHistoryDao.update(delegateHistory); 73 } 74 75 public void removeRecord(Long id) { 76 77 DelegateInfo delegateInfo=delegateInfoDao.get(DelegateInfo.class, id); 78 delegateInfoDao.delete(delegateInfo); 79 } 80 81 public void addDelegateInfo(String assignee, String attorney,Date startTime, Date endTime, String processDefinitionId) { 82 DelegateInfo delegateInfo=new DelegateInfo(); 83 delegateInfo.setAssignee(assignee); 84 delegateInfo.setAttorney(attorney); 85 delegateInfo.setEnd_Time(endTime); 86 delegateInfo.setStart_Time(startTime); 87 delegateInfo.setProcess_Definition_Id(processDefinitionId); 88 delegateInfo.setStatus(1); 89 delegateInfoDao.save(delegateInfo); 90 } 91 92 private boolean timeNotBetweenNow(Date startTime, Date endTime) { 93 Date now = new Date(System.currentTimeMillis()); 94 95 if (startTime != null) { 96 return now.before(startTime); 97 } 98 99 if (endTime != null) { 100 return now.after(endTime); 101 } 102 103 return false; 104 } 105 }
(5)TaskAsigneeListenerImpl監聽類
1 /** 2 * 3 */ 4 package light.mvc.workflow.taskListener; 5 6 import java.util.Map; 7 8 import light.mvc.service.workflow.impl.DelegateServiceImpl; 9 import light.mvc.workflow.model.DelegateInfo; 10 11 import org.activiti.engine.delegate.DelegateTask; 12 import org.activiti.engine.delegate.TaskListener; 13 import org.springframework.beans.factory.annotation.Autowired; 14 15 /** 16 * 17 * 項目名稱:lightmvc 18 * 類名稱:TaskAsigneeListenerImpl 19 * 類描述: 20 * 創建人:鄧家海 21 * 創建時間:2017年6月1日 下午11:48:55 22 * 修改人:deng 23 * 修改時間:2017年6月1日 下午11:48:55 24 * 修改備注: 25 * @version 26 * 27 */ 28 29 public class TaskAsigneeListenerImpl implements TaskListener { 30 @Autowired 31 private DelegateServiceImpl delegateServiceImpl; 32 @Override 33 public void notify(DelegateTask delegateTask) { 34 Map<String,Object> map = delegateTask.getVariables(); 35 delegateTask.setAssignee(map.get("userId").toString()); 36 37 //如果有委托,設置委托人 38 String assignee = delegateTask.getAssignee(); 39 String processDefinitionId = delegateTask.getProcessDefinitionId(); 40 DelegateInfo delegateInfo = delegateServiceImpl.getDelegateInfo(assignee, 41 processDefinitionId); 42 43 if (delegateInfo == null) { 44 return; 45 } 46 47 String attorney = delegateInfo.getAttorney(); 48 delegateTask.setAssignee(attorney); 49 delegateServiceImpl.saveRecord(assignee, attorney, delegateTask.getId()); 50 51 } 52 }
(6)act_hi_delegate表
1 SELECT * FROM lightmvc.act_hi_delegate;CREATE TABLE `act_hi_delegate` ( 2 `ID` bigint(20) NOT NULL AUTO_INCREMENT, 3 `ASSIGNEE` varchar(200) DEFAULT NULL, 4 `ATTORNEY` varchar(200) DEFAULT NULL, 5 `DELEGATE_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 `TASK_ID` varchar(100) DEFAULT NULL, 7 `STATUS` int(11) DEFAULT NULL, 8 PRIMARY KEY (`ID`) 9 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
(7)act_ru_delegate表
1 CREATE TABLE `act_ru_delegate` ( 2 `ID` bigint(20) NOT NULL AUTO_INCREMENT, 3 `ASSIGNEE` varchar(200) DEFAULT NULL, 4 `ATTORNEY` varchar(200) DEFAULT NULL, 5 `START_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 `END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 7 `PROCESS_DEFINITION_ID` varchar(100) DEFAULT NULL, 8 `STATUS` int(11) DEFAULT NULL, 9 PRIMARY KEY (`ID`) 10 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;