前言
zyj工作的本质就是将Hec4.0项目逻辑全部搬到zyj项目,整个过程中的工作内容如下:
- 迁表:将Hec4.0的Oracle表迁到 zyj 的mysql表
- 迁移后端代码
- 迁移前端代码
代码部分需针对原有逻辑做相应的调整。
一、迁表
整个迁表过程如下:
1.修改Oracle表
- 表名
- 将复数表名改为单数
- Number字段
- 将Oracle数据库中整型含义的Number都改为Number(38),这样生成groovy时就会生成为bigint
- 浮点型含义的Number不用改,生成groovy时会生成为 Decimal
2.生成groovy
2.1 生成groovy
-
访问
groovy 文件生成器
地址:http://192.168.12.107:16060 -
填写 author、fileName,检索表,勾选表名,然后点击 `生成Groovy
- 将生成的
groovy 文件
放入到hap-core-db
工程下
2.2 修改groovy
-
描述ID
- 将生成的groovy文件中的comment中的id去掉
-
索引名
3.导入mysql
- 将项目重新
clean install
先将hap-core-db模块clean install 一下,然后再将hap-db模块clean install一下(没有测过)
- 在parent下执行 process-resources 命令
mvn process-resources -D skipLiquibaseRun=false -D db.driver=com.mysql.jdbc.Driver -D db.url=jdbc:mysql://192.168.12.68:3306/hap_dev -D db.user=root -D db.password=h@ndhec
二、迁移后端代码
1.生成后端代码
启动zyj 项目,访问代码生成器页面
http://{port}/${context}/generator/generator.html
com.hand.hec
2.后端调整
2.1 DTO
- 字段需要与表结构严格对应
- DTO名称与表名严格对应
- 多表关联字段尽量使用JoinTable方式实现
2.2 Mapper迁移指引
- Mapper接口应当与DTO命名严格对应
- 没有特殊要写的SQL逻辑,无需单独编写mapper文件
- SQL语句需要确保未使用数据库方言,能够在多数据库之间兼容
- …
2.3 Service迁移指引
- 基表类Service应当与DTO命名严格对应,复杂逻辑类Service命名应当与原PKG相对应
- 没有特殊验证逻辑,无需编写Service方法体
- 业务逻辑型验证应当写在Service中
- …
2.4 Controller
- 一个页面应当对应一个Controller
- 一个页面内的同模块内的操作应当尽量在当前页面的Controller内完成
- 如果当前页面没有初始化查询,无需特殊设置ModelAndView,在Controller类上设置RequestMapping
- RequestMapping格式为:/模块名/A (A中单词用中划线分割)
三、迁移前端页面
新增的页面请遵循开发规范
若是迁移表,则screen页面中需要做如下修改:
-
动态取值
- 页面的所有[]
-
字段名
- 页面内的字段名(filed、column等)需要改成驼峰形式,以匹配DTO字段
-
prompt
- 字段级别的prompt在
dataSet
的field上进行定义 - 格式为小写
- 所有的prompt都需要在hap内注册
- 字段级prompt的命名规则为:基表名.字段名
- 校验级prompt的命名规则为:基表名.校验结果
- 字段级别的prompt在
-
baseUrl
、queryUrl
、submitUrl
- 页面内的DataSet的model切换成
baseUrl
,queryUrl
和submitUrl
也切换成相关地址 baseUrl
无需加添加 context
- 页面内的DataSet的model切换成
-
model-query
、loadData
- 页面的所有的初始化查询:
model-query
和loadData
,都需要改为由Controller
中返回值为ModelAndView
的方法中转
- 页面的所有的初始化查询:
四、常见问题汇总
1.日期选择
日期格式要精确到时分秒,否则后台无法接收
使用 renderer="Aurora.formatDateTime"
和 <a:dateTimePicker id="GLD2030_grid_dp"/>
示例如下:
<a:grid id="GLD2030_responsibility_centers_grid" bindTarget="GLD2030_responsibility_centers_result_ds" marginHeight="115" marginWidth="3" navBar="true">
<a:columns>
<a:column name="responsibilityCenterCode" align="center" editor="GLD2030_grid_tf" editorFunction="GLD2030_responsibilityCenterEditFun" sortable="true" width="80"/>
<a:column name="responsibilityCenterName" align="left" editor="GLD2030_grid_tf" width="200"/>
<a:column name="respCenterTypeCodeDisplay" align="center" editor="GLD2030_grid_cb" width="80"/>
<a:column name="startDateActive" align="center" editor="GLD2030_grid_dp" renderer="Aurora.formatDateTime" width="80"/>
<a:column name="endDateActive" align="center" editor="GLD2030_grid_dp" renderer="Aurora.formatDateTime" width="80"/>
<a:column name="summaryFlag" align="center" editor="GLD2030_grid_cx" editorFunction="GLD2030_responsibilityCenterEditFun" width="80"/>
<a:column name="assignBgtEntity" align="center" renderer="GLD2030_AssignBgtEntityRender" width="80"/>
<a:column name="childResponsibilityCenter" align="center" renderer="GLD2030_setSubResbonsibilitys" width="80"/>
</a:columns>
<a:editors>
<a:textField id="GLD2030_grid_tf" typeCase="upper"/>
<a:checkBox id="GLD2030_grid_cx"/>
<a:comboBox id="GLD2030_grid_cb"/>
<a:dateTimePicker id="GLD2030_grid_dp"/>
</a:editors>
</a:grid>
2.lov
2.1 lov实现
(1)field中使用lovCode
<a:field name="bgtEntityName" lovCode="LOV.BGT_ENTITY.BGT_ENTITY_NAME" required="true" prompt="BGT_ENTITY.DESCRIPTION" title="BGT_ENTITY.BGT_ENTITY_NAME">
<a:mapping>
<a:map from="entityId" to="bgtEntityId"/>
<a:map from="entityName" to="bgtEntityName"/>
</a:mapping>
</a:field>
(2)grid中使用lov编辑器
<a:grid id="GLD2030_respCenterRefBeGrid" bindTarget="GLD2030_respCenterRefBeDs" marginHeight="200" navBar="true" width="1000">
<a:columns>
<a:column name="bgtEntityName" align="center" editor="GLD2030_accEntityRefLov" width="200"/>
<a:column name="bgtCenterName" align="center" editorFunction="GLD2030_BgtCenterEditorFun" width="120"/>
<a:column name="defaultFlag" editor="GLD2030_accEntityRefCk" prompt="TRAVEL_PLAN_TYPE.DEFAULT_FLAG"/>
<a:column name="enabledFlag" editor="GLD2030_accEntityRefCk" prompt="FND_COMPANY_GROUP.ENABLED_FLAG"/>
</a:columns>
<a:editors>
<a:lov id="GLD2030_accEntityRefLov"/>
<a:checkBox id="GLD2030_accEntityRefCk"/>
</a:editors>
<a:events>
<a:event name="cellclick" handler="GLD2030_GridCellClick"/>
</a:events>
</a:grid>
(3)然后页面上去定义lov
2.1 lov字段带出
需求说明:实现页面一加载时就自动带出关联的lov字段的值,如下图
(1)通过给实体类添加 JoinTable
、 JoinColumn
注解
/**
* 预算中心ID
*/
@JoinTable(name = "bgtCenterJoin", joinMultiLanguageTable = false, target = com.hand.hec.zyj.bgt.dto.BgtCenter.class,
type = JoinType.LEFT, on = {@JoinOn(joinField = com.hand.hec.zyj.bgt.dto.BgtCenter.FIELD_CENTER_ID)})
@NotNull
private Long bgtCenterId;
/**
* 预算中心名称
*/
@Transient
@Length(max = 500)
@JoinColumn(joinName = "bgtCenterJoin", field = com.hand.hec.zyj.bgt.dto.BgtCenter.FIELD_DESCRIPTION)
private String bgtCenterName;
(2)使用 Criteria 和 selectOptions
@RequestMapping("/query")
@ResponseBody
public ResponseData query(RespCenterRefBe dto, @RequestParam(defaultValue = DEFAULT_PAGE) int page,
@RequestParam(defaultValue = DEFAULT_PAGE_SIZE) int pageSize, HttpServletRequest request) {
IRequest requestContext = createRequestContext(request);
Criteria criteria = new Criteria(dto);
criteria.where(new WhereField(RespCenterRefBe.FIELD_BGT_ENTITY_NAME),
new WhereField(RespCenterRefBe.FIELD_BGT_CENTER_NAME));
return new ResponseData(service.selectOptions(requestContext,dto,criteria));
}
(3)同时mapper.xml注意要添加 transient字段
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.hand.hec.gld.mapper.RespCenterRefBeMapper">
<resultMap id="BaseResultMap" type="com.hand.hec.gld.dto.RespCenterRefBe">
<result column="REF_ID" property="refId" jdbcType="DECIMAL" />
<result column="RESP_CENTER_ID" property="respCenterId" jdbcType="DECIMAL" />
<result column="BGT_ENTITY_ID" property="bgtEntityId" jdbcType="DECIMAL" />
<result column="BGT_CENTER_ID" property="bgtCenterId" jdbcType="DECIMAL" />
<result column="DEFAULT_FLAG" property="defaultFlag" jdbcType="VARCHAR" />
<result column="ENABLED_FLAG" property="enabledFlag" jdbcType="VARCHAR" />
<result column="BGT_ENTITY_NAME" property="bgtEntityName" jdbcType="VARCHAR" />
<result column="BGT_CENTER_NAME" property="bgtCenterName" jdbcType="VARCHAR" />
</resultMap>
</mapper>
2.3 lov传参
(1)、lovCode传参
field的lovCode属性参数用?连接
<a:field name="unitCode" autoComplete="true" autoCompleteField="unitName" lovGridHeight="320" lovHeight="450" lovCode="LOV_EXP_UNIT_ASSIGN?companyId=$[/parameter/@companyId]?magOrgId=$[/parameter/@magOrgId]" lovWidth="500" required="true" prompt="exp_org_units.unit_code" title="acp_invoice_lines.unit_id">
<a:mapping>
<a:map from="unitId" to="unitId"/>
<a:map from="unitCode" to="unitCode"/>
<a:map from="unitName" to="unitName"/>
<a:map from="companyCode" to="companyCode"/>
<a:map from="companyShortName" to="companyName"/>
</a:mapping>
</a:field>
(2)、js传参
record.getField('unitCode').setLovPara('companyId', '$[/parameter/@companyId]');
2.4 lov定义自定义sql示例
<if test="pageId != null">
<bind name="pageIdNum" value="@java.lang.Long@parseLong(pageId)"/>
</if>
SELECT
layout_id,
layout_code,
layout_desc
FROM
bpm_page_layout_basic b
where
b.page_id = #{pageIdNum}
<if test="layoutCode!=null">
and b.layout_code LIKE concat('%',
concat(#{layoutCode,jdbcType=VARCHAR}, '%'))
</if>
<if test="layoutDesc!=null">
and b.layout_desc LIKE concat('%',
concat(#{layoutDesc,jdbcType=VARCHAR}, '%'))
</if>
ORDER BY
layout_sequence
3.多语言
- 类上加 @MultiLanguage
- 多语言字段上加 @MultiLanguageField
- 页面上多语言field添加
dto
、dtoId
<a:field name="responsibilityCenterName" required="true" prompt="GLD_RESPONSIBILITY_CENTER.RESPONSIBILITY_CENTER_NAME" dto="com.hand.hec.gld.dto.ResponsibilityCenter" dtoId="responsibilityCenterId"/>
-
页面上多语言选择框如下
<a:tledit id="GLD2030_responsibilityCenterNameTld"/>
4.comboBox
4.1 普通dataset型
- 获取数据
此步骤也可采取设置默认值的方式,由Controller中转
<a:dataSet id="FND2140_gldCoaStructureDs" autoQuery="true" fetchAll="true" baseUrl="/gld-coa-structure" queryUrl="$[/request/@context_path]/gld-coa-structure/query">
<a:fields>
<a:field name="coaStructureCode"/>
<a:field name="coaStructureId"/>
<a:field name="structureFormat"/>
<a:field name="description"/>
</a:fields>
</a:dataSet>
- 映射数据到字段上
<a:field name="coaStructureCode" displayField="coaStructureCode" options="FND2140_gldCoaStructureDs" required="true" valueField="coaStructureId" returnField="coaStructureId" prompt="GLD_COA_STRUCTURE.COA_STRUCTURE_CODE">
<a:mapping>
<a:map from="coaStructureId" to="coaStructureId"/>
<a:map from="coaStructureCode" to="coaStructureCode"/>
<a:map from="description" to="coaDescription"/>
<a:map from="structureFormat" to="coaStructureFormat"/>
</a:mapping>
</a:field>
4.2 SysCode型
- 取数据,注意queryUrl后面有个
/
<a:dataSet id="GLD2030_respCenterTypeCodeDs" autoQuery="true" fetchAll="true" queryUrl="$[/request/@context_path]/common/auroraCode/GLD.RESP_CENTER_TYPE/"/>
- 映射数据到comboBox上
<a:field name="respCenterTypeCode"/>
<a:field name="respCenterTypeCodeDisplay" displayField="meaning" options="GLD2030_respCenterTypeCodeDs" prompt="GLD_RESPONSIBILITY_CENTER.RESP_CENTER_TYPE_CODE_DISPLAY" required="false" valueField="value" returnField="respCenterTypeCode" />
- comboBox编辑器
<a:comboBox id="GLD2030_gridCb"/>
5.条件查询和模糊查询
hap对查询参数进行了限制,所有的条件查询,均需用
@Where
和Criteria
手动声明
- 条件查询:Comparison.EQUAL
- 模糊查询:Comparison.LIKE
- 其他匹配条件请参见
Comparison
枚举类
- 实体类添加
@Where
/**
* 责任中心名称
*/
@Length(max = 500)
@MultiLanguageField
@Where
private String responsibilityCenterName;
- 查询时结合
Criteria
进行查询
@RequestMapping("/query")
@ResponseBody
public ResponseData query(ResponsibilityCenter dto, @RequestParam(defaultValue = DEFAULT_PAGE) int page,
@RequestParam(defaultValue = DEFAULT_PAGE_SIZE) int pageSize, HttpServletRequest request) {
IRequest requestContext = createRequestContext(request);
Criteria criteria = new Criteria(dto);
if(dto.getResponsibilityCenterCode()!=null){
criteria.where(new WhereField(ResponsibilityCenter.FIELD_RESPONSIBILITY_CENTER_CODE, Comparison.LIKE));
}
if(dto.getResponsibilityCenterName()!=null){
criteria.where(new WhereField(ResponsibilityCenter.FIELD_RESPONSIBILITY_CENTER_NAME, Comparison.LIKE));
}
return new ResponseData(service.selectOptions(requestContext,dto,criteria));
}
6.设置默认值
Screen页面上的mode-query是为了准备默认值的,因此迁移时,针对默认值,可以做如下操作以实现相同的功能。
参见部门类型定义ExpOrgUnitTypeController
6.1 中转站
首先跳转到页面之前先经过Controller的方法中转一下。在此Controller方法,将页面上需要的参数(也就是默认值)准备好。
- 功能维护:
解读:
将部门类型定义这个功能的入口页面配置成
exp/org-unit-type/index
,这样在点击功能时,会先跳转到exp/org-unit-type/index
,这样我们可以在这个controller方法中准备好页面上需要的参数,然后再跳转到页面上。
- Controller方法:
@RequestMapping(value = "/index")
public ModelAndView index(HttpServletRequest request, Long accountSetId, Long accountId) {
IRequest requestContext = createRequestContext(request);
List<FndManagingOrganization> fndManagingOrganizationList = fndManagingOrganizationService.magOrgOption(requestContext);
ModelAndView view = new ModelAndView("exp/EXP1012/exp_org_unit_type");
view.addObject("fndManagingOrganizationList", fndManagingOrganizationList);
return view;
}
6.2 页面取值
要想取值,就得先弄懂 AuroraContext,因为值都在AuroraContext中。
(1)AuroraContext的结构
通过在页面上加入xmlns:p="uncertain.proc"
命名空间和<p:echo/>
节点,可以页面加载时,在tomcat控制台输出上下文的内容。
可以看到AuroraContext结构如下:
- context
- cookie
- parameter
- session
- model
- 具体model(在此示例中为 fndManagingOrganizationList)
- records
- record
- record
- records
- 具体model(在此示例中为 fndManagingOrganizationList)
(2)取值
取值就按照AuroraContext的结构取,根节点为/
,取到节点中的值时使用@
符号。于是取值示例如下:
'$[/model/fndManagingOrganizationList/records/record/@magOrgCodeName]'
(3)设置默认值
7.批量分配
以部门类型定义页面 批量分配公司为例:
[外链图片转存失败(img-DmptGygU-1562294483758)(images/1548397772141.png)]
主要步骤:
- 获取所有被选中的记录
- 发送Ajax请求
// 见 exp_org_unit_type_batch_assign_com_all.screen
var datas = $au('EXP1012_batch_assign_all_result_ds').getJsonData(true); //获取所有选中的记录
Aurora.request({ //发送ajax请求
lockMessage: '$[l:hap_waiting]',
url: $au('EXP1110_org_unit_type_asgn_com_batch_assign_all_link').getUrl(),
para: datas,
success: EXP1012_batchAssignAllBackFun,
scope: this
});
//另外可参见:exp_mo_unit_group_relations_bath_assign.screen
8.附件相关
// 1.隐藏 上传附件 按钮
//var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId;
// 2.显示 上传附件 按钮
// var url = $au('upload_image_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId;
// 3.隐藏 上传附件与附件删除 按钮
var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId + '&showDelete=false';
8.1 使用附件实现上传、下载、删除功能
可参见
管理组织定义(
fnd_managing_organizations.screen
) -> 附件上传
目前已实现附件上传、下载、删除功能
要使用附件相关功能,需完成以下3个步骤:
(1)在 screen(fnd_managing_organizations.screen)页面上定义附件上传页面的地址:
<a:link id="upload_image_link" url="$[/request/@context_path]/app/APP2030/app_uploadFile.screen"/>
(2)渲染附件上传按钮
function upload_file_link(value, record, name) {
var magOrgId = record.get('magOrgId');
return '<a href="javascript:uploadFile(' + magOrgId + ')">$[l:prompt.upload_attachment]</a>';
}
<a:column name="uploadFile" align="center" renderer="upload_file_link" width="80"/>
(3)跳转附件上传页面时需要带上两个参数:
tableName
: 本条记录所在表名tablePkValue
: 本条记录对应的主键值showDelete
:是否显示删除按钮,默认为false,不显示删除按钮
function uploadFile(magOrgId) {
var url = $au('upload_image_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId+ '&showDelete=true';
new Aurora.Window({
url: url,
title: '$[l:prompt.upload_attachment]',
id: 'upload_image_screen',
width: 600,
height: 400
});
}
8.2 隐藏附件上传按钮
附件上传按钮的隐藏与显示是通过使用两个页面来实现的:
app_uploadFile.screen
:显示附件上传按钮的页面app_uploadFile_cannotUpload.screen
:不显示附件上传按钮的页面
因此若要隐藏附件上传按钮:
(1)定义附件上传页面的地址:
<a:link id="upload_image_cannotUpload_link" url="$[/request/@context_path]/app/APP2030/app_uploadFile_cannotUpload.screen"/>
(2)构造跳转附件上传页面的url
var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId;
因此若要显示附件上传按钮:
(1)定义附件上传页面的地址:
<a:link id="upload_image_link" url="$[/request/@context_path]/app/APP2030/app_uploadFile.screen"/>
(2)构造跳转附件上传页面的url
var url = $au('upload_image_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId;
8.3 隐藏删除按钮
删除按钮的显示与隐藏是通过传参来控制的
showDelete
:
true
: 显示删除按钮false
或其他值:隐藏删除按钮。- 默认值为false
因此附件上传按钮、删除按钮的显示隐藏组合如下:
(1)隐藏附件上传按钮,隐藏删除按钮
var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId;
(2)隐藏 上传附件按钮,显示 附件删除 按钮
var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId + '&showDelete=true';
(3)显示 上传附件按钮 并 显示 附件删除 按钮
var url = $au('upload_image_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId + '&showDelete=true';
(4)显示 上传附件按钮 并 隐藏 附件删除 按钮
var url = $au('upload_image_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId ;
默认值为false
因此附件上传按钮、删除按钮的显示隐藏组合如下:
(1)隐藏附件上传按钮,隐藏删除按钮
var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId;
(2)隐藏 上传附件按钮,显示 附件删除 按钮
var url = $au('upload_image_cannotUpload_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId + '&showDelete=true';
(3)显示 上传附件按钮 并 显示 附件删除 按钮
var url = $au('upload_image_link').getUrl() + '?tableName=FND_MANAGING_ORGANIZATIONS&tablePkValue=' + magOrgId + '&showDelete=true';