1、说明
某天接到领导的一个需求,让某个用户拥有超级审批权限,当该用户执行超级审批时,同一节点的其它审批人将默认通过。例如某会签节点中,张三、李四、王五均是审批人,若此时李四和王五尚未审批通过,且张三有超级审批的权限,则可以直接让李四和王五默认通过,从而达到快速通过审批节省时间的目的。
2、编写service
总体思路:①获取所需审批节点的所有有权限审批的人员id列表 ②调用ekp自带的审批接口逐一对节点进行审批
package com.landray.kmss.xp.lbpm.workflow.service;
import java.util.List;
/**
* @author liquid
*/
public interface IXpLbpmService {
/**
* 当前节点审批通过(针对当前登录人)
* @author liquid
* @date 2022年6月28日
* @param fdModelId 流程实例id(km_review_main.fd_id)
* @param factNodeId 流程id(N2\N3\N4...)
*/
void passProcess(String fdModelId, String factNodeId);
/**
* 获取节点实际处理人(有权限处理)id列表
* @param fdModelId 流程实例id(km_review_main.fd_id)
* @param factNodeId 流程id(N2\N3\N4...)
*/
List<String> getActualHandlesByFactNodeId(String fdModelId, String factNodeId) throws Exception;
/**
* 超级审批
* @author liquid
* @date 2022年6月28日
* @param fdModelId 流程实例id(km_review_main.fd_id)
* @param factNodeId 流程id(N2\N3\N4...)
* @return 审批结果(fail、success)
*/
String superPassProcess(String fdModelId, String factNodeId, String handleId, String approveContent, String nextFactNodeId) throws Exception;
}
package com.landray.kmss.xp.lbpm.workflow.service.spring;
import com.alibaba.fastjson.JSONObject;
import com.landray.kmss.km.review.model.KmReviewMain;
import com.landray.kmss.km.review.webservice.IKmReviewWebserviceService;
import com.landray.kmss.km.review.webservice.KmReviewParamterForm;
import com.landray.kmss.sys.lbpm.engine.api.NodeInstanceHolder;
import com.landray.kmss.sys.lbpm.engine.api.ProcessHolder;
import com.landray.kmss.sys.lbpm.engine.api.WorkitemHolder;
import com.landray.kmss.sys.lbpm.engine.integrate.expecterlog.ILbpmExpecterLogService;
import com.landray.kmss.sys.lbpm.engine.persistence.model.LbpmProcess;
import com.landray.kmss.sys.lbpmservice.api.ApproveParameter;
import com.landray.kmss.sys.lbpmservice.api.LbpmService;
import com.landray.kmss.sys.lbpmservice.operation.handler.HandlerPassData;
import com.landray.kmss.sys.lbpmservice.support.service.ILbpmProcessService;
import com.landray.kmss.sys.lbpmservice.support.service.spring.LbpmAPIService;
import com.landray.kmss.sys.lbpmservice.support.service.spring.LbpmProcessServiceImp;
import com.landray.kmss.sys.organization.model.SysOrgElement;
import com.landray.kmss.util.SpringBeanUtil;
import com.landray.kmss.util.UserUtil;
import com.landray.kmss.xp.app.km.review.util.XpReviewUtil;
import com.landray.kmss.xp.lbpm.workflow.service.IXpLbpmService;
import com.landray.kmss.xp.org.organizational.constant.XpSysOrgElementConstant;
import com.landray.kmss.xp.org.organizational.util.XpOrgUtil;
import java.util.List;
import java.util.Map;
/**
* @author liquid
*/
public class XpLbpmServiceImp implements IXpLbpmService {
private ILbpmProcessService lbpmProcessService;
private ILbpmExpecterLogService lbpmExpecterLogService;
private LbpmService lbpmApiService;
private IKmReviewWebserviceService kmReviewWebserviceService;
public ILbpmProcessService getLbpmProcessService() {
if (lbpmProcessService == null) {
lbpmProcessService = (LbpmProcessServiceImp)SpringBeanUtil.getBean("lbpmProcessServiceTarget");
}
return lbpmProcessService;
}
public ILbpmExpecterLogService getLbpmExpecterLogService() {
if (lbpmExpecterLogService == null) {
lbpmExpecterLogService = (ILbpmExpecterLogService)SpringBeanUtil.getBean("lbpmExpecterLogService");
}
return lbpmExpecterLogService;
}
public LbpmService getLbpmApiService() {
if (lbpmApiService == null) {
lbpmApiService = (LbpmAPIService)SpringBeanUtil.getBean("lbpmServiceTarget");
}
return lbpmApiService;
}
public IKmReviewWebserviceService getKmReviewWebserviceService() {
if (kmReviewWebserviceService == null) {
kmReviewWebserviceService = (IKmReviewWebserviceService)SpringBeanUtil.getBean("kmReviewWebserviceServiceTarget");
}
return kmReviewWebserviceService;
}
/**
* 当前节点审批通过(针对当前登录人)
* @author liquid
* @date 2022年6月28日
* @param fdModelId 流程实例id(km_review_main.fd_id)
* @param factNodeId 流程id(N2\N3\N4...)
*/
@Override
public void passProcess(String fdModelId, String factNodeId) {
// 获取【主文档实例】
KmReviewMain mainModel = (KmReviewMain) XpReviewUtil.getReviewMainModelById(fdModelId);
// 获取【主文档实例对应的流程信息】
LbpmService lbpmApiService = getLbpmApiService();
ProcessHolder processHolder = lbpmApiService.load(mainModel);
// 流程当前节点信息列表
List<? extends NodeInstanceHolder> nodeInstHolders = processHolder.getCurrentNodeInfos();
for (NodeInstanceHolder nodeInstHolder : nodeInstHolders) {
// 非目标节点 → 跳过
String thisFactNodeId = nodeInstHolder.getNodeInstance().getFdFactNodeId();
if (!thisFactNodeId.equalsIgnoreCase(factNodeId)) {
continue;
}
// 当前登录人此节点的全部事项
List<? extends WorkitemHolder> workitemHolders = nodeInstHolder.getHandlerWrokitemInfos();
for (WorkitemHolder workitemHolder : workitemHolders) {
// 创建[通过]操作数据,此处就需要去参考[通过]操作的扩展描述
HandlerPassData data = new HandlerPassData();
// 驱动流程的操作参数,此处第二个参数参考[通过]操作的扩展描述
ApproveParameter parameter = new ApproveParameter(workitemHolder, "handler_pass");
parameter.setData(data);
// 设置后续处理人的通知方式
parameter.setNotifyType("todo;email");
// 设置流程结束时通知当前处理人
parameter.setNotifyOnFinish(true);
// 设置审批意见
parameter.setAuditNote(UserUtil.getUser().getFdName() + ": 执行超级审批");
// 驱动流程...
lbpmApiService.approveProcess(mainModel, parameter);
}
}
}
/**
* 获取节点实际处理人(有权限处理)id列表
* @param fdModelId 流程实例id(km_review_main.fd_id)
* @param factNodeId 流程id(N2\N3\N4...)
* @throws Exception 异常
*/
@Override
public List<String> getActualHandlesByFactNodeId(String fdModelId, String factNodeId) throws Exception {
/* 获取实际处理人列表 */
ILbpmExpecterLogService lbpmExpecterLogService = getLbpmExpecterLogService();
Map<String, List<String>> nodeHandlersMap = lbpmExpecterLogService.findCurrentNodeIdHandlerIds(fdModelId);
return nodeHandlersMap.get(factNodeId);
}
/**
* 超级审批
* @author liquid
* @date 2022年6月28日
* @param fdModelId 流程实例id(km_review_main.fd_id)
* @param factNodeId 流程id(N2\N3\N4...)
* @return 审批结果(fail、success)
* @throws Exception 异常
*/
@Override
public String superPassProcess(String fdModelId, String factNodeId, String handleId, String approveContent, String nextFactNodeId) throws Exception {
ILbpmProcessService lbpmProcessService = getLbpmProcessService();
IKmReviewWebserviceService kmReviewWebserviceService = getKmReviewWebserviceService();
/* 获取主文档实例 */
KmReviewMain mainModel = (KmReviewMain) XpReviewUtil.getReviewMainModelById(fdModelId);
/* 获取流程实例 */
LbpmProcess process = (LbpmProcess) lbpmProcessService.findByPrimaryKey(fdModelId);
/* 设置审批参数 */
KmReviewParamterForm kmParamForm = new KmReviewParamterForm();
// 流程模板id
kmParamForm.setFdTemplateId(mainModel.getFdTemplate().getFdId());
// 流程起草人
JSONObject docCreator = new JSONObject();
docCreator.put("Id", process.getFdCreator().getFdId());
kmParamForm.setDocCreator(docCreator.toJSONString());
// 流程实例id
kmParamForm.setFdId(fdModelId);
// 流程主题
kmParamForm.setDocSubject(mainModel.getDocSubject());
// 富文本(如果流程存在富文本内容,则不允许为空)
kmParamForm.setDocContent("");
JSONObject flowParam = new JSONObject();
// 设置处理人
SysOrgElement actualHandler = XpOrgUtil.getSysOrgElementById(handleId, SysOrgElement.class);
// 如处理人是非人员类型(如岗位), 需获取岗位人员(多个取第一个)
if (!actualHandler.getFdOrgType().equals(XpSysOrgElementConstant.ORG_TYPE_PERSON)) {
handleId = ((SysOrgElement)actualHandler.getFdPersons().get(0)).getFdId();
}
JSONObject handler = new JSONObject();
handler.put("Id", handleId);
flowParam.put("handler", handler.toJSONString());
// 操作类型(审批)
flowParam.put("operationType", "handler_pass");
// 审批意见
flowParam.put("auditNote", approveContent);
// 流向下一节点的ID,需要人工决策时设置此参数
flowParam.put("futureNodeId", nextFactNodeId);
kmParamForm.setFlowParam(flowParam.toJSONString());
/* 执行超级审批 */
try {
// 多个处理人操作同一份文档时采用锁机制,必须等上一个处理人操作完成后才能操作下一个
System.out.println("超级审批(参数): " + JSONObject.toJSONString(kmParamForm));
kmReviewWebserviceService.approveProcess(kmParamForm);
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
return "success";
}
}
相关xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<bean
id="xpLbpmTarget"
class="com.landray.kmss.xp.lbpm.workflow.service.spring.XpLbpmServiceImp">
</bean>
</beans>
3、编写action
package com.landray.kmss.xp.app.km.review.actions;
import com.alibaba.fastjson.JSONObject;
import com.landray.kmss.common.actions.ExtendAction;
import com.landray.kmss.common.service.IBaseService;
import com.landray.kmss.sys.formula.provider.function.OrgFunction;
import com.landray.kmss.util.SpringBeanUtil;
import com.landray.kmss.util.StringUtil;
import com.landray.kmss.util.UserUtil;
import com.landray.kmss.web.action.ActionForm;
import com.landray.kmss.web.action.ActionForward;
import com.landray.kmss.web.action.ActionMapping;
import com.landray.kmss.xp.lbpm.workflow.service.IXpLbpmService;
import com.landray.kmss.xp.util.XpRequestUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import static com.landray.kmss.tic.core.common.actions.TicCoreInvokeFuncAction.ReadAsChars;
/**
* @author liquid
*/
public class XpReviewMainAction extends ExtendAction {
private IXpLbpmService xpLbpmService;
@Override
protected IBaseService getServiceImp(HttpServletRequest request) {
return null;
}
public IXpLbpmService getXpLbpmService() {
if (xpLbpmService == null) {
xpLbpmService = (IXpLbpmService)SpringBeanUtil.getBean("xpLbpmTarget");
}
return xpLbpmService;
}
/**
* @apiNote 当前节点实际审批人(有审批权限)列表
* @author liquid
* @date 2022年6月28日
* 传入参数-
* modelId 流程模板实例id(km_review_main.fd_id,前端lbpm.modelId)
* factNodeId 流程节点id(N1\N2\N3...)
* 返回参数-
* handles 审批人id列表
*/
public ActionForward getActualHandlesByFactNodeId(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject result = new JSONObject();
response.setHeader("Content-Type", "application/json;charset=utf-8");
String bodyStr = ReadAsChars(request);
JSONObject o = JSONObject.parseObject(bodyStr);
/* 获取body参数 */
String fdModelId = o.getString("modelId");
String factNodeId = o.getString("factNodeId");
// 参数校验
if (StringUtil.isNull(fdModelId) || StringUtil.isNull(factNodeId)) {
XpRequestUtil.setFailure(response, "调用失败:请检查参数[名]和[值]");
return null;
}
/* 业务逻辑 */
IXpLbpmService xpLbpmService = getXpLbpmService();
List<String> handles = xpLbpmService.getActualHandlesByFactNodeId(fdModelId, factNodeId);
/* 响应数据 */
result.put("status", XpRequestUtil.STATUS_SUCCESS);
result.put("code", XpRequestUtil.CODE_SUCCESS);
result.put("msg", "调用成功");
result.put("handles", handles == null ? new ArrayList<>() : handles);
XpRequestUtil.setResponseResult(response, result);
return null;
}
/**
* @apiNote 当前节点审批通过(针对当前登录人)
* @author liquid
* @date 2022年6月28日
* 传入参数-
* modelId 流程模板实例id(km_review_main.fd_id,前端lbpm.modelId)
* factNodeId 流程节点id(N1\N2\N3...)
* 返回参数-
* approveInfo 审批通过信息
*/
public ActionForward approveProcessByFactNodeId(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject result = new JSONObject();
response.setHeader("Content-Type", "application/json;charset=utf-8");
String bodyStr = ReadAsChars(request);
JSONObject o = JSONObject.parseObject(bodyStr);
/* 获取body参数 */
String fdModelId = o.getString("modelId");
String factNodeId = o.getString("factNodeId");
/* 业务逻辑 */
IXpLbpmService xpLbpmService = getXpLbpmService();
xpLbpmService.passProcess(fdModelId, factNodeId);
/* 响应数据 */
result.put("status", XpRequestUtil.STATUS_SUCCESS);
result.put("code", XpRequestUtil.CODE_SUCCESS);
result.put("msg", "调用成功");
result.put("approveInfo", "审批成功");
XpRequestUtil.setResponseResult(response, result);
return null;
}
/**
* @apiNote 超级审批(一次仅审批一个处理人)
* @author liquid
* @date 2022年6月29日
* 传入参数-
* modelId 流程模板实例id(km_review_main.fd_id,前端lbpm.modelId)
* factNodeId 流程节点id(N1\N2\N3...)
* handleId 处理人id
* approveContent (可选)审批意见,默认值为:(特权人)特权人姓名 : 超级审批
* nextFactNodeId (可选)当下流为人工决策时,需传入(N1\N2\N3...)
* 返回参数-
* approveInfo 审批通过信息
* approveStatus 审批结果(fail、success、noPermission:无权限)
*/
public ActionForward superApproveProcessByFactNodeId(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject result = new JSONObject();
response.setHeader("Content-Type", "application/json;charset=utf-8");
String bodyStr = ReadAsChars(request);
JSONObject o = JSONObject.parseObject(bodyStr);
/* 获取body参数 */
String fdModelId = o.getString("modelId");
String factNodeId = o.getString("factNodeId");
String handleId = o.getString("handleId");
String approveContent = o.getString("approveContent");
String nextFactNodeId = o.getString("nextFactNodeId");
// 参数校验
if (StringUtil.isNull(fdModelId) || StringUtil.isNull(factNodeId) || StringUtil.isNull(handleId)) {
result.put("status", XpRequestUtil.STATUS_ERROR);
result.put("code", XpRequestUtil.CODE_ERROR);
result.put("msg", "调用失败:请检查参数[名]和[值]");
result.put("approveInfo", "审批失败");
XpRequestUtil.setResponseResult(response, result);
return null;
}
// 设置可选参数默认值
approveContent = StringUtil.isNull(approveContent) ? "超级审批" : approveContent;
approveContent = "(特权人)"+ UserUtil.getUser().getFdName() + " : " + approveContent;
nextFactNodeId = StringUtil.isNull(approveContent) ? "" : nextFactNodeId;
/* 业务逻辑 */
// 1、校验调用者权限
boolean isPrivileged = OrgFunction.chechPersonInGroup(UserUtil.getUser(), "超级审批");
if (!isPrivileged) {
/* 响应数据 - 成功 - 无审批权限 */
result.put("status", XpRequestUtil.STATUS_SUCCESS);
result.put("code", XpRequestUtil.CODE_SUCCESS);
result.put("msg", "调用失败:当前登录人没有超级审批权限");
result.put("approveInfo", "审批失败");
result.put("approveStatus", "noPermission");
XpRequestUtil.setResponseResult(response, result);
return null;
}
// 2、执行超级审批
IXpLbpmService xpLbpmService = getXpLbpmService();
String approveStatus;
try{
approveStatus = xpLbpmService.superPassProcess(fdModelId, factNodeId, handleId, approveContent, nextFactNodeId);
} catch (Exception e) {
e.printStackTrace();
/* 响应数据 - 失败 */
result.put("status", XpRequestUtil.STATUS_ERROR);
result.put("code", XpRequestUtil.CODE_ERROR);
result.put("msg", "调用失败:请检查参数[名]和[值]");
result.put("approveInfo", "审批失败");
result.put("approveStatus", "fail");
XpRequestUtil.setResponseResult(response, result);
return null;
}
/* 响应数据 - 成功 */
result.put("status", XpRequestUtil.STATUS_SUCCESS);
result.put("code", XpRequestUtil.CODE_SUCCESS);
result.put("msg", "调用成功");
result.put("approveInfo", "success".equals(approveStatus) ? "审批成功" : "审批失败");
result.put("approveStatus", approveStatus);
XpRequestUtil.setResponseResult(response, result);
return null;
}
}
相关xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean
class="com.landray.kmss.xp.app.km.review.actions.XpReviewMainAction"
name="/xp/app/km/review/actions/XpReviewMainAction.do"
lazy-init="true"
parent="KmssBaseAction">
</bean>
</beans>
4、前端调用
/**
* 超级审批
* 注意:仅适用于[非编辑状态]的审批节点
* 审批权限:群组 → 系统类 → 超级审批
* 2022年7月2日22:21:04 测试通过
* 2022年7月3日17:37:43 测试通过
* @author liquid
* @date 2022年7月2日
* @param {string}[factNodeId=当前节点] 节点id(N1\N2\N3...)
* @param {string}[approveContent] 审批意见
* @param {string}[nextFactNodeId] 当下流为人工决策时,需传入(N1\N2\N3...)
*/
let superApprovalCountLimit = 15;// 审批失败尝试次数上限(避免未知错误导致不断循环执行审批调用)
let superApprovalCount = 0;
function superApprove(factNodeId, approveContent, nextFactNodeId) {
factNodeId = factNodeId || lbpm.nowNodeId;
lui_loading(true, "<div style='text-align:center;width:100%;color:#6584ed'>正在执行超级审批...</div>");
// 延迟执行超级审批,避免同步执行导致阻塞loading的显示
setTimeout(()=>{
__superApprove();
}, 200);
function __superApprove() {
superApprovalCount ++;
// (同步)获取节点实际审批人id
let handlers;
const _path_1 = "xp/app/km/review/actions/XpReviewMainAction.do?method=getActualHandlesByFactNodeId";
const _data_1 = {
modelId: lbpm.modelId,
factNodeId: factNodeId,
}
ajaxAction(_path_1, _data_1, false, {
success:(res)=>{
if (res.status !== "success") { errorHandler(res); return; }
handlers = res.handles;
}
})
// 可能在点击超级审批的前一刻,最后一个审批人刚好审批完
if (handlers.length === 0) {
lui_loading(false);
lui_fail("当前节点无审批人!");
setTimeout(()=>{
location.reload();
}, 200)
return;
}
// (同步)执行超级审批
let isReturn = false;
for (const handlerId of handlers) {
if (isReturn) return;
const _path_2 = "xp/app/km/review/actions/XpReviewMainAction.do?method=superApproveProcessByFactNodeId";
const _data_2 = {
modelId: lbpm.modelId,
factNodeId: factNodeId,
handleId: handlerId,
approveContent: approveContent,
nextFactNodeId: nextFactNodeId
}
ajaxAction(_path_2, _data_2, false, {
success:(res)=>{
if (res.status !== "success") {
errorHandler(res, false);
lui_loading(false);
return;
}
// 成功回调
console.log("超级审批返回:", res);
// 无权限
if (res.approveStatus === 'noPermission'){
lui_loading(false);
lui_fail("抱歉,您尚无超级审批权限!");
isReturn = true;
}
// 有权限,审批失败(同时操作同一个文档导致)
else if (res.approveStatus === "fail" && superApprovalCount <= superApprovalCountLimit) {
console.log("审批失败,重新调用superApproval(),已调用次数:" + superApprovalCount);
// 失败递归调用
__superApprove(factNodeId);
isReturn = true;
}
else if (superApprovalCount > superApprovalCountLimit) {
console.log("审批失败,重新调用superApproval(),已达调用次数上限:" + superApprovalCount);
lui_loading(false);
lui_fail("超级审批:未知错误,请联系管理员");
isReturn = true;
}
}
})
}
lui_loading(false);
lui_success("超级审批:执行完毕!");
// 延迟刷新页面(以保证获取到最的流程状态信息)
setTimeout(()=>{
location.reload();
}, 500)
}
}