# 1.返回多套编号的列表数据钩子


- 1.1功能说明

流程编号是支持多套编号场景，所有套编号的后台配置信息需要通过接口返回查看；

**支持基线：2411**

- 1.2参数说明

long markObjId 流程ID
string tenantKey 租户key

- 1.3响应说明

List<MarkDefEntity> -多套编号的所有实体类对象

- 1.4示例代码

com.weaver.workflow.mark.util.MarkSdkCommonUtils#getMarkDefEntityByWorkflowId

```
/**
     * 根据markObjId获取多套编号的全部编号设置
     * @param markObjId 流程Id
     * @param tenantKey 租户key
     * @return
     */
    public List<MarkDefEntity> getAllMarkDefEntityByWorkflowId (long markObjId, String tenantKey) {
        return markDefDao.getAllMultiMarkDefByWorkflowId(markObjId, tenantKey);
    }

```

# 2.支持检测编号重复的接口钩子

- 2.1功能说明

流程编号的生成是不支持检测重复的，编号生成之前可以调用这个接口检测是否重复；
  
  **支持基线：2501**

- 2.2参数说明
long markDefId -编号定义Id
long requestId -流程请求Id
int sequenceId -当前流水号

- 2.3响应说明
Boolean -true标识检测生成的编号会没有重复
-false 标识检测生成的编号会有重复

- 2.4示例代码

```
@Override
    public Map<String, Object> execute(CommandContext commandContext) {

        Map<String, Object> resultMap = new HashMap<>();
        // 编号定义Id
        markDefId = DataTransUtil.getLongValue(DataTransUtil.null2String(params.get("markDefId")));
        // 编号请求Id
        requestId = DataTransUtil.getLongValue(DataTransUtil.null2String(params.get("requestId")));
        // 需要检测校验的流水号Id
        sequenceId = DataTransUtil.getIntValue(DataTransUtil.null2String(params.get("sequenceId")));


        List<MarkRecordEntity> markRecordEntityList = markRecordDao.getMarkRecordByRequestMarkDefId(requestId, markDefId, user.getTenantKey());

        if(markRecordEntityList.size() <= 0) {
            return resultMap;
        }

        // 检测编号记录表是否记录了这个requestId的生成
        long count = markRecordEntityList.stream().filter(item -> item.getSequenceId() == sequenceId).count();
        if( count <= 0 ) {
            return resultMap;
        }


        // 根据时间降序排序
        markRecordEntityList.sort(Comparator.comparing(MarkRecordEntity::getCreateTime).reversed());
        if( markRecordEntityList.get(0).getSequenceId() == sequenceId ) {
            throw new ECException("当前的流水号已经被其他流程使用!");
        }

        return resultMap;
    }

```



# 3.调用标准产品生成编号的方法来生成编号,生成过程不做更新起始编号的处理，以及插入记录表的过程处理

- 3.1功能说明

标准流程编号生成方法可以生成预览效果的一个编号用于显示接下来要生成具体的编号信息；
                     
 **支持基线：2411**

- 3.2参数说明

MarkInfoEntity markInfoEntity - 编号生成定义对象
Map<String, MarkSdkFieldValueEntityDto> markSdkFieldValueEntityDtoMap - 编号生成表单字段值
Map<String,Object> otherParams -编号生成扩展参数

- 3.3响应说明

String -返回最终生成的编号字符串

- 3.4示例代码

```
public String generateMark(MarkInfoEntity markInfoEntity, Map<String, MarkSdkFieldValueEntityDto> markSdkFieldValueEntityDtoMap,Map<String,Object> otherParams) {
        try {
            user = markInfoEntity.getUser(); // 当前操作用户
            long creator = markInfoEntity.getCreator(); // 当前流程创建人

            long t1 = System.currentTimeMillis(); // 耗时计算
            logger.info("进入编号生成逻辑============szh"+(markInfoEntity.getMarkId()));

            // 从编号工厂获取方法
            String markModule = (String)otherParams.get("markModule");
            MarkSDKService markSDKService = markSDKServiceFactory.getMarkSDKServiceFromFactory(markModule);

            //1. 组装编号定义MarkDefEntity和编号组成MarkFieldEntity实体类信息
            MarkDefEntity markDefEntity = markSDKService.doComposeMarkDef(markInfoEntity,user,creator);
            logger.info("1. 组装MarkDefEntity实体类对象  耗时:"+(System.currentTimeMillis()-t1)+"=="+markDefEntity.getId());

            //2.根据表单字段的值计算得到下一流水号信息
            MarkSeqEntity markSeqEntity = markSDKService.getMarkSeqInfo(markInfoEntity, markDefEntity, markSdkFieldValueEntityDtoMap,markSDKService);
            logger.info("2.计算得到下一流水号信息  耗时:"+(System.currentTimeMillis()-t1)+"=="+markSeqEntity.getId());

            //3.根据markDefEntity和markSeqEntity计算编号field实体类里面showValue(除了表单字段外)
            markDefEntity = markSDKService.calculateFieldValue(markInfoEntity, markDefEntity,markSeqEntity,markSdkFieldValueEntityDtoMap);
            logger.info("3.根据markDefEntity和markSeqEntity计算编号field实体类里面showValue  耗时:"+(System.currentTimeMillis()-t1));

            //4.计算编号field实体类里面showValue(表单字段)---外部对接编号sdk的实现方法
            Map<String, MarkSdkFieldValueEntity> resultMap = markSDKService.dealMarkSdkFieldValue(markInfoEntity,markDefEntity,markSdkFieldValueEntityDtoMap);
            logger.info("4.封装处理markSdkFieldValueEntityMap的showValue字段信息  耗时:"+(System.currentTimeMillis()-t1));

            //5.生成具体的编号
            String markContent = commandExecutor.execute(new DoComposeMarkTemplateCmd(markDefEntity,user));
            logger.info("5.根据编号组成组装编号模板，并返回编号模板  耗时:"+(System.currentTimeMillis()-t1)+"=="+markContent);

            // 6.将编号插入到编号记录表
            markContent = commandExecutor.execute(new DoComposeMarkCmd(markInfoEntity,markSeqEntity,markDefEntity, markContent,MarkSourceTypeEnum.SYSTEM_MARK.getValue(),markSDKService,otherParams));
            logger.info("6.编号模板替换生成编号,插入到record表记录  耗时:"+(System.currentTimeMillis()-t1)+"=="+markContent);

            return markContent;

        } catch (Exception e) {
            logger.error("=====GenerateMarkServiceImpl======generateMark=========系统生成编号异常",e);
            throw new ECException("系统生成编号异常");
        }
    }

```
  
#  4.流程编号组成、起始编号、预留编号设置页面支持单页地址及编号设置权限支持复写二开口子
  
- 4.1功能说明

标准流程编号生成方法可以生成预览效果的一个编号用于显示接下来要生成具体的编号信息；
                     
 **支持基线：2511**

- 4.2使用说明
  11164365565472276481  表示的是workflowId 
1164365599596159858  表示的是markDefId
https://weapp.yunteams.cn/sp/workflow/mark/1164365565472276481/markField/1164365599596159858
https://weapp.yunteams.cn/sp/workflow/mark/1164365565472276481/markSeq/1164365599596159858
https://weapp.yunteams.cn/sp/workflow/mark/1164365565472276481/markReserved/1164365599596159858

![](${appMdRes}/1764124288-image.png)
  
  
![](${appMdRes}/1764124320-image.png)
  
  
![](${appMdRes}/1764124337-image.png)
  
- 4.3实例代码
  
  
```
  MarkHookEventType markHookEventType= MarkHookEventType.MARK_FIELD_SEQ_RESERVED_RIGHT;
                RegHookEventActuator regHookEventActuator=new RegHookEventActuator(ModuleUtil.getModuleName(),markHookEventType.getEventType());
                List<Object> args = new ArrayList<>();
                args.add(markObjId);
                args.add(user.getUserId());
                RegHookResult execute =regHookEventActuator.execute(args);
                Object result = execute.getResult();
                if(result!=null && result instanceof Boolean){
                    Boolean isReject = (Boolean)result;
                    markRight = isReject;
                }
  ```

#  5.流程预留编号支持占位符模糊搜索二开口子
  - 5.1功能说明

标准流程编号预留编号支持支持占位符快捷搜索；
  
   **支持基线：2512**
  
  - 5.2实例代码
  
 markReserve/getMarkReservedEditTable
  
这个请求的参数扩展:
  
"reservedWord": "测试",
  
"reservedWordPlaceholder": "%"
  
  
    
```
  //  预留编号预览 参数，根据预留编号参数进行模糊搜索，显示对应编号类型的可选预留编号
        if ((!StringUtils.isEmpty(params.get("reservedWord")) && !StringUtils.isEmpty(params.get("reservedWordPlaceholder")))) {
            // 预留编号支持高级搜索
            String reservedWord = DataTransUtil.null2String(params.get("reservedWord"));
            String reservedWordPlaceholder = DataTransUtil.null2String(params.get("reservedWordPlaceholder"));
            String[] splitReservedWordList = reservedWord.split(reservedWordPlaceholder);
            // for循环拼接OR条件
            for (int i = 0; i < splitReservedWordList.length; i++) {
                String reservedWordTemp = splitReservedWordList[i];
                if (i == 0) {
                    // 第一个条件直接like，无需加or（避免SQL开头出现OR）
                    queryWrapper.like("reserve.reservedMark", reservedWordTemp);
                } else {
                    // 从第二个条件开始，先调用or()再拼接条件
                    queryWrapper.or().like("reserve.reservedMark", reservedWordTemp);
                }
            }
        }
                                                                                
    
```
  
  
#  6.流程编号生成逻辑支持复写生成时机二开口子
- 6.1功能说明

标准流程编号生成方法可以生成预览效果的一个编号用于显示接下来要生成具体的编号信息；
  
   **支持基线：2512**
                                                                                
- 6.2实例代码                 
                                                                                
                                                                                
        
```
public List<Map<String, Object>> calculateMarkFieldTableMap(MarkDefEntity markDefEntity) {

        Long markDefId = markDefEntity.getId();
        List<MarkFieldEntity> markFieldEntityList = new ArrayList<>();
        if(markDefEntity.getRuleType() == 1) { // 如果旧数据是引用的公共编号数据
            Long publicMarkDefId = markDefEntity.getRealMarkDefId();
            markFieldEntityList = markFieldUtils.getMarkFieldEntityList(publicMarkDefId, workflowId, user);
        } else {
            markFieldEntityList = markFieldUtils.getMarkFieldEntityList(markDefId, workflowId, user);
        }

        List<Map<String, Object>> markFieldDataList =new ArrayList<>();
        if (markFieldEntityList.size() > 0) { //存在编号组成
            for (MarkFieldEntity markFieldEntity : markFieldEntityList) {
                HashMap<String, Object> markFieldData = new HashMap<>();
                markFieldData.put("id",markFieldEntity.getId()+"");
                String markFieldTypeName = SystemEnv.getHtmlLabelName(markFieldEntity.getMarkFieldType().getLabelid(), markFieldEntity.getMarkFieldType().getShowName());
                markFieldData.put("markFieldTypeName", markFieldTypeName);

                switch (markFieldEntity.getMarkFieldType()) {
                    case FORM_FIELD: {
                        MarkSdkFormFieldEntity markSdkRelateFieldInfo = markSdkService.getMarkSdkRelateFieldInfo(Long.parseLong(markFieldEntity.getRelateObjValue()), user);
                        if (markSdkRelateFieldInfo != null) {
                            // 分部类型，并且是上级分部
                            if(markFieldEntity.getSubComType() == MarkFormFieldSubComType.TOPSUBCOM.getValue() &&
                                    Collections.disjoint(markSdkRelateFieldInfo.getFieldTags().stream().map(WfFieldTypeTag::getTag).collect(Collectors.toList()), Arrays.asList("wf_sub_company")) == false) {
                                markFieldData.put("markRelateObjValue", markSdkRelateFieldInfo.getName());
                            } else {
                                markFieldData.put("markRelateObjValue", markSdkRelateFieldInfo.getName() + "");
                            }
                        }
                        break;
                    }
                    case DATE_YEAR:
                    case DATE_MONTH:
                    case DATE_DAY: {
                        if(!"-2".equals(markFieldEntity.getRelateObjValue())) {
                            MarkSdkFormFieldEntity markSdkRelateFieldInfo = markSdkService.getMarkSdkRelateFieldInfo(Long.parseLong(markFieldEntity.getRelateObjValue()), user);
                            if (markSdkRelateFieldInfo != null) {
                                // 分部类型，并且是上级分部
                                if(markFieldEntity.getSubComType() == MarkFormFieldSubComType.TOPSUBCOM.getValue() &&
                                        Collections.disjoint(markSdkRelateFieldInfo.getFieldTags().stream().map(WfFieldTypeTag::getTag).collect(Collectors.toList()), Arrays.asList("wf_sub_company")) == false) {
                                    markFieldData.put("markRelateObjValue", markSdkRelateFieldInfo.getName());
                                } else {
                                    markFieldData.put("markRelateObjValue", markSdkRelateFieldInfo.getName() + "");
                                }
                            }
                        } else {
                            if(markFieldEntity.getMarkFieldType().equals(MarkFieldTypeEnum.DATE_YEAR)) {
                                markFieldData.put("markRelateObjValue", SystemEnv.getHtmlLabelName(112,"当前年份"));
                            } else if(markFieldEntity.getMarkFieldType().equals(MarkFieldTypeEnum.DATE_MONTH)) {
                                markFieldData.put("markRelateObjValue", SystemEnv.getHtmlLabelName(58762,"当前月份"));
                            } else if(markFieldEntity.getMarkFieldType().equals(MarkFieldTypeEnum.DATE_DAY)) {
                                markFieldData.put("markRelateObjValue", SystemEnv.getHtmlLabelName(58761,"当前日期"));
                            }
                        }
                        break;
                    }

                    case FIX_CONTEXT: {
                        markFieldData.put("markRelateObjValue", markFieldEntity.getRelateObjValue());
                        break;
                    }

                    case SERIAL_NUMBER: {
                        markFieldData.put("markRelateObjValue", markFieldEntity.getRelateObjValue());
                        break;
                    }

                    case SYSTEM_FIELD: {
                        String systemShowName = "";
                        if(markFieldEntity.getRelateObjValue().equals(MarkSystemFieldTypeEnum.APPROVALPERSON.getValue()+"")) {
                            systemShowName = SystemEnv.getHtmlLabelName(62881,"审批发起人");
                        } else if(markFieldEntity.getRelateObjValue().equals(MarkSystemFieldTypeEnum.APPROVALDEPARTMENT.getValue()+"")) { // 所属部门
                            systemShowName = SystemEnv.getHtmlLabelName(79543,"发起人所属部门");
                        } else if (markFieldEntity.getRelateObjValue().equals(MarkSystemFieldTypeEnum.APPROVALSUBDEPARTMENT.getValue()+"")){
                            systemShowName = SystemEnv.getHtmlLabelName(1917,"发起人分部");
                        } else if (markFieldEntity.getRelateObjValue().equals(MarkSystemFieldTypeEnum.APPROVALSPURIORSUBDEPARTMENT.getValue()+"")) {
                            systemShowName = SystemEnv.getHtmlLabelName(58787,"发起人上级分部");
                        }
                        markFieldData.put("markRelateObjValue", systemShowName);
                        break;
                    }

                    case FIELD_VARIABLE: {
                        markFieldData.put("markFieldTypeName", markFieldEntity.getVariableName());
                        markFieldData.put("markRelateObjValue",VariableTypeEnum.getNameByRelateObjValue(markFieldEntity.getRelateObjValue()));
                        break;

                    }

                    default: {
                        markFieldData.put("markRelateObjValue", "");
                        break;
                    }
                }
                markFieldDataList.add(markFieldData);
            }
        }
        return markFieldDataList;
    }
                                                                                    
```
                                                                                
      
  
        
  
#  7.流程编号支持生成预览编号二开接口
- 7.1功能说明

自定义按钮流转指定节点，经过出口生成编号；
  
   **支持基线：2512**
                                                                                
    
                                                                                
- 7.2实例代码      
  
@Value("${weaver.workflow.mark.markGenerateLinkSwitch:0}")
private String markGenerateLinkSwitch;
                                                                                
                                                                                
        
```
                if(!StringUtils.isEmpty(markGenerateLinkSwitch) && "1".equalsIgnoreCase(markGenerateLinkSwitch)) {
                    try {
                        logger.info("====MarkHookEventType.MARK_GENERATE_LINK" + runtimeData.getRequestBaseInfo().getRequestId()+ "====start==="+ LocalDateTime.now());
                        MarkHookEventType markHookEventType= MarkHookEventType.MARK_GENERATE_LINK;
                        RegHookEventActuator regHookEventActuator=new RegHookEventActuator(ModuleUtil.getModuleName(),markHookEventType.getEventType());
                        List<Object> args = new ArrayList<>();
                        args.add(runtimeData.getRequestBaseInfo()); // 当前流程的基础信息
                        args.add(runtimeData.getUserCurrentNode().getId()); // 当前节点

                        // 自定义按钮流转至指定节点 或者 指定流转
                        List<Long> nextLinkIdList = new ArrayList<>();
                        if(runtimeData.getRequestParamData().getSubmitToNodeId() > 0) {
                            nextLinkIdList.add(runtimeData.getRequestParamData().getSubmitToNodeId());
                        }
                        List<RequestSelectFlowDataEntity> selectNextFlowData = runtimeData.getRequestParamData().getSelectFlowParam().getSelectNextFlowData();
                        if(selectNextFlowData != null && selectNextFlowData.size() > 0) {
                            nextLinkIdList.addAll(selectNextFlowData.stream().map(RequestSelectFlowDataEntity::getNodeId).collect(Collectors.toList()));
                        }
                        args.add(nextLinkIdList);

                        RegHookResult execute = regHookEventActuator.execute(args);
                        Object result = execute.getResult();
                        if(result!=null && result instanceof List){
                            List<Long> nextRegHook = (List<Long>)result;
                            nextNodeLinkIdList.addAll(nextRegHook);
                        }
                        logger.info("====MarkHookEventType.MARK_GENERATE_LINK" + runtimeData.getRequestBaseInfo().getRequestId()+ "===end===="+ LocalDateTime.now());
                    } catch (Exception exception) {
                        logger.error("=======MarkHookEventType.MARK_GENERATE_LINK===:{}",exception);
                    }
                                                                                    
```
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                