1、流程啟動及狀態流轉舉例(場景:同一份資料在多個流程中流轉,確定各流程自己的狀態)
1.發起流程核心程式碼
/** * 二進位制方式 * @param orgId * @param node * @param contractIds */ private void startWithPosition(String orgId, String node, List<String> contractIds){ CONTRACTAPPROVE currentNode = CONTRACTAPPROVE.valueOf(node); ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionKey(currentNode.getDefKey()) .latestVersion() .singleResult(); if(processDefinition == null) { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), "流程定義不存在"); } //全域性流程中所有處於未審批和執行中的流程狀態int值 List<Integer> allNoPermitState = Arrays.stream(CONTRACTAPPROVE.values()) .map(e -> Arrays.asList(ApprovalState.WAIT.getCode(), ApprovalState.EXECUTE.getCode()) .stream() .map(s -> Integer.valueOf(s) << e.getStateIndex()) .collect(Collectors.toList())) .flatMap(Collection::stream) .collect(Collectors.toList()); List<ContractEntity> contractList = contractMapper.selectBatchIds(contractIds); Map<String, ContractEntity> collect = contractList.stream() .filter(e -> e.getApprovalState() != null && allNoPermitState.contains(Integer.valueOf(e.getApprovalState()))) .collect(Collectors.toMap(ContractEntity::getId, e -> e)); if(CollUtil.isNotEmpty(collect)) { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), "以下合同已存在進行中的審批:" + JSON.toJSONString(collect.keySet())); } String processDefinitionId = processDefinition.getId(); for(ContractEntity contract : contractList) { String approvalNode = contract.getApprovalNode() == null ? CONTRACTAPPROVE.CONTRACT_PRICE_APPROVE.getCode() : contract.getApprovalNode(); CONTRACTAPPROVE dbNode = CONTRACTAPPROVE.getByCode(approvalNode); Assert.isTrue(currentNode.getCode().compareTo(dbNode.getCode()) >= 0, () -> { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), String.format( "合同%s當前所處的階段【%s】不支援逆向流程發起!!!,正常順序\n(價格報批0000 0001->合同會籤0000 0011->合同釋出0000 0111->合同變更 0000 1111->合同終止 0001 1111)", contract.getId(), approvalNode)); }); contract.setApprovalNode(node); String approvalState = contract.getApprovalState(); Integer approvalStateNumber = StringUtils.isNotEmpty(approvalState)? Integer.valueOf(approvalState): 0; Map<String, Object> map = BeanUtil.beanToMap(contract); map.put("beanName", "contractFlowEventListener"); flowDefinitionService.startProcessInstanceById(processDefinitionId, currentNode.getCode(), "true", map); String waitCode = ApprovalState.WAIT.getCode(); Integer currentNodeState = Integer.valueOf(waitCode)<<currentNode.getStateIndex(); Integer globalState = currentNodeState | approvalStateNumber; contract.setApprovalState(String.valueOf(globalState)); contractMapper.updateById(contract); } }
2.獲取多個流程
private List<Map<String, String>> positionList() { return Arrays.stream(CONTRACTAPPROVE.values()).map(e -> new LinkedHashMap<String, String>() {{ put(e.name(), e.getName() + "," + e.getCode()); }}).collect(Collectors.toList()); }
3、流程組裝成列舉
package cn.togeek.geekbidder.crm.contract.service; import cn.togeek.foundation.web.api.BusinessCode; import cn.togeek.foundation.web.exception.BusinessException; import java.util.Arrays; import java.util.Optional; import org.springframework.util.Assert; public enum CONTRACTAPPROVE { /** * 價格報批0000 0001,合同會籤0000 0011、合同釋出0000 0111、合同變更 0000 1111、合同終止 0001 1111 */ CONTRACT_PRICE_APPROVE("價格報批", "0000 0001", 0,"Process_lzkpuzut") { @Override CONTRACTAPPROVE[] getNearNode() { return new CONTRACTAPPROVE[]{null, CONTRACT_SIGN_APPROVE}; } }, CONTRACT_SIGN_APPROVE("合同會籤", "0000 0011", 3,"Process_lzl5psyn") { @Override CONTRACTAPPROVE[] getNearNode() { return new CONTRACTAPPROVE[]{CONTRACT_PRICE_APPROVE, CONTRACT_PUBLISH_APPROVE}; } }, CONTRACT_PUBLISH_APPROVE("合同釋出", "0000 0111", 6,"Process_lzl5r66s") { @Override CONTRACTAPPROVE[] getNearNode() { return new CONTRACTAPPROVE[]{CONTRACT_SIGN_APPROVE, CONTRACT_CHANGE_APPROVE}; } }, CONTRACT_CHANGE_APPROVE("合同變更", "0000 1111",9,"Process_lzl5tu36") { @Override CONTRACTAPPROVE[] getNearNode() { return new CONTRACTAPPROVE[]{CONTRACT_PUBLISH_APPROVE, CONTRACT_TERMINATE_APPROVE}; } }, CONTRACT_TERMINATE_APPROVE("合同終止", "0001 1111", 12,"Process_lzl5t5h6") { @Override CONTRACTAPPROVE[] getNearNode() { return new CONTRACTAPPROVE[]{CONTRACT_CHANGE_APPROVE, null}; } }; private final String name; private final String code; //狀態所在二進位制位左移的位數 private final Integer stateIndex; private final String defKey; public String getName() { return name; } public String getCode() { return code; } public Integer getStateIndex() { return stateIndex; } public String getDefKey() { return defKey; } CONTRACTAPPROVE(String name, String code, Integer stateIndex, String defKey) { this.name = name; this.code = code; this.stateIndex = stateIndex; this.defKey = defKey; } /** * * 獲取當前節點的相鄰節點 * @return */ abstract CONTRACTAPPROVE[] getNearNode(); public static CONTRACTAPPROVE getByCode(String code) { Optional<CONTRACTAPPROVE> any = Arrays.stream(CONTRACTAPPROVE.values()).filter(n -> n.getCode().equals(code)).findAny(); Assert.isTrue(any.isPresent(), () -> { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), "流程編碼錯誤!!!"); }); return any.get(); } }
4、監聽回撥
package cn.togeek.geekbidder.crm.contract.service; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.AllArgsConstructor; import cn.togeek.foundation.util.DateTimeHelper; import cn.togeek.foundation.web.api.BusinessCode; import cn.togeek.foundation.web.exception.BusinessException; import cn.togeek.geekbidder.crm.contract.entity.ContractEntity; import cn.togeek.geekbidder.crm.contract.enums.ApprovalState; import cn.togeek.geekbidder.crm.contract.mapper.ContractNewMapper; import cn.togeek.maple.process.api.enums.FlowCommentEnum; import cn.togeek.maple.process.api.model.ListenerEvent; import cn.togeek.maple.process.api.model.ListenerEventSource; import java.time.LocalDate; import java.util.Map; import java.util.Objects; import org.flowable.engine.RuntimeService; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * @author hanle * @description * @date 2024/8/6 13:44 * @Company 西安圖跡資訊科技有限公司 */ @Component @AllArgsConstructor public class ContractFlowEventListener implements ApplicationListener<ListenerEventSource> { @Autowired private RuntimeService runtimeService; @Autowired private ContractNewMapper contractNewMapper; /** * 流程結束監聽 * * @param event */ @Override public void onApplicationEvent(ListenerEventSource event) { callBackWithStateCal(event); } /** * 二進位制操作方式 * @param event */ public void callBackWithPositionCal(ListenerEventSource event){ ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(event.getListenerEvent().getProcessInstanceId()) .singleResult(); if(Objects.isNull(processInstance)) { return; } Map<String, Object> processVariables = processInstance.getProcessVariables(); String id = String.valueOf(processVariables.get("id")); ListenerEvent listenerEvent = event.getListenerEvent(); String approvalResults = listenerEvent.getApprovalResults(); LambdaUpdateWrapper<ContractEntity> wrapper = new LambdaUpdateWrapper<ContractEntity>().eq(ContractEntity::getId, id); ContractEntity contractEntity = contractNewMapper.selectById(id); Assert.notNull(contractEntity, () -> { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), String.format( "合同id為"+ id + "的合同不存在,可能已被刪除,請檢查!!!")); }); CONTRACTAPPROVE dbNode = CONTRACTAPPROVE.getByCode(contractEntity.getApprovalNode()); CONTRACTAPPROVE nextNode = dbNode.getNearNode()[1]; //上一個流程節點結束時將全域性流程節點改為下一個節點 if(nextNode!=null) { contractEntity.setApprovalNode(nextNode.getCode()); } Integer dbFinalState = Integer.valueOf(contractEntity.getApprovalState()); String state = Integer.toString(dbFinalState, 2); //向左偏移量 Integer offsetLft = dbNode.getStateIndex(); //3個bit位代表狀態 int bitPoint = 3; state = StrUtil.padPre(state, bitPoint + offsetLft, "0"); String statePre = state.substring(state.length() - offsetLft, state.length()); Integer preStateInt = Integer.valueOf(StringUtils.hasLength(statePre) ? statePre : "0", 2); if(FlowCommentEnum.NORMAL.getType().equals(approvalResults)) { // 審批透過 Integer pass = Integer.valueOf(ApprovalState.CHANGE.getCode()); int curNodeState = pass << offsetLft; int globalNodeState = curNodeState | preStateInt; contractEntity.setContractState(String.valueOf(globalNodeState)); } else if(FlowCommentEnum.REJECT.getType().equals(approvalResults)) { // 審批駁回 Integer reject = Integer.valueOf(ApprovalState.TERMINATION.getCode()); int curNodeState = reject << offsetLft; int globalNodeState = curNodeState | preStateInt; contractEntity.setContractState(String.valueOf(globalNodeState)); } contractNewMapper.updateById(contractEntity); } /** * 常規操作 * @param event */ public void callBackWithStateCal(ListenerEventSource event){ ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(event.getListenerEvent().getProcessInstanceId()) .singleResult(); if(Objects.isNull(processInstance)) { return; } Map<String, Object> processVariables = processInstance.getProcessVariables(); String id = String.valueOf(processVariables.get("id")); ListenerEvent listenerEvent = event.getListenerEvent(); String approvalResults = listenerEvent.getApprovalResults(); ContractEntity contractEntity = contractNewMapper.selectById(id); Assert.notNull(contractEntity, () -> { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), String.format( "合同id為"+ id + "的合同不存在,可能已被刪除,請檢查!!!")); }); ContractApproveGlobal dbNode = ContractApproveGlobal.getByCode(contractEntity.getApprovalNode()); ContractApproveGlobal nextNode = dbNode.getNearNode()[1]; //上一個流程節點結束時將全域性流程節點改為下一個節點 if(nextNode!=null) { contractEntity.setApprovalNode(nextNode.getCode()); } if(FlowCommentEnum.NORMAL.getType().equals(approvalResults)) { // 審批透過 contractEntity.setContractState(null); } else if(FlowCommentEnum.REJECT.getType().equals(approvalResults)) { // 審批駁回 contractEntity.setContractState(ApprovalState.TERMINATION.getCode()); } contractNewMapper.updateById(contractEntity); } }