3.4.1 构建第一个Function

Function做为oinone的可管理的执行逻辑单元,是无处不在的

在3.3.3【模型的数据管理器】和3.3.2【模型类型】一文中的代理模型部分,涉及到包括在Action中自定义函数(action背后都对应一个Function)、重写queryPage的函数、以及独立抽取的公共逻辑函数,Function做为oinone的可管理的执行逻辑单元,是无处不在的。这也是为什么说oinone以函数为内在的原因。

一、构建第一个Function

因为数据管理器和数据构造器是oinone为模型自动赋予的Function,是内在数据管理能力。模型其他Function都需要用以下四种方式主动定义

伴随模型新增函数(举例)

它是跟模型的java类定义在一起,复用模型的命名空间。

Step1 为PetShop增加一个名为sayHello的Function

package pro.shushi.pamirs.demo.api.model;
…… //import

@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields ={"shopName"} )
@Model.Code(sequence = "DATE_ORDERLY_SEQ",prefix = "P",size=6,step=1,initial = 10000,format = "yyyyMMdd")
public class PetShop extends AbstractDemoIdModel {
    public static final String MODEL_MODEL="demo.PetShop";
    …… //省略其他代码
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type=FunctionTypeEnum.QUERY)
    public PetShop sayHello(PetShop shop){
        PamirsSession.getMessageHub().info("Hello:"+shop.getShopName());
        return shop;
    }
}

图3-4-1-1 代码示例

Step2 重启看效果

用graphQL工具Insomnia查看效果

  1. 用Insomnia模拟登陆

a. 创建一个login请求,用于保存login请求,为后续模拟登陆保留快捷方式

1632290598998-aa0d9386-2ed6-426a-bf33-e87d04227092

图3-4-1-2 创建一个login请求

b. 下面为登陆请求的GraphQL,请在post输入框中输入。如果请求输入框提示错误可以,可以点击schema 的Refresh Schema来刷新文档


mutation {
  pamirsUserTransientMutation {
    login(user: {login: "admin", password: "admin"}) {
      broken
      errorMsg
      errorCode
      errorField
    }
  }
}

图3-4-1-3 登陆请求的GraphQL

c. 点击Send按钮,我们可以看到登陆成功的反馈信息

1632291332648-2fcfd998-64d3-4fce-a860-aad07668d165

图3-4-1-4 登陆成功的反馈信息

  1. 用Insomnia模拟访问PetShop的sayHello方法,gql的返回中,我们可以看到两个核心返回

a. 一是方法正常返回的shopName

b. 二是“PamirsSession.getMessageHub().info("Hello:"+shop.getShopName())”代码执行的结果,在messages中有一个消息返回,更多消息机制详见4.1.23【框架之信息传递

query{
  petShopQuery{
    sayHello(shop:{shopName:"cpc"}){
    shopName
    }
  }
}

图3-4-1-5 用Insomnia模拟访问PetShop的sayHello

1632292001565-50717613-1337-442c-a10f-8cbafa2c2db2

图3-4-1-6 代码执行结果

  1. 用Insomnia模拟访问PetShopProxy的sayHello方法

效果同用Insomnia模拟访问PetShop的sayHello方法,体现Function的继承特性。

独立新增函数绑定到模型(举例)

独立方法定义类,并采用Model.model或Fun注解,但是value都必须是模型的编码,如@Model.model(PetShop.MODEL_MODEL)或@Fun(PetShop.MODEL_MODEL)

Step1 提取PetShop的sayHello方法独立到PetShopService中

  1. 注释掉PetShop的sayHello方法
package pro.shushi.pamirs.demo.api.model;
…… //import

@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields ={"shopName"} )
@Model.Code(sequence = "DATE_ORDERLY_SEQ",prefix = "P",size=6,step=1,initial = 10000,format = "yyyyMMdd")
public class PetShop extends AbstractDemoIdModel {
    public static final String MODEL_MODEL="demo.PetShop";
    …… //省略其他代码
//    @Function(openLevel = FunctionOpenEnum.API)
//    @Function.Advanced(type=FunctionTypeEnum.QUERY)
//    public PetShop sayHello(PetShop shop){
//        PamirsSession.getMessageHub().info("Hello:"+shop.getShopName());
//        return shop;
//    }
}

图3-4-1-7 注释掉PetShop的sayHello

  1. 新增PetShopService接口类

接口的方法上要加上@Function注解,这样另模块依赖api包的时候,会自动注册远程服务的消费者

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

import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;

@Fun(PetShop.MODEL_MODEL)
//@Model.model(PetShop.MODEL_MODEL)
public interface PetShopHelloService {
    @Function
    PetShop sayHello(PetShop shop);
}

图3-4-1-8 新增PetShopService接口类

  1. 新增PetShopServiceImpl实现类
package pro.shushi.pamirs.demo.core.service;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.demo.api.service.PetShopHelloService;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;

@Fun(PetShop.MODEL_MODEL)
//@Model.model(PetShop.MODEL_MODEL)
@Component
public class PetShopHelloServiceImpl implements PetShopHelloService {

    @Override
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    public PetShop sayHello(PetShop shop) {
        PamirsSession.getMessageHub().info("Hello:"+shop.getShopName());
        return shop;
    }
}

图3-4-1-9 新增PetShopServiceImpl实现

Step2 重启看效果

同上文【伴随模型新增函数(举例)】的效果一致,同样具备继承特性。

独立新增函数只作公共逻辑单元(举例)

只能java后端访问,不生成GraphQL的schema,即使配置 @Function(openLevel = FunctionOpenEnum.API),也相当于FunctionOpenEnum.LOCAL 。如同3.3.4【模型的继承】一文中的PetCatItemQueryService的作用,提取公共的逻辑,并且可管理。

Step1 修改PetShopService和PetShopServiceImpl的命名空间

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

import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;

@Fun(PetShopHelloService.FUN_NAMESPACE)
public interface PetShopHelloService {

    String FUN_NAMESPACE = demo.PetShopHelloService;

    @Function
    PetShop sayHello(PetShop shop);
}

图3-4-1-10 修改PetShopService命名空间

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

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.demo.api.service.PetShopHelloService;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;

@Fun(PetShopHelloService.FUN_NAMESPACE)
@Component
public class PetShopHelloServiceImpl implements PetShopHelloService {

    @Override
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    public PetShop sayHello(PetShop shop) {
        PamirsSession.getMessageHub().info(Hello:+shop.getShopName());
        return shop;
    }
}

图3-4-1-11 修改PetShopServiceImpl的命名空间

Step2 重启看效果

刷新GraphQL schema,原先的post请求输入框会报错,点击提交结果也会报GraphQL未定义

1632294623181-4fe3d4f5-a63a-4dea-a605-f3cd0b643a48

图3-4-1-12 刷新GraphQL schema看效果

伴随ServerAction新增函数

ServerAction我们前面也多此提到过,比如在3.3.2【模型的类型】一文中关于代理模型传输模型的内容中都定义过ServerAction,其背后都默认定义了一个Function。如PetShopBatchUpdate模型在PetShopBatchUpdateAction类中定义了一个名为“conform”的ServerAction,背后就定义了一个namespace为demo.PetShopBatchUpdate,fun为conform的Function,而且开放级别为API。

二、java同名不同参数方法(不建议)

java的同名不同参数,在很多远程调用框架如dubbo也是不支持的,在oinone中也需要特殊处理,要以不同的name和fun来区别。

Step1 为PetShop定义两个同名方法,并加上Function注解

我们把PetShop模型下的sayHello函数代码恢复下,并增加一个同名方法sayHello,但注解上@Function(name = "sayHello2")和@Function.fun("sayHello2")。修改完以后sayHello和sayHello2都能在Insomnia通过GQL来访问

package pro.shushi.pamirs.demo.api.model;
…… //import

@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = 宠物店铺,summary=宠物店铺,labelFields ={shopName} )
@Model.Code(sequence = DATE_ORDERLY_SEQ,prefix = P,size=6,step=1,initial = 10000,format = yyyyMMdd)
public class PetShop extends AbstractDemoIdModel {
    public static final String MODEL_MODEL=demo.PetShop;
    …… //省略其他代码
    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    public PetShop sayHello(PetShop shop){
        PamirsSession.getMessageHub().info(Hello:+shop.getShopName());
        return shop;
    }
    @Function(name = sayHello2,openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    @Function.fun(sayHello2)
    public PetShop sayHello(PetShop shop, String s) {
        PamirsSession.getMessageHub().info(Hello:+shop.getShopName()+,s:+s);
        return shop;
    }
}

图3-4-1-13 为PetShop定义两个同名方法并加上Function注解

Step2 重启看效果

query{
  petShopQuery{
    sayHello(shop:{shopName:cpc}){
      shopName
    }
    sayHello2(shop:{shopName:cpc},s:sss){
      shopName
    }
  }
}

图3-4-1-14 示例执行效果

1653208575877-bae8d0fd-ccbf-4d9a-886f-c673a987affe

图3-4-1-15 示例执行效果

配置

非模型但带有函数的类必需使用@Fun注解来标识当前类为非模型带函数的类。如果需要提供远程服务,需要在API包中声明注解了@Fun注解的函数接口,并在接口的方法上加上@Function注解。

函数配置

函数定义可以无返回值,也允许定义无参函数。如果入参为原始类型请使用对应封装类型声明。命名空间和函数编码相同的覆盖函数有且仅有一个生效,在模型类中定义优先级高于在模型类外定义,在模型类中靠后优先级越高。

可以使用@Model.model、@Fun注解函数的命名空间。先取@Model.model注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找;若为空则取@Fun注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找;若皆为空则取全限定类名。

可以使用@Function.fun注解配置函数编码。取函数编码先在本类方法查找注解,如果本类方法未配置或注解值为空则在父类或接口方法上查找,若皆为空则取方法名。

如果接口或者父类配置了命名空间和函数编码并且有多个实现类或继承类,如果实现方法使用缺省的方法名作为函数编码,则会导致多个实现方法函数编码冲突,需要使用@Function.fun为每个实现类的对应方法配置唯一的函数编码。但大多数场景一个接口只有一个实现类。

推荐为函数声明接口,并在接口上进行注解(@Fun或@Model.model,@Function),函数实现接口即可;如果需要为函数开启远程服务,必须为函数声明接口并注解,接口须放在api工程中。系统会根据函数开放级别是否是REMOTE来自动注册服务提供者和消费者。

命名空间和函数编码的注解方式适用于所有函数。

函数命名规范

模型属性 默认取值规范 命名规则规范
namespace 先取@Model.model注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找;若为空则取@Fun注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找;若皆为空则取全限定类名 长度必须小于等于128个字符
name 默认使用java方法名 仅支持数字、字母必须以字母开头长度必须小于等于128个字符不能以get、set为开头作为函数名称
fun 默认使用name属性 长度必须小于等于128个字符
summary 默认使用displayName属性 不能使用分号长度必须小于等于500个字符
descripition NULL,注解无法定义 长度必须小于等于65535个字符
openLevel 函数的开放等级默认值:{FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE} FunctionOpenEnum枚举值LOCAL(2L, "本地调用", "本地调用"), REMOTE(4L, "远程调用", "远程调用"), API(8L, "开放接口", "开放接口");
Advanced.displayName 默认使用name属性 长度必须小于等于128个字符
Advanced.timeout 超时时间默认:5秒,远程调用时配置生效

表3-4-1-1 函数命名规范

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

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

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

相关推荐

  • 5.4 基础支撑之商业关系域

    PamirsPartner作为商业关系与商业行为的主体,那么PamirsPartner间的关系如何描述,本文将介绍两种常见的设计思路,从思维和实现两方面进行对比,给出oinone为啥选择关系设计模式的原因。 一、两种设计模式对比 设计模式思路介绍 角色设计模式思路介绍 从产品角度枚举所有商业角色,每个商业角色对应一个派生的商业主体,并把主体间的关系类型进行整理。 图5-4-1 角色设计模式 关系设计模式思路介绍 从产品角度枚举所有商业角色,每个商业角色对应一个派生的主体间商业关系 图5-4-2 关系设计模式 设计模式对应实现介绍 角色设计模式实现介绍 不单商业主体需要扩展,关系也要额外维护,可以是字段或是关系表。一般M2O和O2M字段维护,M2M关系表维护。 创建合同场景中甲方选择【商业主体A】,乙方必须是【商业主体A】有关联的经销商、分销商、零售商、供应商等,则在角色设计模式下就非常麻烦,因为关系都是独立维护的 图5-4-3 角色设计模式实现介绍 关系设计模式实现介绍 只需维护商业关系扩展 同时在设计上收敛了商业关系,统一管理应对不同场景都比较从容 图5-4-4 关系设计模式实现介绍 二、oinone商业关系的默认实现 首先oinone的商业关系选择关系设计模式 其次模型上采用多表继承模式,父模型上维护核心字段,子模型维护个性化字段。

    2024年5月23日
    1.0K00
  • 4.1.14 Search之非存储字段条件

    search默认查询的是模型的queryPage函数,但我们有时候需要替换调用的函数,这个特性会在下个版本支持。其核心场景为当搜索条件中有非存储字段,如果直接用queryPage函数的rsql拼接就会报错,所以非存储字段不会增加在rsql中。本文介绍一个比较友好的临时替代方案。 非存储字段条件(举例) Step1 为PetTalent新增一个非存储字段unStore @Field(displayName = "非存储字段测试",store = NullableBoolEnum.FALSE) private String unStore; 图4-1-14-1 为PetTalent新增一个非存储字段unStore Step2 修改PetTalent的Table视图的Template 在标签内增加一个查询条件 <field data="unStore" /> 图4-1-14-2 修改PetTalent的Table视图的Template Step3 重启看效果 进入宠物达人列表页,在搜索框【非存储字段测试】输入查询内容,点击搜索跟无条件一致 Step4 修改PetTalentAction的queryPage方法 package pro.shushi.pamirs.demo.core.action; …… 引入依赖类 @Model.model(PetTalent.MODEL_MODEL) @Component public class PetTalentAction { ……其他代码 @Function.Advanced(type= FunctionTypeEnum.QUERY) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.API}) public Pagination<PetTalent> queryPage(Pagination<PetTalent> page, IWrapper<PetTalent> queryWrapper){ QueryWrapper<PetTalent> queryWrapper1 = (QueryWrapper<PetTalent>) queryWrapper; Map<String, Object> queryData = queryWrapper.getQueryData(); String unStore = (String) queryData.get(LambdaUtil.fetchFieldName(PetTalent::getUnStore)); if (StringUtils.isNotEmpty(unStore)) { //转换查询条件 queryWrapper1.like( 图4-1-14-3 修改PetTalentAction的queryPage方法 Step5 重启看效果 在搜索框【非存储字段测试】输入查询内容,跟通过【达人】字段搜索的效果是一致的 图4-1-14-4 示例效果

    2024年5月23日
    96900
  • 3.3.10 字段类型之关系描述的特殊场景

    在3.3.9【字段类型之关系与引用】一文中已经描述了各种关系字段的常规写法,还有一些特殊场景如:关系映射中存在常量,或者M2M中间表是大于两个字段构成。 举例说明关系字段-高级用法 场景描述 PetTalent模型增加talentType字段,PetItem与PetTalent的多对多关系增加talentType(达人类型),PetItemRelPetTalent 中间表维护petItemId、petTalentId以及talentType,PetDogItem和PetCatItem分别重写petTalents 字段,关系中增加常量描述。示意图如下 实际操作步骤: Step1 新增 TalentTypeEnum package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.BaseEnum; @Dict(dictionary = TalentTypeEnum.DICTIONARY,displayName = "达人类型") public class TalentTypeEnum extends BaseEnum<TalentTypeEnum,Integer> { public static final String DICTIONARY ="demo.TalentTypeEnum"; public final static TalentTypeEnum DOG =create("DOG",1,"狗达人","狗达人"); public final static TalentTypeEnum CAT =create("CAT",2,"猫达人","猫达人"); } Step2 PetTalent模型增加talentType字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.TalentTypeEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(PetTalent.MODEL_MODEL) @Model(displayName = "宠物达人",summary="宠物达人",labelFields ={"name"}) public class PetTalent extends AbstractDemoIdModel{ public static final String MODEL_MODEL="demo.PetTalent"; @Field(displayName = "达人") private String name; @Field(displayName = "达人类型") private TalentTypeEnum talentType; } Step3 修改PetItem的petTalents字段,在关系描述中增加talentType(达人类型) @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = "PetItemRelPetTalent") @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step4 PetDogItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = "PetItemRelPetTalent") @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step5 PetCatItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#2#"}, domain = " talentType == CAT") private List<PetTalent> petTalents; Step6 清除中间表demo_core_pet_item_rel_pet_talent的数据记录 清除PetItem与PetTalent的多对多中间表demo_core_pet_item_rel_pet_talent的数据记录 Step7 重启看效果 修改达人记录,选择不同达人类型 PetItem、PetCatItem、PetDogItem不同的交互页面

    2024年5月23日
    1.5K00

Leave a Reply

登录后才能评论