基于业务场景插件扩展方案介绍
# 1.整体介绍
本方案分别从金蝶云标准产品的业务开发人员、二次开发人员两种角色,说明如何实现基于业务场景的插件扩展。
![image.webp](/download/010099155fc9135e451992024697cf0c4d4e.webp)
- **业务开发人员**:由业务领域在开发业务功能时,确定好允许二开扩展的业务场景,为此封装出扩展接口,注册到平台的业务扩展场景列表中;
- **二次开发人员**:实现接口,完成插件开发,绑定到相应业务场景上。
其中业务扩展场景与业务插件之间的关系。如图
![image.webp](/download/0100543dfb848e644e74b5d443f652000bd7.webp)
## 1.1 系统路径
V5.0以下:【系统服务云】-【配置工具】-【业务扩展】
V6.0及以上:【开发服务云】-【扩展平台】-【业务扩展点】
# 2.金蝶云标准产品业务开发:确定业务扩展场景
本章适用于金蝶云标准产品各业务领域开发人员及行业产品开发伙伴,确定业务扩展场景主要分为三步骤:
## 第一步:封装业务扩展插件接口
### 操作步骤
标准产品业务功能开发时,需要提前考虑,此业务场景是否允许二开扩展?或者是否已经有客户现场反馈要对此场景作扩展?
1. 对允许扩展的业务场景,需要对实现逻辑进行整理、细分,寻找一个或多个允许二开扩展的时机点
2. 封装成事件方法,并定义此事件方法需要的参数
3. 将所有事件方法组合起来,定义出扩展插件接口,如图
![image.webp](/download/0100803a879b4e3b4bb7a9f08520a99f036c.webp)
### 开发指南
接口、事件方法、事件参数,使用Java代码定义,无其他特别要求。
#### 案例说明
将某界面上某按钮的点击处理,开放出来供二次开发扩展,为此封装了一个插件接口;并根据扩展需要,封装了如下几个事件方法(本例列举的事件方法仅供演示,无实际用途,实际插件需自行定义事件方法):
- beforeXXX事件:预置逻辑前执行,允许插件调整参数;
- doXXX事件:预置逻辑执行,允许插件重写,替代预置逻辑;
- afterXXX事件:预置逻辑后执行,允许插件同步处理其他数据;
#### 示例代码
```language
package kd.bos.bizextplugin.sample;
import kd.bos.bizextplugin.sample.args.XXXAfterArgs;
import kd.bos.bizextplugin.sample.args.XXXBeforeArgs;
import kd.bos.bizextplugin.sample.args.XXXDoingArgs;
/**
* 业务扩展插件,接口定义
*/
public interface IXXXSubPlugin {
/**
* 插件事件方法:业务逻辑执行前触发,提前准备参数,检查数据
* @param args
*/
default void beforeXXX(XXXBeforeArgs args) {}
/**
* 插件事件方法:业务逻辑执行时触发,重写此方法,可改写业务实现逻辑
*/
default void doXXX(XXXDoingArgs args) {}
/**
* 插件事件方法:业务逻辑执行完后触发,同步处理其他数据
*/
default void afterXXX(XXXAfterArgs args) {}
}
```
## 第二步:注册业务扩展场景
通过平台提供的业务扩展场景列表,注册允许扩展的业务场景。注册完毕后点击列表引出菜单,生成预插元数据文件,提交svn或git代码库,随产品发布。
### 操作步骤
1. 访问路径:系统服务云-配置工具-业务扩展-业务扩展场景列表
2. 在业务扩展场景列表中,点击【新增】按钮
3. 填写配置详情页内容,包括业务场景编码、业务场景名称、业务扩展插件接口长类名等信息,详情查看图3-2
4. 提交并审核,界面锁定,不允许改动
5. 点击列表【引出】按钮,生成元数据压缩文件。解压后把业务扩展场景元数据文件提交到svn或git代码库-datamodel-应用的metadata子目录下。
![image.webp](/download/0100c63286cbf9244ea79844deb96a9abc33.webp)
### 界面元素说明
- **业务场景编码**:业务场景全局唯一编码:实际运行时,平台根据业务场景编码搜索绑定的插件;如果编码有重复,就可能会取到无关的插件,导致业务逻辑错误。
> 编码格式建议一:
[云][.应用][.业务对象][.控件][.功能说明]
如:demo.dev.demo_bizextpluginsample.buttonap.click
编码格式建议二:
[业务组件][.功能说明]
如:kd.bos.bizextplugin.sample.XXXBizFormPlugin.myButtonClick”
- **业务场景名称**:填写中文的场景名称,以便二次开发理解此业务场景的主要用途
- **扩展接口**:填写插件接口长类名
- **开发商**:自动填写,读取当前开发商标识
- **所属应用**:业务场景所属的应用
- **所属业务对象**:业务场境所属的业务对象(指表单、单据、基础资料等),如果和业务对象无关,可留空不填
- **扩展接口说明**:插件接口说明,包括事件方法说明及参数定义、示例代码,以便二开能够理解插件各事件方法的用途,确定方案;支持md格式,需描述清晰美观,如图
![image.webp](/download/0100c9ede7c537b74e2694b3b9c356316a7e.webp)
## 第三步:修改业务代码,触发业务场景扩展插件
在业务场景的代码中,构造插件代理对象,传入当前业务场景编码,由平台自动加载并创建绑定的扩展插件。在允许扩展的时机点,准备好事件参数对象,调用插件代理对象的方法,触发插件事件,如图
![image.webp](/download/01001c95eb06fbec4a41978de70aa0a2396a.webp)
平台提供两种模式触发插件事件,效果有差异,需业务场景根据需要进行选择,如图
![image.webp](/download/01007ca2b88ef4634ec9a6af81f4e52160c5.webp)
- **扩展模式**:对标准业务逻辑进行补充,在内置逻辑前或后执行,通常用于准备参数、同步关联数据,对结果再加工等;
- **覆盖模式**:替换标准业务逻辑;
### 开发指南
**示例1**:扩展模式-在默认实现前调用
```language
// 模式1.扩展: callBefore :在调用默认实现前,先调用扩展插件的同名方法(扩展插件、默认实现,二者依次执行)
pluginProxy.callBefore(p ->{p.beforeXXX(beforgArgs); return null;});
```
**示例2**:扩展模式-在默认实现后调用
```language
// 模式1.扩展: callAfter:在调用默认实现后,再调用扩展插件的同名方法(默认实现、扩展插件,二者都执行)
pluginProxy.callAfter(p ->{ p.afterXXX(afterArgs); return null;});
```
**示例3**:覆盖模式-如果无扩展则不执行
```// 模式2.替换: callReplace, 如果有扩展,则调用扩展;没扩展则不执行(仅执行扩展插件)
pluginProxy.callReplace(p ->{ p.doXXX(doingArgs); return null;});
```
**示例4**:覆盖模式-如果无扩展则执行默认逻辑
```language
// 模式2.替换: callReplaceIfPresent,如果存在扩展则替换,否则调用默认实现。(扩展插件和默认实现,二选一)
pluginProxy.callReplaceIfPresent(p -> {p.doXXX(doingArgs); return null;});
```
**示例5**:完整的触发业务扩展插件代码
```language
List<String> execLogs = new ArrayList<String>(10); // 输出日志集合
// 初始化业务扩展插件事件参数(把次要代码放在前面,避免影响主题代码的阅读)
XXXBeforeArgs beforgArgs = new XXXBeforeArgs();
beforgArgs.setExecLogs(execLogs);
XXXDoingArgs doingArgs = new XXXDoingArgs();
doingArgs.setExecLogs(execLogs);
XXXAfterArgs afterArgs = new XXXAfterArgs();
afterArgs.setExecLogs(execLogs);
IXXXSubPlugin defXXXSubPlugin = new DefXXXSubPlugin(); // 构建插件默认实现类
// 初始化插件管理器,参数:(1.默认实现类 2.插件接口,3.业务场景编码(全局唯一),4.插件过滤条件)
PluginProxy<IXXXSubPlugin> pluginProxy = PluginProxy.create(defXXXSubPlugin, IXXXSubPlugin.class,
"kd.bos.bizextplugin.sample.XXXBizFormPlugin.myButtonClick", null);
// 模式1.扩展: callBefore :在调用默认实现前,先调用扩展插件的同名方法(扩展插件、默认实现,二者依次执行)
execLogs.add("调用callBefore :扩展插件、默认实现,二者依次执行");
pluginProxy.callBefore(p ->{p.beforeXXX(beforgArgs); return null;});
// 模式2.替换: callReplace, 如果有扩展,则调用扩展;没扩展则不执行(仅执行扩展插件)
execLogs.add("\r\n调用callReplace: 仅执行扩展插件");
pluginProxy.callReplace(p ->{ p.doXXX(doingArgs); return null;});
// 模式2.替换: callReplaceIfPresent,如果存在扩展则替换,否则调用默认实现。(扩展插件和默认实现,二选一)
execLogs.add("\r\n调用callReplaceIfPresent: 优先执行扩展插件,无扩展则执行默认实现");
pluginProxy.callReplaceIfPresent(p -> {p.doXXX(doingArgs); return null;});
// 模式1.扩展: callAfter:在调用默认实现后,再调用扩展插件的同名方法(默认实现、扩展插件,二者都执行)
execLogs.add("\r\n调用callAfter: 默认实现,扩展插件,依次执行"); // 输出执行日志
pluginProxy.callAfter(p ->{ p.afterXXX(afterArgs); return null;});
// 把执行过程生成的日志,显示在界面上
String ret = StringUtils.join(execLogs.toArray(), " \r\n");
this.getModel().setValue("execlogs", ret);
```
# 3.客户现场二次开发:实现业务扩展插件
本章适用客户现场二次开发人员阅读,对于业务场景进行扩展,整体分为三步骤:
## 第一步:查找业务扩展场景
1. 访问路径:++系统服务云 – 配置工具 – 业务扩展 – 业务扩展场景列表++
2. 按应用、业务对象搜索业务场景,确认需扩展的业务场景是否在列表中,如图:
a.如果不存在,可提单反馈要求开放
b.找到业务场景后,获取对应的扩展插件接口,仔细阅读扩展说明,按扩展说明,实现扩展插件接口,开发插件。
![image.webp](/download/0100b561e09f0f1d4fd3b3c97cfbe8194936.webp)
## 第二步:开发插件
参考业务场景扩展说明,实现扩展插件接口,开发插件。
### 示例
业务扩展场景,扩展接口长类名为 “kd.bos.bizextplugin.sample.IXXXSubPlugin”,如图,该接口定义了三个方法,二开可以根据需要重写其中一个或几个方法,从而实现业务扩展,如图
![image.webp](/download/0100282c8127d9bc4bfea25c417fa2a86d33.webp)
![image.webp](/download/0100931dd22f3bee4f5a80ec54d006c3d534.webp)
## 第三步:绑定插件
### 操作步骤
1. 插件开发完成后,点击业务扩展场景列表的”绑定插件”菜单,如图4-4
2. 跳转至配置详情页,点击【增行】,将现场二开的插件,绑定到业务场景上,填写基本信息,具体权限如图
3. 绑定完成后,平台会根据业务场景编码,读取到绑定的插件,调用其方法,执行扩展逻辑。若业务场景绑定了多个插件,会按照顺序依次触发各插件的相同事件。
![image.webp](/download/010072627135e1844fcc9fd4ddda86f4a25d.webp)
![image.webp](/download/0100d0387d718f254c5fa839e986939a960d.webp)
### 界面元素说明
- **启用**:插件是否启用,默认关闭
- **插件**:填写插件类名,
- **插件说明**:填写此插件说明,以便读者理解此插件的主要用途
- **系统禁用**:系统升级预留字段,开发商无法操作;表示当前插件,系统升级后标记为禁用
- **开发商**:自动填充
- **创建人**:自动填充
- **创建时间**:自动填充
- **修改人**:自动填充
- **修改时间**:自动填充
- **版本**:版本号预留字段,为支持插件灰度发布
# 附录:示例代码
本章附上完整的示例代码,为方便不同的读者阅读,把示例代码分为两个部分:
- 供标准产品业务开发人员阅读的示例,演示在业务场景中如何植入业务扩展点;详情查看- 业务开发
- 供二次开发人员阅读的示例,演示如何开发插件;详情查看- 二次开发
## 金蝶云标准产品业务开发-代码示例
金蝶云标准产品业务开发,为业务场景提供业务扩展点,封装业务扩展插件接口、并提供业务场景的默认实现逻辑。
### 业务场景-1.插件接口定义
为业务场景的扩展,定义专门的接口,包含事件方法及事件参数。
如下接口示例定义了三个事件方法:beforeXXX, doXXX, afterXXX,分别象征业务逻辑执行前、中、后三个时机点,允许二开干预。
```language
package kd.bos.bizextplugin.sample;
import kd.bos.bizextplugin.sample.args.XXXAfterArgs;
import kd.bos.bizextplugin.sample.args.XXXBeforeArgs;
import kd.bos.bizextplugin.sample.args.XXXDoingArgs;
/**
* 业务扩展插件,接口定义
*/
public interface IXXXSubPlugin {
/**
* 插件事件方法:业务逻辑执行前触发,提前准备参数,检查数据
* @param args
*/
default void beforeXXX(XXXBeforeArgs args) {}
/**
* 插件事件方法:业务逻辑执行时触发,重写此方法,可改写业务实现逻辑
*/
default void doXXX(XXXDoingArgs args) {}
/**
* 插件事件方法:业务逻辑执行完后触发,同步处理其他数据
*/
default void afterXXX(XXXAfterArgs args) {}
}
```
### 业务场景-2.默认逻辑实现
业务扩展插件接口定义好之后,接口可以仅供二开实现,标准业务不提供默认实现。
但如果需要支持覆盖模式, 标准业务则实现提供默认实现,把允许覆盖的逻辑,放在默认实现中:
- 如果二开未扩展,则执行默认实现逻辑;
- 如果二开作了扩展,则执行二开扩展逻辑;
如下示例代码,是针对插件接口的默认实现类。示例逻辑是在执行时输出日志,以便回顾逻辑执行过程。
```language
package kd.bos.bizextplugin.sample;
import kd.bos.bizextplugin.sample.args.XXXAfterArgs;
import kd.bos.bizextplugin.sample.args.XXXBeforeArgs;
import kd.bos.bizextplugin.sample.args.XXXDoingArgs;
public class DefXXXSubPlugin implements IXXXSubPlugin {
public void beforeXXX(XXXBeforeArgs args) {
String ret = getClass().getName() + ".beforeXXX()";
args.getExecLogs().add(ret);
System.out.println(ret);
}
/**
* 插件事件方法:业务逻辑执行时触发,重写此方法,可改写业务实现逻辑
*/
public void doXXX(XXXDoingArgs args) {
String ret = getClass().getName() + ".doXXX()";
args.getExecLogs().add(ret);
System.out.println(ret);
}
/**
* 插件事件方法:业务逻辑执行完后触发,同步处理其他数据
*/
public void afterXXX(XXXAfterArgs args) {
String ret = getClass().getName() + ".afterXXX()";
args.getExecLogs().add(ret);
System.out.println(ret);
}
}
```
### 业务场景-3.触发插件事件
在业务场景的代码中,初始化插件代理对象,触发插件事件,执行插件的扩展逻辑。
如下示例,是一个表单插件,捕获了按钮点击事件,在按钮点击处理逻辑中,触发前述示例提供的业务扩展插件。
```language
package kd.bos.bizextplugin.sample;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import kd.bos.dataentity.utils.StringUtils;
import kd.bos.form.control.Button;
import kd.bos.form.control.Control;
import kd.bos.form.plugin.AbstractFormPlugin;
import kd.bos.form.plugin.PluginProxy;
import kd.bos.bizextplugin.sample.args.XXXAfterArgs;
import kd.bos.bizextplugin.sample.args.XXXBeforeArgs;
import kd.bos.bizextplugin.sample.args.XXXDoingArgs;
/**
* 业务插件:表单插件示例,在此插件中触发业务扩展插件
*/
public class XXXBizFormPlugin extends AbstractFormPlugin {
@Override
public void registerListener(EventObject e) {
super.registerListener(e);
Button button = this.getView().getControl("buttonap");
button.addClickListener(this);
}
@Override
public void click(EventObject evt) {
Control button = (Control)evt.getSource();
if (StringUtils.equals(button.getKey(), "buttonap")) {
this.doXXX(evt);
}
}
/**
* 按钮点击处理逻辑:处理过程中,触发业务扩展插件的事件
*/
private void doXXX(EventObject evt) {
List<String> execLogs = new ArrayList<String>(10); // 输出日志集合
// 初始化业务扩展插件事件参数(把次要代码放在前面,避免影响主题代码的阅读)
XXXBeforeArgs beforgArgs = new XXXBeforeArgs();
beforgArgs.setExecLogs(execLogs);
XXXDoingArgs doingArgs = new XXXDoingArgs();
doingArgs.setExecLogs(execLogs);
XXXAfterArgs afterArgs = new XXXAfterArgs();
afterArgs.setExecLogs(execLogs);
IXXXSubPlugin defXXXSubPlugin = new DefXXXSubPlugin(); // 构建插件默认实现类
// 初始化插件管理器,参数:(1.默认实现类 2.插件接口,3.业务场景编码(全局唯一),4.插件过滤条件)
PluginProxy<IXXXSubPlugin> pluginProxy = PluginProxy.create(defXXXSubPlugin, IXXXSubPlugin.class,
"kd.bos.bizextplugin.sample.XXXBizFormPlugin.myButtonClick", null);
// 模式1.扩展: callBefore :在调用默认实现前,先调用扩展插件的同名方法(扩展插件、默认实现,二者依次执行)
execLogs.add("调用callBefore :扩展插件、默认实现,二者依次执行");
pluginProxy.callBefore(p ->{p.beforeXXX(beforgArgs); return null;});
// 模式2.替换: callReplace, 如果有扩展,则调用扩展;没扩展则不执行(仅执行扩展插件)
execLogs.add("\r\n调用callReplace: 仅执行扩展插件");
pluginProxy.callReplace(p ->{ p.doXXX(doingArgs); return null;});
// 模式2.替换: callReplaceIfPresent,如果存在扩展则替换,否则调用默认实现。(扩展插件和默认实现,二选一)
execLogs.add("\r\n调用callReplaceIfPresent: 优先执行扩展插件,无扩展则执行默认实现");
pluginProxy.callReplaceIfPresent(p -> {p.doXXX(doingArgs); return null;});
// 模式1.扩展: callAfter:在调用默认实现后,再调用扩展插件的同名方法(默认实现、扩展插件,二者都执行)
execLogs.add("\r\n调用callAfter: 默认实现,扩展插件,依次执行"); // 输出执行日志
pluginProxy.callAfter(p ->{ p.afterXXX(afterArgs); return null;});
// 把执行过程生成的日志,显示在界面上
String ret = StringUtils.join(execLogs.toArray(), " \r\n");
this.getModel().setValue("execlogs", ret);
}
}
```
### 事件参数1:XXXBeforeArgs
前述示例中,插件beforeXXX方法的参数定义:
```language
package kd.bos.bizextplugin.sample.args;
import java.util.List;
public class XXXBeforeArgs {
private List<String> execLogs; // 执行日志:执行为演示效果,特别定义一个List<String>,插件代码向其中添加信息,作为已执行凭据
/**
* 执行日志(仅供演示用,实际业务扩展不需要)
* @return
*/
public List<String> getExecLogs() {
return execLogs;
}
public void setExecLogs(List<String> execLogs) {
this.execLogs = execLogs;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
```
### 事件参数2:XXXDoingArgs
前述示例中,插件doXXX方法的参数定义:
```language
package kd.bos.bizextplugin.sample.args;
import java.util.List;
public class XXXDoingArgs {
private List<String> execLogs; // 执行日志:执行为演示效果,特别定义一个List<String>,插件代码向其中添加信息,作为已执行凭据
/**
* 执行日志(仅供演示用,实际业务扩展不需要)
* @return
*/
public List<String> getExecLogs() {
return execLogs;
}
public void setExecLogs(List<String> execLogs) {
this.execLogs = execLogs;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
```
### 事件参数3:XXXAfterArgs
前述示例插件中,afterXXX事件的参数定义:
```language
package kd.bos.bizextplugin.sample.args;
import java.util.List;
public class XXXAfterArgs {
private List<String> execLogs; // 执行日志:执行为演示效果,特别定义一个List<String>,插件代码向其中添加信息,作为已执行凭据
/**
* 执行日志(仅供演示用,实际业务扩展不需要)
* @return
*/
public List<String> getExecLogs() {
return execLogs;
}
public void setExecLogs(List<String> execLogs) {
this.execLogs = execLogs;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
```
## 二次开发-代码示例
二次开发实现业务扩展插件接口,完成业务逻辑扩展。
```language
package kd.bos.subplugin.sample.isv1;
import kd.bos.subplugin.sample.IXXXSubPlugin;
import kd.bos.subplugin.sample.args.XXXAfterArgs;
import kd.bos.subplugin.sample.args.XXXBeforeArgs;
import kd.bos.subplugin.sample.args.XXXDoingArgs;
public class Isv1XXXSubPlugin implements IXXXSubPlugin {
public void beforeXXX(XXXBeforeArgs args) {
String ret = getClass().getName() + ".beforeXXX()";
args.getExecLogs().add(ret);
System.out.println(ret);
}
/**
* 插件事件方法:业务逻辑执行时触发,重写此方法,可改写业务实现逻辑
*/
public void doXXX(XXXDoingArgs args) {
String ret = getClass().getName() + ".doXXX()";
args.getExecLogs().add(ret);
System.out.println(ret);
}
/**
* 插件事件方法:业务逻辑执行完后触发,同步处理其他数据
*/
public void afterXXX(XXXAfterArgs args) {
String ret = getClass().getName() + ".afterXXX()";
args.getExecLogs().add(ret);
System.out.println(ret);
}
}
```
## 效果演示
本节演示前述示例代码运行效果。
如下是业务场景代码节选图,使用两种调用扩展插件的执行:扩展、替换
![image.webp](/download/0100afbf75c898fa40569bd88fbb96a5f6e7.webp)
### 未注册二开插件
如下是未注册二开插件,仅运行默认实现时输出的日志内容:
![image.webp](/download/0100643b63e5ed264a59a1b4b68c5dcc0902.webp)
### 注册了二开插件
如下是注册了二开扩展插件Isv1XXXSubPlugin、Isv2XXXSubPlugin之后的执行日志:
![image.webp](/download/0100ba4e5130849d43f0bee39189bc1b9d9e.webp)
**【适用版本】** 该功能适用版本为苍穹V5.0
基于业务场景插件扩展方案介绍
# 1.整体介绍本方案分别从金蝶云标准产品的业务开发人员、二次开发人员两种角色,说明如何实现基于业务场景的插件扩展。![image.webp](/do...
点击下载文档
本文2024-09-23 00:28:45发表“云苍穹知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-cangqiong-139722.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章