# 飞书云文档支持三方评审方案

某客户使用飞书云文档时有这样一个场景：
> 文档编写完成后，作者可以发起文档评审。相关评委需要对文档的内容发起评审，输入专业意见。文档编写者收到评审意见后，确认是否采纳，修改文档内容。

最早客户是基于文档的 **划词评论** 功能来做评审的，但是在推进的过程中遇到以下几个问题：
**注意事项**：1. 评论关联的正文内容删除后，评论卡片会消失
1. 无法识别评审中提出的问题是否解决了
1. 评审数据散落在各篇文档里，无法结构化留存
1. 评论卡片的 UI 结构无法定制化

# 业务诉求

划词评论无法完全满足业务诉求主要体现在两个方面：1. 评论数据的存储；2. 评论面板的交互。如果这些能力可以由业务方定制化开发，那是不是就能解决前面提到的几个问题了？

[飞书云文档组件](https://open.feishu.cn/web-component/docs-component/) 支持客户将云文档集成到自己的系统中，并且提供了一系列的配置，允许定制文档部分功能的 UI 表现，这其中就包含划词评论功能。因此，使用飞书云文档组件，基于云文档的划词评论能力，辅以部分定制化开放，即可实现 **文档支持三方定制化评审** 的场景。

# 实现方案

## 整体交互
![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/d317e2aa3b527d78fc5ebe604683ca3e_gcC3wfHCF6.png?height=1028&lazyload=true&width=3184)

## 流程图

![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/230c1ec8f9900d9c06afdd157ccb94bc_qilYkjwl59.png?height=1528&lazyload=true&width=1548)

## 实现步骤

### 接入云文档组件

首先要 [接入云文档组件](https://open.feishu.cn/document/uAjLw4CM/uYjL24iN/docs-add-on/03-cloud-document-widget-quick-development-guide/03-cloud-document-widget-quick-developme)，以确保业务系统能够正常嵌入云文档并与之进行交互 。
其次，在业务系统内实现一个自定义的评审面板，用于后续和飞书文档做评审数据的流通。

![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/a779f3c6b1807fdcda2f6392c6788d2e_sXQcZj1oin.png?height=958&lazyload=true&width=1326)

### 定制化云文档组件配置

添加部分自定义配置，以实现“评论能力”由业务系统的评审面板接管。
1. 隐藏评论面板。参考示例里的 [extensions.comment](https://open.feishu.cn/document/uYjL24iN/uYDO3YjL2gzN24iN3cjN/feature-config)。
1. 添加【评审】入口。云文档组件允许给工具栏添加自定义按钮，参考下列实例里的 [extensions.content.toolbox.customToolBoxItem](https://open.feishu.cn/document/uYjL24iN/uYDO3YjL2gzN24iN3cjN/feature-config)。
```js
<script src="https://sf1-scmcdn-cn.feishucdn.com/obj/feishu-static/docComponentSdk/lib/1.0.3.js"></script>
const myComponent = new window.DocComponentSdk({
  src: "https://bytedance.feishu.cn/docx/xxxxxxxx",
  mount: document.querySelector('#doc-pos'), 
  auth: {...},
  config: {
    extensions: {
        // 配置隐藏文档中的配置面板, 因为宿主会在外部自行实现对应的「评审面板」
      comment: {
        partial: {
          disable: false, //禁用局部评论
          open: false, //关闭评论面板
        }
      },
      // 配置工具栏中的自定义按钮, 会默认新增到工具栏最后
      content: {
        toolbox: {
          customToolBoxItem: [
            {
              icon: "PaSelfReviewOutlined",
              text: "评审",
              type: "Comment",
            },
          ]
        }
      }
    }
  } 
});
```
配置完成后效果如下：
![wmremove-transformed.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/654a15bd21d295c5ced046da98122a40_ZJQSkAPbg3.png?height=338&lazyload=true&width=1030)

### 监听入口点击事件

评委对文档发起评审后，选取部分文本，点击工具栏的【评审】按钮。飞书文档会生成一条临时评论数据。
此时，业务方需要：
1. 监听该事件。当收到事件时，在业务方系统内创建对应的评审卡片，注意该评审卡片应该是处于草稿态，不做数据持久化处理。
1. 缓存文档返回的临时评论卡片 ID，该ID在后续执行新增评论的操作时会作为参数被使用。
```js
myComponent.register('ON_CREATE_TEMP_COMMENT', (eventData) => {
    const {
        tempCommentId,
        quota
    } = eventData

// 创建一张评审卡片, 当对评审卡片数据做持久化时，需要对应在文档侧新增一个评论
    // tempCommentId是新增评论的参数之一
    createLocalPanel({
       tempCommentId
    })
});
```

### 新增评论

评委在评审卡片上填写完评审内容后，需要：
1. 在云文档中同时生成一张评论卡片 （调用文档[新增评论](https://open.feishu.cn/document/uYjL24iN/uYDO3YjL2gzN24iN3cjN/invoke-api/addnewcomment)完成）。
1. 将飞书文档返回的评论卡片 ID 和业务方系统的评审数据关联，并落盘评审数据（业务方自行完成）。
```js
myComponent.invoke('ADD_NEW_COMMENT', {
    // 来自上一步 ON_CREATE_TEMP_COMMENT 监听的回调数据
    tempCommentId: 'xxx',
    // 新增评论不发送通知
    notify: false,
    // 新增评论的内容
    content: '评审人：张三；问题等级：S1；评审意见：此处需要修改硬件参数'
}).then(res => {
    const {
        code,
        commentId,
    } = eventData

if(code === 0) {
       // 创建成功，提交评审卡片
    }
    // 创建一张评审面板
    saveData(commentId);
});
```

至此，实现三方评审的主流程已完成，回顾前文提到的几个问题，看看是否全部解决：
1. 评论关联的正文内容删除后，评论卡片会消失 -> **数据在业务系统** **落盘** **了，** **初始化** **评审系统时，在评审面板里渲染出来即可**
1. 无法识别评审中提出的问题是否解决了 -> **评审数据是业务自定义的，可以通过某个字段记录问题是否解决**
1. 评审数据散落在各篇文档里，无法结构化留存 -> **在业务系统中，业务方可以根据评审状态、人员、类型...自由组织聚合数据**
1. 评论卡片的 UI 结构无法定制化 -> **评审面板独立于文档外，完全由业务自定义**

## 交互场景

### 点击正文内容，激活对应评审卡片

当用户点击文档中黄色下划线的内容时，业务系统预期会作出响应，如高亮对应的评审卡片，这个交互需要业务方：
- 监听云文档的激活评论事件
- 触发回调后，激活业务系统中对应的评审卡片
```js
 // 监听文档中点击评论内容的事件
myComponent.register('ON_ACTIVE_COMMENT', (function(eventData) {
   const {
       commentId // 评论 id
   } = eventData

// 激活对应的评审卡片
   activeCard(commentId)
});
```

### 点击评审卡片，转至对应的审批内容

当用户点击业务系统中的评审卡片时，预期是云文档会作出响应，滚动到对应的评论内容位置，这个交互需要业务方：
- 监听业务系统中评审卡片的激活事件
- 触发回调后，调用`JUMP_TO_COMMENT` API，让云文档滚动至对应评论内容位置
```js
// 点击宿主的评审卡片时，跳转到对应评论
myComponent.invoke('JUMP_TO_COMMENT', commentId).then(function(response) {
  const { code, msg } = response;
});
```

# Q & A

## Q：评审卡片提交时是否会创建一条真实文档评论？

**A**：是的，使用文档链接而非在评审系统里打开文档时，会看到评审过程中生成的评论卡片。
![wmremove-transformed (1).png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/b821640836ff9cb1ead36785d1e80893_x7m2hGAvae.png?height=486&lazyload=true&width=566)

## Q：创建评审卡片时会发送飞书通知吗？如何关闭？

**A：** ADD_NEW_COMMENT 事件提供了一个 `notify` 配置，当值为 `false` 时，会关闭消息通知。