跳至主要內容

函数Function的定义

Mr.Hope大约 6 分钟

函数Function的定义

Function做为oinone的可管理的执行逻辑单元

函数基础概念

在 Oinone 中,函数被视为无处不在的可管理的执行逻辑单元。这也解释了为什么说 Oinone 以函数为内在的原因。

函数属性与使用

I、函数详解

定义

  • 函数定义可以无返回值,也允许定义无参函数。
    • 如果入参为原始类型请使用对应封装类型声明。
    • 命名空间和函数编码相同的覆盖函数有且仅有一个生效,在模型类中定义优先级高于在模型类外定义,在模型类中靠后优先级越高。
  • 使用@Model.model 或 @Fun注解函数的命名空间。
    • 先取@Model.model注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找
    • 若为空则取@Fun注解值,先在本类查找注解,如果本类未配置或注解值为空则在父类或接口上查找;
    • 若皆为空则取全限定类名。
  • 使用@Function.fun注解配置函数编码。
    • 函数编码先在本类方法查找注解,如果本类方法未配置 或 注解值为空 则在父类或接口方法上查找,若皆为空则取方法名。
  • 当接口 或者 父类配置了命名空间和函数编码并且有多个实现类或继承类,如果实现方法使用缺省的方法名作为函数编码,则会导致多个实现方法函数编码冲突,需要使用@Function.fun为每个实现类的对应方法配置唯一的函数编码。但大多数场景一个接口只有一个实现类。
  • [推荐] 为函数声明接口,并在接口上进行注解 (@Fun或@Model.model,@Function),函数实现接口即可;
    • 如果需要为函数开启远程服务,必须为函数声明接口并添加注解,接口须放在api工程中。
    • 系统会根据函数开放级别open in new window是否是 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.LOCALFunctionOpenEnum.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、伴随模型新增函数

  1. 定义

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

  1. 示例
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、独立新增函数绑定到模型

  1. 定义

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

  1. 示例
    1. 新增接口类 接口的方法上要加上@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);
}
  1. 新增接口实现类
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、独立新增函数只作公共逻辑单元

  1. 定义:

    1. 只能java后端访问,不生成GraphQL的schema
    2. 即使配置 @Function(openLevel = FunctionOpenEnum.API),也相当于FunctionOpenEnum.LOCAL
    3. 主要作用 提取公共的逻辑,并且可管理。
  2. 示例:

    修改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新增函数

  1. 定义:
    1. 如PetShopBatchUpdate模型在PetShopBatchUpdateAction类中定义了一个名为“conform”的ServerAction,背后就定义了一个namespace为demo.PetShopBatchUpdate,fun为conform的Function,而且开放级别为API。
  2. 示例:
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中同名不同参数方法(不建议)

  1. 定义:
    1. Java的同名不同参数,在很多远程调用框架如dubbo也是不支持的,在Oinone中也需要特殊处理,要以不同的name和fun来区别。
  2. 示例:
    1. PetShop定义两个同名方法,并加上Function注解。
    2. 我们在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;
    }
}
  1. 前端接口测试
query{
  petShopQuery{
    sayHello(shop:{shopName:"cpc"}){
      shopName
    }
    sayHello2(shop:{shopName:"cpc"},s:"sss"){
      shopName
    }
  }
}