上篇文章我們一起學習了 Flowable 中的動態表單,動態表單説白了就是把變量打包定義,零存整取。但是小夥伴們可能很難實實在在 GET 到動態表單一些有創造性的功能,所以今天我們就來繼續看看 Flowable 中的外置表單怎麼玩,這個跟動態表單有一些本質上的差別。
1. 外置表單
首先,所謂的外置表單,其實説白了,類似我們平時在 HTML 中寫的 form 表單。
現在的 flowable 中,我們既可以利用 JSON 的形式來定義 form 表單,也可以直接就使用 HTML 來定義,都是 OK 的。本文為了直觀,鬆哥這裏採用 HTML 來定義表單。
現在假設我有如下一個請假流程:
在開始節點中,我們需要一個表單來輸入用户提交的請假信息,在組長審批和經理審批這兩個節點中我們希望能夠看到用户提交的請假信息,那麼我們準備兩個表單文件,第一個是提交請假信息的表單文件 askleave.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="">
<table>
<tr>
<td>請假天數:</td>
<td><input type="text" name="days"></td>
</tr>
<tr>
<td>請假理由:</td>
<td><input type="text" name="reason"></td>
</tr>
<tr>
<td>起始時間:</td>
<td><input type="date" name="startTime"></td>
</tr>
<tr>
<td>結束時間:</td>
<td><input type="date" name="endTime"></td>
</tr>
<tr>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
小夥伴們看到,這其實就是一個普通的 HTML 頁面,這裏為了省事,我就沒寫 form 的 action 了。
還有一個是查看用户提交的請假信息的表單 leader_approval.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="">
<table>
<tr>
<td>請假天數:</td>
<td><input type="text" name="days" value="${days}"></td>
</tr>
<tr>
<td>請假理由:</td>
<td><input type="text" name="reason" value="${reason}"></td>
</tr>
<tr>
<td>起始時間:</td>
<td><input type="date" name="startTime" value="${startTime}"></td>
</tr>
<tr>
<td>結束時間:</td>
<td><input type="date" name="endTime" value="${endTime}"></td>
</tr>
<tr>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
和前面的 askleave.html 文件相比,leader_approval.html 文件中,各個表單屬性只是多了 value 屬性而已,value 給了一個預填的變量,其他都是一樣的。
兩個表單文件定義完成之後,接下來我們為我們的流程來配置這兩個表單文件,如下圖,為開始節點設置表單 key 為 askforleave.html,為組長審批和經理審批節點設置表單 key 為 leader_approval.html:
另:在 Spring Boot 項目中,外置表單默認放在resources/forms目錄下,也就是説,凡是放在這個目錄下的表單文件,會被自動部署(要求文件後綴為.form)。
好啦,這樣我們的流程圖就準備完成了。
2. 流程部署
小夥伴們需要注意,外置表單的部署需要和流程圖一起部署,只有一起部署,他們才會有相同的 DEPLOYMENT_ID,否則兩者的 DEPLOYMENT_ID 不同,在後續的查找中就找不到對應的表單。
因此,我們來修改一下流程部署的接口:
@RestController
public class ProcessDeployController {
@Autowired
RepositoryService repositoryService;
@PostMapping("/deploy")
public RespBean deploy(MultipartFile[] files) throws IOException {
System.out.println(new Date());
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
.category("javaboy的工作流分類")
.name("javaboy的工作流名稱")
.key("javaboy的工作流key666");
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
deploymentBuilder.addInputStream(file.getOriginalFilename(), file.getInputStream());
}
Deployment deployment = deploymentBuilder
.deploy();
return RespBean.ok("部署成功", deployment.getId());
}
}
小夥伴們看到,這裏我將上傳文件改為了數組,也就是流程圖、form 表單等統統都以文件的形式上傳,然後在部署的時候,統一都調用 addInputStream 方法進行添加。
我們來看下使用 POSTMAN 部署的方式:
部署成功之後,我們來看下 ACT_GE_BYTEARRAY 表中的記錄,如下:
小夥伴們看到,四條記錄具有相同的 DEPLOYMENT_ID,這一點尤為重要。
3. 流程開啓與執行
在流程開啓之前,我們首先可以通過如下方式查詢啓動節點上的表單內容:
@Test
void test05() {
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().latestVersion().processDefinitionKey("FormDemo02").singleResult();
String startFormKey = formService.getStartFormKey(pd.getId());
String renderedStartForm = (String) formService.getRenderedStartForm(pd.getId());
System.out.println("startFormKey = " + startFormKey);
System.out.println("renderedStartForm = " + renderedStartForm);
}
控制枱輸出的內容如下:
可以看到,表單的內容就被輸出來了。
如果我們這裏是一個 Web 工程,那麼可以通過 Ajax 來請求到這個表單數據,並動態渲染到前端,然後在前端輸入對應的值,點擊提交按鈕,就可以在服務端開啓一個流程了。
服務端開啓流程方式如下:
@Test
void test02() {
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey("FormDemo02").latestVersion().singleResult();
Map<String, String> vars = new HashMap<>();
vars.put("startTime", "2022-10-10 10:10");
vars.put("endTime", "2022-10-12 10:10");
vars.put("reason", "玩兩天");
vars.put("days", "3");
ProcessInstance pi = formService.submitStartFormData(pd.getId(), vars);
}
調用 submitStartFormData 方法來開啓一個流程,我這裏參數直接硬編碼了。
流程開啓之後,接下來組長 zhangsan 要來審批這個流程,審批之前他需要先查看一下用户提交的表單信息,查看方式如下:
@Test
void test06() {
Task task = taskService.createTaskQuery().taskAssignee("zhangsan").singleResult();
String renderedTaskForm = (String) formService.getRenderedTaskForm(task.getId());
System.out.println("renderedTaskForm = " + renderedTaskForm);
}
小夥伴們注意,這個 getRenderedTaskForm 方法只有外置表單才有,動態表單調用這個方法是沒有東西的,因為動態表單單純的就只是變量的傳遞,不涉及到渲染問題,我們來看下這裏打印出來的結果:
小夥伴們看到,和前面的表單相比,這裏的表單都渲染出來了對應的值。如果這是一個 Web 項目,那麼我們就可以使用 Ajax 請求這個渲染後的表單,並展示在前端頁面。當然實際審批中,這裏可以有更多的字段,組長填完之後,進入到下一個環節。
zhangsan 進行流程審批的代碼如下:
@Test
void test08() {
Task task = taskService.createTaskQuery().taskAssignee("zhangsan").singleResult();
Map<String, String> vars = new HashMap<>();
vars.put("startTime", "2022-10-30 10:10");
vars.put("endTime", "2022-12-30 10:10");
vars.put("reason", "玩十天");
vars.put("days", "10");
formService.submitTaskFormData(task.getId(),vars);
}
可以使用 formService#submitTaskFormData 方法進行審批,也可以使用 taskService.complete 方法進行審批。
剩下的玩法就和普通流程一樣了。
好啦,這就是和大家介紹的外置表單。