Repository navigation
Product Analytics Events
Reference. Implementation and current repository policy take precedence if this page differs.
- 版本: 1.0
- 状态: RND-409 Phase 0 契约;RND-162 实现、测试与评审的唯一事件定义来源
- 适用范围: 平台超级管理员的跨租户产品使用分析
- 不适用范围: 租户业务 Analytics、安全/合规审计、Session Replay、广告归因
本契约只收集平台运营回答下列问题所需的最小事件集:
- 哪些已开通租户在指定周期内真实使用了产品;哪些租户连续未使用。
- 管理员登录是否成功,失败率是否异常。
- 会话查阅、搜索、筛选、媒体预览及设置等核心能力分别被多少租户和管理员采用。
- 单个租户最后一次登录及最后一次核心产品行为发生在何时。
产品使用分析与 AuditLog 是两个独立领域:审计日志用于安全、合规和业务操作证据,不能作为本契约的事件来源或存储替代品。产品事件也不得用于重放用户操作、查看聊天内容或改变任何业务授权决定。
第一版不建设第三方分析平台、事件存储/API、前端埋点或超管分析页面;这些由 RND-162 按本契约实现。
| 名称 | 定义 |
|---|---|
| 已开通租户 |
tenants.lifecycle_status IN ('active', 'frozen', 'suspended') 的租户。provisioning 租户不计入。 |
| 核心产品行为 | 表 4 中标记为 core_workflow 的有效事件;登录成功、设置打开、语言变更和反馈提交不单独构成“真实使用”。 |
| 活跃租户 | 在查询窗口内至少发生一次核心产品行为的已开通租户。 |
| 活跃管理员 | 在查询窗口内至少发生一次核心产品行为的、可解析到内部 admin_user_id 的管理员。 |
| 最近登录 | 租户内最后一条 product.auth.login_succeeded.v1 的 occurred_at。 |
| 最近产品行为 | 租户内最后一条核心产品行为的 occurred_at;不把登录或失败登录冒充为产品使用。 |
| 未使用租户 | 已开通租户在连续未使用窗口内没有核心产品行为。它是运营跟进信号,不等同于已流失。 |
| 查询窗口 | 默认最近 30 个完整自然日;页面必须支持 7、14、30、60、90 日及明确起止时间。趋势按平台时区 Asia/Shanghai 的自然日聚合。 |
| 指标 | 业务含义 | 计算口径 | 来源 |
|---|---|---|---|
| 已开通租户数 | 当前可运营的租户基数 | 查询时符合“已开通租户”定义的租户数 |
tenants 当前状态 |
| 活跃租户数 / 活跃率 | 有真实核心使用的租户规模 | 窗口内活跃租户数;活跃率 = 活跃租户数 / 窗口结束时已开通租户数 | 全部 core_workflow 事件 |
| 未使用租户数 / 列表 | 需运营跟进的租户 | 连续 30 日(或用户指定窗口)无核心产品行为的已开通租户;列表显示最后产品行为时间 | 全部 core_workflow 事件 + tenants
|
| 最近登录时间 | 租户最近成功进入产品的时间 | 每租户 MAX(login_succeeded.occurred_at)
|
product.auth.login_succeeded.v1 |
| 最近产品行为时间 | 租户最近真实使用产品的时间 | 每租户 MAX(core_workflow.occurred_at)
|
表 4 的核心事件 |
| 登录成功次数 / 失败次数 | 登录链路健康度 | 窗口内相应认证事件数;失败事件按已解析租户和未解析租户分别统计 | 两个登录事件 |
| 登录失败率 | 登录是否异常 |
失败次数 / (成功次数 + 失败次数);分母为 0 时显示“无登录尝试”,不显示 0% |
两个登录事件 |
| 活跃管理员数 | 实际使用产品的管理员规模 | 窗口内 COUNT(DISTINCT admin_user_id),仅核心产品行为且 admin_user_id 非空 |
全部 core_workflow 事件 |
| 功能采用租户数 / 管理员数 | 各核心功能的采用情况 | 在窗口内至少发生一次该功能事件的去重 tenant / admin 数;同时展示事件量但不得只以事件量判断采用 | 表 4 的功能事件 |
| 登录与功能使用趋势 | 使用量是否变化 | 每自然日按事件名聚合的成功/失败登录、活跃租户、功能采用租户与事件量 | 所有 v1 事件 |
未解析租户的失败登录只进入平台总失败计数,不能归属到任一租户,也不得出现在任一租户详情中。
事件名的唯一格式为 product.<domain>.<action>.v<major>,全部使用小写 ASCII 与下划线。表 4 是 v1 的完整、封闭清单;实现不得临时新增同义事件名。
-
v1后缀与schema_version: 1必须一致。 - 可选字段的纯新增先更新本文档的次版本和测试;字段改名、删除、类型变化、语义变化或隐私等级放宽必须新建
v2事件名。 - 旧事件在其保留期内仍可查询;聚合层按事件名版本明确处理,不能把不同大版本静默混合。
- 未列在本文档中的事件、字段或枚举值必须在采集边界拒绝,并记录不含事件载荷的内部技术计数。
| 字段 | 类型 | 必填 | 隐私等级 | 规则 |
|---|---|---|---|---|
event_id |
UUID | 是 | P1 内部关联标识 | 由事件源为一次逻辑操作生成;重试必须复用同一值。 |
event_name |
string literal | 是 | P0 分类 | 必须是表 4 的完整事件名。 |
schema_version |
integer | 是 | P0 分类 | v1 固定为 1。 |
occurred_at |
RFC 3339 UTC datetime | 是 | P0 时间 | 后端认证事件使用服务器时间;前端事件提交客户端时间,接收端校验后规范化,超出允许时钟漂移时使用接收时间。 |
received_at |
RFC 3339 UTC datetime | 是 | P0 时间 | 由接收端写入;不得由客户端提供。 |
tenant_id |
internal UUID/string | 条件必填 | P1 内部关联标识 | 从已认证服务端上下文派生,前端不得提交或覆盖。仅未解析的失败登录可为 null。 |
admin_user_id |
internal UUID/string | 条件必填 | P1 内部关联标识 | 从已认证服务端上下文派生;失败登录可为 null。绝不是企业微信 userid。 |
source |
enum | 是 | P0 分类 |
frontend 或 backend。 |
event_class |
enum | 是 | P0 分类 |
authentication、core_workflow、secondary_workflow。由事件名固定,不接受客户端任意值。 |
P0 是非敏感的固定分类/时间;P1 是受访问控制保护的内部或不可逆关联标识。所有事件字段都必须先经过表 4 的 allowlist 校验;未定义的键不可持久化。
RND-162 的实现必须遵守以下契约:
-
开关:
PRODUCT_ANALYTICS_ENABLED控制全部产品事件采集,默认false;关闭时不创建事件、不排队、不重试。读取既有数据的超管分析权限不因开关关闭而扩大。 - 非阻塞: 前端仅以异步、best-effort 方式提交;后端核心业务路径不得等待统计持久化、重试或聚合完成。
- 失败降级: 采集、队列、存储、清理或统计接口故障必须被吞掉并产生安全的运维日志/指标;登录、会话查阅、搜索、筛选、媒体预览、设置与反馈的原有成功/失败语义均不得改变。
-
服务端赋权: 事件的
tenant_id、admin_user_id、事件名 allowlist 和服务端时间由接收端校验或派生;客户端提交的身份、租户、自由文本和任意扩展字段一律忽略/拒绝。 -
租户隔离: 普通租户路由不提供跨租户事件、聚合或明细。仅独立的
PlatformAdmin身份可读取跨租户分析,且超管页面只返回本契约允许的聚合、时间和内部租户元数据,不返回事件敏感载荷。 - 无第三方出站: 第一版事件只写入本平台受控存储;不得把事件或其标识符发送给第三方分析、广告或会话重放服务。
所有下表事件均含 3.2 的统一信封。事件字段表只列额外字段;未列字段一律禁止。
| 事件名 | 类别 | 触发位置 / 成功条件 | 额外字段(类型、必填、隐私) | 直接支持的指标 |
|---|---|---|---|---|
product.auth.login_succeeded.v1 |
authentication |
后端:认证与会话建立成功后。仅一次,不因页面跳转重复。 | 无 | 最近登录、成功次数、失败率 |
product.auth.login_failed.v1 |
authentication |
后端:认证尝试被拒绝后。不得记录账号输入值或拒绝原因原文。 |
tenant_resolution(enum resolved/unresolved,是,P0) |
失败次数、失败率 |
product.conversation.review_opened.v1 |
core_workflow |
前端:已认证管理员打开会话查阅入口并完成页面初始化。 | 无 | 活跃租户/管理员、会话查阅采用 |
product.directory.view_selected.v1 |
core_workflow |
前端:管理员主动切换员工或联系人视图;初始默认视图不发送。 |
view_kind(enum employee/contact,是,P0) |
员工/联系人视图采用 |
product.directory.subject_selected.v1 |
core_workflow |
前端:管理员选择一个员工或联系人后,选择已被界面接受。 |
subject_kind(enum employee/contact,是,P0) |
员工/联系人对象选择采用 |
product.conversation.detail_opened.v1 |
core_workflow |
前端:打开一个具体会话并收到可渲染的成功结果后。 | 无 | 打开会话采用 |
product.conversation.older_messages_loaded.v1 |
core_workflow |
前端:用户请求且成功加载更早消息后;“无更多消息”不是成功加载。 | 无 | 加载更早消息采用 |
product.search.executed.v1 |
core_workflow |
前端:搜索请求已被提交并收到正常业务响应后,含零结果。 |
search_scope(enum contact_nickname/message_content/combined,是,P0) |
搜索采用 |
product.search.filter_applied.v1 |
core_workflow |
前端:用户应用或清除一个筛选维度并收到正常业务响应后;每个维度单独一条。 |
filter_dimension(enum date/person/message_type,是,P0);filter_action(enum applied/cleared,是,P0) |
筛选器采用与维度采用 |
product.media.preview_opened.v1 |
core_workflow |
前端:媒体预览容器成功打开后;下载失败或仅显示占位符不发送。 | 无 | 媒体预览采用 |
product.settings.opened.v1 |
secondary_workflow |
前端:管理员打开设置页并完成初始化后。 | 无 | 设置采用(不计入真实使用) |
product.settings.language_changed.v1 |
secondary_workflow |
后端:语言偏好成功保存后。 | 无 | 语言设置采用(不计入真实使用) |
product.feedback.submitted.v1 |
secondary_workflow |
后端:反馈入口存在且反馈已成功持久化后。当前反馈入口由 RND-161 提供;若该能力被关闭,本事件不发送,不阻塞其他 v1 事件。 | 无 | 反馈入口采用(不计入真实使用) |
- 员工/联系人选择不记录被选择人的名字、备注、企业微信 userid、数据库主键或稳定哈希;本阶段只需判断该能力是否被采用及选择的是哪类对象。
- 会话打开不记录会话 ID、群 ID、成员、标题或任何消息。
- 搜索和筛选只记录固定的功能类别;不记录关键词、日期范围、人员、消息类型具体值或搜索结果。
- 媒体预览、设置、语言变更和反馈提交不记录任何额外字段;不记录媒体 ID、URL、文件名、MIME、大小、内容、语言值、反馈正文或反馈附件。
- 新增任何枚举或字段前必须更新本文档并完成隐私评审;不得以自由文本、路径、页面标题或 query string 充当事件字段。
| 运营问题 | 使用的数据 / 事件 | 不可替代的原因 |
|---|---|---|
| 哪些租户近期真实使用? | 已开通租户状态 + 任一 core_workflow 事件 |
登录或安全审计不能证明产品核心功能被实际使用。 |
| 哪些租户连续未使用? | 已开通租户状态 + 每租户核心事件的最大时间 | 可显示最后产品行为,且不把停用或配置中的租户误判为流失。 |
| 管理员是否能成功登录、失败是否异常? | 两个 product.auth.* 事件 |
后端在认证结果已确定时记录,避免依赖前端页面加载。 |
| 哪些管理员在活跃使用? |
core_workflow 的内部 admin_user_id 去重 |
以真正的产品动作而非安全审计或企业微信身份判断。 |
| 会话查阅是否采用? |
review_opened、view_selected、subject_selected、detail_opened、older_messages_loaded
|
分别回答入口、视图切换、对象选择、会话打开和历史浏览。 |
| 搜索与筛选是否采用? |
search.executed、filter_applied
|
可按范围、结果状态和筛选维度分析,且不读取查询/筛选原文。 |
| 媒体预览、设置、语言、反馈是否采用? | 对应 media、settings、feedback 事件 |
只收集功能发生与必要枚举,不收集业务内容或反馈正文。 |
- 每次逻辑用户操作只生成一个 UUID
event_id。网络重试、前端重新发送和队列重放必须携带原event_id。 - RND-162 事件存储对
event_id建唯一约束;重复值视为成功但不新增数据。认证事件同样使用每次认证请求生成的单一event_id。 - 前端不得因组件重渲染、轮询、自动刷新或失败重试生成新的事件。页面初始化类事件仅在该页面实例首次完成初始化时生成一次。
- 不以“短时间窗口”粗暴合并不同的用户动作,避免将真实的连续搜索、筛选或会话打开静默丢弃。幂等保障针对重复投递,不改变用户行为语义。
- 去重键、队列元数据和运维日志不得包含 Cookie、Token、请求 body、搜索词、对象 ID 或媒体标识。
| 数据 | 保留期 | 清理策略 |
|---|---|---|
| 原始产品事件 | 180 天 | 每日异步分批删除 occurred_at 早于 180 天的记录;失败仅告警/重试,绝不阻塞主业务。 |
| 按天、租户、事件名聚合的统计行 | 24 个月 | 每月异步删除早于 24 个月的聚合行。 |
| 事件幂等键 | 与原始产品事件相同的 180 天 |
event_id 是原始事件主键,无单独去重表;原始事件删除时一并移除。 |
保留期从事件被持久化时执行。部署调度每日运行 backend/scripts/purge_product_analytics_events_once.py;它仅调用产品事件清理逻辑。清理只处理 RND-162 产品分析存储,不删除或修改 AuditLog、归档消息、媒体、登录会话或其他业务数据。任何保留期变更均须更新本文档版本、迁移/作业测试和运维说明。
任一产品分析事件、请求日志、错误日志、死信队列、去重记录或聚合结果均不得包含:
- 聊天正文、消息片段、解密内容、会话标题、群成员或收发对象;
- 搜索关键词原文、搜索结果、联系人昵称、备注名、企业微信 userid;
- 筛选值原文(包括具体日期、人员、消息类型、会话 ID);
- 媒体 URL、路径、文件名、媒体 ID、MIME 原文、文件内容或缩略图;
- Cookie、Session ID、Token、Secret、密码、授权码、HTTP Authorization、请求/响应完整 body;
- IP 地址、User-Agent、指纹、完整页面 URL/query string、完整 DOM、截图、Session Replay 或热力图数据;
- 反馈正文、附件、联系信息或 AI 对话内容。
允许的关联标识只有服务端派生的内部 tenant_id 与 admin_user_id,并只在受限的产品分析存储和超管查询中使用;它们不得暴露给普通租户管理员或第三方。
RND-162 开发前必须以本契约建立事件 allowlist、配置开关、存储、清理作业、聚合查询和超管只读页面。至少应自动化验证:
-
PRODUCT_ANALYTICS_ENABLED=false时所有事件源均不写入、不排队,原业务结果不变。 - 采集和存储异常被安全降级,登录、会话、搜索、筛选、媒体、设置和反馈的业务流程不受影响。
- 普通租户管理员不能读取跨租户事件、聚合或任一其他租户的数据;只有
PlatformAdmin可读超管分析接口。 - 重复
event_id只产生一条事件/聚合贡献;重试不污染趋势和采用率。 - 每一个表 4 事件仅接收声明字段,敏感字段或未知字段被拒绝且不进入日志。
- 超管 API 和页面只显示本文定义的统计、时间、租户元数据与内部受限标识,绝不返回第 8 节禁止字段。
- 原始事件、聚合和去重记录的保留/清理边界按第 7 节可重复验证。
任何新增产品问题、指标或事件都应先更新本契约并完成隐私评审,再改变采集实现。