# APPS-API解耦开发
# 背景
V5产品由几十个工程组成,使用Maven做依赖管理,在Maven框架内工程间做到逻辑解耦:
- 应用组(apps工程)之间无法直接调用,防止代码污染
- 应用组(apps工程)之间通过apps-api桥接:接口定义在apps-api,用于应用组之间调用
- 工作流通过ctp-workflow-api对外开放接口,实现解耦
- 表单应用通过cap-api对外开放接口,实现解耦
- apps-common是应用组件工程,存放水印这类应用公共组件
- ctp-common提供平台基础组件,如国际化、上传下载组件
- ctp-organization提供组件机构的支持
- ctp-core提供平台核心开发框架支撑
我们工程间做到解耦,工程之间只能依靠接口的形式进行调用,而且有依赖顺序。
# 解耦调用示例
前面说我们工程间做到解耦,工程之间只能依靠接口的形式进行调用,而且有依赖顺序。我们所谓的接口调用一般是基于Spring IOC的形式进行注入调用。
下面以一个APP应用模块为例,介绍APP调用不同模块的接口示例。
# 什么是应用级插件化开发
我们90%的开发都是apps级应用插件开发,什么是“apps级应用插件”?我们的apps-bbs、apps-task、apps-collaboration、apps-edoc等等都是通过标准的插件化开发出来的功能应用,标准产品中apps-开头的都是“apps级应用插件”,这些工程的职责就是开发功能应用,属于最上层应用封装,可以调用ctp-平台的代码,也可以apps之间相互调用,但调用需要基于Maven规则进行apps-api接口调用。
如下是标准产品apps-bbs的pom.xml配置,其中parent artifactId=apps-root决定了它的依赖关系。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.seeyon</groupId>
<artifactId>apps-root</artifactId>
<version>standard-V8.0SP2LTS-feature_202205M-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apps-bbs</artifactId>
<version>${apps.version}</version>
</project>
我们的客户化开发也应该遵守此规则进行插件开发。
如下示例展示了“apps级应用插件”可以调用哪些工程模块,开发过程中需要按照下面规范进行代码维护。
# 调用平台级工程
平台级工程主要是ctp-core、ctp-common、apps-common这几个工程的核心库,一般插件化开发的应用模块可以直接引用,示例如下:
package com.seeyon.apps.demo.manager;
import java.util.Map;
import com.seeyon.ctp.common.AppContext;
import com.seeyon.ctp.common.appLog.manager.AppLogManager;
import com.seeyon.ctp.common.exceptions.BusinessException;
import com.seeyon.ctp.common.filemanager.manager.FileManager;
import com.seeyon.ctp.common.usermessage.UserMessageManager;
import com.seeyon.ctp.util.annotation.Inject;
/**
*注意:DemoManagerImpl一定要在Spring Bean容器中注册
*<bean id="demoManager" class="com.seeyon.apps.demo.manager.DemoManagerImpl"></bean>
**/
public class DemoManagerImpl implements DemoManager {
@Inject
private AppLogManager appLogManager;
@Inject
private FileManager fileManager;
@Inject
private UserMessageManager userMessageManager;
@Override
public void saveDemo(Map<String, Object> params) throws BusinessException {
// 上传组件
fileManager.update(file);
// 审计日志组件
appLogManager.insertLog(AppContext.getCurrentUser(), 123, "");
// 发送消息组件
userMessageManager.sendSystemMessage(arg0, arg1, arg2, arg3, arg4);
}
}
# 调用组织机构接口示例
组织机构是核心中的核心,获取人员、部门、单位、岗位、职务等信息都需要通过组织机构接口来完成,应用组调用组织机构接口的方式也很简单,一般有两种方案二选一:引入OrgManager Bean的形式,或直接调用OrgHelper的形式。
public class DemoManagerImpl implements DemoManager {
@Inject
private OrgManager orgManager;
@Override
public void saveDemo(Map<String, Object> params) throws BusinessException {
// 方案一:注入orgManager的形式,能调用组织机构全部的接口
V3xOrgMember member = orgManager.getMemberById(arg0);
// 方案二:不注入Bean,直接使用OrgHelper来调用组织机构接口
member = OrgHelper.getMember(arg0);
}
}
# 调用CAP表单应用接口示例
表单也是V5里面非常核心的业务,表单无处不在,表单也分CAP3、CAP4(这块内容请自行学习),接口也做了区分。表单的接口公布在cap-api这个工程,下面是表单比较常见的调用示例。
public class DemoManagerImpl implements DemoManager {
@Inject
private FormApi4Cap3 formApi4Cap3;
@Inject
private FormApi4Cap4 formApi4Cap4;
@Override
public void saveDemo(Map<String, Object> params) throws BusinessException {
com.seeyon.cap4.form.bean.FormBean formBean4 = formApi4Cap4.getForm(var1);
com.seeyon.ctp.form.bean.FormBean formBean3 = formApi4Cap3.getForm(var2);
}
}
# 调用协同插件应用接口示例
协同模块归属于apps应用组,是经常被调用的模块,apps应用组之间的代码是无法被直接调用,我们通过apps-api作为桥接器实现各模块之间的物理调用。
实现原理很简单:apps-api定义Interface接口,apps-collaboration等具体应用组实现Interface,来达到解耦效果。
/**apps-api工程**/
package com.seeyon.apps.collaboration.api;
import com.seeyon.apps.collaboration.po.ColSummary;
import com.seeyon.ctp.common.exceptions.BusinessException;
public interface CollaborationApi {
/**
* 根据id获取协同对象
*
* 正常:<br>
* 1、传入正确的协同id,能获取到协同的实体<br>
*
* @param id 协同id
* @return ColSummary对象
* @throws BusinessException
*/
public ColSummary getColSummary(Long id) throws BusinessException;
}
/**apps-collaboration工程**/
package com.seeyon.apps.collaboration.api;
import org.apache.commons.logging.Log;
import com.seeyon.apps.collaboration.manager.ColManager;
import com.seeyon.apps.collaboration.po.ColSummary;
import com.seeyon.ctp.common.exceptions.BusinessException;
import com.seeyon.ctp.common.log.CtpLogFactory;
import com.seeyon.ctp.util.annotation.Inject;
public class CollaborationApiImpl implements CollaborationApi{
private static final Log LOG = CtpLogFactory.getLog(CollaborationApiImpl.class);
@Inject
private ColManager colManager;
@Override
public ColSummary getColSummary(Long id) throws BusinessException {
ColSummary colSummary = colManager.getColSummaryById(id);
if (null != colSummary) {
try {
ColSummary clone = (ColSummary) colSummary.clone();
clone.setId(colSummary.getId());
clone.setAudited(colSummary.isAudited());
return clone;
} catch (CloneNotSupportedException e) {
LOG.error("", e);
}
}
return null;
}
}
/**apps-demo应用组调用**/
package com.seeyon.apps.demo.manager;
import java.util.Map;
import com.seeyon.apps.collaboration.api.CollaborationApi;
import com.seeyon.ctp.common.exceptions.BusinessException;
import com.seeyon.ctp.util.annotation.Inject;
public class DemoManagerImpl implements DemoManager {
@Inject
@PluginQualifier(pluginName= "collaboration") //这个注解的目的是:在没有collaboration插件的时候,防止注入报错
private CollaborationApi collaborationApi;
@Override
public void saveDemo(Map<String, Object> params) throws BusinessException {
ColSummary summary = collaborationApi.getColSummary(arg0);
}
}
← Event事件监听开发 动态接口 →