> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpertai.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 插件示例：飞书文档

本篇展示一个完整的插件示例，基于 [飞书](https://www.feishu.cn/) 平台实现。
示例包含插件功能、配置、代码结构、接口实现、增强点等内容，帮助你理解如何在 **Xpert AI 平台** 开发类似的集成插件。

***

## 背景介绍

Xpert AI 插件系统允许开发者通过 **插件扩展** 平台功能。以飞书文档为例，我们希望实现以下场景：

* **系统集成**：接入飞书应用的 AppID、AppSecret 等凭证，完成鉴权。
* **文档加载**：从飞书的文件夹或文档 API 中获取文档内容。
* **知识管理**：将文档转化为可供 Xpert AI 知识库和智能体使用的结构化内容。
* **扩展策略**：通过策略（Strategy）接口规范，实现接入、文档源、文档转换的扩展点。

最终效果是：用户只需配置好飞书集成信息，Xpert AI 就能自动从飞书文档中读取内容，并将其纳入 AI 对话和知识库之中。

***

## 1. 克隆插件模版

我们提供了一个 **插件开发起点模版**，方便快速构建新的插件：

```sh theme={null}
git clone git@github.com:xpert-ai/xpert-plugins-starter.git lark
cd lark
```

安装依赖：

```sh theme={null}
npm install
```

使用 `nx` 新建一个插件库，其中 `packages/lark` 是插件代码在项目中的目录，`@xpert-ai/plugin-lark` 是插件 npm 包名：

```sh theme={null}
npx nx g @nx/js:lib packages/lark \
  --publishable \
  --importPath=@xpert-ai/plugin-lark \
  --bundler=tsc \
  --unitTestRunner=jest \
  --linter=eslint
```

确认插件信息：

```sh theme={null}
npx nx show project @xpert-ai/plugin-lark
```

> ✅ 这里我们生成了一个名为 **`@xpert-ai/plugin-lark`** 的插件，它将承载飞书文档的所有集成功能。

***

## 2. 插件入口代码

插件的入口代码主要包含 **元信息（meta）、配置（config）、生命周期函数（register/onStart/onStop）**。

```ts theme={null}
import { z } from 'zod';
import { type XpertPlugin } from '@xpert-ai/plugin-sdk';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { initI18n } from './lib/i18n.js';
import { LarkModule } from './lib/lark.module.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const ConfigSchema = z.object({});

const plugin: XpertPlugin<z.infer<typeof ConfigSchema>> = {
  meta: {
    name: '@xpert-ai/plugin-lark',
    version: '1.0.0',
    category: 'doc-source',
    displayName: 'Lark Plugin',
    description: 'Integrate Lark functionality',
    keywords: ['lark', 'feishu', 'document source'],
    author: 'Xpert AI team',
  },
  config: {
    schema: ConfigSchema,
  },
  register(ctx) {
    ctx.logger.log('register lark plugin');
    initI18n(join(__dirname, '../src'));
    return { module: LarkModule, global: true };
  },
  async onStart(ctx) {
    ctx.logger.log('lark plugin started');
  },
  async onStop(ctx) {
    ctx.logger.log('lark plugin stopped');
  },
};

export default plugin;
```

### 核心要点

1. **meta**：定义插件的基本信息（名字、版本、分类、关键词等）。
2. **config**：描述插件的配置 Schema，用户可在 UI 中输入配置等（预留机制，暂未实现）。
3. **register**：注册 `LarkModule`，将插件挂载到 NestJS 模块体系。
4. **onStart/onStop**：插件的生命周期回调。

***

## 3. NestJS 模块结构

插件实际是一个 [NestJS 模块](https://docs.nestjs.com/modules)（`LarkModule`），它将不同的 **策略类（Strategy）、控制器（Controller）或者服务（Service）** 注册进来。

```ts theme={null}
import chalk from 'chalk';
import { XpertServerPlugin, IOnPluginBootstrap, IOnPluginDestroy } from '@xpert-ai/plugin-sdk';
import { LarkIntegrationStrategy } from './integration.strategy.js';
import { LarkController } from './lark.controller.js';
import { LarkSourceStrategy } from './source.strategy.js';
import { LarkDocTransformerStrategy } from './transformer.strategy.js';

@XpertServerPlugin({
	controllers: [LarkController],
	providers: [
		LarkIntegrationStrategy,
		LarkSourceStrategy,
		LarkDocTransformerStrategy
	]
})
export class LarkModule implements IOnPluginBootstrap, IOnPluginDestroy {
	onPluginBootstrap(): void {
		console.log(chalk.green(`${LarkModule.name} is being bootstrapped...`));
	}
	onPluginDestroy(): void {
		console.log(chalk.green(`${LarkModule.name} is being destroyed...`));
	}
}
```

> ✅ 这里的 `Strategy` 就是插件的扩展点，例如：
>
> * `LarkIntegrationStrategy`：处理与飞书系统的集成。
> * `LarkSourceStrategy`：定义如何从飞书加载文档。
> * `LarkDocTransformerStrategy`：定义如何将文档转化为知识库可用的格式。

***

## 4. API 接口

[控制器](https://docs.nestjs.com/controllers) 用于暴露插件的 REST 接口。例如：测试连接飞书。

```ts theme={null}
@Post('test')
async connect(@Body() integration: IIntegration) {
  try {
    const botInfo = await this.integrationStrategy.validateConfig(integration.options)
    return integration
  } catch (err) {
    throw new ForbiddenException('Credentials failed')
  }
}
```

调用 `/lark/test` 接口时(目前是测试系统集成连通性的默认路径规则，后续会改成统一验证接口)，系统会去验证配置是否正确（AppID/AppSecret 是否有效）。

***

## 5. 插件增强点

### 5.1 系统集成增强

[系统集成策略 `LarkIntegrationStrategy`](./integration/) 用于处理飞书应用凭证验证，获取机器人信息等。

关键逻辑：

```ts theme={null}
async validateConfig(config: TLarkIntegrationConfig) {
  if (!config.appId || !config.appSecret) {
    throw new Error('App ID and Secret required')
  }
  const larkClient = new LarkClient({ options: config } as IIntegration)
  const botInfo = await larkClient.getBotInfo()
  if (!botInfo) throw new ForbiddenException('Bot permission denied')
  return botInfo
}
```

> ✅ 这里使用 `LarkClient` 封装飞书 OpenAPI 请求。

***

### 5.2 文档源增强

[文档源策略 `LarkSourceStrategy`](./source/) 定义如何从飞书中获取文档列表。

* 配置参数：文件夹 Token、文档类型（docx/sheet/file等）。
* 加载逻辑：调用 `client.listDriveFiles(folderToken)`，返回 `Document[]`。

***

### 5.3 文档转换增强

[文档转换策略 `LarkDocTransformerStrategy`](./transformer/) 定义如何将文档内容转化为知识库条目。

* 调用飞书 API 获取文档正文。
* 将正文切分为 `Document`（可供向量化）。
* 输出为 `IKnowledgeDocument` 格式。

***

### 5.4 客户端封装

所有 API 请求都通过 `LarkClient` 统一封装 `@larksuiteoapi/node-sdk` 功能，提供以下方法：

* `getBotInfo()` 获取机器人信息
* `listDriveFiles(folderToken)` 列出文件夹内容
* `getDocumentContent(docToken)` 获取文档正文
* `getAllDocsInFolder(folderToken)` 递归获取所有文档

这样，插件的 **策略层只需调用 Client 方法**，无需关心 API 细节。

***

## 6. 开发流程总结

1. **准备模版**：克隆插件模版并生成插件项目。
2. **编写入口**：定义插件 meta、config 和生命周期。
3. **构建模块**：在 `LarkModule` 中注册控制器和策略类。
4. **实现策略**：

   * 集成策略：处理 AppID、Secret 验证。
   * 文档源策略：从飞书获取文档。
   * 转换策略：把文档转化为知识库内容。
5. **封装客户端**：统一管理飞书 API 调用。
6. **测试接口**：通过 `/lark/test` 验证集成是否正常。

## 7. 构建、发布与 npm 发布流程

当我们完成了 `@xpert-ai/plugin-lark` 插件的开发，就可以通过 **Nx 提供的构建与发布工具链** 将其构建并发布到 npm 仓库。整个流程分为以下几个步骤：

### 7.1 构建插件

执行构建命令：

```bash theme={null}
npx nx build @xpert-ai/plugin-lark
```

* 该命令会调用 Nx 的构建器（默认使用 TypeScript 编译器 `tsc`），将源码编译为 JavaScript。
* 构建结果会输出到：

  ```
  packages/lark/dist
  ```
* 输出目录中的内容就是可发布的产物（包含 `index.js`、类型声明 `.d.ts` 等）。

### 7.2 版本管理

为了确保 npm 包的迭代规范，需要正确维护插件版本号。
推荐使用 Nx 提供的 **release 流程** 自动管理版本：

#### 自动 bump 版本

```bash theme={null}
npx nx release patch @xpert-ai/plugin-lark
```

* `patch`：修复版本号（例如 1.0.0 → 1.0.1）
* `minor`：小版本更新（例如 1.0.0 → 1.1.0）
* `major`：大版本更新（例如 1.0.0 → 2.0.0）

命令会自动：

1. 修改 `package.json` 中的版本号。
2. 更新 `CHANGELOG.md`。
3. 生成对应的 Git tag。

> 如果不使用 `nx release`，也可以手动修改 `package.json` 的 `version` 字段。

### 7.3 发布到 npm

构建和版本 bump 完成后，就可以执行发布命令：

```bash theme={null}
npx nx run @xpert-ai/plugin-lark:nx-release-publish --access public --otp=<one-time-password-if-needed>
```

* `--access public`
  指定包为公开发布（适用于 `@xpert-ai` 这种 npm scope 下的包）。
* `--otp=<code>`
  如果 npm 账号开启了 **双因素认证 (2FA)**，需要输入一次性密码 (OTP)。

> ⚠️ 注意：发布前请确认 `package.json` 中以下字段是否正确：
>
> * **`main`**：指向构建产物入口，例如 `"dist/index.js"`
> * **`types`**：指向类型声明，例如 `"dist/index.d.ts"`
> * **`files`**：确保只包含需要发布的文件（例如 `"dist/**/*"`）
> * **`publishConfig`**：可定义默认发布权限（如 `"access": "public"`）

### 7.4 最终流程示例

综合上述步骤，发布 `@xpert-ai/plugin-lark` 插件的完整命令如下：

```bash theme={null}
# 1. 构建插件
npx nx build @xpert-ai/plugin-lark

# 2. 自动 bump 版本（此处为 patch 版本）
npx nx release patch @xpert-ai/plugin-lark

# 3. 发布到 npm
npx nx run @xpert-ai/plugin-lark:nx-release-publish --access public --otp=<one-time-password-if-needed>
```

执行成功后，npm 上就能看到最新的 `@xpert-ai/plugin-lark` 版本。 🎉

***

## 8. 最终使用效果

完成插件开发后，你可以在 Xpert AI 平台中：

* 配置飞书 AppID/AppSecret
* 选择文档文件夹和类型
* 自动加载飞书文档内容
* 将文档转化为知识库，支持 AI 对话和智能体分析
