函数Function的定义
大约 6 分钟
函数Function的定义
Function做为oinone的可管理的执行逻辑单元
函数基础概念
在 Oinone 中,函数被视为无处不在的可管理的执行逻辑单元。这也解释了为什么说 Oinone 以函数为内在的原因。
函数属性与使用
I、函数详解
定义
- 函数定义可以无返回值,也允许定义无参函数。
- 如果入参为原始类型请使用对应封装类型声明。
- 命名空间和函数编码相同的覆盖函数有且仅有一个生效,在模型类中定义优先级高于在模型类外定义,在模型类中靠后优先级越高。
- 使用@Model.model 或 @Fun注解函数的命名空间。
- 先取@Model.model注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找
- 若为空则取@Fun注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找;
- 若皆为空则取全限定类名。
- 使用@Function.fun注解配置函数编码。
- 函数编码先在本类方法查找注解,如果本类方法未配置 或 注解值为空 则在父类或接口方法上查找,若皆为空则取方法名。
- 当接口 或者 父类配置了命名空间和函数编码并且有多个实现类或继承类,如果实现方法使用缺省的方法名作为函数编码,则会导致多个实现方法函数编码冲突,需要使用@Function.fun为每个实现类的对应方法配置唯一的函数编码。但大多数场景一个接口只有一个实现类。
- [推荐] 为函数声明接口,并在接口上进行注解 (@Fun或@Model.model,@Function),函数实现接口即可;
- 如果需要为函数开启远程服务,必须为函数声明接口并添加注解,接口须放在api工程中。
- 系统会根据函数开放级别是否是 REMOTE 来自动注册 服务提供者和消费者。
- 命名空间和函数编码的注解方式适用于所有函数。
- [特殊] 非模型但带有函数的类必需使用@Fun注解来标识当前类为非模型带函数的类。如果需要提供远程服务,需要在API包中声明注解了@Fun注解的函数接口,并在接口的方法上加上@Function注解。
属性解释
@Fun
@Fun(value=PetShop.MODEL_MODEL)
value
: 命名空间,不可更改,默认与全限定类名相同。当action
配置在非模型类中时,需要配置此属性指定模型编码;扩展点需要配置此属性指定所扩展函数命名空间。若字段需要使用该函数,则value
需要设置为"pamirs"
。
作用范围:
- 注解标识在类上。
@Function
@Function(name = "", scene = {},summary = "",openLevel = FunctionOpenEnum)
name
: api名称,供接口调用使用,非实际调用方法名称,默认与方法名称相同。scene
: 函数可执行场景,是一个 FunctionSceneEnum 类型的数组。summary
: 描述。openLevel
: 函数开放级别,是一个 FunctionOpenEnum 类型的数组,默认包括FunctionOpenEnum.LOCAL
和FunctionOpenEnum.REMOTE
。
作用范围:
- 注解标识在方法上。
@Function.Advanced
@Function.Advanced(
displayName = "",
type = FunctionTypeEnum.QUERY,
managed = false,
language = FunctionLanguageEnum.JAVA,
builtin = false,
check = false,
category = FunctionCategoryEnum.OTHER,
group = "pamirs",
version = "1.0.0",
timeout = 5000,
retries = 0,
isLongPolling = false,
longPollingKey = "userId",
longPollingTimeout = 1
)
displayName
: 展示名称。type
: 函数类型,是一个 FunctionTypeEnum 类型的数组,默认为FunctionTypeEnum.UPDATE
。managed
: 是否是数据库管理器函数,默认为false
。language
: 函数语言,默认为FunctionLanguageEnum.JAVA
。builtin
: 是否内置函数,默认为false
。check
: 校验,默认为false
。category
: 函数分类,默认为FunctionCategoryEnum.OTHER
。group
: 系统分组,默认为FunctionDefaultsConstants.GROUP
。version
: 系统版本,默认为FunctionDefaultsConstants.VERSION
。timeout
: 超时时间,默认为FunctionDefaultsConstants.TIMEOUT
。retries
: 重试次数,默认为0
。isLongPolling
: 是否支持 long polling,默认为false
。longPollingKey
: long polling 的唯一 key,从前端上下文中获取,默认为"userId"
。longPollingTimeout
: long polling 的超时时间,单位为秒,默认为1
。
作用范围:
- 注解标识在方法上。
@Function.fun
@Function.fun(value="hello2Fun")
value
: 函数编码,不可更改,默认与方法名称相同。
作用范围:
- 注解标识在方法上。
II、函数的使用
由于数据管理器和数据构造器是 Oinone 为模型自动赋予的函数,它们代表着内在的数据管理能力。而模型的其他函数则需要通过以下四种方式来主动定义。
- 伴随模型新增函数
- 独立新增函数绑定到模型
- 独立新增函数只作公共逻辑单元
- 伴随ServerAction新增函数
函数的最佳实践
I、伴随模型新增函数
- 定义:
它是跟模型的java类定义在一起,复用模型的命名空间
- 示例:
package pro.shushi.pamirs.demo.api.model;
@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields ={"shopName"} )
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;
}
}
II、独立新增函数绑定到模型
- 定义:
独立方法定义类,并采用Model.model或Fun注解,但是value都必须是模型的编码, 如@Model.model(PetShop.MODEL_MODEL)
或@Fun(PetShop.MODEL_MODEL)
- 示例:
- 新增接口类 接口的方法上要加上@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)
public interface PetShopHelloService {
@Function
PetShop sayHello(PetShop shop);
}
- 新增接口实现类
package pro.shushi.pamirs.demo.core.service;
@Fun(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;
}
}
III、独立新增函数只作公共逻辑单元
定义:
- 只能java后端访问,不生成GraphQL的schema
- 即使配置
@Function(openLevel = FunctionOpenEnum.API)
,也相当于FunctionOpenEnum.LOCAL
。 - 主要作用 提取公共的逻辑,并且可管理。
示例:
修改PetShopService和PetShopServiceImpl的命名空间
package pro.shushi.pamirs.demo.api.service;
…… //import
@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
@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;
}
}
IV、伴随ServerAction新增函数
- 定义:
- 如PetShopBatchUpdate模型在PetShopBatchUpdateAction类中定义了一个名为“conform”的ServerAction,背后就定义了一个namespace为demo.PetShopBatchUpdate,fun为conform的Function,而且开放级别为API。
- 示例:
package pro.shushi.pamirs.demo.core.action;
…… //import
@Model.model(PetShopBatchUpdate.MODEL_MODEL)
@Component
public class PetShopBatchUpdateAction {
@Action(displayName = "确定",
bindingType = ViewTypeEnum.FORM,
contextType = ActionContextTypeEnum.SINGLE)
public PetShopBatchUpdate conform(PetShopBatchUpdate data){
List<PetShopProxy> proxyList = data.getPetShopList();
for(PetShopProxy petShopProxy:proxyList){
petShopProxy.setDataStatus(data.getDataStatus());
}
new PetShopProxy().updateBatch(proxyList);
return data;
}
}
函数的平台特性
I、在Java中同名不同参数方法(不建议)
- 定义:
- Java的同名不同参数,在很多远程调用框架如dubbo也是不支持的,在Oinone中也需要特殊处理,要以不同的name和fun来区别。
- 示例:
- PetShop定义两个同名方法,并加上Function注解。
- 我们在PetShop模型下的增加一个同名方法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;
}
}
- 前端接口测试
query{
petShopQuery{
sayHello(shop:{shopName:"cpc"}){
shopName
}
sayHello2(shop:{shopName:"cpc"},s:"sss"){
shopName
}
}
}