java實現用一個變數表示多個屬性的狀態

海的味道發表於2024-08-09

1、流程啟動及狀態流轉舉例(場景:同一份資料在多個流程中流轉,確定各流程自己的狀態)

1.發起流程核心程式碼

java實現用一個變數表示多個屬性的狀態
/**
    * 二進位制方式
    * @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);
      }
   }
View Code

2.獲取多個流程

java實現用一個變數表示多個屬性的狀態
   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());
   }
View Code

3、流程組裝成列舉

java實現用一個變數表示多個屬性的狀態
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();
   }
}
View Code

4、監聽回撥

java實現用一個變數表示多個屬性的狀態
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);
   }
}
View Code

相關文章