Title: 如何正确的标识用户 Locale: zh URL: https://sensorswave.com/docs/data-integration/user-identification/ Description: 用户标识的最佳实践,理解匿名 ID 和登录 ID 准确地识别和追踪用户是数据分析的基础。Sensors Wave 提供了完善的用户标识体系,帮助您打通用户在登录前后、跨设备使用时的行为数据,实现完整的用户行为分析。 ## 为什么用户标识很重要? 在实际业务场景中,用户的行为路径往往是这样的: 1. **匿名浏览**:用户首次访问应用,浏览商品、阅读内容 2. **注册登录**:用户决定注册账号并登录 3. **跨设备使用**:同一用户在手机、平板、电脑等多个设备上使用 4. **设备共享**:家人共用一台设备,不同用户先后登录 如果无法正确识别用户,会导致: - 同一用户的登录前后行为被割裂,无法分析完整用户旅程 - 跨设备行为被识别为多个不同用户,用户数统计不准确 - 转化漏斗分析不准确,影响业务决策 Sensors Wave 的用户标识体系可以解决这些问题,确保数据分析的准确性。 ## 用户标识的核心原则 ### 自动合并用户身份 当您调用 `identify` 方法时,Sensors Wave 会自动将用户登录前的匿名行为和登录后的行为关联到同一个用户下。这个过程是**自动且透明**的,您无需手动处理用户数据合并。 **示例**: ``` 用户 A 的行为轨迹: 1. 匿名浏览商品(匿名 ID: device_001) 2. 加入购物车(匿名 ID: device_001) 3. 注册登录(调用 identify('user_123')) ↓ Sensors Wave 自动关联:device_001 → user_123 4. 完成购买(登录 ID: user_123) 结果:所有行为(步骤 1-4)都归属到用户 user_123 ``` ### 保留历史数据 用户的所有历史行为数据都会被保留,即使设备信息发生变化: - 用户清除 Cookie 或应用数据后,再次登录时历史数据仍然完整 - 用户换设备登录时,新设备的行为会自动归属到同一用户 - 用户的完整行为轨迹可追溯,不会因设备变更而丢失 ### 跨设备用户识别 同一用户在多个设备上的行为会自动聚合: ``` 用户 user_123 的跨设备行为: - 设备 A(手机):浏览、加购 - 设备 B(电脑):登录、完成购买 - 设备 C(平板):查看订单 所有设备的行为都归属到 user_123 用户数统计:1 个用户(而不是 3 个) ``` ## 核心概念 ### 匿名 ID(Anonymous ID) 匿名 ID 用于标识用户的设备或浏览器,在用户首次访问应用时自动生成并存储在本地。 **不同平台的匿名 ID**: | 平台 | 生成规则 | 存储方式 | |------|---------|---------| | **Web 浏览器** | SDK 自动生成 UUID | 存储在 Cookie 中 | | **Android 应用** | SDK 自动生成 UUID | 存储在应用本地存储中 | | **iOS 应用** | SDK 自动生成 UUID | 存储在应用本地存储中 | **特点**: - SDK 自动生成和管理,无需手动设置 - 在用户未登录时追踪用户行为 - 存储在本地,清除应用数据或 Cookie 后会重新生成 ### 登录 ID(Login ID) 登录 ID 是用户登录后的唯一标识符,通常是业务系统中的用户主键。 **常见的登录 ID**: - 用户 ID(如 `user_12345`) - 自定义账号名(如 `zhangsan`) - 手机号(如 md5(`13800138000`)) - 邮箱(如 md5(`user@example.com`)) (注意:如果涉及个人敏感信息,不能使用明文传递) **特点**: - 唯一标识登录用户 - 跨设备保持一致 - 需要在用户登录时主动设置 ### 用户唯一 ID(SSID) Sensors Wave 会根据您上报的匿名 ID 和登录 ID,在服务端自动生成一个用户唯一 ID(即 SSID),用于数据分析时识别唯一用户。 **生成规则**: - 仅有匿名 ID 时:使用匿名 ID 生成用户 ID - 仅有登录 ID 时:使用登录 ID 生成用户 ID - 同时有两者时:优先使用登录 ID 生成用户 ID ## 客户端 SDK 使用指南 对于 Web、iOS、Android 等客户端 SDK,您只需要关注两个关键方法。 ### identify - 用户登录时调用 当用户完成注册或登录时,调用 `identify` 方法将登录 ID 与当前设备的匿名 ID 关联起来。 > **关键点**:调用 `identify` 方法会**自动生成并发送 `$Identity` 事件**,您无需手动发送此事件。这是客户端 SDK 与服务端 SDK 的重要区别。 **JavaScript 示例**: ```javascript // 用户登录成功后 const userId = 'user_12345'; // 从登录接口获取用户 ID // 调用 identify 关联用户身份 SensorsWave.identify(userId); // identify 会自动生成 $Identity 事件,无需额外追踪登录事件 // 如需记录登录方式等额外信息,可以选择性追踪业务登录事件 SensorsWave.trackEvent('UserLogin', { login_method: 'email' }); ``` **iOS 示例**: ```swift // 用户登录成功后 let userId = "user_12345" // 调用 identify 关联用户身份 SensorsWave.identify(userId) // identify 会自动生成 $Identity 事件,无需额外追踪登录事件 // 如需记录登录方式等额外信息,可以选择性追踪业务登录事件 SensorsWave.trackEvent("UserLogin", properties: [ "login_method": "email" ]) ``` **Android 示例**: ```java // 用户登录成功后 String userId = "user_12345"; // 调用 identify 关联用户身份 SensorsWave.identify(userId); // identify 会自动生成 $Identity 事件,无需额外追踪登录事件 // 如需记录登录方式等额外信息,可以选择性追踪业务登录事件 JSONObject properties = new JSONObject(); properties.put("login_method", "email"); SensorsWave.trackEvent("UserLogin", properties); ``` **背后发生了什么**: 1. SDK 将登录 ID 存储在本地 2. **SDK 自动发送一个 `$Identity` 事件**,将匿名 ID 和登录 ID 关联 3. 后续所有事件都会同时上报匿名 ID 和登录 ID > **关于 `$Identity` 事件**:这是 Sensors Wave 的预置事件,用于标记用户身份关联行为。**调用 `identify` 方法会自动生成并发送此事件**,您无需也不应该手动发送。此事件包含 `anon_id` 和 `login_id` 两个字段,服务端会根据这个事件自动处理用户身份合并。`identify` 之后追踪的 `UserLogin` 等业务事件是可选的,主要用于记录登录方式、登录时间等业务信息。 ### reset - 用户登出时调用 当用户登出时,调用 `reset` 方法解除登录 ID 与设备的绑定。 **JavaScript 示例**: ```javascript // 用户登出时 SensorsWave.reset(); // 默认保留匿名 ID // 如果需要同时重置匿名 ID(如公共设备场景) SensorsWave.reset(true); ``` **参数说明**: - `reset()`:登出,保留匿名 ID(默认行为) - `reset(true)`:登出并重置匿名 ID,生成新的匿名 ID > **使用建议**:大多数情况下使用 `reset()` 即可,只在公共设备场景(如商场的体验设备)才需要重置匿名 ID。 ### 完整登录登出示例 ```javascript // 初始化 SDK SensorsWave.init('YOUR_SOURCE_TOKEN', { apiHost: 'https://your-api-endpoint.com', autoCapture: true }); // 用户登录流程 async function handleLogin(email, password) { try { const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email, password }) }); const { userId } = await response.json(); // 关联用户身份(会自动生成 $Identity 事件) SensorsWave.identify(userId); // 可选:追踪业务登录事件,记录登录方式等业务信息 SensorsWave.trackEvent('UserLogin', { login_method: 'email', login_time: new Date().toISOString() }); } catch (error) { console.error('登录失败:', error); } } // 用户登出流程 function handleLogout() { // 解除用户身份绑定 SensorsWave.reset(); // 追踪登出事件 SensorsWave.trackEvent('UserLogout'); } ``` ## 服务端 SDK 使用指南 服务端 SDK 由于运行环境的限制,需要您手动管理匿名 ID 和登录 ID。 ### 服务端 SDK 的限制 - 无法自动采集匿名 ID(服务端无法访问客户端的 Cookie 或设备信息) - 无法使用本地存储(每次请求都是独立的) 因此,服务端 SDK 需要您主动设置 ID 信息。 ### 场景一:追踪匿名用户事件 如果需要在服务端追踪匿名用户行为,需要从客户端获取匿名 ID 并传递给服务端。 **Go 示例**: ```go // 从客户端获取匿名 ID(如通过 HTTP Header 或请求参数) anonId := r.Header.Get("X-Anon-ID") // 发送事件时设置匿名 ID user := sensorswave.User{ AnonID: anonId, } err := client.TrackEvent(user, "PageView", sensorswave.Properties{ "page_url": "/products/123", }) ``` ### 场景二:追踪登录用户事件 如果用户已登录,可以直接使用登录 ID。 **Go 示例**: ```go // 从 session 或 token 中获取用户 ID userId := session.Get("user_id") // 发送事件时设置登录 ID user := sensorswave.User{ LoginID: userId, } err := client.TrackEvent(user, "Purchase", sensorswave.Properties{ "order_id": "ORDER-001", "total_amount": 299.00, }) ``` ### 场景三:服务端追踪用户登录行为 **重要**:与客户端 SDK 不同,服务端 SDK 需要**手动调用 `Identify` 方法**来关联用户身份。在服务端追踪登录行为时,**必须同时提供匿名 ID 和登录 ID**,才能将用户登录前后的行为关联起来。 **Go 示例**: ```go // 用户登录接口 func handleLogin(w http.ResponseWriter, r *http.Request) { // 从请求中获取匿名 ID(客户端通过表单或 Header 传递) anonId := r.FormValue("anon_id") // 验证用户名密码,获取用户 ID userId := authenticateUser(username, password) // 服务端需要手动关联用户身份 user := sensorswave.User{ AnonID: anonId, LoginID: userId, } err := client.Identify(user) if err != nil { log.Printf("关联用户失败: %v", err) } } ``` > **客户端 vs 服务端**: > - **客户端 SDK**:调用 `identify()` 方法会自动生成并发送 `$Identity` 事件 > - **服务端 SDK**:需要手动调用 `Identify()` 方法,同时设置 User 对象的 `AnonID` 和 `LoginID` **客户端配合示例**(JavaScript): ```javascript // 登录表单提交时,将匿名 ID 一起发送给服务端 async function submitLogin(email, password) { const anonId = SensorsWave.getAnonId(); // 获取当前匿名 ID const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email, password, anon_id: anonId // 传递给服务端 }) }); // 登录成功后,客户端也要调用 identify(会自动生成 $Identity 事件) const { userId } = await response.json(); SensorsWave.identify(userId); } ``` ## 典型业务场景 ### 场景一:新用户注册流程 **业务描述**:新用户从首次访问到注册登录的完整流程。 **用户行为路径**: ``` 1. 用户首次访问网站(匿名阶段) └─> SDK 生成匿名 ID: anno_001 2. 用户浏览商品(匿名阶段) └─> 事件: ProductView (anon_id: anno_001) 3. 用户加入购物车(匿名阶段) └─> 事件: AddToCart (anon_id: anno_001) 4. 用户注册并登录 └─> 调用: SensorsWave.identify('user_001') └─> SDK 自动生成事件: $Identity (anon_id: anno_001, login_id: user_001) 5. 用户完成购买(登录后) └─> 事件: Purchase (anon_id: anno_001, login_id: user_001) ``` **数据分析效果**: - 用户登录前后的所有行为都归属到同一个用户(`user_001`) - 可以分析从首次访问到完成购买的完整转化路径 - 计算用户数时,`anno_001` 和 `user_001` 被识别为同一用户 ### 场景二:用户多设备登录 **业务描述**:同一用户在多个设备上登录使用。 **用户行为路径**: ``` 1. 用户在设备 A 上登录 └─> 调用: SensorsWave.identify('user_001') └─> SDK 自动生成事件: $Identity (anon_id: device_a, login_id: user_001) 2. 用户在设备 B 上浏览(未登录) └─> 事件: ProductView (anon_id: device_b) 3. 用户在设备 B 上登录 └─> 调用: SensorsWave.identify('user_001') └─> SDK 自动生成事件: $Identity (anon_id: device_b, login_id: user_001) 4. 后续在任一设备上的行为 └─> 都归属到同一用户 user_001 ``` **数据分析效果**: - 设备 A 和设备 B 的行为都归属到 `user_001` - 可以分析用户的跨设备使用习惯 - 用户数统计准确,不会重复计算 ### 场景三:设备共享(家庭场景) **业务描述**:多个用户共用一台设备,不同用户先后登录。 **用户行为路径**: ``` 1. 设备初始状态(匿名) └─> SDK 生成匿名 ID: device_shared 2. 用户 A 登录 └─> 调用: SensorsWave.identify('user_a') └─> SDK 自动生成事件: $Identity (anon_id: device_shared, login_id: user_a) 3. 用户 A 使用应用 └─> 事件: ProductView (anon_id: device_shared, login_id: user_a) 4. 用户 A 登出 └─> 调用: SensorsWave.reset() 5. 用户 B 登录(同一设备) └─> 调用: SensorsWave.identify('user_b') └─> SDK 自动生成事件: $Identity (anon_id: device_shared, login_id: user_b) 6. 用户 B 使用应用 └─> 事件: ProductView (anon_id: device_shared, login_id: user_b) ``` **数据分析效果**: - 设备在不同时间段的行为归属到不同用户 - 用户 A 和用户 B 被正确识别为两个不同的用户 - 第一次登录前的匿名行为归属到用户 A > **提示**:在这种场景下,匿名 ID 会同时存在于多个用户的历史记录中,Sensors Wave 会通过时间戳和登录事件来正确归属行为数据。 ### 场景四:公共设备(商场体验设备) **业务描述**:商场的公共体验设备,需要在每次用户登出时清除用户数据。 **最佳实践**: ```javascript // 用户登出时重置匿名 ID function handleLogoutOnPublicDevice() { // 清除用户身份并生成新的匿名 ID SensorsWave.reset(true); // 参数为 true // 追踪登出事件 SensorsWave.trackEvent('UserLogout', { device_type: 'public_kiosk' }); } ``` **效果**: - 每次登出后生成新的匿名 ID - 不同用户的数据完全隔离 - 保护用户隐私 ## 注意事项 ### 客户端 SDK **推荐做法**: - (推荐) 用户登录成功后立即调用 `identify` 方法 - (推荐) 用户登出时调用 `reset` 方法 - (推荐) 让 SDK 自动管理匿名 ID,无需手动设置 - (推荐) 在单页应用中确保 SDK 初始化在路由之前完成 **避免的做法**: - (不推荐) 不要在未登录时调用 `identify` 方法 - (不推荐) 不要频繁调用 `reset(true)` 重置匿名 ID(除非是公共设备) - (不推荐) 不要在页面刷新时重复调用 `identify` 方法 - (不推荐) 不要使用可变的值作为登录 ID(如 session ID) ### 服务端 SDK **推荐做法**: - (推荐) 追踪登录行为(`$Identity`)时,务必同时提供匿名 ID 和登录 ID - (推荐) 从客户端获取匿名 ID 并传递给服务端 - (推荐) 使用稳定的用户主键作为登录 ID **避免的做法**: - (不推荐) 不要在服务端生成随机的匿名 ID(会导致无法关联用户) - (不推荐) 不要在登录事件中只提供登录 ID,缺少匿名 ID - (不推荐) 不要使用会话 ID 或 Token 作为用户 ID ### 登录 ID 选择 **推荐做法**: - (推荐) 使用业务系统的用户主键(如 `user_id`) - (推荐) 使用不可变的唯一标识(如用户 ID、邮箱) - (推荐) 确保登录 ID 在系统中全局唯一 - (推荐) 跨平台使用相同的登录 ID 标识同一用户 **避免的做法**: - (不推荐) 不要使用会变化的值(如手机号,用户可能换号) - (不推荐) 不要使用 Session ID 或临时 Token - (不推荐) 不要在不同平台使用不同的用户标识(会被识别为不同用户) ## 常见问题 ### 什么时候应该调用 identify 方法? **推荐时机**: 1. **用户注册成功后** ```javascript async function handleSignup(email, password) { const { userId } = await registerUser(email, password); // 调用 identify 会自动生成 $Identity 事件 SensorsWave.identify(userId); } ``` 2. **用户登录成功后** ```javascript async function handleLogin(email, password) { const { userId } = await loginUser(email, password); // 调用 identify 会自动生成 $Identity 事件 SensorsWave.identify(userId); } ``` 3. **应用启动时,如果用户已登录** ```javascript // 应用初始化时检查登录状态 const currentUser = getCurrentUser(); if (currentUser) { // 调用 identify 会自动生成 $Identity 事件 SensorsWave.identify(currentUser.userId); } ``` **不推荐的时机**: - (不推荐) 页面每次刷新时都调用(会产生重复的 `$Identity` 事件) - (不推荐) 用户未登录时调用 - (不推荐) 使用临时 ID(如 session ID)调用 ### 多次调用 identify 会有什么影响? 对同一用户多次调用 `identify` 方法是安全的,但会产生多个 `$Identity` 事件: ```javascript // 第一次调用 SensorsWave.identify('user_123'); // 发送 $Identity 事件 // 第二次调用(相同的 user_123) SensorsWave.identify('user_123'); // 再次发送 $Identity 事件(冗余) ``` **最佳实践**: ```javascript // 记录是否已经调用过 identify let hasIdentified = false; function identifyUser(userId) { if (!hasIdentified) { SensorsWave.identify(userId); hasIdentified = true; } } ``` 或者使用 SDK 提供的方法检查: ```javascript // 检查当前登录 ID const currentLoginId = SensorsWave.getLoginId(); if (currentLoginId !== userId) { SensorsWave.identify(userId); } ``` ### identify 和 trackEvent 的调用顺序重要吗? **重要!** 应该先调用 `identify`,再追踪登录后的业务事件。 **推荐顺序**: ```javascript async function handleLogin(email, password) { const { userId } = await loginUser(email, password); // 1. 先关联用户身份(自动生成 $Identity 事件) SensorsWave.identify(userId); // 2. 可选:追踪业务登录事件,记录额外的登录信息 SensorsWave.trackEvent('UserLogin', { login_method: 'email' }); // 3. 其他登录后的业务逻辑 loadUserProfile(userId); } ``` **说明**: - `identify` 方法会自动生成 `$Identity` 事件用于身份关联,这是必须的 - `UserLogin` 等业务事件是可选的,主要用于记录登录方式、登录来源等业务信息 - 如果只需要关联用户身份,可以只调用 `identify`,不需要额外追踪 `UserLogin` 事件 **不推荐的顺序**: ```javascript // 错误示例 async function handleLogin(email, password) { const { userId } = await loginUser(email, password); // 先追踪事件(此时用户身份还未关联) SensorsWave.trackEvent('UserLogin'); // 后调用 identify(UserLogin 事件会带有旧的身份信息) SensorsWave.identify(userId); } ``` ### 如何处理用户修改了登录 ID 的情况? 如果用户修改了登录 ID(如更换邮箱、更换手机号),您需要根据业务需求选择处理方式: **方式一:使用不可变的用户主键(推荐)** ```javascript // 使用数据库的用户主键作为登录 ID SensorsWave.identify(user.id); // 'user_12345',永远不会变 // 邮箱、手机号作为用户属性记录 SensorsWave.profileSet({ email: user.email, phone: user.phone }); ``` **方式二:使用新的登录 ID 重新 identify** ```javascript // 用户更换邮箱后 function handleEmailChange(oldEmail, newEmail) { // 使用新邮箱重新 identify SensorsWave.identify(newEmail); // 追踪邮箱变更事件 SensorsWave.trackEvent('EmailChanged', { old_email: oldEmail, new_email: newEmail }); } ``` > **建议**:优先使用方式一,使用不可变的用户主键(如数据库自增 ID、UUID)作为登录 ID,可变信息(如邮箱、手机号)作为用户属性。 ### 用户清除了 Cookie 或应用数据怎么办? 清除 Cookie 或应用数据后,SDK 会生成一个新的匿名 ID。当用户再次登录时: 1. SDK 调用 `identify` 方法关联新的匿名 ID 和登录 ID 2. Sensors Wave 会自动将新设备归属到该登录用户 3. 之前的匿名 ID 对应的历史数据仍然保留在该用户名下 **示例**: ``` 原始设备(清除数据前): - 匿名 ID: device_old - 用户行为: 浏览商品、加购 用户清除数据后: - 匿名 ID: device_new(SDK 生成新的匿名 ID) 用户重新登录: - 调用 identify('user_123') - Sensors Wave 自动关联: device_new → user_123 结果: - user_123 的历史数据包含: (支持) device_old 的行为(浏览、加购) (支持) device_new 的行为(登录后的所有行为) ``` **结果**:用户的所有历史数据都能正确归属,不会丢失。 ### 如何验证用户标识配置是否正确? **方法一:使用调试模式** ```javascript // 开启调试模式,查看事件上报详情 SensorsWave.init('YOUR_SOURCE_TOKEN', { apiHost: 'https://your-api-endpoint.com', debug: true // 在控制台查看事件详情 }); ``` **方法二:在 Sensors Wave 后台查看实时事件** 1. 登录 Sensors Wave 控制台 2. 进入「数据中心」→「数据流」→「实时事件」 3. 查看最新事件,确认 `anon_id` 和 `login_id` 字段正确上报 **方法三:测试完整流程** ```javascript // 测试流程 console.log('1. 匿名 ID:', SensorsWave.getAnonId()); // 追踪匿名事件 SensorsWave.trackEvent('TestAnonymousEvent'); // 模拟登录 SensorsWave.identify('test_user_001'); console.log('2. 登录 ID:', SensorsWave.getLoginId()); // 追踪登录后事件 SensorsWave.trackEvent('TestLoginEvent'); // 在后台查看这两个事件是否关联到同一用户 ``` ### 单页应用(SPA)需要特殊处理吗? 不需要。SDK 会自动处理路由变化,只需确保: 1. SDK 在应用初始化时加载(在路由配置之前) 2. 开启 `isSinglePageApp` 配置 3. 用户登录登出时正常调用 `identify` 和 `reset` 方法 ```javascript // React 应用示例 // 在应用入口初始化(早于路由) SensorsWave.init('YOUR_SOURCE_TOKEN', { apiHost: 'https://your-api-endpoint.com', isSinglePageApp: true // 开启 SPA 支持 }); ``` ### 如何处理第三方登录(微信、支付宝等)? 第三方登录的处理方式与普通登录相同,关键是使用统一的用户 ID: ```javascript // 微信登录成功后 async function handleWechatLogin(wechatCode) { // 通过微信 code 换取用户信息 const response = await fetch('/api/login/wechat', { method: 'POST', body: JSON.stringify({ code: wechatCode }) }); const { userId } = await response.json(); // 使用业务系统的用户 ID,而不是微信的 openId // identify 会自动生成 $Identity 事件 SensorsWave.identify(userId); // 可选:追踪业务登录事件,记录登录方式 SensorsWave.trackEvent('UserLogin', { login_method: 'wechat' }); } ``` > **建议**:使用业务系统的统一用户 ID,而不是第三方平台的 ID(如微信 openId),这样可以将不同登录方式的用户数据关联到同一个用户。 ## 相关文档 - [JavaScript SDK 使用指南](sdks/javascript.mdx):详细的 SDK 集成说明 - [埋点方案选择](tracking-strategy.mdx):了解服务端埋点和客户端埋点的优劣 - [数据模型](data-model.mdx):理解 Sensors Wave 的数据结构 - [事件和属性](events-and-properties.mdx):事件设计规范和最佳实践 --- **最后更新时间**:2026 年 1 月 20 日