npm package: @kbapp/data-analysis
Description: 开吧app埋点平台
Author: along
Latest version: 0.0.1

# 开吧App埋点平台

> ⚠️ 重要提示：本 SDK 基于 TypeScript 开发，所有 API 参数均带有明确的必填 / 选填类型标注。使用时请务必开启 TS 类型检查或在 IDE 中查看参数提示，必填项缺失会直接提示错误，选填项可按需传入，请勿忽略类型提示以避免使用异常。

## 功能特点

- 异步加载核心上报SDK，避免包体积过大
- 支持全局公共属性设置
- 提供普通事件和持续事件（开始/结束）上报
- 内置标准事件模型（DocActionEvent、DocVisitEvent）
- 完全TypeScript支持，提供完整的类型定义

## 下载

npm:

```bash
npm install @kbapp/data-analysis
```

### 或通过 script 标签引入

使用 unpkg CDN：

```html
<script src="https://unpkg.com/@kbapp/data-analysis@latest/dist/index.umd.js"></script>
```

使用 jsdelivr CDN：

```html
<script src="https://cdn.jsdelivr.net/npm/@kbapp/data-analysis@latest/dist/index.umd.js"></script>
```

> 通过 script 标签引入后，SDK 将作为全局变量 `kbDataAnalysis` 挂载在 window 对象上

## 基本使用

### 1. 初始化埋点实例

通过 npm 安装并使用 ES module 导入：

```typescript
import { DataAnalysis } from '@kbapp/data-analysis';

// 创建埋点实例，初始化时可以配置埋点上报域名和应用ID
const kbTracker = new DataAnalysis({
  sdkUrl: 'https://page.kaiba315.com.cn/js/gsido-h5-min-1.0.7.1_final.js',
  appid: '由开吧分配',
  debugger: false, // 开发环境可设为 true，开启埋点日志输出
});
```

通过 script 标签引入并使用全局变量：

```html
<script src="https://unpkg.com/@kbapp/data-analysis@latest/dist/index.umd.js"></script>

<script>
// 通过全局变量kbDataAnalysis创建埋点实例
const kbTracker = new kbDataAnalysis.DataAnalysis({
  sdkUrl: 'https://page.kaiba315.com.cn/js/gsido-h5-min-1.0.7.1_final.js',
  appid: '由开吧分配',
  debugger: false, // 开发环境可设为 true，开启埋点日志输出
});
</script>
```

### 2. 设置全局公共属性

```typescript
// 全局公共属性将附加到所有上报事件中
kbTracker.setReportGlobalAttrs({
  userId: '123456', // 用户唯一标识
  platform: 'h5',   // 平台类型（h5/ios/android）
  appVersion: '1.0.0', // APP版本号
});

// 支持多次调用，属性将合并
kbTracker.setReportGlobalAttrs({
  channel: 'official',
});
```

### 3. 上报普通事件

```typescript
// 基本事件上报
kbTracker.report({
  name: 'page_view', // 事件名称
  params: {
    pageId: 'home_page',
    stayTime: 1000,
    // 其他自定义参数...
  },
});
```

### 4. 上报持续事件

```typescript
// 开始持续事件
kbTracker.reportBeginEvent({
  name: 'video_play',
  params: {
    videoId: 'v001',
    startTime: Date.now(),
  },
});

// 结束持续事件（需要与开始事件使用相同的事件名）
kbTracker.reportEndEvent({
  name: 'video_play',
  params: {
    videoId: 'v001',
    endTime: Date.now(),
    duration: 60000,
  },
});
```

### 5. 使用标准事件模型

#### 5.1 稿件浏览事件（DocVisitEvent）

```typescript
import { DocVisitEvent } from '@kbapp/data-analysis';

kbTracker.report({
  name: 'DocVisitEvent',
  params: DocVisitEvent.createDocTrackEvent({
    biz: '业务类型',
    unit: '业务单元',
    ref1: '对象标识1 可为空',
    ref2: '对象标识2 可为空',
    ref3: '对象标识3 可为空',
    sid: '站点 ID 可为空',
    title: '稿件标题',
    channel: '所属单位 可为空',
    depart: '所属部门 可为空',
    suid: '分享人的用用户id 可为空'
  }),
});
```

#### 5.2 稿件动作事件（DocActionEvent）

```typescript
import { DocActionEvent } from '@kbapp/data-analysis';

kbTracker.report({
  name: 'DocActionEvent',
  params: DocActionEvent.createDocTrackEvent({
    act: '动作标识',
    biz: '业务类型',
    unit: '业务单元',
    ref1: '对象标识1 可为空',
    ref2: '对象标识2 可为空',
    ref3: '对象标识3 可为空',
    sid: '站点 ID 可为空',
    title: '稿件标题',
    channel: '所属单位 可为空',
    depart: '所属部门 可为空',
    suid: '分享人的用用户id 可为空'
  }),
});
```

## API 参考

### DataAnalysis 类

#### 构造函数

```typescript
constructor(params: {
  sdkUrl: string; // 个推SDK地址
  appid: string; // 埋点应用ID
  debugger?: boolean; // 是否开启调试模式
})
```

#### 方法

##### setReportGlobalAttrs
设置全局公共属性，将附加到所有上报事件中

```typescript
setReportGlobalAttrs(attrs: Record<string, any> = {}): void
```

##### report
上报普通事件

```typescript
report(params: {
  name: string; // 事件名称
  params?: Record<string, any>; // 事件参数
}): void
```

##### reportBeginEvent
上报持续事件的开始

```typescript
reportBeginEvent(params: {
  name: string; // 事件名称
  params?: Record<string, any>; // 事件参数
}): void
```

##### reportEndEvent
上报持续事件的结束

```typescript
reportEndEvent(params: {
  name: string; // 事件名称（需与开始事件相同）
  params?: Record<string, any>; // 事件参数
}): void
```

##### getGsido
获取原始的 GsIdo 对象（慎用）

```typescript
getGsido(): typeof GsIdo | undefined
```

### 标准事件模型

#### DocVisitEvent
稿件浏览事件，用于记录用户访问稿件的行为

```js
class DocVisitEvent {
    /**
     * 访问来源, {@link Doc#getDocId()} 上级页面的稿件标识. 可为空.
     */
    referer?: string

    /**
     * 业务类型. {@link KbModule}. 非空.
     */
    biz: string

    /**
     * 业务单元. 用以保持稿件定义的同构性: 同一个业务单元下的 ref 含义和层级结构总是一致的. 可为空.
     */
    unit?: string

    /**
     * 对象标识1. 非空.
     */
    ref1: string

    /**
     * 对象标识2. 可为空.
     */
    ref2?: string

    /**
     * 对象标识3. 可为空.
     */
    ref3?: string

    /**
     * 站点 ID. 可为空.
     */
    sid?: number

    /**
     * 稿件标题. 非空.
     * 此为冗余字段, 以便在分析平台查询. 考核统计场景下应使用维表.
     */
    title: string

    /**
     * 所属单位. 可为空.
     * 此为冗余字段, 以便在分析平台查询. 考核统计场景下应使用维表.
     */
    channel?: string

    /**
     * 所属部门. 部门是单位的下属组织. 可为空.
     * 此为冗余字段, 以便在分析平台查询. 考核统计场景下应使用维表.
     */
    depart?: string

    /** 分享人的用用户id, 分享出去的链接自动追加 suid=xxx */
    suid?: string

    /** 稿件标识, 拼接非空字段得到: biz-unit-ref1-ref2-ref3-siteId, 可通过 createDocTrackEvent 自动生成  */
    docId!: string

    constructor(params: Omit<DocVisitEvent, 'docId'>) {
        this.biz = params.biz
        this.unit = params.unit
        this.ref1 = params.ref1
        this.ref2 = params.ref2
        this.ref3 = params.ref3
        this.sid = params.sid
        this.title = params.title
        this.channel = params.channel
        this.depart = params.depart
        this.referer = params.referer
        this.suid = params.suid

        Object.defineProperty(this, 'docId', {
            enumerable: true,
            configurable: false,
            get: () => {
                return DocVisitEvent.generateDocId(this)
            },
        })
    }

    /**
     *
     * @description 生成稿件标识
     */
    static generateDocId(params: DocVisitEvent) {
        return [params.biz, params.unit, params.ref1, params.ref2, params.ref3, params.sid].filter((value) => !!value).join('-')
    }

    /**
     *
     * @description 生成稿件数据(自动生成docId)
     */
    static createDocTrackEvent(params: Omit<DocVisitEvent, 'docId'>) {
        return filterUndefinedProperties(new DocVisitEvent(params))
    }
}
```

#### DocActionEvent
稿件动作事件，用于记录用户对稿件的操作行为


```js
class DocActionEvent {
    /**
     * 动作标识. 以下为保留关键字, 语义相同的动作请复用如下定义:
     * 点赞: like
     * 分享: share
     * 评论: reply
     * 评论的评论: reply_r
     * 评论的点赞: reply_l
     * 投票: vote
     * 签到: sign
     * 购买: buy
     * 打赏: reward
     *
     * 动作语义不符合如上定义时, 请自行定义动作标识.
     */
    act: string

    /** 业务类型. {@link KbModule}. 非空. */
    biz: string

    /** 业务单元. 用以保持稿件定义的同构性: 同一个业务单元下的 ref 含义和层级结构总是一致的. 可为空. */
    unit?: string

    /** 对象标识1. 非空. */
    ref1: string

    /** 对象标识2. 可为空. */
    ref2?: string

    /** 对象标识3. 可为空. */
    ref3?: string

    /** 站点 ID. 可为空. */
    sid?: number

    /**
     * 稿件标题. 非空.
     * 此为冗余字段, 以便在分析平台查询. 考核统计场景下应使用维表.
     */
    title: string

    /**
     * 所属单位. 可为空.
     * 此为冗余字段, 以便在分析平台查询. 考核统计场景下应使用维表.
     */
    channel?: string

    /**
     * 所属部门. 部门是单位的下属组织. 可为空.
     * 此为冗余字段, 以便在分析平台查询. 考核统计场景下应使用维表.
     */
    depart?: string

    /** 分享人的用用户id, 分享出去的链接自动追加 suid=xxx */
    suid?: string

    /** 稿件标识, 拼接非空字段得到: biz-unit-ref1-ref2-ref3-siteId  */
    docId!: string

    constructor(params: Omit<DocActionEvent, 'docId'>) {
        this.act = params.act
        this.biz = params.biz
        this.unit = params.unit
        this.ref1 = params.ref1
        this.ref2 = params.ref2
        this.ref3 = params.ref3
        this.sid = params.sid
        this.title = params.title
        this.channel = params.channel
        this.depart = params.depart
        this.suid = params.suid

        Object.defineProperty(this, 'docId', {
            enumerable: true,
            configurable: false,
            get: () => {
                return DocActionEvent.generateDocId(this)
            },
        })
    }

    /**
     *
     * @description 生成稿件标识
     */
    static generateDocId(params: DocActionEvent) {
        return [params.biz, params.unit, params.ref1, params.ref2, params.ref3, params.sid].filter((value) => !!value).join('-')
    }

    /**
     *
     * @description 生成稿件数据(自动生成docId)
     */
    static createDocTrackEvent(params: Omit<DocActionEvent, 'docId'>) {
        return filterUndefinedProperties(new DocActionEvent(params))
    }
}
```

## 最佳实践

1. **全局唯一实例**：建议在应用入口处创建一个全局唯一的DataAnalysis实例
4. **合理使用全局属性**：将通用信息（如用户ID、平台等）设置为全局属性
5. **开发环境开启调试**：在开发环境设置`debugger: true`以查看埋点日志

## 注意事项

1. `sdkUrl`和`appid`是必需参数，需要正确配置
2. DocActionEvent中的`act`字段有标准值，语义相同时应复用（如点赞使用'like'）

## 依赖

- lodash.clonedeep: 用于深度克隆对象，确保属性合并时不影响原始数据
- @kbapp/open-jssdk: 提供开吧App环境检测和桥接功能
- @kbapp/market-partner-sdk: 提供开吧商城接入jssdk