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日

相关推荐

  • 4.1.24 框架之分库分表

    随着数据库技术的发展如分区设计、分布式数据库等,业务层的分库分表的技术终将成老一辈程序员的回忆,谈笑间扯扯蛋既羡慕又自吹地说到“现在的研发真简单,连分库分表都不需要考虑了”。竟然这样为什么要写这篇文章呢?因为现今的数据库虽能解决大部分场景的数据量问题,但涉及核心业务数据真到过亿数据后性能加速降低,能给的方案都还有一定的局限性,或者说性价比不高。相对性价比比较高的分库分表,也会是现阶段一种不错的补充。言归正传oinone的分库分表方案是基于Sharding-JDBC的整合方案,所以大家得先具备一点Sharding-JDBC的知识。 一、分表(举例) 做分库分表前,大家要有一个明确注意的点就是分表字段的选择,它是非常重要的,与业务场景非常相关。在明确了分库分表字段以后,甚至在功能上都要做一些妥协。比如分库分表字段在查询管理中做为查询条件是必须带上的,不然效率只会更低。 Step1 新建ShardingModel模型 ShardingModel模型是用于分表测试的模型,我们选定userId作为分表字段。分表字段不允许更新,所以这里更新策略设置类永不更新,并在设置了在页面修改的时候为readonly package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.boot.base.ux.annotation.field.UxWidget; import pro.shushi.pamirs.boot.base.ux.annotation.view.UxForm; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.enmu.FieldStrategyEnum; @Model.model(ShardingModel.MODEL_MODEL) @Model(displayName = "分表模型",summary="分表模型",labelFields ={"name"} ) public class ShardingModel extends AbstractDemoIdModel { public static final String MODEL_MODEL="demo.ShardingModel"; @Field(displayName = "名称") private String name; @Field(displayName = "用户id",summary = "分表字段",immutable=true/* 不可修改 **/) @UxForm.FieldWidget(@UxWidget(readonly = "scene == 'redirectUpdatePage'"/* 在编辑页面只读 **/ )) @Field.Advanced(updateStrategy = FieldStrategyEnum.NEVER) private Long userId; } 图4-1-24-1 新建ShardingModel模型 Step2 配置分表策略 配置ShardingModel模型走分库分表的数据源pamirsSharding 为pamirsSharding配置数据源以及sharding规则 a. pamirs.sharding.define用于oinone的数据库表创建用 b. pamirs.sharding.rule用于分表规则配置 pamirs: load: sessionMode: true framework: system: system-ds-key: base system-models: – base.WorkerNode data: default-ds-key: pamirs ds-map: base: base modelDsMap: "[demo.ShardingModel]": pamirsSharding #配置模型对应的库 图4-1-24-2 指定模型对应数据源 pamirs: sharding: define: data-sources: ds: pamirs pamirsSharding: pamirs #申明pamirsSharding库对应的pamirs数据源 models: "[trigger.PamirsSchedule]": tables: 0..13 "[demo.ShardingModel]": tables: 0..7 table-separator: _ rule: pamirsSharding: #配置pamirsSharding库的分库分表规则 actual-ds: – pamirs #申明pamirsSharding库对应的pamirs数据源 sharding-rules: # Configure sharding rule ,以下配置跟sharding-jdbc配置一致 – tables: demo_core_sharding_model: #demo_core_sharding_model表规则配置 actualDataNodes: pamirs.demo_core_sharding_model_${0..7} tableStrategy: standard: shardingColumn: user_id shardingAlgorithmName: table_inline shardingAlgorithms: table_inline: type: INLINE props: algorithm-expression: demo_core_sharding_model_${(Long.valueOf(user_id) % 8)} props: sql.show: true 图4-1-24-3 分库分表规则配置 Step3 配置测试入口 修改DemoMenus类增加一行代码,为测试提供入口 @UxMenu("分表模型")@UxRoute(ShardingModel.MODEL_MODEL) class ShardingModelMenu{} 图4-1-24-4 配置测试入口 Step4 重启看效果 自行尝试增删改查 观察数据库表与数据分布 图4-1-24-5 自行尝试增删改查 图4-1-24-6 观察数据库表与数据分布 二、分库分表(举例) Step1 新建ShardingModel2模型 ShardingModel2模型是用于分库分表测试的模型,我们选定userId作为分表字段。分库分表字段不允许更新,所以这里更新策略设置类永不更新,并在设置了在页面修改的时候为readonly package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.boot.base.ux.annotation.field.UxWidget; import pro.shushi.pamirs.boot.base.ux.annotation.view.UxForm; import…

    2024年5月23日
    1.3K00
  • 3.5.1 构建第一个Menu

    在前面章节中我们也涉及到菜单,因为菜单我们模块就是地图、导航,没有地图、导航就无法畅游模块并进行相关业务操作。在3.3.4【模块的继承】一文关于多表继承的内容就有涉及到菜单的初始化,本文将展开介绍初始化Menu的两种方式分别是:注解式、数据初始化式。 注解式(举例) Step1 分析现有菜单注解 用@UxMenus声明DemoMenus为菜单初始化入口,同时该类在DemoModule配置扫描路径中,那么通过DemoMenus初始化的菜单都挂在demo_core这个模块上。 如果采用这种模式,建议同一个模块的菜单都只配置在一处 package pro.shushi.pamirs.demo.core.init; import pro.shushi.pamirs.boot.base.constants.ViewActionConstants; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus; import pro.shushi.pamirs.demo.api.model.*; import pro.shushi.pamirs.demo.api.proxy.PetShopProxy; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyA; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyB; @UxMenus public class DemoMenus implements ViewActionConstants { @UxMenu("商店管理")@UxRoute(PetShopProxy.MODEL_MODEL) class PetShopProxyMenu{} @UxMenu("商店管理A")@UxRoute(PetShopProxyA.MODEL_MODEL) class PetShopProxyAMenu{} @UxMenu("商店管理B")@UxRoute(PetShopProxyB.MODEL_MODEL) class PetShopProxyBMenu{} @UxMenu("商品管理")@UxRoute(PetItem.MODEL_MODEL) class ItemMenu{} @UxMenu("宠狗商品")@UxRoute(PetDogItem.MODEL_MODEL) class DogItemMenu{} @UxMenu("萌猫商品")@UxRoute(PetCatItem.MODEL_MODEL) class CatItemMenu{} @UxMenu("宠物品种")@UxRoute(PetType.MODEL_MODEL) class PetTypeMenu{} @UxMenu("萌猫品种")@UxRoute(PetCatType.MODEL_MODEL) class CatTypeMenu{} @UxMenu("宠狗品种")@UxRoute(PetDogType.MODEL_MODEL) class DogTypeMenu{} @UxMenu("宠物达人")@UxRoute(PetTalent.MODEL_MODEL) class PetTalentMenu{} } 图3-5-1-1 菜单注解 Step2 改造现有菜单注解 菜单的层级关系通过@UxMenu的嵌套进行描述 菜单点击效果有三种分别对应不同的Action的类型,关于Action的类型详见3.5.3【Action的类型】一文。 通过@UxRoute定义一个与菜单绑定的viewAction,@UxMenu("创建商店")@UxRoute(value = PetShop.MODEL_MODEL,viewName = "redirectCreatePage",viewType = ViewTypeEnum.FORM),其中viewName代表视图的name(其默认值为redirectListPage,也就是跳转到列表也),value代码视图所属模型的编码,viewType代表view类型(其默认值为ViewTypeEnum.TABLE) @UxServer定义一个与菜单绑定的serverAction,@UxMenu("UxServer")@UxServer(model = PetCatItem.MODEL_MODEL,name = "uxServer") ,其中name代表serverAction的name,model或value代码serverAction所属模型的编码 @UxLink定义一个与菜单绑定的UrlAction,@UxMenu("Oinone官网")@UxLink(value = "http://www.oinone.top”,openType= ActionTargetEnum.OPEN_WINDOW) ,其中value为跳转url,openType为打开方式默认为ActionTargetEnum.ROUTER,打开方式有以下几种 ROUTER("router", "页面路由", "页面路由") DIALOG("dialog", "页面弹窗", "页面弹窗") DRAWER("drawer", "打开抽屉", "打开抽屉") OPEN_WINDOW("openWindow", "打开新窗口", "打开新窗口") 配合菜单演示,PetCatItemAction增加一个uxServer的ServerAction package pro.shushi.pamirs.demo.core.action; ……包引用 @Model.model(PetCatItem.MODEL_MODEL) @Component public class PetCatItemAction extends DataStatusBehavior<PetCatItem> { ……省略其他代码 @Action(displayName = "uxServer") public PetCatItem uxServer(PetCatItem data){ PamirsSession.getMessageHub().info("uxServer"); return data; } } 图3-5-1-2 示例代码 新的菜单初始化代码如下 package pro.shushi.pamirs.demo.core.init; import pro.shushi.pamirs.boot.base.constants.ViewActionConstants; import pro.shushi.pamirs.boot.base.enmu.ActionTargetEnum; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxLink; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxServer; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus; import pro.shushi.pamirs.demo.api.model.*; import pro.shushi.pamirs.demo.api.proxy.PetShopProxy; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyA; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyB; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; @UxMenus public class DemoMenus implements ViewActionConstants { @UxMenu("商店") class ShopMenu{ @UxMenu("UxServer")@UxServer(model = PetCatItem.MODEL_MODEL,name = "uxServer") class ShopSayHelloMenu{ } @UxMenu("创建商店")@UxRoute(value = PetShop.MODEL_MODEL,viewName = "redirectCreatePage",viewType = ViewTypeEnum.FORM) class ShopCreateMenu{ }…

    2024年5月23日
    1.4K00
  • 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.5K00
  • 3.5.7.1 基础概念

    模块(module) 概念 在 Oinone 系统的架构中,模块(module)是核心组成元素之一,可以被理解为域(domain)的一个具象化概念。模块的来源有两种:一种是基于后端代码定义,另一种是通过无代码新增。具体的代码定义方式,请参考“[占位符]”,而无代码定义的相关信息则可在“[占位符]”找到。在 Oinone 体系中,模块对应两种实体:模块和应用。 模块: 这是一类特定能力的集合,它可以依赖其他模块,也可以被其他模块依赖。 应用: 它是一种特殊的模块,具备模块的所有特性,并在此基础上可被终端用户访问。 使用 在前端开发中,module通常以应用的形式出现,它们往往对前端用户保持透明。在接下来的讨论中,我们主要围绕应用来探讨module的使用。从应用的角度出发,我们可以在前端开发中识别出以下几种典型使用场景,并通过具体的业务案例来加以说明 应用菜单扩展: 实现自定义母版来定义特定应用的菜单 表格布局扩展: 用于自定义布局的工具,以定义特定应用的表格布局 在这些场景中,我们着重实现了应用层面的隔离,确保每个模块都能在应用的维度上独立运作 查找 在实际业务开发中,有3个方式可以找到应用 浏览器url查找(查找速度快,可能不准) 图3-5-7-1 浏览器url查找模块(module) 接口返回查找 第一步找到截图类似请求 图3-5-7-2 接口找到viewActionQuery 第二步根据返回找应用 图3-5-7-3 接口返回查找模块(module) vue调试器选中对应的组件查找 图3-5-7-4 vue调试器查找模块(module) 推荐使用浏览器url查找,若与预期不符,可用另外两种方式查找 模型(model) 概念 在 Oinone 系统的架构中,模型(model)是另一个关键核心组成部分。模型在业务层面主要体现之一为数据库的实体表,它是承载业务实现的基础结构。要了解模型的详细介绍,请参考“[占位符]”,前端所用的模型,对应后端代码定义来说,代表的是模型的编码。 关于模型的定义,我们提供了两种方法: 代码定义: 对于需要通过编程实现的模型定义,您可以参考“[占位符]”来了解具体的代码实现方法; 无代码定义:如果您倾向于使用无代码工具来定义模型,具体的操作和流程可以在“[占位符]”中找到 使用 在前端开发中,模型是前端运行的必要条件,以下场景中,模型不直接感知: 视图渲染 页面之间跳转交互 与后端交互 以下场景中,模型会直接决定前端的渲染逻辑 母版扩展:为某模型扩展母版 布局扩展:为某模型扩展布局 页面扩展:为某模型扩展个性化页面 字段扩展: 扩展字段时加上模型的范围 动作扩展: 扩展动作时加上模型的范围 以上场景中,涵盖了前端工作的方方面面,在OInone体系中,模型不止是后端运行得基础,同样也决定了前端如何运行,那这样做有什么好处呢? 前后端几乎不需要联调,联调的协议用模型来承载 前端无需定义路由、权限埋点 查找 在实际业务开发中,有3个方式可以找到模型 浏览器url查找 图3-5-7-5 浏览器url查找模型(model) 接口返回查找 第一步找到类似截图请求 图3-5-7-6 接口找到viewActionQuery 第二步根据返回找模型 图3-5-7-7 接口找到viewActionQuery vue调试器选中对应的组件查找 图3-5-7-8 vue调试器查找模型(model) 动作(action) 概念 动作(action)定义了终端用户得交互,它描述了前端与前端、前端与后端之间的交互。 动作涵盖了前端以下部分: 页面跳转(router) 调用后端接口 页内交互(打开弹窗、打开抽屉) 它有两部分的来源: 模型内定义动作 窗口动作(页面跳转、打开弹窗、打开抽屉) 服务器动作(调接口) 前端定义客户端动作,可自定义其它逻辑,例如: 把选中行的某一列数据复制一下 使用 动作的使用绝大部分的情况是由平台自动执行的,在平台执行不符合预期时可以使用自定义动作自行扩展 查找 vue调试器选中对应的组件查找 选中服务器动作(ServerAction) 图3-5-7-9 vue调试器查找服务器动作(ServerAction) 选中窗口动作(ViewAction) 图3-5-7-10 vue调试器查找窗口动作(ViewAction) 字段(field) 概念 在我们的后端模型中,字段(Field)是核心的定义元素,它们在数据库中表现为数据表的列。更重要的是,这些字段在前端应用中发挥着数据传输的关键作用。例如,当前端需要调用后端接口时,它会发送如下结构的数据: 图3-5-7-11 name字段数据举例 这里的 "name" 是一个字段实例,它连接了前后端的交互。在后端,该字段不仅用于数据存储,也参与逻辑运算。 字段在 Oinone 系统中的加强应用 在 Oinone 系统中,字段的功能得到了扩展。除了基本的前后端数据交互,字段的定义还直接影响前端的用户界面(UI)交互。例如: 前端交互组件的选择:前端交互组件的类型取决于字段的数据类型。对于 String 类型的 "name" 字段,前端会使用输入框来收集用户输入的 "张三"。 数据存储和类型定义:在后端,"name" 字段被明确定义为 String 类型,这决定了它如何存储和处理数据。 字段与前端组件定义的解耦 一个关键的设计原则是,前端组件的定义与具体的字段值或字段名(如 "name" 或 "张三")不直接相关,而是基于字段的数据类型(此例中为 String)。这种设计实现了: 前端组件的一致性:确保所有组件的输入输出遵循同一数据类型(如 String)。 高度的组件复用性:在满足 UI 要求的前提下,任何 String 类型的字段都可以使用这种通用的组件设计。 使用 Oinone 系统中的视图与字段交互的灵活性 Oinone 系统为每种视图和字段类型(Ttype)提供了默认的交互模式。这不仅保证了前端工程启动时所有界面的即时展示,也为开发者带来了高度的灵活性和扩展能力。以下是这一设计理念的关键点: 1. 视图与字段交互的默认实现 每种视图都有对应字段类型(Ttype)的默认交互实现,确保用户界面一致且直观。这使得在前端工程启动时,所有界面能够无需额外配置即可正常展示。 2. 灵活性与扩展能力 尽管系统提供了默认的交互方式,开发者仍然拥有自定义这些交互方式的能力。这意味着开发者可以根据应用需求,设计更加贴合业务逻辑和用户体验的交互模式。 3. 覆盖和替换默认组件 最为重要的是,开发者不仅可以添加新的交互方式,还可以完全覆盖和替换系统的默认组件。这提供了极大的自由度,使开发者能够根据具体场景重新设计和实现界面组件,从而达到完全定制化的用户体验。 查找 vue调试器选中对应的组件查找 图3-5-7-12 vue调试器查找字段(field) 视图类型(viewType) 概念 在 Oinone 系统中,视图是模型在前端的具体表现形式。视图的核心组成和功能如下: 1. 组成要素 字段:视图中的字段代表了模型的数据结构,它们是界面上数据显示和交互的基础。 动作:视图包含的动作定义了用户可以进行的操作,如添加、编辑、删除等。 前端UI:视图的界面设计,包括布局、元素样式等,决定了用户的交互体验。 2. 数据源与交互 数据源:视图的数据直接来源于后端模型。这意味着前端视图展示的内容是根据后端模型中定义的数据结构动态生成的。 交互:视图不仅展示数据,还提供与数据交互的能力。这些交互也是基于后端模型定义的,包括数据的增删改查等操作。 3. 灵活性 视图可以灵活选择是否采用模型的交互。这意味着开发者可以根据需求决定视图仅展示模型的数据,或者同时提供与数据的交互功能。 使用 在 Oinone 系统中,用户可以通过无代码界面设计器轻松配置视图。系统内置了以下主要视图类型: 表格(Table) 表单(Form) 详情(Detail) 搜索(Search) 画廊(Gallery) 树(Tree) 界面设计器配置…

    2024年5月23日
    1.4K00
  • 6.5 权限体系(改)

    做好企业级软件,首先得过权限这一关 在企业的IT部门沟通中,权限是避免不了。自嘲下在我们刚出来创业时,为了收获客户对我们技术能力的信任,每每与跟客户沟通时都会说我们是阿里出来的,但在权限设计这个环节不那么灵验,反而被打上了不懂B端权限设计的标签,会问很多问题。我就很奇怪难道大厂就没有内部管理系统了?大厂只有C端交易,没有B端交易?但从侧面说明权限不简单还特别重要。做好企业级软件,首先得过权限这一关。 整体介绍 对于平台运行来说,权限是必须,但我们的auth模块不是必须的,auth模块只是我们提供的一种默认实现,客户可以根据平台的spi机制进行替换。auth模块利用了平台的Hook特性做到与业务无关,在我们开发上层应用的时,是不用感知它的存在的。 auth模块涉及:功能权限、数据权限 数据权限:行权限和列权限。备注:数据权限的控制只能用于【存储模型】 表级权限:表达的语义是:是否该表可读/写(修改和新增) 列权限:表达的语义是:是否该列可读/写(修改和新增) 行权限:表达的语义是:是否对该行可读/写(修改)/删除 功能权限:表达的语义是ServerAction/Function是否可执行/展示,viewAction是否可展示,菜单是否可以显示 范围说明: 配置多个权限项的时候,取并集 配置多个角色的时候,取并集 模型设计 产品体验 Step1 创建角色 通过App Finder 切换至【权限】应用,点击新增按钮创建一个名为oinone的角色 Step2 创建数据权限项 在【权限项列表】菜单,点击【创建】按钮新增一个名为【宠物达人数据权限项】,同时宠物达人的数据权限设置为只能查看性别为男的记录 配置说明: 名称: 代表该项配置的权限的名字(必填)(必须是全系统唯一) 权限模型: 选择需要拦截的数据所在的表,即为模型,可以搜索使用 描述:对该权限项的描述 权限条件的配置: 满足全部:对条件一和条件二要同时满足的数据才能被看见 满足任一:对条件一和条件二要任意满足的数据都能被看见 读权限:对该数据是否有读取的权限 写权限:对该数据是否有修改的权限 删除权限:对该条件内的数据是否有删除的权限 Step3 为角色配置权限 编辑oinone角色,只开通oinoneDemo工程应用 选中【数据权限】选项卡点击【添加】按钮,勾选宠物达人数据权限项点击【确定】按钮 整体点击保存,回到列表页记得点击【权限生效】按钮 Step4 新建用户绑定角色 切换到用户中心模块,点击【创建】按钮填写必要信息,并在角色选中oinone权限组。 退出admin用户,用oinone登陆,权限效果: 只能看见demo模块 oinone登陆只能看到性别为男的宠物达人记录 admin用户登陆 oinone用户登陆 因为宠物达人的页面没有把性别字段放出来,我们看下数据库数据 auth模块扩展 在日常项目开发中,难免会碰到一些针对权限管理的特殊需求,或是为提升性能做的特殊逻辑。接下来我给大家介绍auth模块扩展性。 权限全局配置 对所有权限角色都做限制,而且不想让用户感知,可以实现PermissionFunApi接口,API接口实现的配置方式【只能用于支持全局的数据权限配置】 实现接口PermissionFunApi 将实现托管给SpringAOP 接口的具体实现看下图的代码 package pro.shushi.pamirs.demo.core.service; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.service.PermissionFunApi; import pro.shushi.pamirs.demo.api.model.PetTalent; @Component public class PetTalentPermissionFunApi implements PermissionFunApi { @Override public String permissionDomain(Object… args) { //获取当前组织中 return "name == '张学友'"; } @Override public String nameSpace() { return PetTalent.MODEL_MODEL; } } 不参与权限控制 如果某一接口不想做权限控制,则可以在启动工程的application-dev.yml文件中配置不需要权限过滤的接口 pamirs: auth: fun-filter: – namespace: demo.PetTalent fun: queryPage 换一个没有配置宠物达人权限的用户(除管理员以外)进入系统,则也可以看到数据。注意【权限全局控制】还是生效的 API 1. 获取当前用户对该模型的行权限 Result<String> result = CommonApiFactory.getApi(AuthApi.class).canReadAccessData("Model"); 返回值为 { 'data':"name=in=('hahaha')" 'success':true … } 用法 : 场景:前端发起的请求都会经过权限拦截,后端代码逻辑发起的数据请求都是不经过任何权限的过滤,但是某些特殊情况需要在后端代码逻辑发起的数据请求也带上权限过滤 入参:请求的模型 出参:Result 数据结构中data会存储一段字符串,该字符串为Rsql 将该Rsql追加到wrapper中 Result<String> result = CommonApiFactory.getApi(AuthApi.class).canReadAccessData("base.UeModule"); String data=result.getData(); QueryWrapper<UeModule> wrapper = Pops.query(); wrapper.setEntity(ueModule); if (!StringUtils.isBlank(data)) { wrapper.apply(data); }

    2024年5月23日
    1.3K00

Leave a Reply

登录后才能评论