feat(流程管理): 新增流程相关

1.流程设计器新增可动态指定任务处理人。
2.可执行跳过第一个节点。
3.获取自定义配置验证是否需要指定动态人员、组。
approve-sys
tony 3 years ago
parent 1162897ba1
commit 0ed995e0c0

@ -0,0 +1,27 @@
package com.ruoyi.common.constant;
/**
*
* @author Xuan xuan
* @date 2021/4/17 22:46
*/
public class ProcessConstants {
/** 动态数据 */
public static final String DATA_TYPE = "dynamic";
/** 单个审批人 */
public static final String USER_TYPE_ASSIGNEE = "assignee";
/** 候选人 */
public static final String USER_TYPE_USERS = "candidateUsers";
/** 审批组 */
public static final String USER_TYPE_ROUPS = "candidateGroups";
/** 单个审批人 */
public static final String PROCESS_APPROVAL = "approval";
}

@ -0,0 +1,22 @@
package com.ruoyi.flowable.domain.dto;
import com.ruoyi.common.core.domain.entity.SysUser;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
*
* @author Xuan xuan
* @date 2021/4/17 22:59
*/
@Data
public class FlowNextDto implements Serializable {
private String type;
private String vars;
private List<SysUser> userList;
}

@ -1,6 +1,7 @@
package com.ruoyi.flowable.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.entity.SysUser;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;

@ -151,8 +151,11 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
@Override
public AjaxResult startProcessInstanceById(String procDefId, Map<String, Object> variables) {
try {
// 设置流程发起人Id到流程中
Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
identityService.setAuthenticatedUserId(userId.toString());
variables.put("initiator",userId);
variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true);
runtimeService.startProcessInstanceById(procDefId, variables);
return AjaxResult.success("流程启动成功");
} catch (Exception e) {

@ -5,12 +5,14 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.ruoyi.common.constant.ProcessConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.flowable.domain.dto.FlowCommentDto;
import com.ruoyi.flowable.domain.dto.FlowNextDto;
import com.ruoyi.flowable.domain.dto.FlowTaskDto;
import com.ruoyi.flowable.domain.vo.FlowTaskVo;
import com.ruoyi.flowable.factory.FlowServiceFactory;
@ -23,6 +25,7 @@ import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
@ -86,34 +89,23 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
taskService.addComment(task.getTaskId(), task.getInstanceId(), "1", task.getComment());
}
Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
// 变量信息
if (task.getValues() != null && task.getValues().size() > 0) {
Map<String, Object> map = (Map<String, Object>) task.getValues().get("mainTable");
map.put("FormOrigData" + task.getTaskId(), JSON.toJSONString(task.getValues()));
// List<TaskDto> list = getTaskFormList(task.getTaskId());
// // 节点上挂载的表单ID
// if (!CollectionUtils.isEmpty(list)) {
// List<Object> formList = new ArrayList<>();
// for (TaskDto taskDto : list) {
// formList.addAll(taskDto.getFormList());
// 读取变量 动态设置下一环节审批人
// Map<String, Object> taskValues = task.getValues();
// if (taskValues != null && taskValues.size() > 0) {
// if(StringUtils.isNotBlank((taskValues.get("assignee").toString()))){
// taskService.ad
// }
// if(CollectionUtils.isNotEmpty((List<String>) taskValues.get("candidateUsers"))){
//
// }
// if(CollectionUtils.isNotEmpty((List<String>) taskValues.get("candidateGroups"))){
//
// List<String> strs = new ArrayList<>();
// for (int k = 0; k < formList.size(); k++) {
// Map<String, Object> map1 = (Map<String, Object>) formList.get(k);
// strs.add(map1.get("id").toString());
// }
// }
taskService.setVariables(task.getTaskId(), map);
}
// 设置任务审批人员
taskService.setAssignee(task.getTaskId(), userId.toString());
// todo : 这种方式无法动态设置任务接收人
// if (StringUtils.isNotBlank(task.getAssignee())) {
// taskService.addCandidateUser(task.getTaskId(), task.getAssignee());
// }
// 提交任务
taskService.complete(task.getTaskId());
taskService.complete(task.getTaskId(),task.getValues());
}
@ -617,6 +609,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
.processInstanceId(task.getProcessInstanceId())
.singleResult();
SysUser startUser = sysUserService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId()));
// SysUser startUser = sysUserService.selectUserById(Long.parseLong(task.getAssignee()));
flowTask.setStartUserId(startUser.getNickName());
flowTask.setStartUserName(startUser.getNickName());
flowTask.setStartDeptName(startUser.getDept().getDeptName());
@ -757,6 +750,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
map.put("finished", false);
}
}
// 第一次申请获取初始化表单
if (StringUtils.isNotBlank(deployId)) {
SysForm sysForm = sysInstanceFormService.selectSysDeployFormByDeployId(deployId);
map.put("formData", JSONObject.parseObject(sysForm.getFormContent()));
@ -852,7 +846,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
@Override
public AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo) {
Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
List<FlowTaskDto> nextNodeList = new ArrayList<>();
FlowNextDto flowNextDto = new FlowNextDto();
if (Objects.nonNull(task)) {
ExecutionEntity ee = (ExecutionEntity) runtimeService.createExecutionQuery()
.executionId(task.getExecutionId()).singleResult();
@ -862,26 +856,34 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(crruentActivityId);
// 输出连线
List<SequenceFlow> outFlows = flowNode.getOutgoingFlows();
for (SequenceFlow sequenceFlow : outFlows) {
// 下一个审userTask
FlowElement targetFlow = sequenceFlow.getTargetFlowElement();
if (targetFlow instanceof UserTask) {
FlowTaskDto flowTaskDto = new FlowTaskDto();
// 当流程设计时未指定任务接受人员/组时 判定为用户动态选择下一任务审批人
// todo 1. 读取自定义节点属性来验证是否动态选择审批人
// 2. 验证表达式
if (StringUtils.isBlank(((UserTask) targetFlow).getAssignee()) &&
CollectionUtils.isEmpty(((UserTask) targetFlow).getCandidateGroups()) &&
CollectionUtils.isEmpty(((UserTask) targetFlow).getCandidateUsers())) {
flowTaskDto.setTaskDefKey(targetFlow.getId());
flowTaskDto.setTaskName(targetFlow.getName());
nextNodeList.add(flowTaskDto);
String dataType = targetFlow.getAttributeValue("http://flowable.org/bpmn", "dataType");
String userType = targetFlow.getAttributeValue("http://flowable.org/bpmn", "userType");
// Map<String, List<ExtensionAttribute>> attributes = targetFlow.getAttributes();
// List<ExtensionAttribute> extensionAttributes = attributes.get("dataType");
// for (ExtensionAttribute attribute : extensionAttributes) {
// String value = attribute.getValue();
// }
if (ProcessConstants.DATA_TYPE.equals(dataType)){
if (ProcessConstants.USER_TYPE_ASSIGNEE.equals(userType)){
List<SysUser> list = sysUserService.selectUserList(new SysUser());
flowNextDto.setVars(ProcessConstants.PROCESS_APPROVAL);
flowNextDto.setType(ProcessConstants.USER_TYPE_ASSIGNEE);
flowNextDto.setUserList(list);
}
}
}
}
}
return AjaxResult.success(nextNodeList);
return AjaxResult.success(flowNextDto);
}
/**

@ -68,6 +68,10 @@ export default {
{ label: '候选人员', value: 'candidateUsers' },
{ label: '候选组', value: 'candidateGroups' }
],
dataTypeOption: [
{ label: '固定', value: 'fixed' },
{ label: '动态', value: 'dynamic' }
],
dialogName: '',
executionListenerLength: 0,
taskListenerLength: 0,
@ -116,6 +120,31 @@ export default {
dic: _this.userTypeOption,
show: !!_this.showConfig.userType
},
{
xType: 'radio',
name: 'dataType',
label: '指定方式',
dic: _this.dataTypeOption,
show: !!_this.showConfig.dataType
},
// {
// xType: 'input',
// name: 'assigneeFixed',
// label: '()',
// show: !!_this.showConfig.assigneeFixed && _this.formData.userType === 'assignee' && _this.formData.dataType === 'fixed'
// },
// {
// xType: 'input',
// name: 'candidateUsersFixed',
// label: '()',
// show: !!_this.showConfig.candidateUsersFixed && _this.formData.userType === 'candidateUsers' && _this.formData.dataType === 'fixed'
// },
// {
// xType: 'input',
// name: 'candidateGroupsFixed',
// label: '()',
// show: !!_this.showConfig.candidateGroupsFixed && _this.formData.userType === 'candidateGroups' && _this.formData.dataType === 'fixed'
// },
{
xType: 'select',
name: 'assignee',
@ -253,6 +282,15 @@ export default {
})
}
},
//
'formData.dataType': function(val) {
const that = this
this.updateProperties({'flowable:dataType': val})
if (val === 'dynamic') {
debugger
this.updateProperties({'flowable:userType': that.formData.userType})
}
},
'formData.assignee': function(val) {
if (this.formData.userType !== 'assignee') {
delete this.element.businessObject.$attrs[`flowable:assignee`]

@ -6,9 +6,13 @@ export default {
},
'bpmn:UserTask': {
userType: true,
dataType: true,
assignee: true,
candidateUsers: true,
candidateGroups: true,
// assigneeFixed: true,
// candidateUsersFixed: true,
// candidateGroupsFixed: true,
async: true,
priority: true,
formKey: true,

@ -29,6 +29,7 @@
</el-tooltip>
</div>
<div>
<el-button size="mini" icon="el-icon-download" @click="showXML()">xml</el-button>
<el-button size="mini" icon="el-icon-download" @click="saveXML(true)">xml</el-button>
<el-button size="mini" icon="el-icon-picture" @click="saveImg('svg', true)">下载svg</el-button>
<el-button size="mini" type="primary" @click="save"></el-button>
@ -44,7 +45,9 @@
</el-aside>
</el-container>
</el-container>
<el-dialog :title="xmlTitle" :visible.sync="xmlOpen" width="50%" append-to-body>
<div v-html="xmlContent" />
</el-dialog>
</div>
</template>
@ -67,6 +70,9 @@ export default {
type: String,
default: ''
},
xmlTitle: "",
xmlOpen: false,
xmlContent: "",
users: {
type: Array,
default: () => []
@ -287,6 +293,17 @@ export default {
console.log(err)
}
},
async showXML() {
try {
const { xml } = await this.modeler.saveXML({ format: true })
this.xmlOpen = true;
this.xmlTitle = 'xml查看';
debugger
// this.xmlContent = this.parseXml(xml);
} catch (err) {
console.log(err)
}
},
async saveImg(type = 'svg', download = false) {
try {
const { svg } = await this.modeler.saveSVG({ format: true })
@ -321,6 +338,50 @@ export default {
a.download = filename
a.click()
window.URL.revokeObjectURL(url)
},
parseXml(content) {
let xml_doc = null
try {
xml_doc = (new DOMParser()).parseFromString(content.replace(/[\n\r\s]/g, ''), 'text/xml')
} catch (e) {
return false
}
let flag = 0
function build_xml(index, list, element) {
let t = []
for (let i = 0; i < flag; i++) {
t.push('&nbsp;&nbsp;&nbsp;&nbsp;')
}
t = t.join('')
list.push(t + '&lt;<span class="code-key">' + element.nodeName + '</span>&gt;<br/>')
for (let i = 0; i < element.childNodes.length; i++) {
const nodeName = element.childNodes[i].nodeName
if (element.childNodes[i].childNodes.length === 0) {
const value_txt = ''
const item = t + '&nbsp;&nbsp;&nbsp;&nbsp;&lt;<span class="code-key">' + nodeName +
'</span>&gt;' + value_txt + '&lt;/<span class="code-key">' + nodeName + '</span>&gt;<br/>'
list.push(item)
} else if ((element.childNodes[i].childNodes.length === 1 && element.childNodes[i].childNodes[0].nodeValue != null)) {
const value = element.childNodes[i].childNodes[0].nodeValue
const value_color = !isNaN(Number(value)) ? 'code-number' : 'code-string'
const value_txt = '<span class="' + value_color + '">' + value + '</span>'
const item = t + '&nbsp;&nbsp;&nbsp;&nbsp;&lt;<span class="code-key">' + nodeName +
'</span>&gt;' + value_txt + '&lt;/<span class="code-key">' + nodeName + '</span>&gt;<br/>'
list.push(item)
} else {
flag++
build_xml(++index, list, element.childNodes[i])
flag--
}
}
list.push(t + '&lt;/<span class="code-key">' + element.nodeName + '</span>&gt;<BR/>')
}
const list = []
build_xml(0, list, xml_doc.documentElement)
return list.join('')
}
}
}
@ -374,6 +435,12 @@ export default {
min-height: 650px;
}
.code-string{color:#993300;}
.code-number{color:#cc00cc;}
.code-boolean{color:#000033;}
.code-null{color:magenta;}
.code-key{color:#003377;font-weight:bold;}
// .highlight.djs-shape .djs-visual > :nth-child(1) {
// fill: green !important;
// stroke: green !important;

@ -404,8 +404,6 @@ export default {
},
/** 挂载表单 */
submitFormDeploy(){
debugger
console.log(this.formDeployParam)
addDeployForm(this.formDeployParam).then(res =>{
this.msgSuccess(res.msg);
this.formDeployOpen = false;

@ -14,7 +14,6 @@
<script>
import { readXml, saveXml, userList } from "@/api/flowable/definition";
// import bpmnModeler from "workflow-bpmn-modeler";
import bpmnModeler from '@/components/Process/index'
export default {
@ -26,7 +25,8 @@ export default {
return {
xml: "", // xml
users: [
// { name: "", id: "1" },
{ nickName: "#{initiator}", userId: "#{initiator}" },
{nickName: "#{approval}", userId: "#{approval}"}
// { name: "", id: "2" },
// { name: "", id: "100" },
],
@ -78,7 +78,13 @@ export default {
//
// }
userList().then(res =>{
this.users = res.data
res.data.forEach(val =>{
let obj = {};
obj.userId = val.userId;
obj.nickName = val.nickName;
this.users.push(obj)
})
debugger
})
},
},

@ -239,9 +239,6 @@ export default {
this.total = response.data.total;
this.loading = false;
});
// readXml("fc8504de-9063-11eb-b009-00e04c1b045a").then(res =>{
// this.xmlData = res.data
// });
},
//
cancel() {

@ -140,7 +140,8 @@ export default {
deployId: "", //
taskId: "" ,//
procDefId: "", //
assignee: null
assignee: null,
vars: "",
},
userList:[], //
formConf: {}, //
@ -234,11 +235,9 @@ export default {
taskId: taskId
}
getNextFlowNode(params).then(res => {
if (res.data){
userList().then(res =>{
this.userList = res.data;
if (res.data.userList){
this.userList = res.data.userList;
this.taskForm.noUserShow = true;
})
}
})
}
@ -247,6 +246,10 @@ export default {
handleComplete() {
this.$refs["taskForm"].validate(valid => {
if (valid) {
let values = {
"approval": this.taskForm.assignee
}
this.taskForm.values =values
complete(this.taskForm).then(response => {
this.msgSuccess(response.msg);
this.goBack();

Loading…
Cancel
Save