import 'package:patbond_flutter/analytics/page_view_tracker.dart'; /// health_record 域埋点强类型封装(06 号规划 §1.4 字典 v2;后端白名单 /// 已扩充就绪,24 号报告 §2.2)。沿用 13 号规范 §3.1 惯例:枚举编译期 /// 锁死,业务代码禁止手拼事件名与属性。 /// /// T2-13 挂接创建漏斗三事件 + viewed;edit/deleted 事件的挂接随 /// 编辑/删除交互落地(「标记完成」等)另行接线,见 25 号报告遗留。 /// 记录类型(06 §1.4 recordType 枚举,四类记录接口对应)。 enum HealthRecordType { weight('weight'), vaccine('vaccine'), healthEvent('health_event'), reminder('reminder'); const HealthRecordType(this.value); final String value; } /// 记录创建入口(06 §1.4 entryPoint 枚举)。 enum HealthRecordEntryPoint { petDetail('pet_detail'), recordList('record_list'), reminder('reminder'); const HealthRecordEntryPoint(this.value); final String value; } /// 创建失败原因(06 §1.4 基底 + M2 验收新增三值)。与 pet 域同款 /// 网络归并口径:断网/超时/5xx 均并入 network_error,server_error /// 保留给无法归类的兜底。 enum HealthRecordFailureReason { validationError('validation_error'), permissionDenied('permission_denied'), notFound('not_found'), rateLimited('rate_limited'), networkError('network_error'), serverError('server_error'); const HealthRecordFailureReason(this.value); final String value; } /// viewed 的来源(06 §1.4 source 枚举)。 enum HealthRecordViewSource { recordList('record_list'), petDetail('pet_detail'), reminder('reminder'); const HealthRecordViewSource(this.value); final String value; } class HealthRecordAnalytics { HealthRecordAnalytics(this._track); /// 生产传 `AnalyticsService.trackEvent`,测试传录制桩。 final TrackEventFn _track; /// 进入某类记录的创建表单并产生首次输入(每次进入记一次,表单层去重)。 void createStarted({ required HealthRecordType recordType, required HealthRecordEntryPoint entryPoint, }) { _track('health_record_create_started', { 'recordType': recordType.value, 'entryPoint': entryPoint.value, }); } /// 创建接口成功响应(漏斗事件,北极星与 H1/H3/H4 的核心数据源)。 /// /// [photoCount] 无照片为 0(M2 不做媒体上传,恒 0,字段随字典保留)。 void createSucceeded({ required HealthRecordType recordType, required int durationMs, int photoCount = 0, }) { _track('health_record_create_succeeded', { 'recordType': recordType.value, 'durationMs': durationMs, 'photoCount': photoCount, }); } /// 创建失败:失败响应 / 超时 / 本地校验拦截。 /// /// [errorCode] 为业务错误码(本地校验/网络错误时缺席); /// [httpStatus] 由五位业务码推导(`code ~/ 100`); /// [attemptSeq] 为本次表单会话内第几次提交尝试(从 1 起)。 void createFailed({ required HealthRecordType recordType, required HealthRecordFailureReason reason, required int attemptSeq, int? errorCode, }) { _track('health_record_create_failed', { 'recordType': recordType.value, 'failureReason': reason.value, 'attemptSeq': attemptSeq, 'errorCode': ?errorCode, if (errorCode != null && errorCode >= 10000) 'httpStatus': errorCode ~/ 100, }); } /// 记录曝光。06 §1.4 定义在详情页可见;M2 体重/疫苗无独立详情页, /// 以「列表页每次进入首个成功加载」为曝光时点(每次进入一次、 /// 不随滚动逐条上报,防事件洪水的意图不变)。 void viewed({ required HealthRecordType recordType, required HealthRecordViewSource source, }) { _track('health_record_viewed', { 'recordType': recordType.value, 'source': source.value, }); } }