# 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级应用插件”可以调用哪些工程模块,开发过程中需要按照下面规范进行代码维护。

# 调用平台级工程

1714302235029.png

平台级工程主要是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);
 }
 
}

# 调用组织机构接口示例

1714302261781.png

组织机构是核心中的核心,获取人员、部门、单位、岗位、职务等信息都需要通过组织机构接口来完成,应用组调用组织机构接口的方式也很简单,一般有两种方案二选一:引入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表单应用接口示例

1714302322707.png

表单也是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);
 }
 
}

# 调用协同插件应用接口示例

1714302336202.png

协同模块归属于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);
 }
 
}
创建人:het
修改人:het