开发指南/历史版本（不推荐）/多维表格插件/多维表格自动化插件开发指南
# 多维表格自动化插件开发指南

## 步骤一：创建插件应用

1. 登录[Lark开发者后台](https://open.larksuite.com/app)。
2. 在开发者后台首页，单击 **创建企业自建应用**，填写应用名称、描述以及图标信息，然后单击 **创建**。
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/acdac87264faa1ba8ff7858aaf4907cb_QKSomb7ff1.png?height=1526&lazyload=true&width=2512)
> 本指南以 **企业自建应用** 为例介绍如何开发多维表格自动化插件。如果你想创建一个给不同企业使用的应用，可选择 **创建商店应用**。创建商店应用请参考[服务商入驻](https://open.larksuite.com/document/uMzNwEjLzcDMx4yM3ATM/uUzNwEjL1cDMx4SN3ATM)。关于 **商店应用** 和 **企业自建应用** 更详细的区别和介绍请查阅 [应用类型简介 - 快速了解Lark中的应用类型](https://open.larksuite.com/document/home/app-types-introduction/overview)。

3. 点击左侧 **添加应用能力**，选择 **多维表格插件** ，选择 **自动化操作**，最后点击 **+添加**。

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/e06a2a5eb526c96c32ed650642e8bf5d_EJ5qigG02C.png?height=1652&lazyload=true&width=3088)![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/105e562413a98f7a8b7bd29a8b77a656_ngZUGVKrne.png?height=1652&lazyload=true&width=3088)

4. 记录 App ID 和 BlockTypeID。

在创建出来的页面，记录下 BlockTypeID，切换到 **凭证与基础信息**，记录下 App ID。App ID 和 BlockTypeID 将用于项目初始化。

![41.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/dc334acaf04cf480c04a04101c2b0b6e_cNJugKMaVu.png?height=1388&lazyload=true&width=2824)

![51.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/c9aeb9dcb9eddbae2616f5bc414fd9ba_6zLI5Y39kv.png?height=1288&lazyload=true&width=2954)

## 步骤二：搭建开发环境

你需要在本地安装Lark开发者工具并使用工具初始化自动化插件项目。

1. 安装 CLI。
```
npm install @lark-opdev/cli@latest -g -f
```
> CLI 详细使用方式和指令请参考：[Lark开发者工具-命令行](https://open.larksuite.com/document/uYjL24iN/uEzMzUjLxMzM14SMzMTN/ide-with-commands)。
2. 登录 CLI。
    - CLI 初次使用需要登录，打开终端，执行下面命令登录：
      ```
      opdev login
      ```
    - 开发环境选择 `Lark`：

![cli登陆.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/518971a6fc6cbdcef4e3484fc70a06a4_IliFk63Td5.png?height=264&lazyload=true&width=1680)

选择开发环境后会自动打开浏览器扫码登录页面，请根据指示进行登录：

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/29a9d31d89de178dbb46556595685fc0_PqS8sjyOOM.png?height=1826&lazyload=true&width=2344)

扫码完成登录后命令行会提示登录成功：

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/d09ec83ffa1cff6b5afba3ffde0f46d9_g51b5f5wCO.png?height=100&lazyload=true&width=734)

3. 创建项目。使用下面命令创建模板项目：

- app-dir 需替换成你项目的目录名。
    - action-starter 需替换成你插件的目录名。
    ```
    opdev create app-dir/action-starter -a bitable-extensions -s automated-action
    ```
    创建过程中会询问 "Do you need to create a new application?"

- 选择 **No**，则会要求填写步骤一中获取的 ID 信息 ： App ID 和 BlockTypeID。

你也可以在创建完成后的项目中修改 `block.json` 文件中的 `blockTypeID` 和 `app.json` 文件中的`appId`，让代码用于你的其它应用插件。关于这两个文件的详细说明，可以参考本文常见问题小节中的**代码上传的原理与二次开发**。

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/52ab1b93fb43db4597f8e9d02a88fd92_CNV7g3aPaF.png?height=180&lazyload=true&width=1654)

> 在生成模板的 block.json 文件中的 blockTypeID 就是初始化的时候命令行中所填的 BlockTypeID。
    >
    > `app.json` 文件中的 `appId` 就是初始化的时候命令行中所填写的 App ID。

- 选择 **Yes**：

- 如果没有 ISV 资质，将自动创建一个企业自建应用以及一个自动化插件。
    	- 如果有 ISV 资质，则可以选择创建商店应用`Store Apps`，或者企业自建应用 `Custom Apps`。

![32.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/140eefe0ac29cfd4ea04c33063b7ca33_5z2BlTnurV.png?height=396&lazyload=true&width=3270)

4. 初始化项目。
使用下面命令安装项目依赖：
    ```
    cd ${app-dir}/${action-starter} 
    npm install
    ```

## 步骤三：调试

在入口文件`src/register.ts` 内调用`basekit.addAction`即可定义自动化插件，插件核心是由 `formItems`、`execute` 和 `resultType` 三个属性组成，详细使用方式请查看[addAction](https://open.larksuite.com/document/uAjLw4CM/uYjL24iN/base-extensions/base-automation-extensions/api/addaction)。

### formItems

`formItems`用于定义自动化表单的UI，以及接收用户传入的参数，例如你可以使用`Input`和`SingleSelect`组件配置文本输入和单选组件。关于formltems属性的详细说明参见[FormItem](https://open.larksuite.com/document/uAjLw4CM/uYjL24iN/base-extensions/base-automation-extensions/data-type/formitem)。

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/48bf40caa49f0d2e7c24818539dfae52_7TIEyYDzb8.png?height=1802&lazyload=true&width=3762)

### execute

`execute`定义自动化插件的运行逻辑，该函数包含以下入参。
- `args`是实际运行时用户传入的参数，该对象的`key`会与`formItems`中定义的 `itemId` 值保持一致。
- `context`是每次调用的上下文，请访问[Context](https://open.larksuite.com/document/uAjLw4CM/uYjL24iN/base-extensions/base-automation-extensions/data-type/context) 查看具体参数。

`execute`必须要有一个返回值，这样用户才能消费你的运行结果。如果你的自动化插件不需要用户消费运行结果，可以返回一个空对象，例如以下示例代码。
```
import { basekit, ParamType } from '@lark-opdev/block-basekit-server-api';
basekit.addAction({
    // ...
    execute: async function(args, context) {
        return {};
    },
    resultType: { type: ParamType.Object },
});
```

### resultType

`resultType`用于定义自动化插件返回值的类型，这样用户才能在后续节点中引用你的返回结果。请前往[ResultType](https://open.larksuite.com/document/uAjLw4CM/uYjL24iN/base-extensions/base-automation-extensions/data-type/resulttype) 查看支持的返回类型。
注意：在`resultType`中声明的类型，需要与 `execute` 函数对应的返回值类型一致，否则运行时会报错。
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/3e63c7e12538d33f76a61c6e37c348db_Chp6OcM8tG.gif?height=1258&lazyload=true&width=1648)

### 本地调试

在插件目录下执行 `npm run test`会模拟调用`execute` 函数并在控制台输出执行结果，你可以在 `test/index.ts` 文件修改调用的入参。

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/a34843b7e37501a26fcb8b21972dc11e_bCCXRHkBqQ.gif?height=1248&lazyload=true&width=1662)

## 步骤四：发布与安装

### 发布

1. 上传代码
你可以使用下面命令来使用编译并上传：
```
npm run upload
```
上传过程中，需要输入**插件的版本号**，请使用形如 `0.0.1` 的语义化版本，且保证版本递增。
你也可以修改block.json文件中的blockTypeID和app.json文件中的appId，让代码用于你的其它应用插件。

![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/c5de5fee643f17a072f933efc0be180d_SBZL6KiZPr.png?height=132&lazyload=true&width=852)

2. 重新进入开发者后台，切换到 **多维表格自动化操作**。**小组件版本** 选刚上传时填入的版本号，然后上传图标、填写名称和介绍。最后点击 **保存**。

![52.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/823fe2c0adaaddca8c67d1402e4a5eee_qJVC5nOnwA.png?height=2014&lazyload=true&width=2732)

3. 以 **企业自建应用** 的发布流程为例。 如果你使用的是 **商店应用**，则需要注意商家发布需要经过严格的审核流程，详细流程请参考[服务商入驻](https://open.larksuite.com/document/uMzNwEjLzcDMx4yM3ATM/uUzNwEjL1cDMx4SN3ATM)。
- 切换到 **版本管理与发布**。然后点击 **创建版本**，填入 **应用版本号**、**更新说明**（注意：这里的版本是应用版本，一个应用可能有多个插件）。

![42.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/0a548859c3b7d7bf5151b2dd6eb63163_85yD69yroM.png?height=1482&lazyload=true&width=2966)

![43.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/a7af7b7a9f81a96f6b0b7f8a6b6d4229_Pw6n5NEIvO.png?height=1770&lazyload=true&width=2704)

- 确认权限和可见性范围，确认无误后点击 **保存**。
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/762521c4f364b6112c64c53808e8c6df_Ai34P9A2DJ.png?height=1540&lazyload=true&width=2164)
- 点击 **申请线上发布**，等待管理员审核。
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/f1dbc5bff364be18b918833b92f96c27_4m6bZMGsPZ.png?height=1312&lazyload=true&width=3250)

### 安装

这里以企业自建应用为例，应用审核通过后可见用户便可以将你的插件添加到多维表格自动化流程之中。
1. 点击多维表格右上角的机器人图标：
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/513c16ac27d733c104089c82acb0bdf7_vLVpvIGke9.png?height=768&lazyload=true&width=3454)
2. 然后点击「创建自动化流程」就可以看到你创建的自动化插件：
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/c25f49c3633beb05818313064066b6f4_HmsiwgIQBq.png?height=1998&lazyload=true&width=3454)

![7.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/2e908a514492fe1f577823001bdccb5e_4XUbDHql56.png?height=1670&lazyload=true&width=2256)
## 预览调试

在上面创建的项目中与 `package.json` 同级目录下打开终端，然后执行 `npm run preview` 会在控制台返回调试文档链接，访问该链接并在自动化内点击 **保存并启用**，等约 30 秒插件部署成功后，即可实现无需发布应用调试插件。

注意：由于需要重启插件，所以插件在[开放平台](https://open.larksuite.com/app)至少完成一次发布后才能使用预览调试功能。建议先用模板代码完成一次发布，再使用预览调试你的业务逻辑。
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/fbcb463a2e173ecda42574eaf884ca91_RXPzoj2dy7.png?height=2142&lazyload=true&width=2852)
如果你想在已有多维表格上调试，可以在`block.json` 的 `url`填入你多维表格链接地址。
```
// block.json
{
  "blockTypeID": "blk_xxx",
  "url": "你的多维表格链接地址"
}
```

## 常见问题

### Q：如何请求外部数据？

**请勿使用axios、got等三方类库请求**，由于安全限制，这些类库在线上是无法使用的
使用 `context.fetch` 函数请求外部数据，具体语法可见[node-fetch](https://github.com/node-fetch/node-fetch)
```
basekit.addAction({
    execute: async function(args, context) {
        const { fetch } = context;
        // 发起带Query的请求文本数据
        const query = new URLSearchParams({
            foo: 'bar',
        });
        // 发起 GET 请求text数据
        fetch(`https://api.com?${query.toString()}`).then(res => res.text());
        // 发起 POST 请求json数据
        fetch('https://api.com', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                foo: 'bar',
            }),
        }).then(res => res.json());
    },
    ...
});
```

### Q：应用如何获取文档权限？

通过Lark开放平台的OpenAPI操作云文档资源，如上传素材、获取多维表格附件的下载地址等需要[文档权限](https://open.larksuite.com/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#2431c595)，即需要应用被添加到对应文档中。通过在 `addAction` 时增加 `permission` 字段可声明该应用需要文档权限，在启用该插件时会自动将应用添加到文档中，代码如下：
```
basekit.addAction({
    // 声明需要文档权限
    permission: {
        type: 2,
    },
    ...
})
```

### Q：如何支持国际化？

支持通过 `basekit.setI18n` 方法声明国际化资源，通过`t`方法使用国际化 key，目前支持中文（`zh-CN`）、中国香港（`zh-HK`）、中国台湾（`zh-TW`）、英文（`en-US`）、日文（`ja-JP`）5种语言环境，如果当前的语言环境不属于其中5种，则会使用默认（`defaultLocale`）的语言资源。
```
import { basekit, ParamType, t } from '@lark-opdev/block-basekit-server-api';
basekit.setI18n({
  // 指定默认的国际化资源
  defaultLocale: 'en-US',
  messages: {
      // 指定中文资源
      'zh-CN': {
            "source_text": "源文本",
            "result": "转换结果"
       },
       // 指定英文资源
      'en-US': {
          "source_text": "Source Text",
          "result": "Conversion Result"
       },
  },
});
basekit.addAction({
    formItems: [
        {
            ...
            label: t('source_text'), // 使用 t(i18n_key) 指定国际化资源
            ...
        },
    ],
    resultType: {
        type: ParamType.Object,
        properties: {
            text: {
                type: ParamType.String,
                label: t('result')。 // 使用 t(i18n_key) 指定国际化资源
            }
        }
    }
});
```

### Q：在FaaS里如何获取附件的下载链接?

步骤如下：
1. 在[开平后台](https://open.larksuite.com/app)的「权限管理」申请查看多维表格的权限
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/56c9dcd503b044907f0b5a6c989233d7_O2MkA9KThq.png?height=1752&lazyload=true&width=3838)
2. 在`addAction`方法里声明`permission`字段，值为`{ type: 2 }`
2. 在`formItems`里声明里附件组件 `Attachment`
2. 在`execute`函数调用开放平台的[获取素材临时下载链接](https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/batch_get_tmp_download_url)获取临时下载url，或者通过[下载素材OpenAPI](https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/download)下载文件
```
import { basekit, Component } from '@lark-opdev/block-basekit-server-api';
basekit.addAction({
  useTenantAccessToken: true,
  permission: { type: 2 },
  formItems: [
    {
      itemId: "attachments",
      label: '附件',
      component: Component.Attachment, // 使用附件组件
      componentProps: {
        placeholder: '请选择附件',
      }
    },
  ],
  execute: async function (args, context) {
    const { attachments, tenantAccessToken } = args;
    const fileBuffer = await download(attachments[0], context);
  }
});
async function download(attachment, context) {
  const { attachmentToken, extra } = attachment;
  const searchParams = new URLSearchParams({
    extra,
  });
  const response = await context.fetch(// 不得使用axios之类的请求方法，否则线上无法使用
    `https://open.larksuite.com/open-apis/drive/v1/medias/${attachmentToken}/download?${searchParams.toString()}`,
    {
      headers: {
        Authorization: `Bearer ${context.tenantAccessToken}`,
      },
    }
  );
  if (!response.ok) {
    throw new Error(`unexpected response ${response.statusText}`);
  }
  return await response.buffer();
}
```

### Q：自动化插件实例的一些限制

1. 内存使用大小不超过1G
1. 请求的并发数不超过400

### Q：能否使用三方库

可以，除了以下不能在沙箱运行库外的npm库都能使用
- axios
- got
- bcrypt
- moment
- jsdom
- sharp

### Q：代码上传的原理与二次开发
当执行npm run upload时，开发工具@lark-opdev/cli会在执行该命令的目录同层级下寻找app.json，并识别其中的appId字段，其中appId的含义便是开放平台中的你的应用的应用凭证，如果想将代码上传到其他的应用，就可以修改app.json中的appId字段为目标应用的appId

![image.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/b62b3ab961ffb3c12969e2a8e5c983df_hMdu7r7yDc.png?height=936&lazyload=true&width=2630)

接下来会在执行该命令下一层级的目录中的block.json中找到blockTypeID，这里的blockTypeID是应用（appId对应的应用）中的某个应用能力的BlockTypeID。

需要注意应用能力的类型需要与代码的模板类型对应上，比如自动化插件代码中的blockTypeID只能填写开放平台中的**多维表格自动化操作**类型的BlockTypeID，而不可填写为**多维表格记录视图**或者**多维表格数据表视图**的BlockTypeID。

![image.png](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/e3a1c35fe6cb2b637a299fa56a9ca9e1_NQVm0eXJbG.png?height=788&lazyload=true&width=1956)

然后@lark-opdev/cli会在当前登陆用户的应用中根据根据app.json中appId找到需要上传的目标应用，然后根据blockTypeID在该应用下找到对应的应用能力，最终将代码上传到该应用能力。

如果想要将代码上传到其他的应用能力中，就可以直接修改这2个文件，之后再执行`npm run upload`。

如果上传失败，可以使用`opdev whoami` 命令查看当前登录用户。确保当前用户的开放平台中拥有app.json中appId对应的应用，并且该应用中拥有block.json中blockTypeID对应的应用能力。
