3.4.3.3 SPI机制-扩展点

扩展点结合拦截器的设计,oinone可以点、线、面一体化管理Function

扩展点用于扩展函数逻辑。扩展点类似于SPI机制(Service Provider Interface),是一种服务发现机制。这一机制为函数逻辑的扩展提供了可能。

一、构建第一个扩展点

自定义扩展点(举例)

在我们日常开发中,随着对业务理解的深入,往往还在一些逻辑中会预留扩展点,以便日后应对不同需求时可以灵活替换某一小块逻辑。

在3.3.4【模型的继承】一文中的PetCatItemQueryService,是独立新增函数只作公共逻辑单元。现在我们给它的实现类增加一个扩展点。在PetCatItemQueryServiceImpl的queryPage方法中原本会先查询PetCatType列表,我们这里假设这个逻辑随着业务发展未来会发生变化,我们可以预先预留【查询萌猫类型扩展点】

Step1 新增扩展点定义PetCatItemQueryCatTypeExtpoint

  1. 扩展点命名空间:在接口上用@Ext声明扩展点命名空间。会优先在本类查找@Ext,若为空则往接口向上做遍历查找,返回第一个查找到的@Ext.value注解值,使用该值再获取函数的命名空间;如果未找到,则返回扩展点全限定类名。所以我们这里扩展点命名空间为:pro.shushi.pamirs.demo.api.extpoint.PetCatItemQueryCatTypeExtpoint

  2. 扩展点技术名称:先取@ExtPoint.name,若为空则取扩展点接口方法名。所以我们这里技术名为queryCatType

package pro.shushi.pamirs.demo.api.extpoint;

import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

import java.util.List;

@Ext
public interface PetCatItemQueryCatTypeExtpoint {

    @ExtPoint(displayName = "查询萌猫类型扩展点")
    List<PetCatType> queryCatType();

}

图3-4-3-11 新增扩展点定义PetCatItemQueryCatTypeExtpoint

Step2 修改PetCatItemQueryServiceImpl(用Ext.run模式调用)

修改queryPage,增加扩展点的使用代码。扩展点的使用有两种方式

方法一,使用命名空间和扩展点名称调用Ext.run(namespace, fun, 参数);

方法二,使用函数式接口调用Ext.run(函数式接口, 参数);

我们这里用了第二种方式

  1. 用PetCatItemQueryCatTypeExtpoint的全限定类名作为扩展点的命名空间(namespace)

  2. 用queryCatType的方法名作为扩展点的技术名称(name)

  3. 根据namespace+name去找到匹配扩展点实现,并根据规则是否匹配,以及优先级唯一确定一个扩展点实现去执行逻辑

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

……省略依赖包

@Model.model(PetCatItem.MODEL_MODEL)
@Component
public class PetCatItemAction extends DataStatusBehavior<PetCatItem> {

    @Override
    protected PetCatItem fetchData(PetCatItem data) {
        return data.queryById();
    }
    @Action(displayName = "启用")
    public PetCatItem dataStatusEnable(PetCatItem data){
        data = super.dataStatusEnable(data);
        data.updateById();
        return data;
    }

    @Function.Advanced(displayName = "查询模型数据的默认过滤条件", type = FunctionTypeEnum.QUERY, managed = true)
    @Function(openLevel = {LOCAL})
    public String queryFilters() {
        StringBuilder sqlWhereCondition = new StringBuilder();
//        List<PetCatType> typeList = new PetCatType().queryList();
        List<PetCatType> typeList = Ext.run(PetCatItemQueryCatTypeExtpoint::queryCatType, new Object[]{});
        if(!CollectionUtils.isEmpty(typeList)){
//          sqlWhereCondition.append("type_id");
            sqlWhereCondition.append(PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(PetCatItem::getTypeId)));
            sqlWhereCondition.append(StringUtils.SPACE).append(SqlConstants.IN).append(CharacterConstants.LEFT_BRACKET);
            for(PetCatType petCatType: typeList){
                sqlWhereCondition.append(petCatType.getId()).append(CharacterConstants.SEPARATOR_COMMA);
            }
            sqlWhereCondition.deleteCharAt(sqlWhereCondition.lastIndexOf(CharacterConstants.SEPARATOR_COMMA));
            sqlWhereCondition.append(StringUtils.SPACE).append(CharacterConstants.RIGHT_BRACKET);
        }
        return sqlWhereCondition.toString();
    }

    ……省略其他函数
}

图3-4-3-12 修改PetCatItemQueryServiceImpl

Step3 新增扩展点实现PetCatItemQueryCatTypeExtpointOne

  1. 扩展点命名空间要与扩展点定义一致,用@Ext(PetCatItemQueryCatTypeExtpoint.class)

  2. @ExtPoint.Implement声明这是在@Ext声明的命名空间下,且技术名为queryCatType的扩展点实现

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

import pro.shushi.pamirs.demo.api.extpoint.PetCatItemQueryCatTypeExtpoint;
import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.meta.api.session.PamirsSession;

import java.util.List;

@Ext(PetCatItemQueryCatTypeExtpoint.class)
public class PetCatItemQueryCatTypeExtpointOne implements PetCatItemQueryCatTypeExtpoint {

    @Override
    @ExtPoint.Implement(displayName = "查询萌猫类型扩展点的默认实现")
    public List<PetCatType> queryCatType() {
        PamirsSession.getMessageHub().info("走的是第一个扩展点");
        List<PetCatType> typeList = new PetCatType().queryList();
        return typeList;
    }
}

图3-4-3-13 新增扩展点实现PetCatItemQueryCatTypeExtpointOne

Step4 重启看效果

  1. 萌猫商品-列表页面的逻辑没有变化正常,说明typeList从扩展点中是取到了

image.png

图3-4-3-14 示例效果

  1. 用Insomnia直接发起GraphQL请求,返回结果里可以明确知道这是扩展点实现【PetCatItemQueryCatTypeExtpointOne】执行的结果

image.png

图3-4-3-15 示例效果

Step5 自行测试扩展点的优先级

附上第二个扩展点实现的代码,快去试试吧

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

import pro.shushi.pamirs.demo.api.extpoint.PetCatItemQueryCatTypeExtpoint;
import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.meta.api.session.PamirsSession;

import java.util.List;

@Ext(PetCatItemQueryCatTypeExtpoint.class)
public class PetCatItemQueryCatTypeExtpointTwo implements PetCatItemQueryCatTypeExtpoint {

    @Override
    @ExtPoint.Implement(priority = 95,displayName = "查询萌猫类型扩展点的实现,优先级取胜")
    public List<PetCatType> queryCatType() {
        PamirsSession.getMessageHub().info("走的是第二个扩展点");
        List<PetCatType> typeList = new PetCatType().queryList();
        return typeList;
    }
}

图3-4-3-16 测试扩展点的优先级(第二个扩展点实现代码)

默认扩展点(举例)

由前端直接发起调用oinone后端Function(能被前端直接发起的Function前提是namespace挂在模型上),当前端通过GraphQL发起对函数的请求是,oinone都会默认执行三个内置扩展点分别是前置扩展点、覆盖扩展点和后置扩展点。

默认扩展点与函数的关联关系

扩展点扩展的函数与扩展点通过扩展点的命名空间和技术名称关联。扩展点与所扩展函数的命名空间一致。前置扩展点、重载扩展点和后置扩展点的技术名称的规则是所扩展函数的函数编码fun加上“Before”、“Override”和“After”后缀;方法体内调用扩展点直接使用接口调用,所以技术名称可以任意定义,只需要在同一命名空间下唯一即可。

我们在3.3.4【模型继承】一文中关于多表继承的内容有提到过通过实现扩展点来保证子模型与父模型数据同步。此次列子中我们来替换下PetShop的sayHello函数

Step1 新增扩展点定义PetShopSayhelloOverrideExtpoint

package pro.shushi.pamirs.demo.api.extpoint;

import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

@Ext(PetShop.class)
public interface PetShopSayhelloOverrideExtpoint {

    @ExtPoint(displayName = "覆盖PetShop的sayHello执行逻辑")
    public PetShop sayHelloOverride(PetShop shop);

}

图3-4-3-17 新增扩展点定义PetShopSayhelloOverrideExtpoint

Step2 新增扩展点实现PetShopSayhelloOverrideExtpointImpl

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

import pro.shushi.pamirs.demo.api.extpoint.PetShopSayhelloOverrideExtpoint;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.meta.api.session.PamirsSession;

@Ext(PetShop.class)
public class PetShopSayhelloOverrideExtpointImpl implements PetShopSayhelloOverrideExtpoint {

    @ExtPoint.Implement(displayName = "覆盖PetShop的sayHello执行逻辑")
    public PetShop sayHelloOverride(PetShop shop){
        PamirsSession.getMessageHub().info("OverrideExtpoint Hello:"+shop.getShopName());
        return shop;
    }
}

图3-4-3-18 新增扩展点实现PetShopSayhelloOverrideExtpointImp

Step3 确保PetShop的sayHello函数存在

详见3.4.1【构建第一个Function】一文

Step4 重启查看效果

image.png

图3-4-3-19 示例效果

二、总结

oinone用默认扩展点为Function提供三种默认扩展点,并通过自定义扩展点在Function逻辑内部任意插入扩展点,让Function作为oinone的逻辑管理单元的可管理性大大提升。同时结合拦截器的设计,oinone可以点、线、面一体化管理Function

注:默认扩展点,不是由前端发起而是后端编程调用,默认不会生效,如果要生效请参考4.1.9【函数之元位指令】的一文

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

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

(0)
史, 昂的头像史, 昂数式管理员
上一篇 2024年5月23日
下一篇 2024年5月23日

相关推荐

  • 3.5.7.4 自定义页面

    页面是什么 在Oinone前端体系中,页面是一个核心概念,它代表着终端用户所看到的当前视图。这个视图可以有多种形式,主要取决于页面是如何定义和构建的。在深入理解页面之前,我们需要了解两个关键的功能:自定义布局 和 自定义母版。 作用场景 自定义布局 提供了布局调整的强大功能,但在某些情况下,它可能无法完全满足特定的需求。这时,自定义页面就显得尤为重要。自定义页面是对 自定义布局 的补充,允许开发者从更深层次自由地控制和设计用户界面。 当标准布局无法实现所需的视觉效果或功能时,自定义页面提供了更高的灵活性。开发者可以通过自定义页面来实现独特的布局设计,添加特定的交互元素,或者整合复杂的业务逻辑,以创造独特且丰富的用户体验。 自定义页面 自定义视图组件允许开发者定义和使用特定于业务需求的视图布局。下面是一个具体的示例,展示了如何定义、注册和使用通过 setComponent 结合 TypeScript 和 Vue 的自定义视图组件。 示例工程目录 以下是需关注的工程目录示例,main.ts更新导入./view: 图3-5-7-48 自定义页面工程目录示例 1. 定义 TypeScript 组件 首先,我们定义了一个名为 CustomViewWidget 的 TypeScript 组件,并在该组件中通过 setComponent 结合 Vue 单文件组件。 import { BaseElementWidget, BaseElementViewWidget, SPI, ViewWidget } from '@kunlun/dependencies'; import CustomViewVue from './CustomView.vue'; @SPI.ClassFactory(BaseElementWidget.Token({ widget: 'CustomViewWidget' })) export class CustomViewWidget extends BaseElementViewWidget { public initialize(props) { super.initialize(props); this.setComponent(CustomViewVue); return this; } } 图3-5-7-49 定义TypeScript组件代码示例 2. Vue 单文件组件 其次,我们创建了对应的 Vue 单文件组件 CustomView.vue,用于展示自定义视图的具体内容。 <template> <div class="custom-view-wrapper"> <h1>自定义视图</h1> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ inheritAttrs: false, name: 'ViewComponentVue' }); </script> <style lang="scss"> .custom-view-wrapper {} </style> 图3-5-7-50 定义Vue组件代码示例 3. 注册自定义视图布局 接下来,我们使用 registerLayout 函数注册了一个表格视图布局,并在其中引入了通过 setComponent 结合的自定义视图组件。 import { registerLayout, ViewType } from "@kunlun/dependencies"; export const registerCustomView = () => { registerLayout( ` <view type="TABLE"> <element widget="CustomViewWidget" /> </view> `, { viewType: ViewType.Table, moduleName: 'resource', model: 'resource.ResourceCountryGroup' } ); }; registerCustomView(); 图3-5-7-51 注册自定义视图布局代码示例 效果 图3-5-7-52 自定义页面效果示例 4. 自定义视图在表格中的应用 当我们注册了自定义视图后,它就可以在表格视图中被使用。在表格视图的布局中,我们通过 标签将自定义视图嵌套在表格中,从而覆盖了表格的默认布局 5. 入参一致性 值得强调的是,registerLayout 函数和自定义布局的规则是一致的,这意味着开发者可以在自定义布局中使用与 registerLayout 相同的入参规则,从而实现更加灵活和统一的视图布局设计 与内置组件结合 1. 注册视图元素布局 首先,我们使用 registerLayout 函数注册了一个表格视图的布局。这个布局包含了搜索框、操作栏、以及一个自定义视图组件。 import { registerLayout, ViewType } from "@kunlun/dependencies"; import { CustomViewWidget } from…

    2024年5月23日
    1.5K00
  • 5.2 CDM之工程模式

    两种工程模式介绍 oinone推荐的两种工程模式都保留互联网特性,如跟业务无关的基础平台还是采用平台化思路建设。二种侧重点差异如下 第一种:比较适合企业采用多供应商联合开发场景,先以业务区分,各个业务线有独立的领域平台,最大限度保持不同业务线的独立性,有利于各个业务线独立发展(目前oinone上层星空系列产品采用这种工程模式,因为我们期望的时候帮助企业构建软件生态,必然要考虑不同供应商联合开发场景) 第二种:比较接近传统互联网架构,先按平台领域区分,如商品领域:商品平台做总工程,但里面按业务区分模块分子工程来保持业务相互独立,相对于第一种把领域的代码放一起,带来好处强化大家思考模型通用性。但不适用于跨公司主体间配合。 图5-2-1 Oinone-CDM的两种工程模式 注意事项: oinone兼容传统互联网架构 不管哪种模式,都需要解决CDM的维护问题 CDM维护的常见问题: Q:CDM层缺少模型怎么办? A:CDM层模型是逐步完善和丰富的。如果是特定业务自己需要的模型,这类模型无通用性。则加到自己的工程中;如果是通用的,则架构组确定是否需要纳入到CDM。 Q:CDM层已有的模型缺少字段怎么办? A:CDM层模型的字段也是逐步完善和丰富的,通用的字段在架构组确定后也会被吸收进来 Q:CDM层不同业务线相互影响怎么办? A:扩展字段最好带上自有前缀标志,如果觉得通用则提交架构组走模型缺少字段加入 Q:CDM层某模型新增加了的字段,但原先业务线已经加了相同含义字段 A:业务线可以把自己的字段related到CDM增加的新字段,并做数据迁移

    2024年5月23日
    1.3K00
  • 文件

    文件应用下包含“导入/导出模版、导入任务、导出任务”三个菜单。其中导入/导出任务菜单比较常用。 导入/导出模版 当前版本会为租户的表格视图自动创建导出模版,此处可进行编辑、查看详情的操作。 导入任务 导入任务可以下载导入文档,点击详情可以查看该条记录的导入结果,任务信息分组中可以查看错误信息。 导出任务 和导入任务一致,导出任务菜单中可以下载导出文档,点击详情可以查看该条记录的导出结果,任务信息分组中可以查看错误信息。

    2024年6月20日
    1.0K00
  • 2.1 数字化时代软件业的另一个本质变化

    随着企业从信息化向数字化转变,软件公司提供的产品也由传统的企业管理软件向企业商业支撑软件发展。这一变化带来了许多技术上的挑战和机遇。在之前的章节中,我们提到企业的视角已经从内部管理转向业务在线和生态在线协同,这也带来了一系列新的需求。但是,我们常常会忽视这一变化所带来的对系统要求的变化。在本章中,我们将探讨这些技术上的变化,以及这些变化所带来的机遇和挑战。 图2-1 从信息化到数字化软件本质变化 在信息化时代,企业的业务围绕着内部管理效率展开,借鉴国外优秀的管理经验,企业将其管理流程固化下来,典型的例子是ERP项目。这类项目上线后往往长期稳定,不轻易更改,因此信息化时代软件的技术流派侧重于通过模型对业务进行全面支持。例如,SAP具有丰富的配置能力,将已有企业管理思想抽象到极致。其功能基本上可以通过配置来实现,因此其模型设计特别复杂。但是,我们也应该清楚地了解到,配置是面向已知问题的。在数字化时代,创新和业务迭代速度非常快,这种方法可能就不太适合了。我们知道,模型抽象是在设计时具有前瞻性的,一旦不适合,修改起来就会异常困难。 随着数字化时代的到来,企业主的关注点已经从单一企业内部管理转变为了围绕企业上下游价值链的协同展开。这种变化给企业信息化系统提出了更高的要求,例如业务需求的响应速度、系统性能和用户体验等方面。现在,企业对软件不仅是管理需求的承载,更是业务在线化的承载。传统的重模型设计软件模式已经不再适用,因为业务本身不断创新和变化。因此,数字化时代需要新的软件技术流派,这种流派必须是轻模型加上低代码技术的结合体。通过模型抽象80%的通用场景,剩余的20%个性化需求可以通过技术手段来完成。这样的设计可以让每家企业的研发人员轻松理解模型,而不像ERP模型那样异常复杂,无法进行修改。此外,配合低代码技术可以快速研发和上线。如果说配置化是面向已知问题的,那么低代码就是面向未知问题设计的。虽然低代码的概念可以追溯到上个世纪80年代,当时是为了满足企业内部部门之间有协同需求,但又没有专业软件支撑,定制化开发又不划算的辅助场景。但现在它的核心原因是企业数字化的核心场景不稳定,变化很快,每家企业都有强烈的个性化需求。因此,低代码成为解决这些问题的核心手段,数字化时代的低代码需要具备处理复杂场景的能力,而不仅仅是围绕着内部管理展开。 企业在数字化转型的过程中需要考虑到不仅是成熟的全链路业务解决方案,还要应对数字化场景的快速变化和持续创新的需求。为此,Oinone打造了一站式低代码商业支撑平台,从业务与技术两个维度来帮助企业建立开放、链接、安全的数字化平台。这将在水平和垂直两个维度上全面推动企业数字化转型。 另外,低代码的另一个好处是完成了软件本身的数字化建设。通过基于元数据设计,元数据成为软件中数据、逻辑和交互的数据,软件结合AI可以有更多的创造可能。想象一下,AI了解软件的元数据后可以自我运作,人在极少情况下才需要参与,人机交互也会发生大的改变。未来的软件交互不再需要研发提前预设,而是能够实现用户所需即所呈现的效果。作为一家帮助企业进行数字化转型的软件公司,请问您的数字化转型是否已经完成呢?

    2024年5月23日
    1.3K00

Leave a Reply

登录后才能评论