# 基础开发文档-编码规则API

本页主要介绍编码规则的API方法，开发者可以调用这些接口获取到相应编码规则的信息，生成新的编码，导出编码规则等。

如果您有什么建议，欢迎在[金蝶社区](https://club.kdcloud.com/)留言反馈，我们会根据您的需求进行优化。







## 快速开始

快速开始意在通过一个小 demo, 教会读者自定义自己的编码插件.

注意, 此 demo 仅针对编码规则类型为 "新增" 的情况, 若是其他类型, 需要在代码中进行额外逻辑判断.



### 准备工作

1. 创建一张表单

![image-20200521190415677](./image/image-20200521190415677.png)

2. 启用 "业务控制" 中的编码规则

![image-20200521190613185](./image/image-20200521190613185.png)

3. 关闭表单的自带插件, 使用自定义插件

![image-20200521190810495](./image/image-20200521190810495.png)

4. 关闭 "保存类型" 操作与 "提交类型" 操作绑定的插件, 使用自定义插件

![image-20200521191350011](./image/image-20200521191350011.png)

5. 在编码规则列表页, 针对刚刚创建的表单新增编码规则

![image-20200521191653895](./image/image-20200521191653895.png)



### 开始编写代码

刚刚创建了两个自定义类, 分别实现自己的表单插件与操作插件. 

接下来会在这两个类中, 通过调用编码服务API, 完成表单的编号自生成.



**CustomtomCodeRulePlugin 表单插件**

作用: 在点击新增打开一张单据时, 赋予单据编号字段一个编号

```java
public class CustomCodeRulePlugin extends AbstractBasePlugIn {

    /**
     * 给 billno 单据编号字段赋予生成的编号
     * 此处生成的编号用于界面上展示给用户
     * @param e
     */
    @Override
    public void afterCreateNewData(EventObject e) {
        String entityId = this.getModel().getDataEntityType().getName();
        DynamicObject dataInfo = this.getModel().getDataEntity();
        String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

        String readNumber = CodeRuleServiceHelper.readNumber(entityId, dataInfo, orgId);
        this.getModel().setValue("billno", readNumber);
    }
  
  	@Override
    public void afterBindData(EventObject e) {
        super.afterBindData(e);
        // 锁定单据编号控件, 防止人为输入
        this.getView().setEnable(Boolean.FALSE, "billno");
    }
}
```



**CustomCodeRuleOp 操作插件**

作用: 保存或提交操作触发时, 消耗真实流水号, 将生成的 "唯一" 编号返回给实体

```java
public class CustomCodeRuleOp extends AbstractOperationServicePlugIn {

    @Override
    public void onAddValidators(AddValidatorsEventArgs e) {
        DynamicObject[] dynamicObjects = e.getDataEntities();
        DynamicObject dataInfo = dynamicObjects[0];

        String entityId = dataInfo.getDataEntityType().getName();
        String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

        String newNumber = CodeRuleServiceHelper.getNumber(entityId, dataInfo, orgId);
        dataInfo.set("billno", newNumber);
    }
}
```



**生成编号成功!**

![image-20200521194919839](/Users/Tulane/Library/Application Support/typora-user-images/image-20200521194919839.png)















## API概要

| 方法                                                         | 说明                                 |
| ------------------------------------------------------------ | ------------------------------------ |
| static CodeRuleInfo getCodeRule(String entityId, DynamicObject dataInfo, String orgID); | 获取编码规则 (根据实体)              |
| static String readNumber(String entityId, DynamicObject dataInfo, String orgId); | 读取编号                             |
| static String readNumber(CodeRuleInfo codeRuleInfo, DynamicObject dataInfo); | 读取编号                             |
| static String getNumber(String entityId, DynamicObject dataInfo, String sorgId); | 生成编号（会消耗流水号或者消耗断号） |
| static String getNumber(CodeRuleInfo codeRuleInfo, DynamicObject dataInfo); | 生成编号（会消耗流水号或者消耗断号） |
| static String[] getBatchNumber(String entityId, DynamicObject dataInfo, String orgID, int count); | 批量生成编号                         |
| static boolean recycleNumber(String entityId, DynamicObject dataInfo, String orgID, String number); | 回收指定的编号                       |
| static boolean[] recycleBatchNumber(String entityId, DynamicObject[] dataInfo, String orgID, String[] numbers): | 批量回收指定的编号                   |
| static List<CoderuleFile> exportCodeRuleByEntityID(List<String> entityIDList); | 导出编码规则 (根据实体标识id)        |
| static List<CoderuleFile> exportCodeRuleByAppID(List<String> appIDList); | 导出编码规则 (根据应用id)            |
| static List<CoderuleFile> exportCodeRuleByCoderuleID(List<String> coderuleIDList); | 导出编码规则 (根据编码id)            |











## API详情



### 获取编码规则 (根据实体)

**接口名：getCodeRuleByEntityID**

> 这个接口会根据实体编码获取可用的编码规则（可能有多个，因为支持不同受控组织，不同适用条件下使用不同的规则）

**参数说明**

| 参数名称 | 数据类型 | 必选 | 说明     |
| -------- | -------- | ---- | -------- |
| entityId | String   | 是   | 实体编码 |

**返回值**

| 参数名称 | 数据类型     | 说明                       |
| -------- | ------------ | -------------------------- |
| codeRule | CodeRuleInfo | 记录编码规则详细信息的对象 |

**代码示例**

```java
String entityId = this.getModel().getDataEntityType().getName();
DynamicObject dataInfo = this.getModel().getDataEntity();
String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(entityId, dataInfo, orgId);
```



### 读取编号

**接口名：readNumber**

> 这个接口会根据指定实体，实体对象信息，受控组织ID，生成合适的编号。调用后最大号不变，断号记录也不会被消耗。

**参数说明**

| 参数名称  | 数据类型      | 必选 | 说明                                                       |
| --------- | ------------- | ---- | ---------------------------------------------------------- |
| entityNum | String        | 是   | 指定实体编码                                               |
| dataInfo  | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |
| orgId     | String        | 是   | 指定受控组织ID，如果没有配置受控组织，传null即可           |

**返回值**

| 参数名称   | 数据类型     | 说明       |
| ---------- | ------------ | ---------- |
| readNumber | CodeRuleInfo | 当前的编号 |

**代码示例**

```java
String entityId = this.getModel().getDataEntityType().getName();
DynamicObject dataInfo = this.getModel().getDataEntity();
String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

String readNumber = CodeRuleServiceHelper.readNumber(entityId, dataInfo, orgId);
```



### 读取编号 (重载)

**接口名：readNumber**

> 这个接口会根据指定实体，实体对象信息，受控组织ID，生成合适的编号。调用后最大号不变，断号记录也不会被消耗。

**参数说明**

| 参数名称 | 数据类型      | 必选 | 说明                                                       |
| -------- | ------------- | ---- | ---------------------------------------------------------- |
| codeRule | CodeRuleInfo  | 是   | 记录编码规则详细信息的对象                                 |
| dataInfo | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |

**返回值**

| 参数名称   | 数据类型 | 说明       |
| ---------- | -------- | ---------- |
| readNumber | String   | 当前的编号 |

**代码示例**

```java
String entityId = this.getModel().getDataEntityType().getName();
DynamicObject dataInfo = this.getModel().getDataEntity();
String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(entityId, dataInfo, orgId);
String readNumber = CodeRuleServiceHelper.readNumber(codeRule, dataInfo);
```





### 生成编号（会消耗流水号或者消耗断号）

**接口名：getNumber**

> 这个接口会根据指定实体，实体对象信息，受控组织ID，生成合适的编号。
>
> 调用后会导致最大号加1，或者导致断号记录被消耗（当前编码规则的应用规则为“不允许断号”时，才会消耗断号表的记录）

**参数说明**

| 参数名称 | 数据类型      | 必选 | 说明                                                       |
| -------- | ------------- | ---- | ---------------------------------------------------------- |
| entityId | String        | 是   | 实体编码                                                   |
| dataInfo | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |
| orgId    | String        | 是   | 指定受控组织ID，如果没有配置受控组织，传null即可           |

**返回值**

| 参数名称  | 数据类型 | 说明                              |
| --------- | -------- | --------------------------------- |
| newNumber | String   | 当前的编号 (数据库中已消耗此编号) |

**代码示例**

```java
String entityId = this.getModel().getDataEntityType().getName();
DynamicObject dataInfo = this.getModel().getDataEntity();
String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

String newNumber = CodeRuleServiceHelper.getNumber(entityId, dataInfo, orgId);
```





### 生成编号（重载）

**接口名：getNumber**

> 这个接口会根据指定实体，实体对象信息，受控组织ID，生成合适的编号。
>
> 调用后会导致最大号加1，或者导致断号记录被消耗（当前编码规则的应用规则为“不允许断号”时，才会消耗断号表的记录）

**参数说明**

| 参数名称 | 数据类型      | 必选 | 说明                                                       |
| -------- | ------------- | ---- | ---------------------------------------------------------- |
| codeRule | CodeRuleInfo  | 是   | 记录编码规则详细信息的对象                                 |
| dataInfo | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |

**返回值**

| 参数名称  | 数据类型 | 说明                              |
| --------- | -------- | --------------------------------- |
| newNumber | String   | 当前的编号 (数据库中已消耗此编号) |

**代码示例**

```java
String entityId = this.getModel().getDataEntityType().getName();
DynamicObject dataInfo = this.getModel().getDataEntity();
String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(entityId, dataInfo, orgId);
String newNumber = CodeRuleServiceHelper.getNumber(codeRule, dataInfo);
```





### 批量生成编号（一次生成多个编号，会增大最大号记录或者消耗断号记录）

**接口名：getBatchNumber**

> 这个接口会一次批量生成指定数量的编号，如果当前编码规则的应用规则是“不允许断号”，会优先消耗断号记录，然后再增加最大号记录；否则只增加最大号。

**参数说明**

| 参数名称  | 数据类型      | 必选 | 说明                                                       |
| --------- | ------------- | ---- | ---------------------------------------------------------- |
| entityNum | String        | 是   | 指定实体编码                                               |
| dataInfo  | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |
| orgId     | String        | 是   | 指定受控组织ID，如果没有配置受控组织，传null即可           |
| count     | int           | 是   | 指定调用当次生成的编号数量                                 |

**返回值**

| 参数名称  | 数据类型 | 说明                           |
| --------- | -------- | ------------------------------ |
| arrNewNum | String[] | 每一个元素就是一个新生成的编号 |

**代码示例**

```java
String entityId= ...;  
List<DynamicObject> dataInfos = ...;
String orgId = ...;   // 编码规则中不启用"适用组织", 则无需获取当前组织
int count = dataInfos.size();

String[] arrNewNum = CodeRuleServiceHelper.getBatchNumber(entityId, dataInfo, orgId, count);
```





### 回收指定的编号

**接口名：recycleNumber**

> 这个接口会根据指定的条件下（指定实体，实体对象信息，受控组织ID），回收指定的编号。回收编号会记录到断号表中，以便下次生成编号时优先消耗回收的编号。

**参数说明**

| 参数名称  | 数据类型      | 必选 | 说明                                                       |
| --------- | ------------- | ---- | ---------------------------------------------------------- |
| entityNum | String        | 是   | 指定实体编码                                               |
| dataInfo  | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |
| orgId     | String        | 否   | 指定受控组织ID，如果没有配置受控组织，传null即可           |
| number    | String        | 是   | 指定回收的编号                                             |

**返回值**

| 参数名称 | 数据类型 | 说明                              |
| -------- | -------- | --------------------------------- |
| flag     | String[] | 回收成功，返回true，否则为false。 |

**代码示例**

```java
String entityId = this.getModel().getDataEntityType().getName();
DynamicObject dataInfo = this.getModel().getDataEntity();
String orgId = null; // 编码规则中不启用"适用组织", 则无需获取当前组织

String serialNumber = ...; // 需要回收的编号
boolean flag = CodeRuleServiceHelper.recycleNumber(entityId, dataInfo, orgId, serialNumber);
```





### 批量回收指定的编号

**接口名：recycleBatchNumber**

> 回收指定编号的批量版

**参数说明**

| 参数名称  | 数据类型      | 必选 | 说明                                                       |
| --------- | ------------- | ---- | ---------------------------------------------------------- |
| entityNum | String        | 是   | 指定实体编码                                               |
| dataInfo  | DynamicObject | 是   | 需要生成编码的实体对象（对象上需要具有生成编码的所需信息） |
| orgId     | String        | 否   | 指定受控组织ID，如果没有配置受控组织，传null即可           |
| number    | String        | 是   | 指定回收的编号                                             |

**返回值**

| 参数名称 | 数据类型  | 说明                              |
| -------- | --------- | --------------------------------- |
| flags    | boolean[] | 回收成功，返回true，否则为false。 |

**代码示例**

```java
String entityId= ...;  
DynamicObject[] dataInfos = ...;
String orgId = ...;   // 编码规则中不启用"适用组织", 则无需获取当前组织
String[] serialNumbers = ...;

boolean[] flags = CodeRuleServiceHelper.recycleBatchNumber(entityId, dataInfos, orgId, serialNumbers);
```





### 导出编码规则 (根据实体标识id)

**接口名：exportCodeRuleByEntityID**

> 这个接口会根据指定实体列表，导出编码规则的信息。

**参数说明**

| 参数名称     | 数据类型     | 必选 | 说明                               |
| ------------ | ------------ | ---- | ---------------------------------- |
| entityIDList | List<String> | 是   | 指定的要导出编码规则的实体编码列表 |

**返回值**

| 参数名称      | 数据类型           | 说明                                                       |
| ------------- | ------------------ | ---------------------------------------------------------- |
| codeRuleFiles | List<CoderuleFile> | 每一个CoderuleFile对象中有sql脚本，即用于预置编码规则的SQL |

**代码示例**

```java
List<String> entityIDList = ...;

List<CoderuleFile> codeRuleFiles = CodeRuleServiceHelper.exportCodeRuleByEntityID(entityIDList);
```





### 导出编码规则 (根据应用id)

**接口名：exportCodeRuleByAppID**

> 这个接口会根据指定应用ID列表，导出编码规则的信息。

**参数说明**

| 参数名称  | 数据类型     | 必选 | 说明                             |
| --------- | ------------ | ---- | -------------------------------- |
| appIdList | List<String> | 是   | 指定的要导出编码规则的应用ID列表 |

**返回值**

| 参数名称      | 数据类型           | 说明                                                       |
| ------------- | ------------------ | ---------------------------------------------------------- |
| codeRuleFiles | List<CoderuleFile> | 每一个CoderuleFile对象中有sql脚本，即用于预置编码规则的SQL |

**代码示例**

```java
List<String> appIdList = ...;

List<CoderuleFile> codeRuleFiles = CodeRuleServiceHelper.exportCodeRuleByAppID(appIdList);
```





### 导出编码规则 (根据编码id)

**接口名：exportCodeRuleByCoderuleID**

> 这个接口会根据指定编码规则ID列表，导出编码规则的信息。

**参数说明**

| 参数名称       | 数据类型     | 必选 | 说明                         |
| -------------- | ------------ | ---- | ---------------------------- |
| coderuleIdList | List<String> | 是   | 指定的要导出编码规则的ID列表 |

**返回值**

| 参数名称      | 数据类型           | 说明                                                       |
| ------------- | ------------------ | ---------------------------------------------------------- |
| codeRuleFiles | List<CoderuleFile> | 每一个CoderuleFile对象中有sql脚本，即用于预置编码规则的SQL |

**代码示例**

```java
List<String> coderuleIdList = ...;

List<CoderuleFile> codeRuleFiles = CodeRuleServiceHelper.exportCodeRuleByCoderuleID(coderuleIdList);
```











## 使用案例

正常情况下, 只需使用自带的表单插件与操作插件, 即可满足绝大多数场景需求. 但有些特殊场景, 仍需要自己开发自定义插件, 满足这些场景的编号生成.

本栏目会不断收纳业务自开发的编码规则插件的用例, 通过场景分享, 提供给读者参考.



### 场景一: 单据生成方式的多样性

*分享人: 兰婷 (深圳应用开发部)*



**场景分析**

* 单据的来源方式有两种: 表单列表本身的新增, 以及表单的转换生成
* 新增方式生成的表单需要使用配置的编码规则生成编号
* 其他表单转换生成的方式, 不需要使用编码规则, 直接继承表单原有编号

**特殊点**: 仅使用原生插件, 两种方式都会由编码规则生成编号

**解决方案**: 编写自定义插件, 判断两种生成方式, 并选择性的使用编码规则







(持续更新...)









## 后期规划

本栏目会介绍编码规则后期的规划方向.



1. 改进批量生成编号的性能问题, 现阶段的生成速率表现一般.
2. 提供更完善的批量生成编号接口, 现阶段的批量接口不能满足一部分编码规则, 例如: 生成的编号中含有表单上的"业务字段".
3. 提供集成了基础编码规则功能的插件基类, 供业务开发时更简单化. 







## 意见反馈

欢迎发送邮件反馈使用中遇到的问题, 及提供宝贵的建议. 

邮箱地址: tulane_zhu@kingdee.com