4.1.13 Action之校验

在3.5.3【Action的类型】一文中有涉及到“ServerAction之校验”部分,本文介绍一个特殊的写法,当内置函数和表达式不够用的时候,怎么扩展。还是拿PetShopProxyAction举例,修改如下:

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

……引依赖类

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

……其他代码

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

    @Function
    public Boolean checkName(PetShopProxy data) {
        String field = "name";
        String name = data.getShopName();
        boolean success = true;
        if (StringUtils.isBlank(name)) {
            PamirsSession.getMessageHub()
                    .msg(Message.init()
                            .setLevel(InformationLevelEnum.ERROR)
                            .setField(field)
                            .setMessage("名称为必填项"));
            success = false;
        }
        if (name.length() > 128) {
            PamirsSession.getMessageHub()
                    .msg(Message.init()
                            .setLevel(InformationLevelEnum.ERROR)
                            .setField(field)
                            .setMessage("名称过长,不能超过128位"));
            success = false;
        }
        return success;
    }

……其他代码

}

图4-1-13-1 PetShopProxyAction扩展配置

注:

  1. check属性指定了校验函数名称,命名空间必须与服务器动作一致。

  2. 校验函数的入参必须与服务器动作一致

  3. 使用PamirsSession#getMessageHub方法可通知前端错误的属性及需要展示的提示信息,允许多个。

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

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

Like (0)
史, 昂's avatar史, 昂数式管理员
Previous 2024年5月23日 am8:50
Next 2024年5月23日

相关推荐

  • 3.4.2 函数的开放级别与类型

    一、函数开放级别 我们在日常开发中通常会因为安全性,为方法定义不同的开放层级,或者通过应用分层把需要对web开放的接口统一定义在一个独立的应用中。oinone也提供类似的策略,所有逻辑都通过Function来归口统一管理,所以在Function是可以定义其开放级别有API、REMOTE、LOCAL三种类型,配置可多选。 四种自定义新增方式与开放级别的对应关系 函数 本地调用(LOCAL) 远程调用(REMOTE) 开放(API) 伴随模型新增函数 支持 支持【默认】 支持 独立新增函数绑定到模型 支持 支持【默认】 支持 独立新增函数只作公共逻辑单元 支持 支持【默认】 伴随ServerAction新增函数 必选 表3-4-2-1 四种自定义新增方式与开放级别的对应关系 远程调用(REMOTE) 如果函数的开放级别为本地调用,则不会发布远程服务和注册远程服务消费者 非数据管理器函数 提供者:如果函数定义在当前部署包的启动应用中,则主动发布远程服务提供者。 消费者:如果函数定义在部署依赖包中但未在当前部署包的启动应用中,则系统会默认注册远程消费者。发布注册的远程服务使用命名空间和函数编码进行路由。 所以非数据管理器函数的消费者并不需要感知该服务是否是本地提供还是远程提供。而服务提供者也不需要手动注册远程服务。 数据管理器类函数 提供者:如果数据管理器函数所在模型定义在当前部署包的启动应用中,则系统会主动发布数据管理器的远程服务作为数据管理器的远程服务提供者; 消费者:如果模型定义在部署依赖包中但未在当前部署包的启动应用中,则系统会主动注册数据管理器的远程服务消费者。 所以数据管理器类函数的消费者与服务提供者并不需要感知函数的远程调用。 二、函数类型 函数的类型语义分为:增、删、改、查,在编程模式下目前用于Function为API级别,生成GraphQL的Schema时放在query还是mutation。查放在query,其余放mutation。 三、函数分类 TBD 在无代码编辑器中,函数分类主要用函数选择的分类管理。

    Oinone 7天入门到精通 2024年5月23日
    1.8K00
  • 5.5 基础支撑之结算域

    一、基础介绍 随着企业的业务不断进行数字化改造、业务越来越在线化,给企业财务工作带来几个明显的变化和挑战: 变化: 业务在线后,不同类收费、预售、授信模式的创新层出不穷,需要财务不仅只从事单一传统的会计核算工作,还需要积极地参与到业务中去。 从事后算账事后报账,变成财务业务一体化信息的实时处理 挑战: 业务系统与财务系统明显割裂,业务部门与财务部门各自采用一套软件处理其数据,不能及时沟通信息和协同更正信息。 财务系统往往都是单体的传统架构,凭证处理能力无法适应今天企业的不断爆棚的业务发展。 财务的严谨性与业务的灵活性中间有巨大的鸿沟,导致业务要做一种创新的模式,财务可能是最大阻碍。 不论是传统软件公司喜欢说的业财一体化还是互联网平台公司喜欢说的结算平台,都是为了解决以上变化和挑战的。业财一体化主要是从财务部门角度出发进行,在业务支撑上化被动为主动。结算中心往往是结合财务部门和业务运营部门的需求。如果拿我们下面介绍的,计费、账务、会计三个领域来说,业财一体化项目往往只包括账务和会计,结算中心往往包括:计费、账务、会计。或者说业财一体化弱化了计费,没有纳入企业统一管理,把如何计价给到了业务系统自行决定或者简单处理只要产生应收应付单据(计费详单)就好了。 结算域的是一个相对比较专业的领域,没有一定背景知识甚至连一些专业名词都很难理解,更不用说模型设计了,这里我尽快地简单去描述定位而不是描述细节。而且2.1.9版本的结算领域相对还是没有那么完善,这里介绍的是下个版本的内容,所以大家看当前版本的时候会有一些对不上。 二、子领域职责 图5-5-1 子领域职责 计费 计费的价值 随着企业多业务发展以及融合计费需求,我们需要引入计费模型,对灵活计价模式进行支持,快速支撑未来可能的计费方式等 计费的核心设计理念 所有的计算器都继承自虚函数计算器y=f(x) 平滑兼容-默认斜率计算器y=a+bxY – 求值结果(用下标描述结果是什么)A – 偏移量(计算固定值)B – 斜率(费率值)X – 变量(数量)任何计算都是通过一组斜率组合出来的 利用区间限定定义各种斜率组合出各种算法交易额0-100w:y=0.03x >100w:y=0.02x;时间0:00-6:00:y=0.02x 6:00-24:00:y=0.03xX- 变量,数量 图5-5-2 计费的核心设计理念 更灵活多维区间组合,时间维度、计数器维度、其它属性维度计数器区间斜率限定,比如交易额、空间、使用月份数… 计费的核心功能 通过产品定义运营方案 通过订购产品完成商务合同的签订来决定客户计费策略,或者通过系统产品定义通用计费策略 支撑各类产品的模拟计费 以事件驱动,根据事件、产品、订购关系完成产品路由,并实时产生计费详单 根据计费科目与账务科目,打通账务进行核销 账务 账务的价值 以账户账本为中心,提供记账、账户管理,以及账务的实时监控与持续对账。如果计费是对接业务,那么账务的价值是对接财务系统 账务的核心设计理念 不依赖计费,可独立对接,所有业务最终都需要反馈到帐户账本的操作上,并通过账本明细记录所有操作 账务的核心功能 记账:充值、转账、提现,冻结、解冻,差错处理 账务管理:开户、科目维护 账务查询:对账 会计(暂不在计划内) 会计的价值 结算平台的会计模块不是严格意义上的会计系统,它主要是衔接其他的财务系统,做凭证前置处理。在于汇总凭证,产出业务帐,对接到财务总帐系统,缓解财务系统压力。 三、模型介绍 图5-5-3 模型介绍 四、结算基础流程 图5-5-4 结算基础流程

    2024年5月23日
    1.1K00
  • 组件属性

    1. 通用属性 介绍组件和其属性之前,先介绍一些大部分组件通用支持的一些基础属性。 1.2 标题 标题是字段在当前页面的展示名称,标题默认为字段名称,可以修改。 1.2 占位提示 在未填写内容时,输入框或选择框内的浅色提示文字,仅用于提示,不会影响字段的值。 1.3 描述说明 一个字段的描述信息,通常用于说明当前字段的范围、注意事项等。描述说明大部分在组件的下方显示,特殊的是,分组的描述说明在标题的左侧。 1.4 默认值 创建数据时,有些字段大概率都是相同值,可设置默认值,减少添加人员的操作步骤,提高录入数据效率。 1.5 只读 设为只读时,字段可见,但不可编辑。 除了只读和非只读的对立选项,也可以设置条件只读,在设置的条件下才只读,条件不符合则非只读。 1.6 禁用 设为禁用时,字段可见,但不可编辑。 除了禁用和非禁用的对立选项,也可以设置条件禁用,在设置的条件下才禁用,条件不符合则非禁用。 1.7 隐藏 设为隐藏时,字段不可见,也不可编辑。但是在页面设计时,隐藏的组件也会展示,效果如下图。 除了隐藏和非隐藏的对立选项,也可以设置条件隐藏,在设置的条件下才隐藏,条件不符合则非隐藏。 1.8 必填 控制字段在当前页面是否必填,若设置为必填则在标题前会有红色的*作为标识。除了必填和非必填的对立选项,也可以设置条件必填,在设置的条件下才必填,条件不符合则非必填。 1.9 标题排列方式 字段的标题可以自定义横向排列还是纵向排列。每个字段组件都支持设置,设置后之间互不影响。除了组件支持自定义,表单、分组、选项卡也可以设置排列方式,对于这种布局容器类组件,设置后会将其容器内的所有组件的标题排列方式都改为所设置的值。 1.10 宽度 定义在页面中的宽度:占整行的比例,一般可选项有1/4、1/3、1/2、2/3、3/4、1。 其中1/2表示占当前行的一半;1表示占当前行一整行;以此类推。部分组件特殊,如富文本仅支持宽度为1,即占一整行。 2. 分组 分组是一个布局类组件,类似一个容器,可以把业务含义相近的内容放在这个分组容器内。 2.1 属性 2.1.1 标题 分组可以定义一个标题名称,标题显示在分组左上角,可以不设置标题。 2.1.2 描述说明 分组的描述说明显示在分组标题右侧。 2.1.3 标题排列方式 分组中的标题排列方式属性并不是控制分组的标题,而是控制分组内组件的标题。设置后对分组内的所有组件生效。 3. 选项卡 选项卡是一个布局类组件,类似一个容器,每个选项卡可以添加多个选项页,可以把业务含义相近的内容放在选项卡的选项页容器内。 3.1 选项卡属性 3.1.1 选项页排列方式 选中选项卡,可选择选项卡中的选项页排列方式:水平排列、竖直排列。默认是水平排列,效果如下图。 3.1.2 标题排列方式 选项卡中的标题排列方式控制选项卡内组件的标题。设置后对选项卡内的所有组件生效。 3.2 选项页属性 3.2.1 标题 选中选项页,显示选项页的属性设置,选项卡中的每个选项页支持设置标题。 3.2.2 标题排列方式 选项页中的标题排列方式控制选项页内组件的标题。设置后对选项页内的所有组件生效。同一个选项卡,不同选项页之间的标题排列方式可以不同。 4. 单行文本 单行文本输入框,常用于记录名称、身份证号或其他普通的文字内容。 4.1 创建字段 单行文本仅支持创建「文本」业务类型的字段,填写规则、内容和模型设计器端创建字段一致。 4.2 表单属性 4.2.1 通用属性 在表单中,单行文本可以设置一些通用的属性:标题、占位提示、描述说明、默认值、只读、隐藏、禁用、必填、标题排列方式、宽度。 4.2.2 文本类型 文本类型选项为文本或密码。若设置为文本,输入时内容是可见的;若设置为密码,输入时是不可见的密码形态,如下图: 4.2.3 最小/大长度 设置输入框输入内容的长度,输入框会根据设置的值进行校验。 最大长度默认为创建字段时填写的长度,且设置的最大长度不可以大于字段的长度 最小长度默认为空,为空则为不限制最小长度 4.2.4 输入格式 设置输入格式为网址或者身份证时,会进行格式的校验 无:默认为无,不会校验内容 网址:进行网址校验 身份证:进行身份证号校验,设置后,需要输入正确的身份证号 4.2.5 显示计数器 当需要用户关注输入内容长度时,可以开启显示计数器,在输入时会实时显示当前内容的长度。 4.2.6 显示清除按钮 若开启了清除按钮,则在输入框有内容时,点击清除按钮一键清除已有内容 5. 多行文本 多行文本输入框,常用于记录字数较多的文字,如意见、复杂备注等。 5.1 创建字段 多行文本仅支持创建「多行文本」业务类型的字段,填写规则、内容和模型设计器端创建字段一致。 5.2 表单属性 5.2.1 通用属性 在表单中,单行文本可以设置一些通用的属性:标题、占位提示、描述说明、默认值、只读、隐藏、禁用、必填、标题排列方式、宽度。 5.2.2 最小/大长度 设置输入框输入内容的长度,输入框会根据设置的值进行校验。 -最大长度默认为创建字段时填写的长度,且设置的最大长度不可以大于字段的长度 -最小长度默认为空,为空则为不限制最小长度 5.2.3 显示计数器 当需要用户关注输入内容长度时,可以开启显示计数器,在输入时会实时显示当前内容的长度。 5.2.5 显示清除按钮 若开启了清除按钮,则在输入框有内容时,点击清除按钮一键清除已有内容 6. 整数 整数输入框,常用于输入整数的天数、数量等,如果会出现小数,请使用小数组件。 6.1 创建字段 整数仅支持创建「整数」业务类型的字段,填写规则、内容和模型设计器端创建字段一致。 6.2 表单属性 6.2.1 通用属性 在表单中,整数可以设置一些通用的属性:标题、占位提示、描述说明、默认值、只读、隐藏、禁用、必填、标题排列方式、宽度。 6.2.2 最小/大值 设置整数输入框输入内容的最值,输入框会根据设置的最值进行校验。 最大值可设置的范围根据字段填写的长度转换,如设置长度为3,则最大值最大不可以超过999 截图 6.2.3 显示千分位 数字过长时,不便于查看,可开启显示千分位。 7. 小数 小数输入框,常用于输入金额、单价等,会出现小数的数值。 7.1 创建字段 小数仅支持创建「浮点型」业务类型的字段,填写规则、内容和模型设计器端创建字段一致。 7.2 表单属性 7.2.1 通用属性 在表单中,小数可以设置一些通用的属性:标题、占位提示、描述说明、默认值、只读、隐藏、禁用、必填、标题排列方式、宽度。 7.2.2 最小/大值 设置小数输入框输入内容的最值,输入框会根据设置的最值进行校验。 最大值可设置的范围根据字段填写的长度转换,如设置长度为3,则最大值最大不可以超过999 7.2.3 保留小数位数 支持设置小数的保留位数,设置后在页面中输入内容时,将进行校验 可设置的保留小数位数不可大于该字段的精度,如字段精度为2,则组件属性中的小数位数最大只能为2; 7.2.4 显示千分位 数字过长时,不便于查看,可开启显示千分位。 8. 下拉单选 从多个选项中下拉选择一个数据,作为数据值。选项可以是关联模型的数据,也可以是数据字典或布尔型开关。 8.1 创建字段 下拉单选支持创建三种业务类型的字段,分别是:多对一、布尔型、数据字典。 多对一:创建字段时需要选择关联的模型,关联模型的数据将作为下拉选项; 布尔型:下拉选项默认只有是、否; 数据字典:创建字段时需要选择数据字典,其数据字典项将作为下拉选项 8.2 表单属性 8.2.1 通用属性 在表单中,下拉单选可以设置一些通用的属性:标题、占位提示、描述说明、只读、隐藏、禁用、必填、标题排列方式、宽度。 8.2.2 选项类型…

    2024年6月20日
    1.4K00
  • 3.5.7.8 自定义菜单栏

    在业务中,可能会遇到需要对菜单栏的交互或UI做全新设计的需求,这个时候可以自定义菜单栏组件支持。 首先继承平台的CustomMenuWidget 组件,将自己vue文件设置到component处 import { NavMenu, SPI, ViewWidget } from '@kunlun/dependencies'; import Component from './CustomMenu.vue'; @SPI.ClassFactory( ViewWidget.Token({ // 这里的widget跟平台的组件一样,这样才能覆盖平台的组件 widget: 'nav-menu' }) ) export class CustomMenuWidget extends NavMenu { public initialize(props) { super.initialize(props); this.setComponent(Component); return this; } } vue文件中继承平台的props,编写自定义页面代码 export const NavMenuProps = { /** * 当前模块 */ module: { type: Object as PropType<IModule | null> }, /** * 树结构的菜单 */ menus: { type: Array as PropType<IResolvedMenu[]>, default: () => [] }, /** * 菜单类型,现在支持垂直、水平、和内嵌模式三种 */ mode: { type: String as PropType<'vertical' | 'horizontal' | 'inline'>, default: 'inline' }, /** * 菜单栏是否折叠收起 */ collapsed: { type: Boolean, default: false }, /** * 当前展开的 SubMenu 菜单项 key 数组 */ openKeys: { type: Array as PropType<string[]>, default: () => [] }, /** * 当前选中的菜单项 key 数组 */ selectKeys: { type: Array as PropType<string[]>, default: () => [] }, /** * 菜单搜索下拉选中菜单项 */ selectMenuBySearch: { type: Function as PropType<(menuName: String) => void> }, /** * 选中菜单项 */ selectMenu: { type: Function as PropType<(menuName: String) => Promise<void>> }, /** * SubMenu 展开/关闭的回调 */ openChange: { type: Function as PropType<(openKeys: string[]) => void> }, /**…

    2024年5月23日
    1.6K00
  • 3.4.3.2 面向切面-拦截器

    一、拦截器 拦截器为平台满足条件的函数以非侵入方式根据优先级扩展函数执行前和执行后的逻辑。 使用方法上的@Hook注解可以标识方法为拦截器。前置扩展点需要实现HookBefore接口;后置扩展点需要实现HookAfter接口。入参包含当前拦截函数定义与该函数的入参。拦截器可以根据函数定义与入参增加处理逻辑。 拦截器分为前置拦截器和后置拦截器,前者的出入参为所拦截函数的入参,后者的出入参为所拦截函数的出参。可以使用@Hook注解或Hook模型的非必填字段module、model、fun、函数类型、active来筛选出对当前拦截方法所需要生效的拦截器。若未配置任何过滤属性,拦截器将对所有函数生效。 根据拦截器的优先级priority属性可以对拦截器的执行顺序进行调整。priority数字越小,越先执行。 二、前置拦截(举例) 增加一个前置拦截,对PetShop的sayHello函数进行前置拦截,修改函数的入参的shopName属性,在其前面增加"hookbefore:"字符串。并查看效果 Step1 新增PetShopSayHelloHookBefore实现HookBefore接口 为run方法增加@Hook注解 配置module={DemoModule.MODULE_MODULE},这里module代表的是执行模块,该Hook只匹配由DemoModule模块为发起入口的请求 配置model={PetShop.MODEL_MODEL},该Hook只匹配PetShop模型 配置fun={"sayHello"},该Hook只匹配函数编码为sayHello的函数 package pro.shushi.pamirs.demo.core.hook; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.core.faas.HookBefore; import pro.shushi.pamirs.meta.api.dto.fun.Function; @Component public class PetShopSayHelloHookBefore implements HookBefore { @Override @Hook(module = {DemoModule.MODULE_MODULE},model = {PetShop.MODEL_MODEL},fun = {"sayHello"}) public Object run(Function function, Object… args) { if(args!=null && args[0]!=null){ PetShop arg = (PetShop)args[0]; arg.setShopName("hookbefore:"+ arg.getShopName()); } return args; } } 图3-4-3-5 新增PetShopSayHelloHookBefore实现HookBefore接口 Step2 重启查看效果 用graphQL工具Insomnia查看效果,如果访问提示未登陆,则请先登陆。详见3.4.1【构建第一个Function】一文 用 http://127.0.0.1:8090/pamirs/base 访问,结果我们会发现PetShopSayHelloHookBefore不起作用。是因为本次请求是以base模块作为发起模块,而我们用module={DemoModule.MODULE_MODULE}声明了该Hook只匹配由DemoModule模块为发起入口的请求 图3-4-3-6 示例效果 用 http://127.0.0.1:8090/pamirs/demoCore 访问,前端是以模块名作为访问入口不是模块编码这里大家要注意下 图3-4-3-7 示例效果 用 http://127.0.0.1:8090/pamirs/demoCore 访问,更换到petShop的子模型petShopProxy来访问sayHello函数,结果我们发现是没有效果的。因为配置model={PetShop.MODEL_MODEL},该Hook只匹配PetShop模型 三、后置拦截(举例) 增加一个后置拦截,对PetShop的sayHello函数进行后置拦截,修改函数的返回结果的shopName属性,在其后面增加"hookAfter:"字符串。并查看效果 Step1 新增PetShopSayHelloHookAfter实现HookAfter接口 为run方法增加@Hook注解 配置model={PetShop.MODEL_MODEL},该Hook只匹配PetShop模型 配置fun={"sayHello"},该Hook只匹配函数编码为sayHello的函数 package pro.shushi.pamirs.demo.core.hook; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.core.faas.HookAfter; import pro.shushi.pamirs.meta.api.dto.fun.Function; @Component public class PetShopSayHelloHookAfter implements HookAfter { @Override @Hook(model = {PetShop.MODEL_MODEL},fun = {"sayHello"}) public Object run(Function function, Object ret) { if (ret == null) { return null; } PetShop result =null; if (ret instanceof Object[]) { Object[] rets = (Object[])((Object[])ret); if (rets.length == 1) { result = (PetShop)rets[0]; } } else { result = (PetShop)ret; } result.setShopName(result.getShopName()+":hookAfter"); return result; } } 3-4-3-8 新增PetShopSayHelloHookAfter实现HookAfter接口 Step2 重启查看效果 用 http://127.0.0.1:8090/pamirs/base 访问,结果我们会发现PetShopSayHelloHookAfter是起作用。PetShopSayHelloHookBefore没有配置模块过滤。 3-4-3-9 示例效果 用访问,结果我们会发现PetShopSayHelloHookAfte和PetShopSayHelloHookBefore同时起作用 3-4-3-10 示例效果 我们会发现HookAfter只对结果做了修改,所以message中可以看到hookbefore,但看不到hookAfter 四、注意点 不管前置拦截器,还是后置拦截器都可以配置多个,根据拦截器的优先级priority属性可以对拦截器的执行顺序进行调整。priority数字越小,越先执行。小伙伴们可以自行尝试 拦截器必须是jar依赖,不然执行会报错。特别是有的小伙伴配置了一个没有过滤条件的拦截器,就要非常小心 模块启动yml文件可以过滤不需要执行的hook,具体配置详见4.1.1【模块之yml文件结构详解】一文 调用入口不是由前端发起而是后端编程中直接调用,默认不会生效,如果要生效请参考4.1.9【函数之元位指令】一文

    2024年5月23日
    1.5K00

Leave a Reply

Please Login to Comment