44 KiB
Ecology9 开发手册
在线地址: http://wcode.store/#/./weaver/Ecology9%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C
1. 环境搭建
1.1 Ecology安装和启动
- 准备
ecology
|resin
|jdk1.8[可直接使用已安装的]
- 修改resin配置
resin/config/resin.conf
// 修改jdk路径:相对路径或者绝对路径
<javac compiler="C:\Program Files\Java\jdk1.8.0_151\bin\javac" args="-encoding UTF-8"/>
// 修改ecology路径
<web-app id="/" root-directory="F:\WEAVER\ecology">
- 修改resin启动配置
resin/resinstart.bat
其中java_home
为jdk
的路径,可以是相对路径也可以是绝对路径
set java_home=C:\Program Files\Java\jdk1.8.0_151
- 启动
resin
点击resinstart.bat
- 初始验证码文件路径
ecology/WEB-INF/code.key
1.2 主要目录介绍
ecology/classbean
:存放编译后的class文件ecology/log
:系统中日志存放目录ecology/WEB-INF/prop
:web
配置文件目录ecology/WEB-INF/lib
:系统jar
包路径
1.3 数据库配置
如果数据库与Ecology不在同一台服务器上,则可以修改数据库配置文件中的数据库配置。
数据库配置文件路径:
ecology/WEB-INF/prop/weaver.properties
# sqlServer
DriverClasses = com.microsoft.sqlserver.jdbc.SQLServerDriver
ecology.url = jdbc:sqlserver://host:port;DatabaseName=dbname
ecology.user = username
ecology.password = password
# oracle
DriverClasses = oracle.jdbc.OracleDriver
ecology.url = jdbc:oracle:thin:@host:port:ecology
ecology.user = username
cology.password = password
2. E9常见表结构
2.1 流程相关数据存储表
数据库表名 | 中文说明 | 备注 |
---|---|---|
workflow_base | 流程基本信息 | isbill=1 |
workflow_bill | 流程表单信息 | id > 0固定表名 id < 0 动态生成 |
workflow_billfield | 表单字段信息 | |
workflow_billdetailtable | 表单明细表 | |
workflow_nodebase | 节点信息 | |
workflow_flownode | 流程节点信息 | |
workflow_nodelink | 流程出口信息 | |
workflow_nodegroup | 节点操作人信息 | |
workflow_groupdetail | 节点操作人详情 | |
workflow_requestbase | 请求基本信息 | |
workflow_currentoperator | 请求节点操作人 | |
workflow_requestlog | 请求签字意见 | |
workflow_nownode | 请求当前节点 | |
workflow_browserurl | 系统浏览按钮信息 | |
workflow_selectitem | 下拉框信息 |
2.2 人力资源相关数据存储
表名 | 说明 | 备注 |
---|---|---|
HrmResource | 人力资源基本信息表 | - |
HrmResource_online | 人员在线信息表 | - |
HrmResourceManager | 系统管理员信息表 | - |
HrmDepartment | 人力资源部门表 | - |
HrmDepartmentDefined | 人力资源部门自定义字段信息表 | - |
HrmSubCompany | 人力资源分部表 | - |
hrmroles | 角色信息表 | - |
hrmrolemembers | 角色人员 | - |
hrmjobtitles | 岗位信息表 | - |
3. 前端开发
ecology9
前端上采用react
+antd
+mobx
+react-router
等框架实现的单页面应用
3.1 流程开发
流程表单前端接口:E9流程表单前端接口API
- 所有接口统一封装在全局对象
window.WfForm
中 - 表单字段相关操作,不推荐使用jQuery,禁止原生JS直接操作DOM结构!
- 在开发过程中,推荐都使用API接口操作,由产品统一运维;同时使用API才能完整的兼容移动终端
3.2 建模开发
3.2.1 布局代码块
建模布局代码块使用上与流程表单的代码块基本一致,区别在于接口的SDK不同,建模表单的所有接口统一封装在全局对象
window.ModeForm
中。
3.2.2 自定义按钮
后端应用中心 -> 建模引擎 -> 查询
任选一个查询页面 -> 自定义按钮 -> 右键 -> 新建
-
方法体中存在多行代码时,每个语句必须以
;
结尾;否则会报错! -
params
的值等于‘field1+field2+field3’
这个值是一个字符串 -
id
指的是数据ID
前端显示
- 配置
ecode
使用
新建前置文件
index.js
并将方法挂到全局对象window.g
下
新建自定义按钮
3.2.3 页面扩展
后端应用中心 -> 建模引擎 -> 模块
任选一个模块 -> 页面扩展 -> 右键 -> 新建
- 扩展用途:卡片页面、查询列表(批量操作)、卡片页面和查询列表
- 卡片页面:可以设置页面扩展显示在卡片信息页面,可以选择在新建页面、编辑页面、查看页面显示页面扩展。
- 查询列表(批量操作):设置在查询列表时,则会在引用该模块的查询列表的批量操作中显示页面扩展项,在批量操作中勾选后会在前台列表中显示对应的页面扩展项。
- 卡片页面和查询列表:可以设置页面扩展项既显示在对应的卡片页面又显示在查询列表(批量操作)中。
javascript:test()
: 该方法可以在建模引擎 -> 查询 -> 该模块的查询列表 -> 编辑代码块
中定义
前端按钮测试如下
- 页面扩展同样可以配置
ecode
使用,将链接目标地址改成:javascript: window.g.test()
即可,建议这样做,方便后续代码维护。
3.3 Ecode在线编辑
ecode官方文档:https://e-cloudstore.com/ecode/doc
E9 技术站地址: https://e-cloudstore.com/e9/index2.html?tdsourcetag=s_pctim_aiomsg
使用已经封装好的组件进行页面开发或者页面改写会更加便捷迅速。
3.4 动态注入代码
通过前置加载动态注册代码,完全脱离代码块的实现方式。非官方的方案,使用中有任何问题概不负责!
官方用法请参考:全局流程代码块整合
注意:Ecode部分版本不能使用,更新至最新版本即可
-
打开ecode页面,将 config.js 中的代码复制到
系统配置 -> config.js
中 -
使用
Wcode.runScript
动态注入代码,参数说明如下
参数 | 类型 | 可选 | 默认 | 说明 |
---|---|---|---|---|
id | string | 必填 | ‘’ | 流程代码块,则 id 指的是 流程的workflowid;建模代码块,则 id 指的是 模块的 modeId;查询列表代码块,则 id 指的是查询列表的id,即 customid |
mode | string | 必填 | '' | Wcode.WF_TYPE: 流程代码块;MODE_TYPE:建模代码块;LIST_TYPE:查询列表代码块 |
appId | function | 必填 | '' | ecode 中应用的 id, 可使用 ${appid} 占位符替换 |
nodeId | integer|string | 选填 | 0 | 流程使用,指定节点时才加载代码,不指定则只在创建节点时加载 |
type | integer|string | 选填 | 0 | 建模使用,指定布局时才加载代码,不指定则只在新建布局时加载 |
menuIds | string | 选填 | ‘ALL_MODE_LIST’ | 列表使用,指定查询列表在哪个菜单下时加载,不指定则默认所有菜单下存在该列表都加载。 |
noCss | boolean | 选填 | true | 是否禁止单独加载css,通常为了减少css数量,css默认前置加载 |
cb | function | 选填 | function() {} | 代码块成功加载完毕的回调方法 |
在 Ecode 中任意创建一个应用,应用下新建一个
register.js
的前置文件,根据不同场景加入对应的代码
- 流程节点使用
Wcode.runScript({
mode: Wcode.WF_TYPE,
id: '4',
appId: '${appId}'
})
- 建模布局使用
Wcode.runScript({
mode: Wcode.MODE_TYPE,
id: '1',
appId: '${appId}',
type: Wcode.SHOW_TEMPLATE,
})
- 查询列表使用
Wcode.runScript({
mode: Wcode.LIST_TYPE,
id: '1',
appId: '${appId}'
})
4. 后端开发
代码案例:E9Demo
工具类 wcode 代码: https://github.com/Y-Aron/wcode
4.1 Java
项目环境搭建
4.1.1 web.xml
部分配置
API
接口的xml
配置
<!-- ecology/WEB-INF/web.xml -->
<servlet>
<servlet-name>restservlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<!-- 设置jersey的扫包路径;可配置多个,以;隔开 -->
<param-value>com.cloudstore;com.api</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restservlet</servlet-name>
<url-pattern>/api/*</url-pattern>
<!-- 自定义接口前缀,绕开过滤器拦截 -->
<url-pattern>/port/*</url-pattern>
</servlet-mapping>
注意: /api/* 的请求必须在用户登录时才能进行访问。否则会被过滤器过滤。其 web.xml 配置 SessionFilter 实 现了过滤拦截。当然也可以自定义接口前缀,如上配置了 /port/*
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>com.cloudstore.dev.api.service.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<!-- 拦截 /api/* 请求,做是否登录以及其他逻辑判断 -->
<url-pattern>/api/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/page/interfaces/*.jsp</url-pattern>
</filter-mapping>
4.1.2 项目环境搭建
传统
JavaWeb
项目搭建
- 创建
Java
项目 - 添加所需依赖
File -> Project Structure -> Project Settings -> Libraries
需要添加的ecology
的依赖路径有: ecology/WEB-INF/lib
; resin/lib
; ecology/classbean
;
其中classbean
是必须要引入的, 其他两个按需引入
- 编译
Java
文件将编译后的class
文件放入ecology/classbean/
目录下即可
注意: ecology/classbean
最好备份, 因为IDEA
在编译的时候可能会清除掉已有的classbean
使用
Maven
构建项目
相对于传统的 JavaWeb
项目而言,使用 maven 可以做到快速搭建项目,项目结构层次分明,并且 jar 间的依赖关系可以得到直观的体现。当然需要二开人员对 maven 有一定的了解,并且熟悉 ec 系统所使用的依赖,防止版本冲突。
后端项目结构以及开发案例详见手册: E9后端开发指南
4.1.3 Java项目结构
一般情况下遵循以下项目结构进行后端开发
其中,com.api.wcode
和com.engine.wcode
中的 wcode
为自定义包名,不与已有的包重复即可。
com.api.wcode
|-- web 接口定义层
com.engine.wcode
|-- web 接口实现层
|-- service 服务定义层
|-- impl 服务实现层
|-- domain 数据层
|-- cmd 原子操作层定义
|-- dao Dao层
|-- mapper mapper接口定义层
4.2 后端API路径规范
模块 | 文件路径(含下级) | 接口访问地址 |
---|---|---|
流程 | com.api.workflow | /api/workflow/… |
门户 | com.api.portal | /api/portal/… |
文档 | com.api.doc | /api/doc/… |
建模 | com.api.formmode | /api/formmode/… |
移动建模 | com.api.mobilemode | /api/mobilemode/… |
会议 | com.api.meeting | /api/meeting/… |
人力 | com.api.hrm | /api/hrm/… |
财务 | com.api.fna | /api/fna/… |
项目 | com.api.prj | /api/prj/… |
公文 | com.api.odoc | /api/odoc/… |
集成 | com.api.integration | /api/integration/… |
微博 | com.api.blog | /api/blog/… |
4.3 自定义Java
接口
4.3.1 流程节点前后附加操作
在节点前后附加操作中可设置接口动作,完成流程自定义附加操作
接口动作标识不能重复;接口动作类文件必须是类全名,该类必须实现接
weaver.interfaces.workflow.action
方法public String execute(RequestInfo request)
参考代码如下
package com.engine.wcode.action;
import com.weaver.general.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weaver.hrm.User;
import weaver.interfaces.workflow.action.Action;
import weaver.soa.workflow.request.*;
public class TestAction implements Action {
private String customParam; //自定义参数
private final Logger logger = LoggerFactory.getLogger(TestAction.class);
@Override
public String execute(RequestInfo requestInfo) {
logger.debug("进入action requestid = {}", requestInfo.getRequestid());
showCurrentForm(requestInfo);
showFormProperty(requestInfo);
showDetailsTables(requestInfo);
logger.debug("Action 执行完成,传入自定义参数:{}", this.getCustomParam());
// requestInfo.getRequestManager().setMessagecontent("返回自定义的错误消息");
// requestInfo.getRequestManager().setMessageid("自定义消息ID");
// return FAILURE_AND_CONTINUE; // 注释的三句话一起使用才有效果!
return SUCCESS;
}
private void showCurrentForm(RequestInfo requestInfo) {
String requestid = requestInfo.getRequestid(); // 请求ID
String requestLevel = requestInfo.getRequestlevel(); // 请求紧急程度
// 当前操作类型 submit:提交/reject:退回
String src = requestInfo.getRequestManager().getSrc();
// 流程ID
String workFlowId = requestInfo.getWorkflowid();
// 表单名称
String tableName = requestInfo.getRequestManager().getBillTableName();
// 表单数据ID
int bill_id = requestInfo.getRequestManager().getBillid();
// 获取当前操作用户对象
User user = requestInfo.getRequestManager().getUser();
// 请求标题
String requestName = requestInfo.getRequestManager().getRequestname();
// 当前用户提交时的签字意见
String remark = requestInfo.getRequestManager().getRemark();
// 表单ID
int form_id = requestInfo.getRequestManager().getFormid();
// 是否是自定义表单
int isbill = requestInfo.getRequestManager().getIsbill();
logger.debug("requestid: {}", requestid);
logger.debug("requestLevel: {}", requestLevel);
logger.debug("src: {}", src);
logger.debug("workFlowId: {}", workFlowId);
logger.debug("tableName: {}", tableName);
logger.debug("bill_id: {}", bill_id);
logger.debug("user: {}", user);
logger.debug("requestName: {}", requestName);
logger.debug("remark: {}", remark);
logger.debug("form_id: {}", form_id);
logger.debug("isbill: {}", isbill);
}
/**
* 获取主表数据
*/
private void showFormProperty(RequestInfo requestInfo) {
logger.debug("获取主表数据 ...");
// 获取表单主字段值
Property[] properties = requestInfo.getMainTableInfo().getProperty();
for (Property property : properties) {
// 主字段名称
String name = property.getName();
// 主字段对应的值
String value = Util.null2String(property.getValue());
logger.debug("name: {}, value: {}", name, value);
}
}
/**
* 取明细数据
*/
private void showDetailsTables(RequestInfo requestInfo) {
logger.debug("获取所有明细表数据 ...");
// 获取所有明细表
DetailTable[] detailTables = requestInfo.getDetailTableInfo().getDetailTable();
if (detailTables.length > 0) {
for (DetailTable table: detailTables) {
// 当前明细表的所有数据,按行存储
Row[] rows = table.getRow();
for (Row row: rows) {
// 每行数据再按列存储
Cell[] cells = row.getCell();
for (Cell cell: cells) {
// 明细字段名称
String name = cell.getName();
// 明细字段的值
String value = cell.getValue();
logger.debug("name: {}, value: {}", name, value);
}
}
}
}
}
public String getCustomParam() {
return customParam;
}
public void setCustomParam(String customParam) {
this.customParam = customParam;
}
}
配置:后端应用中心 -> 流程引擎 -> 路径管理 -> 路径设置
任选一个流程 -> 流程设置 -> 节点信息
任选一个节点 -> 节点前 / 节点后附加操作
4.3.2 页面扩展接口
页面扩展 -> 接口动作 -> 自定义接口动作
执行页面扩展的后续操作,通过配置自定义
Java
接口动作类实现。接口动作类文件必须是类全名。该类必须继承
weaver.formmode.customjavacode.AbstractModeExpandJavaCode
方法public void doModeExpand(Map<String, Object> param)
参考代码
package com.engine.wcode.formmode.extend;
import weaver.conn.RecordSet;
import weaver.general.Util;
import weaver.hrm.User;
import weaver.soa.workflow.request.RequestInfo;
import weaver.formmode.customjavacode.AbstractModeExpandJavaCode;
import java.util.Map;
public class ModeExpandTemplate extends AbstractModeExpandJavaCode {
@Override
public void doModeExpand(Map<String, Object> param) throws Exception {
// 当前用户
User user = (User) param.get("user");
int billid = -1; // 数据id
int modeid = -1; // 模块id
RequestInfo requestInfo = (RequestInfo) param.get("RequestInfo");
if (requestInfo != null) {
billid = Util.getIntValue(requestInfo.getRequestid());
modeid = Util.getIntValue(requestInfo.getWorkflowid());
if (billid > 0 && modeid > 0) {
RecordSet rs = new RecordSet();
//------请在下面编写业务逻辑代码------
}
}
}
}
配置:后端应用中心 -> 建模引擎 -> 模块
任选一个模块 -> 页面扩展 -> 任选一个扩展名称 -> 接口动作 -> 点击
+
号 -> 自定义接口动作
4.3.3 计划任务
通过配置自定义
Java
接口的实现类,定时执行相应的代码
- 按照设定的时间定时执行任务,计划任务标识不能重复
- 计划任务类必须是类的全名,该类必须继承
weaver.interfaces.schedule.BaseCronJob
类,重写方法public void execute() {}
- 时间格式按
Cron
表达式的定义
参考代码
package com.engine.wcode.cron;
import weaver.interfaces.schedule.BaseCronJob;
public class CronTemplate extends BaseCronJob {
@Override
public void execute() {
//------请在下面编写业务逻辑代码------
}
}
配置:后端应用中心 -> 集成中心 -> 计划任务 -> 任务列表 -> 新建
通过计划任务列表的每个计划任务的自定义按钮,可以对每个任务进行状态操作,具体使用如下所示
状态详解:
-
启用: 计划任务将根据Cron表达式执行;
-
禁用: 计划任务将不再执行,重启服务也不会再次执行;
-
暂停: 针对计划任务进行停止,重启服务将恢复正常状态;
-
恢复: 针对暂停状态的计划任务进行恢复,恢复后计划任务将继续执行;
-
执行: 单次执行计划任务,不影响Cron表达式周期执行;
-
测试: 检查填写的计划任务类是否符合规范(继承weaver.interfaces.schedule.BaseCronJob类,重写方法public void execute() {})
4.3.4 自定义按钮接口
通过配置自定义
Java
类,判断自定义按钮在查询列表中是否显示
参考代码
package com.engine.wcode.formmode.button;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weaver.formmode.interfaces.PopedomCommonAction;
public class CustomBtnShowTemplate implements PopedomCommonAction {
private Logger logger = LoggerFactory.getLogger(CustomBtnShowTemplate.class);
/**
* 得到是否显示操作项
* @param modeid 模块id
* @param customid 查询列表id
* @param uid 当前用户id
* @param billid 表单数据id
* @param buttonname 按钮名称
* @retrun "true"或者"false"true显示/false不显示
*/
@Override
public String getIsDisplayOperation(String modeid, String customid,String uid, String billid, String buttonname) {
logger.debug("modeId: {}", modeid);
logger.debug("customId: {}", customid);
logger.debug("uid: {}", uid);
logger.debug("billId: {}", billid);
logger.debug("buttonname: {}", buttonname);
return "false";
}
}
配置:后端应用中心 -> 建模引擎 -> 查询
任选一个查询列表 -> 自定义按钮 -> 右键 -> 新建
前端查询列表中,由于接口中返回false,则 ==受控按钮== 不显示
4.4 数据库操作
4.4.1 CURD
使用
weaver.conn.RecordSet
可以对数据库进行CURD
等数据库操作
参考代码:
RecordSet rs = new RecordSet();
String sql = "select loginid, lastname from hrmresource where id=?";
// 防止sql注入, objects 为动态参数
rs.executeQuery(sql, 2);
if (rs.next()) {
String loginid = rs.getString("loginid");
String lastname = rs.getString("lastname");
}
String updateSql = "update hrmresource lastname=? where id=?";
// 返回是否更新成功
boolean bool = rs.executeUpdate(sql, "孙悟空", 2);
4.4.2 使用事务
使用
weaver.conn.RecordSetTrans
可以对数据库进行事务操作
参考代码
RecordSetTrans rst = new RecordSetTrans();
// 开启事务
rst.setAutoCommit(false);
String sql = "update hrmresource lastname=? where id=?";
try {
int a = 1/0;
rst.executeUpdate(sql, "猪八戒", 2);
// 提交事务
rst.commit();
} catch (Exception e) {
e.printStackTrace();
// 事务回滚
rst.rollback();
}
4.4.3 Mybatis的使用
目前E9没有成熟的使用Mybatis的方案,以下是通过自己的研究给出一个不成熟的方案,有
Bug
概不负责!支持多数据源,数据源配置如下:
后端应用中心 -> 集成中心-> 数据源设置 -> 新建
xml
⽅式开发
创建 mapper
接⼝
package com.engine.wcode.mapper;
import java.util.List;
import java.util.Map;
public interface TestMapper {
List<Map<String, String>> selectAll();
}
在 ../ecology/WEB-INF/config/mapper/
路径下创建 test.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.engine.wcode.mapper.TestMapper">
<select id="selectAll" resultType="java.util.HashMap" >
select loginid, password, lastname from hrmresource
</select>
</mapper>
注解⽅式开发
与
xml
⽅式相⽐,使⽤注解开发的⽅便之处在于不⽤写mapper.xml
⽂件
创建 mapper
接⼝
package com.engine.wcode.mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface HrmMapper {
@Select("select loginid, password, lastname from hrmresource")
List<Map<String, String>> selectHrm();
}
使用
Mybatis
操作数据库
- 获取
Mapper
package com.wcode.db;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.session.SqlSession;
import weaver.conn.ConnectionPool;
import weaver.conn.WeaverConnection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static weaver.conn.mybatis.MyBatisFactory.sqlSessionFactory;
/**
* ⽅法参数不接受 null或 ""
* @author Y-Aron
* @create 2019/5/8
*/
public class MapperUtil {
private static final Map<Class, Object> cacheMapper = new ConcurrentHashMap<>(1);
private static final Map<String, SqlSession> cacheSqlSession = new ConcurrentHashMap<>(1);
public static <T> T getMapper(Class<T> clazz) {
return MapperUtil.getMapper(clazz, false);
}
public static <T> T getMapper(Class<T> clazz, boolean enableCache) {
return MapperUtil.getMapper(clazz, null, ExecutorType.SIMPLE, enableCache);
}
public static <T> T getMapper(Class<T> clazz, String dataSource) {
return MapperUtil.getMapper(clazz, dataSource, false);
}
public static <T> T getMapper(Class<T> clazz, String dataSource, boolean enableCache) {
return MapperUtil.getMapper(clazz, dataSource, ExecutorType.SIMPLE, enableCache);
}
public static <T> T getMapper(Class<T> clazz, String dataSource, ExecutorType executorType, boolean enableCache) {
String threadName = Thread.currentThread().getName();
SqlSession sqlSession = cacheSqlSession.get(threadName);
if (sqlSession == null) {
ConnectionPool pool = ConnectionPool.getInstance();
WeaverConnection connection = pool.getConnection(dataSource);
Configuration config = sqlSessionFactory.getConfiguration();
if (executorType == null) {
executorType = config.getDefaultExecutorType();
}
if (enableCache) {
config.setLocalCacheScope(LocalCacheScope.STATEMENT);
}
sqlSession = sqlSessionFactory.openSession(executorType, connection);
}
cacheSqlSession.put(threadName, sqlSession);
return MapperUtil.getMapper(clazz, sqlSession);
}
public static <T> T getMapper(Class<T> clazz, SqlSession sqlSession) {
if (cacheMapper.containsKey(clazz)) {
//noinspection unchecked
return (T) cacheMapper.get(clazz);
}
Configuration config = sqlSession.getConfiguration();
if (!config.hasMapper(clazz)) {
config.addMapper(clazz);
}
T mapper = sqlSession.getMapper(clazz);
cacheMapper.put(clazz, mapper);
return mapper;
}
private static SqlSession getCurrentSqlSession() {
if (cacheSqlSession.size() == 0) return null;
return cacheSqlSession.get(Thread.currentThread().getName());
}
}
- 数据库操作
HrmMapper mapper = MapperUtil.getMapper(HrmMapper.class);
mapper.selectHrm();
4.5 缓存SDK
缓存基类:
Util_DataCache
方法名称 | 方法作用 |
---|---|
getObjVal(String name) | 从所有缓存获取 缓存数据 (主要函数) |
setObjVal(String name, Object value) | 设置所有缓存数据 (主要函数) |
setObjVal(String name, Object value,int seconds) | 设置所有缓存数据 支持 超时自动消失 (主要函数) |
containsKey(String name) | 判断该键名的所有缓存是否存在 |
clearVal(String name) | 清除该键名的所有缓存 |
setObjValWithEh(String name,Object value) | 设置本地缓存 ( 特定情况下使用) |
getObjValWithEh(String name) | 获取本地缓存(特定情况下使用) |
setObjValWithRedis(String name,Object value) | 设置Redis缓存 需要自己释放数据( 特定情况下使用) |
setObjValWithRedis(String name,Object value,int seconds) | 单独设置Redis缓存 超时时间(s)后释放数据( 特定情况下使用) |
getObjValWithRedis(String name) | 单独获取Redis缓存( 特定情况下使用) |
containsKeylWithEh(String name) | 判断本地缓存是否存在该键名( 特定情况下使用) |
clearValWithEh(String name) | 清除本地缓存( 特定情况下使用) |
containsKeyWithRedis(String name) | 判断Redis上是否存在该键名( 特定情况下使用) |
clearValWithRedis(String name) | 清除Redis缓存 |
检查页面
chechRedis.jsp
检查Redis
环境的状态
getRedis.jsp
检查DataKey
的数据
注意数据变更后必须再次执行setObjVal
把数据推送到Redis
import com.cloudstore.dev.api.util.Util_DataCache;
public Map<String,String> refreshDataFormDB() {
Map<String,String> map = new HashMap<String, String>();
Map<String,String> mapdb = getSystemIfo("y");
map.putAll(mapdb);
if(mapdb.size()>0) {
Util_DataCache.setObjVal(em_url, mapdb.get(em_url));
Util_DataCache.setObjVal(em_corpid, mapdb.get(em_corpid));
Util_DataCache.setObjVal(accesstoken,mapdb.get(accesstoken));
Util_DataCache.setObjVal(ec_id,mapdb.get(ec_id));
Util_DataCache.setObjVal(ec_url, mapdb.get(ec_url));
Util_DataCache.setObjVal(ec_name, mapdb.get(ec_name));
Util_DataCache.setObjVal(rsa_pub, mapdb.get(rsa_pub));
Util_DataCache.setObjVal(ec_version, mapdb.get(ec_version));
Util_DataCache.setObjVal(ec_iscluster, mapdb.get(ec_iscluster));
Util_DataCache.setObjVal(em_url, mapdb.get(em_url));
Util_DataCache.setObjVal(em_url_open, mapdb.get(em_url_open));
}
return map;
}
4.6 Rest API 接口
4.6.1 流程表单数据接口
https://www.evernote.com/l/AuMO8ps7HVpMlYkjCMpRC9xyc1VYIcbo1I0/
4.6.2 流程待办列表接口
https://www.evernote.com/l/AuM0l0TdGS9OvZuHd1eztup5KNwIgFokDTU/
4.6.3 流程列表数据接口
https://www.evernote.com/l/AuP8WpYtOmhHkYgnhs7aOKy_AL9kMACGWm4/
5. 对接异构系统
5.1 接口白名单配置
5.1.1 web.xml
配置
在
web.xml
中配置(兼容以前的格式使用weaver_session_filter.properties
后可以不添加参数配置)
SessionCloudFilter
过滤器修改一下unchecksessionurl
路径主要功能超时自动跳转首页
-
checkurl
头部验证路径 适用于 EM -
uncheckurl
头部验证放行路径 适用于 EM -
unchecksessionurl
路径为不检查放行的路径(白名单)
<filter>
<filter-name>SessionCloudFilter</filter-name>
<filter-class>com.cloudstore.dev.api.service.SessionFilte</filter-class>
<init-param>
<param-name>checkurl</param-name>
<param-value>/api/hrm/emmanager;</param-value>
</init-param>
<init-param>
<param-name>uncheckurl</param-name>
<param-value>/api/ec/dev/app/getCheckSystemInfo;/api/ec/dev/app/emjoin;</param-value>
</init-param>
<init-param>
<param-name>unchecksessionurl</param-name>
<param-value></param-value>
</init-param>
</filter>
// ……(其他配置信息不动)
5.1.2 properties
配置
weaver_session_filter.properties
(系统)
外部配置文件,配置放行的路径地址。(启用了weaver_session_filter.properties
会自动覆盖原web.xml
中的路径)
weaver_session_filter_dev.properties
(用户自定义)
用户自定义配置文件,配置放行的路径地址(项目二开的路径建议放用户定义配置文件,升级时不被覆盖)会自动覆盖web.xml 中的路径地址。
# 头部验证路径 适用于 EM
checkurl=/api/hrm/emmanager;
# 头部验证放行路径 适用于 EM
uncheckurl=/api/ec/dev/app/getCheckSystemInfo;/api/ec/dev/app/emjoin;
# session验证放行 不检查放行的路径(白名单)
unchecksessionurl=/api/doc/upload/mobile/uploadFile;/api/doc/upload/mobile/shareFile;/weaver/weaver.file.FileDownload;/api/ec/dev/app/getCheckSystemInfo;/api/ec/dev/app/emjoin;/api/hrm/emmanager/;
5.2 Token认证
5.3 单点登录
单点登录:直接通过URL打开
E9
页面
- 在
ecology/WEB-INF/web.xml
文件默认加上以下配置
<servlet>
<servlet-name>getToken</servlet-name>
<servlet-class>weaver.weaversso.GetToken</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getToken</servlet-name>
<url-pattern>ssologin/getToken</url-pattern>
</servlet-mapping>
<filter>
<filter-name>WeaverLoginFilter</filter-name>
<filter-class>weaver.weaversso.WeaverLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WeaverLoginFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.html</url-pattern>
</filter-mapping>
- 在
ecology/WEB-INF/prop/WeaverLoginClient.properties
配置文件中加入以下参数
其中 test
指的是下文中的 appid
参数,127.0.0.1
指的是授权的 IP 地址
test=127.0.0.1
-
重启
E9
服务 -
使用
Java
测试
package com.wcode.util;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author King
* @version 1.0.0
* @create 2019/11/5 16:17
*/
public class SsoUtils {
private static final String SSO_API = "/ssologin/getToken";
public static String getToken(String ip, int port, String appId, String loginId) throws IOException {
String url = ip + ":" + port + SSO_API;
HttpPost req = new HttpPost(url);
List<NameValuePair> params = new ArrayList<>(2);
params.add(new BasicNameValuePair("appid", appId));
params.add(new BasicNameValuePair("loginid", loginId));
req.setEntity(new UrlEncodedFormEntity(params, HTTP.DEF_CONTENT_CHARSET));
return HttpUtils.getString(req);
}
public static void main(String[] args) throws IOException {
String token = SsoUtils.getToken("http://127.0.0.1", 80, "test", "lcs");
System.out.println("ssoToken: " + token);
String url = "http://127.0.0.1/systeminfo/version.jsp";
HttpPost req = new HttpPost(url);
List<NameValuePair> params = new ArrayList<>(1);
params.add(new BasicNameValuePair("ssoToken", token));
req.setEntity(new UrlEncodedFormEntity(params));
System.out.println(HttpUtils.getString(req));
}
}
控制台输出内容如下:
6. 其他开发
6.1 日志配置
Ecology
底层采用的是log4j
日志框架, 可根据环境自定义日志配置
log4j
配置文件路径:ecology/WEB-INF/log4jinit.properties
- 打开配置文件, 在文件末尾加上如下代码, 然后重启
resin
服务
# appender
log4j.logger.debug=DEBUG,debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
# 按日期滚动文件
log4j.appender.debug.DatePattern='_'yyyyMMdd'.log'
# 自定义日志文件路径
log4j.appender.debug.File=@debug/debug.log
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
# 输出内容格式
log4j.appender.debug.layout.ConversionPattern=%d{HH:mm:ss.SSS}[%p] %l: %m%n
log4j.additivity.debug=false
- 代码中使用:
// 获取自定义的 logger, 其中 debug为配置文件中 log4j.logger.debug中的debug
Logger logger = LoggerFactory.getLogger("debug");
// 支持占位符输出, 不定参数
logger.debug("debug级别消息: {}, {}", "参数1", "参数2");
logger.info("info级别消息!");
logger.warn("warn级别消息!");
logger.error("error级别消息!");
- 最终日志输出路径:
建议: 将重要的日志以 info
级别以上输出, 开发的日志以 debug
级别输出, 这样的话再正式环境下只需修改配置, 即可实现只输出 info
级别的日志, 减少日志的输出!
# 将日志级别提升至INFO
log4j.logger.debug=INFO,debug
6.2 Redis 配置
- 本地下载
redis
ecology/WEB-INF/prop/weaver_new_session.properties
#1表示启用新的session方式,其他值表示不启用
status=1
#用于调试的USERID
debugUsers=
#session id生成模式,1表示自定义生成模式(UUID模式),其他值表示中间件自定义生成模式
useCustomSessionId=1
#同步频率设置(单位,秒)
#主表同步频率
SessionTableSync=2
#明细表同步频率
SessionItemTableSync=5
#超时扫描频率
SessionOverTime=300
#垃圾数据清理扫描频率
SessionLeak=3600
#启动模式,默认是数据库模式
#className=weaver.session.util.DBUtil
className=weaver.session.util.RedisSessionUtil
#redis ip
redisIp=127.0.0.1
#redis port
redisPort=6379
#redis password
redisPassword=123456
enableImmediatelySync=true
etoken=
6.3 lombok 配置
- 下载
lombok.jar
idea
安装lombok plugin
- 代码中使用
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
public class TestDTO {
private String name;
private String password;
}
6.4 Webservice 使用
使用
SoapUI
解析webservice
接口
- SoapUI 介绍和使用方式可以参考博文: https://www.cnblogs.com/yatou-de/p/9273346.html
- 软件下载:https://pan.baidu.com/s/1j36r6_NO_pcTfNDCwjLrog,提取码: 8v9x
使用
HttpClient
以xml
报文形式调用webservice
服务
请求报文可以通过 SoapUI
查看
import com.wcode.util.WsUtils;
import java.io.IOException;
public class TestWebService {
public static void main(String[] args) throws IOException, DocumentException {
String url = "http://www.webxml.com.cn/webservices/ChinaTVprogramWebService.asmx";
String xml = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://WebXml.com.cn/\">\n" +
" <soapenv:Header/>\n" +
" <soapenv:Body>\n" +
" <web:getTVstationDataSet>\n" +
" <web:theAreaID>18</web:theAreaID>\n" +
" </web:getTVstationDataSet>\n" +
" </soapenv:Body>\n" +
"</soapenv:Envelope>";
String resp = WsUtils.execute(url, xml);
Element rootElement = WsUtils.getRootElement(resp);
List<Element> elements = WsUtils.getElements(rootElement, "TvStation");
for (Element element : elements) {
Element e1 = element.element("tvStationID");
System.out.println(e1.getTextTrim());
Element e2 = element.element("tvStationName");
System.out.println(e2.getTextTrim());
}
}
}
6.5 SQL缓存注意事项
- 原则上禁止通过非程序渠道直接修改oa数据库数据。如果一定要修改,请修改完数据后,chrome浏览器访问/commcache/cacheMonitor.jsp界面,点击重启加载配置。这样操作修改的数据可以及时生效。
- 如果存在第三方程序修改oa数据库的表,则需要将会修改的表的名称以(名称=名称)的格式增加到例外配置文件:ecology\WEB-INF\prop\cacheBackList.properties中,然后再使用重启加载配置,使其生效。
- 如果客户二次开发中存在非RecordSet(系统标准sql操作类)类修改数据库里的表,也需要将该表名按注意事项2的方式操作,将其加入例外配置文件中。
- 如果客户二次开发中还存在调用自己新建的存储过程,视图,函数(方法)。也需要将存储过程,视图,函数(方法)中涉及到的表名加入到例外配置文件中ecology\WEB-INF\prop\cacheBackList.properties。然后再使用重启加载配置,使其生效。
- 集群环境,如果开启sql缓存,必须所有节点全部开启,关闭也必须所有节点同时全部关闭,否则必然存在缓存不同步问题
6.6 远程调试
该方案只能在测试或者开发环境中使用,不允许在生产环境中使用!切记 !!!
配置JVM
远程调试参数: resin\conf\resin.properties
# 初始配置
jvm_args : -Xmx5550m -Xms5550m -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+DisableExplicitGC -javaagent:wagent.jar
# 修改后的配置
jvm_args : -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9090 -Xmx5550m -Xms5550m -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+DisableExplicitGC -javaagent:wagent.jar
总结:更为详细的配置过程参考: https://www.jianshu.com/p/4c657bf02cb3