DataProviderExtension

DataProviderExtension 让插件以编程方式向平台提供下拉框、lookup 与查询数据。每个 provider 用一个全局唯一的 provider key 注册,返回一组 DataItemvalue / label / metadata)。当某个数据需要从多个 model、外部系统或 DSL 无法表达的计算中组合时,可以用 provider 把这套查找逻辑封装在插件内,再由前端按 key 引用。

概念

每个 provider 暴露一个唯一的 provider key,格式为 namespace:provider-name(如 billing:currencieshr:departments)。平台通过 ExtensionRegistry.getDataProvider(providerKey)getProviderKey() 匹配到对应的 DataProviderExtension,并调用 fetchData(DataRequest) 取数。

DataRequest 携带 provider 取数所需的上下文:

  • tenantId —— 当前租户
  • searchTerm —— 用户输入的搜索词(下拉框联想用)
  • filters —— 额外的过滤条件 map
  • offset / limit —— 分页(默认 offset=0limit=100
  • pluginId / namespace / providerKey / settings —— 其余上下文

Provider 返回 List<DataItem>,每个 DataItemvalue(提交值)、label(显示值)与可选的 metadata(附加信息 map)。其结构面向 lookup / 下拉框,而非任意 field-code → value 的行集

接口签名

package com.auraboot.framework.plugin.extension;

import org.pf4j.ExtensionPoint;

import java.util.List;
import java.util.Map;

public interface DataProviderExtension extends ExtensionPoint {

    /** 唯一的 provider key,格式 "namespace:provider-name"。 */
    String getProviderKey();

    /** 根据请求返回数据项。 */
    List<DataItem> fetchData(DataRequest request);

    /** 可选:匹配请求的总条数,用于分页(默认取 fetchData 的 size)。 */
    default long getCount(DataRequest request) {
        return fetchData(request).size();
    }

    /** 可选:本 provider 是否能处理该 key(默认按 getProviderKey() 比对)。 */
    default boolean supports(String providerKey) {
        return getProviderKey().equals(providerKey);
    }

    /** 可选:结果是否可缓存(默认 true)。 */
    default boolean isCacheable() { return true; }

    /** 可选:缓存 TTL,单位秒(默认 300)。 */
    default int getCacheTtlSeconds() { return 300; }
}

DataItemDataRequest 是接口内的嵌套 record:

record DataItem(String value, String label, Map<String, Object> metadata) {
    DataItem(String value, String label) { this(value, label, Map.of()); }
}

record DataRequest(
    Long tenantId, String pluginId, String namespace, String providerKey,
    String searchTerm, Map<String, Object> filters,
    int offset, int limit, Map<String, Object> settings
) {
    static Builder builder() { /* ... */ }
}

实现示例

一个返回币种列表的 lookup provider:

package com.acme.billing.provider;

import com.auraboot.framework.plugin.extension.DataProviderExtension;
import org.pf4j.Extension;

import java.util.List;
import java.util.Map;

@Extension
public class CurrencyDataProvider implements DataProviderExtension {

    @Override
    public String getProviderKey() {
        return "billing:currencies";
    }

    @Override
    public List<DataItem> fetchData(DataRequest request) {
        return List.of(
            new DataItem("usd", "US Dollar", Map.of("symbol", "$")),
            new DataItem("eur", "Euro",      Map.of("symbol", "€")),
            new DataItem("cny", "人民币",     Map.of("symbol", "¥"))
        );
    }
}

当下拉框联想时,可读取 request.searchTerm() 过滤;当列表很大时,按 request.offset() / request.limit() 分页并重写 getCount()

注册

仅需 @Extension 注解 —— 通过 META-INF/extensions.idx 发现,平台启动时由 ExtensionRegistry 收集为 getAllDataProviders()。Provider key 是全局命名空间;请始终带上插件自己的 namespace 前缀以避免冲突(如 billing:currencies 优于裸 currencies)。

常见陷阱

  • 返回 null 项或 nullvalue / label 下拉框会渲染异常;缺数据时返回空 List 更清晰。
  • 忽略 getCount() lookup 数据量大时请重写它返回真实总数,并在 fetchData() 中遵守 offset / limit,否则分页会错乱。
  • 绕过 tenant 作用域。 若查询外部系统或共享数据,请显式按 request.tenantId() 限制范围。平台不会替你过滤返回值。
  • fetchData() 中触发副作用。 该方法会被频繁调用且结果可能被缓存(见 isCacheable() / getCacheTtlSeconds())。请保持只读。

相关