页面树结构
转至元数据结尾
转至元数据起始

正在查看旧版本。 查看 当前版本.

与当前比较 查看页面历史

版本 1 下一个 »

需求说明

需求场景: 报表有个客户id字段, 每个业务人员负责一批客户(这个是动态的, 可能有不同批次), 业务人员需要在报表中筛选自己负责的某批客户. 因此期望筛选器的备选值是批次号, 表格真正刷新时, 是用该批次的客户id进行筛选.

来源jira: SMS-63446

接口说明

IBeforeQueryHandler

名称: 查询前处理器

作用: 在查询执行前, 处理查询请求. 比如修改条件值.

生效对象: 仪表盘, 即席, 透视的所有组件取数.


interface IBaseQueryHandler {
  // 空接口
}
interface IBeforeQueryHandler extends IBaseQueryHandler {
  /**
   * @param context 查询上下文
   */
  void before(QueryContext context);
}

ICustomExecutor

名称: 查询处理器

作用: 自定义组件查询的处理器. 比如自定义筛选器备选值

生效对象: 仪表盘, 即席, 透视的筛选器组件取数.

// 一般不直接实现此接口
// 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: [] }]

QueryHandlerManager

名称: 查询处理器管理类

作用: 用于注册/注销 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);
}

接口依赖的类

QueryContent

查询上下文, 包括报表ID, 报表类型, 查询请求, 查询ID

// 查询上下文
class QueryContext {
  /**
   * @return 请求ID
   */
  String getQueryId();
  /**
   * @return 报表ID
   */
  String getReportId();
  /**
   * @return 报表类型
   */
  String getReportType();
  /**
   * @return 查询请求
   */
  IQueryOption getQueryOption();
}

IQueryOption

查询请求, 包括筛选器/参数搜索时的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();
}

IQueryOrder

排序设置

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();
}

IQueryCondition

查询条件

// 查询条件
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, 这一步骤需要前端二开实现.

IFilter

// 即席, 透视, 仪表盘筛选器
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 项目

  1. 新建筛选器取数器, 用于自定义筛选器的备选值

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());
	}
}
  1. 新建查询前处理器, 用于修改被该筛选器影响的组件的条件值(比如批次号替换为一系列客户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;
	}
}
  1. 将上面两个处理器注册

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);
    }
  
  1. 前端二开设置筛选器取数时, 使用自定义的取数器

// 透视
this.on(AD_HOC_FILTER_ON_INIT, (filter, iAdHocAnalysis) => {
  filter.setQueryType('Custom_Portlet_Executor')
})


  • 无标签