V6.4.1.0

发布清单

标识 主题 问题类型 报告人 需重新生成代码 需手工修改配置文件 项目归属 演示
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.可是化中配置控件类型 及控件的必须属性

  • 控件属性 rows
  • 查询数据源标识名称(在控件查询时候,会把这个参数传到后台,后台根据这个来区分查询数据源)
  • QQ图片20200525100844

    QQ图片20200525100905

    <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.配置

  • 在config.properties 中配置全局的 数据源查询请求及 返回数据笔数限制
  • //在 controls 节点下,新增 exactMatching
     controls : {
       exactMatching: {
          //全局的查询数据请求路径
          queryUrl: '/search/store/queryMatching',
          checkboxLabel: '精确匹配',
          //最大返回数据笔数限制
          limitCount: 100
      }
    }
    

    3.后台请求URL,可以采用平台的全文搜索引擎;也可以自定义后台请求实现

  • 全文搜索引擎的精确查询URL – /search/store/queryMatching
  • 使用全文搜索引擎,需要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();
           });
       };
    

    云平台数据权限支持多系统表名称重复说明

  • 由于项目历史原因,导致分布式系统中可能存在不同的子系统存在相同名称的表,这时候数据权限控制并不能分别配置,因为数据权限是按表名称来区分的。
  • 云平台在SYS_ACL_TABLE 表结构上新增了 SYSTEM_CODE字段用来区分表所在的子系统;在SYS_DAC_TABLE 表中新增 SYSTEM_CODE用来区分配置的数据权限是配置在哪个子系统编码哪个表上。
  • 后台在启动时候,会根据应用上下文路径来区分比如 xy_oms_business 、xy_tms_business、xy_common_business等,平台再导出SYS_ACL_TABLE表数据时候,会根据视图上的应用上下文路径/xy_oms_business去掉/ 当成表所属的系统编码。
  • 功能所需JAR支持、功能修改范围

    1、表新增字段;SYS_ACL_TABLE SYS_DAC_TABLE 新增字段

    2、需要修改一下【数据归集管理】页面HTML等

  • sysDacManage.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>
    
  • 在sysDacManageCtrl.js 新增扩展方法
  • /**
    * 表名称搜索帮助 选中事件,回填相关属性
    * @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等

  • sysAclTableManage.html
  • 表格列中添加【systemCode】字段
  • g-hot-column  cell-align="left" width="250" data="systemCode"
    title="nls.systemCode" sortable  >
    </g-hot-column>
    
  • 系统管理www目录下nls/system.js nls/en/system.js 加入字段多语言文本信息 “systemCode”:”系统编码”
  • 4、修改搜索帮助配置文件[IPF_DML_TABLEShlpSetting.js]

    // columns中新增
    {"width": "100px", "displayExpress": "systemCode", "text": "系统编码"}
    

    5、 如果是重名的表,需要重新导出脚本,即 表SYS_ACL_TABLE中需要带 SYSTEM_CODE值区分数据

    6、由于象屿项目比较特殊部分代码在项目中,以下需要手工合并修改下

  • SysDacTable.java 新增字段
  • /**
      * 系统代码
      */
      @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;
     }
    
  • SysDacTableMapper.xml 新增字段 SYSTEM_CODE相关
  • 7、 修改唯一性检验配置,加入systemCode

  • SysDacTable_Default.js 中 修改 唯一性检验配置 ,加入字段 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"]
     }
    
  • SysAclTable_default.js 中修改唯一性检验配置,加入字段 systemCode
  • 提供参考文件,注意象屿的类包路径 xy 平台的应该是放在 platform.sys.dac下。
    

    8、本地缓存刷新机制

    因为在数据权限列表,按各子系统的系统编码(上下文路径)区分,计算完以后会缓存本地guava缓存中,需要处理本地缓存的刷新机制。所以在认证中心中加入本地缓存的redis广播刷新机制。
    com.gillion.system.localcache.LocalCacheDestoryer.java 在认证中心api包中ttt_authentication_api,这个api包,其他所有的业务系统都引用。在其他的LoginController 登出中加入清除数据权限缓存的广播通知。
    另也可以在界面上新增一个按钮,用来刷新数据权限缓存。

  • LocalCacheDestoryer.java 订阅缓存刷新通知
  • /*
    * @(#)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、 需要更新以下前端文件

  • utils.js static/app/service/utils.js
  • GillionMsgModule.js static/app/framework/msg/GillionMsgModule
  • //861行新增
                //TODO 2020-05-11 Add UrL上处理静态资源版本号
                if(window.addVersionParams){
                  if(opts.url){
                    opts.url = window.addVersionParams(opts.url);
                    console.log("页签组件自动处理静态资源版本号.",
    opts.url);
                 }
               }
    
  • GillionTabModule.js static/app/framework/tab/GillionTabModule
  • //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);
             }
           }