package jnpf.flowable.model.flowable; import com.alibaba.fastjson.JSONObject; import jnpf.config.ConfigValueUtil; import jnpf.constant.MsgCode; import jnpf.exception.WorkFlowException; import jnpf.util.JsonUtil; import jnpf.util.StringUtil; import jnpf.util.UserProvider; import jnpf.util.text.CharsetKit; import jnpf.util.wxutil.HttpUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.net.URLDecoder; import java.util.List; import java.util.Map; @Slf4j @Component @RequiredArgsConstructor public class FlowAbleUrl { private final ConfigValueUtil configValueUtil; public static final String DEPLOYMENT_ID = "?deploymentId="; public static final String TASK_ID = "?taskId="; public static final String TASK_KEY = "&taskKey="; public String deployFlowAble(String flowXml, String key) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/definition/deploy"; FlowAbleForm flowAbleForm = new FlowAbleForm(); flowAbleForm.setKey(key); try { flowAbleForm.setBpmnXml(URLDecoder.decode(flowXml, CharsetKit.UTF_8)); } catch (Exception e) { flowAbleForm.setBpmnXml(flowXml); } JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(flowAbleForm)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF090.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } FlowAbleData data = JsonUtil.getJsonToBean(flowAbleModel.getData(), FlowAbleData.class); return data.getDeploymentId(); } private String getFlowAbleUrl() { return configValueUtil.getFlowDomain(); } /** * 引擎 HTTP 调用统一出口:在原 HttpUtil.httpRequest 外记录往返耗时, * 日志标记 [flow-engine-timing],与引擎侧 [engine-timing] 相减即 HTTP 编排开销, * 为"是否内嵌引擎"决策积累数据(tasks/handoff-20260609-flow-engine-perf.md)。 * 异常与返回值原样透传,finally 只读计时不改变任何行为。 */ private JSONObject request(String url, String method, String body) { long start = System.nanoTime(); JSONObject result = null; try { result = HttpUtil.httpRequest(url, method, body, UserProvider.getToken()); return result; } finally { long costMs = (System.nanoTime() - start) / 1_000_000; log.info("[flow-engine-timing] {} {} {}ms success={}", method, endpointPattern(url), costMs, isBizSuccess(result)); } } /** * 取 /api/Flow 起始的端点模式(去域名与 query,并把 ID 路径段规整为 {id}), * 使日志可按端点聚合、且不泄露实例/任务业务标识。 */ private String endpointPattern(String url) { int i = url.indexOf("/api/Flow"); String path = i < 0 ? url : url.substring(i); int q = path.indexOf('?'); if (q >= 0) { path = path.substring(0, q); } // UUID(引擎主键雪花/uuid)与纯数字段统一成 {id},避免高基数 return path.replaceAll("/[0-9a-fA-F]{8}-[0-9a-fA-F-]{27,}", "/{id}") .replaceAll("/\\d{6,}", "/{id}"); } /** 业务是否成功(success!=false 视为成功;null 响应记 false),供耗时日志区分慢与失败 */ private boolean isBizSuccess(JSONObject result) { if (result == null) { return false; } Object success = result.get("success"); return !Boolean.FALSE.equals(success); } /** * 启动流程实例 * * @param deploymentId 引擎部署ID * @param variables 变量 */ public String startInstance(String deploymentId, Map variables) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/instance/start"; InstanceStartFo fo = new InstanceStartFo(); fo.setDeploymentId(deploymentId); fo.setVariables(variables); JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(fo)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF091.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } FlowAbleData data = JsonUtil.getJsonToBean(flowAbleModel.getData(), FlowAbleData.class); return data.getInstanceId(); } /** * 获取当前任务 * * @param instanceId 引擎实例ID */ public List getCurrentTask(String instanceId) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/list/" + instanceId; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF092.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), FlowableTaskModel.class); } /** * 删除流程实例 * * @param instanceId 实例ID * @param deleteReason 删除原因 */ public void deleteInstance(String instanceId, String deleteReason) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/instance?instanceId=" + instanceId; if (StringUtil.isNotEmpty(deleteReason)) { url += "&deleteReason=" + deleteReason; } JSONObject jsonObject = request(url, "DELETE", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel || !flowAbleModel.getSuccess()) { log.error("流程实例删除异常: {}", flowAbleModel); } } /** * 获取出线Key集合 * * @param fo 参数类 */ public List getOutgoingFlows(OutgoingFlowsFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/outgoing/flows"; if (StringUtil.isNotEmpty(fo.getTaskId())) { url += TASK_ID + fo.getTaskId(); } else { url += DEPLOYMENT_ID + fo.getDeploymentId() + TASK_KEY + fo.getTaskKey(); } JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF094.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 获取线之后的任务节点 * * @param deploymentId 部署id * @param flowKey 连接线的Key */ public List getTaskKeyAfterFlow(String deploymentId, String flowKey) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/flow/target?deploymentId=" + deploymentId + "&flowKey=" + flowKey; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF095.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 获取下一级任务节点集合 * * @param fo 参数类 */ public List getNext(NextOrPrevFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/next"; if (StringUtil.isNotEmpty(fo.getTaskId())) { url += TASK_ID + fo.getTaskId(); } else { url += DEPLOYMENT_ID + fo.getDeploymentId() + TASK_KEY + fo.getTaskKey(); } JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF096.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), FlowableNodeModel.class); } /** * 获取上一级任务节点id集合 * * @param fo 参数类 */ public List getPrev(NextOrPrevFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/prev"; if (StringUtil.isNotEmpty(fo.getTaskId())) { url += TASK_ID + fo.getTaskId(); } else { url += DEPLOYMENT_ID + fo.getDeploymentId() + TASK_KEY + fo.getTaskKey(); } JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF097.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 完成流程任务 * * @param fo 参数类 */ public void complete(CompleteFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/complete"; JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(fo)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF098.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } } /** * 获取流程实例 * * @param instanceId 实例id */ public FlowableInstanceModel getInstance(String instanceId) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/instance/" + instanceId; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF099.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToBean(flowAbleModel.getData(), FlowableInstanceModel.class); } /** * 获取未经过的节点 * * @param instanceId 引擎实例主键 */ public List getTobePass(String instanceId) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/tobe/pass/" + instanceId; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF100.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 获取节点的后续节点 * * @param fo 参数 */ public List getAfter(AfterFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/after"; JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(fo)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF101.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 获取可回退的节点ID * * @param taskId 引擎任务主键 */ public List getFallbacks(String taskId) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/fallbacks/" + taskId; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF102.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 退回 * * @param fo 参数 */ public void back(BackFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/back"; JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(fo)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF103.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } } /** * 节点跳转 * * @param fo 参数 */ public void jump(JumpFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/jump"; JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(fo)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF104.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } } /** * 任务完成的补偿 * * @param fo 参数 */ public List compensate(CompensateFo fo) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/compensate"; JSONObject jsonObject = request(url, "POST", JsonUtil.getObjectToString(fo)); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error(MsgCode.WF105.get()); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), FlowableTaskModel.class); } /** * 获取历史节点 * * @param instanceId 实例主键 */ public List getHistoric(String instanceId) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/historic/" + instanceId; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error("获取历史节点失败"); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), FlowableHistoricModel.class); } /** * 获取历史结束节点 * * @param instanceId 实例主键 */ public List getHistoricEnd(String instanceId) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/historic/end/" + instanceId; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error("获取历史结束节点失败"); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToList(flowAbleModel.getData(), String.class); } /** * 获取元素信息 * * @param deploymentId 部署id * @param key 节点key */ public FlowableNodeModel getElementInfo(String deploymentId, String key) throws WorkFlowException { String url = getFlowAbleUrl() + "/api/Flow/task/element/info"; url += DEPLOYMENT_ID + deploymentId + "&key=" + key; JSONObject jsonObject = request(url, "GET", null); FlowAbleModel flowAbleModel = JsonUtil.getJsonToBean(jsonObject, FlowAbleModel.class); if (null == flowAbleModel) { log.error("获取元素信息失败"); throw new WorkFlowException(MsgCode.WF142.get()); } if (Boolean.FALSE.equals(flowAbleModel.getSuccess())) { throw new WorkFlowException(flowAbleModel.getMsg()); } return JsonUtil.getJsonToBean(flowAbleModel.getData(), FlowableNodeModel.class); } }