后端:如何自定义表达式实现特殊需求?扩展内置函数表达式

平台提供了很多的表达式,如果这些表达式不满足场景?那我们应该如何新增表达式去满足项目的需求?
目前平台支持的表达式内置函数,参考


1. 扩展表达式的场景

注解@Validation的rule字段支持配置表达式校验
如果需要判断入参List类型字段中的某一个参数进行NULL校验,发现平台的内置函数不支持该场景的配置,这里就可以通过平台的机制,对内置函数进行扩展。

常见的一些代码场景,如下:

package pro.shushi.pamirs.demo.core.action;

……引用类

@Model.model(PetShopProxy.MODEL_MODEL)
@Component
public class PetShopProxyAction extends DataStatusBehavior<PetShopProxy> {

    @Override
    protected PetShopProxy fetchData(PetShopProxy data) {
        return data.queryById();
    }
    @Validation(ruleWithTips = {
            @Validation.Rule(value = "!IS_BLANK(data.code)", error = "编码为必填项"),
            @Validation.Rule(value = "LEN(data.name) < 128", error = "名称过长,不能超过128位"),
    })
    @Action(displayName = "启用")
    @Action.Advanced(invisible="!(activeRecord.code !== undefined && !IS_BLANK(activeRecord.code))")
    public PetShopProxy dataStatusEnable(PetShopProxy data){
        data = super.dataStatusEnable(data);
        data.updateById();
        return data;
    }

……其他代码

}

2. 新建一个自定义表达式的函数

校验入参如果是个集合对象的情况下,单个对象的某个字段如果为空,返回false的函数。

例子:
新建一个CustomCollectionFunctions类

package xxx.xxx.xxx;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.common.constants.NamespaceConstants;
import pro.shushi.pamirs.meta.util.FieldUtils;

import java.util.List;

import static pro.shushi.pamirs.meta.enmu.FunctionCategoryEnum.COLLECTION;
import static pro.shushi.pamirs.meta.enmu.FunctionLanguageEnum.JAVA;
import static pro.shushi.pamirs.meta.enmu.FunctionOpenEnum.LOCAL;
import static pro.shushi.pamirs.meta.enmu.FunctionSceneEnum.EXPRESSION;

/**
 * 自定义内置函数
 */
@Fun(NamespaceConstants.expression)
@Component
public class CustomCollectionFunctions {

    /**
     * LIST_FIELD_NULL 就是我们自定义的表达式,不能与已经存在的表达式重复!!!
     *
     * @param list
     * @param field
     * @return
     */
    @Function.Advanced(
            displayName = "校验集成的参数是否为null", language = JAVA,
            builtin = true, category = COLLECTION
    )
    @Function.fun("LIST_FIELD_NULL")
    @Function(name = "LIST_FIELD_NULL", scene = {EXPRESSION}, openLevel = LOCAL,
            summary = "函数示例: LIST_FIELD_NULL(list,field),函数说明: 传入一个对象集合,校验集合的字段是否为空"
    )
    public Boolean listFieldNull(List list, String field) {
        if (null == list) {
            return false;
        }
        if (CollectionUtils.isEmpty(list)) {
            return false;
        }
        for (Object data : list) {
            Object value = FieldUtils.getFieldValue(data, field);
            if (value == null) {
                return false;
            }
        }
        return true;
    }

}

3. 将自定义的表达式类,注册到平台的白名单

新建CustomFaasScriptAllowListApi类,@Order优先级要高于平台默认的优先级才会生效。

package xxx.xxx.xxx;

import org.apache.commons.collections4.SetUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.framework.faas.fun.builtin.*;
import pro.shushi.pamirs.framework.faas.spi.api.guard.FaasScriptAllowListApi;
import pro.shushi.pamirs.meta.common.constants.NamespaceConstants;
import pro.shushi.pamirs.meta.common.spi.SPI;

import java.util.Set;

/**
 * 自定义:支持表达式调用的函数白名单与黑名单SPI实现
 */
@Order(1) //此处把自定义的类优先级调高
@Component
@SPI.Service
public class CustomFaasScriptAllowListApi implements FaasScriptAllowListApi {

    //白名单
    public static final Set<String> DEFAULT_SET = SetUtils.hashSet(

            //白名单,直接复制默认实现,pro.shushi.pamirs.framework.faas.spi.service.DefaultFaasScriptAllowListApi
            CollectionFunctions.class.getName(),
            ContextFunctions.class.getName(),
            DateFunctions.class.getName(),
            LogicFunctions.class.getName(),
            MapFunctions.class.getName(),
            MathFunctions.class.getName(),
            ObjectFunctions.class.getName(),
            RegexFunctions.class.getName(),
            TextFunctions.class.getName(),

            //下面添加自己的白名单类
            CustomCollectionFunctions.class.getName()

    );

    @Override
    public Set<String> classWhiteList() {
        return DEFAULT_SET;
    }

    @Override
    public Set<String> namespaceWhiteList() {
        return SetUtils.hashSet(NamespaceConstants.expression);
    }

}

4. 使用自定义的表达式

使用场景demo:

    /**
     * 注意点:自定义函数的 [field]字段是个文本,一定要加个引号代表参数是文本,不然无法解析到数据,其他场景类似
     *
     * @param data
     * @return
     */
    @Action.Advanced(name = FunctionConstants.create, managed = true)
    @Action(displayName = "确定", summary = "创建", bindingType = ViewTypeEnum.FORM)
    @Function(name = FunctionConstants.create)
    @Function.fun(FunctionConstants.create)
    @Validation(ruleWithTips = {
            @Validation.Rule(value = "LIST_FIELD_NULL(data.itemAttributes,'itemId')", error = "字段不能为空"),
    })
    public DemoItem create(DemoItem data) {
        return demoItemService.create(data);
    }

结语:可以通过平台的机制,去沉淀一套满足自己场景需求的表达式。

Oinone社区 作者:oinone原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/13164.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
oinone's avataroinone
Previous 2024年5月30日 pm4:26
Next 2024年5月30日 pm10:03

相关推荐

  • 工作流用户待办过滤站内信

    工作流用户待办过滤站内信 全局过滤 启动工程application.yml中配置: pamirs: workflow: notify: false 个性化过滤 实现pro.shushi.pamirs.workflow.app.api.service.WorkflowMailFilterApi接口 返回true表示需要发送站内信 返回false表示不需要发送站内信 示例: import org.apache.commons.lang3.StringUtils; import pro.shushi.pamirs.message.model.PamirsMessage; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.user.api.model.PamirsUser; import pro.shushi.pamirs.workflow.app.api.model.WorkflowUserTask; import pro.shushi.pamirs.workflow.app.api.service.WorkflowMailFilterApi; /** * MyWorkflowMailFilterImpl * * @author yakir on 2025/02/24 16:28. */ @Fun(WorkflowMailFilterApi.FUN_NAMESPACE) public class MyWorkflowMailFilterImpl implements WorkflowMailFilterApi { @Override @Function public Boolean filter(WorkflowUserTask workflowUserTask, PamirsUser user, PamirsMessage message) { // 按用户待办过滤 workflowUserTask if (10000L == workflowUserTask.getInitiatorUid()){ return true; } // 按用户过滤 user if (1000L == user.getId()){ return true; } // 按站内信消息过滤 message if (StringUtils.contains(message.getBody(), "你好")) { return true; } return false; } }

    2025年2月24日
    1.3K00
  • 如何在代码中使用自增ID和获取序列

    在使用继承IDModel或CodeModel时,id和code是系统默认自动生成, 默认值规则:ID–>分布式ID; CODE–>根据定义的SequenceConfig规则自动生成。 在特定情况下需要落库前先生成ID或者Code,这些场景下可参照如下代码示例 一、使用自增ID 单个字段设置方式 // 主键字段,可以使用mysql的自增能力 @Field.Integer @Field.PrimaryKey(keyGenerator = KeyGeneratorEnum.AUTO_INCREMENT) @Field.Advanced(batchStrategy = FieldStrategyEnum.NEVER) @Field(displayName = "id", summary = "Id字段,⾃增") private Long id; @Field.Integer @Field(displayName = "自增版本") @Field.Sequence(sequence = "SEQ", initial = 1) private Long version; 全局设置方式 该方式会作用到每一个存储模型的id字段,在application.yml配置文件中修改id的生成规则,查找配置项关键字key-generator,默认为DISTRIBUTION(分布式id),可修改为 AUTO_INCREMENT(自增id) 二、手动方式获取序列 获取方式示例1 /** * 在特定场景下需要手动生成Id或者code时,可参照这个示例 */ public void manualSetIdCode(){ DemoItem demoItem = new DemoItem(); //手动生成ID和code Object idObj = Spider.getDefaultExtension(IdGenerator.class).generate(PamirsTableInfo.fetchKeyGenerator(DemoItem.MODEL_MODEL)); demoItem.setId(TypeUtils.createLong(idObj)); Object codeObj = CommonApiFactory.getSequenceGenerator().generate("SEQ",DemoItem.MODEL_MODEL); String code = TypeUtils.stringValueOf(codeObj); demoItem.setCode(code); //…… } 获取方式示例2 1、在系统启动的时候初始化SequenceConfig package pro.shushi.pamirs.demo.core.init; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand; import pro.shushi.pamirs.boot.common.extend.MetaDataEditor; import pro.shushi.pamirs.core.common.InitializationUtil; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.core.constant.SeqConstants; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.meta.Meta; import pro.shushi.pamirs.meta.enmu.SequenceEnum; import java.util.Map; /** * DemoMetadataEditor */ @Slf4j @Component public class DemoMetadataEditor implements MetaDataEditor { @Override public void edit(AppLifecycleCommand command, Map<String, Meta> metaMap) { InitializationUtil util = InitializationUtil.get(metaMap, DemoModule.MODULE_MODULE, DemoModule.MODULE_NAME); if (util == null) { log.error("获取初始化序列失败"); return; } bizSequence(util); } private void bizSequence(InitializationUtil util) { util.createSequenceConfig("申请单编码生成", SeqConstants.NABEL_SAMPLE_APPLY_SEQ, SequenceEnum.ORDERLY_SEQ, 8) .setStep(1) .setInitial(80000000L) .setIsRandomStep(false); util.createSequenceConfig("订单编码生成", SeqConstants.NABEL_SAMPLE_ORDER_SEQ_YP, SequenceEnum.ORDERLY_SEQ, 8) .setPrefix("YP") .setStep(1) .setInitial(80000000L) .setIsRandomStep(false); } } 2、在代码中使用序列 public static String getSaleOrderCode() { Object sequence = CommonApiFactory.getSequenceGenerator().generate(SequenceEnum.ORDERLY_SEQ.value(), SeqConstants.NABEL_SAMPLE_STRUCTURE_SEQ); return TypeUtils.stringValueOf(sequence); } public static String getApplyOrderCode(String prefix) { Object sequence = CommonApiFactory.getSequenceGenerator().generate(SequenceEnum.ORDERLY_SEQ.value(), SeqConstants.NABEL_SAMPLE_APPLY_SEQ); return…

    2024年5月25日
    2.4K00
  • 集成设计器数据流程、流程设计器可以暴露接口触发

    集成设计器数据流程暴露接口触发 需求:在集成设计器配置的连接器、数据流程链接到外部接口,需要可以有一个管理页面,统一管理这些集成配置。比如对接多个医院的挂号系统,希望可以配置好数据流程之后,能够在已经实现的开放接口上,动态的调用集成平台配置的数据流程。 连接器暴露接口触发 设计连接器资源配置模型。使用业务ID+接口唯一标识+连接器实现连接器资源和业务唯一。 @Model.model(EipConnectorResourceSetting.MODEL_MODEL) @Model(displayName = "连接器资源配置", summary = "连接器资源配置") @Model.Advanced(unique = {"hospitalId, interfaceUnique"}) public class EipConnectorResourceSetting extends IdModel { public static final String MODEL_MODEL = "hr.simple.EipConnectorResourceSetting"; @UxTableSearch.FieldWidget(@UxWidget()) @Field(displayName = "医院", required = true) @Field.Relation(relationFields = {"hospitalId"}, referenceFields = {"id"}) private Hospital hospital; @Field.Integer @Field(displayName = "医院ID", invisible = true) private Long hospitalId; @UxTableSearch.FieldWidget(@UxWidget()) @Field.String @Field(displayName = "接口唯一标识", required = true) private String interfaceUnique; @UxTableSearch.FieldWidget(@UxWidget()) @Field.many2one @Field(displayName = "连接器", required = true) @Field.Relation(relationFields = {"connectorId"}, referenceFields = {"id"}) private EipConnector connector; @Field.Integer @Field(displayName = "连接器ID", invisible = true) private Long connectorId; @UxTableSearch.FieldWidget(@UxWidget()) @Field.many2one @Field(displayName = "连接器接口", required = true) @Field.Relation(relationFields = {"integrationInterfaceId"}, referenceFields = {"id"}, domain = "connectorId==${activeRecord.connector.id}") private EipConnectorResource connectorResource; @Field.Integer @Field(displayName = "连接器接口ID", invisible = true) private Long connectorResourceId; } 开放接口定义,文档参考:https://doc.oinone.top/oio4/9326.html外部平台调用开放接口,实现动态调用连接器资源 @Model.model(HospitalSection.MODEL_MODEL) @Model(displayName = "医院科室", summary = "医院科室") public class HospitalSection extends IdModel { public static final String MODEL_MODEL = "net.example.HospitalSection"; @Field(displayName = "科室Code", required = true) private String deptCode; @Field(displayName = "科室名称", required = true) private String deptName; @Field(displayName = "科室描述") private String deptDesc; } @Slf4j @Fun @Component public class TestOpenService { @Resource private TestCommonService testCommonService;…

    2025年4月21日
    98300
  • 国际化-语言和时区设置

    国际化-翻译 1、引入翻译的包 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-translate</artifactId> </dependency> 2、默认逻辑:在系统的右上角,切换【系统语言后】,用户所选择的语言会保存到对应的用户信息中,后续所有的请求都会拿这个「语言」的值,并将其放入到PamirsSession#Lang中。3、实际项目可以通过自定义Session逻辑,根据实际业务覆盖掉默认方式,并将其设置在PamirsSession中: PamirsSession.setLang(langCode); 构建自定义Session参考:https://shushi.yuque.com/yoxz76/oio3/kg2sgr 构建自定义Session的逻辑中,根据业务逻辑把获取到的langCode设置都PamirsSession 4、目标语言编码说明语言编码必须符合ISO标准,即语言ISO代码。国际化-语言代码表-Language Codes参考下面的链接:https://blog.csdn.net/qq827245563/article/details/131552695 国际化-时区 1、引入时区的包 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-timezone</artifactId> </dependency> 2、时区设置类似语言(langCode)3、在自定义Session(与设置语言共同的Session自定义)中,根据实际业务覆盖掉默认方式,并将其设置在TimezoneSession中: pro.shushi.pamirs.timezone.session.TimezoneSession#setTimezone(TimeZone timezone) 其他说明 PamirsSession 和 TimezoneSession 都是请求级别的;即每次请求都会自动销毁; 因此在自定义Session中覆盖这两个属性的默认值的时候特别注意一下性能。

    2023年12月4日
    1.5K00
  • 【OpenGauss】后端部署使用OpenGauss高斯数据库

    Gauss数据库配置 适配版本 4.7.8.3之后的版本 配置步骤 Maven配置 去华为官网下周驱动包:gsjdbc4.jar;https://support.huaweicloud.com/mgtg-dws/dws_01_0032.html <dependency> <groupId>org.postgresql</groupId> <artifactId>gsjdbc</artifactId> <version>4</version> <scope>system</scope> <!– 下面两种方式都可以–> <systemPath>${pom.basedir}/libs/gsjdbc4.jar</systemPath> <!–<systemPath>/Users/wangxian/java-tools/guassdb/gsjdbc4.jar</systemPath>–> </dependency> 导入 scope 为 system 的包,spring 编译插件需要增加 includeSystemScope: true 配置。 <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> JDBC连接配置 pamirs: datasource: pamirs: type: com.alibaba.druid.pool.DruidDataSource driverClassName: org.postgresql.Driver url: jdbc:postgresql://127.0.0.1:5432/pamirs?currentSchema=demo username: XXXXXX password: XXXXXX initialSize: 5 maxActive: 200 minIdle: 5 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true asyncInit: true base: type: com.alibaba.druid.pool.DruidDataSource driverClassName: org.postgresql.Driver url: jdbc:postgresql://127.0.0.1:5432/pamirs?currentSchema=demo_base username: XXXXXX password: XXXXXX initialSize: 5 maxActive: 200 minIdle: 5 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true asyncInit: true 连接url配置 点击查看官方文档:官方文档 url格式 jdbc:postgresql://${host}:${port}/${database}?currentSchema=${schema} 在pamirs连接配置时,${database}和${schema}必须完整配置,不可缺省。 其他连接参数如需配置,可自行查阅相关资料进行调优。 方言配置 pamirs方言配置 pamirs: dialect: ds: base: type: GaussDB version: 5 majorVersion: 5.0.1 pamirs: type: GaussDB version: 5 majorVersion: 5.0.1 数据库版本 type version majorVersion 5.x GaussDB 5 5.0.1 PS:由于方言开发环境为5.0.1版本,其他类似版本(5.x)原则上不会出现太大差异,如出现其他版本无法正常支持的,可在文档下方留言。 schedule方言配置 pamirs: event: enabled: true schedule: enabled: true dialect: type: GaussDB version: 5 major-version: 5.0.1 type version majorVersion GaussDB 5 5.0.1 PS:由于schedule的方言在多个版本中并无明显差异,目前仅提供一种方言配置。 其他配置 逻辑删除的值配置 pamirs: mapper: global: table-info: logic-delete-value: (EXTRACT(epoch FROM CURRENT_TIMESTAMP) * 1000000 + EXTRACT(MICROSECONDS FROM CURRENT_TIMESTAMP))::bigint Gauss数据库用户初始化及授权 — init root…

    2024年3月27日
    2.9K00

Leave a Reply

Please Login to Comment