- 今天內容安排:
- 1、流程實例管理(查詢流程實例、查看流程實例運行狀態)
- 2、將bos系統中的用戶和角色同步到activiti框架的用戶表和組表中去
- 3、設計物流配送流程
- 4、啓動物流配送流程
- 5、組任務操作(查詢、拾取)
- 6、個人任務操作(查詢、辦理)
1、流程實例管理
1.1、查詢流程實例
第一步:創建一個流程實例管理ProcessInstanceAction,提供list()方法,查詢流程實例列表數據
// 注入Service @Autowired private RuntimeService runtimeService; /** * 查詢流程實例(同步) * @return */ public String list() { // 創建流程實例查詢對象 ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery(); query.orderByProcessInstanceId().desc(); // 根據流程實例id排序,降序 List<ProcessInstance> list = query.list(); // 執行查詢,獲得流程實例列表數據 // 將流程實例列表數據壓入值棧中 ActionContext.getContext().getValueStack().set("list", list); return "list"; }
第二步:配置struts.xml
<!-- 流程實例管理:配置processInstanceAction--> <action name="processInstanceAction_*" class="processInstanceAction" method="{1}"> <result name="list">/WEB-INF/pages/workflow/processinstance.jsp</result> </action>
第三步:提供processinstance.jsp頁面,展示列表數據
<tbody> <s:iterator value="list"> <tr> <td>${id}</td><!-- 流程實例id --> <td>${processDefinitionId}</td> <td>${activityId}</td><!-- 流程id(任務默認id) --> <td> <div id="div${id}"></div> <script type="text/javascript"> // 根據流程實例id查詢流程變量 $.post("${pageContext.request.contextPath}/processInstanceAction_findData.action",{"id":'${id}'},function(data) { $("#div${id}").html(data); }); </script> </td> <td> <a class="easyui-linkbutton" data-options="iconCls:'icon-search'" onclick="showPng('${id}');" href="#">查看流程圖</a> </td> </tr> </s:iterator> </tbody>
第四步:在ProcessInstanceAction中提供findData()的方法,根據流程實例id查詢對應的流程變量數據(ajax)
// 接收頁面提交過來的參數:流程實例id private String id; public void setId(String id) { this.id = id; } /** * 根據流程實例id查詢對應的流程變量數據(ajax) * @return * @throws IOException */ public String findData() throws IOException { Map<String, Object> variables = runtimeService.getVariables(id); ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8"); ServletActionContext.getResponse().getWriter().print(variables.toString()); return "none"; }
瀏覽器效果如下圖所示:
1.2、查詢流程實例運行狀態
第一步:爲“查看流程圖”按鈕綁定事件,注意:點擊查看流程圖按鈕,實際上發起了2次請求
<script type="text/javascript"> function showPng(id) { // 彈出新窗口 window.open("${pageContext.request.contextPath}/processInstanceAction_showPng.action?id=" + id); } </script>
第二步:在ProcessInstanceAction中提供showPng()方法,根據流程實例id獲取部署id、圖片名稱、圖片座標
/** * 根據流程實例id獲取部署id、圖片名稱、圖片座標 * * @return */ public String showPng() { // 1、根據流程實例id創建流程實例查詢對象 ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery(); query.processInstanceId(id); // 根據流程實例id過濾 ProcessInstance processInstance = query.singleResult(); // 得到流程實例對象 // 2、根據流程實例對象獲取流程定義id String processDefinitionId = processInstance.getProcessDefinitionId(); // 3、根據流程定義id創建流程定義查詢對象 --> 根據流程定義id過濾 --> 得到流程定義對象(鏈式編程),注意:此方式只查詢了流程定義表act_re_procdef ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult(); // 4、根據流程定義對象獲取部署id deploymentId = processDefinition.getDeploymentId(); // 5、根據流程定義對象獲取圖片名稱 imageName = processDefinition.getDiagramResourceName(); // 把部署id和圖片名稱壓入值棧後,在image.jsp頁面就可以正常獲取出來這兩個參數,取出來之後該頁面又請求新的地址,新的地址又把這兩個參數提交過來,我們在Action中又需要接收(設置)這兩個參數,是不是有些麻煩呢? // 所以針對這種情況,我們需要把部署id和圖片名稱由局部變量提升爲成員變量,並且提供getter和setter方法,終於這兩個方法都能用着啦!getter方法用於頁面EL表達式獲取參數,setter方法用於接收頁面提交回來的參數。 // 因爲成員變量本身就在struts的值棧裏面,因爲struts框架本身將Action對象壓入值棧中。 // 獲取座標 // 1、獲取當前流程實例執行到哪個節點了 String activityId = processInstance.getActivityId(); // usertask1 // 2、加載bpmn(xml)文件,獲得一個流程定義對象,注意:此方式不僅查詢了流程定義表act_re_procdef,還查詢了act_ge_bytearray ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId); // 使用流程定義對象的實現類,該實現類裏面擴展了很多方法,其中就包括關於座標的 // 3、根據activityId獲取含有座標信息的對象 ActivityImpl findActivity = processDefinitionEntity.findActivity(activityId); int x = findActivity.getX(); int y = findActivity.getY(); int width = findActivity.getWidth(); int height = findActivity.getHeight(); // 將座標信息壓入值棧中 ActionContext.getContext().getValueStack().set("x", x); ActionContext.getContext().getValueStack().set("y", y); ActionContext.getContext().getValueStack().set("width", width); ActionContext.getContext().getValueStack().set("height", height); return "showPng"; }
第三步:配置struts.xml,跳轉到image.jsp頁面
<result name="showPng">/WEB-INF/pages/workflow/image.jsp</result>
第四步:提供image.jsp頁面
<body> <!-- 1.獲取到規則流程圖 --> <img style="position: absolute;top: 0px;left: 0px;" src="processInstanceAction_viewImage?deploymentId=${deploymentId}&imageName=${imageName}"> <!-- 2.根據當前活動的座標,動態繪製DIV --> <div style="position:absolute;border:1px solid red;top:${y-1}px;left:${x-1}px;width:${width}px;height:${height}px;"> </div> </body>
第五步:在ProcessInstanceAction中提供viewImage()方法,根據部署id和圖片名稱獲得對應的輸入流
/** * 根據部署id和圖片名稱獲取對應的png輸入流 * @return */ public String viewImage() { // 獲取png圖片對應的輸入流 InputStream pngStream = repositoryService.getResourceAsStream(deploymentId, imageName); // 使用Struts框架提供的文件下載功能(文件下載結果集):通過輸出流把服務端的資源寫到客戶端 // 先把輸入流壓入值棧,再到頁面把流讀出來即可 ActionContext.getContext().getValueStack().set("pngStream", pngStream); return "viewImage"; }
第六步:配置struts.xml
<result name="viewImage" type="stream"> <param name="contentType">image/png</param> <param name="inputName">pngStream</param><!-- 輸入流壓棧時起的名字 --> <!-- <param name="contentDisposition">attachment;filename="abc.png"</param> <param name="bufferSize">1024</param> --> </result>
瀏覽器效果如下圖所示:
2、將bos系統中的用戶和角色同步到activiti框架的用戶表和組表中去
2.1、將角色同步到 act_id_group 表中去
修改RoleServiceImpl中的save()方法,如下圖所示:
2.2、將用戶同步到 act_id_user 表中去
注意:我們在添加用戶的時候會選擇角色,在activiti框架中對應的是用戶表關聯組表。 修改UserServiceImpl的save()方法,如下圖所示:
3、設計物流配送流程
- 流程定義的id:
- 使用排他網關:
- 使用組任務:
- 任務的id(對應Action中的方法名):
4、啓動物流配送流程
準備工作:
- 修改工作單類
- 修改工作單對應的hbm映射文件
- 爲了和我們之前的命名規則一致,修改
數據庫表auth_function
中的“啓動配送流程”中的page屬性的值爲workordermanageAction_list.action
4.1、查詢工作單列表數據
第一步:在工作單管理Action中提供list()方法,查詢流程狀態start爲0的工作單(即未啓動的工作單)
/** * 查詢流程狀態start爲0的工作單(即未啓動的工作單) * @return */ public String list() { List<Workordermanage> list = workordermanageService.findListNotStart(); ActionContext.getContext().getValueStack().set("list", list); // 將查詢到的結果壓棧 return "list"; }
Service層代碼:
/** * 查詢流程狀態start爲0的工作單(即未啓動的工作單) */ public List<Workordermanage> findListNotStart() { // 創建離線條件查詢對象 DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Workordermanage.class); // 向離線條件查詢對象中封裝查詢條件 detachedCriteria.add(Restrictions.eq("start", "0")); return workordermanageDao.findByCriteria(detachedCriteria ); }
第二步:配置struts.xml
<!-- 工作單管理:配置workordermanageAction--> <action name="workordermanageAction_*" class="workordermanageAction" method="{1}"> <result name="list">/WEB-INF/pages/workflow/startransfer.jsp</result> </action>
第三步:提供startransfer.jsp頁面,展示工作單列表數據
瀏覽器顯示效果:
4.2、根據key啓動流程實例
第一步:修改列表頁面中“啓動按鈕”綁定事件
<td> <s:a action="workordermanageAction_start" cssClass="easyui-linkbutton" iconCls="icon-edit">啓動 <s:param name="id" value="#workOrderManage.id"></s:param> </s:a> </td>
第二步:在工作單Action中提供start()方法,啓動物流配送流程對應的流程實例 注意:“修改工作單中的start值爲1,啓動流程實例,設置流程變量”爲多個數據庫表的操作,所以儘量不要在Action中寫這麼複雜的邏輯,爲什麼呢? 答:建議寫在service裏面去,因爲涉及到多個數據庫表的操作,這樣可以保證事務控制。
/** * 啓動物流配送流程 * @return */ public String start() { // 獲取工作單id String id = model.getId(); // 注意:“修改工作單中的start值爲1,啓動流程實例,設置流程變量”爲多個數據庫表的操作,所以儘量不要在Action中寫這麼複雜的邏輯,爲什麼呢? // 答:建議寫在service裏面去,因爲涉及到多個數據庫表的操作,這樣可以保證事務控制。 // 啓動流程實例 workordermanageService.start(id); return "toList"; }
第三步:在工作單Service中提供start()方法,實現數據庫操作
/** * 修改工作單中的start值爲1,設置流程變量,啓動流程實例 */ public void start(String workorderId) { // 修改工作單中的start值爲1 Workordermanage workordermanage = workordermanageDao.findById(workorderId); workordermanage.setStart("1"); // 設置流程變量 String processDefinitionKey = "transfer"; // 流程定義key == 畫圖時給圖起的id,這個不需要查詢數據庫,爲已知數據 String businessKey = workorderId; // 業務主鍵 == 業務表(工作單)的主鍵值,因爲實際當中攜帶的數據往往是業務表中數據 ==> 業務主鍵存在實例流程表中,工作流框架通過業務主鍵可以找到業務數據 Map<String, Object> variables = new HashMap<String, Object>(); // 流程變量 variables.put("業務數據", workordermanage); // 我們在流程變量裏面也存一份業務數據,就是說,“業務數據”我可以存在業務主鍵中,也可以存在流程變量中 // 啓動流程實例 runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables); }
如下圖所示:
5、組任務操作
5.1、查詢組任務
第一步:創建一個TaskAction類,提供查詢組任務的方法findGroupTask()
@Autowired private TaskService taskService; /** * 查詢組任務 * @return */ public String findGroupTask() { // 創建任務查詢對象 TaskQuery query = taskService.createTaskQuery(); String candidateUser = BOSContext.getLoginUser().getId(); // 獲取當前登錄用戶的id query.taskCandidateUser(candidateUser); // 根據組任務過濾,即根據候選人過濾 List<Task> list = query.list(); // 執行查詢,獲得任務列表數據 // 將任務列表數據壓入值棧中 ActionContext.getContext().getValueStack().set("list", list); return "grouptasklist"; }
第二步:配置struts.xml
<!-- 任務管理:配置taskAction--> <action name="taskAction_*" class="taskAction" method="{1}"> <result name="grouptasklist">/WEB-INF/pages/workflow/grouptask.jsp</result> </action>
第三步:提供grouptask.jsp頁面,展示任務列表數據
第三步:在任務Action中,提供顯示業務數據的方法showData()
// 接收頁面提交過來的參數:任務id private String taskId; public void setTaskId(String taskId) { this.taskId = taskId; } /** * 根據任務id查詢對應的流程變量數據(ajax) * @return * @throws IOException */ public String showData() throws IOException { Map<String, Object> variables = taskService.getVariables(taskId); ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8"); ServletActionContext.getResponse().getWriter().print(variables.toString()); return "none"; }
瀏覽器顯示效果:
5.2、拾取組任務
第一步:修改jsp頁面中“拾取按鈕”事件
<td> <s:a action="taskAction_takeTask" namespace="/" cssClass="easyui-linkbutton">拾取 <s:param name="taskId" value="id"></s:param> </s:a> </td>
第二步:在TaskAction中提供拾取任務的方法takeTask()
/** * 拾取組任務 * @return */ public String takeTask() { String userId = BOSContext.getLoginUser().getId(); // 獲取當前登錄用戶的id taskService.claim(taskId, userId); return "togrouptasklist"; }
第三步:配置struts.xml
<!-- 任務管理:配置taskAction--> <action name="taskAction_*" class="taskAction" method="{1}"> <result name="grouptasklist">/WEB-INF/pages/workflow/grouptask.jsp</result> <result name="togrouptasklist" type="redirectAction"> taskAction_findGroupTask </result> </action>
6、個人任務操作
6.1、查詢個人任務
第一步:在TaskAction中提供findPersonalTask()方法,查詢當前登錄用戶的個人任務
/** * 查詢個人任務 */ public String findPersonalTask() { // 創建任務查詢對象 TaskQuery query = taskService.createTaskQuery(); String assignee = BOSContext.getLoginUser().getId(); // 獲取當前登錄用戶的id query.taskAssignee(assignee); // 根據個人任務過濾 List<Task> list = query.list(); // 執行查詢,獲得任務列表數據 // 將任務列表數據壓入值棧中 ActionContext.getContext().getValueStack().set("list", list); return "personaltasklist"; }
第二步:配置struts.xml
<result name="personaltasklist">/WEB-INF/pages/workflow/personaltask.jsp</result>
第三步:提供personaltask.jsp頁面,展示個人任務列表數據 圖片同查詢組任務中的第三步。 瀏覽器顯示效果:
6.2、辦理個人任務
6.2.1、辦理審覈工作單任務
第一步:修改personaltask.jsp頁面中“辦理任務”按鈕的事件
第二步:在TaskAction中提供checkWorkOrderManage()方法,辦理審覈工作單任務
// 接收頁面提交過來的參數:check // 審覈結果:1 表示審覈通過,0表示審覈不通過 private Integer check; public Integer getCheck() { return check; } public void setCheck(Integer check) { this.check = check; } /** * 辦理審覈工作單任務(實際上是跳轉到審覈工作單頁面 + 真正辦理審覈工作單任務) */ public String checkWorkOrderManage() { // 根據傳遞過來的任務id獲取任務對象(任務查詢對象 --> 根據任務id過濾 --> 任務對象) Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); // 根據任務對象獲取流程實例id String processInstanceId = task.getProcessInstanceId(); // 根據流程實例id獲取流程實例對象 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); String workorderId = processInstance.getBusinessKey(); // 業務主鍵 == 工作單id // 根據工作單id獲取工作單對象 Workordermanage workordermanage = workordermanageService.findId(workorderId); if (check == null) { // 說明需要跳轉到審覈頁面 // 將工作單數據壓入值棧中 ActionContext.getContext().getValueStack().set("map", workordermanage); // 跳轉到一個審覈工作單頁面,展示當前對應的工作單信息 return "check"; } else { // 真正辦理審覈工作單任務 workordermanageService.checkWorkOrderManage(taskId, check, workorderId); return "topersonaltasklist"; // 重新回顯查詢個人任務 } }
第三步:如果是跳轉到審覈頁面,配置struts.xml
<result name="check">/WEB-INF/pages/workflow/check.jsp</result>
第四步:提供check.jsp頁面,展示審覈工作單表單頁面
第五步:在工作單Service中提供checkWorkOrderManage()方法,處理審覈工作單任務 注意:spring配置文件中需要注入historyService。
// 注入service @Autowired private TaskService taskService; // 注入service @Autowired private HistoryService historyService; /** * 辦理審覈工作單任務 */ public void checkWorkOrderManage(String taskId, Integer check, String workorderId) { // 如果審覈不通過,修改工作單中的start值爲0 Workordermanage workordermanage = workordermanageDao.findById(workorderId); // 根據傳遞過來的任務id獲取任務對象(任務查詢對象 --> 根據任務id過濾 --> 任務對象) Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); // 根據任務對象獲取流程實例id String processInstanceId = task.getProcessInstanceId(); Map<String, Object> variables = new HashMap<String, Object>(); // 設置流程變量:check,check會在排他網關中使用 variables.put("check", check); // 辦理審覈工作單任務 taskService.complete(taskId, variables); if (check == 0) { // 說明審覈不通過 workordermanage.setStart("0"); // 刪除歷史流程實例數據 historyService.deleteHistoricProcessInstance(processInstanceId); } }
6.2.2、辦理其他任務
我們簡寫了。
/** * 辦理出庫任務 */ public String outStore() { taskService.complete(taskId); return "topersonaltasklist"; } /** * 辦理配送任務 */ public String transferGoods() { taskService.complete(taskId); return "topersonaltasklist"; } /** * 辦理簽收任務 */ public String receive() { taskService.complete(taskId); return "topersonaltasklist"; }