行业场景事件建模示例
以下电商、B2B 和内容场景展示事件命名、属性与触发时机的设计方法。示例是可映射的业务模板,不是 SDK 全局预置属性或后端固定合同;接入方需根据业务事件定义、隐私要求和权威数据来源确定最终事件。
一、电商与零售
电商示例从浏览、加购、结算延伸到支付和退款。关联同一订单的事件可使用稳定的业务订单 ID;每条事件仍须有自己的 eventId,不要把不同事件复用成同一个事件 ID。支付和退款完成事件应由经过认证的业务服务端依据支付/退款结果产生,客户端点击只能记录意图或操作行为,不能代替账务事实。
[商品流/推荐位曝光] ──> [商品详情页浏览] ──> [加入购物车] ──> [发起提单结算] ──> [支付成功] ──> [退款成功]
item_exposure view_product add_to_cart initiate_checkout order_paid refund_issued1. 事件模板与推荐属性
| 事件名称 | 业务阶段 | 关键属性定义 | 属性类型 | 示例值 |
|---|---|---|---|---|
view_product | 浏览商品 | product_idproduct_namecategory_idoriginal_pricesale_price | string string string number number | "p_99023""无线降噪耳机 Pro""c_digital_audio"899.00699.00 |
add_to_cart | 加入购物车 | product_idsku_idquantitycart_total_price | string string number number | "p_99023""sku_black_01"1699.00 |
initiate_checkout | 发起提单结算 | checkout_idcart_items_countorder_subtotal | string number number | "chk_20260908_001"31498.00 |
order_paid | 支付成功 | order_idtransaction_idamountcurrency | string string number string | "ord_88291039""cap_55091"699.00"CNY" |
refund_issued | 退款成功 | refund_idorder_idrefund_amountcurrency | string string number string | "ref_3319""ord_88291039"199.00"CNY" |
2. 模板语义与字段映射
order_paid表示业务后端已确认成功扣款/收款的金额;transaction_id标识一次成功的支付交易。同一订单有多次独立成功扣款时,每次使用不同交易 ID。refund_issued表示一笔退款已成功完成;部分退款按每笔退款分别记录,refund_amount使用该笔退款的正数金额,refund_id唯一标识该退款。报表计算净收入时必须把退款按退款语义扣减,不能直接将两类正数相加。- 金额属性采用数值以支持分析计算,但本模板不是账务精度或对账合同。项目必须明确金额对应的币种、折扣/税费口径及精度;金额字段可以映射到客户已有名称(例如
pay_amount),SDK 不会自动重命名,也不会把这些字段加到全局预置属性。 currency建议使用 ISO 4217 货币代码。SDK Tracking Plan 可校验字符串类型和长度,但不替代业务服务端的币种代码校验。order_id、transaction_id、refund_id应使用不含姓名、手机号、邮箱、卡号或访问令牌的业务标识。
接入前确认事件名、金额单位、精度和业务口径;使用本地 Tracking Plan 时,可据此配置客户端校验规则。
业务服务端应先对支付/退款通知做业务级去重,再创建事件。SDK 在传输重试时保持同一事件的 eventId,供接收端去重;它不会识别两个不同事件是否代表同一笔业务交易。
3. 客户端与服务端的职责、验证边界
客户端可以记录 view_product、add_to_cart、initiate_checkout 或支付按钮点击等行为。支付成功、退款成功应由业务后端在确认状态后产生;若要求与订单数据库事务一致,应在同一事务写入业务记录和 Outbox 事件,再由 Relay 发送。参见 、Java、Go 和 服务端接入指南。
本地 Tracking Plan 只校验客户端事件,四种服务端 SDK 尚未集成该校验。服务端业务字段校验、幂等落库及收入报表应在实际部署环境中验证。
二、 B2B 组织分析与服务端事实
B2B 事件需要区分分析组织和授权租户。客户端 groupId 可用于分析组织维度;服务端 tenantId 如果用于租户隔离,应从服务端已认证的宿主上下文取得。它们不能因为示例值相同就被视为同一字段或信任边界。
[创建工作空间] ──> [邀请成员] ──> [使用核心功能] ──> [套餐变更]
workspace_created member_invited feature_engaged plan_changed这些事件名和字段只是可映射模板,需按企业自己的产品指标和 Tracking Plan 确认。客户端事件可记录成员邀请操作、功能使用等行为;套餐、席位、支付或退款等已完成的业务事实,应由确认该事实的业务服务端产生。
1. 客户端组织分析示例
客户端通过 group(groupId, traits) 设置分析组织:
await analytics.identify('user-123');
await analytics.group('org-456', { plan: 'enterprise' });
analytics.track('feature_engaged', {
module_name: 'advanced_analytics',
action_type: 'export_csv',
});groupId 是宿主显式提供的分析上下文;traits 随 $group 事件发送,不会自动并入每个后续事件。客户端可上报的组织或租户属性不能作为后端授权依据。
2. 服务端权威业务事件
支付、退款、订阅和席位变化应在业务服务确认状态后从服务端发出。服务端 SDK 的 ServerIdentity 支持显式 tenantId,宿主应从已认证的业务上下文填充它;SDK 不替代认证、权限校验、业务去重或事务 Outbox。金额口径、币种、退款表示及 ARR 定义必须由业务、财务和数仓共同确认,不作为 SDK 的全局预置属性。具体边界见 B2B 组织属性与服务端租户边界 和服务端事件接入合同。
三、内容社区与流媒体
内容产品可记录阅读、播放进度和互动,用于分析内容消费深度与参与度。
[信息流曝光] ──> [进入正文/播放页] ──> [消费进度打卡] ──> [社交点赞/评论/分享]
feed_exposure view_content content_progress social_interaction1. 事件模板与推荐属性
| 事件名称 | 业务阶段 | 关键属性定义 | 属性类型 | 说明 |
|---|---|---|---|---|
content_exposure | 内容瀑布流曝光 | content_idcontent_typeposition_indexrec_algorithm_id | string string number string | 用于推荐算法负样本与曝光未点击率分析 |
view_content | 打开内容详情 | content_idcategory_idauthor_idpublish_time | string string string string | 开始消费图文或视频 |
content_progress | 阅读/播放进度 | content_idprogress_percentplayback_position_s | string number number | 可按项目定义的进度节点触发;播放位置不是累计观看时长 |
social_interaction | 社交互动 | content_idinteraction_typetarget_user_id | string string string | interaction_type: "like", "comment", "share_timeline" |
2. 按进度节点记录事件
// 假设 analytics 已初始化。每次播放创建一个 reporter,同一次播放每个节点最多记录一次。
function createProgressReporter(contentId: string) {
const reported = new Set<number>();
const checkpoints = [25, 50, 75, 100];
return (currentTime: number, duration: number) => {
if (!Number.isFinite(currentTime) || !Number.isFinite(duration) || duration <= 0) return;
const position = Math.min(100, Math.max(0, (currentTime / duration) * 100));
for (const checkpoint of checkpoints) {
if (position >= checkpoint && !reported.has(checkpoint)) {
reported.add(checkpoint);
analytics.track('content_progress', {
content_id: contentId,
progress_percent: checkpoint,
playback_position_s: Math.round(currentTime),
});
}
}
};
}
const reportProgress = createProgressReporter('content_10029');
video.addEventListener('timeupdate', () => reportProgress(video.currentTime, video.duration));// 在持有 sdk 和 hasTrackedCompletion 的 UIScrollViewDelegate 中使用。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let maxOffset = scrollView.contentSize.height - scrollView.bounds.height
guard maxOffset > 0 else { return }
let progress = Int((scrollView.contentOffset.y / maxOffset) * 100)
if progress >= 100 && !hasTrackedCompletion {
hasTrackedCompletion = true
sdk.track("content_progress", properties: [
"content_id": .string("art_10029"),
"progress_percent": .number(100)
])
}
}四、字段治理建议
- 高频进度回调应按项目定义的里程碑去重记录,避免把每次 UI 回调都变成事件。选择哪些节点取决于业务分析目标。
- 不发送密码、访问令牌、完整支付凭据或无关个人信息。哈希不等于匿名化;不要把哈希当作通用脱敏方式。确需关联用户时,由业务和数据治理团队确定允许的标识、用途和留存规则。
- 为自定义属性选定团队约定的命名风格并保持一致。SDK 接受自定义字段名,不会强制转换为
snake_case;接收端映射规则需单独确认。 - 金额字段使用什么单位、精度和币种由项目定义,并确保每条金额记录带有可解释的币种信息。SDK 不会统一换算或改写金额。