5b34fa336b
- WeightRecordsPage:cursor 分页(加载更多/末页收起/翻页失败保留重试)、 loading/empty/error/retry 四态、下拉刷新;viewer 隐藏录入入口 - WeightFormPage:weightKg 契约区间 (0,500] 与两位小数前端校验 + 40000 兜底;称重时间转 UTC 上送;错误三层分层沿用登录纵切 - health_record 域埋点强类型封装(06 §1.4:create_started/succeeded/ failed + viewed,httpStatus 由业务码推导;viewed 按工单口径取列表曝光) - health_record_display 纯函数(体重解析/展示、疫苗状态映射、42201 规则前端拦截函数);PetsController 暴露 repository(22 号 §7 交接) - 测试 177 → 205(+28)全绿 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
126 lines
3.9 KiB
Dart
126 lines
3.9 KiB
Dart
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,
|
||
});
|
||
}
|
||
}
|