c0a8a56e91
- pet_create_started / succeeded / failed 三事件(13 号规范 §3.1 强类型惯例:枚举编译期锁死,业务代码禁止手拼事件名与属性) - entryPoint 三枚举(post_register_guide 预留);failureReason 四枚举 (pet_limit_reached 因产品未设上限未纳入;5xx 与断网/超时客户端 不可区分,并入 network_error) - errorCode 携带业务码并推导 httpStatus(code ~/ 100);attemptSeq 记录表单会话内提交序号 - 单元测试 4 个(属性齐备、可空属性缺席语义) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
84 lines
3.0 KiB
Dart
84 lines
3.0 KiB
Dart
import 'package:patbond_flutter/analytics/page_view_tracker.dart';
|
||
import 'package:patbond_flutter/features/pets/pet_models.dart';
|
||
|
||
/// pet 域埋点强类型封装(06 号规划 §1.4/§4 字典 v2 三事件;沿用
|
||
/// 13 号规范 §3.1 惯例:枚举编译期锁死,业务代码禁止手拼事件名与属性)。
|
||
///
|
||
/// 三事件只覆盖「建宠」漏斗:`page_viewed(pet_form)` 承接到达段(路由
|
||
/// 埋点自动采集),started 在表单**首次输入**触发、每次进入记一次;
|
||
/// 编辑不设事件(06 §1.4 自觉取舍:短表单漏斗价值低于事件成本)。
|
||
|
||
/// 建宠表单入口(06 §4:pet_create_started.entryPoint 枚举)。
|
||
/// post_register_guide 预留:注册后引导流尚未落地,落地时启用。
|
||
enum PetCreateEntryPoint {
|
||
profileEmptyState('profile_empty_state'),
|
||
petList('pet_list'),
|
||
postRegisterGuide('post_register_guide');
|
||
|
||
const PetCreateEntryPoint(this.value);
|
||
|
||
final String value;
|
||
}
|
||
|
||
/// 建宠失败原因(06 §4 枚举;pet_limit_reached 因产品未设上限未纳入,
|
||
/// 若后续拍板设上限随字典增补)。客户端网络层不区分 5xx 与断网/超时
|
||
/// (均为 ApiNetworkException),两者并入 network_error,server_error
|
||
/// 保留给无法归类的兜底。
|
||
enum PetCreateFailureReason {
|
||
validationError('validation_error'),
|
||
rateLimited('rate_limited'),
|
||
networkError('network_error'),
|
||
serverError('server_error');
|
||
|
||
const PetCreateFailureReason(this.value);
|
||
|
||
final String value;
|
||
}
|
||
|
||
class PetAnalytics {
|
||
PetAnalytics(this._track);
|
||
|
||
/// 生产传 `AnalyticsService.trackEvent`,测试传录制桩。
|
||
final TrackEventFn _track;
|
||
|
||
/// 进入建宠表单并产生首次输入(每次进入记一次,表单层负责去重)。
|
||
void createStarted({required PetCreateEntryPoint entryPoint}) {
|
||
_track('pet_create_started', {'entryPoint': entryPoint.value});
|
||
}
|
||
|
||
/// 建宠接口成功响应(漏斗事件)。
|
||
///
|
||
/// [durationMs] 为表单打开到成功的耗时;[petIndex] 为该用户第几只宠物
|
||
/// (H2 假设的直接数据源,与 pet.pets 事实表交叉验证)。
|
||
void createSucceeded({
|
||
required int durationMs,
|
||
required PetSpecies species,
|
||
required int petIndex,
|
||
}) {
|
||
_track('pet_create_succeeded', {
|
||
'durationMs': durationMs,
|
||
'species': species.name,
|
||
'petIndex': petIndex,
|
||
});
|
||
}
|
||
|
||
/// 建宠失败:失败响应 / 超时 / 本地校验拦截。
|
||
///
|
||
/// [errorCode] 为业务错误码(本地校验/网络错误时缺席);
|
||
/// [httpStatus] 由五位业务码推导(`code ~/ 100`);
|
||
/// [attemptSeq] 为本次表单会话内第几次提交尝试(从 1 起)。
|
||
void createFailed({
|
||
required PetCreateFailureReason reason,
|
||
required int attemptSeq,
|
||
int? errorCode,
|
||
}) {
|
||
_track('pet_create_failed', {
|
||
'failureReason': reason.value,
|
||
'attemptSeq': attemptSeq,
|
||
'errorCode': ?errorCode,
|
||
if (errorCode != null && errorCode >= 10000)
|
||
'httpStatus': errorCode ~/ 100,
|
||
});
|
||
}
|
||
}
|