Skip to content
lixia edited this page Jul 6, 2014 · 11 revisions

概要说明

对于一个企业系统,数据查询需求无处不在,作为一个基础框架,一方面需要向用户提供功能强大,交互友好的查询支持可以极大提升用户体验,另一方面又需要考虑开发层面尽量抽取封装以简化开发人员重复的,繁琐的数据组装等代码编写也非常关键,同时还能保证设计实现的规范一致性。

动态参数/分页/排序

框架基本打通前端UI到后端数据访问的动态参数/分页/排序整个环节,基本只需要少量的按照参数名称规则,对于Grid表格数据查询基本不用任何编码,即可实现非常灵活而强大的动态数据访问支持。基本流程是把Web前端传入的按照约定的参数集合转换为结构化查询对象,然后传入业务层组装为基于JPA Criteria语法的Spring JPA Data识别的动态查询对象,最终由Hibernate转换为底层数据库查询SQL语句。

快速过滤查询

组件上方的快速过滤查询区域,可以实现对列数据方便快速的过程查询,点击左侧小三角可以切换过滤条显示控制。 支持常见的三种输入类型:

  • 文本框: 可以快速输入文本、数字等查询条件
  • 下拉框:可以对于一些枚举类型数据以下拉框选取方式快速过滤查询
  • 日期选取组件:日期类型数据快速选取查询

grid-filter-toolbar

除此,点击各查询输入组件左侧的操作符链接即可快速切换查询类型,各列会根据列数据类型或用户定义动态显示可选的查询类型:

grid-filter-oper

分页、排序

  • 分页导航信息和操作区域
  • 排序:支持多字段组合排序,根据点击先后和次数组合多字段排序

grid-filter-pagesort

高级组合查询

点击弹出高级组合查询窗口,在此窗口可实现复杂的嵌套组合条件查询;

grid-filter-advsearch

具体实现详见:core-service/src/main/java/lab/s2jh/core/pagination 目录下面的查询参数封装对象定义,以及BaseService基础服务接口中findByPage相关实现。

分组/聚合/统计

除了基本的增删改查数据访问,对于企业应用系统还有一种很常见的分组聚合统计,例如需要把明细库存信息按照库存地/商品,销售单明细按照商品毛利/订单毛利/销售员利润等不同维度进行分组汇总统计显示。

一般情况此类非常规CURD数据访问,首先会想到采用JQL/HQL或Native SQL形式编写自由而复杂连表风组聚合统计,但是一个此种方式一个比较繁琐的问题就是不便于以简单统一的方式处理动态参数的组装,还有排序分页之类的特性支持,以及返回集合数据的组装。

为了简化此类需求的开发,框架做了一个基于JPA Criteria的进一步封装,开发层面只需Web层简单传入需要分组和聚合规则(典型的加减乘除以及case when和as语法支持等)的属性列表,即可返回与之对应便于用于JSON序列化的Map结构数据,最方便的是即便是聚合属性也能像常规的对象属性查询一样支持动态参数/分页/排序等特性。

库存分组聚合统计

query-sum

  • Controller代码示例:
    @MetaData(value = "按库存地汇总库存量")
   public HttpHeaders findByGroupStorageLocation() {
       setModel(findByGroupAggregate("commodity.id", "commodity.sku", "commodity.barcode", "commodity.title",
               "storageLocation.id", "sum(curStockQuantity)", "sum(salingTotalQuantity)",
               "sum(purchasingTotalQuantity)", "sum(stockThresholdQuantity)", "sum(availableQuantity) as sumAvailableQuantity"));
       return buildDefaultHttpHeaders();
   }

   @MetaData(value = "按商品汇总库存量")
   public HttpHeaders findByGroupCommodity() {
       setModel(findByGroupAggregate("commodity.id", "commodity.sku", "commodity.barcode", "commodity.title",
               "sum(curStockQuantity)", "sum(salingTotalQuantity)", "sum(purchasingTotalQuantity)",
               "sum(stockThresholdQuantity)", "sum(availableQuantity)"));
       return buildDefaultHttpHeaders();
   }
  • JSP/JS(部分)代码示例:
       url : WEB_ROOT + '/biz/stock/commodity-stock!findByGroupStorageLocation',
       colModel : [ {
           label : '商品主键',
           name : 'commodity.id',
           hidden : true,
           hidedlg : true,
           editable : true
       }, {
           label : '商品编码',
           name : 'commodity.sku',
           width : 80,
           align : 'center'
       }, {
           label : '商品名称',
           name : 'commodity.title',
           width : 200,
           align : 'left'
       }, {
           label : '库存地',
           name : 'storageLocation.id',
           width : 150,
           stype : 'select',
           editoptions : {
               updatable : false,
               value : Biz.getStockDatas()
           }
       }, {
           label : '当前库存量',
           name : 'sum(curStockQuantity)',
           width : 60,
           editable : true,
           formatter : 'number'
       }, {
           label : '销售锁定库存',
           name : 'sum(salingTotalQuantity)',
           width : 60,
           editable : true,
           formatter : 'number'
        }    

销售分组聚合统计

query-sum

  • Controller代码示例:
   @MetaData(value = "销售商品毛利统计", comments = "由于可能出现完全赠品类型的0销售额订单,需要引入case when判断处理否则会出现除零错误")
   public HttpHeaders findByGroupCommodity() {
       setModel(findByGroupAggregate("commodity.id", "commodity.sku", "commodity.title",
               "max(case(equal(amount,0),-1,quot(diff(amount,costAmount),amount))) as maxProfitRate",
               "min(case(equal(amount,0),-1,quot(diff(amount,costAmount),amount))) as minProfitRate",
               "sum(diff(amount,costAmount)) as sumProfitAmount", "sum(amount)", "sum(quantity)",
               "case(equal(sum(amount),0),-1,quot(sum(diff(amount,costAmount)),sum(amount))) as avgProfitRate"));
       return buildDefaultHttpHeaders();
   }
  • JSP/JS(部分)代码示例:
url : WEB_ROOT + "/biz/sale/sale-delivery-detail!findByGroupCommodity",
       colModel : [ {
           name : 'commodity.id',
           hidden : true,
           hidedlg : true
       }, {
           label : '商品编码',
           name : 'commodity.sku',
           width : 50,
           align : 'center'
       }, {
           label : '商品名称',
           name : 'commodity.title',
           width : 200,
           align : 'left'
       }, {
           label : '最低毛利率',
           name : 'minProfitRate',
           width : 40,
           formatter : 'percentage'
       }, {
           label : '最高毛利率',
           name : 'maxProfitRate',
           width : 40,
           formatter : 'percentage'
       }, {
           label : '平均毛利率',
           name : 'avgProfitRate',
           width : 50,
           formatter : 'percentage'
       }, {
           label : '总计销售数量',
           name : 'sum(quantity)',
           width : 50,
           formatter : 'number'
       }, {
           label : '总计销售金额',
           name : 'sum(amount)',
           width : 50,
           formatter : 'currency'
       }, {
           label : '总计毛利金额',
           name : 'sumProfitAmount',
           width : 50,
           formatter : 'currency'
       } ] 

主要接口实现定义

    /**
    * 分组聚合统计,常用于类似按账期时间段统计商品销售利润,按会计科目总帐统计等
    * 
    * @param groupFilter 过滤参数对象
    * @param pageable 分页排序参数对象,TODO:目前有个限制未实现总记录数处理,直接返回一个固定大数字
    * @param properties 属性集合,判断规则:属性名称包含"("则标识为聚合属性,其余为分组属性 
    * 属性语法规则:sum = + , diff = - , prod = * , quot = / , case(condition,when,else)
    * 示例:
    *     sum(amount)
    *     sum(diff(amount,costAmount))
    *     min(case(equal(amount,0),-1,quot(diff(amount,costAmount),amount)))
    *     case(equal(sum(amount),0),-1,quot(sum(diff(amount,costAmount)),sum(amount)))
    * @return Map结构的集合分页对象
    */
   public Page<Map<String, Object>> findByGroupAggregate(GroupPropertyFilter groupFilter, Pageable pageable,
           String... properties) 
Clone this wiki locally