You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

311 lines
15 KiB

2 weeks ago
# IGG4 小程序埋点实施计划
## 概要
基于 `doc/track.md` 文档和信达项目的成熟埋点模式,为 IGG4 项目实施页面级 PV 埋点和点击级事件埋点。采用信达项目已验证的 `PG_` / `BTN_` 双层命名体系,复用现有 `app.mpBehavior()` 基础设施。
## 现状分析
| 维度 | 现状 |
|------|------|
| 埋点函数 | `app.mpBehavior({ PageName, doctor? })` — 仅 1 处调用 (`MINI_PROGRAM_VISIT`) |
| API 接口 | 患者端 `?r=igg4/mp-behavior/add`,医生端 `?r=igg4/doctor/mp-behavior/add` |
| 类型签名 | `{ PageName: string; doctor?: boolean }` — 需扩展以支持 `detailId` 等可选参数 |
| 页面级埋点 | 完全未实现(track.md 中 25+ 页面均未添加调用) |
| 点击级埋点 | 完全未实现 |
| 命名规范 | 无(信达项目使用 `PG_`/`BTN_` 前缀命名体系) |
## 命名规范(参照信达项目)
| 类别 | 前缀 | 格式 | 示例 |
|------|------|------|------|
| 页面浏览(患者端) | `PG_` | `PG_Patient{功能名}` | `PG_PatientHome`、`PG_PatientRepository` |
| 页面浏览(医生端) | `PG_` | `PG_Doctor{功能名}` | `PG_DoctorHome`、`PG_DoctorPatient` |
| 点击事件(患者端) | `BTN_` | `BTN_Patient{功能名}{操作}` | `BTN_PatientHomeDoctorCard`、`BTN_PatientMyVip` |
| 点击事件(医生端) | `BTN_` | `BTN_Doctor{功能名}{操作}` | `BTN_DoctorHomeInvite`、`BTN_DoctorPatientDetail` |
### PageName 与 track.md 页面对照表
**患者端页面级埋点(PG_):**
| track.md 页面路径 | PageName |
|-------------------|----------|
| pages/index/index | `PG_PatientHome` |
| gift/pages/recordList/index | `PG_PatientRecordList` |
| gift/pages/record/index | `PG_PatientRecordAdd` |
| gift/pages/recordDetail/index | `PG_PatientRecordDetail` |
| pages/interactivePatient/index | `PG_PatientInteractive` |
| pages/doctorDetail/index | `PG_PatientDoctorDetail` |
| pages/repository/index | `PG_PatientRepository` |
| pages/repositoryDetail/index | `PG_PatientRepositoryDetail` |
| pages/my/index | `PG_PatientMy` |
| pages/qaFormDetail/index | `PG_PatientQaFormDetail` |
| pages/qaForm/index | `PG_PatientQaForm` |
| pages/caseReport/index | `PG_PatientCaseReport` |
| pages/personalInformation/index | `PG_PatientPersonalInfo` |
| pages/cancellation/index | `PG_PatientCancellation` |
| pages/mySave/index | `PG_PatientMySave` |
| pages/feedback/index | `PG_PatientFeedback` |
**医生端页面级埋点(PG_):**
| track.md 页面路径 | PageName |
|-------------------|----------|
| doctor/pages/d_home/index | `PG_DoctorHome` |
| doctor/pages/d_invite/index | `PG_DoctorInvite` |
| doctor/pages/d_patient/index | `PG_DoctorPatient` |
| doctor/pages/d_patientDetail/index | `PG_DoctorPatientDetail` |
| doctor/pages/d_taskList/index | `PG_DoctorTaskList` |
| doctor/pages/d_taskDetail/index | `PG_DoctorTaskDetail` |
| doctor/pages/d_createTask/index | `PG_DoctorCreateTask` |
| doctor/pages/d_interactive/index | `PG_DoctorInteractive` |
| doctor/pages/d_interactiveDoctor/index | `PG_DoctorInteractiveDoctor` |
| doctor/pages/d_my/index | `PG_DoctorMy` |
| doctor/pages/d_userInfo/index | `PG_DoctorUserInfo` |
| doctor/pages/d_changeDoctor/index | `PG_DoctorChangeDoctor` |
## 实施步骤
### 第 1 步:扩展 mpBehavior 类型签名
**文件**: `src/app.ts` + `typings/index.d.ts`
扩展 `mpBehavior` 方法签名,支持可选的 `detailId` 参数(用于详情页等场景):
```ts
// typings/index.d.ts
mpBehavior: (data: { PageName: string; doctor?: boolean; detailId?: string }) => void
// src/app.ts — 实现签名同步更新
mpBehavior(data: { PageName: string; doctor?: boolean; detailId?: string }) {
// ... 现有逻辑不变,detailId 会随 data 对象一起 POST 到后端
}
```
> 说明:与信达项目一致,`detailId` 等额外字段作为 POST data 的一部分直接发送给后端,无需在 `mpBehavior` 函数内做特殊处理。
### 第 2 步:实施患者端页面级埋点(16 个页面)
在每个页面的 `onShow` 中(有 `waitLogin` 的在 `waitLogin().then()` 内),调用 `app.mpBehavior()`
**通用模式:**
```ts
// 患者端页面(无 waitLogin)
onShow() {
app.mpBehavior({ PageName: 'PG_PatientXxx' })
}
// 患者端页面(有 waitLogin)
onShow() {
app.waitLogin().then(() => {
app.mpBehavior({ PageName: 'PG_PatientXxx' })
// ... 原有业务逻辑
})
}
```
**修改文件清单:**
| # | 文件路径 | PageName | 嵌入位置 |
|---|---------|----------|---------|
| 1 | `src/pages/index/index.ts` | `PG_PatientHome` | `onShow``waitLogin().then()` 内首行 |
| 2 | `src/gift/pages/recordList/index.ts` | `PG_PatientRecordList` | `onShow``waitLogin().then()` 内首行 |
| 3 | `src/gift/pages/record/index.ts` | `PG_PatientRecordAdd` | `onShow``waitLogin().then()` 内首行 |
| 4 | `src/gift/pages/recordDetail/index.ts` | `PG_PatientRecordDetail` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 5 | `src/pages/interactivePatient/index.ts` | `PG_PatientInteractive` | `onLoad``waitLogin().then()` 内首行 |
| 6 | `src/pages/doctorDetail/index.ts` | `PG_PatientDoctorDetail` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 7 | `src/pages/repository/index.ts` | `PG_PatientRepository` | `onShow``waitLogin().then()` 内首行 |
| 8 | `src/pages/repositoryDetail/index.ts` | `PG_PatientRepositoryDetail` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 9 | `src/pages/my/index.ts` | `PG_PatientMy` | `onShow``waitLogin().then()` 内首行 |
| 10 | `src/pages/qaFormDetail/index.ts` | `PG_PatientQaFormDetail` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 11 | `src/pages/qaForm/index.ts` | `PG_PatientQaForm` | `onShow``waitLogin().then()` 内首行 |
| 12 | `src/pages/caseReport/index.ts` | `PG_PatientCaseReport` | `onShow``waitLogin().then()` 内首行 |
| 13 | `src/pages/personalInformation/index.ts` | `PG_PatientPersonalInfo` | `onShow``waitLogin().then()` 内首行 |
| 14 | `src/pages/cancellation/index.ts` | `PG_PatientCancellation` | `onShow``waitLogin().then()` 内首行 |
| 15 | `src/pages/mySave/index.ts` | `PG_PatientMySave` | `onShow``waitLogin().then()` 内首行 |
| 16 | `src/pages/feedback/index.ts` | `PG_PatientFeedback` | `onShow``waitLogin().then()` 内首行 |
### 第 3 步:实施医生端页面级埋点(12 个页面)
医生端埋点需传入 `doctor: true`,API 路径切换为 `?r=igg4/doctor/mp-behavior/add`
**通用模式:**
```ts
// 医生端页面
onShow() {
app.waitLogin().then(() => {
app.mpBehavior({ doctor: true, PageName: 'PG_DoctorXxx' })
// ... 原有业务逻辑
})
}
```
**修改文件清单:**
| # | 文件路径 | PageName | 嵌入位置 |
|---|---------|----------|---------|
| 1 | `src/doctor/pages/d_home/index.ts` | `PG_DoctorHome` | `onLoad``waitLogin().then()` 内首行 |
| 2 | `src/doctor/pages/d_invite/index.ts` | `PG_DoctorInvite` | `onShow``waitLogin().then()` 内首行 |
| 3 | `src/doctor/pages/d_patient/index.ts` | `PG_DoctorPatient` | `onShow``waitLogin().then()` 内首行 |
| 4 | `src/doctor/pages/d_patientDetail/index.ts` | `PG_DoctorPatientDetail` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 5 | `src/doctor/pages/d_taskList/index.ts` | `PG_DoctorTaskList` | `onShow``waitLogin().then()` 内首行 |
| 6 | `src/doctor/pages/d_taskDetail/index.ts` | `PG_DoctorTaskDetail` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 7 | `src/doctor/pages/d_createTask/index.ts` | `PG_DoctorCreateTask` | `onShow``waitLogin().then()` 内首行 |
| 8 | `src/doctor/pages/d_interactive/index.ts` | `PG_DoctorInteractive` | `onShow``waitLogin().then()` 内首行 |
| 9 | `src/doctor/pages/d_interactiveDoctor/index.ts` | `PG_DoctorInteractiveDoctor` | `onShow``waitLogin().then()` 内首行,带 `detailId` |
| 10 | `src/doctor/pages/d_my/index.ts` | `PG_DoctorMy` | `onShow``waitLogin().then()` 内首行 |
| 11 | `src/doctor/pages/d_userInfo/index.ts` | `PG_DoctorUserInfo` | `onShow``waitLogin().then()` 内首行 |
| 12 | `src/doctor/pages/d_changeDoctor/index.ts` | `PG_DoctorChangeDoctor` | `onShow``waitLogin().then()` 内首行 |
### 第 4 步:实施点击级埋点(BTN_)
在关键交互的事件处理函数首行,插入 `app.mpBehavior()` 调用。
#### 患者端点击埋点
**首页 — `src/pages/index/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| `routerTo(e)` 中 doctor 类卡片 | `BTN_PatientHomeDoctorCard` | 迷你医生卡片点击 |
| `routerTo(e)` 中功能入口 | `BTN_PatientHomeFunctionCard` | 功能卡片跳转 |
| `handleNextVisit()` | `BTN_PatientHomeNextVisit` | 下次复诊入口 |
| `handleArticleView(e)` | `BTN_PatientHomeArticle` | 文章点击 |
| `handleArticleSave(e)` | `BTN_PatientHomeArticleSave` | 文章收藏 |
| `handleToastOk()`(绑定医生确认) | `BTN_PatientHomeBindDoctor` | 绑定医生弹窗确认 |
| `handlePublic()` | `BTN_PatientHomeFollowPublic` | 关注公众号 |
**我的页面 — `src/pages/my/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| `routerVipTo()` | `BTN_PatientMyVip` | VIP 认证入口 |
| `handleMyHealthRecord(e)` | `BTN_PatientMyHealthRecord` | 健康档案 |
| `handleQaForm()` | `BTN_PatientMyQaForm` | 健康问卷 |
| `routerTo(e)` 中 mySave | `BTN_PatientMySave` | 我的收藏 |
| `routerTo(e)` 中 personalInfo | `BTN_PatientMyPersonalInfo` | 个人信息 |
| `routerTo(e)` 中 feedback | `BTN_PatientMyFeedback` | 问题反馈 |
| `handleMiniDoctor()` | `BTN_PatientMyMiniDoctor` | 迷你医生 |
**医患互动 — `src/pages/interactivePatient/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| `handleHot(e)` | `BTN_PatientInteractiveHotQuestion` | 热门问题点击 |
| `handleQuestion(e)` | `BTN_PatientInteractiveQuestion` | 快捷问题点击 |
| `handleConfirm()` | `BTN_PatientInteractiveSend` | 发送消息 |
| `handleAdl()` | `BTN_PatientInteractiveAdl` | ADL 评估入口 |
| `handleQol()` | `BTN_PatientInteractiveQol` | QoL 入口 |
| `handleDoctorDetail()` | `BTN_PatientInteractiveDoctorDetail` | 查看医生详情 |
**知识库 — `src/pages/repository/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| `tabChange(e)` | `BTN_PatientRepositoryTab` | Tab 切换 |
| `handleSearch()` | `BTN_PatientRepositorySearch` | 搜索 |
| `handleCate(e)` | `BTN_PatientRepositoryCategory` | 分类筛选 |
| `handleDetail(e)` | `BTN_PatientRepositoryArticle` | 文章详情 |
| `handleStar(e)` | `BTN_PatientRepositoryStar` | 点赞 |
| `handleToggleSave(e)` | `BTN_PatientRepositorySave` | 收藏 |
**就诊记录列表 — `src/gift/pages/recordList/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 新增就诊记录入口 | `BTN_PatientRecordListAdd` | 新增记录按钮 |
| 点击记录项 | `BTN_PatientRecordListItem` | 记录列表项点击 |
**就诊记录新增 — `src/gift/pages/record/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 提交/保存按钮 | `BTN_PatientRecordAddSubmit` | 保存记录 |
**医生详情 — `src/pages/doctorDetail/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 查看互动 | `BTN_PatientDoctorDetailInteractive` | 进入互动页 |
#### 医生端点击埋点
**医生首页 — `src/doctor/pages/d_home/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| `handlePatient(e)` | `BTN_DoctorHomePatient` | 患者列表入口 |
| `handleInvite()` | `BTN_DoctorHomeInvite` | 邀约按钮 |
| `handleSelectYear1()` | `BTN_DoctorHomeSelectYear` | 年份筛选 |
**邀约患者 — `src/doctor/pages/d_invite/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 发送邀约按钮 | `BTN_DoctorInviteSend` | 发送邀约 |
**患者列表 — `src/doctor/pages/d_patient/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 搜索 | `BTN_DoctorPatientSearch` | 搜索患者 |
| 点击患者项 | `BTN_DoctorPatientItem` | 患者列表项 |
**互动列表 — `src/doctor/pages/d_interactive/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 搜索 | `BTN_DoctorInteractiveSearch` | 搜索互动 |
| 点击互动项 | `BTN_DoctorInteractiveItem` | 互动列表项 |
**互动页 — `src/doctor/pages/d_interactiveDoctor/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 发送消息 | `BTN_DoctorInteractiveSend` | 发送消息 |
**群发列表 — `src/doctor/pages/d_taskList/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 新增群发 | `BTN_DoctorTaskListAdd` | 新增群发入口 |
| 点击群发项 | `BTN_DoctorTaskListItem` | 群发列表项 |
**群发新增 — `src/doctor/pages/d_createTask/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 提交群发 | `BTN_DoctorCreateTaskSubmit` | 创建群发 |
**切换医生 — `src/doctor/pages/d_changeDoctor/index.ts`**
| 方法 | PageName | 说明 |
|------|----------|------|
| 选择医生 | `BTN_DoctorChangeDoctorSelect` | 切换医生选择 |
> **注意**:以上点击埋点 PageName 是基于代码探索的预定义清单。实施时需逐页确认实际的方法名和交互行为,如有差异需现场调整。
### 第 5 步:更新 doc/track.md 文档
将完整的 PageName 对照表(页面级 + 点击级)更新到 `doc/track.md`,作为埋点实施的参考文档。
## 假设与决策
1. **命名规范**:采用信达项目的 `PG_/BTN_` 前缀体系,患者端用 `Patient` 前缀,医生端用 `Doctor` 前缀。这样命名清晰且便于后端区分数据来源。
2. **detailId 参数**:扩展 `mpBehavior` 类型签名以支持可选 `detailId`,用于详情页场景。实际在 JS 层该字段会随 data 对象一起 POST,无需函数内额外处理。
3. **页面级埋点位置**:有 `waitLogin` 的页面在 `.then()` 内首行调用;无 `waitLogin` 的在 `onShow` 首行调用。确保用户登录后才上报,避免无效数据。
4. **点击级埋点位置**:在事件处理函数首行调用 `mpBehavior`,先上报再执行业务逻辑(与信达项目一致)。
5. **医生端 `doctor: true`**:所有医生端埋点均传入 `doctor: true`,确保 API 路径正确切换。
6. **MINI_PROGRAM_VISIT 保留**:`app.ts` 中现有的 `MINI_PROGRAM_VISIT` 埋点保留不变,它记录小程序整体打开事件,与页面级 PV 埋点互不冲突。
7. **无集中常量文件**:PageName 采用硬编码字符串(与信达项目一致),不创建额外的常量/枚举文件,避免增加维护成本。
## 验证步骤
1. 编译检查:确保 `pnpm run lint:fix` 无新增错误
2. 逐页验证:在微信开发者工具中打开每个页面,检查 console 中 mpBehavior POST 请求是否正常发出
3. 参数验证:确认患者端请求路径为 `?r=igg4/mp-behavior/add`,医生端为 `?r=igg4/doctor/mp-behavior/add`
4. doctor 参数验证:确认 `doctor` 字段在发送前被正确删除(不出现在 POST body 中)
5. detailId 验证:确认详情页的 `detailId` 正确传入 POST body