2017年3月8日
#
组织树报表中由id与父id来实现组织树报表,若层级数较多时,对每个单元格设置过滤条件和形态会比较繁琐,因此FineReport提供了一种特殊的数据集——树数据集,只需要简单的设置就能自动递归出层级,方便的实现如下图组织树报表:
图一
图二
构建树
新建工作薄,添加数据集ds1取出原始数据,SQL语句为SELECT * FROM 公司部门。
1)根据父字段构建树
使用情形:原始表结构中符合ID、parentID结构,我们可以通过父ID这个字段生成树,添加树数据集,如下图:
2)根据数据长度构建树
使用情形:原始表结构中所有ID都在一列中,且没有父ID字段,但是ID是有规律的,每组的长度相同,且子级的前N位就是父级编号,添加树数据集,如下图:
预览树数据集,可看到已自动生成递归树数据,FR_GEN_0为最高层,依次往下,如下:
纵向组织树编辑
按照下图所示将对应的数据列拖入到单元格中,并将A2单元格的左父格设置为A1,A3单元格的左父格设置为A2:
有上面预览数据可以看到从二层FR_GEN_1开始,就会有空白数据,这是因为数据库中存储的数据有上一级部门本身的部门名称和部门ID,其上一级部门的部门级数会低一级,比如说上述数据的第一行为总部,虽然总部下面有子部门,但是数据库中还是要存储总部这个部门的部门名称和部门ID的,总部对应的级数为一级,那么其对应的数据记录行里面就只有FR_GEN_0层,下面的FR_GEN_1和FR_GEN_2这两层就会没有数据,显示为空白。
在模板制作过程中,从第二层级开始就会有空白数据,需要将空白数据隐藏掉,选中A2和A3单元格,添加条件属性,当数据为空时隐藏该行,如下图:
如果组织结构的层级结构不确定,即有的层级有子层,有的层级没有子层时,其组织树报表的实现方式请查看不规范组织树报表
由于自动生成的字段是编码,可以使用数据字典将其转为对应的部门名称,如下图:
保存模板,点击分页预览,效果如图一。
横向组织树编辑
按照下图所示将对应的数据列拖入到单元格中,在右侧单元格属性表-扩展属性中将B1、C1单元格的扩展方向设为横向,
并将B1单元格的左父格设置为A1,C1单元格的左父格设置为B1:
有上面预览数据可以看到从二层FR_GEN_1开始,就会有空白数据,这是因为数据库中存储的数据有上一级部门本身的部门名称和部门ID,其上一级部门的部门级数会低一级,比如说上述数据的第一列为总部,虽然总部下面有子部门,但是数据库中还是要存储总部这个部门的部门名称和部门ID的,总部对应的级数为一级,那么其对应的数据记录列里面就只有FR_GEN_0层,下面的FR_GEN_1和FR_GEN_2这两层就会没有数据,显示为空白。
在模板制作过程中,从第二层级开始就会有空白数据,需要将空白数据隐藏掉,选中B1和C1单元格,添加条件属性,当数据为空时隐藏该列,如下图:
如果组织结构的层级结构不确定,即有的层级有子层,有的层级没有子层时,其组织树报表的实现方式请查看不规则组织树报表
由于自动生成的字段是编码,可以使用数据字典将其转为对应的部门名称,如下图:
保存模板,点击分页预览,效果如图二。
在企业应用中,通常单个计算机的配置是有限的,而企业应用又是高并发的需求,这个时候会通过计算机集群的方式来提高并发数,从而提高整体应用服务的性能。集群是将多台计算机作为一个整体来提供相关应用的服务。FineBI支持多计算机服务的集群部署,通过集群部署利用有限的计算机资源来有效提高整体应用的并发性能。本文主要介绍整体FineBI集群的思路。
FineBI采用负载均衡集群的模式,将多台服务器创建为一个集群服务器。这里碰到这几个问题:1)web工程的存储问题:FineBI在集群中,由于自身的问题需要多台服务器读取同一个web工程。因此要实现web工程分享。2)系统数据一致性:在FineBI的运行过程中,存在读写的操作,同时有部分的数据的配置文件要写入数据库。需要保证集群的情况下,系统数据的一致性。3)负载均衡:一方面通过负载均衡来处理session的问题,另一方面达成负载均衡的集群环境,使用代理服务器可以将请求转发给集群内部的服务器,可以将负载均衡和代理服务器的高速缓存技术结合在一起,提供有益的性能。4)FS平台集群:如FineBI使用FS平台,则FS平台的各种配置也需要进行集群配置。
如下图是一个FineBI进去的架构的案例示意图,这种方式通过NFS文件共享来处理web工程。
Web工程存储问题
Web工程的存储,我们要解决的是多个服务器保证读取同一个web工程。我们可以通过ceph做到多块物理硬盘组件一块逻辑硬盘,从而实现所有节点都是在访问同一地址;也可以通过linux本身带有的nfs共享文件服务来达成访问同一web工程。无论使用哪一种方式,我们要保证:
<!--[if !supportLists]-->1)<!--[endif]-->访问同一web工程
<!--[if !supportLists]-->2)<!--[endif]-->Cube存储地址是一致的
因为同一个web工程下,要求cube的存储地址是一致的,因此要求cube存储地址一定要一样。
而真正使用的时候,ceph的实现需要至少三台计算机来实现,而实际企业应用中,比较少使用三台;而nfs均可以且是linux本身的,因此使用“nfs”方案。
系统数据配置
单节点的情况下,利用缓存和通过操作系统的文件系统来保存数据的方式,在集群模式下不再合适。主要原因在于数据的一致性问题,多个节点可能进行同时读写,更改系统数据,最终势必会造成整体数据不一致。最好的解决方案是系统配置数据全部交给MySQL等关系型数据库来管理。但由于这样工程量好大,更主要的原因为许多代码缺少维护,贸然更改可能带来意想不到的bug。于是我们采用一种折中的做法。在集群中选出一台几点作为主节点,简称M。其余节点担当子节点,简称S。当S上所有与更改系统配置相关的操作,全部发送到M上进行处理。M负责来更改系统状态,维护整个系统到底一致的状态。S节点放弃全部的缓存数据,读取状态的时候,不再通过读取自身数据,而是通过向M发送读取请求,获得M上的数据。M节点自身可以存在缓存数据。其他数据S节点与M节点时等同的,不存在从属关系。
因此按上述原由我们提供如下解决方案:
<!--[if !supportLists]-->1)<!--[endif]-->mysql数据库:原web工程中存在finedb的配置信息转存到mysql数据库中。因为finedb数据库只能有一个连接,无法多节点同时读取,而mysql数据库则不存在。Logdb也需迁移;
<!--[if !supportLists]-->2)<!--[endif]-->主子节点:我们使用主子节点的方式来配置集群,系统数据的更改均在主节点上进行,子节点只读取主节点上的数据;
<!--[if !supportLists]-->3)<!--[endif]-->Zookeeper:为了保证读写情况下,主子节点保证数据一致性,还需要zookeeper进行通信,充当文件锁的功能。
负载均衡
在FineBI的集群环境中,我们可以使用任何支持负载均衡的服务器来完成轮发的任务,并保证session粘滞。此处我们使用的是nginx反向代理,使用IP标识轮发,保证同一个用户在同一个session。(在一个服务器一个节点的情况下,同一个IP就保证session粘滞)。
FS平台集群
使用FS平台集群插件,将FS平台配置能够满足集群需求。在FS平台集群中,FS平台的所有操作都是发到主节点上来操作;子节点只是作计算服务器。
报表服务器
安装钉钉管理插件后,打开报表管理平台,管理系统下会增加钉钉管理节点,钉钉相关的配置管理都将会放在这个节点中去配置:
同时,设置定时任务的最后一步输出设置中,会增加推送钉钉消息:
钉钉企业应用
管理员登录钉钉企业号,进入微应用设置,需要关注的信息有:
CorpID:是企业在钉钉中的标识,每个企业拥有一个唯一的CorpID;
CorpSecret:是企业每个应用的凭证密钥
登录FineReport管理平台,点击管理系统>钉钉管理节点:将钉钉中的CorpID和CorpSecret分别填到钉钉企业号ID和管理组凭证密码中,设置Token获取路径,可以自定义获取的url,如果不写则采用内置的方式获取,保存,如下图:
钉钉提供的获取token接口本身有缺陷:
1)钉钉提供的接口,使用corpid和secret获取token,默认不会缓存,但是钉钉提倡做token缓存,2小时内可以重复使用,这样减少对钉钉服务器的访问,以免出现问题;
2)钉钉中集成多个应用的话,如果多个应用都会用到corpid和secret获取token,如果某个应用缓存了token,就会冲突
例如:应用a,做了缓存,第一次访问后获取到token1,应用b使用会重新取token2,此时应用a还是用的token就无法访问了。
针对这种情况,处理方式是所有应用统一到一个地方去取token,此时需要设置自定义token的获取地址
在钉钉插件中,钉钉管理>基本信息>设置Token获取路径,可以自定义获取的url,如果不写则采用内置的方式获取
Token获取接口规则:返回json类型的数据,{access_token:"xxxxx", jsapi_ticket:"xxxxx"}
钉钉管理后台创建FineReport报表微应用时,例如http://www.finereporthelp.com:8181/app2/ReportServer?op=fs&corpid=$CORPID$,后面要加上参数&corpid=$CORPID$,这样后台会获取到cropid,然后取到钉钉userid,做单点登录;
按照钉钉提供的方法,读取钉钉通讯录的成员,需要的字段有uesrid,name,department进入FineReport报表管理平台fs,点击钉钉管理节点,除了基本信息外,增加了钉钉成员管理,点击效果如下:
表格中会自动读取钉钉企业号通讯录中的所有成员,并且在每次打开该页面时刷新为最新的;默认钉钉成员与报表用户相同,因为大部分情况下,钉钉成员名和报表用户名是统一的;
如果您的钉钉成员名与报表用户名是不一样的,此时,可以取消勾选钉钉成员与报表用户相同设置,此时报表用户名,可以将钉钉成员与报表用户进行关联,如下图:
钉钉提供免登服务,与微信类似,通过code可以获取当前取号的userid,获取到userid后,通过关联关系自动登录报表后台
开发服务器可以主动的发送消息给企业成员,比如使用FineReport定时器生成报表后,发送消息给相应的人员进行查看。进入FineReport管理平台,添加定时任务:
到输出设置这步,比如我们将定时生成的结果挂在其他这个目录下面,并且命名为钉钉文档测试:
通知与存档选择推送钉钉消息,企业应用(AgentID)需要根据钉钉管理里的进行填写,例如下设置,这样定时任务结束后,这个钉钉成员可以收到消息
定时生成结果的文件名:对应发送消息的标题名
企业应用AgentID:发消息至哪个应用
钉钉用户:发消息给哪些成员,下拉框中会自动读取钉钉通讯录中的所有成员,多个成员之间用|分割,比如Jane|Saber
部门ID:发消息给某个部门的所有成员,该属性与钉钉用户是并的关系,不同部门之间也用|分割
消息内容:定义消息的正文内容
定时结果访问连接:勾选的话会在消息正文最后加上定时生成的结果连接,点击后就可以直接打开定时结果;不选的话则只发送纯文本消息。
定时任务设置好后,比如任务每天都会执行,每次执行后就会推送消息给对应的成员,效果如下:
点击连接,就可以看到定时生成的结果。
当报表中列出数据太多时,想通过显示按钮隐藏明细数据只显示统计数据。如下图示例,那么该如何实现呢?本文以FineReport为例,来讲述JS如何实现点击参数面板按钮显示或隐藏数据。
打开报表
在参数面板添加一个标签控件,控件名为lable,设置标签控件不可见,控件值为“显示”。
在参数面板添加一个按钮控件,控件名为button,控件值为“只显示合计数据”,并添加点击事件。
编辑点击事件,添加下面的JavaScript代码:
/*获取隐藏的标签控件的值*/
var label= this.options.form.getWidgetByName("label").getValue();
/*判断标签控件的值*/
if(label=='显示')
{
/*当标签控件的值为显示时,则改为隐藏,并修改按钮名称为显示所有数据*/
this.options.form.getWidgetByName("label").setValue("隐藏");
this.options.form.getWidgetByName("button").setValue("显示所有数据");
}
else
{
/*当标签控件的值不为显示时,则改为显示,并修改按钮名称为只显示合计数据*/
this.options.form.getWidgetByName("label").setValue("显示");
this.options.form.getWidgetByName("button").setValue("只显示合计数据");
}
/*执行查询*/
_g().parameterCommit();
点击参数面板空白处,将“点击查询前不显示报表内容”属性的勾去掉。
回到报表设计界面,右键B3单元格,添加条件属性,设置行高为0毫米,添加公式条件为$label = '隐藏'。
保存模板,点击分页预览即可看到上图的效果。