工作流使用

lishihuan大约 8 分钟

工作流使用

1. 设置详情、审批界面

注:这里的配置是针对,走统一的消息待办,移动端的实现是靠消息待办实现

模型下设置 业务表详情界面

  • formPage:PC端的路由path (需要注意的是,如果是三级菜单需要带入二级菜单路径,例如:ocdetail/ocBargainingForm)
  • appFormPage: APP 端的路由path
  • approvalPage:自定义的审批界面(针对驳回的场景,目前一般都是跳转到添加界面)如果都是跳转到详情界面操作则不用指定这个字段 PC端
  • appApprovalPage:自定义的审批界面(针对驳回的场景,目前一般都是跳转到添加界面)如果都是跳转到详情界面操作则不用指定这个字段 app端
  • formPopupPage: popup 考虑某些页面是用弹窗的方式进行操作

注:目前考虑到如果跳转的是页面(不是tab页面),这样就会出现缓存问题,这里,需要使用自定义的手动刷新功能

所以 设置 formPage/appFormPage 就不建议是path,因为如果是三级菜单,需要带上二级路径才能正常跳转,同时 手动刷新功能需要的是组件name,

这里需要保证,几个名称一致

  • 菜单设置path
  • 组件名称
  • 路由对应的实际name(目前代码中会强行将首字母改为大写)
image-20230415164142343
image-20230415164142343
image-20230415165959020
image-20230415165959020

2. 设置自定义表单

点击 任务节点,选择自定义的表单

目前至少要有 (字段尽量和我的保持一直,后期会取这2个变量,展示在流转记录中)

​ 审批描述:comment

​ 审批意见:commentType

image-20230415164347763
image-20230415164347763
image-20230415164714970
image-20230415164714970

3. 流转条件

  • 取消: ${commentType=='cancel'}

  • 通过:${commentType=='y'}

  • 驳回:${commentType=='n'}

  • 撤回:${commentType=='recall'}

4. 业务表数据状态变更的处理

对于涉及到业务表状态变革的,通过自定义变量实现

目前考虑了2个场景:

  • businessStatus:针对的是 主表记录

    参数说明 : status:1

    • status: 表示主表状态对于的字段
    • 1 :表示当前状态对于的数值
  • subsidiaryTableStatus: 针对的是附属表

    参数说明:status:3:apply_id:zjjc_person 总共记录4个参数

    • status: 表示附属表状态对于的字段
    • 1 :表示附属表状态对于的数值
    • apply_id: 附属表的外键(关联主表的字段)
    • zjjc_person: 附属表表名
image-20230415165009818
image-20230415165009818

5. 代办列表

代办列表现在封装在 actTaskList.vue 中需要指定 流程定义的keyprocDefKey ,不指定表示查询全部

6. 流转记录

流转记录也封装出去了,需要指定 流程实例id即可

image-20230415172438412
image-20230415172438412
image-20230415172351370
image-20230415172351370

7. 自定义表单的渲染

saveForm () {
    // this.$refs.formLayoutRef.taskAudit(this.form); // 方式1 用弹窗的形式,把 审批嵌入到 form-head-layout.vue 组件中,作为公共方法
    if (!this.form.taskId) {
        this.$message.error('流程异常,无法审批!')
        return
    }
    const res = this.$refs.wfFormPageRef.getVFormData()
    deepCopy(this.form, res)// js 深拷贝。直接赋值 可能会覆盖数据
    this.submitForm();
},
image-20230415170211863
image-20230415170211863

8. 调用工作流

taskContent:作为 代办内容,后期计划和消息绑定,需要在启动时指定一个默认的(目前还不完善)

image-20230415170452191
image-20230415170452191
@Override
    @Transactional(readOnly = false)
    public void saveData(ZjjcPersonApply zjjcPersonApply) {
    	// 1. 业务表保存
	    Long businessId = zjjcPersonApply.getId();
        if(zjjcPersonApply.getId()==null){
	        // 需要验证人员 编号是否在系统人员表中存在,因为后期计划是将其作为登录账号的
	        //checkUserName(zjjcPersonApply);
	        checkUserIdCard(zjjcPersonApply); // 验证身份证号是否存在
            insertZjjcPersonApply(zjjcPersonApply);
        }else{
            updateZjjcPersonApply(zjjcPersonApply);
        }
        // zj_user_audit 角色
        // 2. 人员附属表保存
	    zjjcPersonService.saveUser(zjjcPersonApply);
        // 3. 流程    handleType 操作类型 START_FLOW:启动流程
	    if(START_FLOW.equals(zjjcPersonApply.getHandleType())){
		    Map<String, Object> vars = Maps.newHashMap();
		    getPushMess(zjjcPersonApply,vars);
		    vars.put("business_dept",zjjcPersonApply.getUnitId());// 审批需要对 审批单位进行限定,需要指定单位下的指定角色才能审批
	    	if(businessId==null){ // 表示 添加,执行启动流程
			    actTaskService.startProcess(FLOW[0],FLOW[1],zjjcPersonApply.getId().toString(),"桩基检测人员审核01",vars);
		    }else{
			    vars.put("commentType",zjjcPersonApply.getCommentType()); // 操作类型  y n  目前只要用于 流转记录中 打标签
			    Task task = actTaskService.completeForBusinessStatus(FLOW[1], zjjcPersonApply.getId().toString(), zjjcPersonApply.getProcInsId(), zjjcPersonApply.getComment(), vars);// zjjcPersonApply.getTaskId(),
		        // 审批通过后,需要讲人员往 系统用户表中创建,并且需要验证人员编号是否重复
			    if (zjjcPersonApply.getCommentType()!=null && "Y".equals(zjjcPersonApply.getCommentType().toUpperCase()) && task==null) {
				    saveSysUser(zjjcPersonApply); // 人员添加
			    }
		    }
	    }
    }
@Autowired
	private RemotePushService remotePushService;
	// 推送附加信息
	public void getPushMess(ZjjcPersonApply zjjcPersonApply, Map<String, Object> vars){
		JSONObject jsonData= new JSONObject(new LinkedHashMap());
		jsonData.put("basic_info",getPushBasicInfo(zjjcPersonApply));// 基本信息,主要是针对 消息
//		jsonData.put("extra_info",getPushExtraInfo(zjjcPersonApply));
		vars.put("push_mess",jsonData);
		// 更新上个环节发送的消息 置为 已操作
		remotePushService.updateMessPushStatus(null,zjjcPersonApply.getId(),zjjcPersonApply.getTaskId(),null,zjjcPersonApply.getCommentType());
	}

	/**
	 * 针对需要 拓展消息显示字段的场景
	 * @param zjjcPersonApply
	 * @return
	 */
	public JSONObject getPushExtraInfo(ZjjcPersonApply zjjcPersonApply){
		SysUser applyUser = utils.getSysUser(zjjcPersonApply.getCreateBy());
		JSONObject jsonObject= new JSONObject(new LinkedHashMap());
		jsonObject.put("title","桩基人员申请");
		jsonObject.put("sub_title","");// 副标题
//		jsonObject.put("level","");//等级   jsonObject.put("level_name","");//等级name
		jsonObject.put("type","zj_user_flow");
		// 附加信息
		JSONObject sonNode= new JSONObject(new LinkedHashMap());
		sonNode.put("申请人员",applyUser.getNickName());
		sonNode.put("申请开始",DateUtils.formatDate(zjjcPersonApply.getCreateTime() ,"yyyy.MM.dd"));
		jsonObject.put("data",sonNode);
		return jsonObject;
	}

	// 基本信息 主要是针对 消息模板中需要自定义参数的场景
	public JSONArray getPushBasicInfo(ZjjcPersonApply zjjcPersonApply){
		JSONArray array=new JSONArray();
		SysUser applyUser = utils.getSysUser(); // zjjcPersonApply.getCreateBy()
		array.add(zjjcPersonApply.getApplyNo()); // 单号
		array.add(applyUser.getDept().getDeptName()); // 目前检测公司是人员所在部门
//		array.add(applyUser.getCompanyName()); // 公司
		array.add(applyUser.getNickName()); // 当前登陆人
		return array;
	}


	/**
	 * 流程提供 删除和撤回功能
	 * @param zjjcPersonApply
	 * @return
	 */
	@Override
	@Transactional(readOnly = false)
	public void withdrawAndDele(ZjjcPersonApply zjjcPersonApply) {
		if("chehui".equals(zjjcPersonApply.getHandleType())){ // 撤回
			Map<String, Object> vars = Maps.newHashMap();
//			getPushMess(zjjcPersonApply,vars);
			// 更新上个环节发送的消息 置为 已操作
			remotePushService.updateMessPushStatus(null,zjjcPersonApply.getId(),zjjcPersonApply.getTaskId(),null,"recall"); // 给上一个消息进行回执-标记为 撤回
			actTaskService.withdraw(FLOW[1],zjjcPersonApply.getId().toString(),zjjcPersonApply.getProcInsId(),"uzer_edit", "申请人撤回申请", vars);
		} else { // 删除
			deleteApply(zjjcPersonApply);
			actTaskService.deleteProcessInstance(zjjcPersonApply.getProcInsId(),"删除");
		}
	}

9. 监听

监听目前总共2个目的

  1. 考虑到审批需要指定角色+ 部门来筛选数据,所以指定 businessDept
  2. 配置消息,同时消息会重写代办的描述/内容
image-20231123182319500
image-20231123182319500
  • 指定需要过滤的部门 businessDept 表达式 参数 ${business_dept}
  • pushCode : 对应消息的code,用来推送消息的
  • pushMess:消息推送内容 表达式 参数 ${push_mess}
  • businessDept: 指定部门+岗位进行审批 参数:${business_dept}
  • isNeedSms: 用来控制是否发送短信,1:表示发送,0:表示不发送(结合系统管理-参数设置中的开关)

如果想实现,指定部门+岗位进行审批,指定流程变量 business_dept -- 这个字段对应的是 监听器中配置的

image-20230707090612287
image-20230707090612287

10. 消息配置

// 工作流业务
public void saveActTask(ZjjcPersonApply zjjcPersonApply){
    if(START_FLOW.equals(zjjcPersonApply.getHandleType())){
        Map<String, Object> vars = Maps.newHashMap();
        getPushMess(zjjcPersonApply,vars); // 推送消息
        vars.put("business_dept",zjjcPersonApply.getUnitId());// 审批需要对 审批单位进行限定,需要指定单位下的指定角色才能审批
        if(businessId==null){ // 表示 添加,执行启动流程
            actTaskService.startProcess(FLOW[0],FLOW[1],zjjcPersonApply.getId().toString(),"桩基检测人员审核01",vars);
        }else{
            vars.put("commentType",zjjcPersonApply.getCommentType()); // 操作类型  y n  目前只要用于 流转记录中 打标签
            Task task = actTaskService.completeForBusinessStatus(FLOW[1], zjjcPersonApply.getId().toString(), zjjcPersonApply.getProcInsId(), zjjcPersonApply.getComment(), vars);// zjjcPersonApply.getTaskId(),
            // 审批通过后,需要讲人员往 系统用户表中创建,并且需要验证人员编号是否重复
            if (zjjcPersonApply.getCommentType()!=null && "Y".equals(zjjcPersonApply.getCommentType().toUpperCase()) && task==null) {
                saveSysUser(zjjcPersonApply); // 人员添加
            }
        }
    }
}




@Autowired
private RemotePushService remotePushService;
// 推送附加信息
public void getPushMess(ZjjcPersonApply zjjcPersonApply, Map<String, Object> vars){
    JSONObject jsonData= new JSONObject(new LinkedHashMap());
    jsonData.put("basic_info",getPushBasicInfo(zjjcPersonApply));// 基本信息,主要是针对 消息
    //		jsonData.put("extra_info",getPushExtraInfo(zjjcPersonApply));
    vars.put("push_mess",jsonData);
    // 更新上个环节发送的消息 置为 已操作
    remotePushService.updateMessPushStatus(null,zjjcPersonApply.getId(),zjjcPersonApply.getTaskId(),null,zjjcPersonApply.getCommentType());
}

/**
	 * 针对需要 拓展消息显示字段的场景
	 * @param zjjcPersonApply
	 * @return
	 */
public JSONObject getPushExtraInfo(ZjjcPersonApply zjjcPersonApply){
    SysUser applyUser = utils.getSysUser(zjjcPersonApply.getCreateBy());
    JSONObject jsonObject= new JSONObject(new LinkedHashMap());
    jsonObject.put("title","桩基人员申请");
    jsonObject.put("sub_title","");// 副标题
    //		jsonObject.put("level","");//等级   jsonObject.put("level_name","");//等级name
    jsonObject.put("type","zj_user_flow");
    // 附加信息
    JSONObject sonNode= new JSONObject(new LinkedHashMap());
    sonNode.put("申请人员",applyUser.getNickName());
    sonNode.put("申请开始",DateUtils.formatDate(zjjcPersonApply.getCreateTime() ,"yyyy.MM.dd"));
    jsonObject.put("data",sonNode);
    return jsonObject;
}

// 基本信息 主要是针对 消息模板中需要自定义参数的场景
public JSONArray getPushBasicInfo(ZjjcPersonApply zjjcPersonApply){
    JSONArray array=new JSONArray();
    SysUser applyUser = utils.getSysUser(); // zjjcPersonApply.getCreateBy()
    array.add(zjjcPersonApply.getApplyNo()); // 单号
    array.add(applyUser.getCompanyName()); // 公司
    array.add(applyUser.getNickName()); // 当前登陆人
    return array;
}
image-20230708143345238
image-20230708143345238

11. 待办列表处理按钮的名称

支持通过连线来获取

12. 消息

INSERT INTO sys_push_modules(`name`, `title`, `code`, `type_id`, `msg_type`, `opt_type`, `msg_url`, `data_scope`, `user_posts`, `object_ids`, `template`, `param_num`, `sort`, `remarks`, `del_flag`, `create_by`, `create_date`, `update_by`, `update_date`) VALUES ( '补偿协议审批单.审核', '补偿协议审批单.审核', 'oc_bcxyspd_push', 8, '1', '2', 'OcCompensationApprovalForm', '9', '', '', '{0}提交了{1},待您审批', 2, 1, 'xxx提交了2024年补偿协议审批单申请', '0', '1', '2023-04-17 00:00:00', '1', '2024-09-06 12:11:41');

INSERT INTO sys_push_modules(`name`, `title`, `code`, `type_id`, `msg_type`, `opt_type`, `msg_url`, `data_scope`, `user_posts`, `object_ids`, `template`, `param_num`, `sort`, `remarks`, `del_flag`, `create_by`, `create_date`, `update_by`, `update_date`) VALUES ( '补偿协议审批单审批.驳回', '补偿协议审批单审批.驳回', 'oc_bcxyspd_push:reject', 8, '1', '2', 'AddOcCompensationApproval', '9', NULL, '', '{0} 驳回您的{1},待您处理', 2, 20, NULL, '0', '1', '2023-04-17 00:00:00', '1', '2024-09-06 14:54:38');

INSERT INTO sys_push_modules(`name`, `title`, `code`, `type_id`, `msg_type`, `opt_type`, `msg_url`, `data_scope`, `user_posts`, `object_ids`, `template`, `param_num`, `sort`, `remarks`, `del_flag`, `create_by`, `create_date`, `update_by`, `update_date`) VALUES ('补偿协议审批单审批申请', '补偿协议审批单审批.申请', 'oc_bcxyspd_add_push', 8, '1', '2', 'AddOcCompensationApproval', '9', '', '', '{0}已经审批通过补偿协议审批单,待您提交{1}', 2, 1, '', '0', '1', '2023-04-17 00:00:00', '1', '2024-09-09 19:55:23');

其他

1. 控制是否签收

考虑到有限虚拟的节点不能进行签收,这个场景通过isNotClaim 来控制 0:需要签收,1:不需要签收

场景: 清障管理中,年计划分为主流程和子流程,需要讲2个流程串联起来,所有需要在子流程中添加一个虚拟节点,但是如果进行签收,则这个虚拟节点就会变成主流程的最后一个审批人的已办任务,显然不合理

if(ocDeptApply.getIsNotClaim()!=null){
                vars.put("isNotClaim", ocDeptApply.getIsNotClaim());
            }
Act act = actTaskService.completeForBusinessStatus(BUSINESS_TABLE, ocDeptApply.getId().toString(), ocDeptApply.getProcInsId(), ocDeptApply.getComment(), vars, ocDeptApply.getTaskId());// ocDeptApply.getTaskId(),