# 视频回溯 packBack-sdk 使用指南

## 使用方式

### 方式1：npm 安装

```
npm i pageback-sdk
```

```js
import Pageback from "pageback-sdk";

const pageBack = new Pageback();
Vue.prototype.$pageBack = pageBack;
```

### 方式2：CDN

CDN 下载地址为： [pageback-sdk](https://www.jsdelivr.com/package/npm/pageback-sdk)， 可选择对应版本使用或下载
```html
    <script src="https://cdn.jsdelivr.net/npm/pageback-sdk@1.0.0/dist/index.umd.js"></script>
```

## 接口说明

###  initRecord(options)

能通过pageBackSDK.initRecord(options)初始化回溯参数，Promise 接口

**参数options：**

| 名称           | 类型   | 可选 | 默认值 | 描述                                 |
| -------------- | ------ | ---- | ------ | ------------------------------------ |
| `initUrl`    | string | 否   |        | 回溯生成全局唯一序列号的接口地址 |
| `confirmUrl` | string | 否   |        | 回溯结束视频上报的接口地址       |
| `saveUrl`    | string | 否   |        | 回溯实时上传视频的接口地址      |
| `requestHooks` | object | 是   | 透传默认实现 | 请求钩子，用于入参（data/headers）改写与出参（response.data）改写，详见下方 requestHooks |


示例：
```js
this.$pageBack.initRecord({
  initUrl: "http://test.com/ltapp/rrv/v/vbtrack/get_snowflake_id",
  confirmUrl: "http://test.com/ltapp/rrv/v/vbtrack/up_trace",
  saveUrl: "http://test.com/ltapp/rrv/v/vbtrack/up_video"
});
```


注：若需要获取当前实例信息，可在执行 initRecord 之后从实例pageBack中按需获取，实例属性包含以下内容

| 名称            | 描述                                 |
| -------------- | ------------------------------------ |
| `tractId` | 视频回溯序列号 |
| `index` | 视频节点序号 |
| `firstStartTime` | 视频流程开始时间 |
| `emitData` | 当前内存中保存的采集数据 |

示例：
```js
this.$pageBack.initRecord({
  initUrl: "http://test.com/ltapp/rrv/v/vbtrack/get_snowflake_id",
  confirmUrl: "http://test.com/ltapp/rrv/v/vbtrack/up_trace",
  saveUrl: "http://test.com/ltapp/rrv/v/vbtrack/up_video"
}).then(() => {
  console.log('tractId', this.$pageBack.tractId)
});
```


#### requestHooks

`requestHooks` 在 `initRecord` 时注入，作用于全部上报请求。不传时行为与历史版本完全一致（透传）。

可用于在请求发出前改写请求体、请求头，以及在响应返回后改写响应体（如国密加解密、header 扩展）。SDK 不内置加解密算法，加密、解密、header 扩展均由接入方在 hook 内自行实现。


**作用的上报链路**

| 场景 | 触发点 | 接口 | apiType |
|------|--------|------|---------|
| 初始化取号 | `initRecord` | `initUrl` GET | `'init'` |
| 实时/阈值上传 | `startRecord` → `emit` → `postData`；`stopRecord` 收尾也会 `postData` | `saveUrl` POST | `'save'` |
| 结束确认 | `stopRecord(type=1)` | `confirmUrl` POST | `'confirm'` |

**钩子一览**

| 名称 | 类型 | 可选 | 描述 |
| ---- | ---- | ---- | ---- |
| `onRequest` | `({ data, headers, apiType }) => { data, headers }` | 是 | 入参 hook（**同步**）。可分别更新请求体 `data` 与请求头 `headers`，**返回值也只包含这两项** |
| `onResponse` | `({ data, apiType }) => { data }` | 是 | 出参 hook（**同步**）。入参 `data` 为响应体 `response.data`，`apiType` 用于区分接口（`init`/`save`/`confirm`）。**返回值必须为 `{ data: any }` 形式**，SDK 取其 `.data` 作为新的 `response.data` |

**onRequest 入参字段**

| 字段 | 类型 | 描述 |
| ---- | ---- | ---- |
| `data` | any | 原始请求体（POST 为已 `JSON.stringify` 的字符串；GET 为 query） |
| `headers` | object | 当前请求头，默认含 `content-type` |
| `apiType` | `'init' \| 'save' \| 'confirm'` | 接口类型：`init` 初始化取号、`save` 实时上传、`confirm` 结束上报（仅 hook 上下文可见，不进入请求） |

**onRequest 返回约定**

```js
// 必须返回对象，仅识别 data / headers 两个字段（可只改其中一个）
return {
  data,     // 改写后的请求体（如加密串）
  headers,  // 会与默认 headers 合并，同名 key 以 hook 返回值为准
};
```

**onResponse 入参字段**

| 字段 | 类型 | 描述 |
| ---- | ---- | ---- |
| `data` | any | 原始响应 data（response.data） |
| `apiType` | `'init' \| 'save' \| 'confirm'` | 接口类型：`init` 初始化取号、`save` 实时上传、`confirm` 结束上报（仅 hook 上下文可见，不进入请求） |

**onResponse 返回约定**

```js
return {
  data,     // 改写后的响应体
};
```

**完整示例（国密/自定义加解密场景）**

```js
// geneEncryptPayload / decryptBizResponse 为接入方自有实现，SDK 不内置加解密算法
this.$pageBack.initRecord({
  initUrl: "http://test.com/ltapp/rrv/v/vbtrack/get_snowflake_id",
  confirmUrl: "http://test.com/ltapp/rrv/v/vbtrack/up_trace",
  saveUrl: "http://test.com/ltapp/rrv/v/vbtrack/up_video",
  requestHooks: {
    // 入参 hook：在同一处完成 body 加密 + header 扩展，返回 { data, headers }
    onRequest({ data, headers, apiType }) {
      if (apiType === "init") {
        // 获取traceId时特有逻辑
      }

      const { encrypt, aesKeyEn } = geneEncryptPayload(data);
      return {
        data: `encrypt=${encrypt}`,
        headers: {
          ...headers,
          "content-type": "application/x-www-form-urlencoded;charset=utf-8",
          hssessionid: localStorage.getItem("sessionId") || "",
          secretKey: aesKeyEn,
        },
      };
    },

    // 出参 hook：处理 response.data，可根据 apiType 区分场景
    onResponse({ data, apiType }) {
      return { data: decryptBizResponse(data) };
    },
  },
});
```

**执行时序**

```
onRequest({ data, headers, apiType })
        │  返回 { data, headers }
        ▼
   发送请求（使用 hook 返回的 data / headers）
        │
        ▼
onResponse({ response.data, apiType })
        │  返回 { data: any }，SDK 取 data 作为新的 response.data
        ▼
   SDK 按既有逻辑消费（如 tractId / error_no）
```

**说明与边界**

1. 执行顺序：`onRequest` → 发送 → `onResponse`。
2. 两个 hook **均为同步**，不支持返回 Promise。
3. `onRequest` 返回的 `headers` 会与默认 headers **合并**（同名 key 以 hook 返回值为准），无需手动保留 `content-type` 等。
4. hooks 在 `initRecord` 时以不可枚举、不可写属性写入实例，运行期无法通过 `pageBack.requestHooks` 等公开属性直接篡改。
5. 多页整页跳转后 JS 上下文是新的，hooks 不会自动继承；新页需按既有方式重新 `initRecord`（含 `requestHooks`）后再 `startRecord`。
6. `apiType` 仅存在于 hook 上下文，用于区分三类接口（`init` 取号 / `save` 实时上传 / `confirm` 结束上报），不会作为请求字段发送。


### startRecord(options)

能通过packBack.startRecord(options) 开启回溯，Promise 接口

**参数options：**

| 名称              | 类型   | 可选 | 默认值       | 描述                                                                                           |
| ----------------- | ------ | ---- | ------------ | ---------------------------------------------------------------------------------------------- |
| `page_id`       | string | 是   | ''         | 页面id，默认 '' |
| `page_name`     | string | 是   | ''           | 页面名称，默认 '' |
| `thresholdSize` | number | 是   | `512*1024` | 实时上报字符数量阈值，number类型，默认 `512*1024`，上限 3 M  |
| `recordConfig`  | object | 是   |              | 录制配置参数，具体见下方 recordConfig 参数参考 |


**recordConfig 参数参考**

| key | 默认项 | 功能 |
| --- | --- | --- |
| isPack | false | 是否开启单数据压缩 |
| blockClass | 'rr-block' | 字符串或正则表达式，可用于自定义屏蔽元素的类名 |
| blockSelector | null | 字符串或正则表达式，可用于自定义屏蔽元素的类名 |
| maskTextClass | 'rr-mask' | 字符串或正则表达式，可用于自定义忽略元素 text 内容的类名 |
| maskTextSelector | null | null 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 |
| maskAllInputs | false | 将所有输入内容记录为 \* |
| recordCanvas | false | 是否记录 canvas 内容, 可用选项：false, true |
| assetsUrl | null | 静态资源服务地址。通过该参数可以将项目内图片、css资源路径改写成指定的静态资源服务地址 |
| assetsVersion | null | 静态资源服务资源路径。通过该参数可以将项目内图片、css资源路径改写成指定的静态资源服务地址 |
| assetsReplace | [] | 参数类型 {origin: string; target: string}[] 。静态资源路径替换。例如：当配置参数如下：[{origin:"http://www.aaa.com",target:"http://www.bbb.com"}],会将录制页面中域名为http://www.aaa.com 的图片替换成 http://www.bbb.com ,最终回放界面以http://www.bbb.com 的资源进行回放 |


更多录制参数参见：[rrweb 配置参数](https://github.com/rrweb-io/rrweb/blob/master/guide.zh_CN.md#%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0)

示例：
```js
this.$pageBack.startRecord({
  page_id: "0001",
  page_name: "测试1",
  thresholdSize: 512,
  recordConfig: {
    inlineStylesheet: true,
    recordCanvas: true,
    sampling: {
      canvas: 10,
    },
    // 图像的格式
    dataURLOptions: {
      type: 'image/webp',
      quality: 0.6,
    },
    blockClass: "vc-panel"
  }
});
```
### stopRecord(type, bizInfo)

能通过packBack.stopRecord暂停或结束回溯，Promise 接口

**参数：**

| 名称        | 类型   | 可选 | 默认值 | 描述                                                     |
| ----------- | ------ | ---- | ------ | -------------------------------------------------------- |
| `type`    | number | 是   | 0      | 停止录制类型。0:暂停录制，1:结束录制                 |
| `bizInfo` | object | 否   | ''     | 回溯流程标记相关参数，暂停录制时可不填，结束录制必选 |

**bizInfo**

| 名称                | 类型   | 可选 | 默认值 | 描述                                           |
| ------------------- | ------ | ---- | ------ | ---------------------------------------------- |
| `biz_no`          | string | 否   |        | 流程号，必传，必须在平台存在，否则无法上报 |
| `sdk_version`     | string | 否   |        | sdk版本                                    |
| `product_id`      | string | 是   |        | 产品id                                     |
| `product_name`    | string | 是   |        | 产品名                                     |
| `product_version` | string | 是   |        | 产品版本                                |

示例：
```js
this.$pageBack.stopRecord(1, {
  biz_no: "LC0001", // 流程号
  sdk_version: "0.0.1", // sdk版本
  product_id: "index-A", // 产品id
  product_name: "测试demopdf", // 产品名
  product_version: "0.1.1", // 产品版本
});
```

## 兼容性
| IE	| Chrome	| Safari	| iOS |
| ------------------- | ------ | ---- | ------ |
| 11+	| 68+	| 10.1+ | 	10.1+ |


