前言
當(dāng)工作流流程圖發(fā)布完成之后京闰,下一步就是啟動(dòng)工作流帆喇,也是工作流引擎的核心功能乳附,本篇重點(diǎn)將要對(duì)工作流的啟動(dòng)和流程進(jìn)行詳細(xì)說(shuō)明辛蚊。
說(shuō)明:所有項(xiàng)目配置均在系列第一篇文章中進(jìn)行介紹,配置系列通用窟扑。
系列三內(nèi)容
初始化參數(shù)棒口、啟動(dòng)工作流、執(zhí)行工作流
頁(yè)面總覽
功能詳細(xì)說(shuō)明
初始化參數(shù)
說(shuō)明
在實(shí)際項(xiàng)目中辜膝,有可能會(huì)有如下需求:需要在啟動(dòng)工作流的時(shí)候无牵,提前向工作流中傳參(全局變量),這些參數(shù)往往在業(yè)務(wù)上具有重要意義厂抖,例如:xxId茎毁,status等。因此此工作流管理系統(tǒng)特加入此功能忱辅,在啟動(dòng)流程前對(duì)參數(shù)進(jìn)行定義七蜘,并在啟動(dòng)時(shí)將這些參數(shù)賦值到流程實(shí)例中。
此處包含兩個(gè)子功能:
打開(kāi)參數(shù)列表功能墙懂、保存參數(shù)列表功能
頁(yè)面
前端關(guān)鍵代碼
TableData.vue
打開(kāi)參數(shù)列表
<Button type="info" :disabled="row.defId === null" size="small" @click="initParams(row)">初始化參數(shù)</Button>
<Modal v-model="initParamsModal" width="540" :mask-closable="false" :closable="false" :footer-hide="true">
<input type="hidden" v-model="flowId"/>
請(qǐng)輸入初始化參數(shù):<Input v-model="paramsName" placeholder="各參數(shù)間以','隔開(kāi)" style="width: 260px" />
<Button type="primary" size="small" @click="confirmParams">確認(rèn)</Button>
<Button type="primary" size="small" @click="clearParams">清空</Button>
<Button type="primary" size="small" @click="cancelParams">取消</Button>
</Modal>
return {
initParamsModal:false,
paramsName:''橡卤,
flowId:''
}
methods: {
//初始化方法
initParams(row){
this.initParamsModal = true
const flowId = row.id ;
this.flowId = flowId ;
openParams({
flowId:flowId
}).then(res => {
if(res.data.responseCode === 1){
let paramsName = res.data.responseData.paramsName ;
this.paramsName = paramsName === null ? '':paramsName ;
}else{}
})
}
}
保存參數(shù)列表
//保存參數(shù)列表
confirmParams(){
const flowId = this.flowId
const paramsName = this.paramsName
this.initParamsModal = false
saveParams({
flowId:flowId,
paramsName:paramsName
}).then(res => {
if(res.data.responseCode === 1){
this.$Message.success('保存參數(shù)成功!');
}else{
this.$Message.error('保存參數(shù)失斔鸢帷碧库!');
}
//清空數(shù)據(jù)
this.paramsName = ''
})
}
后端關(guān)鍵代碼
ParamsRecordController
打開(kāi)原參數(shù)列表
@Autowired
private ParamsRecordService paramsRecordService ;
/**
* 打開(kāi)參數(shù)列表
* @return
*/
@RequestMapping("/open")
@Transactional
public JsonResult open(Long flowId){
JsonResult jr = new JsonResult() ;
resultMap = new HashMap<String,Object>() ;
try {
String paramsName = paramsRecordService.getParamsNameByFlowId(flowId);
//獲取到原參數(shù)列表(以“坝冕,”隔開(kāi)的字符串)
resultMap.put("paramsName",paramsName) ;
jr.setResponseData(resultMap);
}catch (Exception e){
e.printStackTrace();
}
return jr;
}
保存參數(shù)列表
/**
* 保存實(shí)體
* @return
*/
@RequestMapping("/save")
@Transactional
public JsonResult save(Long flowId,String paramsName){
JsonResult jr = new JsonResult() ;
try {
//先清空所有的參數(shù)
paramsRecordService.deleteBatchByFlowId(flowId);
if(StringUtils.isNotEmpty(paramsName)){
//進(jìn)行保存廷支,如果參數(shù)列表不為空,則執(zhí)行保存操作础浮,否則只清空颅悉,為了應(yīng)對(duì)前端清除按鈕的功能
List<ParamsRecord> list = strToObj(flowId,paramsName) ;
paramsRecordService.insertBatch(list) ;
}
jr.setResponseMessage(ResultEnum.SUCCESS);
jr.setResponseCode(1);
}catch (Exception e){
e.printStackTrace();
jr.setResponseMessage(ResultEnum.EXCEPTION);
jr.setResponseCode(0);
}
return jr;
}
/**
* 將前臺(tái)獲取到的str轉(zhuǎn)化為ParamsRecord實(shí)體類
* str -> paramsRecord obj
* @param str
*/
private static List<ParamsRecord> strToObj(Long flowId,String str){
String[] paramsName = str.trim().split(",");
List<ParamsRecord> list = new ArrayList<ParamsRecord>() ;
for(String pname:paramsName){
ParamsRecord pr = new ParamsRecord() ;
pr.setFlowId(flowId);
pr.setIsInit(true);
pr.setParamName(pname);
list.add(pr) ;
}
return list ;
}
ParamsRecordServiceImpl
@Override
public List<ParamsRecord> getParamsListByFlowId(Long flowId) {
//根據(jù)flowId獲取該流程定義下的原參數(shù)列表
List<ParamsRecord> list = mapper.getParamsListByFlowId(flowId) ;
if(list == null) return null ;
return list ;
}
啟動(dòng)工作流
說(shuō)明
在本系統(tǒng)中沽瞭,根據(jù)上文內(nèi)容,啟動(dòng)工作流分為含參啟動(dòng)和無(wú)參啟動(dòng)剩瓶,二者的區(qū)別即:含參啟動(dòng)是將上文中的參數(shù)列表進(jìn)行羅列驹溃,并分別傳初始參數(shù)值后啟動(dòng);無(wú)參啟動(dòng)則不包含此過(guò)程延曙。
頁(yè)面展示
前端代碼
TableData.vue
<Button type="success" :disabled="row.defId === null" size="small" @click="startProcess(row)">啟動(dòng)</Button>
<Modal :closable="false" :footer-hide="true" :mask-closable="false" v-model="startModal"
width="350">
<FormItemActiveAdd @change-modal="changeModal" ref="formItemActiveAdd"></FormItemActiveAdd>
</Modal>
components: {
FormItemActiveAdd
}
startProcess(row){
//流程定義ID
const processDefinitionId = row.defId ;
//流程定義業(yè)務(wù)主鍵ID
const flowId = row.id ;
let o = {}
getParamsListByFlowId({
flowId:flowId
}).then(res => {
this.startModal = true
let obj = {};
let arr = res.data.responseData.list ;
if(arr.length === 0){
//如果數(shù)組的長(zhǎng)度為0豌鹤,則不需要打開(kāi)
this.$Message.warning('未獲取到初始化參數(shù)列表,直接啟動(dòng)即可搂鲫!');
}else{
for(let key in arr){
obj[key] = arr[key]
}
this.$refs.formItemActiveAdd.paramsRecord = obj ;
}
this.$refs.formItemActiveAdd.processDefinitionId = processDefinitionId ;
})
},
FormItemActiveAdd.vue
<template>
<Form :model="paramsRecord" style="width: 100%;text-align: left">
<FormItem
v-for="(item, index) in paramsRecord"
v-if="item.id"
:key="index"
:label="'參數(shù)'+(++index)+':'+item.paramName"
:prop="item.paramName" class="FormItem-class">
<Row>
<Col span="28">
<br><Input type="text" v-model="item.paramVal" placeholder="Enter something..."></Input>
</Col>
</Row>
</FormItem>
<FormItem>
<Button type="success" @click="handleSubmit" style="margin-left: 95px">啟動(dòng)</Button>
<Button type="primary" @click="handleReturn" style="margin-left: 8px">返回</Button>
</FormItem>
</Form>
</template>
<script>
import {
start
} from '../api/activityManagement'
export default {
name: 'FormItemActiveAdd',
data () {
return {
index: 1,
processDefinitionId:'',
paramsRecord:{
},
}
},
methods: {
handleSubmit () {
const processDefinitionId = this.processDefinitionId ;
const obj = Object.values(this.paramsRecord) ;
let o = {} ;
for(let i = 0,len=obj.length; i < len; i++) {
o[obj[i].paramName] = obj[i].paramVal ;
}
start({
processDefinitionId:processDefinitionId,
paramMap:o
}).then(res =>{
if(res.data.responseCode === 1){
this.$Message.success('啟動(dòng)成功傍药!流程實(shí)例ID:'+res.data.responseData.pid);
}else {
this.$Message.error('啟動(dòng)失敾瞧健魂仍!');
}
this.$emit('change-modal',false)
})
},
handleReturn () {
this.paramsRecord = {}
this.$emit('change-modal',false)
}
},
mounted () {
}
}
</script>
<style scoped>
</style>
注:參數(shù)列表的展示使用的是FormItem組件拐辽,具體可查看iview官方文檔的4.x版本說(shuō)明
activityManagement.js
/**啟動(dòng)工作流**/
export const start = (processDefinitionId,paramMap) => {
return axios.request({
url: 'workflow/start',
params: processDefinitionId,
data:paramMap,
method: 'post'
})
}
后端代碼
WorkflowController
/**
* 啟動(dòng)工作流
* @param processDefinitionId,paramVal
* @return
*/
@RequestMapping("/start")
public JsonResult start(String processDefinitionId, String paramMap){
JsonResult jr = new JsonResult() ;
resultMap = new HashMap<String,Object>() ;
try {
Map<String, Object> map = JsonUtils.stringToMap(paramMap);
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId,map);
//啟動(dòng)成功后獲取流程實(shí)例ID
String processInstanceId = processInstance.getId() ;
jr.setResponseCode(1);
jr.setResponseMessage(ResultEnum.SUCCESS);
resultMap.put("pid",processInstanceId) ;
jr.setResponseData(resultMap) ;
}catch (Exception e){
e.printStackTrace();
jr.setResponseCode(0);
jr.setResponseMessage(ResultEnum.EXCEPTION);
}
return jr ;
}
JsonUtils
/**
* String -> Map
* @param str
* @return
*/
public static Map<String ,Object> stringToMap(String str) {
if (StringUtils.isEmpty(str)) {
return null;
}
Map<String, Object> map = new HashMap<String, Object>();
map = gson.fromJson(str, Map.class);
return map;
}
執(zhí)行工作流
說(shuō)明
執(zhí)行工作流即當(dāng)前實(shí)例繼續(xù)向下執(zhí)行,直至實(shí)例完成擦酌。
特殊說(shuō)明:該篇內(nèi)容中只介紹執(zhí)行下一步的相關(guān)功能實(shí)現(xiàn)俱诸,但實(shí)際在此功能模塊中涉及到其他內(nèi)容,此模塊莫心急赊舶,會(huì)在之后的系列篇中進(jìn)行介紹
頁(yè)面展示
亮點(diǎn)介紹
如上圖所示睁搭,此功能包含下一步路由提醒功能。目的在于由于下一步可能存在互斥網(wǎng)關(guān)等情況笼平,會(huì)在連接線上填寫條件园骆,但是在實(shí)際運(yùn)轉(zhuǎn)過(guò)程中往往會(huì)忘記這個(gè)條件,所以在此添加該功能寓调,使得系統(tǒng)更加人性化~
前端代碼
ActiveProcess.vue
<template>
<div class="div-class">
<Card style="height: 750px">
<p slot="title">
<Icon type="ios-film-outline"></Icon>
活動(dòng)實(shí)例列表({{this.$route.params.flowKey}})
</p>
<Button type="primary" class="btn-class" @click="returnPage">返回</Button>
<ul>
<Table height="550" border ref="selection" :columns="columns12" :data="data6" style="margin-top: 50px">
<template slot-scope="{ row, index }" slot="action">
<div class="slot_class">
<Button type="info" size="small" @click="execute(row)">執(zhí)行下一步</Button>
</div>
</template>
</Table>
</ul>
</Card>
</template>
<script>
import {
execute,
openExecute
} from '../../api/activityManagement'
import FormItemExecuteAdd from '../FormItemExecuteAdd'
export default {
name: 'ActiveProcess',
components: {
FormItemExecuteAdd
},
data () {
return {
flowId:'',
processInstanceId:'',
executeModal:false
}
},
methods: {
execute(row){
const processInstanceId = row.processInstanceId ;
const flowId = this.$route.params.flowId ;
if(processInstanceId === '' || flowId === ''){
this.$Message.warning('數(shù)據(jù)異常锌唾,無(wú)法加載!');
return ;
}
openExecute({
flowId:this.$route.params.flowId,
processInstanceId:processInstanceId
}).then(res => {
if(res.data.responseCode === 1){
const resArr = res.data.responseData.list ;
let message = ''
resArr.forEach(function(e){
message += '<p>' + e.toString() + '</p>'
});
console.log(message)
this.$Notice.open({
name:'executeNotice',
title: '下一步路由提醒',
duration:0,
desc: message
});
this.executeModal = true ;
this.$refs.paramList.processInstanceId = processInstanceId ;
}else{
this.$Message.warning('數(shù)據(jù)異常夺英,無(wú)法加載晌涕!');
}
})
}
}
}
</script>
activityManagement.js
/**打開(kāi)下一步Modal**/
export const openExecute = params => {
return axios.request({
url: 'workflow/openExecute',
params: params,
method: 'post'
})
}
FormItemExecuteAdd.vue
<template>
<Form ref="paramList" :model="paramList" :label-width="80" style="width: 300px">
<FormItem
v-for="(item, index) in paramList.items"
:key="index"
:label="'參數(shù):'"
:prop="'items.' + index + '.value'">
<Row>
<Col span="18">
<Input style="width: 70px" type="text" v-model="item.key" placeholder="key"></Input> =
<Input style="width: 80px" type="text" v-model="item.value" placeholder="value"></Input>
</Col>
<Col span="4" offset="1">
<Button @click="handleRemove(index)">刪除</Button>
</Col>
</Row>
</FormItem>
<FormItem>
<Row>
<Col span="12">
<Button type="dashed" long @click="handleAdd" icon="md-add">添加</Button>
</Col>
</Row>
</FormItem>
<FormItem>
<Button type="success" @click="handleSubmit('paramList')">執(zhí)行</Button>
<Button type="primary" @click="handleReturn('paramList')" style="margin-left: 8px">返回</Button>
</FormItem>
</Form>
</template>
<script>
import {execute} from '../api/activityManagement'
export default {
data () {
return {
index: 1,
processInstanceId:'',
paramList: {
items: [
]
}
}
},
methods: {
handleSubmit (name) {
const obj = Object.values(this.paramList.items) ;
let o = {} ;
for(let i = 0,len=obj.length; i < len; i++) {
if((obj[i].key !== '' && obj[i].key !== undefined && obj[i].key !== null) &&
(obj[i].value !== '' && obj[i].value !== undefined && obj[i].value !== null)){
o[obj[i].key] = obj[i].value ;
}
}
execute({
processInstanceId:this.processInstanceId,
paramMap:o
}).then(res =>{
if(res.data.responseCode === 1){
this.$Message.success('執(zhí)行成功!');
}else {
this.$Message.error('執(zhí)行失斖疵酢余黎!');
}
this.$emit('change-modal',false)
})
},
handleReturn (name) {
// this.paramList = {items: [{}]}
this.paramList = {items:[]}
this.$emit('change-modal',false)
},
handleReset (name) {
this.$refs[name].resetFields();
},
handleAdd () {
this.index++;
this.paramList.items.push({
key:'',
value: '',
index: this.index
});
},
handleRemove (index) {
this.index-- ;
this.paramList.items.splice(index,1)
}
}
}
</script>
后端代碼
WorkflowController
獲取下一步路由的方法,前提是已經(jīng)將任務(wù)環(huán)節(jié)信息以及各環(huán)節(jié)間邏輯關(guān)系保存到自己定義的業(yè)務(wù)表中载萌,此系列該表為task_def惧财,具體實(shí)現(xiàn)方案請(qǐng)參照系列第二篇~
/**
* 打開(kāi)執(zhí)行下一步操作時(shí),查看下一步路由
* @param flowId
* @param processInstanceId
* @return
*/
@RequestMapping("/openExecute")
public JsonResult openExecute(Long flowId,String processInstanceId){
JsonResult jr = new JsonResult() ;
resultMap = new HashMap<String,Object>() ;
try {
//這里需要可以獲取下一步路由
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
List<TaskDef> nextTasks = taskDefService.getTaskNexts(flowId, task.getTaskDefinitionKey());
List<String> list = new ArrayList<String>() ;
String str = "" ;
for(TaskDef td:nextTasks){
String type = td.getType() ;
//只要不是任務(wù)類或者事件扭仁,那么應(yīng)該繼續(xù)向下查詢
if(!type.contains("Task") && !type.contains("Event")){
getNextTasks(flowId,td.getTaskKey(),task.getName()+"->",list);
}else{
String expressionStr = td.getExpression() ;
str = task.getName() + "("+(StringUtils.isEmpty(expressionStr) ? "-" : expressionStr)+")"+ "->" + td.getName() ;
list.add(str) ;
}
}
resultMap.put("list",list) ;
jr.setResponseData(resultMap);
jr.setResponseCode(1);
jr.setResponseMessage(ResultEnum.SUCCESS);
}catch (Exception e){
e.printStackTrace();
jr.setResponseCode(0);
jr.setResponseMessage(ResultEnum.EXCEPTION);
}
return jr ;
}
/**
* private methods 01
* 獲取下一步路由
* @param flowId
* @param taskKey
* @return
*/
private List<String> getNextTasks(Long flowId,String taskKey,String str,List<String> l){
//根據(jù)流程定義業(yè)務(wù)主鍵ID和該環(huán)節(jié)的key查找其下一步路由
List<TaskDef> taskNexts = taskDefService.getTaskNexts(flowId, taskKey);
StringBuffer tStr = new StringBuffer(str) ;
for(TaskDef td : taskNexts){
String type = td.getType() ;
String taskName = td.getName() ;
//只要不是任務(wù)類或者事件可缚,那么應(yīng)該繼續(xù)向下查詢
if(!tStr.toString().contains(taskName) && (!type.contains("Task") && !type.contains("Event"))){
if(!StringUtils.isEmpty(td.getExpression())){
//如果涉及多個(gè)網(wǎng)關(guān)條件,則需要一一列出
tStr.append("("+td.getExpression()+")") ;
}
getNextTasks(flowId,td.getTaskKey(),tStr.toString(),l) ;
}else{
String expressionStr = td.getExpression() ;
tStr.append("("+(StringUtils.isEmpty(expressionStr) ? "-" : expressionStr)+")"+td.getName()) ;
l.add(tStr.toString()) ;
//初始化tStr斋枢,讓其=str
tStr = new StringBuffer(str) ;
}
}
return l ;
}
/**
* 執(zhí)行下一步(dubbo)
* @param processInstanceId
* @param paramMap
* @return
*/
@RequestMapping("/execute")
public JsonResult execute(String processInstanceId, String paramMap){
JsonResult jr = new JsonResult() ;
resultMap = new HashMap<String,Object>() ;
try {
Map<String, Object> map = JsonUtils.stringToMap(paramMap);
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
taskService.complete(task.getId(),map);
jr.setResponseCode(1);
jr.setResponseMessage(ResultEnum.SUCCESS);
}catch (Exception e){
e.printStackTrace();
jr.setResponseCode(0);
jr.setResponseMessage(ResultEnum.EXCEPTION);
}
return jr ;
}
總結(jié)
至此帘靡,有關(guān)于初始化參數(shù)、啟動(dòng)工作流瓤帚、執(zhí)行工作流的開(kāi)發(fā)全部完成描姚,此處涉及大量的業(yè)務(wù)端代碼,由于本人對(duì)于前端掌握的并不是很好戈次,所以可能途中會(huì)存在一些細(xì)節(jié)問(wèn)題待完善轩勘,因此各位若對(duì)代碼有任何不同的意見(jiàn)或建議歡迎下方留言,大家共同進(jìn)步怯邪!
下篇預(yù)告
1.正在運(yùn)行中實(shí)例和歷史流程查看
2.工作流的掛起與激活
敬請(qǐng)期待~~~
第三篇完結(jié)~