纷享销客开发者手册 纷享销客开发者手册
  • APL开发手册
  • PWC开发手册
  • OpenAPI 文档
APL代码介绍
API Reference
开发工具
更新日志
  • 简体中文
  • English
APL代码介绍
API Reference
开发工具
更新日志
  • 简体中文
  • English
  • Java离线高代码开发
    • 准备工作
    • 环境准备工作
    • 开始开发
    • 将代码发布到租户
    • 注意事项
    • 代码Demo
    • 七、常见问题
  • VSCode离线开发
  • WebIDE在线开发
目录

Java离线高代码开发

# 准备工作

下载并安装Maven插件 https://maven.apache.org/install.html 下载并安装JDK1.8

安装开发IDE(例如IDEA、VSCode、Eclipse等)

申请开发者证书(纷享销客网页版-后台管理-定制开发平台/开发者证书)

  • 申请证书时需要给企业ID、员工ID,该证书会与租户以及员工进行绑定
  • 每次申请的证书有效期为30/60/90天
  • 证书绑定的员工如果停用、禁止登录或者修改密码,该证书都会立即失效

下载脚手架 https://a9.fspage.com/FSR/base/apl/template920.zip

  • 解压脚手架,建议解压到企业帐号/企业信息目录下,例如D:\workspace\facishare, 得到以下目录结构
|-- lib
  apl-sdk-maven-plugin.jar maven插件
  fs-paas-function-api.jar 代码依赖的api包
|-- src
  |-- main
    |-- java
      |-- fx
        |-- custom
    	  |-- apl
            |-- jar
        	  代码示例
	|-- resources
      application.properties 租户开发证书
pom.xml
README.md 本说明文件

  • 打开IDE导入项目

图片alt

# 环境准备工作

修改pom.xml中的 artifactId为Jar包ApiName

apiName 命名规则:

1.只允许英文字母开头

2.中间允许使用英文字母,数字,下划线

3.下划线不允许连续出现,生成后会在apl平台以__c结尾

4.不能超过50个字符

图片alt

设置开发者证书

  • 证书申请地址:纷享销客网页版-后台管理-定制开发平台/开发者证书 (opens new window)
  • 将准备工作中的开发者证书,设置到application.properties中

图片alt

首次安装apl插件

  • IDE进入控制台
  • 在控制台输入以下命令

mvn install:install-file "-Dpackaging=maven-plugin" "-Dfile=lib/apl-sdk-maven-plugin.jar" "-DgroupId=com.facishare" "-DartifactId=apl-sdk-maven-plugin" "-Dversion=1.0-SNAPSHOT"

安装sdk

  • 在控制台输入以下命令

mvn install:install-file "-Dpackaging=jar" "-Dfile=lib/fs-paas-function-api.jar" "-DgroupId=com.facishare" "-DartifactId=fs-paas-function-api" "-Dversion=1.0-DEBUG-SNAPSHOT" "-Dsources=lib/fs-paas-function-api-sources.jar"

  • 命令执行成功,会看到下图

图片alt

安装apl插件后,在maven项目的plugin中可以看到目前支持的插件列表

img.png

所有插件可通过双击该插件,或maven命令行操作;如mvn apl-sdk:sdk

插件功能说明

  • apl-sdk:pluginUpgrade 该命令作用为apl插件的自更新,函数有新的插件功能时可以通过该命令更新

  • apl-sdk:pluginUpgrade-dev 该命令作用同样为apl插件的自更新(包含灰度测试阶段的插件)

  • apl-sdk:pushJar 该命名作用为上传jar包,需要提前手动对项目进行打包,通过该命令将该包上传到函数服务器中

  • apl-sdk:pkgAndPushJar 该命令是pushJar的升级版,在上传jar包前会自动进行打包,简化上传jar包操作

  • apl-sdk:sdk 该命令是用来下载/升级api-jar包的,首先会拉取最新版本api包自动下载到lib/fs-paas-function-api.jar,其次会进行api-jar包打包到本地mvn仓库,最后会进行项目打包,生成新的项目jar包

  • apl-sdk:sdk-dev 该命令是用来下载/升级api-jar“灰度包”的,包含函数内部灰度的一些apu功能也会在该jar包体现

注:

  • 1.该插件需要配置mvn环境变量配置,支持全局mvn命令操作
  • 2.windows电脑如果使用插件报错"CreateProcess error=2, 系统找不到指定的文件",尝试使用上方的插件自更新命令尝试解决;如果更新后仍无效则联系技术同学支持
  • 3.如果安装了新版本插件后,仍没有图中插件提示,可以清理idea缓存(File/Invalidate Caches),重新加载maven后查看;
  • 4.当前项目idea的maven配置,需要与电脑中的mvn环境配置,保持一致,否则下载sdk会失败

首次安装sdk(下载函数api依赖jar包) 通过上方插件的apl-sdk:sdk /apl-sdk:sdk-dev来进行函数api jar包的下载与升级

该命令是用来升级api-jar包的,首先会拉取最新版本api包自动下载到lib/fs-paas-function-api.jar,其次会进行api-jar包打包到本地mvn仓库,最后会进行项目打包,生成新的项目jar包;

img_1.png

如果idea有缓存没有显示插件列表,可以清理idea缓存处理,也可以通过如下命令行进行操作

mvn apl-sdk:sdk

下载/升级结果

img_2.png

# 开始开发

# 新建代码

# 在fx.custom.apl.jar下新建代码

图片alt

实现函数的命名空间对应的接口,所有已支持的接口在 com.fxiaoke.functions.template, 如下图 接口对应的实现类模板,在fx.custom.apl.template路径

图片alt

一个新的APL代码新建就好了,如下图所示

图片alt

# 增加main函数,以方便进行代码调试

DebugHelper helper = new DebugHelper(); //构造一个调试器 helper.init(); //对调试器进行初始化 FunctionContext context = helper.context("AccountObj", "63100e7915d6a300017121cc"); //获取一个调试上下文,以方便写代码 Map<String, Object> argument = Maps.newHashMap(); //如果函数需要有其他参数,可以构造一个Map,进行参数传递 最后效果如下图

图片alt

# 开始开发

  • 在代码中,可以使用Fx进行对象的查询、修改、以及新增,例如 QueryResult ret = Fx.object.select(String.format("SELECT _id, name FROM AccountObj WHERE name = '%s'", search)).result();
  • 在代码需要按照接口的约定,进行参数返回,如下图所示

图片alt

# 调试

  • 点击main函数最左边绿色的箭头,可以对进行项目的编译以及调试
  • 可以通过IDE,对代码进行断点分析,如下图所示

图片alt

# 将代码发布到租户

# 方式一:通过插件上传

打开插件,找到Plugins的apl-sdk插件,点击apl-sdk:pkgAndPushJar,会自动打包并上传jar包

img_3.png

# 方式2:手动打包并上传

部分场景需要手动打包,打开IDE的Maven插件. 找到Lifecycle中的,package/install进行打包,如图所示;或者直接通过命令行mvn clean package 或者 mvn clean install打包

然后通过后台管理,开发Jar包管理来手动上传jar包(三方提供的包也通过该方式手动上传)

img_4.png

# 开发Jar包管理

  • Jar包上传时进行默认安装,如果代码存在编译问题,会在上传阶段提示报错信息,请按照提示信息进行修改代码后重新上传代码
  • Jar包管理后台地址:纷享销客网页版/后台管理/开发Jar包管理,也可在后台直接搜索开发Jar包管理
  • 可以根据artifactId,找到刚才开发Jar的ApiName
  • 更新Jar包方式,1.直接本地修改后重新上传Jar包;2 可以通过Jar包管理平台进行手动卸载并安装Jar包

图片alt 代码上传报错提示 图片alt

# 关联场景

  • 找到需要执行代码的地方,新建函数

图片alt

  • 选择Jar包函数
  • ApiName输入 类名 + __c结尾,以便定位到具体类名
  • 新建函数的命名空间、返回值与类对应的接口必须一致,否则会执行失败

图片alt

  • 新建完成后,就可以进行验证

# 注意事项

  • 首次安装成功后,第二次推送Jar包,会直接更新Jar包内容,同时也会直接影响租户使用
  • 实施/用户按照标准代码管理规范,进行代码分支管理
  • Jar限制大小为10兆,切勿将第三方Jar包一并打包上传
  • 如有依赖第三方如es等Jar包,请到管理后台进行手动上传
  • APL自带FastJson以及Guava等通用工具包,如果上传了通用工具包,会以APL优先
  • 用户证书拥有对应有效期,如果用户停用、禁止登录、修改密码会失效,需要重新申请

# 代码Demo

这里举例个别代码示例,如按钮前验证代码,流程代码,范围规则代码,更多示例代码详见详见fx.custom.apl.template包下代码

# 按钮前验证代码示例


package fx.custom.apl.jar;

import com.fxiaoke.functions.FunctionContext;
import com.fxiaoke.functions.Fx;
import com.fxiaoke.functions.client.DebugHelper;
import com.fxiaoke.functions.model.ButtonValidateResult;
import com.fxiaoke.functions.model.QueryResult;
import com.fxiaoke.functions.template.IButtonBeforeAction;
import com.fxiaoke.functions.utils.Maps;
import com.google.common.collect.ImmutableMap;
import com.mycompany.pkg.MyTest;

import java.io.IOException;
import java.util.Map;

import static com.fxiaoke.functions.Fx.log;

/**
 * 1. 根据命名空间以及返回,选择对应的Action
 * 2. 这个Action是函数触发入口,对象勾子触发时,会到Jar包中查找这个类并且检查接口以及接口提供的方法
 * <p>
 * <p>
 * 全部已支持的action详见以下package
 *
 * @author APL
 * @see com.fxiaoke.functions.template
 */
public class ButtonBlockAction implements IButtonBeforeAction {

    MyTest test = new MyTest();

    @Override
    public ButtonValidateResult validate(FunctionContext context, Map<String, Object> map) {
        //从模拟的上下文获取到调试的name字段
        String name = (String) context.getData().get("name");

        //可以自定义包名以及类名,执行逻辑
        String testName = test.DemoFunc(name);

        //使用APL平台提供的 log.info 进行日志输出
        //切勿使用 System.out.println(); 或者其他日志组件进行打印日志
        log.info(testName);

        //假设我们需要从代码里查询名字为test的客户
        String search = "test";

        QueryResult ret = Fx.object.select(String.format("SELECT _id, name FROM AccountObj WHERE name = '%s'", search)).result();

        Map<String, Object> obj = (Map<String, Object>) ret
                .getDataList()
                .stream()
                .findFirst()
                .orElseGet(() -> ImmutableMap.of("name", "测试"));

        return new ButtonValidateResult(
                true,
                "客户名字是:" + obj.get("name"),
                true);
    }

    /**
     * 调试入口
     */
    public static void main(String[] args) throws IOException {
        //调试器
        DebugHelper helper = new DebugHelper();
        //调试器初始化,包括身份以及服务器的地址
        //身份信息联系APL平台获取,具体查看application.properties配置
        helper.init();

        //构造当前执行类
        ButtonBlockAction example = new ButtonBlockAction();
        //模拟调试的上下文,例如开发时想模拟一个客户对象的上下文,以方便开发
        FunctionContext context = helper.context("AccountObj", "63100e7915d6a300017121cc");

        //构造被触发时的参数
        Map<String, Object> argument = Maps.newHashMap();

        //执行函数
        example.validate(context, argument);
    }
}


# 流程代码示例


package fx.custom.apl.jar;

import com.fxiaoke.functions.FunctionContext;
import com.fxiaoke.functions.Fx;
import com.fxiaoke.functions.client.DebugHelper;
import com.fxiaoke.functions.model.APIResult;
import com.fxiaoke.functions.template.IFlowAction;
import com.fxiaoke.functions.tools.ActionAttribute;
import com.fxiaoke.functions.utils.Maps;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static com.fxiaoke.functions.Fx.log;

public class FlowActionExample implements IFlowAction {

    public static void main(String[] args) throws IOException {
        //调试器
        DebugHelper helper = new DebugHelper();
        //调试器初始化,包括身份以及服务器的地址
        //身份信息联系APL平台获取,具体查看application.properties配置
        helper.init();

        //构造当前执行类
        FlowActionExample example = new FlowActionExample();
        //模拟调试的上下文,例如开发时想模拟一个客户对象的上下文,以方便开发
        FunctionContext context = helper.context("AccountObj", "63100e7915d6a300017121cc");

        //构造被触发时的参数
        Map<String, Object> argument = Maps.newHashMap();

        //执行函数
        example.execute(context, argument);
    }

    @Override
    public void execute(FunctionContext functionContext, Map<String, Object> map) {
        ActionAttribute attribute = ActionAttribute.create();

        //使用APL平台提供的 log.info 进行日志输出
        //切勿使用 System.out.println(); 或者其他日志组件进行打印日志
        log.info(functionContext);


        HashMap<Object, Object> account = new HashMap<>();
        account.put("name", System.currentTimeMillis());

        APIResult result = Fx.object.create("AccountObj", account, Maps.newHashMap(), attribute);
        log.info(result);
    }
}



# 范围规则代码示例


package fx.custom.apl.jar;

import com.fxiaoke.functions.FunctionContext;
import com.fxiaoke.functions.client.DebugHelper;
import com.fxiaoke.functions.model.QueryTemplate;
import com.fxiaoke.functions.template.IRangeRuleTemplateAction;
import com.fxiaoke.functions.tools.QueryOperator;
import com.fxiaoke.functions.utils.Lists;
import com.fxiaoke.functions.utils.Maps;

import java.io.IOException;
import java.util.Map;

import static com.fxiaoke.functions.Fx.log;

/**
 * 返回规则的Demo,注意返回值选择的是QueryTemplate
 */
public class RangeRuleExample implements IRangeRuleTemplateAction {
    public static void main(String[] args) throws IOException {
        //调试器
        DebugHelper helper = new DebugHelper();
        //调试器初始化,包括身份以及服务器的地址
        //身份信息联系APL平台获取,具体查看application.properties配置
        helper.init();

        //构造当前执行类
        RangeRuleExample example = new RangeRuleExample();
        //模拟调试的上下文,例如开发时想模拟一个客户对象的上下文,以方便开发
        FunctionContext context = helper.context("AccountObj", "63100e7915d6a300017121cc");

        //构造被触发时的参数
        Map<String, Object> argument = Maps.newHashMap();

        //执行函数
        example.execute(context, argument);
    }

    @Override
    public QueryTemplate execute(FunctionContext context, Map<String, Object> args) {

        //使用APL平台提供的 log.info 进行日志输出
        //切勿使用 System.out.println(); 或者其他日志组件进行打印日志
        log.info(context);

        return QueryTemplate.AND(
                Maps.of("life_status", QueryOperator.NE(Lists.newArrayList("invalid"))
                ));
    }
}


# 七、常见问题

  1. java.security.InvalidKeyException: Illegal key size 异常 参考:https://blog.csdn.net/zhuwangxiangbie/article/details/105124612

  2. 关于jar包引用 函数默认支撑大部分通用第三方包,如代码对其有依赖可直接使用,默认支持的jar,包括但不限于以下举例

    • http相关

      • org.apache.httpcomponents:httpclient:jar:4.5.2
      • org.apache.httpcomponents.client5:httpclient5:jar:5.2.1
      • com.squareup.okhttp3:okhttp:jar:3.14.9
    • util相关

      • org.apache.commons:commons-lang3:jar:3.12.0
      • org.apache.commons:commons-collections4:jar:4.4
      • dom4j:dom4j:jar:1.6.1
      • commons-codec:commons-codec:jar:1.15
      • org.codehaus.woodstox:stax2-api:jar:4.1
  3. jar缺失相关报错如何解决? img_5.png

img_6.png

cannot find matching method fx.custom.apl.jar.GetStation#afterjava.lang.NoClassDefFoundError: Unable to load class xxx due to missing dependency org/dom4j/DocumentException
  • 查看对应的jar包,如果是自定义的包,可以通过插件/手动在开发jar包里上传对应jar包;
  • 如果是一些第三方通用包,且函数未支持,可以手动上传jar包后在高代码群里反馈,函数后续考虑做支持;
  • 如果对应jar包都已上传,仍然提示类/方法/变量不存在的情况,可以在开发jar包管理中将对应jar包下载下来,反编译后对比本地代码内容是否一致; img_7.png
  1. 如何使用jar包中的代码? 如果jar包中的类只是一些工具类,并未与流程,按钮等命名空间关联,则可以在groovy代码中直接引入相关工具类,或使用全限定名直接调用;

img_8.png

如果jar包为一些命名空间的实现类,则需要创建对应jar类型APL函数代码

img_9.png

  1. 关于权限问题 目前高代码为了保障函数应用可用,做了一些安全性限制 如不允许写入系统变量,文件读取权限等,使用上如果出现相关报错,可以在高代码群反馈,由技术同学判断是否可以放开对应权限 img_10.png

  2. 关于网络问题 目前函数支持通过代理的方式访问外网网络权限 okhttp设置代理写法举例

private static Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("egress-proxy.egress", 9999));
private static OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build();

apache 设置代理写法举例

private static HttpClient httpClient = new HttpClient();
static {
// 创建HostConfiguration对象,并设置代理
HostConfiguration hostConfig = new HostConfiguration();
hostConfig.setProxy("egress-proxy.egress", 9999);
httpClient.setHostConfiguration(hostConfig);
}
  1. 关于异常处理 jar包中的代码,要注意一些错误写法,该写法会丢失异常栈信息,无法排查对应问题

错误写法举例 img_11.png

  • 一些固定异常可以通过 Fx.message.throwErrorMessage("xxx错误"); 抛出
  • 一些未知异常可以通过throw异常抛出
  1. 自定义控制器问题

注意:不推荐使用同步自定义控制器,推荐使用公共类

自定义控制器在devjar场景本地调试的话,保留这两行代码;如果要上传到服务器使用,要把这两行代码注释掉;

img_12.png

DebugHelper helper = new DebugHelper();
VSCode离线开发

VSCode离线开发→

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式