Commit c8ca7352 authored by Jefferyne's avatar Jefferyne

Track 52 files into repository.

- modified .gitignore
- untracked assets/cors-login.png
- untracked assets/cors-login1.png
- untracked assets/cors-quer1.png
- untracked assets/cors-query.png
- untracked assets/cors-web.png
- untracked assets/docxGen.png
- untracked assets/docxGenEntry.png
- untracked assets/excelImport.png
- untracked assets/generator2.png
- untracked assets/generator3.png
- untracked assets/generator4.png
- untracked assets/generator5.png
- untracked assets/generator6.png
- untracked assets/gernerator7.png
- untracked assets/gnerator1.png
- untracked assets/paramDefine.png
- untracked assets/paramSet.png
- untracked assets/tempDefine.png
- untracked assets/tempUpload.png
- untracked common-field-javascript.md
- untracked common-mask-javascript.md
- untracked common-window-javascript.md
- untracked docx4j.md
- untracked front-kendoui.md
- untracked jad.md
- untracked mutillanguage.md
- modified project-create.md
- modified 前端组件/box.md
- modified 前端组件/HlsCombobox.md
- removed 前端组件/hlsDataSource
- modified 前端组件/HlsDatePicker.md
- modified 前端组件/hlsMaskedTextBox.md
- modified 前端组件/hlsTextArea.md
- modified 前端组件/hlsTlEdit.md
- untracked 后端开发/3.24-cors.md
- modified 后端开发/activiti-helper.md
- modified 后端开发/activiti.md
- modified 后端开发/activiti_demo.md
- modified 后端开发/activiti_editor_helper.md
- untracked 后端开发/audit.md
- untracked 后端开发/code-generator.md
- modified 后端开发/deployment.md
- untracked 后端开发/elk.md
- untracked 后端开发/lock.md
- untracked 后端开发/RabbitMq消息队列.md
- untracked 后端开发/redis.md
- untracked 后端开发/user-security-strategy.md
- removed 后端开发/消息机制.md
- untracked 框架功能描述/deployment.md
- modified 框架功能描述/计划任务.md
- modified 融租易开发手册.md

Auto commit by GitBook Editor
parent 6e9e80d4
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
_book/
book.pdf
book.epub
book.mobi
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
_book/
book.pdf
book.epub
book.mobi
###字段级通用方法
#### 设置字段必填属性
>Hel.setRequired(id,flag);
第一个参数id为字段的id,第二个参数flag为true时将该字段设为必填,为false时将该字段设为非必填。
```html
<h:hlsMaskedTextBox id="maskedTextBox"/>
<script><![CDATA[
Hel.setRequired('maskedTextBox',true);
]]></script>
```
#### 设置字段只读属性
>Hel.setReadonly(id,flag);
第一个参数id为字段的id,第二个参数flag为true时将该字段设为只读,为false时将该字段设为可输入。
```html
<h:hlsMaskedTextBox id="maskedTextBox"/>
<script><![CDATA[
Hel.setReadonly('maskedTextBox',true);
]]></script>
```
### 锁屏和解屏
#### 锁屏
>Hel.mask(opts);
其中参数为:
属性名 | 类型 | 含义
-------- | ----- | -----
id| String(选填) | 当前窗口的id,缺省值为body
```
Hel.mask(); //缺省值为body
Hel.mask({
id:'',//传入相应的参数
});
```
#### 解屏
```
Hel.unmask();
```
### 窗口级通用方法
#### 打开功能窗口
>Hel.openMenuWindow(opts);
其中参数为:
属性名 | 类型 | 含义
-------- | ----- | -----
menuCode | String(必填) | 菜单代码
title | String(必填) | 窗口描述
url | Object(必填) | 窗口地址
menuName | String| 菜单名称
moduleFlag| String | 模块标志(Y/N)
documentKey| String | 打开同一个功能时,识别不同单据的唯一标识
reflashFlag| String | 关闭该功能菜单时是否刷新父级标志(Y/N)
moduleName| String | 模块名称
```
Hel.openMenuWindow({
menuCode:'',//传入相应的参数
title:'',
url:'',
menuName:'',
moduleFlag:'',
documentKey:'',
reflashFlag:'',
moduleName:''
});
```
#### 关闭功能窗口
>Hel.closeMenuWindow(opts);
其中参数为:
属性名 | 类型 | 含义
-------- | ----- | -----
menuCode | String(必填) | 菜单代码
```
Hel.closeMenuWindow({
menuCode:''//传入相应的参数
});
```
#### 打开右弹窗口
>Hel.openBarWindow(opts);
其中参数为:
属性名 | 类型 | 含义
-------- | ----- | -----
id| String(必填) | 打开窗口的div的id
url| String(必填) | 窗口的url地址
size| String(必填) | 右弹窗口的宽度(THIRD/HALF/FULL/任意px像素)
```
<div id="win"></div>
<script><![CDATA[
Hel.openBarWindow({
id:'win',
content:'',
size:''
});
]]></script>
```
#### 打开抽屉
> Hel.openBox()函数接收一个对象,对象属性含义如下:
属性名 | 类型 | 含义
-------- | ----- | -----
winId | String(必填) | html元素标签的id
functionCode | String(必填) | 抽屉的唯一标识,用以确定该抽屉的相关信息
params | Object | 需传递的参数,将自动拼到url上
closeFunction | function | 关闭抽屉调用的方法
width | Number | 自定义宽度
```javascript
function test(e){
...
}
Hel.openBox({
winId:"domId",
functionCode:"CSH200B",
params:{
contractId:1327,
conContractCashflowId:4321
},
closeFunction:test,
width:900
});
```
##### 注意:
> - winId和functionCode必须有字符串类型的值
> - params可以没有,表示不拼接条件;
> - closeFunction可以没有,表示关闭抽屉不进行操作
> - width可以没有,表示使用配置的宽度
## 合同文本生成
合同文本生成允许用户定义不同内容的合同模板,通过各项数据配置,对其中的文字、表格、水印以及批注等内容进行修改。
1. 进入合同模板定义功能
先在表格中定义好模板的代码、名称、用途、类型以及说明等信息进行保存。
| 字段名 | 说明 |
| :--: | :--: |
| 模板代码 | 模板的唯一标示,不可重复 |
| 模板名称 | 用于标识一个文件模板 |
| 模板用途 | 暂时只有*合同文本*一种用途 |
| 模板类型 | |
| 说明 | 文件模板的说明 |
| 参数集 | 参与该模板生成时,所需要使用到的参数 |
![合同模板定义功能](/assets/tempDefine.png)
2. 上传合同模板
通过合同模板定义功能表格中的**模板上传**功能,将预先设计好的模板上传到服务器。**该功能只支持2007版本以上的,docx后缀的word文档**
![合同模板上传](/assets/tempUpload.png)
3. 定义合同模板参数
通过合同模板定义功能表格中**参数集**设置,将需要与合同模板关联的参数进行设置。
![合同模板参数集设置](/assets/paramSet.png)
- 若是有在已有的参数列表中没有需要的使用参数设置,则可以在参数集配置中手动添加
- 参数定义中的字段说明
> | 字段名 | 说明 |
> | :--: | :--: |
> | 书签代码 | 书签的唯一标识,需要和模板文件中定义的书签名称一致 |
> | 书签描述 | 对该书签的文字描述 |
> | 书签类型 | 书签类型目前有:文本、表单、表单域、横向表单四种 <ul><li>文本类型对应的是替换后为文档中普通文本 </li><li>表单类型对应着word文档中一个表格的数据,表格的每一列的配置信息都是在列配置中完成</li><li>表单域对应word文档中一个文本域\窗体域,最终的替换结果会显示在文本域的位置</li><li>横向表单对应的是替换的文档中,书签所对应的是文档中一整块内容,内容中可能包含了多个书签,每个书签的名称在列配置中进行配置,最后根据结果生成多块结构类似的文本</ul> |
> | 数据源 | 选择该参数的值来源,配置该项之后可不用配置SQL |
> | SQL | 从数据库进行数据查询使用的SQL语句 |
> | 列配置 | 只有在书签类型为表单或横向表单时,才可以进行配置 |
> | 字体型号 | 生成的文本使用的字体类型 |
> | 字体大小 | 生成的文本使用的字体大小 |
> | 下划线 | 生成的文本使用的下划线类型:无、单下划线、双下划线 |
> | 是否加粗 | 生成的文本是否需要加粗 |
![合同模板参数定义](/assets/paramDefine.png)
4. 去生成最终的合同文本
生成合同文本时在合同状态为**新建**时才可以生成合同文本
![合同文本生成入口](/assets/docxGenEntry.png)
![合同文本生成](/assets/docxGen.png)
\ No newline at end of file
# 前端开发手册 \(1.0\)
#### 开发说明
前端界面采用kendoUI开源框架,相同的显示效果可以采用多种代码方式来实现,为了规范项目组成员的代码样式,统一编码风格,避免以后维护的工作量,融租易采用封装标签的开发方式,当然如果在项目开发过程中遇到比较复杂的页面通过标签的方式实现不了的功能,需要跟产品组报备,然后采取源生的开发方式。
## Excel文件导入
使用poi解析xml方式进行导入,只支持单表导入。
数据库批量导入默认batch为100.
支持多sheet导入,sheet1从第三行开始读取数据,其他sheet页从第一行导入数据。
使用方法:
1. Excel文件上传
```javascript
$("#files").kendoUpload({
async: {
saveUrl: "${base.contextPath}/hls/excel/import?${_csrf.parameterName}=${_csrf.token}&templateCode=HLS_FIN_STATEMENT_LN",
removeUrl: "remove"
},
showFileList: false,
upload: onUpload,
success: onSuccess
});
```
| 参数名 | 描述 |
| :--: | :--: |
| _csrf.token | 防跨域token |
| templateCode | 作为模块标识,会出现先在fnd_interface_header中,供后续业务逻辑进行处理 |
2. 处理数据
经过第一步的文件上传,后台会在fnd_interface_header表中生成一条数据,其中包含了传递的templateCode参数作为code,并且,在fnd_interface_lines表中按照顺序,将Excel文件中的每一行数据保存为一条数据,数据的第一列保存在attributes_1上,依次类推。
![ExcelImport](/assets/excelImport.png)
## Excel文件批量导出
本系统支持Excel文件导出排队处理,支持超大数据量,能实时查询文件生成情况,支持取消导出。
#### 前端使用方法
调用js方法
```javascript
Hel.exportExcel=function(opts){
var contextPath =opts.contextPath,
id=opts.id,
view_model=opts.viewModel,
viewModelData=opts.viewModelData,
controller_name = opts.controller,
fileName = opts.fileName,
temp_div_id=opts.tempDivId,
dataSourceId = opts.dataSourceId,
_csrf_token = opts._csrf_token;
```
| 参数名 | 描述 |
| :--: | :--: |
| contextPath | 项目的url路径 |
| id | 页面上grid的id |
| view_model | grid绑定的model数据模型 |
| viewModelData | model模型中的数据 |
| controller_name | 后端控制器名称 |
| fileName | excel文件名 |
| temp_div_id | 临时div的id |
| dataSourceId | 数据源id |
| _csrf_token | 防跨域的token |
#### 后端使用方法
1. 在代码中注入Excel导出Service
```java
@AutoWired
private ExcelExportServiceImpl excelService;
```
2. 在自己的controller中调用方法
```java
excelService.saveExportInfo(sqlId,iRequest,config,rowMaxNumber);
```
| 参数名 | 类型 | 描述 |
| :--: | :--: | :--: |
| sqlId | String | mybatis对应的数据库查询语句的ID |
| iRequest | IRequest | 带有上下文信息的reuqest对象 |
| config | ExportConfig | 包含Excel各列信息的对象,由前台传递json对象转换而来 |
| rowMaxNumber | int | 生成Excel的最大行数,可使用重载方法,不包含次参数默认为1,000,000 |
3. 运行Excel导出程序(获取该程序,请联系部门相关负责人)
```shell
java -jar hel-batch-parent.jar &
```
4. Excel导出情况查询
用户若是为管理员身份,则查询全部人的导出情况,否则只显示当前用户的导出数据
```java
excelService.queryExportInfo(status, iRequest);
```
| 参数名 | 类型 | 描述 |
| :--: | :--: | :--: |
| status | String | 指定Excel导出的状态,该参数可为空查询全部状态数据<br><ll><li>new->等待</li><li>generating->文件导出中</li><li>finished->导出完成</li><li>failed-> 导出失败</li></ll> |
| iRequest | IRequest | 带有上下文信息的reuqest对象 |
5. 下载已完成的Excel文件
```java
excel.downloadExcel(filePath, fileName, request, response);
```
| 参数名 | 类型 | 描述 |
| :--: | :--: | :--: |
| filePath | String | 需要下载的文件的路径 |
| fileName | String | 想要保存的文件的名称 |
| request | HttpServletRequest | 用户的请求对象 |
| response | HttpServletResponse | 用户的响应对象 |
**下载前,需要检查config.properties文件中的export.offerUrl配置项是否配置正确,且为hel-batch-parent.jar提供的文件下载url**
\ No newline at end of file
#界面多语言实现
界面中的多语言在数据库中统一存放在sys_prompts表中,系统启动时加载进redis缓存。界面中实现多语言是通过freemarker的标签以及对应的多语言code来实现。
例如:“确定”的多语言code是“sys.hand.btn.ok”在界面中的写法:
```html
<@spring.message "sys.hand.btn.ok"/>
```
# 新开项目
本章节将引导您从 零 开始,创建一个可以运行的 Demo 工程。此工程基于HEL融租易标准版,包含一个 demo 实例。
## 后端项目
### 确定项目信息
1. groupId 本项目的代号,比如融租易项目,代号为 hel
2. artifactId 本项目的顶层目录名称,使用项目代号(第一个字母大写)
+ Parent,如 HelParent
3. package 包名称,使用项目代号 + core ,如 Hel.core
4. archetypeVersion 是指模板项目的版本,可以使用以下版本号
> 1.0-RELEASE
### 新建项目 (融租易HEL1.0)
确定上述信息后,可以使用如下命令新建项目:
`注意` maven-archetype-plugin `2.4`及以下版本可以正常使用下面命令
```
mvn archetype:generate -D archetypeGroupId=hel-custom -D archetypeArtifactId=hel-custom-parent-archetype -D archetypeVersion=1.0-RELEASE -D groupId=hel -D artifactId=HelParent -D package=Hel.core -D version=1.0-RELEASE -D archetypeRepository=https://hel.hand-china.com/nexus/content/repositories/Hel-Releases
```
在maven-archetype-plugin `3.0.0` 版本中,移除了对-D archetypeRepository参数的支持,会报以下错误
![](/assets/mvn_archetype_plugin.png)
解决方案:
指定使用2.4版本的archetype-plugin插件:
```
mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -D archetypeGroupId=hel-custom -D archetypeArtifactId=hel-custom-parent-archetype -D archetypeVersion=1.0-RELEASE -D groupId=hel -D artifactId=HelParent -D package=Hel.core -D version=1.0-RELEASE -D archetypeRepository=https://hel.hand-china.com/nexus/content/repositories/Hel-Releases
```
> 以下内容所指的 `HelParent` 均指上面命令中的参数 `artifactId` 的值,请按实际情况替换
新的项目目录结构如下:
```
.
├── README.md (项目README,请在此处写上项目开发的注意信息,方便团队协同)
├── core(功能实现项目)
├── pom.xml (子项目core的pom.xml文件)
└── src
└── main
├── java
│ │ ├── hel
│ │ │ └── core(前面的包名称)
│ │ │ ├── db(数据表结构,数据初始化入口文件)
│ │ │ │ └── liquibase.groovy
│ │ └── resources(项目配置文件目录)
│ │ ├── spring (spring配置文件目录)
│ │ ├── config.properties
│ └── logback.xml(日志配置文件)
└── webapp(Webapp目录)
│ ├── resources(UI 资源库目录)
| | └── demo.js(demo.js样例,可以删除)
└── WEB-INF
└── view(页面文件目录)
│ └── demo.html(demo.html样例,可以删除)
├── core-db(数据库脚本及初始化数据项目)
├── pom.xml
└── src
└── main
└── java
└── hel
└── core
└── db
├── data(数据文件)
│ └── (init-data)
└── table(数据库表结构管理)
│ └── 2016-06-01-init-migration.groovy
└── pom.xml
```
### 确定本项目使用的数据库
> 目前已经测试过支持的数据库有Mysql,Oracle,SqlServer
> 请修改 `HelParent/core/src/main/java/hel/core/db/liquibase.groovy` 以适配不同的数据库
确定好数据库后,按照 [Oracle,MySql,Sqlserver数据库配置](database-config.md) 修改项目配置文件。
修改配置文件后,按照[创建数据库](database-init.md) 中的步骤创建数据库
### 编译整个项目
在 HelParent 目录下执行:
```
mvn clean install -Dmaven.test.skip=true
```
<a name="liquibase"> </a>
### 初始化数据库表结构及基础数据
在 HelParent 项目录下执行:
- MySql
- `mvn process-resources -D skipLiquibaseRun=false -D db.driver=com.mysql.jdbc.Driver -D db.url=jdbc:mysql://127.0.0.1:3306/hel -Ddb.user=hel_dev -Ddb.password=hel_dev`
- SqlServer
- `mvn process-resources -D skipLiquibaseRun=false -Ddb.user=hel_dev -Ddb.password=hel_dev-D db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver -D db.url="jdbc:sqlserver://127.0.0.1:1433; DatabaseName=hel"`
- Oracle
- `mvn process-resources -D skipLiquibaseRun=false -D db.driver=oracle.jdbc.driver.OracleDriver -D db.url=jdbc:oracle:thin:@127.0.0.1:1521:hel -Ddb.user=hel_dev -Ddb.password=hel_dev
- 以上命令中,需要按实际情况修改 url,user,password
### 测试
1. 在 HelParent 工程目录下执行命令(IntelliJ IDEA 用户可以跳过此步骤)
```
mvn eclipse:eclipse
```
3. 将所有工程导入 IDE 工具(Eclipse,IntelliJ IDEA)中
4. 配置 Server ,配置 JNDI 数据源,参照[多数据库配置](chapter1.1.md)
- 需要 Tomcat 7+, 不支持 Tomcat 6
5. 将 hel 工程发布到 tomcat/webapps,运行
### 更新HEL依赖
[更新项目依赖的HEL版本](project-update.md)
# 新开项目
本章节将引导您从零开始,创建一个可以运行的 Demo 工程。此工程基于HEL融租易标准版,包含一个 demo 实例。
## 后端项目
### 确定项目信息
1. groupId 本项目的代号,比如融租易项目,代号为 hel
2. artifactId 本项目的顶层目录名称,使用项目代号(第一个字母大写) +Parent,如 HelParent
3. package 包名称,使用项目代号 + core ,如 Hel.core
4. archetypeVersion 是指模板项目的版本,可以使用以下版本号
> 1.0-RELEASE
### 新建项目 (融租易HEL1.0)
确定上述信息后,可以使用如下命令新建项目:
`注意` maven-archetype-plugin `2.4`及以下版本可以正常使用下面命令
```
mvn archetype:generate -D archetypeGroupId=hel-custom -D archetypeArtifactId=hel-custom-parent-archetype -D archetypeVersion=1.0-RELEASE -D groupId=hel -D artifactId=HelParent -D package=Hel.core -D version=1.0-RELEASE -D archetypeRepository=https://hel.hand-china.com/nexus/content/repositories/Hel-Releases
```
在maven-archetype-plugin `3.0.0` 版本中,移除了对-D archetypeRepository参数的支持,会报以下错误
![](/assets/mvn_archetype_plugin.png)
解决方案:
指定使用2.4版本的archetype-plugin插件:
```
mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -D archetypeGroupId=hel-custom -D archetypeArtifactId=hel-custom-parent-archetype -D archetypeVersion=1.0-RELEASE -D groupId=hel -D artifactId=HelParent -D package=Hel.core -D version=1.0-RELEASE -D archetypeRepository=https://hel.hand-china.com/nexus/content/repositories/Hel-Releases
```
> 以下内容所指的 `HelParent` 均指上面命令中的参数 `artifactId` 的值,请按实际情况替换
新的项目目录结构如下:
```
.
├── README.md (项目README,请在此处写上项目开发的注意信息,方便团队协同)
├── core(功能实现项目)
│ ├── pom.xml (子项目core的pom.xml文件)
└── src
└── main
├── java
│ ├── hel
│ │ │ └── core(前面的包名称)
│ │ │ ├── db(数据表结构,数据初始化入口文件)
│ │ │ │ └── liquibase.groovy
│ │ └── resources(项目配置文件目录)
│ │ ├── spring (spring配置文件目录)
│ │ ├── config.properties
│ │ └── logback.xml(日志配置文件)
└── webapp(Webapp目录)
├── resources(UI 资源库目录)
| | └── demo.js(demo.js样例,可以删除)
│ └── WEB-INF
└── view(页面文件目录)
└── demo.html(demo.html样例,可以删除)
├── core-db(数据库脚本及初始化数据项目)
│ ├── pom.xml
└── src
└── main
└── java
└── hel
└── core
└── db
├── data(数据文件)
│ └── (init-data)
└── table(数据库表结构管理)
└── 2016-06-01-init-migration.groovy
└── pom.xml
```
### 确定本项目使用的数据库
> 目前已经测试过支持的数据库有Mysql,Oracle,SqlServer
> 请修改 `HelParent/core/src/main/java/hel/core/db/liquibase.groovy` 以适配不同的数据库
确定好数据库后,按照 [Oracle,MySql,Sqlserver数据库配置](database-config.md) 修改项目配置文件。
修改配置文件后,按照[创建数据库](database-init.md) 中的步骤创建数据库
### 编译整个项目
在 HelParent 目录下执行:
```
mvn clean install -Dmaven.test.skip=true
```
<a name="liquibase"> </a>
### 初始化数据库表结构及基础数据
在 HelParent 项目录下执行:
- MySql
- `mvn process-resources -D skipLiquibaseRun=false -D db.driver=com.mysql.jdbc.Driver -D db.url=jdbc:mysql://127.0.0.1:3306/hel -Ddb.user=hel_dev -Ddb.password=hel_dev`
- SqlServer
- `mvn process-resources -D skipLiquibaseRun=false -Ddb.user=hel_dev -Ddb.password=hel_dev-D db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver -D db.url="jdbc:sqlserver://127.0.0.1:1433; DatabaseName=hel"`
- Oracle
- `mvn process-resources -D skipLiquibaseRun=false -D db.driver=oracle.jdbc.driver.OracleDriver -D db.url=jdbc:oracle:thin:@127.0.0.1:1521:hel -Ddb.user=hel_dev -Ddb.password=hel_dev`
- 以上命令中,需要按实际情况修改 url,user,password
### 测试
1. 在 HelParent 工程目录下执行命令(IntelliJ IDEA 用户可以跳过此步骤)
```
mvn eclipse:eclipse
```
3. 将所有工程导入 IDE 工具(Eclipse,IntelliJ IDEA)中
4. 配置 Server ,配置 JNDI 数据源,参照[多数据库配置](chapter1.1.md)
- 需要 Tomcat 7+, 不支持 Tomcat 6
5. 将 hel 工程发布到 tomcat/webapps,运行
### 更新HEL依赖
[更新项目依赖的HEL版本](project-update.md)
# hlsCombobox标签
ComboBox下拉列表组件,通常数据源以code - meaning形式出现,用户操作的是meaning,实际保存的是code。
```xml
<h:hlsCombobox name="hlsCombobox" id="hlsCombobox"
bind="enabled: isEnabled,source: sdDataSource, value:data.code"
colspan="3"
dataTextField="meaning"
dataValueField="code"
placeholder="hlsCombobox"
prompt="hlsCombobox"
promptColspan="1"
valuePrimitive="true"
style="width:100%"
/>
```
### **主要属性**
属性名 | 类型
-------- | ---
id | String
promptColspan | String
promptClassName | String
promptImage | String
prompt | String
colspan | String
hlsClassName | String
animation | Boolean
autoBind | Boolean
autoWidth | Boolean
cascadeFrom| String
cascadeFromField | String
clearButton| Boolean
dataSource | DataSource
dataTextField | String
dataValueField| String
delay | Number
enable | Boolean
enforceMinLength| Boolean
filter | String
fixedGroupTemplate| String \| Function
footerTemplate| String \| Function
groupTemplate| String \| Function
height| Number
highlightFirst| Boolean
ignoreCase| Boolean
index| Number
minLength| Number
noDataTemplate| String \| Function
placeholder | String
suggest| Boolean
headerTemplate| String \| Function
template| String \| Function
text| String
value| String
valuePrimitive| Boolean
virtual| Boolean
change| Function
close| Function
dataBound | Function
filtering| Function
open| Function
select| Function
cascade| Function
> 属性用法请参考KendoUI文档 [http://docs.telerik.com/kendo-ui/api/javascript/ui/combobox](http://docs.telerik.com/kendo-ui/api/javascript/ui/combobox)
### **用法示例*
```javascript
//对于系统定义好的数据源
<script src="${base.contextPath}/common/code?sdDataSource=FND.CODING_RULE_TYPE" type="text/javascript"></script>
//自定义静态数据源sdDataSource
var sdDataSource= new kendo.data.DataSource({
data:["one","two"]
});
//自定义动态查询到的数据源sdDataSource
$.ajax({
type : 'GET',
url : url,
async: false,
contentType : "application/json; charset=utf-8",
success : function(datas) {
viewModel.set("sdDataSource,",datas.rows);
}
});
//需要注意,bind中写dataSource,需要在viewModel中定义
var viewModel = kendo.observable({
enabled: true,
isEnabled: false,
data: {},
sdDataSource:sdDataSource,
});
//下拉列表渲染函数
var itemTemplate = function (){
//...
}
//选中某一个列表选项后触发
function onComboboxChange(e){
var value = this.value();
// Use the value of the widget.
}
//以上两种函数定义方法皆可
```
```xml
<h:hlsCombobox name="hlsCombobox" id="hlsCombobox"
bind="enabled: isEnabled,source: sdDataSource, value:data.code"
colspan="3"
dataTextField="meaning"
dataValueField="code"
placeholder="hlsCombobox"
prompt="hlsCombobox"
promptColspan="1"
valuePrimitive="true"
style="width:100
change="onComboboxChange"
template="fn:itemTemplate"
/>
```
#### 特别注意 valuePrimitive="true" 如果该值为false,选中的值将不会匹配给dataTextField和dataValueField
dataValueField
> **提示:** 某些属性类型为**String \| Function** 时,表明它可以定义为字符串或者函数。因此当需要定义为Function时需要增加前缀**fn:**来区分(例如函数template="fn:itemTemplate" )
# hlsCombobox标签(下拉框)
ComboBox下拉列表组件,通常数据源以code - meaning形式出现,用户操作的是meaning,实际保存的是code。
```xml
<h:hlsCombobox name="hlsCombobox" id="hlsCombobox"
bind="enabled: isEnabled,source: sdDataSource, value:data.code"
colspan="3"
dataTextField="meaning"
dataValueField="code"
placeholder="hlsCombobox"
prompt="hlsCombobox"
promptColspan="1"
valuePrimitive="true"
style="width:100%"
/>
```
### **主要属性**
属性名 | 类型 | 说明 |
--- | --- | --- |
id | String | id |
promptColspan | String | 提示语句需要占几列 |
promptClassName | String | 提示语句类名,默认不填 |
promptImage | String | 提示前面的文字图形,默认不填 |
prompt | String | 提示信息 |
colspan | String | 所占列数 |
hlsClassName | String | 类名,默认不填 |
dataSource | DataSource | 数据源 |
dataTextField | String | 返回的显示描述字段 |
dataValueField| String | 绑定的英文字段 |
enable | Boolean | 是否可编辑,true为可编辑,false不可编辑 |
placeholder | String | 占位符 |
template| Function| 渲染函数 |
change| Function | 数据变化时触发的函数 |
> 属性用法请参考KendoUI文档 [http://docs.telerik.com/kendo-ui/api/javascript/ui/combobox](http://docs.telerik.com/kendo-ui/api/javascript/ui/combobox)
### **用法示例*
```javascript
//对于系统定义好的数据源
<script src="${base.contextPath}/common/code?sdDataSource=FND.CODING_RULE_TYPE" type="text/javascript"></script>
//自定义静态数据源sdDataSource
var sdDataSource= new kendo.data.DataSource({
data:["one","two"]
});
//自定义动态查询到的数据源sdDataSource
$.ajax({
type : 'GET',
url : url,
async: false,
contentType : "application/json; charset=utf-8",
success : function(datas) {
viewModel.set("sdDataSource,",datas.rows);
}
});
//需要注意,bind中写dataSource,需要在viewModel中定义
var viewModel = kendo.observable({
enabled: true,
isEnabled: false,
data: {},
sdDataSource:sdDataSource,
});
//下拉列表渲染函数
var itemTemplate = function (){
//...
}
//选中某一个列表选项后触发
function onComboboxChange(e){
var value = this.value();
// Use the value of the widget.
}
//以上两种函数定义方法皆可
```
```xml
<h:hlsCombobox name="hlsCombobox" id="hlsCombobox"
bind="enabled: isEnabled,source: sdDataSource, value:data.code"
colspan="3"
dataTextField="meaning"
dataValueField="code"
placeholder="hlsCombobox"
prompt="hlsCombobox"
promptColspan="1"
valuePrimitive="true"
style="width:100
change="onComboboxChange"
template="fn:itemTemplate"
/>
```
#### 特别注意 valuePrimitive="true" 如果该值为false,选中的值将不会匹配给dataTextField和dataValueField
dataValueField
> **提示:** 某些属性类型为**String \| Function** 时,表明它可以定义为字符串或者函数。因此当需要定义为Function时需要增加前缀**fn:**来区分(例如函数template="fn:itemTemplate" )
# hlsDatePicker标签
日期选择器(不包含时分秒)
xml配置
```
<!--基本用法-->
<h:hlsDatePicker id="hlsDatePickerId" bind="enabled:isEnabled,value:data.value" placeholder="hlsDatePicker"/>
```
### **主要属性**
属性名 | 类型
-------- | -----
promptColspan | Integer
promptClassName | String
promptImage | String
prompt | String
colspan | Integer
hlsClassName | String
animation | Boolean
ARIATemplate| String
culture | String
depth| String
footer| String & Function
format| String
max| String
min| String
start| String
value | String
name | String
bind | Function
required | Boolean
change | Function
close| Function
open | Function
placeholder | String
interval |Integer
timeFormat |String
> **提示:** 属性用法请参考kendoui API文档 http://docs.telerik.com/kendo-ui/api/javascript/ui/datetimepicker
### **用法示例**
```javascript
//注意是写在script标签内的
var viewModel = kendo.observable({
data:{},
isEnabled:true
});
function open(){
//...
}
function change(e){
//...
}
```
```xml
<h:hlsDatePicker id="hlsDatePickerId"bind="enabled: isEnabled,value:data.value"
style="width:200px;"
max="2016-6-6"
placeholder="hlsDatePicker"
open="open"
required="true"
change="change"
prompt="日期选择器:"
/>
id:可写可不写;
bind:属性里面包含配置两个选项:
(1)enabled配置日期选择器是否可以编辑,值为Boolean类型的变量,在viewModel里面配置。
(2)value配置日期选择器与变量关联,默认是在ViewModel下面的变量;
max:可选择日期的上限值;
style可改变该控件的样式;
open:点击控件调用的方法;
change:选择日期之后的事件;
prompt:为控件添加一个label,该属性一般和<h:hlsForm><h:hlsHBox>一起使用;
```
> **提示:**
> - 上述所有属性直接添加在标签内即可,(**[属性名] = "..."**),对于既是**String**类型又是**Function**类型的属性,只需要加**fn:**前缀区分即可,(**fn:函数名**)
> - 对于**max min**等本该是**时间类型**的属性,改为日期字符串,且连接符号为"**-**"
# hlsDatePicker标签
日期选择器(不包含时分秒)
xml配置
```xml
<!--基本用法-->
<h:hlsDatePicker id="hlsDatePickerId" bind="enabled:isEnabled,value:data.value" placeholder="hlsDatePicker"/>
```
### **主要属性**
属性名 | 类型 | 说明 |
-------- | -----| --- |
promptColspan | Integer | 描述字段所占列数 |
promptClassName | String| 描述的样式名称 |
prompt | String| 描述 |
colspan | Integer | datePicker所占列数 |
hlsClassName | String | 样式名称 |
animation | Boolean | 动画 |
format| String | 格式化 |
max| String | 最大时间 |
min| String | 最小时间 |
value | String | 值 |
name | String | name属性 |
bind | Function | 绑定数据 |
required | Boolean | 必输 |
change | Function | chang事件 |
placeholder | String | 占位符 |
timeFormat |String| 时间格式化 |
> **提示:** 属性用法请参考kendoui API文档 http://docs.telerik.com/kendo-ui/api/javascript/ui/datetimepicker
### **用法示例**
```javascript
//注意是写在script标签内的
var viewModel = kendo.observable({
data:{},
isEnabled:true
});
function open(){
//...
}
function change(e){
//...
}
```
```xml
<h:hlsDatePicker id="hlsDatePickerId"bind="enabled: isEnabled,value:data.value"
style="width:200px;"
max="2016-6-6"
placeholder="hlsDatePicker"
open="open"
required="true"
change="change"
prompt="日期选择器:"
/>
id:可写可不写;
bind:属性里面包含配置两个选项:
(1)enabled配置日期选择器是否可以编辑,值为Boolean类型的变量,在viewModel里面配置。
(2)value配置日期选择器与变量关联,默认是在ViewModel下面的变量;
max:可选择日期的上限值;
style可改变该控件的样式;
open:点击控件调用的方法;
change:选择日期之后的事件;
prompt:为控件添加一个label,该属性一般和<h:hlsForm><h:hlsHBox>一起使用;
```
> **提示:**
> - 上述所有属性直接添加在标签内即可,(**[属性名] = "..."**),对于既是**String**类型又是**Function**类型的属性,只需要加**fn:**前缀区分即可,(**fn:函数名**)
> - 对于**max min**等本该是**时间类型**的属性,改为日期字符串,且连接符号为"**-**"
## 抽屉通用方法
### 1.打开抽屉
> Hel.openBox()函数接收一个对象,对象属性含义如下:
属性名 | 类型 | 含义
-------- | ----- | -----
winId | String(必填) | html元素标签的id
functionCode | String(必填) | 抽屉的唯一标识,用以确定该抽屉的相关信息
params | Object | 需传递的参数,将自动拼到url上
closeFunction | function | 关闭抽屉调用的方法
```javascript
function test(e){
...
}
Hel.openBox({
winId:"domId",
functionCode:"CSH200B",
params:{
contractId:1327,
conContractCashflowId:4321
},
closeFunction:test
});
```
#### 注意:
> - winId和functionCode必须有字符串类型的值
> - params可以没有,表示不拼接条件;
> - closeFunction可以没有,表示关闭抽屉不进行操作
## 抽屉通用方法
### 1.打开抽屉
> Hel.openBox()函数接收一个对象,对象属性含义如下:
属性名 | 类型 | 含义
-------- | ----- | -----
winId | String(必填) | html元素标签的id
functionCode | String(必填) | 抽屉的唯一标识,用以确定该抽屉的相关信息
params | Object | 需传递的参数,将自动拼到url上
closeFunction | function | 关闭抽屉调用的方法
width | Number | 自定义宽度
```javascript
function test(e){
...
}
Hel.openBox({
winId:"domId",
functionCode:"CSH200B",
params:{
contractId:1327,
conContractCashflowId:4321
},
closeFunction:test,
width:900
});
```
#### 注意:
> - winId和functionCode必须有字符串类型的值
> - params可以没有,表示不拼接条件;
> - closeFunction可以没有,表示关闭抽屉不进行操作
> - width可以没有,表示使用配置的宽度
## hlsMaskedTextBox
格式框
##### xml配置
```xml
<!--h:为命名空间,必须要加的;maskedTextBox为组件名;id可写可不写-->
<h:hlsMaskedTextBox id="hlsMaskedTextBox"/>
```
#### **一般属性**
> **提示:**
>
> * 属性用法请参考kendoui API文档的maskedTextBox:[http:\/\/docs.telerik.com\/kendo-ui\/api\/javascript\/ui\/maskedtextbox](http://docs.telerik.com/kendo-ui/api/javascript/ui/maskedtextbox)
| 属性名 | 类型 |
| --- | --- |
| clearPromptChar | Boolean |
| culture | String |
| mask | String |
| promptChar | String |
| unmaskOnPost | Boolean |
| value | String |
| change | Function |
| caseLetter | String |
| clearButton | Boolean |
| placeholder | String |
| bind | Function |
| required | Boolean |
用法示例:
```xml
<h:hlsMaskedTextBox id="hlsMaskedTextBox" required="true" placeholder="hlsMaskedTextBox" />
```
> **提示:**
>
> * 上述所有属性直接添加在标签内即可,(**\[\*\***属性名\]**\*\* = "..."**)
#### **子标签**
| 属性名 | 类型 |
| --- | --- |
| rules | Object |
```javascript
function rules(){
//...
}
```
```xml
<h:hlsMaskedTextBox id="hlsMaskedtTextBox">
<h:rules "~"="/[+-]/" "^"="fn:rules"/>
</h:hlsMaskedTextBox>
```
> **提示**:
>
> * 遇到**object**类型 就向下添加子标签,当遇到普通类型时,就添加到当前的标签上。
## hlsMaskedTextBox
格式框
##### xml配置
```xml
<!--h:为命名空间,必须要加的;maskedTextBox为组件名;id可写可不写-->
<h:hlsMaskedTextBox id="hlsMaskedTextBox"/>
```
#### **一般属性**
> **提示:**
>
> * 属性用法请参考kendoui API文档的maskedTextBox:[http:\/\/docs.telerik.com\/kendo-ui\/api\/javascript\/ui\/maskedtextbox](http://docs.telerik.com/kendo-ui/api/javascript/ui/maskedtextbox)
| 属性名 | 类型 | 说明 |
| --- | --- | --- |
| id | String | 唯一英文标识 |
| promptColspan | number | 该数字将会与col-sm-?进行拼接,替代问号,生成对应bootstrap的css样式,渲染描述的样式 |
| promptClassName | String | 描述的样式名称,会将对应的样式添加到描述中 |
| promptImage | String | 描述前面的一个文本图像,在设置为必填的时候会出现一个红色的* |
| prompt | String | 中文描述 || colspan| number | 该数字会与col-sm-?进行拼接,替代问号,生成对应bootStrap的css样式,渲染文本框的样式 |
| hlsClassName | String | 标签的样式名称 |
| placeholder | String | 占位符 |
| bind | expressions | 绑定数据 |
用法示例:
```xml
<h:hlsMaskedTextBox id="hlsMaskedTextBox" required="true" placeholder="hlsMaskedTextBox" />
```
> **提示:**
>
> * 上述所有属性直接添加在标签内即可,(**\[\*\***属性名\]**\*\* = "..."**)
#### **子标签**
| 属性名 | 类型 |
| --- | --- |
| rules | Object |
```javascript
function rules(){
//...
}
```
```xml
<h:hlsMaskedTextBox id="hlsMaskedtTextBox">
<h:rules "~"="/[+-]/" "^"="fn:rules"/>
</h:hlsMaskedTextBox>
```
> **提示**:
>
> * 遇到**object**类型 就向下添加子标签,当遇到普通类型时,就添加到当前的标签上。
# hlsTextArea
文本框
##### xml配置
```xml
<!--h:为命名空间,必须要加的;hlsTextArea为组件名;id可写可不写-->
<h:hlsTextArea id="hlsTextArea"/>
```
| 属性 | 类型 | 说明 |
| -------- | -------- | -------- |
\ No newline at end of file
# hlsTextArea(文本域)
文本框
##### xml配置
```xml
<!--h:为命名空间,必须要加的;hlsTextArea为组件名;id可写可不写-->
<h:hlsTextArea id="hlsTextArea"/>
```
| 属性 | 类型 | 说明 |
| -------- | -------- | -------- |
| id | String | 唯一英文标识 |
| promptColspan | number | 该数字将会与col-sm-?进行拼接,替代问号,生成对应bootstrap的css样式,渲染文本域中描述的样式 |
| promptClassName | String | 描述的样式名称,会将对应的样式添加到描述中 |
| promptImage | String | 描述前面的一个文本图像,在设置为必填的时候会出现一个红色的* |
| prompt | String | 文本域前面的中文描述 |
| colspan| number | 该数字会与col-sm-?进行拼接,替代问号,生成对应bootStrap的css样式,渲染文本框的样式 |
| hlsClassName | String | 包裹文本框的div的样式名称 |
\ No newline at end of file
## TlEdit
多语言编辑
##### xml配置
```xml
<!--h:为命名空间,必须要加的;hlsTlEdit为组件名;id可写可不写-->
<h:hlsTlEdit id="hlsTlEdit"/>
```
#### **一般属性**
| 属性名 | 类型 |
| --- | --- |
| idField | String |
| field | String |
| dto | String |
| model | Function |
| open | Function |
| close | Function |
| placeholder | String |
| bind | Function |
用法示例:
```javascript
<h:hlsTlEdit id="tl" placeholder="多语言" bind="value:data.name" idField="id" field="name" dto="com.hand.hap.function.dto.Resource" model="viewModel.data">
</h:hlsTlEdit>
```
> **提示:**
>
> * 以上所有属性都直接可以添加到tlEdit标签上,用法皆为\[**属性名**\]="\[**属性值**\]"
## hlsTlEdit
多语言编辑
##### xml配置
```xml
<!--h:为命名空间,必须要加的;hlsTlEdit为组件名;id可写可不写-->
<h:hlsTlEdit id="hlsTlEdit"/>
```
#### **一般属性**
| 属性名 | 类型 | 说明 |
| --- | --- | --- |
| id | String | 唯一英文标识 |
| promptColspan | number | 该数字将会与col-sm-?进行拼接,替代问号,生成对应bootstrap的css样式,渲染描述的样式 |
| promptClassName | String | 描述的样式名称,会将对应的样式添加到描述中 |
| promptImage | String | 描述前面的一个文本图像,在设置为必填的时候会出现一个红色的* |
| prompt | String | 中文描述 |
| colspan| number | 该数字会与col-sm-?进行拼接,替代问号,生成对应bootStrap的css样式,渲染文本框的样式 |
| hlsClassName | String | 标签的样式名称 |
| placeholder | String | 占位符 |
| bind | expressions | 绑定数据 |
用法示例:
```xml
<h:hlsTlEdit id="tl" placeholder="多语言" bind="value:data.name" idField="id" field="name" dto="com.hand.hap.function.dto.Resource" model="viewModel.data">
</h:hlsTlEdit>
```
> **提示:**
>
> * 以上所有属性都直接可以添加到tlEdit标签上,用法皆为\[**属性名**\]="\[**属性值**\]"
# 跨域访问(CORS)
## 简介:
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
## 使用说明:
#### 1.修改web.xml文件:
添加Filter:
![](/assets/cors-web.png)
各参数含义:
allowedMappings: 允许跨域资源(必填)
allowedHeader:\(允许的头部参数)
allowedOrigin\(允许的请求源)
allowedMethod\(允许的请求方法\)
allowedCredentials\(是否允许携带证书)
MaxAge\(最大时长)
#### 2.发送跨域请求:
2.1 跨域登录,观察http请求response
![](/assets/cors-login.png)
2.2 登录成功所返回的信息:sessionId\(用于用户登录验证) csrf\_token(用于csrf验证)
![](/assets/cors-login1.png)
2.3 将sessionId\(Authorization\) csrf\_\_token\(X-CSRF-\_TOKEN\)添加进requestHeader中进行后续访问
![](/assets/cors-query.png)
2.4 得到返回信息
![](/assets/cors-quer1.png)
# 工作流功能说明
### 1.流程设计
可以新建一个流程或者直接导入BPMN定义文件
融租易的环境有一些样例,可以下载学习。
以下截图以`请假流程`为例
使用activiti editor设计流程
![](/assets/act_vacationReq_ed.png)
如图点击编辑按钮会弹出流程设计器页面
![](/assets/act_vacationReq_exp.png)
> 左边有各种节点,事件等,可以拖拽到中间,右边是当前节点的属性烂
关于常用节点,以及常用属性,参考[流程设计器简易教程](activiti_editor_helper.md)
### 2.流程部署
流程设计好以后,点击流程设计页面操作栏的最后一个按钮(勾)即可发布流程
> 未发布过的流程或者改动过的流程,勾会以绿色展示,反之灰色
发布以后可以在流程部署页面查看部署情况
> 如果有旧的流程启动了,发布的新版不会影响旧流程的运行
### 3.流程启动
实际项目中需要客户化开发流程启动页面,参考`工作流测试`页面
工作流测试页面可以动态解析`表单属性`,请假流程的启动页面如图所示
![](/assets/act_vacationReq_exp_start.png)
### 4.待办事项与历史流程
可以在`我的待办`中查看当前登录用户(根据员工号)需要处理的待办事项
> 管理员可以在`待办事项中(管理员)`查看所有人的待办
在待办页面点击办理,即可处理相应事项。
![](/assets/act_vacationReq_exp_deal.png)
右上角的`审批动作`可以在通过在流程设计器里设置当前`人工任务`的表单属性动态生成,默认为
* 同意
* 拒绝
* 转交
> 对于activiti,审批动作仅仅是传回后台的值不一样
这里的表单信息也需要需要客户化开发的,其实是一个iframe嵌套的页面,会在待办页面自动解析
> 页面路径在流程设计器里指定当前任务的`表单的标识Key`
demo放在`view/activiti/include`
`历史流程`页面可以查看已经结束和正在运行的流程信息
## 审批链
---
功能入口是在 `流程设计` 界面。
<img width='460' src='/assets/approve_chain_entry.png'/>
点击 编辑图标 进入该流程的审批链配置界面
<img width='900' src='/assets/approve_chain@2x.png'/>
在这个编辑页面中,所有的 `UserTask` 节点会以 标签页的形式自动列出来。
切换标签页,下方表格中的数据会自动刷新为对应节点的数据。
上方的 操作按钮 是针对每个 标签页 操作的,并非针对整个页面。
主要属性说明:
* 名称
自定义,任意。`不允许重复`
节点原始名称 和 审批链的名称 拼接起来 作为运行时的名称。
* 审批人
内置 3 种
* 申请人(自己)
* 上级(申请人直接领导)
* 申请人部门领导
其他情况统统归为 `自定义表达式`
> 注意,这个功能要求流程的初始化器(Initiator)必须指定为 `initiator`
* 审批岗位
支持选择系统中的`岗位`,也支持自定义的`表达式`
> 目前在指定了审批人的情况下,再指定审批岗位是无效果的
* 表单
允许该轮次的审批显示指定的单据明细页面。
如果没有指定,则显示该节点原始设置的值。
* 顺序号
用来控制审批的先后 (目前尚不支持多人同时在同一轮次审批)
* 跳过条件
表达式,有值,且值为 true 时,当前轮次跳过。
默认留空,表示不跳过。
* 当前轮次因为 `跳过条件`被跳过时,是否直接结束审批链
* 启用
高优先级过滤条件(不启用肯定就没有作用)
* 描述
无明确用途
---
在设计器中设计审批节点时,由审批链控制的节点,不需要在去指定 `任务派遣`,可以完全在审批链中定义。即使指定了,也会被审批链中的值覆盖!
除了与审批人、岗位有关的属性以外,其他属性的含义、作用保持不变。
`名称`稍微有变化:会和审批链的名称拼接在一起
> 请注意:目前审批链还不支持多例(会签)
## 自定义任务超时时间和动态调整优先级
---
对于人工任务节点,在流程设计器中可以设置任务到期时间
![](/assets/wfl_user_task_approve.png)
支持ISO8601 标准的日期格式,也可以写流程变量。如PT8H(ISO8601标准支持),表明该节点的任务的限定时间是8小时。
HAP 实现了一个简单任务,每30分钟执行一次,会动态调整设置了到期日期的任务的优先级。
如何自定义超时时间的计算方式以及自定义优先级调整策略?
实现 `ICustomTaskProcessor` 即可:
```java
/**
* @param task 任务节点
* 动态设置task的优先级,调用getDueTime,获取任务剩余时间
* 建议不要改动其他属性,仅仅设置优先级
* @return 一般情况返回参数task
*/
Task processPriority(Task task);
/**
* @param startDate task的创建时间
* @param dueTime 当前task的任务的限定时间
*
* 根据task的创建时间和限定时间,返回任务的剩余时间,单位秒
*
* @return 返回经过计算后的任务剩余时间 ,负数表示超时时间
*/
Long getDueTime(Date startDate,Long dueTime);
/**
* 是否继续处理,如果返回false,不会再继续执行其他实现类
* */
boolean processorContinue();
int getOrder();
```
参考 HAP 提供的一个默认实现 `DefaultCustomTaskProcessor`
超时时间没做特殊处理,如果剩余时间不足三分之二将优先级上调一级,如果不足三分之一把优先级设置为高。超时则设置为最高(100)
1.首先实现 `getDueTime` 方法,该方法应该返回实际意义上,当前任务剩余的时间。
> 比如我们设置了PT8H,但是我们想让这个任务的时效是8个工时,则可以在这里写好处理逻辑
> 参数 `dueTime` 是一个 Long 类型的参数,它的值是设置的超时时间(比如8小时,任务的dueDate与startDate的时间差)
2.`processPriority` 方法则可以自定义优先级的调整规则,这里应该调用`getDueTime` 拿到任务的剩余时间或者超时时间,通过设置一定的规则,调整优先级(`task.setPriority(xx)`)
3.`getOrder` 方法用于实现类排序,数值越小,执行该实现类的优先级越高,框架的默认实现类order = 999,想只执行自己的实现类,设置一个较小的数值,重写 `processorContinue` 方法,并返回false 即可
# 工作流功能说明
### 1.流程设计
可以新建一个流程或者直接导入BPMN定义文件
融租易的环境有一些样例,可以下载学习。
以下截图以`请假流程`为例
使用activiti editor设计流程
![](/assets/act_vacationReq_ed.png)
如图点击编辑按钮会弹出流程设计器页面
![](/assets/act_vacationReq_exp.png)
> 左边有各种节点,事件等,可以拖拽到中间,右边是当前节点的属性烂
关于常用节点,以及常用属性,参考[流程设计器简易教程](activiti_editor_helper.md)
### 2.流程部署
流程设计好以后,点击流程设计页面操作栏的最后一个按钮(勾)即可发布流程
> 未发布过的流程或者改动过的流程,勾会以绿色展示,反之灰色
发布以后可以在流程部署页面查看部署情况
> 如果有旧的流程启动了,发布的新版不会影响旧流程的运行
### 3.流程启动
实际项目中需要客户化开发流程启动页面,参考`工作流测试`页面
工作流测试页面可以动态解析`表单属性`,请假流程的启动页面如图所示
![](/assets/act_vacationReq_exp_start.png)
### 4.待办事项与历史流程
可以在`我的待办`中查看当前登录用户(根据员工号)需要处理的待办事项
> 管理员可以在`待办事项中(管理员)`查看所有人的待办
在待办页面点击办理,即可处理相应事项。
![](/assets/act_vacationReq_exp_deal.png)
右上角的`审批动作`可以在通过在流程设计器里设置当前`人工任务`的表单属性动态生成,默认为
* 同意
* 拒绝
* 转交
> 对于activiti,审批动作仅仅是传回后台的值不一样
这里的表单信息也需要需要客户化开发的,其实是一个iframe嵌套的页面,会在待办页面自动解析
> 页面路径在流程设计器里指定当前任务的`表单的标识Key`
demo放在`view/activiti/include`
`历史流程`页面可以查看已经结束和正在运行的流程信息
## 审批链
---
功能入口是在 `流程设计` 界面。
<img width='460' src='/assets/approve_chain_entry.png'/>
点击 编辑图标 进入该流程的审批链配置界面
<img width='900' src='/assets/approve_chain@2x.png'/>
在这个编辑页面中,所有的 `UserTask` 节点会以 标签页的形式自动列出来。
切换标签页,下方表格中的数据会自动刷新为对应节点的数据。
上方的 操作按钮 是针对每个 标签页 操作的,并非针对整个页面。
主要属性说明:
* 名称
自定义,任意。`不允许重复`。
节点原始名称 和 审批链的名称 拼接起来 作为运行时的名称。
* 审批人
内置 3 种
* 申请人(自己)
* 上级(申请人直接领导)
* 申请人部门领导
其他情况统统归为 `自定义表达式`
> 注意,这个功能要求流程的初始化器(Initiator)必须指定为 `initiator`
* 审批岗位
支持选择系统中的`岗位`,也支持自定义的`表达式`
> 目前在指定了审批人的情况下,再指定审批岗位是无效果的
* 表单
许该轮次的审批显示指定的单据明细页面。
果没有指定,则显示该节点原始设置的值。
* 顺序号
来控制审批的先后 (目前尚不支持多人同时在同一轮次审批)
* 跳过条件
达式,有值,且值为 true 时,当前轮次跳过。
认留空,表示不跳过。
* 当前轮次因为 `跳过条件`被跳过时,是否直接结束审批链
* 启用
优先级过滤条件(不启用肯定就没有作用)
* 描述
明确用途
---
在设计器中设计审批节点时,由审批链控制的节点,不需要在去指定 `任务派遣`,可以完全在审批链中定义。即使指定了,也会被审批链中的值覆盖!
除了与审批人、岗位有关的属性以外,其他属性的含义、作用保持不变。
`名称`稍微有变化:会和审批链的名称拼接在一起
> 请注意:目前审批链还不支持多例(会签)
## 自定义任务超时时间和动态调整优先级
---
对于人工任务节点,在流程设计器中可以设置任务到期时间
![](/assets/wfl_user_task_approve.png)
支持ISO8601 标准的日期格式,也可以写流程变量。如PT8H(ISO8601标准支持),表明该节点的任务的限定时间是8小时。
HAP 实现了一个简单任务,每30分钟执行一次,会动态调整设置了到期日期的任务的优先级。
如何自定义超时时间的计算方式以及自定义优先级调整策略?
实现 `ICustomTaskProcessor` 即可:
```java
/**
* @param task 任务节点
* 动态设置task的优先级,调用getDueTime,获取任务剩余时间
* 建议不要改动其他属性,仅仅设置优先级
* @return 一般情况返回参数task
*/
Task processPriority(Task task);
/**
* @param startDate task的创建时间
* @param dueTime 当前task的任务的限定时间
*
* 根据task的创建时间和限定时间,返回任务的剩余时间,单位秒
*
* @return 返回经过计算后的任务剩余时间 ,负数表示超时时间
*/
Long getDueTime(Date startDate,Long dueTime);
/**
* 是否继续处理,如果返回false,不会再继续执行其他实现类
* */
boolean processorContinue();
int getOrder();
```
参考 HAP 提供的一个默认实现 `DefaultCustomTaskProcessor`
超时时间没做特殊处理,如果剩余时间不足三分之二将优先级上调一级,如果不足三分之一把优先级设置为高。超时则设置为最高(100)
1.首先实现 `getDueTime` 方法,该方法应该返回实际意义上,当前任务剩余的时间。
> 比如我们设置了PT8H,但是我们想让这个任务的时效是8个工时,则可以在这里写好处理逻辑
> 参数 `dueTime` 是一个 Long 类型的参数,它的值是设置的超时时间(比如8小时,任务的dueDate与startDate的时间差)
2.`processPriority` 方法则可以自定义优先级的调整规则,这里应该调用`getDueTime` 拿到任务的剩余时间或者超时时间,通过设置一定的规则,调整优先级(`task.setPriority(xx)`)
3.`getOrder` 方法用于实现类排序,数值越小,执行该实现类的优先级越高,框架的默认实现类order = 999,想只执行自己的实现类,设置一个较小的数值,重写 `processorContinue` 方法,并返回false 即可
工作流开发指南
---
## UserTask
---
### 指定审批人、组
在流程设计器中,选定`人工任务`节点,右边属性栏,点击`任务派遣` 弹出对话框
<img width='600' src='/assets/usertask-assignment.png'/>
其中:
* 指派对象
只能有一个人,可以点击`选择`按钮来从系统中选择员工
* 候选用户
可以添加多个,每个指定一个`员工工号`
* 候选组
可以添加多个,每个指定一个`岗位代码`
### 动态审批人、组
与 上面类似,不过动态的审批人和组不是选择的,而是通过表达式计算得来。
表达式有两种情况
* 引用变量
比如`${initiator}`, 其中`initiator` 就是一个变量
* 调用 service 方法
比如`${orgStructure.getDirector(initiator)}`,其中 `orgStructure` 是一个自定的用于工作流的 `bean`,参数则可以使用任意的变量、常量等
> `execution` 是一个固有的、特殊的变量,指代 `org.activiti.engine.delegate.DelegateExecution`,非常有用
关于自定义在工作流中用的 `bean`
HAP 提供一个 接口 `com.hand.hap.activiti.custom.IActivitiBean`
实现这个接口并被定义注册为 bean,其中的任何 `public` 方法都可以在工作流表达式中直接调用。
默认 bean 的 名字就是引用名,也可以覆盖接口的默认实现,指定名称。
### 审批动作
Hap 的工作流在审批的时候默认有两个标准的动作:
* 同意(APPROVED)
* 拒绝(REJECTED)
当审批者点击按钮以后,审批动作 id 会被保存到流程的共享变量区域。
key 为 `approveResult`,这个变量可以直接在表达式中使用。
```
${approveResult=='REJECTED'}
```
流程设计时,可以控制这两个按钮显示的文本;也可以控制只显示其中的一个。
允许自定义额外的审批动作,自动动作的 id 同样会保存在 `approveResult` 变量中,通常需要流程显示处理(比如选择网关)。
<img width='920' src='/assets/custom-approve-action.png'/>
### 自动结束流程(HAP 扩展特性)
当一个任务被拒绝时,绝大多数情况下,这个流程应该直接结束。
但在工作流中,`审批拒绝``审批同意` 仅仅是给变量赋的参数值不同而已,如果希望流程结束,应该加一个 `选择网关`,判断审批结果是否为 `拒绝` ,然后引导流程走向`结束事件`
显然,这很繁琐,尤其是当流程中`人工任务`节点比较多时。
HAP 做了一个优化:
> 如果一个`人工任务` 的下一个节点仍然是 `任务(Task 类型)`,那么 HAP 将会自动插入一个`选择网关`和`终止事件` 来自动做结束流程的操作
这个优化在流程的图上是看不出来的,用户完全无感知。
### 会签设置
自定义属性:`nrOfApproved``nrOfRejected`
集合
变量
任务派遣
完成条件
## 网关
---
选择网关
> 选择网关`必须` 指定一个默认的连线,否则会出现不固定的行为。
并行网关
> 并行网关会在`所有连入`的连线都激活以后才会开始执行
## ServiceTask
---
## 待办通知
---
## 审批链
---
审批链功能不是 Activiti 的标准功能,属于 HAP 开发的外挂性质的扩展。
功能入口是在 `流程设计` 界面。
<img width='460' src='/assets/approve_chain_entry.png'/>
点击 编辑图标 进入该流程的审批链配置界面
<img width='900' src='/assets/approve_chain@2x.png'/>
在这个编辑页面中,所有的 `UserTask` 节点会以 标签页的形式自动列出来。
切换标签页,下方表格中的数据会自动刷新为对应节点的数据。
上方的 操作按钮 是针对每个 标签页 操作的,并非针对整个页面。
主要属性说明:
* 名称
自定义,任意。`不允许重复`
节点原始名称 和 审批链的名称 拼接起来 作为运行时的名称。
* 审批人
内置 3 种
* 申请人(自己)
* 上级(申请人直接领导)
* 申请人部门领导
其他情况统统归为 `自定义表达式`
> 注意,这个功能要求流程的初始化器(Initiator)必须指定为 `initiator`
* 审批岗位
支持选择系统中的`岗位`,也支持自定义的`表达式`
> 目前在指定了审批人的情况下,再指定审批岗位是无效果的
* 表单
允许该轮次的审批显示指定的单据明细页面。
如果没有指定,则显示该节点原始设置的值。
* 顺序号
用来控制审批的先后 (目前尚不支持多人同时在同一轮次审批)
* 跳过条件
表达式,有值,且值为 true 时,当前轮次跳过。
默认留空,表示不跳过。
* 当前轮次因为 `跳过条件`被跳过时,是否直接结束审批链
* 启用
高优先级过滤条件(不启用肯定就没有作用)
* 描述
无明确用途
---
在设计器中设计审批节点时,由审批链控制的节点,不需要在去指定 `任务派遣`,可以完全在审批链中定义。即使指定了,也会被审批链中的值覆盖!
除了与审批人、岗位有关的属性以外,其他属性的含义、作用保持不变。
`名称`稍微有变化:会和审批链的名称拼接在一起
> 请注意:目前审批链还不支持多例(会签)
工作流开发指南
---
## UserTask
---
### 指定审批人、组
在流程设计器中,选定`人工任务`节点,右边属性栏,点击`任务派遣` 弹出对话框
<img width='600' src='/assets/usertask-assignment.png'/>
其中:
* 指派对象
只能有一个人,可以点击`选择`按钮来从系统中选择员工
* 候选用户
可以添加多个,每个指定一个`员工工号`
* 候选组
可以添加多个,每个指定一个`岗位代码`
### 动态审批人、组
与 上面类似,不过动态的审批人和组不是选择的,而是通过表达式计算得来。
表达式有两种情况
* 引用变量
比如`${initiator}`, 其中`initiator` 就是一个变量
* 调用 service 方法
比如`${orgStructure.getDirector(initiator)}`,其中 `orgStructure` 是一个自定的用于工作流的 `bean`,参数则可以使用任意的变量、常量等
> `execution` 是一个固有的、特殊的变量,指代 `org.activiti.engine.delegate.DelegateExecution`,非常有用
关于自定义在工作流中用的 `bean`
HAP 提供一个 接口 `com.hand.hap.activiti.custom.IActivitiBean`
实现这个接口并被定义注册为 bean,其中的任何 `public` 方法都可以在工作流表达式中直接调用。
默认 bean 的 名字就是引用名,也可以覆盖接口的默认实现,指定名称。
### 审批动作
Hap 的工作流在审批的时候默认有两个标准的动作:
* 同意(APPROVED)
* 拒绝(REJECTED)
当审批者点击按钮以后,审批动作 id 会被保存到流程的共享变量区域。
key 为 `approveResult`,这个变量可以直接在表达式中使用。
```
${approveResult=='REJECTED'}
```
流程设计时,可以控制这两个按钮显示的文本;也可以控制只显示其中的一个。
允许自定义额外的审批动作,自动动作的 id 同样会保存在 `approveResult` 变量中,通常需要流程显示处理(比如选择网关)。
<img width='920' src='/assets/custom-approve-action.png'/>
### 自动结束流程(HAP 扩展特性)
当一个任务被拒绝时,绝大多数情况下,这个流程应该直接结束。
但在工作流中,`审批拒绝``审批同意` 仅仅是给变量赋的参数值不同而已,如果希望流程结束,应该加一个 `选择网关`,判断审批结果是否为 `拒绝` ,然后引导流程走向`结束事件`
显然,这很繁琐,尤其是当流程中`人工任务`节点比较多时。
HAP 做了一个优化:
> 如果一个`人工任务` 的下一个节点仍然是 `任务(Task 类型)`,那么 HAP 将会自动插入一个`选择网关`和`终止事件` 来自动做结束流程的操作
这个优化在流程的图上是看不出来的,用户完全无感知。
### 会签设置
自定义属性:`nrOfApproved``nrOfRejected`
集合
变量
任务派遣
完成条件
## 网关
---
选择网关
> 选择网关`必须` 指定一个默认的连线,否则会出现不固定的行为。
行网关
> 并行网关会在`所有连入`的连线都激活以后才会开始执行
## ServiceTask
---
## 待办通知
---
## 审批链
---
审批链功能不是 Activiti 的标准功能,属于 HAP 开发的外挂性质的扩展。
功能入口是在 `流程设计` 界面。
<img width='460' src='/assets/approve_chain_entry.png'/>
点击 编辑图标 进入该流程的审批链配置界面
<img width='900' src='/assets/approve_chain@2x.png'/>
在这个编辑页面中,所有的 `UserTask` 节点会以 标签页的形式自动列出来。
切换标签页,下方表格中的数据会自动刷新为对应节点的数据。
上方的 操作按钮 是针对每个 标签页 操作的,并非针对整个页面。
主要属性说明:
* 名称
自定义,任意。`不允许重复`
节点原始名称 和 审批链的名称 拼接起来 作为运行时的名称。
* 审批人
内置 3 种
* 申请人(自己)
* 上级(申请人直接领导)
* 申请人部门领导
其他情况统统归为 `自定义表达式`
> 注意,这个功能要求流程的初始化器(Initiator)必须指定为 `initiator`
* 审批岗位
支持选择系统中的`岗位`,也支持自定义的`表达式`
> 目前在指定了审批人的情况下,再指定审批岗位是无效果的
* 表单
允许该轮次的审批显示指定的单据明细页面。
如果没有指定,则显示该节点原始设置的值。
* 顺序号
用来控制审批的先后 (目前尚不支持多人同时在同一轮次审批)
* 跳过条件
表达式,有值,且值为 true 时,当前轮次跳过。
默认留空,表示不跳过。
* 当前轮次因为 `跳过条件`被跳过时,是否直接结束审批链
* 启用
高优先级过滤条件(不启用肯定就没有作用)
* 描述
无明确用途
---
在设计器中设计审批节点时,由审批链控制的节点,不需要在去指定 `任务派遣`,可以完全在审批链中定义。即使指定了,也会被审批链中的值覆盖!
除了与审批人、岗位有关的属性以外,其他属性的含义、作用保持不变。
`名称`稍微有变化:会和审批链的名称拼接在一起
> 请注意:目前审批链还不支持多例(会签)
# 工作流demo
整体流程
* 打开工作流模块,设计工作流流程
* 实现一个当前功能启动工作流的接口
* 在自己的业务层调用自己实现的工作流类
* 启动工作流
1 新建工作流并填写对应的信息
![](/assets/wfl_demo_1.png)
![](/assets/wfl_demo_2.png)
2 编辑对应的审批节点链,可参考已实现的工作流程
![](/assets/wfl_demo_3.png)
3 编写一个自己的实现类,实现IActivitiCommonService接口
![](/assets/wfl_demo_4.png)
实现类代码如下
```java
package hls.core.wfl.service.impl;
import com.hand.hap.activiti.dto.ReProcdef;
import com.hand.hap.activiti.service.IActivitiService;
import com.hand.hap.activiti.service.IReProcdefService;
import com.hand.hap.core.IRequest;
import hls.core.prj.dto.PrjProject;
import hls.core.prj.dto.PrjProjectMeeting;
import hls.core.prj.mapper.PrjProjectMeetingMapper;
import hls.core.prj.service.PrjProjectService;
import hls.core.wfl.service.IActivitiCommonService;
import net.sf.json.JSONObject;
import org.activiti.rest.service.api.engine.variable.RestVariable;
import org.activiti.rest.service.api.runtime.process.ProcessInstanceCreateRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;import java.util.List;
import java.util.Map;
@Service
@Transactional
public class PrjActivitStartServiceImpl implements IActivitiCommonService
{
private static final String workFlowType = "PRJ_PROJECT"; @Autowired
private IActivitiService activitiService;
@Autowired
private IReProcdefService reProcdefService;
@Autowired
private PrjProjectService projectService;
@Autowired
private PrjProjectMeetingMapper meetingMapper;
@Override
public String getWorkFlowType() { return workFlowType; }
@Override
public void process(IRequest iRequest, List list, Map params)
{
ProcessInstanceCreateRequest processInstanceCreateRequest = getProcessInstanceCreateRequest((PrjProject)list.get(0),iRequest);
activitiService.startProcess(iRequest, processInstanceCreateRequest);
}
private ProcessInstanceCreateRequest getProcessInstanceCreateRequest(PrjProject prjProject, IRequest iRequest)
{
ProcessInstanceCreateRequest createRequest = new ProcessInstanceCreateRequest();
PrjProject project=projectService.selectByPrimaryKey(iRequest,prjProject); ReProcdef reProcdefs ;
reProcdefs = reProcdefService.queryReProcdef("PRJ_PROJECT_APPROVE","PRJ_PROJECT");
String id = reProcdefs.getId_();
String name = reProcdefs.getName_(); createRequest.setProcessDefinitionId(id); createRequest.setBusinessKey(prjProject.getProjectId().toString());
List<RestVariable> variables = new ArrayList<RestVariable>(); List<RestVariable> transientVariables = new ArrayList<RestVariable>();
RestVariable restVariable1 = new RestVariable(); RestVariable restVariable2 = new RestVariable();
RestVariable restVariable3 = new RestVariable();
RestVariable restVariable4 = new RestVariable();
RestVariable restVariable5 = new RestVariable();
RestVariable restVariable6 = new RestVariable();
restVariable1.setName("processDefinitionId");
restVariable1.setValue(id); variables.add(restVariable1);
restVariable2.setName("prjProject");
JSONObject jsonObject= new JSONObject().fromObject(prjProject); restVariable2.setValue(jsonObject.toString()); variables.add(restVariable2);
restVariable3.setName("iRequest"); restVariable3.setValue(iRequest);
variables.add(restVariable3);
restVariable4.setName("projectNum"); restVariable4.setValue(project.getProjectNumber()); variables.add(restVariable4); restVariable5.setName("projectName"); restVariable5.setValue(project.getProjectName()); variables.add(restVariable5); restVariable6.setName("startUserName"); restVariable6.setValue(iRequest.getEmployeeCode()); variables.add(restVariable6);
RestVariable restVariable7 = new RestVariable();
RestVariable restVariable8 = new RestVariable(); RestVariable restVariable9 = new RestVariable(); restVariable7.setName("documentCategory"); restVariable7.setValue(project.getDocumentCategory()); variables.add(restVariable7); restVariable8.setName("documentType"); restVariable8.setValue(project.getDocumentType());
variables.add(restVariable8); restVariable9.setName("documentId"); restVariable9.setValue(project.getProjectId());
variables.add(restVariable9);
RestVariable restVariable10 = new RestVariable();
restVariable10.setName("doubleFlag");
PrjProjectMeeting meeting = new PrjProjectMeeting();
meeting.setProjectId(prjProject.getProjectId());
List<PrjProjectMeeting> list = meetingMapper.select(meeting); if(list.size() >0) { restVariable10.setValue("Y"); }else{ restVariable10.setValue("N"); }
variables.add(restVariable10);
RestVariable restVariable11 = new RestVariable(); restVariable11.setName("pName");
restVariable11.setValue(name);
variables.add(restVariable11); createRequest.setVariables(variables); createRequest.setTransientVariables(transientVariables);
return createRequest;
}}
```
4 启动工作流
```
Map<String,Object> params = new HashMap<String,Object>();
params.put("workFlowType","PRJ_PROJECT");
activitiStartService.start(iRequest,projects,params);
# 工作流demo
整体流程
* 打开工作流模块,设计工作流流程
* 实现一个当前功能启动工作流的接口
* 在自己的业务层调用自己实现的工作流类
* 启动工作流
1 新建工作流并填写对应的信息
![](/assets/wfl_demo_1.png)
![](/assets/wfl_demo_2.png)
2 编辑对应的审批节点链,可参考已实现的工作流程
![](/assets/wfl_demo_3.png)
3 编写一个自己的实现类,实现IActivitiCommonService接口
![](/assets/wfl_demo_4.png)
实现类代码如下
```java
package hls.core.wfl.service.impl;
import com.hand.hap.activiti.dto.ReProcdef;
import com.hand.hap.activiti.service.IActivitiService;
import com.hand.hap.activiti.service.IReProcdefService;
import com.hand.hap.core.IRequest;
import hls.core.prj.dto.PrjProject;
import hls.core.prj.dto.PrjProjectMeeting;
import hls.core.prj.mapper.PrjProjectMeetingMapper;
import hls.core.prj.service.PrjProjectService;
import hls.core.wfl.service.IActivitiCommonService;
import net.sf.json.JSONObject;
import org.activiti.rest.service.api.engine.variable.RestVariable;
import org.activiti.rest.service.api.runtime.process.ProcessInstanceCreateRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;import java.util.List;
import java.util.Map;
@Service
@Transactional
public class PrjActivitStartServiceImpl implements IActivitiCommonService
{
private static final String workFlowType = "PRJ_PROJECT"; @Autowired
private IActivitiService activitiService;
@Autowired
private IReProcdefService reProcdefService;
@Autowired
private PrjProjectService projectService;
@Autowired
private PrjProjectMeetingMapper meetingMapper;
@Override
public String getWorkFlowType() { return workFlowType; }
@Override
public void process(IRequest iRequest, List list, Map params)
{
ProcessInstanceCreateRequest processInstanceCreateRequest = getProcessInstanceCreateRequest((PrjProject)list.get(0),iRequest);
activitiService.startProcess(iRequest, processInstanceCreateRequest);
}
private ProcessInstanceCreateRequest getProcessInstanceCreateRequest(PrjProject prjProject, IRequest iRequest)
{
ProcessInstanceCreateRequest createRequest = new ProcessInstanceCreateRequest();
PrjProject project=projectService.selectByPrimaryKey(iRequest,prjProject); ReProcdef reProcdefs ;
reProcdefs = reProcdefService.queryReProcdef("PRJ_PROJECT_APPROVE","PRJ_PROJECT");
String id = reProcdefs.getId_();
String name = reProcdefs.getName_(); createRequest.setProcessDefinitionId(id); createRequest.setBusinessKey(prjProject.getProjectId().toString());
List<RestVariable> variables = new ArrayList<RestVariable>(); List<RestVariable> transientVariables = new ArrayList<RestVariable>();
RestVariable restVariable1 = new RestVariable(); RestVariable restVariable2 = new RestVariable();
RestVariable restVariable3 = new RestVariable();
RestVariable restVariable4 = new RestVariable();
RestVariable restVariable5 = new RestVariable();
RestVariable restVariable6 = new RestVariable();
restVariable1.setName("processDefinitionId");
restVariable1.setValue(id); variables.add(restVariable1);
restVariable2.setName("prjProject");
JSONObject jsonObject= new JSONObject().fromObject(prjProject); restVariable2.setValue(jsonObject.toString()); variables.add(restVariable2);
restVariable3.setName("iRequest"); restVariable3.setValue(iRequest);
variables.add(restVariable3);
restVariable4.setName("projectNum"); restVariable4.setValue(project.getProjectNumber()); variables.add(restVariable4); restVariable5.setName("projectName"); restVariable5.setValue(project.getProjectName()); variables.add(restVariable5); restVariable6.setName("startUserName"); restVariable6.setValue(iRequest.getEmployeeCode()); variables.add(restVariable6);
RestVariable restVariable7 = new RestVariable();
RestVariable restVariable8 = new RestVariable(); RestVariable restVariable9 = new RestVariable(); restVariable7.setName("documentCategory"); restVariable7.setValue(project.getDocumentCategory()); variables.add(restVariable7); restVariable8.setName("documentType"); restVariable8.setValue(project.getDocumentType());
variables.add(restVariable8); restVariable9.setName("documentId"); restVariable9.setValue(project.getProjectId());
variables.add(restVariable9);
RestVariable restVariable10 = new RestVariable();
restVariable10.setName("doubleFlag");
PrjProjectMeeting meeting = new PrjProjectMeeting();
meeting.setProjectId(prjProject.getProjectId());
List<PrjProjectMeeting> list = meetingMapper.select(meeting); if(list.size() >0) { restVariable10.setValue("Y"); }else{ restVariable10.setValue("N"); }
variables.add(restVariable10);
RestVariable restVariable11 = new RestVariable(); restVariable11.setName("pName");
restVariable11.setValue(name);
variables.add(restVariable11); createRequest.setVariables(variables); createRequest.setTransientVariables(transientVariables);
return createRequest;
}}
```
4 启动工作流
```
Map<String,Object> params = new HashMap<String,Object>();
params.put("workFlowType","PRJ_PROJECT");
activitiStartService.start(iRequest,projects,params);
```
\ No newline at end of file
流程设计器教程
---
# 1.开始事件和结束事件
---
对应着流程的开始和结束。
> 应该保证每个分支都可以从开始走到结束
## 开始事件
开始事件用来指明流程在哪里开始。开始事件的类型(流程在接收事件时启动, 还是在指定时间启动,等等),定义了流程如何启动。
> 一般使用第一个空的开始事件就可以了,需要手动启动流程
*属性*
![](/assets/act_vacationReq_bg_v.png)
* 初始化器
当流程启动时,把当前登录的用户保存到哪个变量名中,可以在表达式里直接引用该变量
* 表单属性
当执行到开始事件时,所有的流程变量都是可用的,但是可能需要一些用于表单展示的自定义变量,此时可以在表单属性中定义,将会保存到流程的`共享变量`区域
![](/assets/act_vacationReq_form.png)
如图是`请假流程`的开始事件的表单属性,在这里定义的变量,在`工作流测试页面`会自动渲染成表单
支持以下的几种表单属性类型:
* string (org.activiti.engine.impl.form.StringFormType)
* long (org.activiti.engine.impl.form.LongFormType)
* enum (org.activiti.engine.impl.form.EnumFormType)
* date (org.activiti.engine.impl.form.DateFormType)
* boolean (org.activiti.engine.impl.form.BooleanFormType)
> 括号里是作为流程变量存储的对应JAVA类
表达式和变量
如果设置了`变量`,需要通过变量名来引用变量,表达式同理
> 默认则是把id作为变量名使用
* 必须 如果勾选了,提交表单时没有提供该属性则抛出异常
* 可读 不勾选则不会进行自动渲染显示,但是还是可以提交
* 可写 不够勾选,还提交该属性,也会抛出异常
## 结束事件
结束事件表示(子)流程(分支)的结束。 结束事件都是触发事件。 这是说当流程达到结束事件,会触发一个结果。 结果的类型是通过事件的内部黑色图标表示的。
> 一般使用一个空的结束事件就行了,意味着到达事件时不会指定抛出的结果。 这样,引擎会直接结束当前执行的分支,不会做其他事情。
### 自动结束流程(HAP 扩展特性)
当一个任务被拒绝时,绝大多数情况下,这个流程应该直接结束。
但在工作流中,`审批拒绝``审批同意` 仅仅是给变量赋的参数值不同而已,如果希望流程结束,应该加一个 `选择网关`,判断审批结果是否为 `拒绝` ,然后引导流程走向`结束事件`
显然,这很繁琐,尤其是当流程中`人工任务`节点比较多时。
HAP 做了一个优化:
> 如果一个`人工任务` 的下一个节点仍然是 `任务(Task 类型)`,那么 HAP 将会自动插入一个`选择网关`和`终止事件` 来自动做结束流程的操作
这个优化在流程的图上是看不出来的,用户完全无感知。
# 2.任务
---
常用的是人工任务,和服务任务
## 人工任务(UserTask)
![](/assets/act_vacationReq_userTask.png)
### 任务派遣
**指定审批人、组**
点击`任务派遣` 弹出对话框
<img width='600' src='/assets/usertask-assignment.png'/>
其中:
* 指派对象
只能有一个人,可以点击`选择`按钮来从系统中选择员工
* 候选用户
可以添加多个,每个指定一个`员工工号`
* 候选组
可以添加多个,每个指定一个`岗位代码`
**动态审批人、组**
与 上面类似,不过动态的审批人和组不是选择的,而是通过表达式计算得来。
`表达式`有两种情况
* 引用变量
比如`${initiator}`, 其中`initiator` 就是一个变量
* 调用 service 方法
比如`${orgStructure.getDirector(initiator)}`,其中 `orgStructure` 是一个自定的用于工作流的 `bean`,参数则可以使用任意的变量、常量等
> `execution` 是一个固有的、特殊的变量,指代 `org.activiti.engine.delegate.DelegateExecution`,非常有用
关于自定义在工作流中用的 `bean`
HAP 提供一个 接口 `com.hand.hap.activiti.custom.IActivitiBean`
实现这个接口并被定义注册为 bean,其中的任何 `public` 方法都可以在工作流表达式中直接调用。
默认 bean 的 名字就是引用名,也可以覆盖接口的默认实现,指定名称。
### 表单的标识Key
用于在待办明细页面中,动态渲染页面,设置为表单html页面的路径
### 表单属性
跟开始事件的表单属性一样
**审批动作**
Hap 的工作流在审批的时候默认有两个标准的动作:
* 同意(APPROVED)
* 拒绝(REJECTED)
当审批者点击按钮以后,审批动作 id 会被保存到流程的`共享变量`区域。
key 为 `approveResult`,这个变量可以直接在表达式中使用。
```
${approveResult=='REJECTED'}
```
流程设计时,可以控制这两个按钮显示的文本;也可以控制只显示其中的一个。
允许自定义额外的审批动作,自动动作的 id 同样会保存在 `approveResult` 变量中,通常需要流程显示处理(比如选择网关)。
<img width='920' src='/assets/custom-approve-action.png'/>
### 会签设置
会签通过设置`多实例`属性来实现,有串行和并行两种模式。
> 三条竖线表示实例会并行执行。 三条横线表示顺序执行。
<img width='920' src='/assets/activiti_parallel.png'/>
`集合` 应该返回一个字符串数组,表示用户集合
> 并行执行 会立刻为所有集合成员创建任务实例
> 串行执行 会依次创建任务实例,完成一个才会创建下一个
`元素变量` 遍历用户集合的单个对象变量名称
> 任务派遣 设置${元素变量}即可
`完成条件`
> activiti 标准会签,默认有 3 个参数, 可以作为结束条件考量依据.
分别是
* nrOfInstances(实例个数,根据集集合表达式中获取的对象个数确定)
* nrOfCompletedInstances(已经完成的实例个数,比如已经同意或者拒绝的)
* nrOfActiveInstances(还未完成的实例个数)
>
hap 自定义了两个:
* nrOfApproved(同意的实例个数)
* nrOfRejected(拒绝的实例个数)
## 服务任务(ServiceTask)
用来调用外部java类
有三种方式
### 类名
`类名`属性设置类的完整限定名
比如`com.hand.hap.activiti.demo.DemoServiceTask`
> 需要实现JavaDelegate接口
类只会初始化一次, 类似 servlet 的模式。
### 代理表达式
执行解析代理对象的表达式,直接通过表达式确定代理对象
比如`$(demoServiceTaskDelegate)`
> 参考com.hand.hap.activiti.demo.components.DemoServiceTaskDelegate 实现JavaDelegate接口和IActivitiBean
注意是用$符号,这是delegateExpression,与普通的表达式有区别
### 表达式
可以调用指定方法
`#{demoServiceTaskDelegate.method1(execution,task2Output)}`
# 3.网关
网关用来控制流程的流向
网关显示成菱形图形,内部有有一个小图标。 图标表示网关的类型
### 单一网关
内部图标是一个“X”
当流程执行到这个网关,所有外出顺序流都会被处理一遍。 通过设置`跳转条件`控制流程走向。
> 比如 ${approveResult == 'REJECTED'},表示拒绝后的流程走向。
可以设置`默认跳转`,当其他所以顺序流的跳转条件都返回false时触发。
> 注意如果多个顺序流的条件结果为true,单一网关只会选择一个执行。
### 并行网关
内部是一个“+”图标
并行网关的功能是基于进入和外出的顺序流的:
* 分支: 并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
* 汇聚: 所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关*不会解析条件*。 即使顺序流中定义了条件,也会被忽略。
流程设计器教程
---
# 1.开始事件和结束事件
---
对应着流程的开始和结束。
> 应该保证每个分支都可以从开始走到结束
## 开始事件
开始事件用来指明流程在哪里开始。开始事件的类型(流程在接收事件时启动, 还是在指定时间启动,等等),定义了流程如何启动。
> 一般使用第一个空的开始事件就可以了,需要手动启动流程
*属性*
![](/assets/act_vacationReq_bg_v.png)
* 初始化器
当流程启动时,把当前登录的用户保存到哪个变量名中,可以在表达式里直接引用该变量
* 表单属性
当执行到开始事件时,所有的流程变量都是可用的,但是可能需要一些用于表单展示的自定义变量,此时可以在表单属性中定义,将会保存到流程的`共享变量`区域
![](/assets/act_vacationReq_form.png)
如图是`请假流程`的开始事件的表单属性,在这里定义的变量,在`工作流测试页面`会自动渲染成表单
支持以下的几种表单属性类型:
* string (org.activiti.engine.impl.form.StringFormType)
* long (org.activiti.engine.impl.form.LongFormType)
* enum (org.activiti.engine.impl.form.EnumFormType)
* date (org.activiti.engine.impl.form.DateFormType)
* boolean (org.activiti.engine.impl.form.BooleanFormType)
> 括号里是作为流程变量存储的对应JAVA类
表达式和变量
如果设置了`变量`,需要通过变量名来引用变量,表达式同理
> 默认则是把id作为变量名使用
* 必须 如果勾选了,提交表单时没有提供该属性则抛出异常
* 可读 不勾选则不会进行自动渲染显示,但是还是可以提交
* 可写 不够勾选,还提交该属性,也会抛出异常
## 结束事件
结束事件表示(子)流程(分支)的结束。 结束事件都是触发事件。 这是说当流程达到结束事件,会触发一个结果。 结果的类型是通过事件的内部黑色图标表示的。
> 一般使用一个空的结束事件就行了,意味着到达事件时不会指定抛出的结果。 这样,引擎会直接结束当前执行的分支,不会做其他事情。
### 自动结束流程(HAP 扩展特性)
当一个任务被拒绝时,绝大多数情况下,这个流程应该直接结束。
但在工作流中,`审批拒绝``审批同意` 仅仅是给变量赋的参数值不同而已,如果希望流程结束,应该加一个 `选择网关`,判断审批结果是否为 `拒绝` ,然后引导流程走向`结束事件`
显然,这很繁琐,尤其是当流程中`人工任务`节点比较多时。
HAP 做了一个优化:
> 如果一个`人工任务` 的下一个节点仍然是 `任务(Task 类型)`,那么 HAP 将会自动插入一个`选择网关`和`终止事件` 来自动做结束流程的操作
这个优化在流程的图上是看不出来的,用户完全无感知。
# 2.任务
---
常用的是人工任务,和服务任务
## 人工任务(UserTask)
![](/assets/act_vacationReq_userTask.png)
### 任务派遣
**指定审批人、组**
点击`任务派遣` 弹出对话框
<img width='600' src='/assets/usertask-assignment.png'/>
其中:
* 指派对象
只能有一个人,可以点击`选择`按钮来从系统中选择员工
* 候选用户
可以添加多个,每个指定一个`员工工号`
* 候选组
可以添加多个,每个指定一个`岗位代码`
**动态审批人、组**
与 上面类似,不过动态的审批人和组不是选择的,而是通过表达式计算得来。
`表达式`有两种情况
* 引用变量
比如`${initiator}`, 其中`initiator` 就是一个变量
* 调用 service 方法
比如`${orgStructure.getDirector(initiator)}`,其中 `orgStructure` 是一个自定的用于工作流的 `bean`,参数则可以使用任意的变量、常量等
> `execution` 是一个固有的、特殊的变量,指代 `org.activiti.engine.delegate.DelegateExecution`,非常有用
关于自定义在工作流中用的 `bean`
HAP 提供一个 接口 `com.hand.hap.activiti.custom.IActivitiBean`
实现这个接口并被定义注册为 bean,其中的任何 `public` 方法都可以在工作流表达式中直接调用。
默认 bean 的 名字就是引用名,也可以覆盖接口的默认实现,指定名称。
### 表单的标识Key
用于在待办明细页面中,动态渲染页面,设置为表单html页面的路径
### 表单属性
跟开始事件的表单属性一样
**审批动作**
Hap 的工作流在审批的时候默认有两个标准的动作:
* 同意(APPROVED)
* 拒绝(REJECTED)
当审批者点击按钮以后,审批动作 id 会被保存到流程的`共享变量`区域。
key 为 `approveResult`,这个变量可以直接在表达式中使用。
```
${approveResult=='REJECTED'}
```
流程设计时,可以控制这两个按钮显示的文本;也可以控制只显示其中的一个。
允许自定义额外的审批动作,自动动作的 id 同样会保存在 `approveResult` 变量中,通常需要流程显示处理(比如选择网关)。
<img width='920' src='/assets/custom-approve-action.png'/>
### 会签设置
会签通过设置`多实例`属性来实现,有串行和并行两种模式。
> 三条竖线表示实例会并行执行。 三条横线表示顺序执行。
<img width='920' src='/assets/activiti_parallel.png'/>
`集合` 应该返回一个字符串数组,表示用户集合
> 并行执行 会立刻为所有集合成员创建任务实例
> 串行执行 会依次创建任务实例,完成一个才会创建下一个
`元素变量` 遍历用户集合的单个对象变量名称
> 任务派遣 设置${元素变量}即可
`完成条件`
> activiti 标准会签,默认有 3 个参数, 可以作为结束条件考量依据.
分别是
* nrOfInstances(实例个数,根据集集合表达式中获取的对象个数确定)
* nrOfCompletedInstances(已经完成的实例个数,比如已经同意或者拒绝的)
* nrOfActiveInstances(还未完成的实例个数)
>
hap 自定义了两个:
* nrOfApproved(同意的实例个数)
* nrOfRejected(拒绝的实例个数)
## 服务任务(ServiceTask)
用来调用外部java类
有三种方式
### 类名
`类名`属性设置类的完整限定名
比如`com.hand.hap.activiti.demo.DemoServiceTask`
> 需要实现JavaDelegate接口
类只会初始化一次, 类似 servlet 的模式。
### 代理表达式
执行解析代理对象的表达式,直接通过表达式确定代理对象
比如`$(demoServiceTaskDelegate)`
> 参考com.hand.hap.activiti.demo.components.DemoServiceTaskDelegate 实现JavaDelegate接口和IActivitiBean
注意是用$符号,这是delegateExpression,与普通的表达式有区别
### 表达式
可以调用指定方法
`#{demoServiceTaskDelegate.method1(execution,task2Output)}`
# 3.网关
网关用来控制流程的流向
网关显示成菱形图形,内部有有一个小图标。 图标表示网关的类型
### 单一网关
内部图标是一个“X”
当流程执行到这个网关,所有外出顺序流都会被处理一遍。 通过设置`跳转条件`控制流程走向。
> 比如 ${approveResult == 'REJECTED'},表示拒绝后的流程走向。
可以设置`默认跳转`,当其他所以顺序流的跳转条件都返回false时触发。
> 注意如果多个顺序流的条件结果为true,单一网关只会选择一个执行。
### 并行网关
内部是一个“+”图标
并行网关的功能是基于进入和外出的顺序流的:
* 分支: 并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
* 汇聚: 所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关*不会解析条件*。 即使顺序流中定义了条件,也会被忽略。
>参考[Activiti5.16中文手册](http://www.mossle.com/docs/activiti/index.html)
\ No newline at end of file
审计
---
融租易支持在代码级别记录用户对指定表的操作记录.
操作包括 : 插入, 更新, 删除.
## 1. 代码准备
再代码层面, 有两个注解 `AuditEnabled`,`AuditEntry`
#### 1.1 AuditEnabled
这个注解加在 DTO 类上, 表示这个 DTO 代表的 表需要开启审计功能.
```java
@Table("SYS_USER")
@AuditEnabled
public class User {
}
```
#### 1.2 AuditEntry
这个注解加在 `Service 接口的方法`上, 表示, 再这个方法的执行周期内开启审计功能.
> 这个方法内部可能会对多个表进行操作, 也可能调用其他的方法 f1, f1 在去操作其他的某个表.
这些调用涉及到的审计操作, 全部共享一个审计的 session (auditSessionId相同).
```java
public interface UserService {
@AuditEntry
User updateUser(User user);
}
```
## 2. 表结构
所有需要开启审计功能的表, 需要有一个与之相对应的 `审计表`.
命名规则 : `基表名`_a
可以在 `AuditEnabled` 注解中通过参数 `auditTable` 来指定.
>这个规则可以 实现接口 `IAuditTableNameProvider` 来自定义.
实现类需要定义为 spring bean
审计表字段`包含所有字段`, 但不包含`基表的唯一索引, 主键约束`
另外必须包括审计专用字段:
* `audit_id` (bigint) 自增长主键
* `audit_transaction_type` (varchar (10)) 审计操作(insert,update,delete)
* `audit_timestamp` (datetime) 审计时间
* `audit_session_id` (varchar(64)) 审计 session id
## 3 基本原理
`AuditInterceptor` 会拦截所有的 Mybatis 通过 dto 执行的操作,
根据当前有没有 `audit session` , 以及 dto 上的 AuditEnabled 注解 来决定是不是要执行 审计操作.
如果需要执行, 则动态生成 SQL 语句, 完成对新记录的`备份` 操作.
`插入`,`更新` 执行的操作是 先操作, 后记录.
`删除` 执行的操作是 先记录, 后操作.
也就是说, 审计表中插入的都是`最新的快照`.
> 基于这个原理的审计, 只能做到行级的记录, 无法具体到字段级.
但是结合一定的比较手段, 可以知道, 某次修改, 到底修改了哪些字段.
另外, 由于 audit session 的存在, 我们大概也可以知道,某次提交,同时修改了哪些数据.
当然操作的记录如何展示出来, 是需要另外开发页面的.
\ No newline at end of file
# 代码生成器
# 简介:
本功能可根据数据表生成相关的dto,mapper impl,service,controller与html,包含相关的增删改查功能。
**网页入口**:http:\/\/localhost:8080\/hel\/generator\/generator.html
## 使用说明:
**主页面:**
![](/assets/gnerator1.png)
**项目路径**:指向本机hap。
**包父路径:**指向hap的包路径。
**包路径:**为所要生成的包名。
**选择表:**选中所要维护的表。
**文件名:** 将自动根据表名生成对应名称(修改dto可选择是否根据dto修改其余文件,也可单独修改其余文件)( 文件创建之前会判断项目中是否有同名文件 ) 。
**是否创建:**勾选创建文件。
**是否覆盖:**勾选则可以覆盖同路径下同名文件。
**选择ftl模板:**可根据选择的ftl模板生成对应的html页面。
## 生成示例:
**1:相关文件**
![](/assets/generator2.png)
2:相关的dto
![](/assets/generator4.png)
**3:相关controller**(包含增删改查)
![](/assets/generator5.png)
**4:相关mapper**(包含基础resultMap)
![](/assets/generator6.png)
**5:生成基础的增删改查功能的html页面**
![](/assets/generator3.png)
**6:service文件** (mapper类似)
![](/assets/gernerator7.png)
# 部署
# tomcat
<a href="#tomcat"></a>
### 1. 关闭 eclipse server `Auto Reload` 特性
* 禁用默认值
<img src="/assets/disable_autoreload_all.png" width="50%"/>
* 单独禁用项
<img src="/assets/disable_autoreload_1.png" width="50%"/>
* 建议关闭 jar 扫描, 提高启动速度, 减少内存占用 , 修改 `catalina.properties `
```properties
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar
```
### 2. 内存设置
* Linux 修改 `catalina.sh` ,开始处添加
```bash
JAVA_OPTS='-Xms512m -Xmx2048m'
```
* Windows 修改 `catalina.bat`
```bash
set JAVA_OPTS=-Xms512m -Xmx2048m
```
### 3. 启用远程 JMX 连接
修改 catalina.sh , 搜索 `[ "$1" = "start" ]` , 在下方添加
```bash
JAVA_OPTS="-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=1099 \
-Djava.rmi.server.hostname=192.168.1.111 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false $JAVA_OPTS"
```
> linux 系统可以用 `` `hostname -i` `` 获取 ip
# Weblogic
<a href="#weblogic"></a>
weblogic 上的部署有些特殊,主要注意以下几点:
> **以下设置仅仅针对 weblogic 12 版本,其他版本可能存在通配符等问题**
### 1. JNDI 数据源
weblogic 上定义好数据源后,在 `config.properties` 中需要修改 jndi 的名字
```properties
db.jndiName=hap_dev
```
> tomcat 中是 java:comp/env/jdbc/hap_dev,这点不一样
### 2. 新增 weblogic.xml
在 WEB-INF 目录下新增 weblogic.xml
```bash
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app>
<container-descriptor>
<!--优先加载应用下的jar包,解决jar包冲突问题-->
<!--具体jar包冲突问题,具体解决-->
<prefer-application-packages>
<package-name>org.springframework.*</package-name>
<package-name>org.hibernate.*</package-name>
<package-name>javax.validation.*</package-name>
<package-name>javax.validation.spi.*</package-name>
<package-name>org.slf4j.*</package-name>
<package-name>com.fasterxml.*</package-name>
</prefer-application-packages>
<show-archived-real-path-enabled>true</show-archived-real-path-enabled>
</container-descriptor>
</weblogic-web-app>
```
### 3. weblogic部署异常解决方案
weblogic选择要部署的war包,若出现_wl_cls_gen.jar!这样的日志异常,可将war包,直接解压,进行部署。
上述 3 点在部署的时候请留意。
# 部署
# tomcat
<a href="#tomcat"></a>
### 1. 关闭 eclipse server `Auto Reload` 特性
* 禁用默认值
<img src="/assets/disable_autoreload_all.png" width="50%"/>
* 单独禁用项
<img src="/assets/disable_autoreload_1.png" width="50%"/>
* 建议关闭 jar 扫描, 提高启动速度, 减少内存占用 , 修改 `catalina.properties `
```properties
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar
```
### 2. 内存设置
* Linux 修改 `catalina.sh` ,开始处添加
```bash
JAVA_OPTS='-Xms512m -Xmx2048m'
```
* Windows 修改 `catalina.bat`
```bash
set JAVA_OPTS=-Xms512m -Xmx2048m
```
### 3. 启用远程 JMX 连接
修改 catalina.sh , 搜索 `[ "$1" = "start" ]` , 在下方添加
```bash
JAVA_OPTS="-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=1099 \
-Djava.rmi.server.hostname=192.168.1.111 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false $JAVA_OPTS"
```
> linux 系统可以用 `` `hostname -i` `` 获取 ip
# Weblogic
<a href="#weblogic"></a>
weblogic 上的部署有些特殊,主要注意以下几点:
> **以下设置仅仅针对 weblogic 12 版本,其他版本可能存在通配符等问题**
### 1. JNDI 数据源
weblogic 上定义好数据源后,在 `config.properties` 中需要修改 jndi 的名字
```properties
db.jndiName=hap_dev
```
> tomcat 中是 java:comp/env/jdbc/hap_dev,这点不一样
### 2. 新增 weblogic.xml
在 WEB-INF 目录下新增 weblogic.xml
```bash
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app>
<container-descriptor>
<!--优先加载应用下的jar包,解决jar包冲突问题-->
<!--具体jar包冲突问题,具体解决-->
<prefer-application-packages>
<package-name>org.springframework.*</package-name>
<package-name>org.hibernate.*</package-name>
<package-name>javax.validation.*</package-name>
<package-name>javax.validation.spi.*</package-name>
<package-name>org.slf4j.*</package-name>
<package-name>com.fasterxml.*</package-name>
</prefer-application-packages>
<show-archived-real-path-enabled>true</show-archived-real-path-enabled>
</container-descriptor>
</weblogic-web-app>
```
### 3. weblogic部署异常解决方案
weblogic选择要部署的war包,若出现_wl_cls_gen.jar!这样的日志异常,可将war包,直接解压,进行部署。
上述 3 点在部署的时候请留意。
# 日志管理 ELK(ElasticSearch+Logstash+Kibana)
## 简介:
**ElasticSearch:**一个开源分布式搜索引擎,具有分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等特点。<p>**Logstash:**一个完全开源的工具,可以对你的日志进行搜集,过滤,并将其储存供以后使用。<p>**Kibana:**为Logstash和ElasticSearch提供日志分析的友好Web界面,可以汇总,分析和搜索重要的数据日志。
##下载说明:
由于Logstash的运行依赖于Java环境, 而Logstash 1.5以上版本不低于java 1.7,因此推荐使用最新版本的Java。因为我们只需要Java的运行环境,所以可以只安装JRE,不过这里我依然使用JDK,请自行搜索安装<p>
ELK下载https://www.elastic.co/downloads/
## 使用说明:
### ElasticSearch:
#### 启动服务:
运行bin/elasticsearch.bat文件开启服务。查看127.0.0.1:9200出现如下页面运行成功<p>
#![](/assets/elasticsearch.png) <p>
#### 基础操作:es支持restful操作。<p>
获取对应文档:<p>
GET /[_index]/[_type]/_search <p>
上传文档: <p>
```javascript
PUT /[_index]/[_type]/[_id}
{ "first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests": [ "music" ] }
```
<p>
操作文档:http://es.xiaoleilu.com/030_Data/15_Get.html
#### 面向文档:
Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往完全不同,这也是Elasticsearch能够执行复杂的全文搜索的原因之一。<p>
ELasticsearch使用JSON,作为文档序列化格式。
以下使用JSON文档来表示一个用户对象:<p>
```javascript
{
"email": "john@smith.com",
"first_name": "John",
"last_name": "Smith",
"info": {
"bio": "Eco-warrior and defender of the weak",
"age": 25,
"interests": [ "dolphins", "whales" ]
},
"join_date": "2014/05/01"
}
```
在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以画一些简单的对比图来类比传统关系型数据库:<p>
Relational DB -> Databases -> Tables -> Rows -> Columns<p>
Elasticsearch -> Indices -> Types -> Documents -> Fields<p>
Elasticsearch集群可以包含多个索引(indices)(数据库),每一个索引可以包含多个类型(types)(表),每一个类型包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。
#### elasticsearch 分布式:
Elasticsearch为分布式而生
Elasticsearch致力于隐藏分布式系统的复杂性。以下这些操作都是在底层自动完成的:<p>
*将你的文档分区到不同的容器或者分片(shards)中,它们可以存在于一个或多个节点中。<p>
*将分片均匀的分配到各个节点,对索引和搜索做负载均衡。<p>
*冗余每一个分片,防止硬件故障造成的数据丢失。<p>
*将集群中任意一个节点上的请求路由到相应数据所在的节点。<p>
*无论是增加节点,还是移除节点,分片都可以做到无缝的扩展和迁移<p>
#### 集群部署:
伪分布式环境搭建:创建3个es环境,修改其config/elasticsearch.yml配置文件<p>
1:<p>
cluster.name: elasticsearch<p>
node.name: node1<p>
2:<p>
cluster.name: elasticsearch<p>
node.name: node2<p>
http.port: 9202<p>
3:<p>
cluster.name: elasticsearch<p>
node.name: node3<p>
http.port: 9203<p>
es会自动搜索同一网段下的cluster.name(集群名)相同的es做成集群
将indeces(索引)下的分片(默认一个索引5个主分片每个分片一个复制分片)平分到不同es节点(node)下。
#### elasticsearch-head插件安装:
是es集群管理插件。执行如下命令安装插件:<p>
elasticsearch/bin/plugin install mobz/elasticsearch-head<p>
打开:http://localhost:9200/_plugin/head/ <p>
下图为以上操作建立的集群。
![](/assets/es.png) <p>
相关文档:http://es.xiaoleilu.com/020_Distributed_Cluster/05_Empty_cluster.html <p>
### Logstash:
#### 运行:
logstash -e input { stdin { } } output { stdout { codec => rubydebug } } <p>
输入报文 helloworld 可以在控制台看到对应报文。<p>
2016-10-20T09:05:53.446Z LAPTOP-RKIO0CO4 hellowrold <p>
#### 通过配置文件运行:
在bin目录下创建配置文件test.conf: <p>
input为logstash配置输入流,logstash支持多种input方式包括file redis TCP UDP syslog lumberjack等。<p>
filter过滤logstash输入流,并可以解析输入流。<p>
ouput为logstash输出流,支持多种输出方式如elsaticearch file email tcp hdfs等,并可以配置相关参数如在es中配置索引名和type名<p>
<p> 示例使用file方式获取logstash输入流需将path更改为项目log地址<p>
```
input {
file{
path => "C:\Users\zjl\.IntelliJIdea2016.2\system\tomcat\Unnamed_hap-parent\logs\hap.log"
start_position =>"beginning"
}
}
filter{
if ([message] =~ "^\s") {
drop {}
}
grok{
match => ["message", " .* %{GREEDYDATA:level} \[%{GREEDYDATA:accountId}\] \[%{GREEDYDATA:requestId}\] %{GREEDYDATA:dao} - %{GREEDYDATA:info}\s"]
}
}
output {
stdout {
}
elasticsearch {
hosts => "localhost:9200"
}
}
```
<p>若使用tcp为logstash配置输入修改logback.xml文件添加输出方式<p>
```
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{accountId}] [%X{requestId}] %logger - %msg%n</pattern>
</encoder>
<destination>elk-hap:4560</destination></appender>
```
<p>
相关文档:http://kibana.logstash.es/content/logstash/plugins/input/file.html
<p>
在Logstash文件/bin目录下打开终端输入logstsh -f test.conf运行logstash解析日志文件 <p>
作为一个要长期运行的程序。发行包内都带有sysV或者systemd风格的启动程序配置,你只需要直接使用即可,以RPM为例 /etc/init.d/logstash 脚本中,会加载 /etc/init.d/functions 库文件,利用其中的 daemon 函数,将 logstash 进程作为后台程序运行。所以,你只需把自己写好的配置文件,统一放在 /etc/logstash/conf.d 目录下(注意目录下所有配置文件都应该是 .conf 结尾,且不能有其他文本文件存在。因为 logstash agent 启动的时候是读取全文件夹的),然后运行 service logstash start 命令即可。<p>
![](/assets/logstash.png)
<p>
### Kibana:
##### 打开文件bin目录下kibana.bat开启服务 <p> <p>
##### 浏览器打开localhost:5601查看通过logstash解析的日志文件
##### config/kibana.yml文件可进行对kibana相关配置 <p> <p>
![](/assets/kibana.png)
![](/assets/kibanaview.png)
# 锁机制
融租易提供两种锁机制, 满足项目上对数据并发修改的需求.
## 1.数据库锁
***
根据适当的参数进行物理锁定(数据库级锁),支持3种数据库:oracle,mysql,sqlserver。提供 API, 自动执行 sql
>
mysql,orcale 执行 select * from xxx where ... for update
> sqlserver 执行 select * from xxx with (ROWLOCK) where ...
> 都是数据库原生的锁机制
使用方式:
### 单数据源
在Java代码中,注入`DatabaseLockProvider`类,
```java
public class XXXServiceImpl {
@Autowired
private DatabaseLockProvider databaseLockProvider;
public int update (XXX dto, ...){
// 传入锁定对象,锁定数据库相关行
databaseLockProvider.lock(dto);
//...
}
}
```
### 多数据源
在Java代码中,`自己创建DatabaseLockProvider类`,指定`dataSource`
```java
public class XXXServiceImpl {
@Autowired
@Qualifier("xxxDataSource")
private DataSource dataSource;
private DatabaseLockProvider databaseLockProvider;
@Transactional(propagation = Propagation.REQUIRED)
public int update (XXX dto, ...){
databaseLockProvider = new DatabaseLockProvider(dataSource)
// 传入锁定对象,锁定数据库相关行
databaseLockProvider.lock(dto);
//...
}
}
```
> 事务提交以后,锁会自动释放
DatabaseLockProvider提供的接口
```java
//根据传入对象,自动加锁。
//确保bean上有@Table 注解,能自动解析出对应的表。 默认根据有@ID注解的字段进行加锁,确保id有值
public void lock(Object dto)
//根据传入对象和自定义的where条件,自动加锁。
//传入类似 "name = ? and age = ?" 的条件语句,根据whereCondition,依次对应传入属性对应的值。
public void lock(Object dto, String whereCondition, Object... whereParameter)
// 可以自定义表名,不必传入dto
public void lock(String tableName, String whereCondition, Object... whereParameter)
```
## 2.分布式锁
基于`redis`的锁,实现跨平台, 分布式锁
使用方式:
1.根据实际配置的redis,修改配置文件`config.properties`
```properties
redisson.server.url=127.0.0.1:6379
#redissson.server.password=
```
> 支持`多节点`配置,按照如下规范配置
>redisson.server.url=\
> 127.0.0.1:6379,\
> 10.211.103.142:6379
>内部使用redisson提供的`RedLock`进行加锁
2.在java代码中注入DistributedLockProvider
```java
public class XXXServiceImpl {
@Autowired
@Qualifier("distributeLockTemplate")
private DistributedLockProvider distributedLockProvider;
public int update (XXX dto, ...){
//使用回调方式,执行自己的业务逻辑
distributedLockProvider.lock(dto,new DistributedLockCallback<Object>() {
@Override
public Object process() {
//doSomething();
return null;
})
}
}
```
> 回调函数执行以后,锁都会自动释放
DistributedLockProvider提供的接口:
```java
/**
* 使用分布式锁,使用锁默认超时时间。
*
* @param lockKey
* 加锁对象,确保有@ID属性,建议BaseDto
* @param callback
* @return 回调函数返回的内容
*/
<T> T lock(Object lockKey, DistributedLockCallback<T> callback);
/**
* 使用分布式锁,使用锁默认超时时间。
*
* @param lockKey
* 确保唯一
* @param callback
* @return 回调函数返回的内容
*/
<T> T lock(String lockKey, DistributedLockCallback<T> callback);
/**
* 使用分布式锁。自定义锁的超时时间
*
* @param lockKey
* 加锁对象,确保有@ID属性,建议BaseDto
* @param callback
* @param leaseTime
* 锁超时时间。超时后自动释放锁。
* @param timeUnit
* @return 回调函数返回的内容
*/
<T> T lock(Object lockKey, DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit);
/**
* 使用分布式锁。自定义锁的超时时间
*
* @param lockKey
* 确保唯一
* @param callback
* @param leaseTime
* 锁超时时间。超时后自动释放锁。
* @param timeUnit
* @return 回调函数返回的内容
*/
<T> T lock(String lockKey, DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit);
/**
* 使用分布式锁,使用锁默认超时时间。
*
* @param lockKey
* 确保唯一
* @param callback
* @return 回调函数返回的内容
*
* @exception Exception 加锁过程的异常,比如锁已被占用,加锁失败
*/
<T> T tryLock(String lockKey, DistributedLockCallback<T> callback) throws Exception;
/**
* 使用分布式锁,使用锁默认超时时间。默认等待时间1秒
*
* @param lockKey
* 加锁对象,确保有@ID属性,建议BaseDto
* @param callback
* @return 回调函数返回的内容
*
* @exception Exception 加锁过程的异常,比如锁已被占用,加锁失败
*/
<T> T tryLock(Object lockKey, DistributedLockCallback<T> callback) throws Exception;
/**
* 使用分布式锁。自定义锁的超时时间
*
* @param lockKey
* 加锁对象,确保有@ID属性,建议BaseDto
* @param callback
* @param waitTime
* 获取锁等待时间,超过设置时间未获得锁则抛出异常
* @param leaseTime
* 锁超时时间。超时后自动释放锁。
* @param timeUnit
* waitTime,leaseTime 时间单位
* @return 回调函数返回的内容
*
* @exception Exception 加锁过程的异常,比如锁已被占用,加锁失败
*/
<T> T tryLock(Object lockKey, DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit)
throws Exception;
/**
* 使用分布式锁。自定义锁的超时时间
*
* @param lockKey
* 确保唯一
* @param callback
* @param waitTime
* 获取锁等待时间,超过设置时间未获得锁则抛出异常
* @param leaseTime
* 锁超时时间。超时后自动释放锁。
* @param timeUnit
* waitTime,leaseTime 的时间单位
* @return 回调函数返回的内容
*
* @exception Exception 加锁过程的异常,比如锁已被占用,加锁失败
*/
<T> T tryLock(String lockKey, DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit)
throws Exception;
```
# 用户安全策略
框架提供标准的用户安全策略接口
com.hand.hap.security.IUserSecurityStrategy
> 默认实现DefaultUserSecurityStrategy,order值为9999
编写自定义实现类,需要在 Spring 中定义为 bean (也可以通过 @Component之类的注解自动扫描注册)
可以实现下列方法,自定义安全策略:
### 登录成功以后,跳转至index页面前的策略
```java
ModelAndView loginVerifyStrategy(User user, HttpServletRequest request);
```
参数及返回类型说明
> - user 通过登录验证的User对象
- ModelAndView 重定向到该 ModelAndView
>
如果想要正常跳转到index页面,请返回null
注意:会根据order大小依次执行实现类的该方法,注意顺序
### 用户修改密码时,自定义密码复杂度
```java
void passwordVerifyStrategy(IRequest request ,User user,String oldPwd, String newPwd, String newPwdAgain) throws UserException;
```
参数及异常说明
> - user 通过登录验证的User对象
- oldPwd 旧密码
- newPwd 新密码
- newPwdAgain 再次输入的新密码
- 如果验证不通过,请抛出自定义的UserException
一般来说修改了复杂度验证策略,需要修改sys_config页面。
### 新建用户时的策略
```java
User beforeCreateUser(IRequest request ,User user)
```
可以对用户做统一的信息处理,如统一设置一些属性值,注意如果实现了该方法,一定要返回处理后的User对象,默认返回传入的user
> 实现IAuthenticationSuccessListener
接口, 是在用户登录验证成功时执行.
>
一般用来往session里面放一些值,或其他逻辑,如果抛出异常,则会在login页面显示异常信息,注意区别。
# 部署
# tomcat
<a href="#tomcat"></a>
### 1. 关闭 eclipse server `Auto Reload` 特性
* 禁用默认值
<img src="/assets/disable_autoreload_all.png" width="50%"/>
* 单独禁用项
<img src="/assets/disable_autoreload_1.png" width="50%"/>
* 建议关闭 jar 扫描, 提高启动速度, 减少内存占用 , 修改 `catalina.properties `
```properties
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar
```
### 2. 内存设置
* Linux 修改 `catalina.sh` ,开始处添加
```bash
JAVA_OPTS='-Xms512m -Xmx2048m'
```
* Windows 修改 `catalina.bat`
```bash
set JAVA_OPTS=-Xms512m -Xmx2048m
```
### 3. 启用远程 JMX 连接
修改 catalina.sh , 搜索 `[ "$1" = "start" ]` , 在下方添加
```bash
JAVA_OPTS="-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=1099 \
-Djava.rmi.server.hostname=192.168.1.111 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false $JAVA_OPTS"
```
> linux 系统可以用 `` `hostname -i` `` 获取 ip
# Weblogic
<a href="#weblogic"></a>
weblogic 上的部署有些特殊,主要注意以下几点:
> **以下设置仅仅针对 weblogic 12 版本,其他版本可能存在通配符等问题**
### 1. JNDI 数据源
weblogic 上定义好数据源后,在 `config.properties` 中需要修改 jndi 的名字
```properties
db.jndiName=hap_dev
```
> tomcat 中是 java:comp/env/jdbc/hap_dev,这点不一样
### 2. 新增 weblogic.xml
在 WEB-INF 目录下新增 weblogic.xml
```bash
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app>
<container-descriptor>
<!--优先加载应用下的jar包,解决jar包冲突问题-->
<!--具体jar包冲突问题,具体解决-->
<prefer-application-packages>
<package-name>org.springframework.*</package-name>
<package-name>org.hibernate.*</package-name>
<package-name>javax.validation.*</package-name>
<package-name>javax.validation.spi.*</package-name>
<package-name>org.slf4j.*</package-name>
<package-name>com.fasterxml.*</package-name>
</prefer-application-packages>
<show-archived-real-path-enabled>true</show-archived-real-path-enabled>
</container-descriptor>
</weblogic-web-app>
```
### 3. weblogic部署异常解决方案
weblogic选择要部署的war包,若出现_wl_cls_gen.jar!这样的日志异常,可将war包,直接解压,进行部署。
上述 3 点在部署的时候请留意。
# 计划任务
### 1、任务周期定义
![](/assets/task_define.png)
新建一条计划任务后,可点击任务计划修改,打开计划修改页面:
![](/assets/schedule_edit.png)
计划任务可以定义五种频率,分别是:月、周、日、时、分。
* 月:可定义每几个月的第几天(可选择多个,使用逗号隔开)执行,同时可以指定执行的具体时分。
* 周:可定义每几个周的周几执行,同时可指定执行的具体时分。
* 日:可定义每几天执行一次,同时可指定执行的具体时分。
* 时:可定义每几个小时执行一次。
* 分:可定义每几分钟执行一次。
### 2、任务维护
可在任务维护主界面进行新增、修改、删除操作。
![](/assets/task_maintain.png)
新增一条任务记录之后可以对该任务进行参数配置和权限控制:
#### 2.1 任务配置
点击任务配置,打开任务配置明细弹窗,可在此处添加、删除、修改任务所需参数。 ![](/assets/task_parameter.png)
#### 2.2 权限控制
点击权限设置,打开权限控制明细弹窗,可在此处对任务权限人进行添加、删除、修改操作:
角色代码为权限人的代码,角色名称为权限人的名字,起始日期和结束日期控制该人员可对该任务操作的时间。 ![](/assets/task_authority.png)
#### 2.3 任务类的编写
想要自定义一个job,必须要继承一个抽象类AbstractJob,然后在`safeExecute()`方法中执行业务代码,示例代码如下:
```java
public class demoJob extends AbstractJob{
@Autowired
private xxxService service;//业务类
@Override
public void safeExecute(JobExecutionContext jobExecutionContext) throws Exception {
//map中可以获取上方定义的参数,key为参数名称,value为参数值
JobDataMap map = jobExecutionContext.getMergedJobDataMap();
try {
service.dosomething();
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
exception = e;
throw e;
}
if (exception != null) {
setExecutionSummary(exception.getClass().getName() + ":" + exception.getMessage());
} else {
setExecutionSummary("执行完成!" );
}
}
@Override
public boolean isRefireImmediatelyWhenException() {
//任务发生异常时候进行的动作
//false 挂起当前JOB等待处理
//true 继续执行
return false;
}
}
```
注意:
* 调用`setExecutionSummary()`方法可以在执行记录中,记录你的业务数据的记录,或者抛出的异常的具体信息。
* 在上下文中获取JobDataMap可以拿到你在任务配置中定义的参数
### 3、计划任务工作台
#### 3.1 新建任务
1.点击左上角的新建任务按钮,可以进入以下界面创建一个新的任务:
![](/assets/job_create.png)
此处任务名称必须唯一,在lov中选择对应的任务代码。
2.下拉滚动条,可以看到如下所示的界面:
![](/assets/job_create2.png)
周期性:表示一个cron任务,可以在计划名称的lov中选定自己定义的一个周期,然后计划任务可以根据任务周期定义功能中的执行频率,以及执行的开始和结束时间,进行执行。
制定时间:选择执行的时间,然后计划任务可以根据选中的执行时间,开始执行。
立即执行:可以直接出发你定义的计划任务。
以上三种方式,在定义时只能选择一种,点击保存后,任务会自动开始进行调度。
注意:
* 在集群环境中,请确保各个节点的机器的时间一致,否则会造成job的调度出现问题。
* 在集群环境中,请不要用节点外的机器操作job。
* 在开发环境中,在config.properties中调度器自动启动的状态应该默认设置为false,在正式环境中应该设置为true(设置为true,表示调度器随着服务器启动而启动):
在开发环境中,由于隐藏了调度的启动和关闭按钮,所以如果想打开调度器,必须要手动输入url:/job/scheduler/start,否则你的计划任务将不会执行。
#### 3.2 任务状态
1.任务执行时,状态不同,颜色也不同:
![](/assets/job_status.png)
状态说明:
* 正在执行,颜色显示为绿色。
* 发生异常,颜色显示为红色。
* 任务暂停,颜色显示为橙色。
* 任务完成,颜色显示为灰色。
* 任务阻塞,颜色显示为黑色。
2.想要修改任务的状态,或者删除任务,可以勾选对应的计划任务进行操作:
![](/assets/job_change_status.png)
备注:执行完成的任务无法恢复到执行状态。
### 4、执行记录
#### 4.1 查看任务的执行记录
![](/assets/job_infomation.png)
参数说明:
* 任务名称:计划任务名称
* 任务组:计划任务所属组别
* 任务状态:任务完成状态,正常执行完为FINISH,执行出错为FAILED,任务被禁止为VETOED
* 执行概要:任务执行过程中的信息,如执行结果,异常信息等(既setExecutionSummary方法中所设置的值)。
* 上次执行时间:上一次任务预计执行的时间点。
* 计划执行时间:预计完成上一次任务后,根据指定的执行间隔推算的本次任务执行的时间。
* 下次执行时间:预计执行完本次任务后,根据指定的执行间隔推算的下一次执行时间点。
* 实际执行时间:本次任务实际执行的时间点。
# 计划任务
### 1、任务周期定义
![](/assets/task_define.png)
新建一条计划任务后,可点击任务计划修改,打开计划修改页面:
![](/assets/schedule_edit.png)
计划任务可以定义五种频率,分别是:月、周、日、时、分。
* 月:可定义每几个月的第几天(可选择多个,使用逗号隔开)执行,同时可以指定执行的具体时分。
* 周:可定义每几个周的周几执行,同时可指定执行的具体时分。
* 日:可定义每几天执行一次,同时可指定执行的具体时分。
* 时:可定义每几个小时执行一次。
* 分:可定义每几分钟执行一次。
### 2、任务维护
可在任务维护主界面进行新增、修改、删除操作。
![](/assets/task_maintain.png)
新增一条任务记录之后可以对该任务进行参数配置和权限控制:
#### 2.1 任务配置
点击任务配置,打开任务配置明细弹窗,可在此处添加、删除、修改任务所需参数。 ![](/assets/task_parameter.png)
#### 2.2 权限控制
点击权限设置,打开权限控制明细弹窗,可在此处对任务权限人进行添加、删除、修改操作:
角色代码为权限人的代码,角色名称为权限人的名字,起始日期和结束日期控制该人员可对该任务操作的时间。 ![](/assets/task_authority.png)
#### 2.3 任务类的编写
想要自定义一个job,必须要继承一个抽象类AbstractJob,然后在`safeExecute()`方法中执行业务代码,示例代码如下:
```java
public class demoJob extends AbstractJob{
@Autowired
private xxxService service;//业务类
@Override
public void safeExecute(JobExecutionContext jobExecutionContext) throws Exception {
//map中可以获取上方定义的参数,key为参数名称,value为参数值
JobDataMap map = jobExecutionContext.getMergedJobDataMap();
try {
service.dosomething();
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
exception = e;
throw e;
}
if (exception != null) {
setExecutionSummary(exception.getClass().getName() + ":" + exception.getMessage());
} else {
setExecutionSummary("执行完成!" );
}
}
@Override
public boolean isRefireImmediatelyWhenException() {
//任务发生异常时候进行的动作
//false 挂起当前JOB等待处理
//true 继续执行
return false;
}
}
```
注意:
* 调用`setExecutionSummary()`方法可以在执行记录中,记录你的业务数据的记录,或者抛出的异常的具体信息。
* 在上下文中获取JobDataMap可以拿到你在任务配置中定义的参数
### 3、计划任务工作台
#### 3.1 新建任务
1.点击左上角的新建任务按钮,可以进入以下界面创建一个新的任务:
![](/assets/job_create.png)
此处任务名称必须唯一,在lov中选择对应的任务代码。
2.下拉滚动条,可以看到如下所示的界面:
![](/assets/job_create2.png)
周期性:表示一个cron任务,可以在计划名称的lov中选定自己定义的一个周期,然后计划任务可以根据任务周期定义功能中的执行频率,以及执行的开始和结束时间,进行执行。
制定时间:选择执行的时间,然后计划任务可以根据选中的执行时间,开始执行。
立即执行:可以直接出发你定义的计划任务。
以上三种方式,在定义时只能选择一种,点击保存后,任务会自动开始进行调度。
注意:
* 在集群环境中,请确保各个节点的机器的时间一致,否则会造成job的调度出现问题。
* 在集群环境中,请不要用节点外的机器操作job。
* 在开发环境中,在config.properties中调度器自动启动的状态应该默认设置为false,在正式环境中应该设置为true(设置为true,表示调度器随着服务器启动而启动):
在开发环境中,由于隐藏了调度的启动和关闭按钮,所以如果想打开调度器,必须要手动输入url:/job/scheduler/start,否则你的计划任务将不会执行。
#### 3.2 任务状态
1.任务执行时,状态不同,颜色也不同:
![](/assets/job_status.png)
状态说明:
* 正在执行,颜色显示为绿色。
* 发生异常,颜色显示为红色。
* 任务暂停,颜色显示为橙色。
* 任务完成,颜色显示为灰色。
* 任务阻塞,颜色显示为黑色。
2.想要修改任务的状态,或者删除任务,可以勾选对应的计划任务进行操作:
![](/assets/job_change_status.png)
备注:执行完成的任务无法恢复到执行状态。
### 4、执行记录
#### 4.1 查看任务的执行记录
![](/assets/job_infomation.png)
参数说明:
* 任务名称:计划任务名称
* 任务组:计划任务所属组别
* 任务状态:任务完成状态,正常执行完为FINISH,执行出错为FAILED,任务被禁止为VETOED
* 执行概要:任务执行过程中的信息,如执行结果,异常信息等(既setExecutionSummary方法中所设置的值)。
* 上次执行时间:上一次任务预计执行的时间点。
* 计划执行时间:预计完成上一次任务后,根据指定的执行间隔推算的本次任务执行的时间。
* 下次执行时间:预计执行完本次任务后,根据指定的执行间隔推算的下一次执行时间点。
* 实际执行时间:本次任务实际执行的时间点。
### 5、后台创建周期任务
由于在某些业务模块中,可能需要直接在后端业务代码中定义计划任务,那么这个时候,融租易中提供了创建计划任务的接口可以供直接调用。
#### 5.1 通过post请求直接创建周期任务
访问`"/hls/job/create"`地址,必须要为post请求类型,方法返回的值为jobId,请求体中的参数也必须为json格式,如下:
```
{
"jobCreateDto": {
"jobClassName":"default",
"jobName": "jobname",
"jobGroup": "DEFAULT",
"triggerType": "CRON"
},
"taskId": "10001",
"scheduleName": "schname",
"defineStartTime": "14000000",
"defineEndTime": "140000000",
"businessParam": {
"contractId": "1"
}
}
```
json格式说明:
* jobCreateDto:该元素中,只有job是自己定义的,其他全部按照如上所示的规范填写。
* taskId:taskId表示在任务维护中,定义的任务的ID,创建的job执行的类实际上是根据任务中定义的类,所以请确保此处填写无误。
* scheduleName:scheduleName表示任务周期中定义的周期名,任务会按照scheduleName对应的周期执行。
* defineStartTime:自定义的任务开始时间,如果有需要则填写,否则直接根据周期定义中的开始时间执行,传到后台的值必须为Long类型。
* defineEndTime:自定义的任务结束时间,如果有需要则填写,否则直接根据周期定义中的时间结束,传到后台的值必须为Long类型。
* businessParam:业务所需的参数,填写后可以在任务执行时获取到。
#### 5.2 通过service中的方法创建周期任务
注入`HlsJobCreateService`,调用的方法为`createHlsJob(HlsJobDto hlsJobDto, IRequest requestCtx)`,其中HlsJobDto参数可以与上述的json相互转换,实现的效果也是一致的。
\ No newline at end of file
# 融租易开发手册
* [GitBook使用](/gitbook.md)
---
* I. 开发环境准备
* [1.1 Git 使用](/git-guide.md "git指令")
* [1.2 Maven 使用](/maven.md "maven使用")
* [1.3 开发环境搭建](/project-create.md)
* [1.4 更新项目的HEL依赖版本](/project-update.md)
* [1.5 Liquibase](/liquibase-use.md)
* II. 项目开发规范
* [2.1 项目开发规范](/codeStyle.md#backEndDev)
* [2.2 编码规范](/codeStyle.md#backEndName)
* 2.3 Checkstyle
* III. 后端开发
* IV. 前端开发
* 4.1 前端开发说明 \(1.0\)
* [4.2 hlsCombobox](/前端组件/hlsCombobox.md)
* [4.3 hlsDataSource](/前端组件/hlsDataSource.md)
* [4.4 hlsMaskedTextBox](/前端组件/hlsMaskedTextBox.md)
* [4.5 hlsPage](/前端组件/hlsPage.md)
* [4.6 hlsTextArea](/前端组件/hlsTextArea.md)
* [4.7 hlsTlEdit](/前端组件/hlsTlEdit.md)
* [4.8 hlsDatePicker](/前端组件/hlsDatePicker.md)
* [4.9 hlsDateTimePicker](/前端组件/HlsDateTimePicker.md)
* [4.10 hlsLov](/前端组件/HlsLov.md)
* [4.11 hlsToolBar](/前端组件/HlsToolBar.md)
* [4.12 TabStrip](/前端组件/TabStrip.md)
* [4.13 hlsForm](/前端组件/hlsForm.md)
* [4.14 hlsCombobox](/前端组件/HlsCombobox.md)
* [4.15 hlsCheckBox](/前端组件/HlsCheckBox.md)
* [4.16 抽屉通用方法](/前端组件/box.md)
* [4.17 Grid](/前端组件/Grid.md)
* [4.18 DataSource](/前端组件/DataSource.md)
* [4.19 hlsGridBox](/前端组件/hlsGridBox.md)
* [4.20 NumericTextBx](/前端组件/NumericTextBox.md)
* [4.21 HlsNavigationBar](/前端组件/HlsNavigationBar.md)
* V. 框架功能描述
* [5.1 计划任务](/框架功能描述/计划任务.md)
* [5.2 消息机制](/框架功能描述/消息机制.md)
* [5.3 部署](/后端开发/deployment.md)
# 融租易开发手册
* [GitBook使用](/gitbook.md)
---
* I. 开发环境准备
* [1.1 Git 使用](/git-guide.md "git指令")
* [1.2 Maven 使用](/maven.md "maven使用")
* [1.3 开发环境搭建](/project-create.md)
* [1.4 更新项目的HEL依赖版本](/project-update.md)
* [1.5 Liquibase](/liquibase-use.md)
* II. 项目开发规范
* [2.1 项目开发规范](/codeStyle.md#backEndDev)
* [2.2 编码规范](/codeStyle.md#backEndName)
* 2.3 Checkstyle
* III. 后端开发
* [3.1 用户安全策略](/后端开发/user-security-strategy.md)
* [3.2 审计](/后端开发/audit.md)
* [3.3 锁机制](/后端开发/lock.md)
* [3.4 日志管理](/后端开发/elk.md)
* [3.5 代码生成器](/后端开发/code-generator.md)
* [3.6 跨域访问](/后端开发/3.24-cors.md)
* IV. 前端JavaScript开发
* [4.1 前端开发说明 \(1.0\)](/front-kendoui.md)
* [4.2 字段级通用方法](/common-field-javascript.md)
* [4.3 窗口级通用方法](/common-window-javascript.md)
* [4.4 锁屏和解屏通用方法](/common-mask-javascript.md)
* V. 前端UI开发
* [5.1 hlsCombobox(下拉框)](/前端组件/hlsCombobox.md)
* [5.2 hlsDataSource](/前端组件/hlsDataSource.md)
* [5.3 hlsMaskedTextBox](/前端组件/hlsMaskedTextBox.md)
* [5.4 hlsPage](/前端组件/hlsPage.md)
* [5.5 hlsTextArea](/前端组件/hlsTextArea.md)
* [5.6 hlsTlEdit](/前端组件/hlsTlEdit.md)
* [5.7 hlsDatePicker](/前端组件/hlsDatePicker.md)
* [5.8 hlsDateTimePicker](/前端组件/HlsDateTimePicker.md)
* [5.9 hlsLov](/前端组件/HlsLov.md)
* [5.10 hlsToolBar](/前端组件/HlsToolBar.md)
* [5.11 TabStrip](/前端组件/TabStrip.md)
* [5.12 hlsForm](/前端组件/hlsForm.md)
* [5.13 hlsCombobox](/前端组件/HlsCombobox.md)
* [5.14 hlsCheckBox](/前端组件/HlsCheckBox.md)
* [5.15 Grid](/前端组件/Grid.md)
* [5.16 DataSource](/前端组件/DataSource.md)
* [5.17 hlsGridBox](/前端组件/hlsGridBox.md)
* [5.18 NumericTextBx](/前端组件/NumericTextBox.md)
* [5.19 HlsNavigationBar](/前端组件/HlsNavigationBar.md)
* VI. 框架功能描述
* [6.1 计划任务](/框架功能描述/计划任务.md)
* [6.2 RabbitMq消息队列](/后端开发/RabbitMq消息队列.md)
* [6.3 应用服务部署(tomcat/weblogic)](/框架功能描述/deployment.md)
* [6.4 合同文本生成](/docx4j.md)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment