发布清单
标识 | 主题 | 问题类型 | 报告人 | 需重新生成代码 | 需手工修改配置文件 | 项目归属 | 演示 |
GL-19750 | 【云平台】代码生成加入异步生成日志保存功能 | 新功能 | 林进旭 | 云平台 | |||
GL-19737 | 【多行文本精确匹配控件】:支持ES查询,改造点见描述部分。 | 新功能 | 甘惠羡 | 物流-象屿综合物流管理平台项目 | |||
GL-19740 | 【云平台】批量插入接口性能优化 | 新功能 | 林进旭 | 物流-象屿综合物流管理平台项目 | |||
GL-19715 | 【云平台】分布式系统数据权限支持重复表名 | 新功能 | 林进旭 | 云平台 | |||
GL-19749 | 平台所有页面上使用websocket采用URL配置方式,后续外网加权限需要使用,目前xywl内网环境已经使用上,需要测试下所有有进度 | 改进 | 林进旭 | 云平台 | |||
GL-19670 | 【云平台】有配置自定义SQL去外层条件:自定义sql如果有orderby,查询条件会加在order by 后面 | 改进 | 甘惠羡 | 云平台 | |||
GL-19717 | 【多语言】:如果字段或者其他显示名称没有翻译成英文,建议默认取中文显示,不要显示成:XXX 没有未配置 | 改进 | 甘惠羡 | 云平台 | |||
GL-19716 | 【云平台】页签懒加载、DIV延迟加载等支持静态资源版本 | 改进 | 林进旭 | 物流-象屿综合物流管理平台项目 | |||
升级文档
平台多行文本精确查询使用说明
平台新增一种控件类型 【多行精确查询】控件,这种控件为了提高业务作业上的全匹配查询的效率,采用的通过ES搜索引擎中进行全匹配查询,然后把查询到的列表做为数据库精确查询的条件。
备注:
需要更新前端文件 framework/exactMatching/exactMatchingModule
1.可是化中配置控件类型 及控件的必须属性
<g-exact-matching
id="search.itemCode"
name="search.itemCode"
ng-model="search.itemCode"
rows="3"
limit-count="10"
query-name="BasItem"
filed-name="itemCode"
matching-query-url="/aaaa/bbb">
<!--
matching-query-url 请求URL默认使用全局的即可,如果有特殊的需要指定,
可以在可视化上配置指定;原则上是控件上有指定,使用控件上的,如果没指定,
使用全局的(config.properties.js中配置的)
-->
</g-exact-matching>
配置字段说明:
1.查询名称:全文搜索引擎Search.war包应用中的数据库中存在的业务对象名称。
2.字段名称:业务对象中的属性名称 (当前用来精确查询数据源查询与返回的字段名称)
eg:
select itemCode from BAS_ITEM where itemCode like '%AAA%'
2.配置
//在 controls 节点下,新增 exactMatching
controls : {
exactMatching: {
//全局的查询数据请求路径
queryUrl: '/search/store/queryMatching',
checkboxLabel: '精确匹配',
//最大返回数据笔数限制
limitCount: 100
}
}
3.后台请求URL,可以采用平台的全文搜索引擎;也可以自定义后台请求实现
使用全文搜索引擎,需要nginx上配置 , 如下:
location ^~ /search {
proxy_pass http://localhost:8083/search;
break;
}
以下是自定义后台实现的:
目前例子是使用后台的数据库查询,最终项目组可以实现为 ES搜索引擎的查询方式。
/**
* 查询例子分页信息查询
*
* @param
* @return
*/
@RequestMapping(value = "testQuerySample/queryItemCods")
public void queryItemCods(@RequestBody MatchingVo matchingVo ,
HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> result = Maps.newHashMap();
//模糊查询关键字列表
List<String> itemCodes = matchingVo.getValues();
//模糊查询的目标数据源标识名称
String name = matchingVo.getName();
//查询返回的数据笔数限制
int limitCOunt = matchingVo.getLimitCount();
if(CollectionUtils.isNotEmpty(itemCodes)) {
String strSql = "select ITEM_CODE from BAS_ITEM where ITEM_CODE like
";
StringBuilder sql = new StringBuilder();
Map<String,Object> params = Maps.newHashMap();
for(int i=0;i<=itemCodes.size()-1;i++){
if(i>0){
sql.append(" union all ");
}
String itemCode = itemCodes.get(i);
String varName = "itemCode"+i;
sql.append(strSql).append(" :"+varName);
params.put(varName, "%".concat(itemCode).concat("%"));
}
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
SpringContextHolder.getBean(NamedParameterJdbcTemplate.class);
List<String> codes =
namedParameterJdbcTemplate.queryForList(sql.toString(), params, String.class);
result.put("records", codes);
}
ResponseUtils.flushSuccess(response, result);
}
4.需要在扩展JS中加入代码扩展查询
把原来的JS中的查询方法存储在临时的变量中,然后重写覆盖掉 queryTestQuerySample参数 search.itemCode 是 配置了 多行文本精确查询的 控件上的 ID值。
注意需要引入
//在ctrl.js中需要定义 var ExactMatchingService =
injector.get("ExactMatchingService");
injector = angular.element(document).injector();
var ExactMatchingService = injector.get("ExactMatchingService");
var $scope = this.scope;
var superQueryTestQuerySample = $scope.queryTestQuerySample;
$scope.queryTestQuerySample = function () {
return ExactMatchingService.doExactMatching(['search.itemCode'])
.then(function() {
superQueryTestQuerySample();
});
};
// 如果界面上存在多个 【精确查询】控件则扩展JS 如下
$scope.queryTestQuerySample = function () {
return
ExactMatchingService.doExactMatching(['search.itemCode','search.itemName'])
.then(function() {
superQueryTestQuerySample();
});
};
云平台数据权限支持多系统表名称重复说明
功能所需JAR支持、功能修改范围
1、表新增字段;SYS_ACL_TABLE SYS_DAC_TABLE 新增字段
2、需要修改一下【数据归集管理】页面HTML等
<g-hot-column title='系统编码' has-tip="true" align="left" width="150"
data="systemCode" sortable>
<g-hot-column-editor>
<input g-focus-select class="input form-control" ng-
model="row.systemCode" type="text" name="sysDacTable.systemCode"
id="sysDacTable.systemCode" g-trim g-dbc/>
</g-hot-column-editor>
</g-hot-column>
/**
* 表名称搜索帮助 选中事件,回填相关属性
* @param list
* @param item
* @param spilt
*/
$scope.tableNameSysDacTableGchangeRow = function (list, item, spilt) {
angular.element(list.element).scope().row["tableName"] = item ?
item["tableName"] : undefined;
angular.element(list.element).scope().row["tableDesc"] = item ?
item["tableDesc"] : undefined;
angular.element(list.element).scope().row["ipfDmlTableId"] = item ?
item["sysAclTableId"] : undefined;
angular.element(list.element).scope().row["systemCode"] = item ?
item["systemCode"] : undefined;
};
3、需要修改一下【数据权限】页面HTML等
g-hot-column cell-align="left" width="250" data="systemCode"
title="nls.systemCode" sortable >
</g-hot-column>
4、修改搜索帮助配置文件[IPF_DML_TABLEShlpSetting.js]
// columns中新增
{"width": "100px", "displayExpress": "systemCode", "text": "系统编码"}
5、 如果是重名的表,需要重新导出脚本,即 表SYS_ACL_TABLE中需要带 SYSTEM_CODE值区分数据
6、由于象屿项目比较特殊部分代码在项目中,以下需要手工合并修改下
/**
* 系统代码
*/
@Column(name = "SYSTEM_CODE", nullable = true, length = 50)
@RichLength(max = 50, min = 0, groups = {Default.class})
private String systemCode;
public String getSystemCode() {
this.systemCode = systemCode;
}
7、 修改唯一性检验配置,加入systemCode
{
async: true,
pkProperty: "sysDacTable.sysDacTableId",
ruleName: "unique",
message: "Unique.sysDacTable.tableName",
entity: "com.gillion.platform.sys.dac.domain.SysDacTable",
properties: ["sysDacTable.tableName", "sysDacTable.columnName",
"sysDacTable.systemCode", "sysDacTable.sysDacId"]
}
提供参考文件,注意象屿的类包路径 xy 平台的应该是放在 platform.sys.dac下。
8、本地缓存刷新机制
因为在数据权限列表,按各子系统的系统编码(上下文路径)区分,计算完以后会缓存本地guava缓存中,需要处理本地缓存的刷新机制。所以在认证中心中加入本地缓存的redis广播刷新机制。
com.gillion.system.localcache.LocalCacheDestoryer.java 在认证中心api包中ttt_authentication_api,这个api包,其他所有的业务系统都引用。在其他的LoginController 登出中加入清除数据权限缓存的广播通知。
另也可以在界面上新增一个按钮,用来刷新数据权限缓存。
/*
* @(#)LocalCacheDestoryer.java
* 版权声明 厦门吉联科技, 版权所有 违者必究
*
*修订记录:
*1)更改者:林进旭
* 时 间:2020/5/9 17:58
* 描 述:创建
*/
package com.gillion.system.localcache;
import com.gillion.ec.core.utils.ContextHolder;
import com.gillion.eds.extend.redis.RedisInterface;
import com.gillion.platform.system.service.PermissionLocalCacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPubSub;
/**
* <pre>
* The Class LocalCacheDestoryer
* </pre>
*
* <br>
* JDK版本:1.6
*
* @author 林进旭
* @version 1.0
* @see InitializingBean
* @since 1.0
*/
@Component
public class LocalCacheDestoryer implements InitializingBean, DisposableBean {
public static final String LOCAL_CACHE_DELETE_CHANNEL =
"LOCAL_CACHE_DELETE_CHANNEL";
private Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private RedisInterface redisInterface;
/**
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
*
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if
initialization fails.
*/
@Override
public void afterPropertiesSet() throws Exception {
new Thread() {
@Override
public void run() {
log.info("用户数据权限本地缓存刷新线程运行中.....");
redisInterface.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
try{
super.onMessage(channel, message);
processMessage(message);
} catch (Exception ex) {
} finally {
}
}
}, LOCAL_CACHE_DELETE_CHANNEL);
}
}.start();
}
private void processMessage(String message){
log.debug("################ 清理本地缓存 message:{}", message);
PermissionLocalCacheService permissionLocalCacheService =
ContextHolder.getBean(PermissionLocalCacheService.class);
if(null!=permissionLocalCacheService) {
permissionLocalCacheService.clearCache(message);
}
}
/**
* Invoked by a BeanFactory on destruction of a singleton.
*
* @throws Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
@Override
public void destroy() throws Exception {
}
}
认证中心的LoginController.java
@RequestMapping("/logout")
public void logout(HttpServletRequest request, HttpServletResponse response,
@CookieValue(value = "access_token",required = false) String accessToken) {
//linjx 2020-05-09 add 用户登出时候删除数据权限本地缓存
SysUser user = credentialProcessor.parseCredential(request);
redisInterface.publish(LocalCacheDestoryer.LOCAL_CACHE_DELETE_CHANNEL,
user.getUserCookieKey());
credentialProcessor.destroy(response,accessToken);
}
认证中心的RefreshCacheController.java
分别在这个类的2个刷新缓存方法中加入数据权限缓存的广播
//linjx 2020-05-09 add
redisInterface.publish(LocalCacheDestoryer.LOCAL_CACHE_DELETE_CHANNEL,
user.getUserCookieKey());
eg:
@RequestMapping("/refreshSysUserInfoCache")
public CommonResult refreshSysUserInfoCache(HttpServletRequest request)
throws ExecutionException {
SysUser user = credentialProcessor.parseCredential(request);
String redisKey = RedisMQConstant.USER_INFO_KEY_PRE + user.getUserId() +
"_" + user.getAppCode();
redisInterface.del(redisKey);
//linjx 2020-05-09 add
redisInterface.publish(LocalCacheDestoryer.LOCAL_CACHE_DELETE_CHANNEL,
user.getUserCookieKey());
SecurityConfig appConfig = securityConfig.getConfig(user.getAppCode());
String findInfoByAppCodeAndUserIdService =
appConfig.getFindInfoByAppCodeAndUserIdService();
UserInfo userInfo =
ConsumerInvoker.invoke(findInfoByAppCodeAndUserIdService,
UserInfo.class,
user.getAppCode(), user.getUserId()
);
redisInterface.set(redisKey, EntityUtils.toJSONString(userInfo));
return CommonResult.success();
}
平台页签、弹出框静态资源版本优化
HTML中存在页签、页签懒加载、页签DIV延迟加载的,URL不会带上静态资源版本号改进。
1、 需要更新以下前端文件
//861行新增
//TODO 2020-05-11 Add UrL上处理静态资源版本号
if(window.addVersionParams){
if(opts.url){
opts.url = window.addVersionParams(opts.url);
console.log("页签组件自动处理静态资源版本号.",
opts.url);
}
}
//861 行新增
//TODO 2020-05-11 Add UrL上处理静态资源版本号
if(window.addVersionParams){
if(opt.url){
opt.url = window.addVersionParams(opt.url);
console.log("页签组件自动处理静态资源版本号.", opt.url);
}
if(opt.lazyUrl){
opt.lazyUrl = window.addVersionParams(opt.lazyUrl);
console.log("页签组件自动处理静态资源版本号.", opt.lazyUrl);
}
}