Files
lixi c0a8a56e91 新增:pet 域埋点强类型封装(06 号规划字典 v2 三事件)
- 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>
2026-09-08 11:59:32 +08:00

84 lines
3.0 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 §4pet_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_errorserver_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,
});
}
}