EMAP开发API
更新日志
日期 |
修改内容 |
备注 |
2016-09-14 |
增加“2.6系统已使用的参数” |
|
2016-09-22 |
修改第2部分的标题 |
|
2016-09-27 |
增加“1.1.1.4.2获取配置中临时文件的有效时间” 修改“1.1.1.4.8将临时文件保存为正式文件” |
|
2016-09-28 |
增加“1.2.2权限配置” |
|
2016-09-30 |
修改“1.1.1.6.3下载时访问权限检查接口” |
|
2016-10-08 |
增加“2.7 注册系统应用” |
|
2016-10-10 |
修改“1.2.2.3配置说明” |
|
2016-10-12 |
修改“1.1.1.6.3下载时访问权限检查接口” |
|
2016-10-14 |
增加“3.9事务” |
|
2016-10-24 |
修改“1.1.1.4 Ajax提交地址” |
|
2016-11-03 |
增加“1.4.5ws的切面”及“1.4.6调整成员的定义名称” |
|
2016-11-07 |
增加“3.7.8其他说明”如查询时对一些特殊情况的处理 |
|
2016-11-08 |
修改“3.6.5”增加了忽略主键冲突的说明 |
|
2017-01-19 |
修改“1.4.4.1”增加用户信息的相关说明 |
|
2017-02-09 |
修改“1.4.5”webservice的监听 修改“3.3.4”配置的优先级 |
|
2017-02-17 |
新增“1.6”emapDBVMC |
|
2017-03-24 |
新增“3.10”国际化 |
|
2017-04-05 |
新增“3.11”生成webservice客户端代码 |
|
2017-04-20 |
合并“3.7.4和3.7.5”数据提交格式,增加了说明及样例 |
|
2017-04-27 |
修改“2.6”系统已使用的参数,增加保留参数 |
|
2017-05-31 |
修改“1.4”emapWS,增加了标注的说明 |
|
2017-07-11 |
新增“1.2.1”认证的处理流程 |
|
2017-07-25 |
修改“1.1.2.3.2”增加导出模板时导出字典表 修改“1.1.2.3.4”增加webservice导入时分页提交功能 新增“1.1.2.4.9”增加新的导入过程分析接口 |
|
2017-08-08 |
新增“3.6.3”check节点的说明 |
|
2017-08-22 |
新增“3.5.5”在spring中使用配置文件的值 |
|
2017-09-01 |
新增“1.2.4”登出的说明 |
|
2017-12-14 |
新增“3.5.6”自定义的静态资源处理 |
|
2018-07-18 |
新增“3.5.1.1”JSP中调用系统功能 |
|
2018-08-24 |
新增“3.10.6”多语言的国际化 |
|
1. 公共组件
1.1 emapcomponent
1.1.1 上传下载
1.1.1.1 变量说明
1、[root], emap运行环境部署时使用的上下文根
2、[component],附件上传下载所在的应用,默认名称为emapcomponent
3、[params],其他条件参数,如:imageType=1
4、{scope},调用上传的应用中页面模型的ID
5、{fileToken},附件的令牌,调用上传的应用所属实体对象字段的值,每个字段值可以对应多个附件
6、{attatchId},附件唯一标识的wid值
1.1.1.2 上传过程
1、根据scope和fileToken上传到临时目录
2、系统根据附件类型判断:大小、格式、登录权限
3、自定义判断(根据应用是否扩展过)
4、保存业务数据时,把临时文件保存到正式目录下
5、自定义保存(根据应用是否扩展过)
注:系统也提供了定时清理临时文件的功能,间隔时间配置在fileConfig.xml文件中
1.1.1.3 下载过程
1、下载请求
2、自定义生成文件流(根据应用是否扩展过)
3、读取本地文件流(如果有第2步且成功,则本步忽略)
1.1.1.4 Ajax提交地址
1.
1.1.1.1
1.1.1.2
1.1.1.3
1.1.1.4
1.1.1.4.1 获取当前系统时间
【url】:
/[root]/sys/emapcomponent/file/upload/getServerTime.do
【样例】:
返回结果:

1.1.1.4.2 获取配置中临时文件的有效时间
【url】:
/[root]/sys/emapcomponent/file/upload/tempOverdueTime.do
【参数】:
无
【样例】:


1.1.1.4.3 获取类型配置
【url】:
/[root]/sys/emapcomponent/file/upload/getFileConfig.do
【参数】:
storeId:必填项,附件类型的ID,来源于emapcomponent应用中的配置fileConfig.xml
【样例】:
js代码:

返回结果:

1.1.1.4.4 上传到临时文件
【url】:
/[root]/sys/emapcomponent/file/uploadTempFile/{scope}/{fileToken}.do
或者
/[root]/sys/emapcomponent/file/uploadTempFile.do
【参数】:
storeId:必填项,附件类型的ID,来源于emapcomponent应用中的配置fileConfig.xml
fileName:选填,自定义的附件名称,不用带上后缀,会自动取原文件名后缀,不填的话就默认使用原文件名。
isSingle:选填,如果为“1”,表示在上传前会先将该token下的所有临时文件清理掉。
customer:选填,实现了ICustomerFileOp接口的service名称,会执行其中的fileCheck方法,方法的返回值会作为请求返回的错误信息
app:选填,上述service所在应用名称
【样例】:
js代码:

返回结果:

1.1.1.4.5 删除已上传的临时文件
【url】:
/[root]/sys/emapcomponent/file/deleteTempFile/{scope}/{fileToken}/{fileWid}.do
或者
/[root]/sys/emapcomponent/file/deleteTempFile.do
二者都限定为POST请求
【参数】:
scope:上传时指定的scope标识,必填
fileToken:上传时指定的文件token,必填
fileWid:指定要删除的文件id,选填,不填则会删除token下所有临时文件
【样例】:
js代码:

或者

返回结果:
注意:success为true仅表示请求提交到了后台,至于文件路径是否正确,是否正确删除,则不能保证。

1.1.1.4.6 获取上传的临时文件
【url】:
/[root]/sys/emapcomponent/file/getTempFile/{scope}/{fileToken}/{fileId}.do
或者
/[root]/sys/emapcomponent/file/getTempFile.do
【参数】:
scope:上传时指定的scope标识,必填
fileToken:上传时指定的文件token,必填
fileId:临时文件的id,必填
【样例】:
直接输入地址,弹出下载窗口:

1.1.1.4.7 裁剪图片临时文件,生成一个新的临时文件
【url】:
/[root]/sys/emapcomponent/file/cutTempFile/{scope}/{fileToken}/{fileWid}.do
支持裁剪的格式包括:bmp,gif,jpg,jpeg,png,wbmp
【参数】:
x:必填,剪裁区域左上角x坐标
y:必填,剪裁区域左上角y坐标
width:必填,剪裁区域宽度
height:必填,剪裁区域高度
actualWidth:选填,保存文件的实际宽度(缩放后的)
actualHeight:选填,保存文件的实际高度(缩放后的)
【样例】:
剪裁的方式为:
原图中,以(x,y)为左上角顶点,width,height为宽高的矩形部分。然后根据actualWidth和actualHeight对截取部分进行缩放,最后对缩放结果进行保存。
js代码:

返回结果:

1.1.1.4.8 将临时文件保存为附件(正式文件)
【url】:
/[root]/sys/emapcomponent/file/saveAttachment/{scope}/{fileToken}.do
或者
/[root]/sys/emapcomponent/file/saveAttachment.do
【参数】:
scope:必填
fileToken:必填
attachmentParam:必填,包含storeId的json对象。其中可以包含一个key为orderMap的对象,存储wid----orderNum的键值对。后台将fileToken下所有附件的wid与orderMap中的wid做比对,符合的会将orderMap中的orderNum更新到数据库中对应附件的orderNum字段。
widsOfAttsToDelete:选填,表示在保存附件之前需要先删除的正式文件的wid,多个wid用逗号隔开
saveAppName:选填,saveBeanName指定的service所在的app名称
saveBeanName:选填,实现了ICustomerFileOp接口的service名称,会执行其中的fileSave方法,若未成功往数据库里插入这条记录,则还是会执行默认的保存附件方法。
authAppName:选填,authBeanName指定service所在的app名称
authBeanName:选填,实现了ICustomerFileOp接口的service名称。之后所有对附件操作都会执行其中的fileDownloadAuth方法进行权限检查。
【样例】:
js代码:

返回结果:

1.1.1.4.9 删除附件:根据token删除附件
【url】:
/[root]/sys/emapcomponent/file/deleteFileByToken/{fileToken}.do?[params]
或者
/[root]/sys/emapcomponent/file/deleteFileByToken.do
【参数】
fileToken:必填,文件token
[params]:选填,额外的参数,fileName、storeId等等
【样例】:
js代码:

返回结果:

1.1.1.4.10 获取指定令牌下所有的附件信息
【url】:
/[root]/sys/emapcomponent/file/getUploadedAttachment/{fileToken}.do
或者
/[root]/sys/emapcomponent/file/getUploadedAttachment.do
【参数】:
fileToken:必填
【样例】:
js代码:

返回结果:

1.1.1.4.11 获取多个令牌下所有的附件信息,结果会按令牌分组
【url】:
/[root]/sys/emapcomponent/file/getUploadedAttachmentBatch.do
【参数】:
fileTokenArrayString:必填,多个token值以json格式组织起来的字符串
【样例】:


1.1.1.4.12 下载附件:根据附件id
【url】:
/[root]/sys/emapcomponent/file/getAttachmentFile/{fileIdx}.do
或者
/[root]/sys/emapcomponent/file/getAttachmentFile.do
【参数】:
fileIdx:必填,附件id
app:选填,customerFile指定的service所在的app
customerFile:选填,实现了ICustomerFileOp接口的service名称,会执行其中的generateFile方法,若返回null,则还是会执行默认的生成文件流的方法。
【样例】:
可直接输入如下地址,弹出下载对话框:

1.1.1.4.13 下载附件:根据附件token
【url】:
/[root]/sys/emapcomponent/file/getFileByToken/{fileToken}.do
或者
/[root]/sys/emapcomponent/file/getFileByToken.do
【参数】:
fileToken:必填,文件token
app:选填,customerFile指定的service所在的app
customerFile:选填,实现了ICustomerFileOp接口的service名称,会执行其中的generateFile方法,若返回null,则还是会执行默认的生成文件流的方法。
【样例】:
若同一fileToken下存在多个文件,只会返回第一个
可直接输入如下地址,弹出下载对话框:

1.1.1.4.14 下载图片:下载token下指定类型图片
【url】:
/[root]/sys/emapcomponent/file/ getSingleImageByToken.do
1.如果有多个符合条件的图片,只会下载第一张。
2.没有符合条件的图片时,返回404页面。
【参数】:
fileToken:选填,默认为”0”,表示获取哪个尺寸的图片文件,
type:选填,获取图片的类型,可以填写如下三种类型
“ORIGINAL”:表示原始尺寸,缺省类型
“MIDDLE”:表示中型尺寸
“SMALL”:表示小型尺寸
【样例】:
访问如下地址:

弹出下载框:

1.1.1.4.15 批量下载:根据单个fileToken,zip格式返回
【url】:
/[root]/sys/emapcomponent/file/getFileBatchByToken/{fileToken}.do
【参数】:
fileToken:必填,文件token
imageSize:选填,默认为”0”,表示获取哪个尺寸的图片文件,
”0”表示获取原始类型图片,
“1”表示获取中尺寸类型图片,
“2”表示获取小尺寸类型图片。
nameType: 选填,默认为”0”,表示附件的命名方式,
“0”表示以唯一编号命名文件,
“1”表示以原文件名命名文件,此方式会导致所限定token下相同文件名的文件会互相覆盖,只能获取到一个。只适合上传时自定义了唯一文件名称的场景。
customerFile选填,实现了ICustomerFileOp接口的service名称,会执行其中的generateFile方法,若返回null,则还是会执行默认的生成文件流的方法。
app:选填,上述service所在应用的名称
zipName: 选填,表示下载的zip包的名称,不填的话,以(token1&token2……)的形式命名。
【样例】:
返回fileToken下所有文件,以zip格式打包.
1.1.1.4.16 批量下载:根据多个fileToken,zip格式返回
【url】:
/[root]/sys/emapcomponent/file/getFileBatchByTokenArray.do
【参数】:
fileTokenArray:必填,多个token,用逗号隔开
imageSize:选填,默认为”0”,表示获取哪个尺寸的图片文件,
”0”表示获取原始类型图片,
“1”表示获取中尺寸类型图片,
“2”表示获取小尺寸类型图片。
nameType: 选填,默认为”0”,表示附件的命名方式,
“0”表示以唯一编号命名文件,
“1”表示以原文件名命名文件,此方式会导致所限定token下相同文件名的文件会互相覆盖,只能获取到一个。只适合上传时自定义了唯一文件名称的场景。
customerFile:选填,实现了ICustomerFileOp接口的service名称,会执行其中的generateFile方法,若返回null,则还是会执行默认的生成文件流的方法。
app:选填,上述service所在应用的名称
zipName: 选填,表示下载的zip包的名称,不填的话,以(token1&token2……)的形式命名。
【样例】:
由于参数长度不定,需求用post方式提交,并返回流数据。你可以选择自己构建form并提交,或者采用前段封装的组件,调用方式如下:
WIS_EMAP_SERV.batchDownloadFiles({
“contextPath”: “yourContextPath”,
“tokens”: “yourToken1,yourToken2”
})
使用此方法需要引入https://res.wisedu.com/fe_components/emap.js
1.1.1.4.17 删除附件:根据id删除指定附件
【url】:
/[root]/sys/emapcomponent/file/deleteFileByWid/{wid}.do
或
/[root]/sys/emapcomponent/file/deleteFileByWid.do
【参数】:
wid:必填,需要删除的附件id
【样例】:
js代码:

返回结果:

1.1.1.5 java接口
com.wisedu.emap.file.util.IFileDownload
获取方式:
private AppBeanContainer<IFileDownload> fileDl = new AppBeanContainer<IFileDownload>(
"emapcomponent", IFileDownload.BEANID, false);
使用样例:
IFileDownload tmp = fileDl.get();
if (tmp != null) {
tmp.xxx();
}
1.1.1.5.1 通过fileToken获取正式文件信息
【接口方法】:
Map<String,Object> getUploadedAttachmentInfo(String fileToken)
【参数说明】:
fileToken:正式文件的token
【返回值说明】:
正式文件信息,键值为相应属性名,如id,name,fileToken等
1.1.1.5.2 通过fileToken获取文件流
【接口方法】:
InputStream getStreamByToken(String token, Map<String, ?> params)
【参数说明】:
token:正式文件的token
params:如果一个token对应多个文件时,可通过此参数增加额外的区分条件
【返回值说明】:
文件流
1.1.1.6 应用扩展java接口
1.
1.1.1.1
1.1.1.2
1.1.1.3
1.1.1.4
1.1.1.5
1.1.1.6
1.1.1.6.1 上传文件时应用自定义判断接口
【接口类】:
com.wisedu.emap.file.util.ICustomerFileOp
【接口方法】:
StringfileCheck(FileItem fileItem)
【参数说明】:
fileItem:上传的文件
【返回值说明】:
检查通过则返回null,否则返回失败消息,类型为String类型
1.1.1.6.2 上传文件时临时文件保存接口
【接口类】:
com.wisedu.emap.file.util.ICustomerFileOp
【接口方法】:
void fileSave(File file, String fileToken)
【参数说明】:
file:file文件
fileToken:文件的Token字段
1.1.1.6.3 下载时访问权限检查接口
特别说明:本接口应当只用于权限检查。admin用户的任何操作都不会进入此处进行权限检查,默认拥有所有权限。
【接口类】:
com.wisedu.emap.file.util.ICustomerFileOp
【接口方法】:
String fileDownloadAuth (String fileToken)
【参数说明】:
fileToken:token值
【返回类型】:
权限检查通过则返回null,否则返回失败消息,类型为String
【版本要求】:
1.1.37.B37-SNAPSHOT及以上
【代码示例】:
新增了AuthParam类,可直接通过静态方法获取实例。其中保存了附件wid和操作类型。
public String fileDownloadAuth(String token) {
AuthParam authParam = AuthParam.getAuthParam();
//CheckType是 AuthParam类中的枚举类型,注意引用的正确性
AuthParam.CheckType checkType = authParam.getCheckType();
String wid = authParam.getWid();
if (checkType == AuthParam.CheckType.DOWNLOAD) {
//下载相关的权限处理
returnnull;
}
elseif (checkType == AuthParam.CheckType.QUERY) {
//查询相关的权限处理
returnnull;
}
elseif (checkType == AuthParam.CheckType.DELETE) {
//删除相关的权限处理
returnnull;
}
else {
return"unknow checkType!";
}
}
1.1.1.6.4 下载时生成文件接口
【接口类】:
com.wisedu.emap.file.util.ICustomerFileOp
【接口方法】:
InputStream generateFile(String fileToken)
【参数说明】:
fileToken:token值
【返回类型】:
保存文件的输入流InputStream
1.1.1.7 附件表结构
<tablename="EMAP_SAPP_FILE_INDEX"desc="文件索引表">
<columnname="WID"type="String(40)"desc="主键"nullable="false"/>
<columnname="FILE_NAME"type="String(300)"desc="文件名称"nullable="false"/>
<columnname="STORE_NAME"type="String(80)"desc="文件存储名"nullable="false"/>
<columnname="STORE_PATH"type="String(230)"desc="文件纯粹路径"nullable="false"/>
<columnname="UPLOAD_TIME"type="Datetime"desc="上传日期"nullable="false"/>
<columnname="APP_ID"type="String(100)"desc="数据模型所属的应用"/>
<columnname="DATA_OBJ_ID"type="String(100)"desc="数据模型ID"/>
<columnname="DATA_WID"type="String(50)"desc="数据模型主键"/>
<columnname="DATA_ITEM"type="String(60)"desc="数据模型列元素"/>
<columnname="STORE_TOKEN"type="String(80)"desc="数据模型列的值"/>
<columnname="FILE_SIZE"type="long"desc="文件大小"/>
<columnname="FILE_TYPE"type="String(30)"desc="文件类型"/>
<columnname="IS_IMAGE"type="byte"desc="是否是图片"/>
<columnname="IMAGE_TYPE"type="String(2)"desc="图片类型0:原尺寸;1:中尺寸;2:小尺寸"/>
<columnname="IMAGE_RESOLUTION"type="String(50)"desc="图片属性"/>
<columnname="IMAGE_WIDTH"type="int"desc="图片宽"/>
<columnname="IMAGE_HIGTH"type="int"desc="图片高"/>
<columnname="STATUS"type="byte"desc="删除状态"/>
<columnname="STORE_ID"type="String(50)"desc="存储编号"/>
<columnname="ITEM_ID"type="String(10)"desc="存储序列"/>
<columnname="ITEM_TYPE"type="String(10)"desc="存储序列类型"/>
<columnname="TABLE_NAME"type="String(50)"desc="所存表"/>
<columnname="CLRQ"type="Datetime"desc="处理日期"/>
<columnname="CZLX"type="String(10)"desc="操作类型"/>
<columnname="TBRQ"type="Datetime"desc="同步日期"/>
<columnname="TBLX"type="String(10)"desc="同步操作"/>
<columnname="CZRQ"type="Datetime"desc="操作日期"/>
<columnname="CZZ"type="String(30)"desc="操作者"/>
<columnname="CZZXM"type="String(90)"desc="操作者姓名"/>
<columnname="AUTH_APP"type="String(30)"desc="自定义权限APP名称"/>
<columnname="AUTH_BEAN"type="String(30)"desc="自定义权限BEAN"/>
</table>
<indexname="PK_EMAP_SAPP_FILE_INDEX"type="key"tableName="EMAP_SAPP_FILE_INDEX">
<columnname="WID"/>
</index>
1.1.2 导入导出
1.
1.1.1
1.1.2
1.1.2.1 导入过程
1、上传文件,获取上传文件的attatchId
2、读取文件信息,如果自定义了,则使用自定义的读取文件方法
3、执行自定义的转换前数据分析
4、进行字典的转换(value转化成key)
5、执行自定义的转换后数据分析
6、进行数据保存
1)、执行单个数据分析,保存前
2)、执行保存动作,如果有自定义,则执行自定义的保存方法
7、执行自定义的保存后数据分析
8、生成导入结果文件,如果有自定义的则使用自定义结果文件生成方法
9、导入结果文件保存成附件,并返回附件的attatchId
10、通过附件的下载获取导入结果
1.1.2.2 导出过程
1、执行导出定义的动作,生成导出数据
2、执行自定义的导出数据分析方法
3、生成导出结果文件,如果有自定义的则使用自定义结果文件生成方法
4、导出结果文件保存成附件,并返回附件的attatchId
5、通过附件的下载获取导出结果
1.1.2.3 Ajax提交地址
1.
1.1.1
1.1.2
1.1.2.1
1.1.2.2
1.1.2.3
1.1.2.3.1 导出地址
【url】:
/[root]/sys/emapcomponent/imexport/export.do
【参数】:
app:调用导出的应用名称,必填
module:调用导出的模块名,必填
page:调用导出的页面,必填
action:调用导出的动作,必填
colnames:需要导出的字段的name,多个用逗号分隔,选填(不填则全部导出)
analyse:自定义的导出过程分析服务,实现IExportAnalyse,选填
write:自定义的导出写文件服务,实现IExportWrite,选填
filename:自定义的导出文件名,选填
【返回值】:
attachment:导出结果的附件ID
【样例】:
js代码:

返回结果:

1.1.2.3.2 下载导入模板
【url】:
/[root]/sys/emapcomponent/imexport/importTemplate.do
【参数】:
app:必填,调用导入的应用名称
module:必填,调用导入的模块名
page:必填,调用导入的页面
action:必填,调用导入的保存动作
colnames:选填,导入时自定义的字段,多个用逗号分隔
read:选填,实现IGenerateTemplate接口的service名称,实际会执行其中的generateTemplate方法返回模板文件
filename: 选填,模板文件的名称,不填则默认取模型的名称
【返回值】:
attachment:导入模板的附件ID
isGenerateZdb:是否需要在模板中生成字典表数据,默认true,传false时表示不生成
【样例】:
js代码:

返回结果:

1.1.2.3.3 获取导入数据行数
【url】:
/[root]/sys/emapcomponent/imexport/importRownum.do
【参数】:
attachment:必填,excel附件ID
app:选填,read指定的service所在的app
read:选填,实现了IGetRowNum接口的service名称,实际会执行getRowNum方法,返回行数
【返回值】:
rowNumber:数据行数
【样例】:
js代码:

返回结果

1.1.2.3.4 执行导入
【url】:
/[root]/sys/emapcomponent/imexport/import.do
【参数】:
attachment:必填,待上传附件id(可由模板编辑后上传得到)
app:必填,调用导入的应用名称
module:必填,调用导入的模块名
page:必填,调用导入的页面
action:必填,调用导入的保存动作
consts:选填,常量字段,多个用逗号分隔,内容为key:value
guids:选填,GUID字段,多个用逗号分隔
read:选填,实现IReadAndWrite接口的service,实际会执行readImportFile,读取和解析流形式的导入数据,可以比对模型进行数据校验,返回导入数据
analyse:选填,实现IImportAnalyse接口的service,实际会执行beforeAnalyse方法,可在方法中对导入数据进行预处理,返回错误信息(新的导入方式实现INewImportAnalyse)
save:选填,实心IImportSave接口的service,实际会执行save方法,将记录写入数据库,返回错误信息
ignoreEmptyRow:选填,默认为true,是否忽略空行(空格也算空行),true表示读取忽略空行,false表示将空行当成正常记录读取
useNewImport:是否使用新的导入方式,默认为false。设为true表示使用新的导入方式
saveAction:此参数用于指定调用webservice接口的动作名,此时,上面的action参数仅仅用于指定导入的业务模型(useNewImport为true时生效)
transCommitCount:分页时指定一次提交的条数,(useNewImport为true时生效),使用新导入方式时必须设置,否则视为一次性提交所有记录
【返回值】:
status:1表示成功,0表示失败
total:总记录数
success:成功记录数
attachment:导入结果的附件ID号
【注意事项】:
① excel格式校验
我们会将excel填写内容的格式与模型的类型作对比,比如某列在模型中为数字类型,excel中的格式却为文本,则视为出错,会在导入结果中记录错误信息
② 长度和日期校验
针对数据模型中的日期类型,会对导入数据进行日期格式的校验。对于其他的数据类型,会在导入时进行数据长度的校验,在校验不通过的情况下会在导入结果中记录错误信息。
③ 字典校验:
如果数据模型中配置了字典,导入的数据会跟字典的key和value进行匹配,value匹配的情况下会导入数据会被翻译为key,若key和value都不匹配,则导入出错,并在导入结果中记录错误信息。
导入数据 |
实际存储结果 |
0 |
0 |
女 |
1 |
3 |
出错! |
未知性别 |
出错! |
④ 推荐使用生成的模板
我们推荐使用根据模型生成的模板,而非自己造一个。因为默认的导入动作会把无效列视为错误处理,并记录错误信息。使用生成的模板可以有效避免无效列导致的错误。
【样例】:
js代码:

返回结果:

导入文件展示:

数据库导入结果展示:

1.1.2.4 应用扩展java接口
1.
1.1.1
1.1.2
1.1.2.1
1.1.2.2
1.1.2.3
1.1.2.4
1.1.2.4.1 导出数据分析
com.wisedu.emap.framework.imexport.IExportAnalyse
1.1.2.4.2 导出写文件
com.wisedu.emap.framework.imexport.IExportWrite
1.1.2.4.3 导入读文件(不建议使用,已拆分为下面三个接口)
com.wisedu.emap.framework.imexport.IImportRead
1.1.2.4.4 导入模板生成
com.wisedu.emap.framework.importread. IGenerateTemplate
1.1.2.4.5 导入数据行数计算
com.wisedu.emap.framework.importread. IGetRowNum
1.1.2.4.6 导入文件读取和导入结果文件生成
com.wisedu.emap.framework.importread. IReadAndWrite
1.1.2.4.7 导入过程分析
com.wisedu.emap.framework.imexport.IImportAnalyse
1.1.2.4.8 导入保存
com.wisedu.emap.framework.imexport.IImportSave
1.1.2.4.9 新导入过程分析
com.wisedu.emap.framework.imexport.INewImportAnalyse
1.1.3 字典刷新
1.
1.1.1
1.1.2
1.1.3
1.1.3.1 刷新某个应用下某个字典表
【url】:
/[root]/sys/emapcomponent/clearDicCache.do
【参数】:
app:应用名称
dic:字典ID号
【样例】:
正确传递参数后,刷新成功:

1.1.3.2 刷新某个应用下所有字典表
【url】:
/[root]/sys/emapcomponent/clearAppDicCache.do
【参数】:
app:应用名称
【样例】:
正确传递参数后,刷新成功:

1.1.3.3 刷新整个EMAP工作空间内字典表
【url】:
/[root]/sys/emapcomponent/clearAllDicCache.do
【参数】:
无
【样例】:
无需传递参数,正确访问地址,刷新成功:

1.1.3.4 刷新字典的Java接口
com.wisedu.emap.dicOperation.IDicOperation
获取方式:
private AppBeanContainer<IDicOperation> dicOpt = new AppBeanContainer<IDicOperation>(
"emapcomponent", IDicOperation.BEANID, false);
注:上面这段代码是类的成员变量定义
使用样例:
IDicOperationtmp = this.dicOpt.get();
if (tmp != null) {
tmp.xxx();
}
1.1.4 缓存
1.
1.1.1
1.1.2
1.1.3
1.1.4
1.1.4.1 概述
缓存的接口在emapcomponent应用中,工厂类为ICacheFactory。
缓存对象分为两种:单值ISingleCache<T>,多值IMultipleCache<T>。
两种缓存的使用场景:
单值缓存主要用于只需保存一个值的情况。如配置信息,一般都是一次读取所有的配置信息,然后作为一个map缓存下来。
多值缓存主要用于需要保存多个值的情况。如人员的基本信息或最新动态,一般是根据人员编号读取一条或一批数据缓存下来。
1.1.4.2 缓存对象的获取样例
private IMultipleCache<Stud> xxxCache;
{
AppBeanContainer<ICacheFactory> factory = new AppBeanContainer<ICacheFactory>(
"emapcomponent", ICacheFactory.BEANID, true);
StudHandler handler = new ...;
xxxCache = factory.get().getMultipleCache(id, CacheType.LOCAL, handler);
// 或者
xxxCache = factory.get().getMultipleCache(id, CacheType.LOCAL, handler,
idleTime, liveTime, maxSize);
}
获取缓存对象时,需要给出一个标识,即id,此名称在同一个应用中必须是唯一的。
然后是缓存的类型,目前只实现的本地缓存(能够在集群间同步)
handler是用于获取缓存数据的处理器,需要实现相应的接口,这个会在下一节说明。
idleTime为缓存的闲置时间,单位:秒。如果设置为0或小于0,则表示没有闲置时间。
liveTime为缓存的保持时间,单位:秒。如果设置为0或小于0,则表示没有保持时间。
maxSize为保留的缓存值的个数。如果设置为0或小于0,则表示没有个数限制。
1.1.4.3 缓存的使用样例
对于单值缓存,可直接通过get()方法获取缓存的值。
如:T value = cache.get();
对于多值缓存,需要给出键值来获取缓存的值。
如:T value = cache.get("key");
1.1.4.4 handler
获取数据的处理器也分为两种:单值ISingleValueHandler<T>,多值IMultipleValueHandler<T>。
在获取缓存的值时,如果缓存不存在,缓存框架会调用handler来获取缓存的值。
一般获取缓存的方式如下图:

上面整个获取的过程需要加同步锁。当然可以将这个过程封装成一个方法,但是每种类别的缓存都需要封装这样一个方法。
使用了handler之后,执行方式如下图所示:

整个处理过程由缓存框架接管,包括同步锁及优化处理不需要再关心了,也无需再封装方法。
1.1.4.5 缓存的清除
如果缓存的源头(如数据库)更新了,那可以调用缓存的清除方法。这样在下次获取缓存时,就会调用handler获取新的数据。
对于本地缓存,会激活集群间的同步处理,所有服务器上的对于缓存都会被清除。
注:如果设置idleTime或liveTime,由超时所触发的自动清除缓存不会触发集群间的同步处理。但通过调用方式执行的清除缓存,仍然能够触发群间的同步处理。
1.1.4.6 Spring中配置缓存
除了代码方式初始化缓存对象,还可以通过配置初始化缓存对象。
spring的配置文件为:[应用目录]/config/spring.xml。此文件如果不存在请自行创建。
缓存对象的配置样例如下:
<bean id="myCache" class="com.wisedu.emap.cache.CacheGenerator">
<property name="handler" ref="strHandler"/>
<property name="idleTime" value="18000"/>
<property name="maxSize" value="20"/>
<!--
单值缓存需要添加如下属性
<property name="singleCache" value="true"/>
-->
</bean>
<bean name="strHandler" class="com.wisedu….StringCacheHandler"/>
代码中缓存对象的注入样例如下:
@Autowired
@Qualifier("myCache")
private IMultipleCache<String> myCache;
缓存的使用样例如下:
String value = this.myCache.get("testId");
1.2 emapAuth
1.2.1 处理流程
下图为访问页面前的认证处理过程。

1.2.2 权限认证
1.
1.1
1.2
1.2.1
1.2.2.1 Java接口
1.
1.1
1.2
1.2.1
1.2.1.1
1.2.2.1.1 IAuthManager
1.
1.1
1.2
1.2.1
1.2.1.1
1.2.1.1.1
1.2.2.1.1.1 获取
com.wisedu.emap.auth.IAuthManager
获取方式:
private AppBeanContainer<IAuthManager> authMgr = new AppBeanContainer<IAuthManager>(
"emapAuth", IAuthManager.BEANID, false);
使用样例:
IAuthManager tmp = authMgr.get();
if (tmp != null) {
tmp.xxx();
1.2.2.1.1.2 主要方法
1. 根据角色编号获取角色对象
IRole getRole(String roleId)
2. 获取所有角色对象
IRole[] getRoles()
3. 更新角色的名称或者添加一个新的角色
boolean modifyRole(String roleId, String roleName)
4. 根据用户编号获取用户信息
Map<String, Object> getUserInfo(String userId)
5. 根据登录名及密码获取登录用户信息,如果不需要验证密码,密码可为空
Map<String, Object> getLoginInfo(String loginName, String password)
6. 更新用户的信息或者添加一个新的用户
boolean modifyUserInfo(String userId, Map<String, Object> userInfo)
7. 修改用户编号
boolean modifyUserId(String userId, String newId)
8. 创建一个新的验证码
String newVerifyCode(String special)
如果需要创建特殊的验证码,可以通过special参数设置,如果只需要普通的验证码(4位的随机数字),special为null即可
9. 获取当前的验证码
String currentVerifyCode()
1.2.2.1.2 IRole
1.
1.1
1.2
1.2.1
1.2.1.1
1.2.1.1.1
1.2.1.1.2
1.2.2.1.2.1 定义
com.wisedu.emap.auth.IRole
通过IAuthManager获取
1.2.2.1.2.2 主要方法
1. 获取角色中包含的所有子角色的编号
Set<String> getSubRoles()
2. 向角色中添加一个子角色,如果要添加多个可以用","分隔
void addSubRole(String subId)
3. 从角色中删除一个子角色,如果要删除多个可以用","分隔
void deleteSubRole(String subId)
1.2.2.2 应用扩展java接口
1.2.2.2.1 IauthHelper
1.2.2.2.1.1 使用
应用中如果需要自定义认证,则需要实现此接口,并把实现类的service名配置到emapAuth下的app.properties中auth.special.authHelper中
1.2.2.2.1.2 主要方法
1、gotoLoginPage
跳转到登录页面
2、parsePassword
密码转换
3、Map<String, String>getLoginUserInfo
获取当前的登录信息,主要的自定义认证实现方法,登录失败返回空的Map,登录成功返回有值的Map
1.2.3 权限配置
1.2.3.1 概述
应用中,如果需要集中配置功能权限,可以在配置在下面这个文件中:
[应用目录]/config/permission.xml
[应用目录] 即应用的工程目录
1.2.3.2 配置样例
<permissions prefix="权限名称前缀,默认为{当前应用名称-},如果不需要前缀请设为空"
defaultLevel="默认的权限等级,默认值为需权限">
<permission name="权限id(同名的将合并)" cn="中文名称" menu="是否为菜单,默认为true">
<attribute name="n2" value="v2"/>
<attribute name="n2" value="v2"/>
<path value="路径" type="路径类型 normal global other" expr="是否为表达式">
<param name="n1" value="v1"/>
<param name="n2" value="v2"/>
</path>
<path value="/getStud.do"/>
<path value="/modules/page1/action1.do"/>
<path value="/modules/page2/*"/>
<path value="/sys/tmp.do" type="global"/>
<path value="/sys/emapcomponent/export.do" type="other"/>
<page name="epg的名称" actionR="排除其中的动作"/>
<permission name="sub" cn="子权限"></permission>
</permission>
<permission name="p1" cn="权限1">
<permission name="p2" cn="权限2">
</permission>
</permission>
<exclude>
<path value="路径" type="路径类型 同前" needLogin="是否需要登录" expr="是否为表达式"/>
</exclude>
<otherApp>
<attribute name="appFlag" value="指定应用的参数名称"/>
<!-- 这里维护需要由其他应用来判断权限的地址 -->
<path value="路径" expr="是否为表达式"/>
</otherApp>
</permissions>
1.2.3.3 配置说明
根节点permissions中的prefix属性,对于需要匹配已有的权限编号的,需要将此值设置为空(如:prefix=""),否则会将应用名称作为前缀添加。
defaultLevel属性,默认值为2,表示需权限;如果要设为需登录,可改为1。
path节点中的type属性,此属性的默认值为normal。
如果设置为global表示全局地址,如获取字典表的地址:
<path value="/code/xxx.do" type="global"/>
如果设置为other表示访问其他应用的地址,如查询结果的导出功能:
<path value="/sys/emapcomponent/imexport/export.do" type="other"/>
otherApp节点用于配置哪些地址需要在其他应用中判断。
其中名称为appFlag的属性用于设置从哪个参数中获取应用名称,如果设置为“$Referer”,表示将根据请求头中的Referer获取应用名称。
没有配置任何path的permission节点将被忽略。
如果父节点被忽略的话,子节点也将被忽略。
样例1:
<permission name="a">
</permission>
权限a将被忽略,等同未定义
样例2:
<permission name="b">
<permission name="c">
<path .../>
</permission>
</permission>
权限b, c都将被忽略,等同未定义
参数中不支持表达式,如:
<path value="/a.do">
<param name="a" value="tmp*"/>
</path>
参数a的值“tmp*”不会被当作表达式,只能全等匹配这个值
如果一个固定地址包含在一个表达式地址中,则最终固定地址会取两个的合集。
样例1:
<permission name="a">
<path value="/a/b.do"/>
<permission>
<permission name="b">
<path value="/a/*"/>
<permission>
对于上面这样的配置,对/a/b.do的最终效果就是a,b权限都能访问。
样例2:
<permission name="a">
<path value="/a/b.do"/>
<permission>
<exclude>
<path value="/a/*" needLogin="false"/>
</exclude>
对于上面这样的配置,对/a/b.do的最终效果就是无需权限就能访问。
注:此规则对两个相同地址也适用。
样例3:
<permission name="a">
<path value="/a/b.do"/>
<permission>
<exclude>
<path value="/a/b.do" needLogin="false"/>
</exclude>
对于上面这样的配置,对/a/b.do的最终效果就是无需权限就能访问。
1.2.3.4 表达式
表达式使用的是antpath,样例如下:
<path value="/a/**/type/*.do" expr="true"/>
但需要注意,简单的起始匹配不属于antpath,如:
<path value="/a/*"/>
如果需要以antpath匹配,那应该这样设置:
<path value="/a/*.do" expr="true"/>
另外,请注意,不符合antpath的路径,即使设置了expr="true",也不会将其作为表达式。
如下面这些,是不会被判断为表达式的。
<path value="/a/*" expr="true"/>
<path value="/a/b.do" expr="true"/>
1.2.4 登出的说明
如果需要注销当前用户,可以使用如下方法
AuthTool.logout(request, response)
调用此方法时需要注意,方法内部会根据使用的登录方式进行重定向,所以不要在调用此方法后做重定向之类的处理。
对于special方式的认证,登出后会重新调用gotoLoginPage方法,并且会在request的属性中添加一个标识为登出的值,判断样例如下:
"1".equals(request.getAttribute(IAuthHelper.LOGOUT_FLAG))
如果上面的表达式为true,那说明当前是在执行登出。如果是登出的话,登录后跳转的地址就不是当前地址(request.getRequestURI()),而应该使用请求头中的referer。
1.3 emapLogger
用于处理日志的记录。
1.3.1 持久化日志
此功能是将数据库操作的日志、出错信息(WARN级别及以上)、请求的环境信息等数据持久化保存到本地文件系统中,存放的目录结构如下:
/[war包名称]/[4位年2位月2位日]/[2位时]/[2位分]/[2位秒3位毫秒][空格][日志id].[info 或 error]
每个日志文件以XML的格式记录一次完整的请求及请求中的出错信息和数据库操作等。
如果一次完整的请求处理中包含ERROR级别的信息或有异常抛出,则以error作为后缀名,反之则以info作为后缀名。
1.3.2 文件日志
此功能是将所有通过日志框架输出的内容写入到的指定的文件夹下的文件中。
文件名称的格式如下:
[进程号]$[机器名]$[IP]$[war包名称]$[日期].log
输出的文件日志主要用于和日志中心对接。
1.3.3 相关配置
默认的配置文件在emapLogger应用的config目录下,文件名为loggerConfig.xml。此文件中有注释对各个配置项进行说明。
另外,此文件也可放在ROOT应用的config目录下。如果两个地方都有这个文件,那会将两个文件中的配置项合并,ROOT应用里的配置项优先生效。
1.4 emapWS
用于处理webservice的发布。
1.4.1 发布的webservice查询地址
[root]/sys/emapWS/list.do
访问以上地址可以查询所有应用已发布的webservice。
1.4.2 定义webservice
如果需要将一个类发布成webservice,首先需要将这个类注册到应用的spring中,然后再添加WebService标注。相关标注的说明可查阅如下文档:
http://docs.oracle.com/javaee/6/api/javax/jws/package-summary.html
目前可以使用的只有:WebService、WebMethod、WebParam这3个。
注:WebParam的header和mode暂不支持。
如果你使用的JDK低于1.7,则可以引入emapWS的发布类,里面有这3个标注。另外还有一些公共类型,这些会在后面的规范中提到。
1.4.3 webservice定义规范
WebService标注只有设置在可注册到spring中的类上才有效。
默认生成的名称为“应用名_类名”,当然也可以通过WebService标注的name属性来设置名称。
如果是设置的名称,不会在前面自动添加应用名称,这样可以自行定义一些短名称。但必需保证此名称全局唯一,即不能与其他应用中的webservice名称有重复。
被WebService标注的类中,所有公共的非静态方法(不包括Object类中的方法),都会自动发布成webservice中的操作。如果有哪些方法不需要发布,则可在方法上添加WebMethod标注,将exclude属性设置为true。
1.4.4 可解析模型的webservice规范
如果需要让设计器能够通过发布的webservice解析模型,那么方法的入参或出参必需符合以下规范。
1.4.4.1 入参规范
入参为以下4个参数:
config:QueryConfig
param:POJO or POJO[]
setting:String
userInfo:UserInfo
根据不同的处理情况,可不定义任何参数,也可定义部分参数,没有先后顺序要求。但是,定义的参数名称和类型必须与要求的完全一致。
a. 查询的配置参数
参数名称:config
参数类型:QueryConfig
是否可选:是
作用:设置查询的分页,排序等。执行时会将DaoParam中的相关数据填入这个参数。
QueryConfig的结构如下:
pageNumber:Integer,起始页,如果该属性的值为-1表示不需要分页
pageSize:Integer,每页记录数
order:String,排序配置
defaultCondition:boolean,是否需要默认条件(字典表需要判断的属性)
dicType:String,字典表的分类(字典表需要判断的属性)
b. 参数对象
参数名称:param
参数类型:POJO对象或 POJO数组对象
是否可选:是
作用:设置调用时所需要的参数。设计器会根据此参数解析入参模型。
POJO对象的所有成员可用的类型如下:
int,Integer,long,Long,double,Double,byte[],String,Date
除了POJO类型,还可以设置带ModelInfo标注的Map类型,如:
queryXxx1(@ModelInfo("USER_INFO") Map<String, Object> param) {
…
}
queryXxx2(@ModelInfo("biz:userCondition") Map<String, Object>[] param) {
…
}
ModelInfo标注在com.wisedu.emap.ws包下,里面的值可以直接设置数据模型的名称,如果需要使用业务模型,需要以“biz:”起始。
c. 条件的配置
参数名称:setting
参数类型:String
是否可选:是
作用:设置动态的查询条件。执行时会将高级查询生成的querySetting传入这个参数。
动态的查询条件是JSON格式的字符串,具体格式参考“3.7.6”中的“提交条件的数据格式”。
d. 用户信息
参数名称:userInfo
参数类型:UserInfo
是否可选:是
作用:获取调用方当前登录的用户信息、所属应用等。
可调用UserInfo.verify(boolean throwInvalid)方法验证用户信息是否有效。
注:验证方法只有在验证字符串正确,且两台服务器的时间相差在5分钟内才会返回true。
另外,还需要注意,带有用户信息参数的WS方法动作将会被判断为需要数据权限的动作。如果这个方法只是需要获取用户信息,并不需要数据权限,那需要再创建一个用户信息对象(可以继承原有的用户信息对象),并在新的用户信息对象中添加一个boolean类型的成员变量,名称为:unrequiredPermission。
1.4.4.2 出参规范
结果对象可以继承AbstractResult,并根据需要添加result属性。
结果对象的结构如下:
code:int,状态码,0表示没有错误
msg:String,返回的信息,如出错信息等
logId:String,日志编号,用于查询相关日志的编号
pageNumber:int,起始页
pageSize:int,每页记录数
totalCount:int,总记录数或受影响的记录数,如果没有统计总记录数,此值可以是-1
result:POJO[] or POJO,结果数组,每行为一个POJO对象,所有成员可用的类型同前面的参数对象
注:设计器会根据result属性解析出参模型。
除了继承AbstractResult类型,还可以设置带ModelInfo标注的ModelResult类型,或其继承类型,如:
public @ModelInfo("USER_INFO") ModelResult queryXxx1(…) {
…
}
ModelResult类在com.wisedu.emap.ws包下。
1.4.5 webservice的监听
1.4.5.1 应用内切面
如果需要对应用内的webservice方法调用进行切面编程,可以实现IServiceAspect接口,并将这个实现类注册到应用的spring中。
如果是注册在父应用中,那么所有子应用都会使用这个切面。如果父应用和子应用都注册了切面,那在子应用中将使用自己的切面。
注:一个应用的spring中只能注册一个实现此接口的对象。如果注册了多个实现此接口的对象,那此应用内的切面将无法生效。
1.4.5.2 对所有webservice的监控
如果需要对系统中所有的webservice方法调用进行监控,可以实现IServiceSupervisor接口,并将这个实现类注册到应用的spring中。
然后需要在配置文件emap.properties中的配置项emap.ws.supervisors中注册这个对象,注册样例如下:
emap.ws.supervisors=app1/beanId1,app2/beanId2
应用名称和bean的编号间用“/”分隔,多个监控者之间用“,”分隔,配置列表中的先后顺序即执行时的调用顺序。如果还存在应用内切面,那切面会在最后执行。
配置文件的具体位置及说明见《EMAP运行环境的配置说明》。
另外,调用参数IServiceInfo的process(Object[] args)方法,可以继续执行webservice的调用过程。
还有,如果希望自动将监控对象设置到配置中,而不需要在实施时调整配置,可以在bean的初始化方法中调用下面这段代码:

1.4.6 对象成员信息的设置
要设置成员的信息,可以使用FieldInfo标注。
如果WSDL定义中的名称和成员的名称不一致,则可通过此标注的name来设置WSDL中定义的名称。
1.4.6.1 字典搜索参数的获取
如果webservice服务作为字典表,那首先需要存在名称为param的参数用于接收字典的搜索条件。之后这个类需要继承AbstractDicSearchInfo,用于接收搜索条件的值。如果无法继承AbstractDicSearchInfo,那可以参考此类的定义,将需要的成员定义到你的类中。
如果只需要接收特定的参数,那也可以直接定义成参数,通过WebParam标注来指定参数名称,样例如下:
public XxxResult getXxxCode(@WebParam(name=AbstractDicSearchInfo.NAME_PREFIX + "pId") String parentId) {
…
}
可接收的参数说明如下:
AbstractDicSearchInfo.NAME_PREFIX + "id",根据字典中的代码值进行全等匹配
AbstractDicSearchInfo.NAME_PREFIX + "name",根据字典中的显示值进行匹配(匹配方式见 mathType)
AbstractDicSearchInfo.NAME_PREFIX + "pId",树型字典有效,对父节点代码值进行全等匹配
AbstractDicSearchInfo.NAME_PREFIX + "matchType",匹配方式
0,根据name参数对显示值进行全等匹配
1,根据name参数对显示值进行模糊匹配
2,根据name参数对代码值或显示值进行模糊匹配
AbstractDicSearchInfo.NAME_PREFIX + "searchValue",格式同高级查询的数据提交格式,用于设置字典对应的表中其它列相关的查询条件
具体说明见“3.7.6”及“3.7.7”。
1.5 emapvalidate
1.5.1 总体介绍
emapvalidate本身是系统应用,已实现功能防跨站点脚本攻击。实现判断逻辑如下:
✓ 判断请求来源是否为空,空合法,否则进行下一步判断
✓ 判断请求来源与请求地址域名是否一致,一致合法请求 否则进行下一步判断
✓ 判断请求来源域名是否与白名单域名一致,一致合法,否则进行下一步判断
✓ 判断请求地址是否属于不判断访问来源的地址,属于合法。
以上都不满足的拒绝请求。
另外还判断了参数中是否包含script脚本,简单来说就是如果请求的参数中有包含“<script”的,将会抛出非法参数的异常。
1.5.2 参数配置
1.5.2.1 emap.properties增加配置参数
app.sys.list=emapvalidate
目的是注册emapvalidate为系统应用。
注:如果运行环境版本高于1.8.19.B95,则可不用配置上面这个参数。
emapvalidate.domains=www.baidu.com,www.google.com,localhost:8080
表示域名白名单,认为这些是合法来源域名。可选参数,当应用没有外部的访问来源时,无需配置此参数。多个域名地址逗号隔开。
注:emap.properties具体位置及说明见《EMAP运行环境的配置说明》。
系统应用见<2.7 注册系统应用配置>。
1.5.2.2 app.properties增加配置参数
比如app1开发菜单m1不需要判断访问来源,则在对应的app1中增加app.properties配置文件。
注:此配置项由开发人员设置,实施时无需关注。
配置示例:
url.referercheck.ignore=/sys/xljkapp/modules/test.do
多个配置逗号隔开,支持AntPath格式。
1.6 emapDBVMC
用于查看数据库脚本初始化的情况,以及对一些出错脚本的重新执行或设置步骤处理完成。
访问地址:
/[root]/sys/emapDBVMC/*default/index.do
注:此地址必须是管理员身份的用户才能访问。
1.6.1 关于数据源
在列表界面中,可以选择数据源,系统会将配置成相同jndi的数据源合并在一起显示。所以,配置数据源时,如果是相同的数据库及用户,请使用相同的jndi。这样可以避免出现有一堆数据源,但却是同一个库。
1.6.2 处理步骤
可以通过过滤条件“只显示出错记录”,查看是否有数据库初始化脚本执行出错的。如果有出错的,可以点击错误处理进入相关的脚本列表界面。
在脚本列表中,可以查看执行的脚本及此脚本是否已执行。可以在界面上点击未执行的脚步,重新执行。对于大部分情况,可能需要进入数据库进行调整。
如果所有脚本都已执行或已在数据库中处理,则可点击“步骤处理完成”,然后重启应用,系统会继续执行后面的步骤。
2. EMAP运行环境
2.1 系统功能的地址
2.1.1 应用重载
应用重载地址在debug模式下,可以任意访问。但在正式环境中,只有指定的用户登录后才能访问。如何配置这个用户,见《EMAP运行环境的配置说明》中的相关配置项“context.managerUser”。另外,后面的“运行环境日志”和“配置信息”也是如此。
2.1.1.1 重载指定应用
/[root]/appManage.do?type=reload&apps=[appName]
2.1.1.2 重载应用管理器
/[root]/appManage.do?type=reInit
重新加载应用管理器可以处理系统应用(如:emapAuth)的更新,应用的增减,应用发布类的更新等。
2.1.2 运行环境日志
/[root]/eterna/setting.jsp
可在运行时起停运行环境日志,查看运行环境日志。
2.1.3 配置信息
/[root]/eterna/config.jsp
可查看公共的配置项、配置项的说明,当前的配置值。
如果需要查看某个应用的配置项,可添加参数appName,如:
/[root]/eterna/config.jsp?appName=emapAuth
2.1.4 日志管理
/[root]/eterna/logback.jsp?log=logName
可查看日志的记录等级,logName为日志的名称。
如果需要设置某个日志的记录等级,可以添加level参数,如:
/[root]/eterna/logback.jsp?log=logName&level=INFO
2.2 EMAP运行环境的主要对象
详细的javadoc(即API文档)可通过设计器打开,如下图:

如果你在金智教育的云内开发,可以通过svn获取运行环境及系统应用的源码:
svn://172.16.200.10/emap/emap-publish/V1.0/src
用户名:guest,密码:wisedu123
2.2.1 CurrentThread
当前上下文相关的工具类,如获取当前请求用户、当前request/response对象,日志串接号getLogSeriesId等。
2.2.2 AuthTool
认证相关的工具类,主要静态方法,一般使用在自定义认证应用中
2.2.3 DaoService
通过spring注入或新建,主要功能:获取数据库时间,获取数据库连接等
2.2.4 IEmapApp
【获取方式】:
通过AppManager.currentApp()获取当前应用,
或AppManager.getInstance().getApp("[appName]")获取指定名称的应用
【主要功能】:
获取应用所在的目录,获取应用的数据源ID,获取应用的配置对象,获取应用的上下文信息
2.2.5 IEmapAppContext
【获取方式】:
通过spring注入或通过IEmapApp接口获取
【主要功能】:
获取应用中定义的各类对象
- 获取数据模型的容器
DataModelContainer getDataModel(String name)
- 获取动作的容器
ActionContainer getAction(String name)
2.2.6 DataModelContainer
【获取方式】:
通过IEmapAppContext接口获取
【主要功能】:
获取数据模型对象,获取数据模型对于的默认动作
- 获取默认的查询动作
<T> IDataModelQueryAction<T> getQueryAction(Class<T> rowType)
rowType为返回的结果中每一行的数据类型,此参数可以省略,默认类型为Map<String, Object>
- 获取默认的更新动作
IDAtaModelUpdateAction getUpdateAction(ActionType actionType)
actionType为更新的类型,可选值有ADD,MODIFY,DELETE,SAVE
2.2.7 ActionContainer
【获取方式】:
通过IEmapAppContext接口获取
【主要功能】:
获取获取自定义的动作
- 获取自定义动作
<T> IEmapAction<T> create(Class<?> type)
type为返回结果的类型或每一行的数据类型
2.2.8 DaoParam
【获取方式】:
新建
【主要功能】:
设置数据库操作相关动作的参数
2.2.9 AppBeanContainer
【获取方式】:
新建
【主要功能】:
获取另一个应用中定义的bean对象,目标应用的接口必须是发布的,见“发布类使用”
样例代码(此对象一般定义为类变量):
private AppBeanContainer<接口类型> container = new AppBeanContainer<接口类型>("目标应用", "spring中的beanId", 目标应用是否必须存在);
使用的样例:
接口类型 obj = container.get();
如果构造时,第三个参数设为false(目标应用可以不存在),需要判断获取的对象是否为null
2.2.10 JSONUtil
Json字符串转化方法
2.3 系统已使用的参数
2.3.1 userId
emapAuth的debug认证使用的参数,用于设置登录的用户id。
2.3.2 roleId
emapAuth的debug认证使用的参数,用于设置登录的用户角色。
2.3.3 querySetting
查询使用的参数,用于设置构造条件的JSON结构。
2.3.4 *order
查询使用的参数,用于设置查询时的动态排序。
2.3.5 pageNumber
查询使用的参数,用于设置查询分页的起始页。
2.3.6 pageSize
查询使用的参数,用于设置查询分页的每页记录数。
2.3.7 callbackFunName
访问页面(epg)及页面绑定的动作使用的参数,用于设置以JSONP方式访问时的回调函数名。
2.3.8 *json
访问页面(epg)使用的参数,用于设置以JSON格式返回页面的模型。
2.3.9 *searchMeta
访问页面(epg)绑定的动作使用的参数,用于设置以JSON格式返回高级查询的模型。
2.3.10 EMAP_LANG
设置当前系统使用的语言。
2.3.11 THEME
设置当前系统使用的主题。
2.4 注册系统应用
相关类的具体说明见javadoc,如何查看请参考“2.5”。
2.4.1 配置
配置文件为emap.properties,具体位置及说明见《EMAP运行环境的配置说明》。
定义哪些为系统应用的配置项为:app.sys.list,如:
app.sys.list=app1,app2,app3
多个应用名称间用逗号分隔,配置的应用顺序将影响初始化及监控对象的调用顺序。如app1和app3都注册了对控制器的监控,则会先调用app1的再调用app3的。
此配置项中,如果配置了不存在的应用名称,系统会将其忽略,不会影响运行环境及其他应用的启动。
2.4.2 系统应用的要求
一个应用必须在其spring中注册一个且只能有一个,实现了ISysAppLoader接口的对象。这样的应用才能作为系统应用,否则只能作为普通的应用。
实现ISysAppLoader接口的init方法,可通过参数中的SysAppConfig对象,注册应用状态变化的监听器、控制器的监控器。
2.4.3 控制器的监控器
接口IControllerSuperviser中的before方法是在控制器执行之前调用的方法,在其实现方法中,如果需要中断控制器的执行有两种方式:
1. 抛出异常,这样会转向出错页面或返回一个JSON结构的出错信息
2. 返回一个与对应控制器返回类型相同的对象,这样会跳转到返回对象所指定的页面
注:如果需要继续直接控制器的方法,请返回null。
2.4.4 控制器监控器的调用顺序
如果有多个系统应用都注册了控制器监控器,则会根据配置列表(见“2.7.1”)中的顺序对before方法进行调用。控制器的方法执行完后,会逆向调用after方法。
如app1和app2都注册了控制器监控器(app1在app2之前),那执行顺序为:app1的before,app2的before,控制器的方法,app2的after,app1的after。
另外,需要注意,如果执行了before方法,那么无论控制器的方法是否被执行,其对应的after方法一定会被执行。如:
app2的before方法调用过程中出现了异常,则会先调app2的after方法,再调用app1的after方法,最后抛出异常。
3. 其他
3.1 发布类使用
3.1.1 概述
APP之间是物理隔离的,不允许直接访问。但也提供了两种交互的方式,一种是继承,别一种是把接口或类发布成公共的。
应用中如果有需要发布到公共目录下接口或类,需要放在pub_classes下,所以,如果一个应用中存在pub_classes目录就说明这个应用有公共的接口或类供其他应用使用。
如果开发中需要使用这些接口,可以在项目中单独设置类引用路径,增加一条指向目标应用的pub_classes。
如果开发的应用中需要有发布类,可以在构建路径中添加pub_src目录,将其编译目录指向pub_classes。* 注:发布的类都应该是接口或pojo类。
3.1.2 规范
3.1.2.1 纯接口
只是作为一个公共的接口类使用,如导入组件中的IExportAnalyse接口,导入组件并能实现此接口,纯粹是给调用导入组件的应用扩展使用。此接口编写没有特殊要求
后续主档中组件的“应用扩展java接口”都属于此类
3.1.2.2 有实现
发布接口的同时,内部还对此接口增加了实现,目的是为了向其它应用提供后台API,如字典刷新组件的IDicOperation接口。
要求接口必须增加一个获取的注释和静态变量BEANID

在上图获取方法一中,为了效率考虑,应该定义成类的属性,如
private AppBeanContainer<IAuthManager> authManagerContainer
实现类中使用BEANID作为服务名

其它应用需要使用此接口时,就可以获取使用了。后续文件中组件的“java接口”都属于此类。
3.1.3 使用其他应用发布的类
3.1.3.1 开发时的引用

如上图,打开“Java构建路径”,在“库”标签中,选择“添加外部类路径”(红色圈出的按钮),选择发布的“pub_classes”(如:红色方框标出的部分)。
设置完成后,才能在当前应用中使用相关应用的发布类。如上面截图的设置,就是需要使用emapcomponent的发布类。
注1:只能使用工作空间下应用中的“pub_classes”,如果引用了其他路径,在运行时将无法找到这些类
注2:如果想要修改图上显示pub_classes为相对路径确保所有人引用路径统一不出现路径错误,方式如下:

3.1.3.2 代码中的使用
首先定义类的成员变量:
private AppBeanContainer<IPubType> xxx = new AppBeanContainer<IPubType>(
"[appName]", IPubType.BEANID, [true or false]);
在代码中的使用方式如下:
xxx.get().xxxMethod(…);
注:如果设置了应用不是必须存在(前面的构造函数中最后个参数设为false),那需要判断get方法的返回值是否为null。
3.2 定时任务
3.2.1 配置
类定义前需要设置Component标注或Service标注,可让spring自动载入。类需要继承IEmapTask接口,用于标识这个类是一个定时调度任务的类。相关的方法上需要添加TaskScheduled标注,用于标识任务的调度时间表。注,执行任务的方法必须是无参的公有方法。如:
@Service("myBeanId")
public class MyClassName implements IEmapTask {
@TaskScheduled(cron="0 0/10 * * * ?")
public void myTask() {
…
}
}
任务默认为集群模式,如果任务需要在每个服务实例上都运行,需要将cluster设置为false。如:
@TaskScheduled(cron="0 0/10 * * * ?", cluster=false)
public void myTask() {
…
}
如果需要动态设置任务的调度时间,可用如下方式:
@TaskScheduled(methodCron="getCron")
public void myTask() {
…
}
public String getCron() {
return "0 0/10 * * * ?";
}
注1:cron和methodCron两项配置只能设置一个,不可同时设置。
注2:如果不需要执行定义任务(移除这个任务),getCron方法可返回null。
在运行过程中,如果需要刷新任务的调度时间,可使用TaskManager的如下方法:
TaskManager.resetTask("myBeanId", taskObj, "myTask ");
如果需要移除一个定时任务,可使用如下方法:
TaskManager.removeTask("myBeanId", MyClassName.class, "myTask ", true);
最后一个参数的值需要根据“@TaskScheduled”中cluster的值(默认为true)来设。
3.2.2 CronExpression表达式
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。按顺序依次为1.秒(0~59)2.分钟(0~59)3.小时(0~23)4.天(月)(0~31,但是你需要考虑你月的天数)5.月(0~11)6.天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)7.年份(1970-2099)
其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?.
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点0 0/30 9-17 * * ??? 朝九晚五工作时间内每半小时0 0 12 ? * WED 表示每个星期三中午12点
有些子表达式能包含一些范围或列表例如:子表达式(天(星期))可以为“MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT”
“*”字符代表所有可能的值因此,“*”在子表达式(月)里表示每个月的含义,“*”在子表达式(天(星期))表示星期的每一天
“/”字符用来指定数值的增量例如:在子表达式(分钟)里的“0/15”表示从第0分钟开始,每15分钟 ;在子表达式(分钟)里的“3/20”表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”
“L”字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写
但是它在两个子表达式里的含义是不同的。
在天(月)子表达式中,“L”表示一个月的最后一天 ,在天(星期)自表达式中,“L”表示一个星期的最后一天,也就是SAT
如果在“L”前有具体的内容,它就具有其他的含义了
例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最后一个星期五
注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题
============================================
CronTrigger配置完整格式为: [秒] [分] [小时] [日] [月] [周] [年]
序号说明是否必填允许填写的值允许的通配符
1 秒是 0-59 , - * /
2 分是 0-59 , - * /
3 小时是 0-23 , - * /
4 日是 1-31 , - * ? / L W
5 月是 1-12 or JAN-DEC , - * /
6 周是 1-7 or SUN-SAT , - * ? / L #
7 年否 empty 或 1970-2099 , - * /
通配符说明:*表示所有值. 例如:在分的字段上设置 "*",表示每一分钟都会触发。? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?- 表示区间。例如在小时上设置 "10-12",表示 10,11,12点都会触发。, 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发/用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五" W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-").# 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;小提示:'L'和 'W'可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发;周字段的设置,若使用英文字母是不区分大小写的,即MON与mon相同;常用示例:
0 0 12 * * ? 每天12点触发
0 15 10 ? * * 每天10点15分触发
0 15 10 * * ? 每天10点15分触发
0 15 10 * * ? * 每天10点15分触发
0 15 10 * * ? 2005 2005年每天10点15分触发
0 * 14 * * ? 每天下午的 2点到2点59分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 2点到2点59分、18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发
0 15 10 15 * ? 每月15号上午10点15分触发
0 15 10 L * ? 每月最后一天的10点15分触发
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)
3.3 配置框架使用
开发中如果需要使用配置文件中设置的信息,可以使用下面的方式编写。
这样获取配置不仅能够能够跟踪到哪些配置在哪里使用了,并且在一些需要反复获取配置的地方能变为直接获取一个类属性,在配置修改时emap会自动修改对应的类属性。
注:需要让Config标注生效,需要在应用中添加app.properties文件,文件位置见第三部分。
3.3.1 配置与属性绑定
可在类的静态非final的属性前添加Config标注,设置这个属性与哪个配置绑定。
如:
@Config(name="emap.config.test", description="是否为测试模式")
private static boolean testMode;
@Config(name="emap.config.name", description="项目的名称")
private static String projectName = "EMAP";
@Config(name="emap.cache.flushTime", description="缓存的刷新时间", defaultValue="0")
private static long cacheFlushTime;
Config标注中可以设置3个属性
name,用于指定此属性与哪个名称的配置绑定
defaultValue,当配置未设置时使用的默认值
description,配置的说明
3.3.2 配置与方法绑定
如果配置变化时,需要执行一些额外的操作。或需要对配置的值进行特殊的转换,可以将配置与一个静态方法绑定。
如:
@Config(name="emap.whiteList", description="白名单列表")
static void setWhiteList(String names)
{
…;
Set<String> whiteSet = …;
}
注:所绑定的方法只能且必须有一个参数,类型必须是String
3.3.3 应用中配置文件的位置
应用中的配置文件app.properties在开发环境中,可直接放在在 src目录下,如果是部署环境,需要放在classes目录下。
注:应用中必须存在app.properties文件,否则所有的Config标注将不会生效。
3.3.4 配置的优先级
应用中需要使用的配置信息可以设置在应用自己的配置文件(app.properties)中,也可以设置在系统的配置文件(ROOT/emap.properties)中。如果两个地方都设置了相同的配置,则应用中配置的值生效。
如果希望在应用的配置文件中设置默认值,但如果系统的配置文件中相同的配置项能够优先生效,则可以在应用的配置文件中增加如下配置:
_parentFirst.propertyNames=name1,name2
可以设置哪些配置的名称是系统配置优先生效,多个配置的名称间使用“,”分隔。
3.3.5 配置信息的查看
访问地址为:/[root]/eterna/config.jsp?appName=[app]
[root]是EMAP运行环境的上下文根
[app]是需要查看配置信息的应用名称
注:此页面不仅可查看配置信息,也可作为配置项的说明文档。
3.3.6 配置文件的继承
项目部署的时候会经常遇到需要修改配置的问题,比如开发环境中配置的一些文件目录和现场环境中的文件目录不一致,开发环境中的一些环境参数(如:redis)和现场环境的参数不一致等。
对这种问题也有简单的解决办法,就是现场环境的配置文件不更新。这对已经完成的项目可以这么做,但对于需要不断迭代升级的项目,一旦新增了某个配置项,或者某个通用的配置项进行了调整。那就需要在部署时更新现场的配置文件,这就增加了对部署人员的要求以及沟通的成本。
如果你使用了EMAP的配置框架,那你就能避免这些问题。
处理方式如下,在ROOT/emap.properties文件中添加如下配置:
_child.properties=../special.properties
此设置表示,为当前的配置文件指定一个子配置文件,子配置文件中的设置会覆盖掉当前文件中的同名配置。
举个例子:
如果在ROOT/emap.properties文件中有配置“temp.dir=D:\\temp”用于设置临时目录在D盘的temp目录下。
如果现场环境的临时目录不在这里,可以不需要改动ROOT/emap.properties,而是在special.properties文件中配置“temp.dir=/opt/temp”,这样系统的临时目录就被改到了opt目录下的temp目录中。
这样,各个现场只需要在special.properties文件中设置与当前部署环境相关的配置项,此文件不随产品的发布进行更新,而那些通用的配置项仍然设置在ROOT/emap.properties文件中和产品一起更新。
3.4 页面干预类
3.4.1 重设返回的页面
String interveneForward(String forwardPath, Map<String, Object> model, IEmapPage page)
forwardPath 原始的返回页面路径
model 需要传递到页面(JSP)中的数据
page 当前的页面对象
样例,跳转到另一个JSP:
return page.getContainer().getApp().locateJSP("/temp/test.jsp");
上面这段代码表示将跳转到 [应用目录]/web/temp/test.jsp
3.4.2 修改页面返回的模型
IEmapModel interveneModel(IEmapModel model, IEmapPage.Action action)
model 当前返回的模型
action 绑定到页面的动作包装对象
样例,修改某列的标题:
if ("[绑定的动作别名]".equals(action.getAlias())) {
CustomModel newModel = new CustomModel(model.getName(), model);
((CustomModel) newModel.getItem("[标识名称]")).setCaption("[新的标题]");
return newModel;
}
3.4.3 修改动作的执行
Object interveneActionExecute(DaoParam param, IEmapPage.Action action)
param 当前执行动作所需的参数
action 绑定到页面的动作包装对象
样例,修改动作执行前的参数及返回值:
if ("[绑定的动作别名]".equals(action.getAlias())) {
param.addParam("参数名", "新的参数值");
QueryResult<Map<String, ?>> r = (QueryResult<Map<String, ?>>) action.getAction().execute(param);
r.getFirst().put("标识名", "新的返回值");
return r;
}
另外页面干预类继承了AbstractPageIntervention
这个类中有获取请求参数等相关的方法,具体说明见源码
3.5 高级扩展使用说明
3.5.1 JSP中调用应用里类的方法
在JSP中,如果需要调用应用中类的方法,首先需要引入emap的标签,样例如下:
<%@ taglib prefix="e" uri="/WEB-INF/tags/emap.tld" %>
使用样例如下:
<e:call id="myBean" method="test" params="string"/>
这个标签就会调用当前应用下,bean的id为myBean中的test方法
类的定义样例如下
@Service("myBean")
public class ClassName {
public String test(String param) {
return "....";
}
}
如果是直接调用某个类的静态方法,使用样例如下:
<e:call className="com.wisedu.xxx.ClassName" method="test" params="string"/>
* call 标签中各个属性的说明
className,指定需要调用的静态方法所在的类
id,指定需要调用的方法所在的对象(spring中的id)
method,需要调用的方法名,目标对象(或类)中此名称的方法最好只有一个
params,需要传入的参数
var,方法调用的结果将以什么名称设置到当前请求的作用域中,如:var="tmpName",后面就能使用 ${tmpName}
printResult,是否要将调用的结果输出到标签所在的位置,默认为false
* 被调用的方法可以设置的入参类型
HttpServletRequest, HttpServletResponse, JspWriter
String 此类型的参数将会获取到params属性中设置的值
3.5.1.1 JSP中调用系统功能
获取应用下的JSP路径,样例如下:
<e:sys type="JSP_PATH" params="/xx.jsp" var="tmpPath"/>
type,固定设置为JSP_PATH
params,设置jsp相对web目录的路径,如果需要获取其它应用的jsp路径格式为:"appName,/xx.jsp"
var,可选参数,如果不设置路径将输出;如果设置了,路径值将以此名称添加到request的attribute中
调用应用中自定义的功能,样例如下:
<e:sys type="appName:beanId" params="…" var="…"/>
type,格式为:[应用名称]:[实现的对象在spring中的名称],对象必须实现ISysTagHandler接口。
params及var,需要根据实现类的要求设置
3.5.2 特殊的条件、参数、输出的处理
如果你需要使用一些特殊的功能,如一些特殊的条件构造方式,一些特殊的结果处理方式。当然,这些处理可以在干预类中实现,如果仅仅是一个特殊的条件构造方式,使用干预类就显得比较费事了。所以EMAP提供了很多扩展口,让你能够定义这些对象。
这些扩展对象需要定义在应用中的config/eterna.xml文件里,相关的对象如何定义将在后面一个个列出。
如果应用是继承自父应用,则父应用的“config/eterna.xml”文件里的配置也会在子应用中生效。
3.5.2.1 自定义条件构造器及列表
<?xml version="1.0" encoding="utf-8"?>
<!--
自定义条件构造器及列表的样例
-->
<eterna-config>
<factory>
<objs>
<!-- 定义一个查询是否在子表中的条件构造器 -->
<builder name="myExists" caption="存在" generator="self.micromagic.util.TemplateBuilder">
<attribute name="template" value="[C0] in (select xxxId from T_XXX where xxxColumn = ?)"/>
</builder>
<!--
说明:
builder中的name属性为条件构造器的标识,可以设置在数据模型或业务模型的相关列的“条件构造器”这个属性项中
builder中的标题属性会显示在高级查询中,操作的下拉列表的选项中
builder中的generator数据用于设置构造类,这里使用这个构造类就行了
子元素中名称为template的属性节点,用于设置构造出的条件模板。
模板的说明:
?, 表示传入的参数
[C0], 表示当前列中的列名项
如果在列名为“T_TEST.testCol”列中设置了此条件构造器,那构造出的条件如下:
select ... from ... where ... T_TEST.testCol in (select xxxId from T_XXX where xxxColumn = ?)
-->
<!--
多个列名项的配置:
在业务模型中添加的自定义列中可以自己设置列名,可以通过","分隔设置多个列名
如:“T_TEST.testCol,T_XXX,xxxColumn”
这样,条件构造器可以按如下方式定义
-->
<builder name="myExists2" caption="存在" generator="self.micromagic.util.TemplateBuilder">
<attribute name="template" value="[C0] in (select xxxId from [C1] where [C2] = ?)"/>
</builder>
<!--
说明:
如果列名是以","分隔的多个名称,[C0]、[C1]、[C2]分别表示列名中的第一个、第二个、第三个
-->
<!--
前面的例子中使用的是等于,如果需要以LIEK的方式进行判断,配置方式如下:
-->
<builder name="myExists3" caption="存在" generator="self.micromagic.util.TemplateBuilder" prepare="escapeStr_include">
<attribute name="template" value="[C0] in (select xxxId from T_XXX where xxxColumn LIKE ? escape '\')"/>
</builder>
<!--
说明:
判断方式改为LIKE ? escape '\',并且添加了一个参数处理器“escapeStr_include”
此参数处理器的作用是在传入的字符串两边添加"%",并且将字符串中出现的"%"和"_"替换为"\%"和"\_"
-->
<!-- 定义条件构造器列表 -->
<builder-list name="myBuilderList">
<builder-name name="include"/>
<builder-name name="equal"/>
<builder-name name="notEqual"/>
<builder-name name="myExists"/>
</builder-list>
<!--
说明:
builder-list中的name属性为条件构造器列表的标识,可以设置在数据模型或业务模型的相关列的“条件构造器列表”这个属性项中
字节的中的每个builder-name节点用于指定一个条件构造器的名称,这个列表将作为高级查询中,操作的下拉列表
-->
</objs>
</factory>
</eterna-config>
注:如果需要调整默认的条件构造器,只需要修改条件构造器列表中的第一个值就行了。如全局定义的字符串条件构造器列表如下:
<builder-list name="cbl_String">
<builder-name name="equal"/>
<builder-name name="notEqual"/>
<builder-name name="include"/>
<builder-name name="notInclude"/>
<builder-name name="beginWith"/>
<builder-name name="endWith"/>
<builder-name name="m_value_include"/>
<builder-name name="m_value_equal"/>
<builder-name name="m_value_not_include"/>
<builder-name name="m_value_not_equal"/>
</builder-list>
现在第一个是“equal”即相等,也就是默认构造的条件是相等,如果把“include”调整到第一个,那默认构造的条件就会变为包含。
你可以在应用的“config/eterna.xml”文件里重新定义这个条件构造器列表,用于调整默认构造的条件。如果多个应用使用的配置相同,则可在公共的父应用中统一设置。
另外,全局定义的转大写构造列表如下:
<builder-list name="cbl_upper_String">
<builder-name name="upper_equal"/>
<builder-name name="upper_notEqual"/>
<builder-name name="upper_include"/>
<builder-name name="upper_notInclude"/>
<builder-name name="upper_beginWith"/>
<builder-name name="upper_endWith"/>
<builder-name name="upper_m_value_include"/>
<builder-name name="upper_m_value_equal"/>
<builder-name name="upper_m_value_not_include"/>
<builder-name name="upper_m_value_not_equal"/>
</builder-list>
3.5.2.2 自定义条件构造器对象
<builder name="myBuilder" caption="包含" generator="com.wisedu.MyBuilder"/>
需要使用的条件构造实现类可以在generator属性中指定,如上面的例子。实现类的编写样例如下:
public class MyBuilder extends AbstractConditionBuilder{
public BuilderResult buildeCondition(String colName, Object value, ConditionProperty cp){
ValuePreparer[] arr = new ValuePreparer[2];
arr[0] = this.createValuePreparer(cp, value);
arr[1] = this.createValuePreparer(cp, "test");
return new BuilderResult(colName + " in (?, ?)", arr);
}
}
参数说明:
colName,为数据模型或业务模型中设置的列名
value,为当前传入的条件值
cp,为条件的配置信息
返回类型为BuilderResult,可以直接构造。第一个参数为语句片段,如果有参数请用“?”占位。第二个参数为与语句片段中的“?”对应的列表,构造方式见上面代码中的样例。
3.5.2.3 自定义对象的使用
使用自定义的条件构造器,可以将它的名称直接设置在数据(/业务)模型的“条件构造器”属性上。
使用自定义的条件构造器列表,可以将它的名称直接设置在数据(/业务)模型的“条件构造器列表”属性上。
注:所有自定义的对象不会在下拉列表中显示出来,需自行输入。
3.5.3 特殊的环境变量
环境变量会使用在如下场景:
1. 数据(/业务)模型字段属性中的默认值,以“env:”起始
2. 页面模型的参数来源,以“env:”起始
3. 数据模型/自定义动作中权限脚本的变量,用“${…}”包裹
4. 字典条件脚本的变量,用“${…}”包裹
5. 动作流的表达式,不加特殊标识
3.5.3.1 获取用户对象中的item
使用样例,custom.currentUserItems.[itemName]
[itemName]为需要获取的item名称。
3.5.3.2 获取指定方法的返回值
使用样例,bean.[beanId].[property]
[beanId]为注册在当前应用spring中对象的id
[property]为对象中的get方法名,此方法必须为无参且有返回值的方法
如spring中对象的id为testBean,其中有个方法为getValue,则配置如下:
bean.testBean.value
此环境变量可以用在权限的脚本中,用于动态生成行过滤需要的条件值。需要的用户对象或request对象可以通过CurrentThread中获取。
注:这样可以灵活的控制行权限,但请不要将权限与查询的需要混在一起。
3.5.4 让spring扫描应用中的lib
如果应用中lib目录下的jar包也要进入spring的扫描,那需要在应用的spring中添加额外的配置。如:<context:component-scan base-package="com.wisedu.xxx.*">,表示需要扫描jar包中“com.wisedu.xxx”package下的类。
注:spring的配置文件为:[应用目录]/config/spring.xml。此文件如果不存在请自行创建。
3.5.5 在spring中使用配置文件的值
如果想要在spring.xml中使用配置文件的值,如:
<bean id="xxx" class="com.xxx">
<property name="theName" value="${configName}"/>
</bean>
只需要在spring.xml中引入spring_properties.xml就行了,样例如下:
<import resource="classpath:spring_properties.xml"/>
引入这个文件后,就可以像前面一样,使用emap.properties文件中配置的值。
3.5.6 自定义的静态资源处理
如果需要在应用中处理动态生成的静态资源,可以使用此部分功能。
注:需要运行环境版本在1.8.81.B157以上。
3.5.6.1 映射的配置
静态资源映射的配置文件需放在应用下,路径及文件名为:config/resourceMapping.xml
配置样例如下:
<mapping>
<resource path="/a.js" beanId="resourceTest1"/>
<resource path="/img/logo.png" beanId="resourceTest2"/>
…
</mapping>
3.5.6.2 配置说明
如上面的例子,在应用“sample”中有如上的配置文件,那么在访问“/sys/sample/a.js”时,就会执行sample应用的spring中id为resourceTest1的对象,在访问“/sys/sample/img/logo.png”时,就会执行sample应用的spring中id为resourceTest2的对象。
path中设置的是静态资源的映射路径,不能使用通配符
beanId中设置的是spring中的对象id,即静态资源的处理对象
3.5.6.3 实现类
处理对象的实现类为:com.wisedu.emap.mvc.IResourceHandler
定义样例如下:
@Service("resourceTest1")
public class JsResource implements IResourceHandler {
public long getLastModified() {
}
public void service(HttpServlerRequest req, HttpServletResponse resp) {
}
}
getLastModified方法需要返回资源的最后修改的时间(1970-01-01开始的毫秒数),如果不需要在浏览器缓存,可返回-1
service方法及静态资源的处理方法,按servlet的处理放在实现即可
3.6 数据库初始化
3.6.1 定义
应用中,如果需要平台为其自动初始化数据库,可以根据EMAP的规范,配置初始化文件。
初始化配置文件的位置如下:
[应用目录]/src/db/[模块名]/version[X].xml
[应用目录] 即应用的工程目录
[模块名] 可以自己任意定义,用于区分一个应用中多个模块的数据库初始化配置
[X] 为数据库初始化配置的版本号,从1开始,之后的版本按顺序编号,编号必须连续
3.6.2 样例
<?xml version="1.0" encoding="GBK"?>
<dbVersion>
<!-- 创建一个表 -->
<table name="T_USER" desc="用户信息表">
<!-- 字符型列 -->
<column name="name" type="String(300)" desc="用户姓名" nullable="false"/>
<!-- 整型列 -->
<column name="age" type="int" desc="年龄" default="10"/>
<!-- 浮点型列 -->
<column name="height" type="double(5,2)" desc="身高(米)"/>
<!-- 日期时间型列,注:没有日期类型 -->
<column name="lastModified" type="Datetime" desc="最后修改时间"/>
</table>
<!-- 创建主键 -->
<index name="PK_T_USER" type="key" tableName="T_USER">
<column name="name"/>
</index>
<!-- 创建索引 -->
<index name="IX_T_USER" tableName="T_USER">
<column name="age"/>
<column name="height"/>
</index>
<!-- 创建唯一键 -->
<index name="UK_T_USER" type="unique" tableName="T_USER">
<column name="lastModified"/>
</index>
<!-- 创建外键 -->
<index name="UK_T_USER" type="foreign" tableName="T_USER" ref="T_CLASS">
<column name="name"/>
<refColumn name="userName"/>
</index>
<!-- 删除主键 -->
<index name="PK_T_USER" type="key" tableName="T_USER" opt="drop">
</index>
<!-- 删除一个表 -->
<table name="T_USER" opt="drop">
</table>
<!-- 修改一个表 -->
<table name="T_USER" opt="modify">
<!-- 添加的列 -->
<column name="newColumn" type="String(10)" desc="新列"/>
<!-- 修改的列 -->
<column name="age" type="long" opt="modify"/>
<!-- 删除的列 -->
<column name="height" type="double(5,2)" opt="drop"/>
<!—修改列名 -->
<column name="oldName" newName="newName" type="String(5)" opt="modify"/>
</table>
<!-- 设置忽略插入数据时的主键冲突错误 -->
<ignore sameKey="true"/>
<!-- 执行一条语句,任何数据库上都会执行 -->
<script>
insert into T_USER (name) values ('tom')
</script>
<!-- 执行一条语句,指定在哪些数据库上执行 -->
<script dataBase="oracle,mysql">
create function …
</script>
</dbVersion>
3.6.3 check节点的说明
如果需要在创建一个表前判断这个表是否存在,可以使用check。
注:EMAP运行环境版本必须在1.8.61.B137以上才能使用此功能。
具体样例如下:
<!-- 当不存在T_USER表时,创建这个表及主键 -->
<check tableName="T_USER">
<table name="T_USER" desc="用户信息表">
…
</table>
<index name="PK_T_USER" type="key" tableName="T_USER">
…
</index>
</check>
<!-- 当存在T_USER.BIRTH列时,创建这个列的索引 -->
<check tableName="T_USER" columnName="BIRTH" exists="true">
<index name="IX_T_USER_BIRTH" type="index" tableName="T_USER">
<column name="BIRTH"/>
</index>
</check>
<!-- 当T_USER表存在主键时,将其删除 -->
<check tableName="T_USER" indexName="$key" exists="true">
<index name="PK_T_USER" type="key" tableName="T_USER" opt="drop"/>
</check>
<!-- 当T_USER表不存在IX_T_USER_ADDR索引时,创建这个索引 -->
<check tableName="T_USER" indexName="IX_T_USER_ADDR">
<index name="IX_T_USER_ADDR" type="index" tableName="T_USER">
<column name="ADDR"/>
</index>
</check>
3.6.4 数据类型
1、 String(40),字符串,长度为40
2、 int,整数型
3、 Datetime,日期时间型
4、 Clob,大文本
5、 Blob,大数据
6、 double(10,2),长度10,小数位2
3.6.5 初始化依赖
如果初始化的模块之间存在着依赖关系,则可以在初始化模块的目录下添加dependent.txt文件,在此文件中添加需要依赖的模块列表。如:
app:baseApp1,app:baseApp2,unit:tables2,unit:tables1
“app:”起始的表示需要依赖哪个应用,“unit:”起始的表示需要依赖本应用中哪个模块,多个依赖项之间可以用“,”分隔。
注:如果一个初始化模块中设置了依赖,那此模块下的delay.txt将不会生效。
3.6.6 忽略初始化出错
如果数据库初始化出错后,会将应用置为无效。如果在数据库初始化出错后,仍然需要使应用可用,则可在应用的标识文件“EMAP_APP”中添加如下配置:
ds.init.ignoreError=true
如果需要忽略查询时的主键冲突错误,可以通过ignore节点来设置,如:
<ignore sameKey="true"/>
那么在此节点之后的插入语句执行时,如果是主键冲突,那就会忽略此错误,继续执行后面的步骤。
3.6.7 问题查看及处理
注:EMAP运行环境1.7.60及之后的版本按此章节处理,之前的版本请参考后面一段旧版本的处理方式。
在EMAP_SYS_VERSION_INFO表中,可以查看到数据库初始化的结果,如下图:

versionName 用于标识初始化哪些类别的表
versionValue 表示当前更新到哪个版本
setp表示当前执行到第几步
optStatus表示当前步骤的状态,是开始执行(BEGIN)还是已完成(DONE),或者是当前版本处理完成(FINISH)
errorInfo 为出错信息,如果这列有信息,表示此类别初始化失败,需要继续查看相关的执行脚本,手动处理
在EMAP_SYS_VERSION_SCRIPT表中,可以查看到初始化出错的脚本,如下图:

versionName和versionValue同前
exeduted 表示此脚本是否已执行,0表示未执行
scriptIndex 表示脚本的执行顺序,重小到大排列
有了脚本之后,就可以进行手动处理了。
将所有未执行的脚本,手动执行掉。如果有些无法执行的,如表或列已存在则跳过。
然后将EMAP_SYS_VERSION_INFO表中的errInfo列清除,optStatus列改为DONE,如下图:

最后,重启应用,再查看EMAP_SYS_VERSION_INFO表中是否还有出错信息。
3.6.8 问题查看及处理(旧)
在EMAP_SYS_VERSION_INFO表中,可以查看到数据库初始化的结果,如下图:

versionName 用于标识初始化哪些类别的表
versionValue 表示当前更新到哪个版本
errorInfo 为出错信息,如果这列有信息,表示此类别初始化失败,需要继续查看相关的执行脚本,手动处理
在EMAP_SYS_VERSION_SCRIPT表中,可以查看到初始化出错的脚本,如下图:

versionName和versionValue同前
exeduted 表示此脚本是否已执行,0表示未执行
scriptIndex 表示脚本的执行顺序,先按已执行的排列,再按未执行的排列
有了脚本之后,就可以进行手动处理了,可分为两种方式
第一种. 将所有未执行的脚本,手动执行掉,然后清除EMAP_SYS_VERSION_INFO表中的出错信息,如下图

第二种. 将所有已执行的脚本恢复(如将已建的表删除),然后清除EMAP_SYS_VERSION_INFO表中的出错信息,将版本号减1,如下图

最后,重启系统,再查看EMAP_SYS_VERSION_INFO表中是否还有出错信息。
3.7 交互的数据格式
3.7.1 相关路径
1、在跳转后的jsp中获取查询动作模型
<%@ taglib uri=”/WEB-INF/tags/emap.tld” prefix=””e%>
<script type=”text/javascript”>
Var pageMeta=<e:page/>;
</script>
2、用地址获取查询动作模型
…/sys/{appName}/{modules}/{page}.do?*json=1
3、执行某个动作,并返回页面上的查询动作模型
…/sys/{appName}/{modules}/{page}.do?*json=1&*datas={action}
4、用地址获取查询动作模型,并放到js对象中
…/sys/{appName}/{modules}/{page}.do?*json=1&*var={jsobj}
5、执行某个动作
…/sys/{appName}/{modules}/{page}/{action}.do
6、获取查询动作上的高级查询模型
…/sys/{appName}/{modules}/{page}/{action}.do?*searchMeta=1
7、获取字典数据
…/code/dicId.do
字典数据地址默认是需要登录的且只能通过ajax访问,如果想不登录就使用,则先建个<页面模型>,设为无需权限,把需要的字典地址配置到<关联地址中>。
8、获取某类型(条件)的字典数据
…/code/dicId/type.do
3.7.2 数据查询
…/sys/{appName}/{modules}/{page}/{action}.do
1、 分页
pageSize每页多少条;
pageNumber,第几页;
totalSize 总记录数
2、 简单相等查询条件
{colName}={value}
3、 复杂的组合查询
var queryobj = [
{name:"条件名",linkOpt:"and||or",value:"条件值",builder:"条件操作, 通过操作符列表选择的值"},
...
]
AJAX提交:data{“querySetting”, JSON.stringify(queryobj)}
4、 排序
*order=+name1,-name2
排序的参数名称为:“*order”,“name1”和“name2”为数据/业务模型中定义的标识名称,前面的“+”和“-”用于标识升序和降序,传参时需要注意“+”需要进行URL编码。
3.7.3 返回的JSON格式
{
code:0, // 必定存在
logId:"", // code不为0时会存在
msg:"", // 可能不存在
models:[ // 可能不存在, 所有的模型数据
{
name:"modelName1",
url:"xxx/xxx.do", // 数据的获取/提交地址
// 模型的类型,get单条、list列表、set更新、temp临时模型(modelRef)
type:"get|list|set|temp",
controls:[ // 模型中的控件列表,get或list才有此项
{name:"a",xtype:"text or select ...",caption:"学号",hidden:true,...},
{name:"b",xtype:"text or select ...",caption:"姓名",readOnly:true,...},
...
] ,
params:[ // 模型中的参数列表,set或temp必有此项,其它可能存在
{
name:"a",dataType:"string or object …",
caption:"学号", // 标题可以不存在
required:true, // 可以不存在,默认为false
modelRef:"modelName2" // 如果是复杂类型,这里指定另一个模型
// 这里不使用嵌套的形式是因为存在递归嵌套的结构
},
{name:"b",dataType:"string or object"},
...
]
},
{name:"modelName2",...}
],
datas:{ // 可能不存在,获取的结果数据
"{dataName1}":{ // 单条记录
name:"yourName",sex:"1",sex_DISPLAY:"男",...
},
"{dataName2}":{ // 多条记录
pageNum:1,pageSize:15,totalCount:100,
rows:[
{name:"name1",sex:"1",sex_DISPLAY:"男",...},
{name:"name2",sex:"2",sex_DISPLAY:"女",...},
...
]
}
}
}
3.7.4 数据提交格式
3.7.4.1 简单的数据提交格式
此方式适合只更新一个表的单条记录的时候
格式为form表单的提交数据,如:
name=tom&sex=1&age=12
此种方式简称:EXF1。
3.7.4.2 JSON的提交数据格式
此方式合适有多个表或多条记录需要更新的时候
json数据添加的form表单中
单条记录多个表
actionName1=urlEncode({name:"tom",sex:"1",age:12})&actionName2=urlEncode({className:"math",userName:"tom"})
多条记录
actionName1=urlEncode([{name:"tom",sex:"1",age:12},{name:"jerry",sex:"2",age:15}])
上面两个例子中,actionName1和actionName2为处理数据包的动作别名(即页面模型epg上的动作别名)或动作流中节点的名称。
此种方式简称:EXF2。
3.7.4.3 字符参数要求
由于我们使用的是http的form-urlencoded,所有的参数值必须是字符串,所以如果是一个复杂结构的数据类型,需要转换成json字符串才行。
3.7.4.4 设计说明
EXF1是符合传统的form表单的提交方式,支持这种入参结构能够让传统的页面更容易接入。
EXF2是为处理复杂的入参数据而设计的,如可编辑表单的多条修改数据的提交。
3.7.4.5 样例说明
动作流的样例

动作流是根据节点的别名进行传参(这里为了演示,将节点的显示名称和别名设为相同的),即一个动作流中的所有赋值节点及动作节点的别名就构成了这个动作流的入参名称集合。所有页面中调用动作流时,传参样例如下:
node0=1&node1=urlEncode({…})&node2=urlEncode([…])
node0需要一个数字,node1需要一个对象,node2需要一个数组,上面这种方式是符合EXF1的格式,如果需要使用EXF2的格式,应该是这样:
flow1=urlEncode({node0:1,node1:{…},node2:[…]})
flow1为这个动作流在epg(页面模型)中的别名。
从这两个样例中,还能看到转字符串的处理也不同,这也是符合前面提到的字符串参数要求。
webservice动作的样例
webservice动作需要的参数在其入参列表中,传递样例如下:
ws1=urlEncode({appId: "app1",roleId: "testRole" })
ws1为这个webservice动作在epg(页面模型)中的别名,这种方式是符合EXF2的格式。
3.7.5 高级查询的格式
获取条件定义的格式如下:
{
code:0,
searchMeta:{
name:"searchName",
controls:[ // 定义的条件列表
{name:"条件名",xtype:"text or select ...",caption:"学号",builderList:"操作符列表名 (bList1)",...},
{name:"b",xtype:"text or select ...",caption:"姓名",builderList:"bList2",visible:false,...},
...
]
builderLists:{ // 条件定义中需要使用的操作符列表
"bList1":[{name:"操作名",caption:"标题"}, ...],
"bList2":[{name:"操作名",caption:"标题"}, ...],
...
}
}
}
提交条件数据的格式
[
{name:"条件名",linkOpt:"条件间的连接符 and or",value:"条件值",builder:"条件操作, 通过操作符列表选择的值"},
...
]
条件构造的规则:
如果条件需要添加括号,将括号中的条件添加到数组中,数组中第一个条件的连接符将作为整个括号的连接符
样例1
[
{name:"col1",value:"a",linkOpt:"and",builder:"include"},
{name:"col2",value:"x",linkOpt:"or",builder:"equal"}
]
结果1: col1 like '%a%' or col2 = 'x'
样例2
[
{name:"col3",value:"b",linkOpt:"and",builder:"include"},
[
{name:"col1",value:"a",linkOpt:"and",builder:"include"},
{name:"col2",value:"x",linkOpt:"or",builder:"equal"}
],
{name:"col4",value:"c",linkOpt:"and",builder:"include"}
]
结果2: col3 like '%b%' and (col1 like '%a%' or col2 = 'x') and col4 like '%c%'
注意:子数组中的第一个linkOpt在这里需要是“and”,如果是“or”的话,组括号将以or的方式与其他条件拼接,见下面的例子
结果3:col3 like '%b%' or (col1 like '%a%' or col2 = 'x') and col4 like '%c%'
builder选项的值如下:
equal:等于
upper_equal:转大写等于
notEqual:不等于
include:包含
upper_include:转大写包含
notInclude:不包含
more:大于
less:小于
moreEqual:大于等于
lessEqual:小于等于
m_value_include:多值包含
m_value_equal:多值相等
注1:如果是多值条件,传入的值以逗号分隔
注2:使用截图

3.7.6 字典的查询
查询路径:[root]/code/dicId.do
带类型的查询路径:[root]/code/dicId/type.do
字典的查询参数如下:
id,根据字典中的代码值进行全等匹配
name,根据字典中的显示值进行匹配(匹配方式见 mathType)
pId,树型字典有效,对父节点代码值进行全等匹配
matchType,匹配方式
0,根据name参数对显示值进行全等匹配
1,根据name参数对显示值进行模糊匹配
2,根据name参数对代码值或显示值进行模糊匹配
searchValue,格式同高级查询的数据提交格式,用于设置字典对应的表中其它列相关的查询条件
checkParent,树型字典有效,是否需要检查每个节点的父节点是否包含在结果中,如果不包含则添加这个父节点
注:
字典默认是需要登录访问的,如果想不登录访问,需要把字典的地址配置到无需权限的页面定义中,在这个页面前台就可以不登录访问。

3.7.7 其他说明
对于所有的查询,高级查询和字典的查询等,如果数据的格式有问题,如数据类型转换出错,那将会忽略这个条件。
如,传入的条件如下:
[
{name:"count",value:"a",linkOpt:"and",builder:"equal"}
]
如果字段的类型为整型,则会类型转换出错,那这个条件就会被忽略,等同未传此条件。
如果条件中的name属性值没有和模型中的标识名称匹配,那么这个条件就会被忽略,等同未传此条件。
3.8 动作流的表达式语法
3.8.1 基本运算符
+,-,*,/,%,!,~,==,!=,>,>=,<,<=,>>>,>>,<<,^,&,|,&&,||
赋值运算符只有两个::=和+=。
注意,这里是“:=”这样可以避免在判断相等的地方错误的写成赋值。
三目运算符:1 > 2 ? "a" : "b"
删除操作:delete,如:
delete $map.name 或 delete $list[2]
另外,// 和 /* … */为注释。
3.8.2 基本对象
字符
'a','\''
字符串
"abc","a\n\"b\""
整型
123,-1,0xff
长整型
1L,0x100L
浮点型
0.1,3.14d,1.1e3
列表(list)
{1, 2, 3}
或list@{1, 2, 3}
集合(set)
set@{1, 2, 3}
映射(map)
map@{a:1, b:2, c:3}
3.8.3 变量
变量是以“$”起始的名称,如$name,$sex。
对变量的属性可以有以下几种访问方式,“.”和“[…]”,如:
$map.name,$map[$key],$map.list[$num],$list[0]。
注意,以两个“$”起始的为全局变量,如:$$flag。在一次请求中里的所有流程中都有效,请谨慎使用。
其他可引用的变量:
param,当前请求的参数集合,如:param.loginName。
attr,当前请求对象中的属性集合。
header,当前请求对象中的头信息集合。
cookie,当前请求对象中的cookie信息集合。
custom,平台中的预定义信息,如:
custom.currentDate,当前日期
custom.currentUserId,当前登录用户编号
3.8.4 基本操作
link,将多个参数连成一个字符串
link('a', 1, 2, 3) =>"a123"
sub,取子对象
sub("12345", 3) =>"45"
sub({"a", "b", "c"}, 0, 2) => {"a", "b"}
split,分割字符串
split("a,b,c") => {"a", "b", "c"}
split("a.b.c", ".") => {"a", "b", "c"}
indexOf,在字符串或列表中查找指定字符或对象的索引值
indexOf("a,b,c", ',') => 1
indexOf("a,b,c", ',', true) => 3
indexOf("a,b,c", ',', 2) => 3
indexOf("a,b,c", ',', 2 true) => 1
indexOf({"a", "b", "c", "b"}, "b") => 1
indexOf({"a", "b", "c", "b"}, "b", true) => 3
charAt,获取指定位置的字符
charAt("abc", 1) =>'b'
length,获取字符串或集合的长度
length("abc") => 3
length({1, 2}) => 2
length(map@{}) => 0
isEmpty,判断对象是否为空
isEmpty(null) => true
isEmpty("") => true
isEmpty("") => false
isEmpty(map@{}) => true
contains,集合中是否包含指定的对象
contains(set@{"a", "b", "c"}, "b") => true
contains(set@{"a", "b", "c"}, "d") => false
contains(map@{a:1, b:2}, "b") => true
throw,抛出一个异常
throw($ex) =>抛出变量“$ex”中的异常
throw("a") =>抛出一个信息为“a”的异常
throw("a", "java.lang.IllegalArgumentException")
=>抛出一个信息为“a”的IllegalArgumentException异常
format,对数字或日期进行格式化
format(1000, "#,##0.00") => 1,000.00
format($date, "yyyy") => 2015
toIterator,将一个列表转换成迭代器
toIterator({1, 2}) => Iterator
toIterator(map@{a:1, b:2}) => map enrty Iterator
toIterator(map@{a:1, b:2}, true) => map key Iterator
toInt,转整型
toLong,转长整型
toDouble,转浮点型
toString,转字符串
toList,转列表
toList(数组) => List
toList(map@{a:1, b:2}) => map value List
toSet,转集合
toSet (数组) => Set
toSet(map@{a:1, b:2}) => map key Set
toDate,转日期
toDate("2015-11-24") =>日期
toDate("20151124", "yyyyMMdd") =>日期
toDate("20151124183000", "yyyyMMddHHmmss") =>日期时间
toDatetime,转日期时间
toDatetime("2015-11-24 18:30:00") =>日期时间
isBoolean,是否为布尔型
isNumber,是否为数字,整型、浮点型等
isString,是否为字符串
isDate,是否为日期或日期时间
isMap,是否为映射对象(map)
isList,是否为列表对象(list)
isSet,是否为集合对象(set)
isArray,是否为数组
hasNext,迭代器(Iterator)对象中是否有下一个
next,获取迭代器(Iterator)对象中的下一个值
first,获取列表、集合、数组等对象中的第一个值
first({1,2}) => 1
first("123") =>"123"
entryKey,获取Map.Entry中的key
entryValue,获取Map.Entry中的value
merge,将第二个对象中的数据合并到第一个对象中
merge({1, 2}, {3, 4, 5}) => {1, 2, 3, 4, 5}
merge({1, 2}, 3, 4, 5) => {1, 2, 3, 4, 5}
merge(map@{a:1}, map@{b:2}) => map@{a:1, b:2}
merge(map@{a:1}, "b", 2) => map@{a:1, b:2}
3.8.5 特殊操作
特殊操作是基于基本操作的扩展,主要扩展了两个功能。
1. 调用流程干预类中的公共方法
invoke_[方法名](参数…)
如:invoke_doService($a, {1, 2, 3})
2. 获取干预类中的公共静态变量
static("[变量名]")
如:static("MAX_VALUE")
3. 调用spring对象中的方法
spring$[beanId]$ [方法名](参数…)
如:spring$myService$doSomeThing("id", -1)
注:使用的spring对象必须是单例模式的
3.8.6 优化建议
为了提高表达式的运行效率,对于引用路径较长的变量,如果需要多次使用,请定义成变量再使用。如:
$tmp.names[1].activeTime + $waitTime > $tmp.names[1].deadTime
应改为:
$tmpObj := $tmp.names[1]
$tmpObj.activeTime + $waitTime > $tmpObj.deadTime
对于常量运算,请放在前面。如:
$i := $tmp * 60 * 60 * 1000
应改为:
$i := 60 * 60 * 1000 * $tmp
或
$i := $tmp * (60 * 60 * 1000)
对于常量对象,即定义后只是读取,而不会再修改的对象,可以加上const标记为常量。如:
$dic := const {map@ student:1,teacher:2,staff:3}
3.9 事务
3.9.1 默认的事务
EMAP默认的事务处理方式如下:
在第一次更新操作执行时开启事务,此事务一直到当前控制器执行结束。然后,根据是否有异常抛出,来确定是提交或回滚。
对于查询操作,如果在未开启事务时,则会在无事务的情况下进行查询。如果已开启了事务,则会在这个事务中进行查询。
3.9.2 声明式事务
声明式的事务采用与spring相同的标注“@Transactional”,如:
@Transactional
public void doXXX()
其工作方式也与spring的相同。
3.9.3 两种事务的混合
已定义了声明式事务的方法如下:
@Transactional
doMyUpdate() {
…
}
情况1:
doSomeUpdate();
doMyUpdate();
由于先执行了更新操作,事务已开启,“doMyUpdate”方法调用时将加入这个事务,此方法结束时事务不会结束,需等到控制器执行结束。
情况2:
doMyUpdate();
doSomeUpdate();
“doMyUpdate”方法会在单独的事务中,方法结束事务也结束,后面的更新操作将重新开启另一个事务。
情况3:
doSomeUpdate();
try {
doMyUpdate();
}
catch …
由于先执行了更新操作,事务已开启,“doMyUpdate”方法调用时将加入这个事务,如果有异常被捕获但未继续抛出,则最后控制器执行结束将提交事务而不是回滚。
这与情况1类似,只是多了个异常的捕获且未继续抛出。如果“doMyUpdate”前没有执行更新操作,那么此方法会在单独的事务中,方法外的异常捕获不会影响事务的提交或回滚。
3.10 国际化
3.10.1 配置文件的位置
国际化的配置文件在放在应用的config目录下,文件名为i18n[_lan].xml。如:
[应用目录]/config/i18n.xml为默认的国际化资源。
[应用目录]/config/i18n_en.xml为英文版的国际化资源。
3.10.2 文件格式
<messages>
<message code="code1" value="中文名称"/>
<message code="code2" value="日期:{0},数字:{1}"/>
<message code="code3" value="number: {1}, date: {0}"/>
</messages>
3.10.3 说明
资源文件中,value值的格式请参考java.text.MessageFormat。
EMAP运行环境的最低版本为1.8.39.115。
3.10.4 使用方式
代码中的国际化使用方式与spring相同,EMAP运行环境已将MessageSource对象添加到了应用(带有i18n配置文件)的spring容器中。
在jsp中请使用EMAP提供的标签,样例如下:
<%@ taglib prefix="e" uri="/WEB-INF/etags/emap.tld" %>
…
<e:message code="code1"/>
此标签可设置的属性同spring的message标签。
如果在页面中,需要通过javascript获取当前应用中所有的国际化资源,可请求这个地址:[root]/i18n.do?appName=[app]。
返回的格式及样例如下:
{
code: "0",
datas: {
"code1": "中文名称",
"code3": "number: {1}, date: {0}",
…
}
}
3.10.5 二开时需要注意的问题
如果基础应用中没有国际化的配置文件,那么在二开的应用中添加的国际化配置文件是无效的。
如果基础应用中没有特定语言的国际化配置文件,那么在二开的应用中添加这种语言的国际化配置文件是无效的。
3.10.6 多语言的国际化
针对多语言的国际化,将在emap.properties文件中增加配置项
emap.multiForeign
此配置默认为false,所有国际化的处理与目前处理方式相同。
如果设置为true,那表示有多种外语的国际化。
相应的变化如下
1. 字典的外文列不再直接是列标识,将作为列标识的前缀
如:设置为foreignValue时,在英文时,将读取foreignValue_en作为字典值;在法语时,将读取foreignValue_fr作为字典值。
2. 模型中的外文名称不再直接作为名称,而是作为i18n的key
如:设置为address是,将根据当前的语言,在i18n的资源中查找key为address的值返回。
3.10.6.1 条件构造器的操作名称
对于条件构造器的操作名称,需要在应用或其父应用的国际化资源文件中添加如下内容:
<message code="等于" value="equal"/>
<message code="不等于" value="not equal"/>
<message code="包含" value="contain"/>
<message code="不包含" value="not contain"/>
<message code="大于" value="greater than"/>
<message code="小于" value="less than"/>
<message code="大于等于" value="greater than or equal"/>
<message code="小于等于" value="less than or equal"/>
<message code="多值包含" value="multiple contain"/>
<message code="多值相等" value="multiple equal"/>
<message code="多值不包含" value="multiple not contain"/>
<message code="多值不相等" value="multiple not equal"/>
<message code="两列包含" value="two column contain"/>
<message code="三列包含" value="three column contain"/>
<message code="多列包含" value="multiple columncontain"/>
<message code="多列相等" value="multiple column equal"/>
<message code="以...开始" value="starts with"/>
<message code="不以...开始" value="not starts with"/>
<message code="以...结束" value="ends with"/>
<message code="不以...结束" value="not ends with"/>
以上只是给出了英文版需要设置的内容,如果需要修改或添加其他语言版本的,请自行添加,保证code值一致即可。
3.11 生成webservice客户端代码
3.11.1 环境要求
EMAP设计器版本必须高于v1.1.24.fix1。
EMAP运行环境版本必须高于1.8.39.B115。
3.11.2 生成步骤
如下图所示,选中一个webservice动作,在右键菜单中
选择EMAP->WS动作生成客户端代码

会出现如下对话框

包名,需要输入你希望将客户端代码生成到那个包路径
类名,需要输入你希望生成的webservice客户端代码的类名,你的程序中将使用这个类来调用webservice。
路径,选择你希望代码生成到哪个目录下。
生成依赖包,在第一次使用时请勾上,会在生成的目录下创建一个lib目录,所有依赖的jar包会生成到这个目录下,你需要将这些jar包(你项目中未引用的)添加到你的项目中。
生成完毕后,将代码添加的你的项目中,就可以使用你生成时指定的类来调用webservice。
3.11.3 WISEDU服务总线
如果你使用WISEDU服务总线的webservice服务,那还需要从“应用管理平台”下载应用的描述文件“app_info.xml”,以及接入学校的证书文件“[应用名称]_dev.cer”;然后将这两个文件放入你的classpath中,如:WEB-INF/classes目录。