E9 二开文档
@Author: 福州 ebu 杨文杰
1. 前端开发根底
1.1 ECMAScript6 的应用
ECMAScript 和 JavaScript 的关系是:前者是后者的规格,后者是前者的一种实现。
ES6 既是一个历史名词,也是一个泛指,含意是 5.1 版当前的 JavaScript 的下一代规范,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年公布的正式版本的语言规范。本书中提到 ES6 的中央,个别是指 ES2015 规范,但有时也是泛指“下一代 JavaScript 语言”。
1.2 ES6 常见的语法
1.2.1 let 和 const 命令
ES6 新增了 let
命令,用来申明变量。它的用法相似于 var
,然而所申明的变量,只在let
命令所在的代码块内无效。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
const
申明一个只读的常量。一旦申明,常量的值就不能扭转。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
1.2.2 变量的解构赋值
ES6 容许依照肯定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
如果解构不胜利,变量的值就等于undefined
- 数组的解构赋值
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [, , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
- 对象的解构赋值
// 常见用法
let {bar, foo, baz} = {foo: 'aaa', bar: 'bbb'};
foo // "aaa"
bar // "bbb"
baz // undefined
// foo 是匹配的模式,baz 才是变量。真正被赋值的是变量 baz,而不是模式 foo
let {foo: baz} = {foo: 'aaa', bar: 'bbb'};
baz // "aaa"
foo // error: foo is not defined
// 嵌套应用
let obj = {
p: [
'Hello',
{y: 'World'}
]
};
// 第一个 p 作为变量,则进行赋值,第二个 p 作为模式,不会进行赋值
let {p, p: [x, { y}] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
1.2.3 … 运算符
- 函数
rest
参数的应用
function f(a, ...b) {console.log(a, b)
}
f(1,2,3,4,5) // 1 [2,3,4,5]
- 数组拆解
const a = [1,2,3]
const b = [4,5,6]
const c = [...a, ...b]
c // [1,2,3,4,5,6]
- 对象拆解
const obj = {a: 111, b:222}
const copyObj = {...obj, c: 333}
copyObj // {a: 111, b:222, c: 333}
1.2.4 函数的扩大
- 参数默认值:ES6 容许为函数的参数设置默认值,即间接写在参数定义的前面。
// 根本用法
function log(x, y = 'World') {console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
// 参数默认值能够与解构赋值的默认值,联合起来应用。function foo({x, y = 5}) {console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
- 箭头函数:ES6 容许应用(
=>
)定义函数。
// 根本用法
var f = v => v;
// 等同于
var f = function (v) {return v;};
// 箭头函数能够与变量解构联合应用。const full = ({first, last}) => first + ' ' + last;
// 等同于
function full(person) {return person.first + ' ' + person.last;}
// rest 参数与箭头函数联合
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5) // [1,2,3,4,5]
const headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5) // [1,[2,3,4,5]]
1.3 React 根底
React 是一个用于构建用户界面的
Javascript
库。React 次要用于构建 UI,很多人认为 React 是 MVC 中的 V(视图)。
React 起源于 Facebook 的外部我的项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
React 领有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和应用它。
1.3.1 JSX 语法
JSX 即 Javascript XML,它是对 JavaScript 语法扩大。React 应用 JSX 来代替惯例的 JavaScript。你也能够认为 JSX 其实就是 JavaScript。当遇到 <
,JSX 就当 HTML 解析,遇到 {
就当 JavaScript 解析。
- 根本用法
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
// 等价于
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
-
在
JSX
中嵌入表达式申明了一个名为
name
的变量,而后在JSX
中应用它,并将它包裹在大括号中:
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
1.3.2 React 简略实例
class HelloMessage extends React.Component {render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="React" />,
document.getElementById('hello-example')
);
2. E-code 前端开发平台
举荐 OA 上的所有前端开发对立应用 ecode 进行代码开发治理。
Ecode 官网文档:https://e-cloudstore.com/ecod…
E9 技术站地址:https://e-cloudstore.com/e9/i…
组件库地址:http://203.110.166.60:8087/#/…
本章节是针对 ecode
文档的细节补充
2.1 流程开发
对于流程页面上的前端开发,倡议不要间接应用代码块进行开发!所有的开发代码对立在 ecode 平台上进行,而后在流程表单代码块中插入如下代码,或者应用 全局流程代码块整合 计划加载代码。
// 倡议在代码块中增加 代码在 ecode 中的文件门路,不便寻找对应的代码。// 默认分类 / 测试
ecodeSDK.load({// ${appId}泛值 ecode 平台中的文件夹主键 id
id: '${appId}',
noCss: true,
cb: function() {}
})
2.2 建模开发
2.2.1 布局代码块
建模布局页面的应用形式与流程基本一致,在代码块中加载代码块。不倡议应用全局流程代码块整合进行加载。
2.2.2 自定义按钮
后端利用核心 -> 建模引擎 -> 查问
任选一个查问页面 -> 自定义按钮 -> 右键 -> 新建
- 办法体中存在多行代码时,每个语句必须以
;
结尾;否则会报错! params
的值等于‘field1+field2+field3’
这个值是一个字符串id
指的是数据ID
自定义按钮实现成果
应用 ecode 进行代码治理
新建前置文件
index.js
并将办法挂到全局对象window.g
下(window.g 为自定义对象,能够任意)自定义按钮的配置如下:
留神:全局办法的参数中,最初一个参数示意以后行的数据 ID,如下例子所示
// ecode 中定义的办法
function test = (p1, p2, p3, id) => {console.log(p1, p2, p3, id)
}
window.g = {test}
// 自定义按钮配置
// javascript:window.g.test(1,2,3) => 输入 1 2 3 数据 ID
// javascript:window.g.test(1,2,3, '任意值') => 输入 1 2 3 数据 ID
2.2.3 页面扩大
后端利用核心 -> 建模引擎 -> 模块
任选一个模块 -> 页面扩大 -> 右键 -> 新建
-
扩大用处:卡片页面、查问列表(批量操作)、卡片页面和查问列表
- 卡片页面:能够设置页面扩大显示在卡片信息页面,能够抉择在新建页面、编辑页面、查看页面显示页面扩大。
- 查问列表(批量操作):设置在查问列表时,则会在援用该模块的查问列表的批量操作中显示页面扩大项,在批量操作中勾选后会在前台列表中显示对应的页面扩大项。
- 卡片页面和查问列表:能够设置页面扩大项既显示在对应的卡片页面又显示在查问列表(批量操作)中。
javascript:test()
: 该办法能够在建模引擎 -> 查问 -> 该模块的查问列表 -> 编辑代码块
中定义
前端按钮测试如下
- 页面扩大同样能够配置
ecode
应用,将 链接指标地址 改成:javascript: window.g.test()
即可,倡议这样做,不便后续代码保护。
3. 后端开发
E9 后端开发详见:https://e-cloudstore.com/e9/f…
本章节是针对笔记中未形容的开发点进行补充!
3.1 JavaWeb 我的项目搭建
- 应用
Idea
创立一个Java
我的项目 - 增加
jar
依赖:File -> Project Structure -> Project Settings -> Libraries
须要增加的 ecology
的依赖门路有: ecology/WEB-INF/lib
; resin/lib
; ecology/classbean
;
其中 classbean
是必须要引入的, 其余两个按需引入
- 编译
Java
文件将编译后的class
文件放入ecology/classbean/
目录下即可
3.2 Maven 我的项目搭建
- 将
ecology/classbean
打成jar
包,进入ecology/classbean
目录,执行以下命令
命令:jar -cvf ecology-[版本号].jar .
例如:jar -cvf ecology-9.1909.04.jar .
- 将
ecology-9.1909.04.jar
退出到本地 maven 仓库
// -Dfile 参数指的是 jar 的门路
mvn install:install-file -DgroupId=com.weaver -DartifactId=ecology -Dversion=9.1909.04 -Dpackaging=jar -Dfile=ecology-9.1909.04.jar
- 创立
maven
我的项目,并在POM.xml
中配置如下
<dependency>
<groupId>com.weaver</groupId>
<artifactId>ecology</artifactId>
<version>9.1909.04</version>
</dependency>
3.3 自定义 Java 接口
3.3.1 流程节点前后附加操作
在节点前后附加操作中可设置接口动作,实现流程自定义附加操作
接口动作标识不能反复;接口动作类文件必须是类全名,该类必须实现接
weaver.interfaces.workflow.action
办法public String execute(RequestInfo request)
代码参考:
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;}
}
接口配置:
后端利用核心 -> 流程引擎 -> 门路治理 -> 门路设置
任选一个流程 -> 流程设置 -> 节点信息
任选一个节点 -> 节点前 / 节点后附加操作
3.3.2 建模页面扩大接口
页面扩大 -> 接口动作 -> 自定义接口动作
执行页面扩大的后续操作,通过配置自定义
Java
接口动作类实现。接口动作类文件必须是类全名。该类必须继承
weaver.formmode.customjavacode.AbstractModeExpandJavaCode
办法public void doModeExpand(Map param)
参考代码如下:
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();
//------ 请在上面编写业务逻辑代码 ------
}
}
}
}
接口配置:
后端利用核心 -> 建模引擎 -> 模块
任选一个模块 -> 页面扩大 -> 任选一个扩大名称 -> 接口动作 -> 点击
+
号 -> 自定义接口动作
3.3.3 打算工作接口
通过配置自定义
Java
接口的实现类,定时执行相应的代码
- 依照设定的工夫定时执行工作,打算工作标识不能反复
- 打算工作类必须是类的全名,该类必须继承
weaver.interfaces.schedule.BaseCronJob
类, 重写办法public void execute() {}
- 工夫格局按
Cron
表达式的定义
参考代码如下:
import weaver.interfaces.schedule.BaseCronJob;
public class CronTemplate extends BaseCronJob {
@Override
public void execute() {//------ 请在上面编写业务逻辑代码 ------}
}
配置:后端利用核心 -> 集成核心 -> 打算工作 -> 工作列表 -> 新建
通过打算工作列表的每个打算工作的自定义按钮,能够对每个工作进行状态操作,具体应用如下所示
状态详解:
- 启用: 打算工作将依据 Cron 表达式执行;
- 禁用: 打算工作将不再执行,重启服务也不会再次执行;
- 暂停: 针对打算工作进行进行,重启服务将恢复正常状态;
- 复原: 针对暂停状态的打算工作进行复原,复原后打算工作将继续执行;
- 执行: 单次执行打算工作,不影响 Cron 表达式周期执行;
- 测试: 查看填写的打算工作类是否符合规范(继承 weaver.interfaces.schedule.BaseCronJob 类, 重写办法 public void execute() {})
3.3.4 自定义按钮接口
通过配置自定义
Java
类,判断自定义按钮在查问列表中是否显示
参考代码如下:
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,则 受控按钮 不显示
3.4 Rest Api 接口
E-cology9 相比 E8,减少了 rest api 接口定义框架
jersey
,应用该框架能够很不便的应用 http 申请进行相干开发。
3.4.1 凋谢接口
- 流程表单数据接口:https://www.evernote.com/l/Au…
- 流程代办列表接口:https://www.evernote.com/l/Au…
- 流程列表数据接口:https://www.evernote.com/l/Au…
3.4.2 自定义 Api 接口
创立 com.api.demo.web.TestActionApi.java
package com.api.demo.web;
import com.demo.jack.web.TestAction;
import javax.ws.rs.Path;
@Path("/demo/test")
public class TestActionApi extends TestAction {}
创立 com.demo.jack.web.TestAction
package com.demo.jack.web;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
public class TestAction {
@GET
@Path("/hello")
@Produces(MediaType.TEXT_PLAIN)
public String hello() {return "hello weaver!";}
}
启动 resin
,关上浏览器输出:http://127.0.0.1/api/demo/test/hello
输入:hello weaver!
3.5 我的项目构造和代码标准
每个人都有属于本人的一套开发标准,以下是一套相对而言比拟标准的二开标准,仅供参考
3.5.1 应用 Weaverboot-E9(beta)
Weaverboot-E9 提供一套残缺的 IOC + AOP 的解决方案。能够很疾速的代理所有的接口。同时提供相似与 spring 的依赖注入的写法。不便疾速编码。该计划目前属于内测版,但举荐应用!
官网文档:https://www.e-cloudstore.com/…
ecology/WEB-INF/web.xml
退出以下配置
<servlet>
<servlet-name>WeaIocInitServlet</servlet-name>
<servlet-class>com.weaverboot.frame.ioc.prop.init.WeaIocInitServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>WeaIocInitServlet</servlet-name>
<url-pattern>/weaIoc/init</url-pattern>
</servlet-mapping>
<filter>
<filter-name>WeaComponentFilter</filter-name>
<filter-class>com.weaverboot.frame.ioc.filter.WeaComponentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WeaComponentFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
ecology/WEB-INF/prop/
中创立weaverboot.properties
# 扫包门路
scanPackage=com.demo.jack.**
ecology/WEB-INF/lib/
下增加Weaverboot-E9.jar
(暂不提供下载链接,想体验找云商店)
3.5.2 基于 weaverboot 的代码分层
临时不对数据层进行拆分,未对
mybatis
进行整合。间接应用 RecordSet 对数据库进行操作即可!
- 接口层
package com.demo.jack.web;
import com.demo.jack.service.TestService;
import com.weaverboot.frame.ioc.anno.fieldAnno.WeaAutowired;
import lombok.extern.slf4j.Slf4j;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Slf4j(topic = "debug")
public class TestAction {
@WeaAutowired
private TestService testService;
@GET
@Path("/get")
@Produces(MediaType.TEXT_PLAIN)
public String testGet() {log.debug("{}", testService.getMessage());
return testService.getMessage();}
}
- 服务层
package com.demo.jack.service;
public interface TestService {String getMessage();
}
package com.demo.jack.service.impl;
import com.demo.jack.service.TestService;
import com.weaverboot.frame.ioc.anno.classAnno.WeaIocService;
@WeaIocService
public class TestServiceImpl implements TestService {
@Override
public String getMessage() {return "this is test service impl ret list";}
}
3.5.3 应用 Fast-boot
FastBoot
框架是针对e9
二次开发中常见的业务场景的实现性能,进行代码抽离封装。奉行规范化,简单化,快速化,高复用的准则。其次要实现了数据库,文档文件,流程,建模,数学,流水号生成等相干工具类,从而简化开发过程,标准开发代码,进步开发效率!
文档详见:https://fzebu_code.gitee.io/c…
3.5.4 应用 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;
}
3.5.5 日志框架的应用
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
3.6 数据库操作
3.6.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);
3.6.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.1 缓存 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.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=
4.3 SQL 缓存注意事项
Ecology 平台具备数据缓存机制,在我的项目中尽量不要间接对数据库进行操作,如执行存储过程或者触发器。如果切实没有方法则须要进行 Sql 缓存配置(切记!)
- 原则上禁止通过非程序渠道间接批改 oa 数据库数据。如果肯定要批改,请批改完数据后,chrome 浏览器拜访 /commcache/cacheMonitor.jsp 界面,点击重启加载配置。这样操作批改的数据能够及时失效。
- 如果存在第三方程序修改 oa 数据库的表,则须要将会批改的表的名称以(名称 = 名称)的格局减少到例外配置文件:ecology\WEB-INF\prop\cacheBackList.properties 中,而后再应用重启加载配置,使其失效。
- 如果客户二次开发中存在非 RecordSet(零碎规范 sql 操作类)类批改数据库里的表,也须要将该表名按注意事项 2 的形式操作,将其退出例外配置文件中。
- 如果客户二次开发中还存在调用本人新建的存储过程,视图,函数(办法)。也须要将存储过程,视图,函数(办法)中波及到的表名退出到例外配置文件中 ecology\WEB-INF\prop\cacheBackList.properties。而后再应用重启加载配置,使其失效。
- 集群环境,如果开启 sql 缓存,必须所有节点全副开启,敞开也必须所有节点同时全副敞开,否则必然存在缓存不同步问题
5. 异构零碎对接
5.1 接口白名单配置
对于 ecology 平台而言,所有的 rest api 接口都要通过登陆认证。否则不容许接入。在内网环境或者保障网络安全的状况下能够应用接口白名单配置。跳过登陆认证进行接口拜访。
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 认证
相比接口白名单,应用 token 进行接口拜访相对而言更加平安,其数据不易被抓取。
官网版本:https://e-cloudstore.com/e9/f…
集体版本:Token 认证
5.3 Webservice 的应用
Web Service 是一个平台独立的,低耦合的,自蕴含的、基于可编程的 web 的应用程序,可应用凋谢的 XML(规范通用标记语言下的一个子集)规范来形容、公布、发现、协调和配置这些应用程序,用于开发分布式的交互操作的应用程序
5.3.1 白名单配置
如果发现
http://oa 地址 /services/
无法访问,将须要调用该 WEBSERVICE 的 IP 增加到白名单中
在 ecology/WEB-INF/securityXML/weaver_security_custom_rules_1.xml
中的 <root>
节点上面增加相似如下的节点(IP 能够指定某个网段,也能够指定具体 IP):
<webservice-ip-list>
<ip>80.16.</ip>
</webservice-ip-list>
5.3.2 应用 Http 调用 Webservice
WebService 的相干办法能够通过应用 http 传输对应的 xml 字符串进行调用。能够应用
SoapUI
解析webservice
接口信息
- SoapUI 介绍和应用形式能够参考博文:https://www.cnblogs.com/yatou…
- 软件下载:https://pan.baidu.com/s/1j36r…,提取码: 8v9x
应用
HttpClient
以xml
报文模式调用webservice
服务
申请报文能够通过 SoapUI
查看
- 代码测试,其中
com.fzebu.fastboot.util.WebServiceUtil
源码地位:WebServiceUtil
package com.fzebu.fastboot.util;
import com.fzebu.fastboot.ws.WebServiceUtil;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import java.io.IOException;
import java.util.List;
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 = WebServiceUtil.execute(url, xml);
Element rootElement = WebServiceUtil.getRootElement(resp);
List<Element> elements = WebServiceUtil.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());
}
}
}
5.4 单点登录
第三方通过 URL 前面拼接
?ssoToken=
参数,容许间接关上 oa 页面官网文档:https://e-cloudstore.com/e9/f…
6. 其余开发
6.1 近程调试
该计划只能在测试或者开发环境中应用,不容许在生产环境中应用!切记!!!
配置 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/4c6…
6.2 本地代码调试
对于本地代码调试这块,上面介绍两种计划
- 传统调试计划
个别状况下,须要启动 e9 服务,而后编写 xx.jsp
进行代码测试,而后看输入的日志进行后果查看。这种调试形式有个缺点,就是须要启动 e9 服务,极大的耗费了电脑资源,并且应用日志的形式进行后果查看效率升高很多。
- 基于
junit
的代码调试
public class TestApp {
@Before
public void startEnv() {
// 本地 e9 的 demo 门路
String rootPath = "D:\\Weaver\\ecology\\";
GCONST.setRootPath(rootPath);
GCONST.setServerName("ecology");
}
@Test
public void test() {RecordSet rs = new RecordSet();
rs.executeQuery("select * from hrmresource");
// 一顿操作
}
}
阐明:能够不应用 junit
,间接在main
中进行测试也是 ok 的,然而你必须要有startEnv()
办法外面的三句话,否则有效!
public static void main(String[] args) {
String rootPath = "D:\\Weaver\\ecology\\";
GCONST.setRootPath(rootPath);
GCONST.setServerName("ecology");
// 一顿操作
}
6.3 容器化部署
具体部署材料:http://fzebu_code.gitee.io/cl…