# 新增用户及首日首次标记

注意：不要在线上页面直接切换不同项目的数据接收地址，会导致首日首次，id 关联异常。建议在线下测试时数据发到测试项目，没问题的话，线上采集的数据直接发往正式项目。

## 1. 新增用户

### 1.1 新增用户的标记

一般来说，互联网产品主要有两种统计新增用户的方法:

* 以用户的注册作为新增用户的统计口径 &#x20;
* 以用户打开或者使用 App 作为新增用户的统计口径

第一种情况较为简单，开发者可以追踪用户注册事件，并在用户注册时设置相关的 Profile 信息，开发者可以直接跳到第三节阅读渠道追踪相关内容；第二种情况，则可以参考本文档的方案。

### 1.2 新增用户标记方案

新用户的标记主要是在客户端完成。对于 iOS 和 Android，基本的原理和步骤如下：

1. 在 App 每次启动时，判断是否有 **启动过** 的标记，这个标记可以记录在客户端的本地存储中；
2. 如果发现已经有 **启动过** 的标记，则不做任何处理；
3. 如果发现没有 **启动过** 的标记，则做如下处理：
   1. 调用 `trackInstallation` 接口，记录一个用户首次访问事件，在后文中，我们会详细介绍这个接口的行为，以及如何使用它获取渠道信息
   2. 在客户端的存储中，加上**启动过**的标记，例如，可以在 Android 的 SharedPreferences 中或 iOS 的 Keychain 中存储该标记

**注：** 如果 Android 和 iOS SDK 使用的是比较新的版本 ( Android SDK 1.6.23及以后的版本、iOS SDK 1.6.24 及以后的版本) ，客户端可以在每次启动 App 时直接调用 `trackInstallation` 接口，SDK 内部会处理是否有 **启动过** 的标记。

对于 Web 端，在使用我们的 JS SDK 时，如果打开了默认收集 `$pageview` 事件的选项，JS SDK 会根据在 Cookie 中的记录，来在事件中增加首日访问这个属性，同时，Profile 中也会有对应的记录。如果想自己进行记录，则可以参考 iOS 和 Android 的做法，只不过**启动过**这个标记改为记录在 Cookie 中。

上述方案是有一定的局限性的，如果用户清理了 App 的本地存储或者清除了 Cookie，则判断会失效，即会造成重复发送首次启动事件。但是，如果记录的 Distinct Id 在清理本地存储或者清除 Cookie 时不会发生改变，则可通过下面 2.3 节介绍的新增用户标记在服务端的修正解决，此外用户的 Profile 中记录的激活相关信息依然是可信的，具体可以参考 4.1 中的统计方法。

### 1.3 “是否首日访问”在服务端的修正

从神策分析 1.8 版开始，所有的单机版用户和部分集群版用户已经默认开启了“是否首日访问”标记在服务端的修正，即以一个专用的数据库记录用户首日访问的时间。当上报的事件包含 `$is_first_day`（是否首日访问）属性并且取值为 `true` 时，服务端导入这条事件时会先在数据库中以触发的 Distinct Id 对应的神策内部 Id 进行查询，若没有查到，那么在数据库中记录该 Distinct Id 首日访问的时间；如果可以查到那么判断本次事件触发时间与之前记录的是否是同一天，若不是同一天那么修改 `$is_first_day` 为 `false`。

这在一定程度上解决了 App 卸载重装被算作新用户的问题（2.2 节），例如一个使用 IDFA （卸载 App 重装，取到的值不变）作为 Distinct Id 的 App 重装后客户端向服务端上报时 `$is_first_day` 的值为 `true`（2.2 节客户端记录的标记卸载时删除），服务端可以根据数据库中的记录判断该值是否应该被修正为 `false`。

在判断新增用户时，神策是选择【App 启动是否⾸日为真】判断的。如果客户在接入神策之前有一 批历史用户，正常接入神策后，由于神策没有保存这些历史用户的首日访问时间，会将这批老用户标记为新增用户。如果想将这批历史⽤户正确标记为老用户。需要在他们的数据上报之前就将他们的首日访问时间导入到该数据库。导入步骤如下：

1. 生成一个文件，每行为一个用户的 Distinct Id 和首日访问时间（以 unix 时间戳表示）：

   ```
   6D92078A-8246-4BA4-AE5B-76104861E7DC 1513577063
   5D3169E2-16BC-316C-12AB-1E2EC1A79E2B 1512057600
   ```

   注意：

   1. 该文件上传之后，只对上传日期之后入库的数据有效。之前已经入库的数据无法修改；
   2. 服务端会根据上传的首日访问时间，对实时导入的事件属性 $is\_first\_day（是否首日访问）取值为 true  修正，也会修正实时导入的用户属性 $first\_visit\_time（首次访问时间） 。
2. 如果文件中的 Distinct Id 是`设备 ID`（参考 [如何准确的标识用户](https://www.sensorsdata.cn/manual/user_identify.html)），继续下一步。若 Distinct Id 是`登录 ID`，则需要通过 SDK 或导入工具对每个 ID 导入一条 `profile_set`，设置 `is_login_id` 为 `true`，`properties` 可以为空。例如 Java SDK 可以调用：

   ```java
   sensorsAnalytics.profileSet("123456789", true, Collections.emptyMap());
   ```
3. 登录神策服务器，使用 `sa_cluster` 用户执行：

   ```
   java -cp '/home/sa_cluster/sa/extractor/lib/extractor-1.0-SNAPSHOT.jar:/home/sa_cluster/sa/commonjars/*' com.sensorsdata.analytics.extractor.utils.ImportFirstTimeUtils --project 导入的项目名 --file 数据文件路径
   ```

## 2. 使用首次触发事件标记

一些分析场景中，我们希望可以通过一个事件属性判断该事件（自定义事件，前端和后端事件皆可）是否是某个用户首次触发，例如 App 启动中的这个属性可以判断用户是否首次启动 App，加入购物车事件中的这个属性可以判断用户是否首次将商品加入购物车。

### 2.1 导入时添加字段

在导入事件时，若认为这次事件 **可能** 是该用户（distinct\_id）首次触发，设置 **$is\_first\_time** 的值为 **true** 即可。

其原理是神策后端导入模块在数据导入过程中会在遇到 **$is\_first\_time** 的值为 **true** 时判断和 **修正** 该值，具体来说，若遇到 **$is\_first\_time** 的值为 **true** 的事件数据，神策后端会在数据库查询该用户在该事件是否有首次访问记录，若已记录的触发时间与本次不同，那么将修改 **$is\_first\_time** 的值为 **false**，否则将记录该用户首次触发事件时间并保持 **$is\_first\_time** 值为 **true**。

### 2.2 一个例子

以 Java SDK 为例：

```
// 标记首次，最终导入值为 true
properties.put("$is_first_time", true); 
sa.track("user1", false, "BuyGold", properties);

// do something else ...

// 标记首次，但最终导入值为 false
properties.put("$is_first_time", true); 
sa.track("user1", false, "BuyGold", properties);
```

虽然两次都设置了 **$is\_first\_time** 为 **true**，但实际导入后仅第一次导入的值为 **true**。

### 2.3 注意事项

* 仅当 **$is\_first\_time** 值为 **true** 时会触发上述逻辑；
* 若之前导入数据没有设置过 **$is\_first\_time** 值为 **true**，那么第一次出现时才会记录首次触发时间，并且以后以这次时间判断；后续我们会提供批量导入用户首次触发记录的工具；
* 该逻辑里判断是否首次触发是以事件触发时间（精确到毫秒）是否与数据库里的值相等作为条件，若两次触发事件时间相同且都设置了 **$is\_first\_time** 值为 **true** 将不会进行修正；一般情况下不会发生这种情况，例如 App 连续发送两次事件，若第二次已经明确知道不是首次时，不应该设置 **$is\_first\_time** 值为 **true**；
* 该逻辑受数据导入顺序影响，例如先导入 6 日数据并标记了首次，再导入 3 日的数据并标记了首次，最终 6 日的数据首次被标记为 **true**，而 3 日的将被标记为 **false**；
* 本功能在 1.8 及以后的版本中支持，且构建时间（可在后端的关于页面查看）在 2017-11-26 之后，否则需要先进行升级。
