# 移动端设备查看埋点方案 ## 一、需求与目标 - **需求**:记录用户**在哪个板块**看了**哪个设备**,以及**停留时长** - **对接接口**:`StartViewRecord`(开始会话)、`EndViewRecord`(结束会话) - **数据落库**:`sb_records` 表(F_ReId、F_Type、F_ContentName、F_DeviceName、F_AddTime、F_LeaveTime、F_DurationSeconds) --- ## 二、首页 8 个板块 vs 需埋点 6 个 | 序号 | 板块名称 | 路由/页面 | 是否埋点 | 说明 | |------|----------|-----------|----------|------| | 1 | 故障排查 | `/pages/new/gzpc/gzpc` → `detail` | ✅ | 有设备/故障关联 | | 2 | 知识库 | `/pages/new/zsk/zsk` → `detail` | ✅ | 有产品/设备关联 | | 3 | 我的设备 | `/pages/myDevice/myDevice` → `detail` | ✅ | 直接是设备 | | 4 | 资料管理 | `/pages/new/zlgl/zlgl` → `detail` | ✅ | 有设备/资料关联 | | 5 | 备件支持 | `/pages/new/bjzc/bjzc` → `detail` | ✅ | 有产品/设备关联 | | 6 | 培训展示 | `/pages/new/pxzs/pxzs` → `detail` | ✅ | 有产品关联 | | 7 | 信息推送 | `/pages/new/xxts/xxts` | ❌ | 无设备维度 | | 8 | 用户反馈 | `/pages/new/yhfk/yhfk` | ❌ | 无设备维度 | --- ## 三、埋点粒度与 reId 约定 **原则**:只在**详情页**埋点(列表页无具体“设备/资料”对象)。 **reId**:当前详情对应的**主实体 id**(用于 sb_records 的 F_ReId)。 **type**:板块名称,用于区分来源。 | 板块 | 详情页路径 | reId 来源 | type 值 | deviceName(设备名称) | contentName(查看内容) | |------|------------|-----------|---------|------------------------|-------------------------| | 故障排查 | `gzpc/detail` | `info.id` | 故障排查 | `info.sbmc` | `info.code`(故障代码) | | 知识库 | `zsk/detail` | `detail.id` | 知识库 | 后端根据 sssb 查 cpgl | `detail.sbm` | | 我的设备 | `myDevice/detail` | `options.id` | 我的设备 | `detail.sbmc` | `detail.sbmc` | | 资料管理 | `zlgl/detail` | `info.id` | 资料管理 | `info.zlm` | `info.zlm` | | 备件支持 | `bjzc/detail` | `info.id` | 备件支持 | `info.glcpmc` | `info.mc` | | 培训展示 | `pxzs/detail` | `info.id` | 培训展示 | `info.sbmc` | `info.pxmc` | > **说明**:sb_records 通过 **记录类型 + 设备名称 + 查看内容** 三字段定位。F_Type、F_DeviceName、F_ContentName 均可由前端传入,未传时后端根据 type+reId 从对应表自动解析。 --- ## 四、埋点时机 | 时机 | 动作 | 说明 | |------|------|------| | 进入详情页 | 调用 `StartViewRecord` | `onLoad` 或 `onShow` 中,拿到 reId、type 后调用 | | 离开详情页 | 调用 `EndViewRecord` | `onUnload` 或 `onHide` 中,传入之前保存的 recordId | **推荐**: - **Start**:在详情数据加载完成后(如 `gzpcxq`、`fetchDetail` 等接口返回后)调用,确保有 reId - **End**:在 `onUnload` 中调用(页面销毁时),`onHide` 可能因切后台未真正离开,可选补充 --- ## 五、各详情页数据流与埋点接入点 ### 5.1 故障排查详情 `gzpc/detail.vue` - **进入**:`onLoad` 从 `uni.getStorageSync("detail")` 取 `info`,调用 `gzpcxq({ id: info.id })` - **reId**:`this.info.id`(故障 id)或 `this.info.sssbId`(若有) - **type**:`"故障排查"` - **埋点**:`gzpcxq` 成功后 `StartViewRecord`;`ht()` 返回 / `onUnload` 时 `EndViewRecord` ### 5.2 知识库详情 `zsk/detail.vue` - **进入**:`onLoad(options)` 取 `options.id`,调用 `fetchDetail` → `zskxq({ id })` - **reId**:`this.id` 或 `this.detail.id` - **type**:`"知识库"` - **埋点**:`fetchDetail` 成功后 `StartViewRecord`;`onUnload` 时 `EndViewRecord` ### 5.3 我的设备详情 `myDevice/detail.vue` - **进入**:`onLoad(options)` 取 `options.id`,调用 `fetchDeviceDetail(deviceId)` - **reId**:`options.id` 或 `this.detail.id` - **type**:`"我的设备"` - **埋点**:`fetchDeviceDetail` 成功后 `StartViewRecord`;`onUnload` 时 `EndViewRecord` ### 5.4 资料管理详情 `zlgl/detail.vue` - **进入**:`onLoad(options)` 取 `options.id`,调用详情接口 - **reId**:`options.id` 或 `this.info.id` - **type**:`"资料管理"` - **埋点**:详情加载成功后 `StartViewRecord`;`onUnload` 时 `EndViewRecord` ### 5.5 备件支持详情 `bjzc/detail.vue` - **进入**:`onLoad` 从 `uni.getStorageSync("detail")` 取 info,调用 `bjzcxq` - **reId**:`this.info.id`(备件 id) - **type**:`"备件支持"` - **埋点**:`bjzcxq` 成功后 `StartViewRecord`;`onUnload` 时 `EndViewRecord` ### 5.6 培训展示详情 `pxzs/detail.vue` - **进入**:`onLoad` 从 `uni.getStorageSync("detail")` 取 info,调用 `pxzsxqs` - **reId**:`info.id`(培训 id) - **type**:`"培训展示"` - **埋点**:`pxzsxqs` 成功后 `StartViewRecord`;`onUnload` 时 `EndViewRecord` --- ## 六、前端实现要点 ### 6.1 API 封装 在 `uniapp_jiju/apis/modules/oauth.js`(或新建 `sbRecords.js`)中增加: ```javascript // 开始设备查看会话(进入详情页时调用) startViewRecord(data) { return request.post('/api/Extend/SbRecords/Actions/StartViewRecord', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }) }, // 结束设备查看会话(离开详情页时调用) endViewRecord(data) { return request.post('/api/Extend/SbRecords/Actions/EndViewRecord', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }) } ``` > 注意:项目规范 GET 用 data 传参;POST 表单可用 `application/x-www-form-urlencoded`,参数为 `reId`、`type`、`recordId`。 ### 6.2 参数格式 - **StartViewRecord**:`reId`(必填)、`type`(必填)、`contentName`(可选,查看内容)、`deviceName`(可选,设备名称)、`item1`(可选)、`item2`(可选) - **EndViewRecord**:`recordId`(必填,Start 返回的 id) > contentName、deviceName 未传时,后端根据 type+reId 从对应表自动解析。 ### 6.3 通用逻辑(可抽成 mixin) ```javascript // 伪代码 data() { return { viewRecordId: null } }, methods: { async startViewTracking(reId, type, contentName, deviceName) { if (!reId || !type) return try { const data = { reId, type } if (contentName) data.contentName = contentName if (deviceName) data.deviceName = deviceName const res = await this.API.startViewRecord(data) async endViewTracking() { if (!this.viewRecordId) return try { await this.API.endViewRecord({ recordId: this.viewRecordId }) } catch (e) { console.warn('EndViewRecord failed', e) } this.viewRecordId = null } }, onUnload() { this.endViewTracking() } ``` 各详情页在数据加载成功后调用 `this.startViewTracking(reId, type, contentName, deviceName)`,在 `onUnload` 中调用 `this.endViewTracking()`。 --- ## 七、实现顺序建议 1. **API 封装**:在 `oauth.js` 或新建模块中增加 `startViewRecord`、`endViewRecord` 2. **Mixin 或工具函数**:抽 `startViewTracking`、`endViewTracking`,避免重复代码 3. **按页面接入**:依次在 6 个详情页接入埋点(建议顺序:我的设备 → 故障排查 → 知识库 → 资料管理 → 备件支持 → 培训展示) 4. **联调与验证**:真机/模拟器操作,查 `sb_records` 表确认 F_Type、F_DeviceName、F_ContentName、F_DurationSeconds 正确 --- ## 八、注意事项 - **未登录**:若接口需 Token,未登录用户可能调用失败,可静默失败或跳过埋点 - **快速返回**:用户进入后立即返回,End 仍会执行,Duration 可能为 0 或很小,属正常 - **页面栈**:`navigateBack` 会触发 `onUnload`,可正常执行 End;`reLaunch`、`redirectTo` 同理 - **recordId 存储**:必须存在组件 data 中,`onUnload` 时能访问到,避免用全局变量导致多页面冲突