为什么要在靠前位置介绍代码工程结构?如果开发需要全面接触V5的代码,就需要对V5整个平台的代码管理、依赖关系、调用模式和具体一个工程的代码结构有个了解,否则不了解代码结构,根本不知道怎么入手。
首先上一张图,研发内部的代码维护模式(部分,非全部):
标准产品的代码使用Gitlab做版本管理,使用Maven做依赖管理---工程间使用Maven做了解耦控制,这是核心,也是基本要求,下面会详细说明。
对于客开,致远提供了CTP-Stuido平台进行代码管理,同样基于Maven做依赖管理,唯一不同的地方是代码版本管理(截至2021-7-11)暂时使用SVN。所以,以下代码结构相关的内容对客开有效。
如果是一个纯后端工程,或前后端不分离工程,代码结构如下:
apps-samples
└── src
├── main
| ├── java Java代码目录
| | └── com
| | └── seeyon
| | └── apps
| | └── samples
| | └── controller
| | └── SamplesController.java
│ └── webapp 资源目录(会输出到webapps/seeyon目录中,不会打入jar包中)
│ ├── apps_res
│ | └── samples apps_res下存放静态资源文件,注意按照插件模块分文件夹存放
│ | └── css
│ | └── image
│ | └── js
│ | └── html
│ └── WEB-INF
│ ├── cfgHome
│ | └── plugin
│ | └── samples
│ | └── i18n 国际化资源存放
│ | └── spring Spring Bean配置Dao、Manager、Controller等
│ | └── pluginCfg.xml 插件定义信息
│ └── jsp JSP页面位置,发散问题:JSP为何要存放在WEB-INF下?
├── test 单元测试目录
│ ├── java
│ │ └── com
│ │ └── seeyon
│ │ └── ctp
│ │ └── common
│ │ └── cache
│ │ └── CacheMapTest.java
│ └── resources
| ├── data
| | └── ctp-organization.xml 单元测试所需要的组织机构
│ └── test.properties 单元测试配置文件
└── pom.xml maven配置文件
V5产品由几十个工程组成,使用Maven做依赖管理,在Maven框架内工程间做到逻辑解耦:
copy by DecouplingDependencyGraph注:不同版本,工程存在一定差异
前面说我们工程间做到解耦,工程之间只能依靠接口的形式进行调用,而且有依赖顺序。我们所谓的接口调用一般是基于Spring IOC的形式进行注入调用。
下面以一个APP应用模块为例,介绍APP调用不同模块的接口示例。
平台组件只要是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容器中注册
*
**/
public class DemoManagerImpl implements DemoManager {
@Inject
private AppLogManager appLogManager;
@Inject
private FileManager fileManager;
@Inject
private UserMessageManager userMessageManager;
@Override
public void saveDemo(Map
// 上传组件
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
// 方案一:注入orgManager的形式,能调用组织机构全部的接口
V3xOrgMember member = orgManager.getMemberById(arg0);
// 方案二:不注入Bean,直接使用OrgHelper来调用组织机构接口
member = OrgHelper.getMember(arg0);
}
}
表单也是V5里面非常核心的业务,表单无处不在,表单也分CAP3、CAP4(这块内容请自行学习),接口也做了区分。表单的接口公布在cap-api这个工程,下面是表单比较常见的调用示例。
public class DemoManagerImpl implements DemoManager {
@Inject
private FormApi4Cap3 formApi4Cap3;
@Inject
private FormApi4Cap4 formApi4Cap4;
@Override
public void saveDemo(Map
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获取协同对象
*
* 正常:
* 1、传入正确的协同id,能获取到协同的实体
*
* @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
ColSummary summary = collaborationApi.getColSummary(arg0);
}
}