# 数据源开发

# 一、数据格式

暂时支持7种数据格式,其中“个人信息”可以不用配置数据源自动获取当前登陆人的信息,如下表:

数据格式 数据编码 数据版本 说明
指标 1 V1 统计值(max, min, sum, count)
快捷入口 2 V1
菜单 3 V1
列表 4 V1
统计列表 5 V1
个人信息 6 V1 暂时不支持数据源配置,自动获取当前登陆人信息
业务导图 7 V1 暂时不支持移动端

# 二、数据源类图

# 三、数据源主要API介绍

3.1 名词说明

名称 说明
数据源 可等同理解为java.sql.DataSource
数据集 可等同数据库表
数据结果 可等同理解为“select * from T where a=1”的查询结果

3.2 AbstractDataSourceProvider:数据源连接基础封装

方法 说明
DataFormat dataFormat() 指定数据格式,具体参考DataFormat
String uuid() 数据连接唯一码, 可以使用Online UUID Generator (opens new window)在线生成
DataSetList getDataSetList(ContextParam contextParam, String[] versions) 返回数据集的列表,用户可以以此进行数据源绑定
isAllow(ContextParam contextParam, DataSetMetadata metadata, Command command) 判断用户是否有权限访问
DataSetResult executeQuery(ContextParam contextParam, DataSetMetadata metadata, String[] versions, Command command) 获取查询结果

::: demo

/**
 * @Title: 数据源连接基础封装
 * @Description:
 * @Company: seeyon.com
 * @Team: Seeyon CAP4
 * @Date: 2019-06-03 13:36
 */
public abstract class AbstractDataSourceProvider implements InvokeHandler, SortOrderable {
    /**
     * 数据格式
     *
     * @return
     */
    public abstract DataFormat dataFormat();

    /**
     * 数据版本
     *
     * @return
     */
    public String[] versions() {
        return new String[]{BizPortalConstants.Version.V1.name()};
    }

    /**
     * 数据连接唯一码, 可以使用https://www.uuidgenerator.net/version1在线生成
     *
     * @return
     */
    public abstract String uuid();

    /**
     * 数据连接名称
     *
     * @return
     */
    public abstract String name();

    /**
     * 获取数据集列表
     *
     * @param contextParam
     * @param versions
     * @return
     * @throws BusinessException
     */
    public abstract DataSetList getDataSetList(ContextParam contextParam, String[] versions) throws BusinessException;

    /**
     * 判断是否有权限
     *
     * @param contextParam
     * @param metadata
     * @param command
     * @return
     * @throws BusinessException
     */
    public abstract boolean isAllow(ContextParam contextParam, DataSetMetadata metadata, Command command) throws BusinessException;

    /**
     * 获取数据实际id
     *
     * @param metadata
     * @param k
     * @return
     * @throws BusinessException
     */
    protected Long attrRealLong(Command command, DataSetMetadata metadata, String k) throws BusinessException {
        return metadata.attrLong(k);
    }
}

:::

3.3 DataSetList:数据集列表

暂时只支持树形结构返回,对应实现类DefaultDataSetList,在使用DefaultDataSetList的时候请确保叶子节点是可绑定的数据集,并设置上metadata属性.

::: demo

/**
 * @Title:默认数据集结果
 * @Description:
 * @Company: seeyon.com
 * @Team: Seeyon CAP4
 * @Date: 2019-06-03 14:26
 */
public class DefaultDataSetList implements DataSetList {
    /**
     * 节点列表
     */
    private List<Node> list = new ArrayList<Node>();

    public List<Node> getList() {
        return list;
    }

    /**
     * 添加节点
     *
     * @param node
     */
    public void addNode(Node node) {
        list.add(node);
    }

    /**
     * 创建根节点
     *
     * @param id
     * @param name
     * @return
     */
    public Node createBranchNode(String id, String name) {
        Node node = new Node();
        node.id = id;
        node.name = name;
        node.branch = true;
        node.parentId = "0";
        return node;
    }

    /**
     * 创建枝干节点
     *
     * @param id
     * @param name
     * @param parentId
     * @return
     */
    public Node createBranchNode(String id, String name, String parentId) {
        Node node = new Node();
        node.id = id;
        node.name = name;
        node.branch = true;
        node.parentId = parentId;
        return node;
    }

    /**
     * 创建叶子节点
     *
     * @param id
     * @param name
     * @param parentId
     * @return
     */
    public Node createLeafNode(String id, String name, String parentId) {
        Node node = new Node();
        node.id = id;
        node.name = name;
        node.fillName = name;
        node.fullName = name;
        node.branch = false;
        node.parentId = parentId;
        return node;
    }

    /**
     * 创建叶子节点
     *
     * @param id
     * @param name
     * @param parentId
     * @param dataSetMetadata
     * @return
     */
    public Node createLeafNode(String id, String name, String parentId, DataSetMetadata dataSetMetadata) {
        Node node = new Node();
        node.id = id;
        node.name = name;
        node.fillName = name;
        node.fullName = name;
        node.branch = false;
        node.parentId = parentId;
        node.metadata = dataSetMetadata;
        return node;
    }

    /**
     * 创建叶子节点
     *
     * @param id
     * @param name
     * @param parentId
     * @param dataSetMetadata
     * @return
     */
    public Node createLeafNode(String id, String name, String fullName, String parentId, DataSetMetadata dataSetMetadata) {
        Node node = new Node();
        node.id = id;
        node.name = name;
        node.fillName = name;
        node.fullName = fullName;
        node.branch = false;
        node.parentId = parentId;
        node.metadata = dataSetMetadata;
        return node;
    }

    /**
     * 创建叶子节点
     *
     * @param id
     * @param name
     * @param parentId
     * @param dataSetMetadata
     * @return
     */
    public Node createLeafNode(String id, String name, String fullName, String fillName, String parentId, DataSetMetadata dataSetMetadata) {
        Node node = new Node();
        node.id = id;
        node.name = name;
        node.fillName = fillName;
        node.fullName = fullName;
        node.branch = false;
        node.parentId = parentId;
        node.metadata = dataSetMetadata;
        return node;
    }

    /**
     * 判断是否有子节点
     *
     * @param id
     * @return
     */
    public boolean existChildren(String id) {
        for (Node node : list) {
            if (StringUtils.equals(id, node.parentId)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 数据节点
     */
    public static class Node implements Serializable {
        /**
         * 节点Id
         */
        private String id;
        /**
         * 名称
         */
        private String name;
        /**
         * 回填名称
         */
        private String fillName;
        /**
         * 完整名称
         */
        private String fullName;
        /**
         * 上级节点
         */
        private String parentId;
        /**
         * 是否枝干节点
         */
        private boolean branch;
        /**
         * 元信息
         */
        private DataSetMetadata metadata;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getFillName() {
            return fillName;
        }

        public void setFillName(String fillName) {
            this.fillName = fillName;
        }

        public String getFullName() {
            return fullName;
        }

        public void setFullName(String fullName) {
            this.fullName = fullName;
        }

        public String getParentId() {
            return parentId;
        }

        public void setParentId(String parentId) {
            this.parentId = parentId;
        }

        public boolean isBranch() {
            return branch;
        }

        public void setBranch(boolean branch) {
            this.branch = branch;
        }

        public DataSetMetadata getMetadata() {
            return metadata;
        }

        public void setMetadata(DataSetMetadata metadata) {
            this.metadata = metadata;
        }
    }
}

:::

3.4 DataSetMetadata:数据集元信息

这里记录的数据集的标志信息,确保根据里面内容能反向查询到这个节点,并可以根据里面的内容做查询等操作。

::: demo

/**
 * @Title: 数据集元信息
 * @Description:
 * @Company: seeyon.com
 * @Team: Seeyon CAP4
 * @Date: 2019-06-03 14:28
 */
public class DataSetMetadata implements Serializable {
    /**
     * 标志符
     * 请与所对应的DataSourceProvider#uuid()保持一致
     */
    private String uuid;
    /**
     * 数据集标志: 用于区分一个数据源中可以获得种数据集的时候判断
     */
    private String sign;
    /**
     * 属性
     */
    private Map<String, Object> attributes = new HashMap<String, Object>();

    public DataSetMetadata() {
    }

    public DataSetMetadata(String uuid) {
        this.uuid = uuid;
    }

    public DataSetMetadata(String uuid, String sign) {
        this.uuid = uuid;
        this.sign = sign;
    }

    /**
     * 设置属性
     *
     * @param att
     * @param val
     * @return
     */
    public DataSetMetadata attr(String att, Object val) {
        attributes.put(att, val);
        return this;
    }

    /**
     * 获取属性
     *
     * @param att
     * @return
     */
    public Object attr(String att) {
        return attributes.get(att);
    }

    /**
     * 获取属性
     *
     * @param att
     * @return
     */
    public Long attrLong(String att) {
        return MapUtils.getLong(attributes, att);
    }

    public Integer attrInt(String att) {
        return MapUtils.getInteger(attributes, att);
    }


    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    public Map<String, Object> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, Object> attributes) {
        this.attributes = attributes;
    }
}

:::

3.5 ContextParam:上下文参数封装

记录了当前用户,当前业务包Id,终端类型(PC还是移动端)信息。

3.6 Command:数据集操作命令

默认op=default,使用场景如下:a) 列表中查看详情;b) 列表中翻页;c) 数据返回和操作不满足客开场景,可以与栏目开发者“协商”好op值和参数格式,并在后端实现一个InvokeHander并注入到对应的数据源接口中即可。

invokeHandlerMap.put("default", new DefaultInvokeHandler());
invokeHandlerMap.put("viewDetail", new DefaultViewDetailInvokeHandler());

# 四、指标数据源开发

4.1 指标数据返回结果

::: demo

public class IndicatorDataSetResult implements DataSetResult {
    @Override
    public Integer getDataFormat() {
        return DataFormat.INDICATOR.getCode();
    }

    @Override
    public String getVersion() {
        return Version.V1.name();
    }

    /**
     * 原始值
     */
    private Object source;
    /**
     * 显示值
     */
    private Object value;
    /**
     * 字段类型
     * @see FieldType#getKey()
     */
    private String fieldType;

    public Object getSource() {
        return source;
    }

    public void setSource(Object realValue) {
        this.source = realValue;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public String getFieldType() {
        return fieldType;
    }

    public void setFieldType(String fieldType) {
        this.fieldType = fieldType;
    }
}

:::

4.2 指标数据源开发参考示例

::: demo

public class DefaultReportIndicatorDataSourceProvider extends DefaultDataSourceProvider {
    @Override
    public int getSortOrder() {
        return 0;
    }

    @Override
    public BizPortalConstants.DataFormat dataFormat() {
        return BizPortalConstants.DataFormat.INDICATOR;
    }

    @Override
    public String uuid() {
        return "ec18b5ba-2125-410c-89be-5429c53c5810";
    }

    @Override
    public String name() {
        return "应用报表指标";
    }
    
    
    @Override
    public DefaultDataSetList getDataSetList(ContextParam contextParam, DataSetMetadata dataSetMetadata, String[] versions) throws BusinessException {
        DefaultDataSetList dataSetList = new DefaultDataSetList();
        BizConfigBean configBean = bizPortalExternAdapterManager.getBizConfigBean(getMultiStepId(dataSetMetadata));
        Long bizId = configBean.getId();

        // 根节点
        DefaultDataSetList.Node rootNode = dataSetList.createBranchNode(bizId.toString(), name());
        dataSetList.addNode(rootNode);

        // 报表指标
        List<ReportIndex> list = bizPortalReportAdapterManager.findReportIndex(ApplicationCategoryEnum.cap4biz.name(), bizId.toString());
        if (CollectionUtils.isNotEmpty(list)) {
            Multimap<Long, ReportIndex> multimap = Multimaps.index(list, new Function<ReportIndex, Long>() {
                @Override
                public Long apply(ReportIndex input) {
                    return input.getReportDesignId();
                }
            });
            List<ReportDesignDefinition> reportConfigs = bizPortalReportAdapterManager.findReportDesignDefinition(Lists.newArrayList(multimap.keySet()));
            for (ReportDesignDefinition definition : reportConfigs) {
                DefaultDataSetList.Node configNode = dataSetList.createBranchNode(definition.getDesignId().toString(), definition.getTitle(), rootNode.getId());
                dataSetList.addNode(configNode);
                Function<String, String> fullNameFunction = getFullNameFunction(configBean, definition);
                for (ReportIndex ri : multimap.get(definition.getDesignId())) {
                    // 这个地方非常关键,请务必将下次查询所需要的参数都放进去
                    DataSetMetadata metadata = new DataSetMetadata(uuid())
                            .attr(BIZ_ID, bizId)
                            .attr("designId", definition.getDesignId())
                            .attr("indexId", ri.getId());
                    dataSetList.addNode(dataSetList.createLeafNode(getNodeId(ri.getId()), ri.getTitle(), fullNameFunction.apply(ri.getTitle()), configNode.getId(), metadata));
                }
            }
        }
        return dataSetList;
    }

    /**
     * 获取统计指标完整名
     *
     * @param bizConfigBean
     * @param designDefinition
     * @return
     */
    private Function<String, String> getFullNameFunction(final BizConfigBean bizConfigBean, final ReportDesignDefinition designDefinition) {
        return new Function<String, String>() {
            @Override
            public String apply(String input) {
                return bizConfigBean.getName() + "-" + designDefinition.getTitle() + "-" + input;
            }
        };
    }

    @Override
    public boolean isAllow(ContextParam contextParam, DataSetMetadata metadata, Command command) throws BusinessException {
        return bizPortalReportAdapterManager.checkAuth(attrRealLong(command, metadata,"designId"), contextParam.getUserId());
    }

    @Override
    public DataSetResult executeQuery(ContextParam contextParam, DataSetMetadata metadata, String[] versions, Command command) throws BusinessException {
        // 查询指标结果,这个地方请注意设置fieldType和source和value,有的时候指标的显示需要用原始值
        ReportIndexResult indexResult = bizPortalReportAdapterManager.getReportIndexResult(attrRealLong(command, metadata, "indexId"));
        if (indexResult != null) {
            IndicatorDataSetResult dataSetResult = new IndicatorDataSetResult();
            dataSetResult.setFieldType(indexResult.getOriginalField().getDbType());
            dataSetResult.setSource(indexResult.getRealValue());
            dataSetResult.setValue(indexResult.getDisplay());
            return dataSetResult;
        }
        return null;
    }

    // 如果要随业务包导出的化,请将所有会变的值全部以下面代码示例转换下
    @Override
    public DataSetMetadata beforeDataSetMetaBind(String dataId, DataSetMetadata metadata) throws BusinessException {
        Long bizId = getBizId(metadata);
        metadata.attr("designId", createBizPortalDataRelation(bizId, dataId, metadata.attrLong("designId")));
        metadata.attr("indexId", createBizPortalDataRelation(bizId, dataId, metadata.attrLong("indexId")));
        return metadata;
    }

    @Override
    protected String getId(Command command, DataSetMetadata metadata) throws BusinessException {
        return attrRealLong(command, metadata, "indexId").toString();
    }

    // fields
    private BizPortalReportAdapterManager bizPortalReportAdapterManager;

    // setters
    public void setBizPortalReportAdapterManager(BizPortalReportAdapterManager bizPortalReportAdapterManager) {
        this.bizPortalReportAdapterManager = bizPortalReportAdapterManager;
    }
}

:::

# 五、快捷入口数据源开发

5.1 快捷入口数据返回结果

快捷入口返回的数据结果在PC端和移动端是有区别的:1. PC端根据返回URL的直接跳转;2.移动端是优先基于CMP的openAppByApi进行跳转,参数格式为{appId: 应用包Id, openApi : 跳转方法, params: 跳转参数}, 如果不能使用openAppByApi跳转时,则采用URL直接跳转.

5.1.1 PC端返回格式

::: demo

public class LinkDataSetResult implements DataSetResult {
    @Override
    public Integer getDataFormat() {
        return DataFormat.LINK.getCode();
    }

    @Override
    public String getVersion() {
        return Version.V1.name();
    }

    public static LinkDataSetResult of(String href) {
        LinkDataSetResult that = new LinkDataSetResult();
        that.href = href;
        return that;
    }

    /**
     * 连接地址
     */
    private String href;

    public String getHref() {
        return href;
    }

    public void setHref(String href) {
        this.href = href;
    }
}

:::

5.1.2 移动端返回格式

:::demo

public class MobileLinkDataSetResult extends LinkDataSetResult {
 /**
  * 应用包Id
  */
 private String appId;
 /**
  * 跳转方法
  */
 private String openApi;
 /**
  * 跳转参数
  */
 private Map<String, ?> params;

 public static MobileLinkDataSetResult of(String appId, String openApi, Map<String, ?> params) {
  MobileLinkDataSetResult that = new MobileLinkDataSetResult();
  that.appId = appId;
  that.openApi = openApi;
  that.params = params;
  return that;
 }

 public String getAppId() {
  return appId;
 }

 public void setAppId(String appId) {
  this.appId = appId;
 }

 public String getOpenApi() {
  return openApi;
 }

 public void setOpenApi(String openApi) {
  this.openApi = openApi;
 }

 public Map<String, ?> getParams() {
  return params;
 }

 public void setParams(Map<String, ?> params) {
  this.params = params;
 }
}

:::

5.1.2 快捷入口数据源开发参考示例

其他部分与指标数据源开发基本一致,以下代码是查看业务空间的快捷入口示例。

:::demo

@Override
 public LinkDataSetResult viewBizPortalSpace(ContextParam contextParam, Long spaceId) throws BusinessException {
  LinkDataSetResult dataSetResult;
  if (isMobile(contextParam)) {
   CAPPortalSpace portalSpace = bizPortalSpaceManager.getCAPPortalSpace(spaceId);
   CAPPortalSpaceConfig spaceConfig = bizPortalSpaceManager.getBizPortalSpaceConfig(spaceId, BizPortalConstants.ConfigStatus.RUNNING);
   Map<String, Long> map = ImmutableMap.of("bizId", portalSpace.getBizId(), "spaceId", spaceId, "configId", spaceConfig.getId());
            // 注意点1:构建移动端openAppByApi的参数格式
   dataSetResult = MobileLinkDataSetResult.of(getAppId(ApplicationCategoryEnum.cap4Form), "openBizInfoBySpaceId", map);
  } else {
            // 注意点2:构建出URL格式
   dataSetResult = LinkDataSetResult.of(replaceURL(VIEW_BIZPORTAL_SPACE_PATTERN, ImmutableMap.of("spaceId", spaceId)));
  }
  return dataSetResult;
 }

:::

# 六、列表数据源开发

列表数据源开发与前面数据源格式相比要复杂些,他支持分页获取数据,支持查看详情(如果数据源支持的话)。故命令对象在就至少存在两个:1. default: 默认列表;2. viewDetail:查看单条记录详情。

6.1 op=default:默认列表数据返回格式

:::demo

public class ListDataSetResult implements DataSetResult {

    @Override
    public Integer getDataFormat() {
        return DataFormat.LIST.getCode();
    }

    @Override
    public String getVersion() {
        return Version.V1.name();
    }

    /**
     * 列表数据结果
     */
    private transient ListDataResult listDataResult;

    public static ListDataSetResult of(ListDataResult listDataResult) {
        ListDataSetResult that = new ListDataSetResult();
        that.listDataResult = listDataResult;
        return that;
    }

    public void setListDataResult(ListDataResult listDataResult) {
        this.listDataResult = listDataResult;
    }

    public List<ListField> getFields() {
        return listDataResult.getFields();
    }

    public List<ListRowData> getData() {
        return listDataResult.getData();
    }

    public int getPage() {
        return listDataResult.getPage();
    }

    public int getPages() {
        return listDataResult.getPages();
    }

    public int getSize() {
        return listDataResult.getSize();
    }

    public int getTotal() {
        return listDataResult.getTotal();
    }

    public Date getExecuteTime (){
        return listDataResult.getExecuteTime();
    }
}

:::

这里最主要的是初始化出listDataResult这个列表对象,列表对象包含了(字段列表,数据结果,页码,总页数,每页显示数量,总体数,执行时间)等信息。

:::demo

public class ListDataResult implements Serializable {
    /**
     * id
     */
    private String id;
    /**
     * 字段列表
     */
    private List<ListField> fields = new ArrayList<ListField>();
    /**
     * 数据结果
     */
    private List<ListRowData> data = new ArrayList<ListRowData>();
    /**
     * 页码
     */
    private int page;
    /**
     * 总页数
     */
    private int pages;
    /**
     * 每页数量
     */
    private int size;
    /**
     * 总数
     */
    private int total;
    /**
     * 执行时间
     */
    private Date executeTime;

    private volatile transient Map<String, String> indexCache;

    public void addListField(ListField listField) {
        fields.add(listField);
    }

    public ListRowData newListRowData() {
        ListRowData listRowData = new ListRowData();
        data.add(listRowData);
        return listRowData;
    }

    public void putListRowData(ListRowData listRowData) {
        data.add(listRowData);
    }

    /**
     * 放数据
     *
     * @param rowData
     * @param key
     * @param data
     */
    public void putListRowData(ListRowData rowData, String key, ListRowData.Data data) {
        initIndexCache();
        rowData.getData().put(indexCache.get(key), data);
    }

    /**
     * 获取数据行中字段数据
     *
     * @param rowData
     * @param listField
     * @return
     */
    public Data getData(ListRowData rowData, ListField listField) {
        initIndexCache();
        return rowData.getData().get(indexCache.get(listField.getKey()));
    }

    /**
     * 初始化key-index对象
     */
    private void initIndexCache() {
        if (indexCache != null) {
            return;
        }
        Map<String, String> map = Maps.newHashMapWithExpectedSize(fields.size());
        for (int i = 0; i < fields.size(); i ++) {
            map.put(fields.get(i).getKey(), String.valueOf(i));
        }
        indexCache = map;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public List<ListField> getFields() {
        return fields;
    }

    public void setFields(List<ListField> fields) {
        this.fields = fields;
    }

    public List<ListRowData> getData() {
        return data;
    }

    public void setData(List<ListRowData> data) {
        this.data = data;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public Date getExecuteTime() {
        return executeTime;
    }

    public void setExecuteTime(Date executeTime) {
        this.executeTime = executeTime;
    }
}
/**
 * @Title: 列表字段
 * @Description:
 * @Company: seeyon.com
 * @Team: Seeyon CAP4
 * @Date: 2019-06-11 14:31
 */

public class ListField implements Serializable {
    /**
     * 枚举ID
     */
    public static String ENUM_ID = "enumId";
    /**
     * 枚举层级
     */
    public static String ENUM_LEVEL = "enumLevel";
    /**
     * 是否末级枚举
     */
    public static String IS_FINAL_CHILD= "isFinalChild";

    /**
     * 字段键值
     */
    private String key;
    /**
     * 字段名称
     */
    private String name;
    /**
     * 控件类型
     * com.seeyon.ctp.form.bean.FormFieldComBean.FormFieldComEnum
     * com.seeyon.ctp.report.engine.api.ReportConstants.FieldComType
     */
    private String inputType;
    /**
     * 字段类型
     * com.seeyon.cap4.form.util.Enums.FieldType
     */
    private String fieldType;
    /**
     * 字段长度
     */
    private String length;
    /**
     * 小数位
     */
    private String digitNum = "0";
    /**
     * 其他属性
     */
    private Map<String, Object> attributes = new HashMap<String, Object>();
    /**
     * 支持操作类型
     * com.seeyon.ctp.report.bizportal.Action
     */
    private List<String> actions = new ArrayList<String>();
    /**
     * 报表字段
     */
    private transient DisplayField displayField;
    /**
     * 值对象
     */
    private ListRowData.Data data;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getInputType() {
        return inputType;
    }

    public void setInputType(String inputType) {
        this.inputType = inputType;
    }

    public String getFieldType() {
        return fieldType;
    }

    public void setFieldType(String fieldType) {
        this.fieldType = fieldType;
    }

    public List<String> getActions() {
        return actions;
    }

    public void setActions(List<String> actions) {
        this.actions = actions;
    }

    public DisplayField getDisplayField() {
        return displayField;
    }

    public void setDisplayField(DisplayField displayField) {
        this.displayField = displayField;
    }

    public Data getData() {
        return data;
    }

    public void setData(Data data) {
        this.data = data;
    }
    public Long getEnumId() {
        return (Long) attributes.get(ENUM_ID);
    }

    public void setEnumId(Long enumId) {
        attr(ENUM_ID, enumId);
    }

    public Integer getEnumLevel() {
        return MapUtils.getInteger(attributes, ENUM_LEVEL);
    }

    public void setEnumLevel(Integer enumLevel) {
        attr(ENUM_LEVEL, enumLevel);
    }

    public Map<String, Object> getAttributes() {
        return attributes;
    }

    public void attr(String key, Object value) {
        this.attributes.put(key, value);
    }

    public void attr(Map<String, Object> map) {
        this.attributes.putAll(map);
    }

    public Boolean getFinalChild() {
        return (Boolean) attributes.get(IS_FINAL_CHILD);
    }

    public void setFinalChild(Boolean finalChild) {
        attr(IS_FINAL_CHILD, finalChild);
    }

    public String getLength() {
        return length;
    }

    public void setLength(String length) {
        this.length = length;
    }

    public String getDigitNum() {
        return digitNum;
    }

    public void setDigitNum(String digitNum) {
        this.digitNum = digitNum;
    }
}
/**
 * @Title: 列表行数据封装
 * @Description:
 * @Company: seeyon.com
 * @Team: Seeyon CAP4
 * @Date: 2019-06-11 14:38
 */
public class ListRowData implements Serializable {
    /**
     * 数据对象
     */
    private Map<String, Data> data = new HashMap<String, Data>();
    /**
     * 行标志符
     */
    private String identifier;
    /**
     * 行号
     */
    private int lineNo;
    /**
     * 支持操作类型
     */
    private List<String> actions = new ArrayList<String>();

    public Map<String, Data> getData() {
        return data;
    }

    public void setData(Map<String, Data> data) {
        this.data = data;
    }

    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    public List<String> getActions() {
        return actions;
    }

    public void setActions(List<String> actions) {
        this.actions = actions;
    }

    public int getLineNo() {
        return lineNo;
    }

    public void setLineNo(int lineNo) {
        this.lineNo = lineNo;
    }

    /**
     * 数据结果封装
     */
    public static class Data implements Serializable {
        /**
         * 原始值
         */
        private Object s;
        /**
         * 显示值
         */
        private Object v;

        public Data() {
        }

        public Data(Object value) {
            this.v = value;
        }

        public Data(Object sValue, Object value) {
            this.s = sValue;
            this.v = value;
        }

        public Object getS() {
            return s;
        }

        public Object getV() {
            return v;
        }
    }
}

:::

6.2 op=viewDetail:查看记录的详情信息

是否支持viewDetail不是必须的,由op=default列表中ListRowData#actions中是否包含“viewDetail”决定,如果某一行支持查看详情,则必须同时设置ListRowData#identifier属性(在获取查看详情链接的时候会带上identified来作为标志用户查看哪一行的详情)。查看详情的数据结果请求返回的是一个多快捷入口返回格式(有i与单一行可能是由多个信息主体关联而成),格式如下:

:::demo

/**
 * @Title: 多超链接返回结果
 * @Description: 针对查询和统计一条数据有多个返回链接
 * @Company: seeyon.com
 * @Team: ouyp Seeyon CAP4
 * @Date: 2019/8/28 20:29
 */
public class MultiLinkDataSetResult implements DataSetResult {

    @Override
    public Integer getDataFormat() {
        return BizPortalConstants.DataFormat.LINK.getCode();
    }

    @Override
    public String getVersion() {
        return BizPortalConstants.Version.V1.name();
    }

    public static MultiLinkDataSetResult of(LinkDataSetResult link) {
        MultiLinkDataSetResult that = new MultiLinkDataSetResult();
        that.links.add(create(link));
        return that;
    }

    public static MultiLinkDataSetResult of(String href) {
        return of(LinkDataSetResult.of(href));
    }

    public static WithLabelLinkDataSet create(LinkDataSetResult linkDataSetResult, String label) {
        WithLabelLinkDataSet withLabelLinkDataSet = new WithLabelLinkDataSet();
        BizPortalUtils.copyProperties(withLabelLinkDataSet, linkDataSetResult);
        withLabelLinkDataSet.label = label;
        return withLabelLinkDataSet;
    }

    public static WithLabelLinkDataSet create(LinkDataSetResult linkDataSetResult) {
        return create(linkDataSetResult, StringUtils.EMPTY);
    }

    /**
     * 超链接列表
     */
    private List<WithLabelLinkDataSet> links = Lists.newArrayList();

    public List<WithLabelLinkDataSet> getLinks() {
        return links;
    }

    public void setLinks(List<WithLabelLinkDataSet> links) {
        this.links = links;
    }

    public static class WithLabelLinkDataSet extends MobileLinkDataSetResult {
        private String label;

        public String getLabel() {
            return label;
        }

        public void setLabel(String label) {
            this.label = label;
        }
    }
}

:::

6.3 列表数据源开发参考示例

:::demo

/**
 * @Title: 数据源连接——列表
 * @Description: 应用报表
 * @Company: seeyon.com
 * @Team: Seeyon CAP4
 * @Date: 2019-06-06 10:25
 */
public class DefaultReportListDataSourceProvider extends DefaultDataSourceProvider implements InitializingBean {
    // fields
    private Map<String, InvokeHandler> invokeHandlerMap = Maps.newHashMap();
    private BizPortalReportAdapterManager bizPortalReportAdapterManager;

    //setter
    public void setInvokeHandlerMap(Map<String, InvokeHandler> invokeHandlerMap) {
        this.invokeHandlerMap = invokeHandlerMap;
    }

    public void setBizPortalReportAdapterManager(BizPortalReportAdapterManager bizPortalReportAdapterManager) {
        this.bizPortalReportAdapterManager = bizPortalReportAdapterManager;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 注意1: 注入标志产品支持的操作方式,如客开添加其他命令,可以在spring配置文件中设置invokeHandlerMap达到功能增强
        invokeHandlerMap.put("default", new DefaultInvokeHandler());
        invokeHandlerMap.put("viewDetail", new DefaultViewDetailInvokeHandler());
    }

    @Override
    public BizPortalConstants.DataFormat dataFormat() {
        return BizPortalConstants.DataFormat.LIST;
    }

    @Override
    public int getSortOrder() {
        return 1;
    }

    @Override
    public String uuid() {
        return "56abc264-6cd6-48a8-8967-96b1fd89f2d3";
    }

    @Override
    public String name() {
        return "应用报表";
    }

    @Override
    public DefaultDataSetList getDataSetList(ContextParam contextParam, DataSetMetadata dataSetMetadata, String[] versions) throws BusinessException {
        DefaultDataSetList dataSetList = new DefaultDataSetList();
        BizConfigBean configBean = bizPortalExternAdapterManager.getBizConfigBean(getMultiStepId(dataSetMetadata));
        Long bizId = configBean.getId();
        // 根节点
        DefaultDataSetList.Node rootNode = dataSetList.createBranchNode(bizId.toString(), name());
        dataSetList.addNode(rootNode);
        // 查询列表
        List<ReportDesignDefinition> reportConfigs = bizPortalReportAdapterManager.findReportDesignDefinition(ApplicationCategoryEnum.cap4biz.name(), bizId.toString(), DesignType.QUERY);
        if (CollectionUtils.isNotEmpty(reportConfigs)) {
            for (ReportDesignDefinition config : reportConfigs) {
                DefaultDataSetList.Node leafNode = dataSetList.createLeafNode(getNodeId(config.getDesignId()),
                        config.getTitle(),
                        configBean.getName() + "-" + config.getTitle(),
                        rootNode.getId(),
                        new DataSetMetadata(uuid()).attr(BIZ_ID, bizId).attr(BIZ_ID, bizId).attr("designId", config.getDesignId()));
                dataSetList.addNode(leafNode);
            }
        }
        return dataSetList;
    }

    @Override
    public boolean isAllow(ContextParam contextParam, DataSetMetadata metadata, Command command) throws BusinessException {
        return bizPortalReportAdapterManager.checkAuth(getDesignId(command, metadata), contextParam.getUserId());
    }

    @Override
    public DataSetResult executeQuery(ContextParam contextParam, DataSetMetadata metadata, String[] versions, Command command) throws BusinessException {
        return getInvokeHandler(command).executeQuery(contextParam, metadata, versions, command);
    }

    @Override
    public DataSetMetadata beforeDataSetMetaBind(String dataId, DataSetMetadata metadata) throws BusinessException {
        return metadata.attr("designId", createBizPortalDataRelation(getBizId(metadata), dataId, metadata.attrLong("designId")));
    }

    @Override
    protected String getId(Command command, DataSetMetadata metadata) throws BusinessException {
        return getDesignId(command, metadata).toString();
    }

    private Long getDesignId(Command command, DataSetMetadata metadata) throws BusinessException {
        return attrRealLong(command, metadata, "designId");
    }

    /**
     * 获取命令操作类
     * @param command
     * @return
     */
    private InvokeHandler getInvokeHandler(Command command) {
        return invokeHandlerMap.get(command.getOp());
    }

    /**
     * 默认命令处理类
     */
    private class DefaultInvokeHandler implements InvokeHandler {
        @Override
        public DataSetResult executeQuery(ContextParam contextParam, DataSetMetadata metadata, String[] versions, Command command) throws BusinessException {
            Long designId = getDesignId(command, metadata);

            FlipInfo fi = new FlipInfo();
            // 注意2:这里是带了分页信息的,列表数据源需要处理分页场景
            PaginationParam paginationParam = PaginationParam.of(command);
            fi.setPage(paginationParam.getPage());
            fi.setSize(paginationParam.getPageSize());
            fi.setNeedTotal(true);

            UserConditions userConditions = new UserConditions();
            ListDataResult list = bizPortalReportAdapterManager.findQueryDataResult(designId, userConditions, fi);

            return ListDataSetResult.of(list);
        }
    }

    /**
     * 查询穿透查看详情
     */
    private class DefaultViewDetailInvokeHandler implements ViewDetailInvokeHandler {
        @Override
        public MultiLinkDataSetResult executeQuery(ContextParam contextParam, DataSetMetadata metadata, String[] versions, Command command) throws BusinessException {
            Long designId = getDesignId(command, metadata);
            String extendParams = command.getAttributes().get("identifier").toString();
            return bizPortalReportAdapterManager.getQueryPenetrateInfo(contextParam, designId, extendParams);
        }
    }
}

:::


# 七、数据版本

由于暂时所以的数据格式都只有V1版本,但不排除后期对数据格式有版本升级,故平台建议数据源开发者

1.不要重写AbstractDataSourceProvider#versions方法;2. 返回的数据结果的versio均指定为Version.V1。

创建人:yinyanting