需求场景: 报表有个客户id字段, 每个业务人员负责一批客户(这个是动态的, 可能有不同批次), 业务人员需要在报表中筛选自己负责的某批客户. 因此期望筛选器的备选值是批次号, 表格真正刷新时, 是用该批次的客户id进行筛选.
来源jira: SMS-63446
名称: 查询前处理器
作用: 在查询执行前, 处理查询请求. 比如修改条件值.
生效对象: 仪表盘, 即席, 透视的所有组件取数.
interface IBaseQueryHandler {
// 空接口
}
interface IBeforeQueryHandler extends IBaseQueryHandler {
/**
* @param context 查询上下文
*/
void before(QueryContext context);
}
名称: 查询处理器
作用: 自定义组件查询的处理器. 比如自定义筛选器备选值
生效对象: 仪表盘, 即席, 透视的筛选器组件取数.
// 一般不直接实现此接口
// package: Commons
// path: smartbix.query.handler
interface ICustomExecutor {
/**
* @return 查询结果, 不同组件查询结果的数据结构不同
*/
Object refresh();
/**
* @return 查询请求
*/
IQueryOption getQueryOption();
}
// 仪表盘组件查询处理器抽象类
// package: Page
// path: smartbix.page.executor2
abstract class AbstractCustomExecutor implements ICustomExecutor {
// 已实现getQueryOption
}
// 即席透视组件查询处理器抽象类
// package: ModelQuery
// path: smartbix.analysis.query
abstract class AbstractCustomExecutor implements ICustomExecutor {
// 已实现getQueryOption
}
自定义仪表盘筛选器(包括参数)的取数接口时, 需要返回如下结构的数据
// 非树筛选器/参数
type result1 = {
data: [
// [realValueCell, displayValueCell?], 可以没有displayValueCell
[ { value, displayValue } ]
]
}
// 树参数
// 没有children, 是打平的列表
type result2 = {
data: [
// [realValueCell, displayValueCell?], 可以没有displayValueCell
[ { id, value, displayValue, parentId } ]
]
}
// 树筛选器
// id和parentId格式必须为: value + delimiter + parentId
// delimiter为: $$$^V^$$$
// 示例: parentId = '东北', id = '东北$$$^V^$$$吉林省'
type result3 = [{ id, parentId, value, displayValue, levelId: '字段的uniqueId', children: [] }]
自定义即席透视筛选器(包括参数)的取数器接口时, 需要返回如下结构的数据
// 非树筛选器/参数
type result1 = {
data: [
{ value, displayValue }
]
}
// 树参数
// 没有children, 是打平的列表
type result2 = {
data: [
{ id, value, displayValue, parentId }
]
}
// 树筛选器
// id和parentId格式必须为: value + delimiter + parentId
// delimiter为: $$$^V^$$$
// 示例: parentId = '东北', id = '东北$$$^V^$$$吉林省'
type result3 = [{ id, parentId, value, displayValue, levelId: '字段的uniqueId', children: [] }]
名称: 查询处理器管理类
作用: 用于注册/注销 IBeforeQueryHandler和ICustomExecutor
生效对象: 仪表盘, 即席, 透视
限制: 注册取数器时, 名称不能含双下划线, 不能与系统中存在的bean名称重复, 因此命名因该特殊些.
// package: Commons
// path: smartbix.query.handler
class QueryHandlerManager {
/**
* @return 实例
*/
static QueryHandlerManager getInstance();
/**
* 注册组件取数器
* @param executorName 取数器名称, 不允许与系统中任何bean命名重复, 不允许包含双下划线
* @param executorClass 取数器类
*/
void addExecutor(String executorName, Class<? extends ICustomExecutor> executorClass);
/**
* 移除取数器
* @param executorName 取数器名称
* @return 取数器存在且移除成功, 则返回true
*/
boolean removeExecutor(String executorName);
/**
* 注册组件取数前处理器
* @param reportType 报表类型, DETAILED_QUERY, AD_HOC_ANALYSIS, SMARTBIX_PAGE
* @param handler 查询前处理器
*/
void add(String reportType, IBaseQueryHandler handler);
/**
* 注销组件取数前处理器
* @param reportType 报表类型, DETAILED_QUERY, AD_HOC_ANALYSIS, SMARTBIX_PAGE
* @param handler 查询前处理器
*/
void add(String reportType, IBaseQueryHandler handler);
}
查询上下文, 包括报表ID, 报表类型, 查询请求, 查询ID
// 查询上下文
class QueryContext {
/**
* @return 请求ID
*/
String getQueryId();
/**
* @return 报表ID
*/
String getReportId();
/**
* @return 报表类型
*/
String getReportType();
/**
* @return 查询请求
*/
IQueryOption getQueryOption();
}
查询请求, 包括筛选器/参数搜索时的key, 查询条件, 私有字段, 分页信息, 取数字段, 数据来源, 排序.
interface IQueryOption {
/**
* @return 筛选器/参数搜索时的key
*/
String getKeyword();
/**
* @return 查询条件
*/
IQueryCondition getQueryCondition();
/**
* @return 私有字段列表
*/
List<IQueryPrivateDefine> getPrivateDatasetFields();
/**
* @return 分页, 格式: { num: 0, size: 1000 }
*/
Pagination getPagination();
/**
* @return 数据来源, 格式: { id: '数据集ID 或 数据源ID 等', type: '数据集类型' }
*/
IDataSource getDataSource();
/**
* @return 查询字段列表, 当是参数取数时, field为 { name: '参数名称' }
*/
List<IQueryField> getQueryFields();
/**
* @return 行排序 (筛选器的排序在这里), 仪表盘可用, 即席透视先从getQueryFiels()的字段中读取getOrderBy()
*/
List<IQueryOrder> getRowQuerySorts();
/**
* @return 列排序
*/
List<IQueryOrder> getColQuerySorts();
}
排序设置
interface IQueryOrder {
/**
* @return 排序方式, ASC 升序, BASC 全局升序, DESC 降序, BDESC 全局降序
*/
SortDirection sortDirection();
/**
* @return 排序的字段
*/
Optional<IQueryField> sortField();
/**
* @return 自定义成员值排序的成员顺序
*/
List<String> sortByCustomization();
/**
* @return 排序依据, 如果不存在, 排序依据就是排序的字段
*/
Optional<IQueryField> sortByField();
/**
* @return 排序优先级, 值越大越优先
*/
int getOrderPriority();
/**
* @return 表头排序时该列的表头路径
*/
List<?> sortMemberPath();
}
查询条件
// 查询条件
interface IQueryCondition {
/**
* @return 是否可忽略(不工作的条件可忽略)
*/
boolean canIgnore();
/**
* @return 条件类型: RELATION 关系, FIELD_FILTER 字段条件, PARAM_FILTER 参数条件, NONE_FILTER 表达式条件
*/
QueryConditionNodeType getNodeType();
}
// 字段条件
interface IQueryFieldConditionNode extends IQueryCondition {
/**
* @return 运算符
*/
FilterOperationType getOperator();
/**
* @param 运算符
*/
void setOperator(FilterOperationType operator);
/**
* @return 条件值
*/
List<Object> getValues();
/**
* @param values 条件值
*/
void setValues(List<Object> values)
/**
* @return 字段
*/
IQueryField getField();
/**
* @param field 字段
*/
void setField(IQueryField field);
}
// 参数条件
interface IQueryParamConditionNode extends IQueryCondition {
/**
* @return 数据模型参数id
*/
String getId();
/**
* @return 数据模型参数名称
*/
String getName();
/**
* @return 参数别名
*/
String getAlias();
/**
* @return 参数值
*/
List<Object> getValues();
/**
* @param values 参数值
*/
void setValues(List<Object> values);
}
// 表达式条件
interface IQueryNoneConditionNode extends IQueryCondition {
/**
* @return 条件值 (必须是两个值)
*/
List<Object> getValues();
/**
* @param values 条件值 (必须是两个值)
*/
void setValues(List<Object> values);
/**
* @return 比较运算符 (条件第一个值和第二个值比较)
*/
FilterOperationType getOperator();
/**
* @param operator 比较运算符 (条件第一个值和第二个值比较)
*/
void setOperator(FilterOperationType operator);
}
// 关系条件
interface IQueryRelationConditionNode extends IQueryCondition {
/**
* @return 关系: OR 或, AND 与, NOR 非
*/
LogicOperatorType getRelation();
/**
* @param relation 关系
*/
void setRelation(LogicOperatorType relation);
/**
* @return 子节点列表
*/
List<IQueryCondition> getChildNodes();
/**
* @param childNodes 子节点列表
*/
void setChildNodes(List<IQueryCondition> childNodes);
}
自定义ICustomExecutor, 需要指定查询的executorName, 这一步骤需要前端二开实现.
// 即席, 透视, 仪表盘筛选器
interface IFilter {
/**
* @param queryType 取数器名称
*/
setQueryType (queryType: string): void
}
项目 | 说明 |
Commons | 提供IBeforeQueryHandler接口, 用于组件取数前修改查询请求(比如条件值) 提供ICustomExecutor接口, 用于定义组件取数器 提供QueryHandlerManger管理器, 用于注册ICustomExecutor和IBeforeQueryHandler 提供QueryCondtionUtil工具类, 用于修改组件取数请求的条件值 |
Framework.interface | 提供IModule接口, 用于扩展包激活时, 注册组件取数执行器, 组件取数前处理器 提供IShutdownHook接口, 用于扩展包卸载时, 注销组件取数执行器, 组件取数前处理器 |
ModelQuery | 提供即席透视的组件取数器的抽象实现smartbix.analysis.query.AbstractCustomExecutor 想要自定义即席透视组件的取数器时, 需要继承该类 |
Page | 提供仪表盘的组件取数器的抽象实现smartbix.page.executor2.AbstractCustomExecutor 想要自定义仪表盘组件的取数器时, 需要继承该类 |
SmartbiVLibManager | 提供ObjectNode和ArrayNode, 用于包装取数器结果 |
更完整的用例, 请访问扩展包http://10.10.201.35:8888/RD/Extensions的 XPlayWrightExtensionTemplate 项目
新建筛选器取数器, 用于自定义筛选器的备选值
package smartbi.smartbix.extension.custom.analysis;
import smartbi.smartbix.extension.custom.StandbyValueProvider;
import smartbix.analysis.query.AbstractCustomExecutor;
/**
* 即席透视列表筛选器(参数)取数器(命名不允许有双下划线)
*/
public class CustomPortletExecutor extends AbstractCustomExecutor {
@Override
public Object refresh() {
String keyword = this.getQueryOption().getKeyword();
return StandbyValueProvider.getStandbyValue(keyword);
}
}
package smartbi.smartbix.extension.custom;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import smartbix.util.SmartbiXObjectMapper;
import smartbixlibs.com.fasterxml.jackson.databind.node.ArrayNode;
import smartbixlibs.com.fasterxml.jackson.databind.node.ObjectNode;
public final class StandbyValueProvider {
private StandbyValueProvider() {
}
/**
* 备选值
* @param keyword 搜索关键词
* @return 备选值
*/
public static ObjectNode getStandbyValue(String keyword) {
List<String> values = getRealValueList(keyword);
ObjectNode result = SmartbiXObjectMapper.getInstance().createObjectNode();
ArrayNode data = result.putArray("data");
values.stream().forEach(v -> {
ObjectNode col = data.addObject();
col.put("value", v);
col.put("displayValue", v);
});
return result;
}
private static List<String> getRealValueList(String keyword) {
List<String> values = new ArrayList<>();
values.add("CUSTOM_X北_CUSTOM");
values.add("华南");
values.add("华东");
values.add("西南");
if (keyword == null || "".equals(keyword)) {
return values;
}
return values.stream().filter(v -> v.indexOf(keyword) >= 0)
.collect(Collectors.toList());
}
}
新建查询前处理器, 用于修改被该筛选器影响的组件的条件值(比如批次号替换为一系列客户id)
package smartbi.smartbix.extension.custom;
import java.util.ArrayList;
import java.util.List;
import smartbix.query.handler.IBeforeQueryHandler;
import smartbix.query.handler.IQueryFieldConditionNode;
import smartbix.query.handler.IQueryParamConditionNode;
import smartbix.query.handler.QueryConditionUtil;
import smartbix.query.handler.QueryContext;
/**
* 组件查询前处理器
*/
public class BeforeQueryHandler implements IBeforeQueryHandler {
@Override
public void before(QueryContext context) {
QueryConditionUtil.handleCondition(context.getQueryOption().getQueryCondition(), (condition) -> {
if (condition instanceof IQueryFieldConditionNode) {
IQueryFieldConditionNode node = (IQueryFieldConditionNode) condition;
node.setValues(processConditionValues(node.getValues()));
} else if (condition instanceof IQueryParamConditionNode) {
IQueryParamConditionNode node = (IQueryParamConditionNode) condition;
node.setValues(processConditionValues(node.getValues()));
}
return condition;
});
}
private List<Object> processConditionValues(List<Object> oldValues) {
List<Object> newValues = new ArrayList<>();
oldValues.forEach(v -> {
if ("CUSTOM_X北_CUSTOM".equals(v)) {
newValues.add("华北");
newValues.add("东北");
newValues.add("西北");
} else {
newValues.add(v);
}
});
return newValues;
}
}
将上面两个处理器注册
package smartbi.smartbix.extension.custom;
import smartbi.framework.IModule;
import smartbi.framework.IShutdownHook;
import smartbix.query.handler.QueryHandlerManager;
/**
* 自定义取数器Module
*/
public class CustomExecutorModule implements IModule, IShutdownHook {
private static CustomExecutorModule instance = new CustomExecutorModule();
private BeforeQueryHandler handler = new BeforeQueryHandler();
/**
* @return CustomExecutorModule
*/
public static CustomExecutorModule getInstance() {
return instance;
}
@Override
public void shutdown() {
QueryHandlerManager.getInstance().remove("DETAILED_QUERY", handler);
QueryHandlerManager.getInstance().remove("AD_HOC_ANALYSIS", handler);
QueryHandlerManager.getInstance().removeExecutor("Custom_Portlet_Executor");
}
@Override
public void activate() {
QueryHandlerManager.getInstance().add("DETAILED_QUERY", handler);
QueryHandlerManager.getInstance().add("AD_HOC_ANALYSIS", handler);
QueryHandlerManager.getInstance().addExecutor("Custom_Portlet_Executor", smartbi.smartbix.extension.custom.analysis.CustomPortletExecutor.class);
}
前端二开设置筛选器取数时, 使用自定义的取数器
// 透视
this.on(AD_HOC_FILTER_ON_INIT, (filter, iAdHocAnalysis) => {
filter.setQueryType('Custom_Portlet_Executor')
})