AEAI DP开发平台精要

1 背景概述

相信很多了解数通畅联软件的人对AEAI DP应用开发平台并不陌生,笔者在入职第一天就开始接触AEAI DP,使用AEAI DP开发过AEAI WM、AEAI CRM以及中国XXXX管理系统项目,在此过程中对AEAI DP有了较为深入了解,工作之余尝试对AEAI DP的工作原理、实际开发工作涉及的技术点进行梳理,希望能够对其他AEAI DP初学者和使用人员有所帮助。

本文相关说明涵盖AEAI DP的远程热部署新特性,AEAI DP V3.5以后版本支持按工程、模块、资源不同粒度实现代码远程增量部署,类似AEAI ESB中的按流程、服务、工程远程热部署模式。

2 预期读者

  • 数通畅联内部技术人员
  • 外部AEAI DP使用人员

3 技能要求

  • 了解Java Web开发过程,熟悉相关知识点
  • 对AEAI DP技术手册所涉及的内容熟练掌握

4 架构及原理

4.1 DP产品架构解读

AEAI DP开发平台包括三部分,第一部分是一站式的Java Web框架,在数通畅联软件家族中命名为Hotweb,第二部分基于Eclipse插件的扩展开发设计器,在数通畅联软件家族中命名为Miscdp Studio,第三部分是用于开发调试的服务器HotServer。基于Miscdp Studio可以开发普通Java Web应用、集成Java Web应用,还可以为BPM流程平台开发业务表单及流程相关功能。

Hotweb是一站式的MVCFramework,对前端页面交互及控制层做了统一封装,各类功能模型的交互方式封装在父类中。每个页面提供一个Handler处理Java类和展现JSP页面,一个功能可能涉及多个页面,复用一套业务逻辑处理Service和通用的数据存取持久工具类DAOHelper,在Service层调用通用数据存取类DAOHelper。数据持久层基于IBatis封装,Service层基于Spring来做事务控制和服务配置。

4.2 Howeb交互机制

  1. DispatchServlet:前端请求分发,配置于web.xml
  2. Handler:具体请求动作处理及控制,调用Service,返回ViewRender
  3. Service:后台业务处理,包括接口及实现,Spring配置,通过DaoHelper调用SQLMap
  4. SQLMap:负责数据持久化处理(CRUD)
  5. ViewRender:前端展现渲染方式定义,实例化PageBean,放置于request的attributes中
  6. PageBean:承载前端展现的属性、方法
  7. JSP:具体页面代码,使用PageBean来获取handler中设置相关数据。

典型请求页面交互过程模式如下:

  • 页面提交发起,一般是post方式至DispatchServlet

Servlet根据HandlerId实例化Handler、根据actionType反射调用对应的处理方法,方法名跟actionType一致

  • Handler处理

在对应方法中,调用Spring配置的Service来存/取数据库

  • 根据需要返回不同的ViewRender

如果是ajaxRender直接response返回页面,异步加载或者做相关处理

如果是dispatchRender,一般是转向其他的handler来进行处理

如果是LocalRender,直接调用prepareDisplay来做处理

4.3 典型目录层次结构

如上图:module该目录是放置各模块的代码,每个模块有各自的Handler、Service、SQLMap以及配置文件,这种文件结构模式能够支持AEAI DP实现按模块来部署。

common包里放置的是公共类,controller包里方式放置的框架级handler,cxmodule里面方式的公共service接口和实现类。

4.4 核心配置文件说明

通过AEAI DP平台开发生成相关的配置,下面对核心配置文件进行说明:

4.4.1 web.xml配置

在web.xml中包含filter的定义以及实现

Servlet的定义以及实现

以及Listener

4.4.2 Handler配置

1.  HandlerIndex.xml:定义了HandlerId所属的模块,通过Id对应的模块查找对应模块下的映射关系

2.  HandlerModule.xml与HandlerContext.xml中均映射关系指向对应的Handler以及JSP。其中HandlerModule.xml中定义的是不同模块下的映射关系而HandlerContext.xml中是公共访问的如主页、左侧功能列表的显示等。

4.4.3 Service 配置

1.  ServiceContext.xml中典型配置属性:如数据库、appConfig等;

2.  ServiceIndex.xml:通过seviceId所对应的模块去对应模块下的ServiceModule.xml配置文件中查找对应的ServiceImpl以及SqlMap等信息

3.   ServiceModule.xml将对应功能模块下的Service层Controller层,用到的java类封装成bean,并通过property指定对应的SqlMap。

4.   ServiceContext.xml中,配置了公共调用的Service的实现、配置数据库连接、以及事务控制机制。

4.4.4 DB 相关配置

1.  hotweb.properties:配置了数据库的连接信息,如用户名、密码等。

2.  SqlMapModule.xml与SqlMapConfig.xml:是sqlmap的索引文件

3.   sqlmap:在对应的xml中定义SQl进行数据库操作,sqlMap文件的namespace默认是由表名来生成,在ServcieContext.xml(ServiceModule.xml)中指向业务处理ServiceImpl中

5 开发与部署

5.1 工具设置

5.2 开发步骤

AEAI DP开发平台基于数据库驱动开发,推崇适度需求调研、数据库设计先行、原型驱动的敏捷开发模式。整体过程如下:

1.     通过工程向导创建可运行调试的工程框架,默认预置系统管理功能

2.     设计器向导选择对应的功能模型,来创建功能实例

3.     功能实例配置信息首先通过数据表或SQL来默认生成

4.     在设计器进行特定调整(如:查询条件、显示字段、表单元素、校验控制等)

5.     由代码生成器,生成相关的代码及配置

6.     直接部署运行,查看效果,根据实际需要调整设置,重复4—6步

7.     根据实际需要扩展功能代码、调试。

注意:一旦扩展了代码,就不能再次代码生成,那样会覆盖扩展的代码。

5.2.1 开发约束说明

  1. 所有的表都需要创建UUID逻辑主键(36位)
  2. 所有编码类数据在编码管理模块中统一维护
  3. 如果列表SQL有参数需要解析,必须有“where 1=1”
  4. 建议开发采用SVN或者CVS进行版本管理
  5. 如果选择上载资源,则资源关联表,只能三个字段,除了逻辑主键,另外两个字段必须为“BIZ_ID”、“RES_ID”,字段类型都为char(36),建议主键命名为“REF_ID”

6.2.2 跨模块调用说明

在AEAI DP中各个功能模块是彼此隔离的,这里就可能存在跨模块调用问题:

  1. 跨模块调用handler

不需要做特殊处理,仍然index?SomeHandlerID方式调用跨模块的handler。

  1. 跨模块调用service

把对应的接口提升到cxmodule包里,可以采用代码重构的方式,选中对应接口类,然后ctrl+shift+v。

  1. 跨模块调用sqlmap

把sqlmap索引配置迁移公共索引配置(即SqlMapModule.xml中对应配置迁移至SqlMapConfig.xml),模块中的sqlmap文件也对应迁移公共sqlmap目录。

5.2.3 常用Java类清单


类别


常用类


功能说明


编码定义及界面元素


FormSelectFactory


基于编码定义的表单下拉框Select工厂类


FormSelect


表单下拉框Select泛化模型


RadioGroup


RadioButton模型,一般基于FormSelect转换生成


CheckBoxGroup


CheckBox模型,一般基于FormSelect转换生成


树构建器及模型


TreeBuilder


树形模型构建类


TreeModel


树形泛化模型,可以生成前端的多选、单选框


权限相关


Profile


身份权限概要模型,在Handler中可以直接获取


User


用户模型,可以通过Profile来获取


Role


角色模型,可以通过User来获取


Group


分组模型,可以通过User来获取


Resource


资源模型,可以通过User来获取


数据库存取相关


DaoHelper


数据访问对象工具类,封装ibatis的相关操作


DBHelper


数据操作工具类,基于Spring的数据源配置


KeyGenerator


主键生成类,一般情况下是UUID格式


工具类相关


DateUtil


日期处理工具类


StringUtil


字符串处理工具类


FileUtil


文件处理工具类


IOUtil


输入输出流工具类

5.3 部署方式

双击项目名弹出如下界面,可以修改服务器的地址并修改对应的用户名,连接对应的服务器后部署应用即可。

注意:服务器用户名、密码在Hotserver中有对应的配置,如下图:

在Miscdp Studio对工程中任意目录、文件都可以右键,在右键菜单底部点击“Miscdp资源部署”,如下图:

弹出部署设置窗口,AEAI DP支持三种粒度部署模式:模块、资源、应用。应用就是整个工程,模块指的module目录下的各个package,除了module下的文件,其他都属于资源。可以支持多个模块或者多个资源一起部署,而且内置的部署机制会自动识别,是否要重新加载模块或者重启应用。

5.3.1 按资源部署

在除模块下部署jsp,xml,js,java文件等为资源部署。当部署的内容包含源码时,会默认重启应用

部署的内容不包含源码时默认不会重启应用,且支持同时部署多个资源文件。

5.3.2 按应用部署

在项目名下右键资源部署为部署应用且默认重启应用。

5.3.3 按模块部署

在模块下选择xml、java文件等右键部署资源均为模块部署,默认会重新加载这个模块

注意:在模块的jsp文件夹下右键资源部署(弹出界面如上图)也为模块部署,默认会重新加载部署的模块。但是如果选择是模块jsp文件夹下的jsp文件,则为部署资源,且不用重启应用(jsp是自动编译)。

5.3.4 部署后目录

正常情况下Web应用的所有的classes都在WEB-INF/classes目录下,但基于AEAI DP开发的Web应用部署后有一个特殊目录modules,所有模块里的classes以及对应的配置文件都放置在这个目录里,如下图。

5.4 调试配置

远程调试是复杂业务逻辑的错误定位过程中常用手法,通过调试也可以使程序员对代码脉络更清楚,是程序员必备的技能。AEAI DP 基于Eclipse内置远程机制进行代码调试,具体参见《AEAI DP开发平台技术手册》中的调试配置部分内容。

6 知识点汇总

6.1 分层解读

6.1.1 控制层

对应的Handler如下图:

1.  prepareDisplay方法

在handler中默认执行prepareDisplay方法

mergeParam(param)方法中通过handlerId的对比确定需要append的param的信息

storeParam方法在页面间跳转时候,将param存到一个session中,跟mergetParam联合使用保证在页面间切换时候,列表页面的查询条件不丢失。

setAttributes(param)方法中做遍历将param中的值进行set将数据放入到页面中

返回new LocalRenderer(getPage()),在LocalRenderer中的executeRender方法中创建PageBean,所有handler中的属性信息都传入至PageBean中,通过request.setAttribute(PAGE_BEAN_KEY,pageBean)方法将handler信息放入页面所以在JSP中可以通过pageBean中封装的getHandlerURL()获得handlerURL。

2.  processPageAttributes方法

processPageAttributes方法用于回调处理扩展属性(下拉框处理、默认值处理)

3. initParameters方法

initParameters方法中可以设置查询条件的默认值

4. getService方法

在getService方法中返回到对应的调用的Service

5. 扩展方法机制说明

方法名与actionType隐藏域变量一致,建议采用@PageActionanotation方式,如果不添加@PageAction,则方法名为do+”actionType”+Action

6.1.2 显示层

顾名思义显示层就是用于前台显示的jsp页面如下图:

1.  Page指令

通过Page指令的contentType将页面编码定义为UTF-8

pageEncoding是jsp文件本身的编码

contentType的charset是指服务器发送给客户端时的内容编码

2.  Jsp动作

通过jsp在头部useBean在页面引用的javaBean(即pageBean),其中class属性指定类的实例并将其绑定到名由id属性定义的变量上,scope为request,如下:

相关jsp内置方法如下:

jsp:include:在页面被请求的时候引入一个文件。

jsp:useBean:寻找或者实例化一个JavaBean。

jsp:setProperty:设置JavaBean的属性。

jsp:getProperty:输出某个JavaBean的属性。

jsp:forward:把请求转到一个新的页面。

jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

3. Jsp表单

一般情况下页面均会包含一个表id为form1,且内部有一个隐藏域actionType在表单提交时需要赋值。

一般情况下明细表单除了actionType以外还会包含一个operaType隐藏域,用来区分当前操作的状态是insert还是 update。

在form表单中action动作指令通过pageBean.getHandlerURL()获得handler指定跳转的路径,method="post"提交方式为post

4. 资源文件

在Jsp页面中引入通用的资源文件:

<%@include file="/jsp/inc/resource.inc.jsp"%>在该页面中包括常用的javascript及css

在script中定义页面处理的JS,也可以直接引用封装与于Util.js中常见的页面校验(非空、长度等)、请求方法(doDelete、doSubmit)等

util.js中封装了常用js方法如:


function doDelete(itemValue)

function doQuery(params)

function doRequest(actionType,params)

function doSubmit(reqOptions)

function postRequest(form,params)(ajax处理)

function sendRequest(targetUrl,params) (ajax处理)

style.css为最常用css,其中定义form、table、tr、td样式以及按钮的图标样式等

6.1.3 服务层

后台业务逻辑生成接口与实现类如下图:

后台业务逻辑都生成接口与实现类,都在ServiceContext.xml(ServiceModule.xml)中配置。在实现类中定义对数据库的操作

1. 继承关系说明

每个功能模型都有自己的父类和接口,继承关系如下图所示:

2. processDataType

根据数据表的元数据来转换处理表单传过来的数据,可以扩展设置类型转换器,类型转换器扩展设置在ServiceContext.xml或者ServiceModule.xml中配置,BaseService中对应代码如下:


protected  void processDataType(DataParam  dataParam,String tableName){

Table table =  getTableMetaData(tableName);

List<Column> dateColumns =  table.getDateTypeColumns();

int  count = dateColumns.size();

for  (int i=0;i < count;i++){

Column column =  dateColumns.get(i);

String columnName =  column.getName();

Object paramValue =  dataParam.getObject(columnName);

if (paramValue != null && paramValue instanceof  String){

if (!String.valueOf(paramValue).trim().equals("") && !String.valueOf(paramValue).trim().equals("null")){

Date  dateValue = DateUtil.getDateTime((String)paramValue);

dataParam.put(columnName,dateValue);

}else{

dataParam.remove(columnName);

}

}

}

if  (!this.fieldConvertMap.isEmpty()){

Iterator<String>  keys = this.fieldConvertMap.keySet().iterator();

while (keys.hasNext()){

String key =  keys.next();

Column column =  table.getColumn(key);

if (column != null){

DataParamConvert  convert = this.fieldConvertMap.get(key);

Object  value = dataParam.get(key);

Object  convertValue = convert.convert(value, column);

dataParam.put(key,convertValue);

}

}

}

}

3. processPrimaryKeys

处理主键默认生成机制,AEAI DP中默认是使用uuid来作为主键的,其实框架层面还支持自增字段主键机制。


protectedvoid processPrimaryKeys(DataParam  param){

KeyGenerator keyGenerator = new KeyGenerator();

if (Constants.PKType.INCREASE.equals(pkGenPolicy)){

param.put(primaryKey,keyGenerator.getMaxId(tableName, primaryKey,getDataSource()));

}

elseif(Constants.PKType.CHARGEN.equals(pkGenPolicy)){

String genPk =  keyGenerator.genKey();

param.put(primaryKey,genPk);

}

}

6.2 重点技术

6.2.1 常用方法

1. 在PageBean中封装了常见的页面中获取值的方法,如下:


public String  inputDate(String key)

public String  inputTime(String key)

public String inputValue(int  i,String key)

public String  selectValue(int i,String key,String formSelectKey)

public RadioGroup  selectRadio(String elementName)

public String  selectedValue(String elementName)

public String  selectedText(String elementName)

public String disabled(boolean  expression)

public String  readonly(String expression)

2. 根据需要返回不同的ViewRender,一般情况下为:AjaxRender、DispatchRender、RedirectRenderer、LocalRender

1)       AjaxRender:直接response返回值页面,异步加载或者做相关处理

2)       DispatchRender:一般是转向其他Handler来进行处理,页面跳转传递request和response可以支持跳转到第三方按Backspace可以返回

3)       RedirectRenderer:也是转向其他Handler但不能传递request和response

4)       LocalRender:显示 JSP页面

3. DateUtil类中预置许多关于日期处理的方法,例如:日期的格式化,日期的计算,获得当月、当年的第一天等等。

4. 主键生成

调用KeyGenerator.instance().genKey()来自动生成主键ID

5.  加密解密

通过CryptionUtil中的加密解密方法进行加密解密

  • 加密方法encryption("root", "12345678");其中root为需要加密的明文、”12345678”为加密的KEY。
  • 解密方法decryption(secretData, secretKey);

6. 在User类中预置了许多关于User的跟角色、群组关联属性等

  • 可以通过User user = (User) this.getUser()获取User对象
  • 并同user对象打点调用不同的方法获得User与角色和群组关联的属性

7. Dataparam转换

  • DataRow调用row.toDataParam();
  • List调用ListUtil.toParamList(records,srcKeys, replaceKeys)

8. StringUtil字符串辅助类

agileai_common的jar包中内置了很多辅助类,如StringUtil,DataUtil,ListUtil、DBUtil等。

9.  ListUtil类转换方法

预置许多List转换方法如listToMap()、toXML()、toDataSet()、toParamList()

10.   PopupBox.js中封装基于div+iframe的弹出框

Js中使用PopupBox方法时需要将对应的var写在方法的外面,每次调用使用的都是同一个弹出框,写在方法内部时每次调用方法都会新生成一个弹出框。

11.   内置Ajax处理方式

functionpostRequest(form,params)

functionsendRequest(targetUrl,params)

第一种是post方式,第二种是get方式。

6.2.2 授权配置

1.  功能授权

系统预置功能可以设置功能目录、功能节点授权,而且可以授权给角色、群组、人员。

注意:

  • 如果授权子节点,则父节点必然会授权。
  • 如果只授权到父节点,子节点没有被任何角色、群组、人员,则可以访问父节点下的所有功能节点。
  • 如果子节点已被其他的角色(群组、人员)授权,则必须要对这些子节点进行授权,而不能通过授权到父节点的方式来授权子节点。

2. 操作授权

  • 通过jsp引入标签按钮配置授权
  • 通过状态、角色控制权限

参见《AEAI DP按钮权限配置说明》,http://my.oschina.net/agileai/blog/533385

6.2.3 典型场景

1.  复制数据插入

1) 把原结果集查询出来,类型为List<DataRow>;

2) 使用ListUtil.toDataParam方法把结果集转换为参数集合,类型为List<DataParam>;

3)  使用DaoHelper调用sqlmap中配置的insertSQL来插入参数集合。

2.  排序控制说明

首先在JSP页面,添加按钮


<td  onmouseout="onMout(this);

" align="center"><input id="upImgBtn"  value="&nbsp;" title="上移" type="button"  class="upImgBtn" style="margin-right:0px;" />上移</td>

<td  onmouseout="onMout(this);

" align="center">

<input id="downImgBtn" value="&nbsp;" title="下移" type="

调用的JS方法如下


function doMoveUp(){

if (!isSelectedRow()){

writeErrorMsg(‘请先选中一条记录!‘);

return;

}

doSubmit({actionType:‘moveUp‘});

}

function doMoveDown(){

if (!isSelectedRow()){

writeErrorMsg(‘请先选中一条记录!‘);

return;

}

doSubmit({actionType:‘moveDown‘});

}

上移下移在handler里的方法


public ViewRenderer  doMoveUpAction(DataParam param){

String  currentSubTableId = param.get("currentSubTableId");

boolean isFirst =  getService().isFirstChild(currentSubTableId,param);

if (isFirst){

setErrorMsg(this.moveUpErrorMsg);

}else{

getService().changeCurrentSort(param,  true);

}

return  prepareDisplay(param);

}

public ViewRenderer  doMoveDownAction(DataParam param){

String  currentSubTableId = param.get("currentSubTableId");

boolean isLast =  getService().isLastChild(currentSubTableId,param);

if (isLast){

setErrorMsg(this.moveDownErrorMsg);

}else{

getService().changeCurrentSort(param,  false);

}

return  prepareDisplay(param);

}

在service方法里调整两个字段排序顺序


publicvoid changeCurrentSort(DataParam  param, boolean isUp) {

String  subId = param.get("currentSubTableId");

String statementId = sqlNameSpace+"."+"find"+StringUtil.upperFirst(subId)+"Records";

List<DataRow>  records = this.daoHelper.queryRecords(statementId, param);

String  tableName = subTableIdNameMapping.get(subId);

String  idField = "当前表的主键字段名";

String  currentId = "记录标识";

String  sortField = "排序字段名";

DataRow  curRow = null;

String  curSort = null;

if (isUp){

DataRow  beforeRow = null;

for (int i=0;i <  records.size();i++){

DataRow  row = records.get(i);

String  tempMenuId = row.stringValue(idField);

if  (currentId.equals(tempMenuId)){

curRow  = row;

beforeRow  = records.get(i-1);

break;

}

}

curSort  = curRow.stringValue(sortField);

String  beforeSort = beforeRow.stringValue(sortField);;

curRow.put(sortField,beforeSort);

beforeRow.put(sortField,curSort);

this.updateSubRecord(subId,curRow.toDataParam(true));

this.updateSubRecord(subId,beforeRow.toDataParam(true));

}else{

DataRow  nextRow = null;

for (int i=0;i <  records.size();i++){

DataRow  row = records.get(i);

String  tempMenuId = row.stringValue(idField);

if  (currentId.equals(tempMenuId)){

curRow  = row;

nextRow  = records.get(i+1);

break;

}

}

curSort  = curRow.stringValue(sortField);

String  nextSort = nextRow.stringValue(sortField);

curRow.put(sortField,nextSort);

nextRow.put(sortField,curSort);

this.updateSubRecord(subId,curRow.toDataParam(true));

this.updateSubRecord(subId,nextRow.toDataParam(true));

}

}

2.  结果集转换为FormSelect

在进行开发的时候,会遇到select下拉框形式 的表单元素,这时就需要在控制层的pricessPageAttributes方法中将查询得到的结果集转换为formselect类型,如下例:


List<DataRow> records =  getService().findPcNameRecords(param);

FormSelect formSelect = new FormSelect();

formSelect.setKeyColumnName("PC_CODE");

formSelect.setValueColumnName("PC_NAME");

formSelect.putValues(records);

setAttribute("pcName",formSelect.addSelectedValue(param.get("pcName")));

首先通过查询得到list结果集records,结果集中需要存在有id(编码)和value(值)两列属性

然后new一个FormSelect类型的变量,然后给这个变量添加对应的id和value两个属性字段,再通过putValues方法把结果集中的数据放到formSelect中。在setAttribute中将param中对应的变量的值通过formSelect将这个选中的值存入到变量pcName中。

3. 基于HTML导出Word、PDF

1) 构造Html模板

2) 将模板转换为IO流

3)  调用对应的API进行格式转换

4.  批量处理结果集

首先需要前台jsp页面添加checkbox复选框,代码如下:


<ec:row styleClass="odd" oncontextmenu="selectRow(this,{ID_ID:‘${row.ID_ID}‘,curColumnId:‘${row.ID_ID}‘});controlUpdateBtn(‘${row.ID_STATE}‘);refreshConextmenu()"  onclick="selectRow(this,{ID_ID:‘${row.ID_ID}‘,curColumnId:‘${row.ID_ID}‘});controlUpdateBtn(‘${row.ID_STATE}‘);">

<ec:column width="50" style="text-align:center"  property="_0" title="序号" value="${GLOBALROWCOUNT}"  />

<ec:column width="25" style="text-align:center"  property="ID_ID" cell="checkbox" headerCell="checkbox"  onclick="$(this).parents(‘tr‘).trigger(‘click‘);"/>

</ec:row>

隐藏域:

<input type="hidden" id="ids"

name="ids" value="<%=pageBean.inputValue("ids")%>" />

上面个代码选中了当前记录的ID,然后点击操作按钮调用对应的JS方法,将ids传到后台handler中处理:


<td   class="bartdx"   hotKey="D"  align="center" onclick="doDeleteRequest()">

<input value="&nbsp;"  title="删除" type="button"  class="delImgBtn" id="delete" />删除</td>


function doDeleteRequest(){

var ids = "";

var confirmsubMsg="您确认删除该数据吗?";

$("input:[name=‘ID_ID‘][checked]").each(function(){

ids  = ids+$(this).val()+",";

});

if (ids.length > 0){

ids  = ids.substring(0,ids.length-1);

}

if(confirm(confirmsubMsg))  {

//showSplash(deleteMsg);

$("#ids").val(ids);

doSubmit({actionType:‘batchDelete‘});

}

}

由于接到的参数为多条记录,并用逗号分隔的,需要在后台进行处理,具体代码如下:


public ViewRenderer doBatchDeleteAction(DataParam param) {

String ids = param.get("ids");

if(!"".equals(ids)){

String[]  idArray = ids.split(",");

for(int i=0;i < idArray.length;i++ ){

String  id = idArray[i];

DataParam  idParam = new DataParam();

idParam.put("ID_ID", id);

getService().deleteClusterRecords(idParam);

}

}

return prepareDisplay(param);

}

5. 数值公式计算

我们在项目使用MVL的TemplateRuntime计算方式,具体代码如下:


publicstaticvoid main(String[] args) {

String  testFormula = "@{value+5}";

Map<String,  Double> vars = new HashMap<String, Double>();

vars.put("value", 3.0);

Number  number  = (Number) TemplateRuntime.eval(testFormula,vars);

System.out.print(number);

}

输出后的结果:

6. 后台控制jsp页面<td>标签合并


public ViewRenderer  prepareDisplay(DataParam param){

initParameters(param);

this.setAttributes(param);

if  (!TreeAndContentManage.BASE_TAB_ID.equals(tabId)){

param.put("columnId",columnId);

List<DataRow>  rsList = getService().findContentRecords(filterTreeModel,tabId,param);

this.processRowSpanField(rsList);

this.setRsList(rsList);

request.setAttribute("rsList", rsList);

}else{

DataParam queryParam = new DataParam(columnIdField,columnId);

DataRow row =  getService().queryTreeRecord(queryParam);

this.setAttributes(row);

}

processPageAttributes(param);

return  new LocalRenderer(getPage());

}

privatevoid processRowSpanField(List<DataRow>  rsList){

int spanCount = 0;

DataRow  beforeRow = null;

for (int i=0;i <  rsList.size();i++){

DataRow  curRow = rsList.get(i);

String  curRlName = curRow.getString("RL_NAME")!=null?curRow.getString("RL_NAME"):"";

if (beforeRow == null){

beforeRow  = curRow;

spanCount++;

beforeRow.put("rowSpan",spanCount);

continue;

}

String  beforeRLName = beforeRow.getString("RL_NAME")!=null?beforeRow.getString("RL_NAME"):"";

if  (beforeRLName.equals(curRlName)){

spanCount++;

beforeRow.put("rowSpan",spanCount);

curRow.put("rowSpan",0);

}

else{

curRow.put("rowSpan",1);

beforeRow  = curRow;

spanCount  = 0;

spanCount++;

}

}

}

在显示列表页面时,调用下面代码


<tr>

<td style="text-align:center;" nowrap><%=(i+1)%></td>

<%if (0 != rowSpan){%>

<td rowspan="<%=rowSpan%>"><%=dataRow.getString("RL_NAME","")%></td>

<%}%>

</tr>

6.3 学习技巧

6.3.1 利用system代码

使用AEAI DP创建工程的时候默认就会创建系统管理相关代码,系统管理里面涉及的模块几乎是所有管理系统都带的功能,这部分代码较多,值得推荐学习,事实上很多开发所需要的代码样例都可以在里面找到。

6.3.2 跟踪研读父类

Hotweb预置很多典型的功能模型,每个Handler和Service都继承对应功能模型的Handler和Service,而且hotweb源码都直接打入hotweb_core.jar,在开发、调试过程中可以跟踪研究父类来深入掌握相关调用机制、借鉴相关代码技巧。

6.3.3 查找类似代码

很多js代码、工具类的调用方式其实都散落在系统框架(预置的以及自动生成)的代码里,可以通过DP Studio(Eclipse)的文件查找方式来了解相关调用机制。

6.3.4 反编译参考代码

在hotweb中也引用了一些地方类库,如:ecside、ibatis、spring、mvel等等,包括AEAI Portal、AEAI BPM的一些classes、jar文件,都可以通过jd-gui来反编译学习分析代码,参考样例,如:反编译预置的portlet代码、参考开发新portlet。

7 感悟与收获

AEAI DP开发平台可以快速上手,对于最开始入职的我较容易产生成就感,但是随着后续深入使用,实际应用过程中的各种扩展开发,逐渐暴露出我自己的知识积累不足,同时也发现Miscdp Studio可以生成典型功能、典型的方法,便于调试部署,但更多代码的精华是藏在Hotweb框架里,Hotweb预留很多方法用于扩展,预置很多工具类通过组合使用可以几乎所有场景的功能,同时第三方的类库也很容易在AEAI DP中调用来满足实际项目的特定需求。

AEAI DP开发平台精要文档            下载

时间: 2024-10-11 21:52:37

AEAI DP开发平台精要的相关文章

AEAI DP开发平台v3.3.0_20150228发版说明

产品说明 AEAI DP应用开发平台专门用于开发MIS类的Java Web应用,也称Miscdp(Misc Develope Platform)综合应用开发平台. 本次发版产品AEAI DP开发平台v3.3.0_20150228是2014年发版的开发平台v3.2.0_20141016的升级版本,该产品现已开源并上传至开源社区http://www.oschina.net/p/aeaidp,大家可以下载介质免费使用.详细内容可登入官网www.agileai.com了解,也可以加入我们的技术交流群29

AEAI DP按钮权限配置说明

1 背景概述 AEAI DP3.5版本以后支持对按钮权限进行灵活的管理配置,本文对配置过程进行详细说明,为相关使用人员提供指导和参考. 2 预期读者 数通畅联技术人员 AEAI DP开发平台使用人员 3 实现思路 在对应的工程的web.xml添加配置保证jsp页面标签的正确引入,在jsp页面中对应的按钮添加定义,使用管理员登录在功能管理—处理器列表中添加对应Handler以及按钮的定义,点击安全设置对不同的按钮进行权限配置. 4 实现步骤 4.1 web.xml配置 在web.xml添加如下配置

实用开发平台对比分析

1 概述 随着软件行业日新月异的发展,使用传统的开发工具已经不能满足现有程序员的诉求,每个项目都要从零开始,代码无复用率:所有的基础功能需要一行一点的敲代码,一个预置基本功能模型,可以快速生成代码的开发平台已经成为了"码农"的迫切需要. 笔者作为"码农"中的一员也深受其害,今天与大家分享笔者对开发平台的认知以及为大家推荐几款实用的开发平台. 2 必备要素 考量一个款实用的开发平台 2.1 操作简单 实用的开发平台作为开发工具需要是操作简便如预置导航引导,以及代码编辑

AEAI DP V3.7.0 发布,开源综合应用开发平台

1  升级说明 AEAI DP 3.7版本是AEAI DP一个里程碑版本,基于JDK1.7开发,在本版本中新增支持Rest服务开发机制(默认支持WebService服务开发机制),且支持WS服务.RS服务的热部署机制. 后续基于AEAI DP开发的开源AEAI应用软件,如:CRM.HR.WM.EM将会陆续基于AEAI DP 3.7版本进行升级. 2  升级内容 1.类库升级: a) 升级JDK1.7.X b) 升级CXF版本为3.1.2 c) 升级Spring版本为4.1.6.RELEASE d

AEAI DP V3.6.0 升级说明,开源综合应用开发平台

AEAI DP综合应用开发平台是一款扩展开发工具,专门用于开发MIS类的Java Web应用,本次发版的AEAI DP_v3.6.0版本为AEAI DP _v3.5.0版本的升级版本,该产品现已开源并上传至开源社区,下载地址:http://pan.baidu.com/s/1gdKlQvD. 1 升级说明 AEAI DP v3.6版本是一次常规升级,也是AEAI DP最后一次支持JDK1.6版本,后续AEAI DP产品升级将会基于JDK1.7进行扩展开发.AEAI DP 3.7版本将扩展支持Res

AEAI DP调试方式说明

1.概述 程序调试是复杂业务逻辑的错误定位过程中非常必要的功能,通过调试也可以使程序员对程序脉络更清楚,是程序员必备的一项基本技能.Miscdp调试分两种,一种是远程调试,一种是本地调试.本篇文档就这两种调试方法,讲解一下它们分别是如何使用的. 2.预期读者 本文档预期读者主要为正在培训或已经培训过该平台使用方法的软件开发.测试人员.作为培训时技术手册的扩展内容,可以对技术手册上关于调试方面的内容加以补充. 3.远程调试 使用远程调试,需要使用HotServer服务器.在命令提示符上进入HotS

趣拍云:深耕细作 打造最专业短视频开发平台

短视频是指视频长度不超过2分钟,通过短视频平台拍摄.编辑.上传.播放.分享.互动的,视频形态涵盖纪录短片.DV短片.视频剪辑.微电影.广告片段等的视频短片的统称,伴随着社交媒体的迅猛发展以及网络带宽.云技术等基础条件的成熟,短视频市场在2016年迎来了爆发式增长,据有关数据显示,预计到2020年,短视频内容消费贡献的广告营收将是现在的10倍,达到600亿元人民币.如此巨大的市场,自然吸引了众多参与者,而杭州短趣网络传媒技术有限公司就是其中一个参与者,他们于2016年1月推出的 "趣拍云"

AEAI Portlet开发心得

1 背景概述 Portlet是AEAI Portal组件API,是基于Java的Web组件,由Portlet容器管理,并由容器处理请求,生产动态内容.AEAI Portal中已经预置了许多Portlet组件,可以直接配置使用.由于不同业务需求也可以将Portlet进行定制开发.本文是本人在中建投项目中由于业务需要动态显示风险统计信息,即对某一风险进行评估时引用不同的风险点对其的影响(可能性与影响程度的乘积)进行分析,并在页面以个数的形式显示不同区间所包含风险点的影响.故而对Portlet的定制开

Cocos2d-x游戏开发技术精解读书摘要(2016-5-27 10:52)

 Cocos2d-x游戏开发技术精解 刘剑卓 著 2013年6月第1版 chap2 Cocos2d-x引擎的开发环境 2.1跨平台的开发 2.2建立开发环境 2.2.1 PC开发环境 2.2.2 Android开发环境 2.2.3 iOS开发环境 2.3引擎中的混合编译 2.3.1 Java与C++的混合编译 2.3.2 Objective-C与C++的混合编译 2.4引擎的起点 2.4.1应用程序入口 2.4.2引擎应用入口 2.5丰富的示例程序 2.5.1 TestCpp示例项目 2.5