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;
}
}
Step2 重启看效果
用graphQL工具Insomnia查看效果
- 用Insomnia模拟登陆
a. 创建一个login请求,用于保存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按钮,我们可以看到登陆成功的反馈信息
- 用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
- 用Insomnia模拟访问PetShopProxy的sayHello方法
效果同用Insomnia模拟访问PetShop的sayHello方法,体现Function的继承特性。
独立新增函数绑定到模型(举例)
独立方法定义类,并采用Model.model或Fun注解,但是value都必须是模型的编码,如@Model.model(PetShop.MODEL_MODEL)或@Fun(PetShop.MODEL_MODEL)
Step1 提取PetShop的sayHello方法独立到PetShopService中
- 注释掉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
- 新增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);
}
- 新增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;
}
}
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);
}
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;
}
}
Step2 重启看效果
刷新GraphQL schema,原先的post请求输入框会报错,点击提交结果也会报GraphQL未定义
伴随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;
}
}
Step2 重启看效果
query{
petShopQuery{
sayHello(shop:{shopName:cpc}){
shopName
}
sayHello2(shop:{shopName:cpc},s:sss){
shopName
}
}
}
配置
非模型但带有函数的类必需使用@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秒,远程调用时配置生效 |
Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9243.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验