day59_BOS專案_11
- 今天內容安排:
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"; }