1. 描述编辑
1.1 版本
报表服务器版本 | JAR 包版本 | 插件版本 |
---|---|---|
11.0 | 2020-12-30 | V2.5 |
1.3 功能介绍
插件提供了额外的取数接口,将 FineReport 的数据集逻辑再次切割为两个部分:
装载器:从数据来源装载数据。
解析器:将装载到的数据组装成 FineReport 支持的数据模型。
插件内置了两个装载器和一个解析器,分别如下:
1)装载器:
参数装载器:数据通过参数直接传入。( 比如 JSON、XML 等结构化数据 )
网络数据装载器:通过配置相关请求,从 Web 端装载数据。( 比如 JSON 服务等 )
2)解析器:
JSON 解析器:对结构化的 JSON 文本进行解析,生成 FineReport 支持的数据模型。
注:只能解析结构型描述的 JSON,业务型描述的 JSON 无法解析,业务型 JSON 解析依赖具体的业务的描述,需要单独实现解析器。
JS解析器:通过JS脚本来解析文本,生成数据模型
注:对于较大数据量时要谨慎使用
原始解析器:原样返回数据,供调试用。
1.2 应用场景
对 FineReport 的程序数据集接口进行改善和封装。
支撑企业IT轻松进行混合数据集成,为restful等数据结构提供高可用的数据处理服务,具有高度扩展性,通过二次开发来适应更复杂的场景。
注:使用该插件需要具备最基础的 FineReport 插件开发能力。
2. 插件介绍编辑
基础使用篇编辑
使用主面板可以应对绝大多数的场景
信息流程
参数处理-装载器-解析器
基础面板包括装载方式,解析方式,参数面板三个部分。
语法:同数据库查询 ${}可包裹参数或公式,右侧参数面板可自动识别参数,参数支持公式,文本等多种形式。支持从报表中读取参数。
使用示例
内置装载器
HTTP装载器
HTTP装载器内容是一个JSON,里面4个字段,分别是url:请求地址 ,type:请求类型 可选GET,POST,JSON,charset:编码,header:请求头,以json键值对形式写入,还有一个JSON模式专有的body字段。
{ url: "" , type: "GET" , charset: "UTF-8" , header:{} } |
参数栏里面的参数会在后台被分为两类,第一类是装载器界面出现过的参数(${参数}),第二类则是未出现过的。出现过的会解析到装载器配置的字符串里,而剩下的则作为请求的参数。
GET示例
{ url: "www.114.com" , type: "GET" , charset: "UTF-8" , header:{ aa:${bb} } } 参数 值 bb p_BB cc p_CC 实际请求为 url:www. 114 .com?cc=p_CC header:{aa:p_BB} |
对于GET,额外参数会作为query参数拼接到url后
POST和GET类似,不同之处就是参数在请求体里
{ url: "www.114.com" , type: "POST" , charset: "UTF-8" , header:{ aa:${bb} } } 参数 值 bb p_BB cc CC 实际请求为 url:www. 114 .com header:{aa:p_BB} body为x-www-form-urlencoded模式 内容为cc=p_CC |
JSON 实际上是post raw模式,第二类参数会被装载成JSON的格式作为请求体发送。
body有两种装载方式,一种是类似用post get的
{ url: "www.114.com" , type: "JSON" , charset: "UTF-8" , header:{ aa:${bb} } } 参数 值 bb p_BB cc p_CC 实际请求为 url:www. 114 .com type:post header。 里面会加上content-type: json header:{aa:p_BB} body为 { cc:p_CC } |
第二种是直接写body键
{ url: "www.514.com" , type: "JSON" , charset: "UTF-8" , header:{ aa:${bb} }, body:{ cc:${dd} } } 参数 值 bb p_BB dd p_DD 实际请求为 url:www. 514 .com type:post。 header 里面会加上content-type: json header:{aa:p_BB} body为 { cc:p_DD } |
单参数装载器
单参数的逻辑就比较简单了,将面板的数据参数计算后原样传给解析器。主要是调试用
${data}${today()} 参数 值 data myData 计算结果为myData今天的日期 |
内置解析器
JSON解析器
提供了不完整的JPATH语法
{ dataPath: "root" , showmap: "path1,value1,path2,value2" } |
root.key1.key2.arr[1] 就表示定位到{ key1 : { key2 : { arr:[ {},{<这里>} ] } } },只解析定位路径下的json内容
如果是默认root则就是处理全部数据
showmap提供列名映射将原始的列名(path1)转换为自定义的列名(value1)
JS解析器
参照JSON数据集插件中JS语法,$data变量为接受到的原始数据,最终需要通过JS生成如下一个数据表对象
返回的对象样例,column为列名,content为二维数组,保存数据集单元格的值 { "column" :[ "col1" , "col2" , "col3" ], "content" :[ [ 1 , 2 , 3 ], [ 4 , 5 , 6 ] ] } |
示例:具体代码请按照实际场景开发
js解析器性能比较低,数据量大的时候要慎用
原始数据
类比单参数装载器 返回一个只有一个单元格的数据集,内容是从装载器接受到的数据,参数面板无意义,
实际案例
获取帆软市场的插件列表
装载方式 HTTP装载器
{ type: "GET" , charset: "UTF-8" , header:{} } |
解析方式 JSON解析器
{ dataPath: "root.result" , showmap: "path1,value1,path2,value2" , } |
具体的装载器逻辑可以单独客制,所以并没有什么统一的写法规范
缓存编辑
数据工厂配置界面的缓存是指数据集的缓存,有缓存的话,不走接口取数,直接从缓存里面取数。
注2:缓存有 3 个选项:无、磁盘、内存。选择无表示不启用缓存功能,选择磁盘表示缓存到本地磁盘,选择内存表示缓存到内存。
进阶使用篇编辑
进阶使用需要有一定的插件开发能力
加入使用者认为的装载方式不足以满足使用场景,又不想重新开发一个装载器/解析器时,可以自定义辅助步骤
高级面板的作用是用来添加装载前,装载后,解析后事件作为装载方式,解析方式的补足,支持多个同步骤事件的添加。
最上层下拉框可以选择 为该数据集单独配置,使用模板配置,使用服务器全局配置。
使用单独配置时下方会出现列表,支持事件的增删改。
选择全局设置/模板设置时不显示列表面板,具体设置项打开全局设置/模板设置进行编辑
各个事件的作用
目前各个事件并没有实装具体的应用,仅提供了接入框架,高级步骤是装载-解析模式的增强和补全,结合开发篇来使用。
装载前事件
提供额外的参数引入 比如说token获取的逻辑可以写在自定义装载前事件里。多个装载前事件参数可以累积,重复的参数前者会被后者覆盖。
在主面板使用,使用方法同常规参数调用,右侧参数栏内默认值无效。
场景示例: 获取token,可以开发一个token获取的装载前事件,将获取的token作为参数引入主逻辑中
装载后事件
提供装载器获取的原始数据的整理 通常装载器返回的是一个字符串类型,可以在这个阶段进行额外的整理行为,多个事件从上到下顺序执行。
场景示例:返回的数据虽然是json但不适合直接用json解析器,用js解析器性能又不够好的时候,可以定制一个装载后事件,把结构格式化为json解析器能够识别的样式
解析后事件
提供解析器返回的数据集的进一步处理 比如增删列,修改列名,重排序,条件过滤等,可以在这个阶段进行额外的整理行为,多个事件从上到下顺序执行
场景示例: 排序,筛选,取前n项,取奇数项,调整数据格式等等自由操作数据集内容。
警告: 使用子插件时升级,需要先禁用子插件,或升级后重启服务器编辑
接口介绍编辑
装载前事件 preprocessor
接口描述
package com.tptj.plugin.hg.fun; import com.fr.script.Calculator; import com.fr.stable.ParameterProvider; /** * 预处理阶段 */ public interface Preprocessor extends Configuration { String XML_TAG = "TableDataPreprocessor" ; int CURRENT_LEVEL = 1 ; /** * 预处理 可以改变参数 * @param calculator 算子 * @param config 面板配置内容 * @return 注入的参数 * @throws Exception */ ParameterProvider[] process(Calculator calculator, String config) throws Exception; } |
装载后事件 formatter
接口描述
package com.tptj.plugin.hg.fun; public interface Formatter extends Configuration{ String XML_TAG = "TableDataFormatter" ; int CURRENT_LEVEL = 1 ; /** * 数据格式化 * @param data 装载器返回的数据 * @param config 配置 * @return 格式化后的数据 * @throws Exception */ Object format(Object data, String config) throws Exception; } |
解析后事件 filter
接口描述
package com.tptj.plugin.hg.fun; import com.tptj.plugin.hg.stable.SimpleDataModel; /** * */ public interface Filter extends Configuration { String XML_TAG = "TableDataFilter" ; int CURRENT_LEVEL = 1 ; /** * 整理数据 * @param dataModel 数据模型 * @param config 配置 * @return 整理后的数据 * @throws Exception */ SimpleDataModel doAction(SimpleDataModel dataModel, String config) throws Exception; } |
装载事件 loader
接口描述
package com.tptj.plugin.hg.fun; import com.fr.script.Calculator; import com.fr.stable.ParameterProvider; import com.fr.stable.fun.mark.Mutable; public interface Loader extends Mutable { String XML_TAG = "TableDataLoader" ; int CURRENT_LEVEL = 1 ; /** * 装载数据 * @param cal 当前算子 * @param params 需要用到的参数 * @param others 其他你可能用到的但是又不希望通过参数控制的配置项,即装载器面板内容 * @return 保存数据的对象 */ Object load( Calculator cal, ParameterProvider[] params, String others); /** * 装载器的名字【显示在数据工厂配置界面的下拉列表里面的】,唯一,支持国际化的key * @return */ String getName(); /** * * @return 默认显示的配置【显示在数据工厂配置界面装载器配置文本域里面的,配置项自定义,其实就是放一些,不想写死在代码里,又不希望被参数篡改的部分】 */ String getDefaultConfig(); } |
解析事件 resolver
接口描述
package com.tptj.plugin.hg.fun; import com.fr.stable.fun.mark.Mutable; import com.tptj.plugin.hg.stable.SimpleDataModel; public interface Resolver extends Mutable { String XML_TAG = "TableDataResolver" ; int CURRENT_LEVEL = 1 ; /** * 解析数据 * @param data 从装载器得到的原始数据 * @param others 解析时会使用,但是不希望是参数的配置,即解析器面板内容 * @return 解析出来的二维数据模型 */ SimpleDataModel parse(Object data, String others ); /** * 解析器的名字【显示在数据工厂配置界面的下拉列表里面的】,唯一,支持国际化的key * @return */ String getName(); /** * * @return 默认显示的配置【显示在数据工厂配置界面解析器配置文本域里面的,配置项自定义,其实就是放一些,不想写死在代码里,又不希望被参数篡改的部分】 */ String getDefaultConfig(); } |
功能点注册编辑
<extra-core> <TableDataLoader class = "com.fr.plugin.tptj.tabledata.factory.demo.DemoLoader" /> <TableDataResolver class = "com.fr.plugin.tptj.tabledata.factory.demo.DemoResolver" /> <TableDataPreprocessor class = "com.fr.plugin.tptj.tabledata.factory.demo.DemoPreprocessor" /> <TableDataFormatter class = "com.fr.plugin.tptj.tabledata.factory.demo.DemoFormatter" /> <TableDataFilter class = "com.fr.plugin.tptj.tabledata.factory.demo.DemoFilter" /> </extra-core> <dependence> <Item key= "com.tptj.plugin.hg.tabledata.factory.v10" type= "plugin" /> <!-- 依赖于数据工厂主框架 --> </dependence> |
我们通常继承抽象类来开发
package com.fr.plugin.tptj.tabledata.factory.demo; import com.fr.base.Parameter; import com.fr.json.JSONObject; import com.fr.script.Calculator; import com.fr.stable.ParameterProvider; import com.tptj.plugin.hg.impl.AbstractPreprocessor; public class DemoPreprocessor extends AbstractPreprocessor { @Override public ParameterProvider[] process(Calculator calculator, String s) { JSONObject jo = new JSONObject(s); ParameterProvider[] results = new ParameterProvider[ 1 ]; results[ 0 ] = new Parameter( "token" , jo.get( "token" )); //jo.get("token"); return results; } @Override public String getName() { return "Demo_PreProcess" ; } @Override public String getDefaultConfig() { return "{\n" + " token:token\n" + "}" ; } } |
package com.fr.plugin.tptj.tabledata.factory.demo; import com.tptj.plugin.hg.impl.AbstractFormatter; public class DemoFormatter extends AbstractFormatter { @Override public Object format(Object o, String s) { String data = (String) o; String[] lines = data.split( "\n" ); StringBuilder result = new StringBuilder(); for ( int i = 1 ; i < lines.length; i++) { result.append(lines[i]).append( "\n" ); } return result.toString(); } @Override public String getName() { return "Demo_Formatter" ; } @Override public String getDefaultConfig() { return "remove first line" ; } } |
package com.fr.plugin.tptj.tabledata.factory.demo; import com.fr.general.data.TableDataException; import com.fr.log.FineLoggerFactory; import com.tptj.plugin.hg.impl.AbstractFilter; import com.tptj.plugin.hg.stable.SimpleDataModel; import java.util.ArrayList; import java.util.List; public class DemoFilter extends AbstractFilter { @Override public SimpleDataModel doAction(SimpleDataModel simpleDataModel, String s) { try { simpleDataModel.removeColumn(simpleDataModel.getColumnCount() - 1 ); List<Object[]> newData = new ArrayList<Object[]>(); for (Object[] row : simpleDataModel.getDatas()) { Object[] newRow = new Object[row.length - 1 ]; System.arraycopy(row, 0 , newRow, 0 , row.length - 1 ); newData.add(newRow); } simpleDataModel.setDatas(newData); //simpleDataModel.getDatas().remove(simpleDataModel.getColumnCount() - 1); } catch (TableDataException e) { FineLoggerFactory.getLogger().error(e, "{}" , e.getMessage()); } return simpleDataModel; } @Override public String getName() { return "Demo_Filter" ; } @Override public String getDefaultConfig() { return "remove last col" ; } } |
package com.tptj.plugin.hg.tabledata.factory; import com.tptj.plugin.hg.impl.AbstractResolver; import com.tptj.plugin.hg.stable.SimpleDataModel; public class DemoResolver extends AbstractResolver { @Override public SimpleDataModel parse(Object jsonStr, String others) { return new SimpleDataModel(); } public static final String KEY = "Demo" ; @Override public String getName() { return KEY; } @Override public String getDefaultConfig() { return "Is a demo" ; } } |
package com.tptj.plugin.hg.tabledata.factory.core.loader; import com.fr.json.JSONObject; import com.fr.script.Calculator; import com.fr.stable.ParameterProvider; import com.tptj.plugin.hg.impl.AbstractLoader; public class SingleParamLoader extends AbstractLoader { @Override public Object load( Calculator calculator, ParameterProvider[] parameters, String others ) { try { JSONObject obj = new JSONObject(others); if ( obj.has( "data" ) ){ return obj.optString( "data" , "{}" ); } } catch (Exception e){ e.printStackTrace(); } return "" ; } public static final String KEY = "Plugin-Factory_Data_Set_Loader_Type_Single_Parameter" ; @Override public String getName() { return KEY; } @Override public String getDefaultConfig() { StringBuilder sb = new StringBuilder(); sb.append( "{\r\n" ) .append( " data:\"待解析的json字符串,支持${p1}这样的参数和公式\",\r\n" ) .append( "}\r\n" ); return sb.toString(); } } |
SimpleDataModel继承的标准数据集格式。负责存储数据的结构为一个二维表datas和一个一维表cols,分别是具体内容和列名。调用对应的set,add方法即可向数据集写入数据 自定义面板接口,很多时候,默认的文本框对于配置来说过于简陋了。如果想要更丰富的UI可以使用这个接口替换默认的文本框
package com.tptj.plugin.hg.fun; import com.fr.stable.fun.mark.Mutable; import javax.swing.*; import java.awt.event.FocusListener; public interface ConfigTable<T extends Configuration> extends Mutable { String XML_TAG = "TableDataConfigTable" ; int CURRENT_LEVEL = 1 ; /** * 与组件名字相同就可以绑定了 * @return */ String getName(); /** * 写入数据到JPanal * @param config */ void setValue(String config); /** * 读取数据到 * @return */ String getValue(); JPanel getTable(FocusListener listener); } |
使用时继承抽象类
package com.tptj.plugin.hg.tabledata.factory.core.filter; import com.fr.design.layout.TableLayout; import com.tptj.plugin.hg.impl.AbstractConfigTable; import javax.swing.*; import java.awt.event.FocusListener; public class DefaultFilterTextTable extends AbstractConfigTable<DefaultFilter> { private final static double P = TableLayout.PREFERRED; private final static double F = TableLayout.FILL; JTextArea textArea; String value; @Override public JPanel getTable(FocusListener listener) { return new JPanel(); } /* public TextTable(String text) { super(); textArea = new JTextArea(text); //textArea.setPreferredSize(new Dimension(300, 300)); this.add(textArea); } */ @Override public void setValue(String text) { value = text; } @Override public String getValue() { return value; } } |
<extra-designer> <TableDataConfigTable class = "com.tptj.plugin.hg.tabledata.factory.core.filter.DefaultFilterTextTable" /> </extra-designer> |