Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9425.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验
Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9425.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验
1. 功能说明 页面设计时界面设计器中「页面」模块的设计入口,在这个界面,进行页面的设置、搭建、设计、排版。 主要分为顶部操作栏、左侧工具栏、中部设计画布区、右侧属性面板。 2. 操作栏 进入页面设计,顶部显示了页面的标题,以及返回、发布等操作。 2.1 发布 页面设计完成后,点击「发布」运行页面生效。若不点击发布,页面也有自动保存的功能,但在发布前,自动保存也等同于草稿,不会正式生效。 发布时如果有属性不符合校验规则(必填的属性未填、输入的内容校验不通过),会发布失败,相应的字段会特殊标记,需要查看并修改属性。 2.2 显示/隐藏母版 进入页面设计时默认不展示母版,可以手动操作显示母版 3. 工具栏 左侧的工具栏中包括组件库、页面设置等模块。 3.1 组件库 组件库中包含组件和模型,组件是当前设计器支持的所有组件,模型是页面所在模型下的所有字段和动作。 3.1.1 组件 组件中展示了系统支持的所有组件。包含 1)布局类组件,如分组、选项卡等,使用布局组件可以将页面进字段分类、分页; 2)字段类组件,如单行文本、整数、日期等等,使用字段类组件时都会在模型下对应创建一个字段; 3)动作类组件,如跳转动作、提交动作、链接动作等。 3.1.2 模型 组件库顶部,由组件可切换为模型:模型选项下,会展示当前模型的所有字段,以及系统默认动作。可以直接拖拽字段至设计画布中,会应用形成某个组件,可对组件进行多样的属性设置,优化交互。 3.1.2 组件和模型有什么区别 1展示内容维度不同 组件中展示的内容是组件信息,如分组、选项卡、单行文本、文件上传等;模型中展示的是模型下已有的所有字段。 2使用功能不同 组件中的组件使用前需要在模型中创建一个字段,当然,创建好的字段也会存在于模型中;模型中的字段可直接使用,并且使用时会在设计画布中对应生成个默认组件。 3使用场景不同 如果模型中已经存在目标字段,应直接选择从模型中拖拽字段;如果模型中没有需要的字段,可以在页面中增加一个组件,实际上也是在新增一个字段。 3.2 页面设置 页面设置中可以修改当前页面的标题、分组、页面描述,同时也是给页面上传缩略图的唯一入口。 4. 设计画布 将组件或字段拖拽至设计画布区,会生成样式。点击组件,右侧可对其进行设置,大部分属性可实时在画布中展示效果。 5. 属性面板 右侧属性面板抽屉中可以设置属性或查看字段信息,通过不同的属性配置化实现组件的多样化。 5.1 属性 属性中包括基本信息(如标题、占位提示、描述说明等)、校验信息(如是否必填、长度校验等)、交互信息(如排序方式、是否展示计数器等) 5.2 字段 首次从组件中拖拽,会先展示字段信息,并且是需要先创建一个字段,字段创建成功后,再次切换字段选项,只读展示当前组件所对应的字段基本信息。同理,如果是直接从字段中拖拽,字段选项中,只读展示当前字段基本信息。
枚举是大家在系统开发中经常用的一种类型,在oinone中也对枚举类型进行了支持,同时也做了相应的加强。希望通过本文能让大家对枚举的使用,有更全面的认知 一、枚举系统与数据字典 枚举是列举出一个有穷序列集的所有成员的程序。在元数据中,我们使用数据字典进行描述。 协议约定 枚举需要实现IEnum接口和使用@Dict注解进行配置,通过配置@Dict注解的dictionary属性来设置数据字典的唯一编码。前端使用枚举的displayName来展示,枚举的name来进行交互;后端使用枚举的value来进行交互(包括默认值设置也使用枚举的value)。 枚举会存储在元数据的数据字典表中。枚举分为两类:1.异常类;2.业务类。异常类枚举用于定义程序中的错误提示,业务类枚举用于定义业务中某个字段值的有穷有序集。 编程式用法 图3-3-6-1 编程式用法 如果一个字段的类型被定义为枚举,则该字段就可以使用该枚举来进行可选项约束(options)。该字段的可选项为枚举所对应数据字典的子集。 可继承枚举 继承BaseEnum可以实现java不支持的继承枚举。同时可继承枚举也可以用编程式动态创建枚举项。 可继承枚举也可以兼容无代码枚举。 图3-3-6-2 可继承枚举 二进制枚举 可以通过@Dict注解设置数据字典的bit属性或者实现BitEnum接口来标识该枚举值为2的次幂。 二、enum不可继承枚举(举例) 我们在介绍抽象基类中AbstractDemoCodeModel和AbstractDemoIdModel就引入了数据状态(DataStatusEnum)字段,并设置了必填和默认值为DISABLED。DataStatusEnum实现了IEnum接口,并用@Dict(dictionary = DataStatusEnum.dictionary, displayName = "数据状态")进行了注解。为什么不能继承呢?因为JAVA语言的限制导致enum是不可继承的 package pro.shushi.pamirs.core.common.enmu; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.IEnum; @Dict(dictionary = DataStatusEnum.dictionary, displayName = "数据状态") public enum DataStatusEnum implements IEnum<String> { DRAFT("DRAFT", "草稿", "草稿"), NOT_ENABLED("NOT_ENABLED", "未启用", "未启用"), ENABLED("ENABLED", "已启用", "已启用"), DISABLED("DISABLED", "已禁用", "已禁用"); public static final String dictionary = "partner.DataStatusEnum"; private String value; private String displayName; private String help; DataStatusEnum(String value, String displayName, String help) { this.value = value; this.displayName = displayName; this.help = help; } public String getValue() { return value; } public String getDisplayName() { return displayName; } public String getHelp() { return help; } } 图3-3-6-3 不可继承枚举 三、BaseEnum可继承枚举(举例) Step1 新增CatShapeExEnum继承CatShapeEnum枚举 package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; @Dict(dictionary = CatShapeExEnum.DICTIONARY,displayName = "萌猫体型Ex") public class CatShapeExEnum extends CatShapeEnum { public static final String DICTIONARY ="demo.CatShapeExEnum"; public final static CatShapeExEnum MID =create("MID",3,"中","中"); } 图3-3-6-4 新增CatShapeExEnum继承CatShapeEnum枚举 Step2 修改PetCatType的shape字段类型为CatShapeExEnum package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.CatShapeExEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.MultiTableInherited(type = PetCatType.KIND_CAT) @Model.model(PetCatType.MODEL_MODEL) @Model(displayName="萌猫品种",labelFields = {"name"}) public class PetCatType extends PetType { public static final String MODEL_MODEL="demo.PetCatType"; public static final String KIND_CAT="CAT"; @Field(displayName = "宠物分类",defaultValue…
做好企业级软件,首先得过权限这一关 在企业的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); }
了解oinone的启动生命周期过程,对于理解oinone或者开发高级功能都有非常大的帮助 一、生命周期大图 图4-1-3-1 生命周期大图 二、平台扩展说明 平台节点通过SPI机制进行扩展,本书籍暂不展开,更多详情请见可关注数式Oinone公众号中的Oinone内核揭秘系列文章。 三、业务扩展说明 接口 说明 使用场景 LifecycleBeginAllInit 系统进入生命周期前置逻辑注:不能有任何数据库操作 系统级别的信息收集上报 LifecycleCompletedAllInit 系统生命周期完结后置逻辑 系统级别的信息收集上报、生命周期过程中的数据或上下文清理 LifecycleBeginInit 模块进入生命周期前置逻辑注:不能有任何数据库操作 预留,能做的事情比较少 LifecycleCompletedInit 模块生命周期完结后置逻辑 本模块需等待其他模块初始化完毕以后进行初始化的逻辑。比如:1.集成模块的初始化2.权限缓存的初始化…… MetaDataEditor 元数据编辑注:不能有任何数据库操作 这个在第3章Oinone的基础入门中已经多次提及,核心场景是向系统主动注册如Action、Menu、View等元数据 ExtendBuildInit 系统构建前置处理逻辑 预留,能做的事情比较少,做一些跟模块无关的事情 ExtendAfterBuilderInit 系统构建后置处理逻辑 预留,能做的事情比较少,做一些跟模块无关的事情 InstallDataInit 模块在初次安装时的初始化逻辑 根据模块启动指令来进行选择执行逻辑,一般用于初始化业务数据。应用启动参数与指令转化逻辑详见4.1.2【模块之启动指令】一文 UpgradeDataInit 模块在升级时的初始化逻辑注:根据启动指令来执行,是否执行一次业务自己控制 ReloadDataInit 模块在重启时的初始化逻辑注:根据启动指令来执行,是否执行一次业务自己控制 表4-1-3-1 业务拓展说明 四、常用生命周期举例 Install\Upgrade\Reload的业务初始化(举例) Step1 新建DemoModuleBizInit DemoModuleBizInit实现InstallDataInit, UpgradeDataInit, ReloadDataInit a. InstallDataInit 对应 init b. UpgradeDataInit 对应 upgrade c. ReloadDataInit 对应 reload modules方法代表改初始化类与哪些模块匹配,以模块编码为准 priority 执行优先级 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.api.init.InstallDataInit; import pro.shushi.pamirs.boot.common.api.init.ReloadDataInit; import pro.shushi.pamirs.boot.common.api.init.UpgradeDataInit; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.api.enumeration.DemoExpEnumerate; import pro.shushi.pamirs.meta.common.exception.PamirsException; import java.util.Collections; import java.util.List; @Component public class DemoModuleBizInit implements InstallDataInit, UpgradeDataInit, ReloadDataInit { @Override public boolean init(AppLifecycleCommand command, String version) { throw PamirsException.construct(DemoExpEnumerate.SYSTEM_ERROR).appendMsg("DemoModuleBizInit: install").errThrow(); //安装指令执行逻辑 // return Boolean.TRUE; } @Override public boolean reload(AppLifecycleCommand command, String version) { throw PamirsException.construct(DemoExpEnumerate.SYSTEM_ERROR).appendMsg("DemoModuleBizInit: reload").errThrow(); //重启指令执行逻辑 // return Boolean.TRUE; } @Override public boolean upgrade(AppLifecycleCommand command, String version, String existVersion) { throw PamirsException.construct(DemoExpEnumerate.SYSTEM_ERROR).appendMsg("DemoModuleBizInit: upgrade").errThrow(); //升级指令执行逻辑 // return Boolean.TRUE; } @Override public List<String> modules() { return Collections.singletonList(DemoModule.MODULE_MODULE); } @Override public int priority() { return 0; } } 图4-1-3-2 新建DemoModuleBizInit Step2 重启看效果 启动指令为-Plifecycle=INSTALL,转化指令为 install为AUTO;upgrade为FORCE 因为DemoModule我们已经执行过好多次了,所以会进入upgrade逻辑。系统重启的效果跟我们预期的结果一致,确实执行了DemoModuleBizInit的upgrade方法 图4-1-3-3 系统重启执行DemoModuleBizInit的upgrade方法 MetaDataEditor 回顾使用情况 最早在3.3.2【模型的类型】一文中介绍“传输模型”时,初始化ViewAction窗口动作时使用到,这里不过多介绍。下面主要介绍下InitializationUtil的工具类包含方法。 注:模块上报元数据只能通过注解或者实现MetaDataEditor接口并使用InitializationUtil工具来进行,更建议用注解方式