# 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