在NC二开中,经常要在保证系统核心业务不变的情况下,对系统的业务逻辑进行扩展,这些行为统称为“后端扩展”。
对于单据而言,“业务规则扩展”和“业务事件扩展”是两种最常见的扩展方式。这两种方式并不冲突,可以同时使用,下面我们就来一步一步探索这两种方式。
我们知道所有的单据都会走单据动作脚本N_XX
,一般来说,动作脚本会调用相应Service
的增删改查方法,方法里会调用诸如InsertAction
之类的动作。一些单据没有自己的action
,会调用使用泛型编程的公共action
,我们就以其中一个为例来分析,先看代码:
public class InsertAction<T extends IBill> {
// ...
public T[] processAction(T[] billVOs) {
// 业务规则处理类
AroundProcesser<T> processor = new AroundProcesser<T>(this.point);
// 添加业务规则
addInnerRule(processor);
// 派发动作前业务事件
fireBeforeBusinessEvent(billVOs);
// 执行动作前业务规则
billVOs = processor.before(billVOs);
// 由于支持FilterRule,所以返回的billVOs可能是null
if (billVOs == null || billVOs.length == 0) {
return null;
}
// 新增保存
T[] resultVOs = this.insert(billVOs);
// 执行动作后业务规则
resultVOs = processor.after(resultVOs);
return resultVOs;
}
/**
* 添加业务规则
*/
protected void addInnerRule(AroundProcesser<T> processor) {
for (IRule<T> rule : beforeRule) {
processor.addBeforeRule(rule);
}
for (IFilterRule<T> rule : beforeFilterRule) {
processor.addBeforeRule(rule);
}
for (IRule<T> rule : afterRule) {
processor.addAfterRule(rule);
}
for (IFilterRule<T> rule : afterFilterRule) {
processor.addAfterRule(rule);
}
}
/**
* 动作前业务事件
*/
protected void fireBeforeBusinessEvent(T[] billVOs) {
try {
EventDispatcherDelegate.fireEvent(IPMEventType.TYPE_INSERT_BEFORE, billVOs);
} catch (BusinessException e) {
ExceptionUtils.wrappException(e);
}
}
/**
* 动作后业务事件
*/
protected void fireAfterBusinessEvent(T[] billVOs) {
try {
EventDispatcherDelegate.fireEvent(IPMEventType.TYPE_INSERT_AFTER, billVOs);
} catch (BusinessException e) {
ExceptionUtils.wrappException(e);
}
}
// ...
}
其实说到底,所谓的“业务规则”和“业务事件”,不过是在代码里预留位置执行的一段逻辑。通过分析上面的代码,我们可以发现他们是如何被触发的:
- 业务规则
通过业务规则处理类例如AroundProcesser
执行,并且可以通过业务规则处理类的addBeforeRule()
和addAfterRule()
方法直接在代码里添加前后规则。 - 业务事件
通过EventDispatcherDelegate
的fireEvent()
进行事件派发。
“后端扩展”要保证系统核心业务不变,理论上来说在代码里直接为AroundProcesser
等业务规则处理类的add
方法添加业务规则是不规范的,因为他还是改动了源码。但是在实际的二开中,如果能获取源码的话,改源码是最方便快捷的方式,下面我们来说说标准的扩展方式:
- 业务规则
在数据表pub_pluginitem
里注册,下面的SQL语句中出现的字段是必须的。
insert into pub_pluginitem (pk_pluginitem, vmodulename, vcomponentname, vextendpointname, veventtype, vextendtype, vruleclass) values ('主键', '模块名', '组件名', '业务规则扩展点', '扩展事件类型(before、after)', '扩展类型(addAfter、addBefore、replace)', '扩展规则类'); - 业务事件
在NC的“业务插件注册”模块里注册,事件源ID为单据对应元数据中的主表ID,事件类型编码需要与代码中EventDispatcherDelegate.fireEvent()
的首个参数对应。
讲了业务规则和业务事件的使用方式,最后来看他们自身的代码实现:
- 业务规则
实现IRule
、ICompareRule
或者IFilterRule
接口。 - 业务事件
实现IBusinessListener
接口,与业务规则能直接获取到VO
不同,业务事件能直接获取到的是IBusinessEvent
类型的业务事件event
,通过event.getEventType()
可以获取到事件类型编码。获取编码的意义在于,我们可以将一个业务事件配置到业务插件注册里的不同事件类型下,然后通过代码判断执行不同的逻辑。这样可以让不同事件类型共享同一段代码,避免代码冗余。
至于VO
如何获取,最好是通过debug分析参数IBusinessEvent
的实例,因为可能获取到不同的事件类型,获取VO
方式也不同,一般来说event.getUserObject()
是第一步。