# keentics 接入指南

面向**业务开发**的完整接入手册：从拿到 Key 到验证数据落库。读完即可接入。

---

## 一、5 分钟跑通（最短路径）

1. 找管理员在控制台「**项目**」页建一个项目，拿到 `project_key` 和 `API Key`。
2. 客户端引入对应 SDK，初始化时填上网关地址、`project_key`、`key`。
3. 在关键位置调 `track('事件名', { 属性 })`。
4. 打开控制台「**总览 → 接入诊断**」，看到「已接收」在涨，就通了。

```js
// Web 最小示例
keentics.init({ url: 'https://collect.你的域名', project: 'your_project', key: '你的API_KEY' });
keentics.track('app_start');
```

---

## 二、核心概念（先理解这几个词）

| 概念 | 说明 |
|---|---|
| **事件 event** | 用户的一次行为，如 `level_complete`、`purchase`。分析的基本单位。 |
| **事件属性 properties** | 描述这次行为的键值，如 `{ level: '3-2', coins: 120 }`。 |
| **distinct_id** | 设备级/匿名 ID。SDK 自动生成并持久化，**不用你管**。 |
| **account_id** | 你的业务用户 ID。用户登录后调 `identify` 绑定。 |
| **用户属性** | 描述「人」的键值（昵称、VIP 等级、累计付费），用 `userSet` 等设置。 |
| **超级属性** | 用 `register` 注册后，**每个事件自动携带**（如渠道、服务器区服）。 |
| **project / API Key** | 项目隔离单位；Key 是该项目的写入凭证（可放客户端，等同公开写入密钥）。 |

> **匿名 → 登录的衔接**：用户没登录时事件挂在 `distinct_id` 上；登录时调 `identify(account_id)`，之后该用户的行为可按业务 ID 关联。

---

## 三、上报协议（所有端共用）

`POST <网关>/collect`，body 为 JSON（可 gzip）。请求头：

```
Content-Type: application/json
X-Keentics-Project: <project_key>
X-Keentics-Key: <API Key>        # 生产环境必填
```

Body：

```json
{
  "project": "your_project",
  "events": [{
    "type": "track",
    "event": "level_complete",
    "event_id": "uuid",          // SDK 自动生成，去重幂等用
    "time": 1717600000000,       // 客户端毫秒时间戳
    "distinct_id": "anon-xxx",
    "account_id": "u_123",       // 可选
    "properties": { "level": "3-2", "coins": 120, "win": true },
    "context": { "platform": "web", "os": "...", "app_version": "1.0.0" }
  }]
}
```

事件 `type`：`track`（行为）/ `identify`（绑定身份）/ `user_set` / `user_set_once` / `user_increment` / `user_append`（用户属性）。

> 一般你不用手拼这个——用 SDK 即可。手拼仅用于服务端或没有 SDK 的环境。

---

## 四、各端接入

### Web / JS

```html
<script src="https://collect.你的域名/sdk/keentics.js"></script>
<script>
  keentics.init({
    url: 'https://collect.你的域名',
    project: 'your_project',
    key: '你的API_KEY',
    autoClick: true,          // 可选：自动采集按钮/链接点击
    autoPageView: true        // 默认开：自动 $page_view
  });
  keentics.register({ channel: 'official_site' });           // 超级属性
  keentics.track('level_complete', { level: '3-2', coins: 120, win: true });
  keentics.identify('u_123', { vip: 2 });                    // 登录后
  keentics.userSet({ nickname: 'Neo' });
</script>
```

### iOS (Swift)

```swift
import Keentics
Keentics.shared.configure(url: "https://collect.你的域名", project: "your_project", key: "你的API_KEY", appVersion: "1.0.0")
Keentics.shared.track("level_complete", properties: ["level": "3-2", "coins": 120])
Keentics.shared.identify("u_123", traits: ["vip": 2])
```
> 生产用 HTTPS，ATS 默认放行，无需额外配置。SPM 引入 `sdk/ios`。

### Android (Kotlin)

```kotlin
Keentics.configure(this, "https://collect.你的域名", project = "your_project", key = "你的API_KEY", appVersion = "1.0.0")
Keentics.track("level_complete", mapOf("level" to "3-2", "coins" to 120))
Keentics.identify("u_123", mapOf("vip" to 2))
```
> 需 `<uses-permission android:name="android.permission.INTERNET"/>`。

### 微信小程序

```js
const keentics = require('../../sdk/keentics.js')
keentics.init({ url: 'https://collect.你的域名', project: 'your_project', key: '你的API_KEY' })
keentics.track('level_complete', { level: '3-2', coins: 120 })
```
> 需在小程序后台把 `collect.你的域名` 加入 **request 合法域名**。

### 服务端（任意语言）

没有 SDK 就直接按「三、上报协议」POST。服务端场景 `distinct_id` 直接用业务用户 ID，`time` 用事件真实发生时间。

---

## 五、事件设计规范（埋点前先想清楚）

- **事件命名**：小写 + 下划线，动宾结构，如 `level_complete`、`item_purchase`、`ad_click`。全产品统一，别一个事件多种叫法。
- **属性命名**：同样小写下划线；同一含义跨事件用同一个属性名（如金额统一 `amount`）。
- **数值就传数值**：`coins: 120` 而不是 `"120"`（虽然底层都转字符串存，但便于以后做求和/分布）。
- **超级属性**用于"每个事件都想带"的维度：渠道、区服、AB 分组。
- **用户属性语义**：
  - `userSet`：覆盖（昵称、最新等级）
  - `userSetOnce`：只首次写（注册时间、首充时间）
  - `userIncrement`：累加（累计付费、登录次数）*
  - `userAppend`：数组追加（拥有的徽章）*
  - \* 当前 increment/append 为快照语义，完整聚合在后续版本。
- **identify 时机**：用户登录/注册成功后立即调一次；退出登录调 `reset()`。

---

## 六、验证接入是否成功

1. 控制台「**总览 → 接入诊断**」：看「已接收」是否增长。
2. 「**总览 → 最近事件流**」：能看到刚发的事件原始记录。
3. 「**事件分析**」：选你的事件，确认有曲线。
4. 没数据？看「接入诊断 → 最近拒绝原因」，对照下表。

### 常见拒绝原因

| 现象 | 原因 | 解决 |
|---|---|---|
| `track 缺少 event 名` | 调用没传事件名 | 检查 `track('名', ...)` |
| `缺少 distinct_id` | 极少见，SDK 未初始化就上报 | 确认先 `init/configure` |
| `未知或停用的项目` | project_key 错 / 项目被停用 | 核对 project_key |
| `API Key 错误` | 生产开了鉴权但 key 不对/没带 | 核对项目页的 API Key |
| `触发限流` | 短时超过项目限流 | 调大项目限流或检查是否死循环上报 |
| HTTP 连不上 | 用了 http / 局域网地址 / 域名没加白名单 | 生产必须 https + 公网域名；小程序加合法域名 |

---

## 七、FAQ

- **API Key 放客户端安全吗？** 安全。它是「写入密钥」，只能上报、不能读数据，类似 GA 的测量 ID、Segment 的 write key。读数据要登录控制台。
- **会不会重复计数？** 不会。每个事件有 `event_id`，网络重试/离线补传都按它去重。
- **离线/弱网会丢点吗？** SDK 本地磁盘缓存 + 失败重试，联网后补传。
- **埋点改了要重新发版吗？** 新增事件不用改服务端，直接 `track` 新事件名即可，平台自动识别。
- **怎么区分环境？** 建议测试和生产用不同 project（如 `game_test` / `game_prod`）。

---

## 八、上线注意

- 生产用 **HTTPS**（`https://collect.你的域名`）；明文 HTTP 在 App/网站会被拦。
- 生产**强制 API Key**，每个端都要带 `key`。
- 控制台含 SQL 查询与删除能力，建议放在 VPN/Tailscale 或 IP 白名单后，别裸奔公网。
- 详见 `deploy/README.md` 上线清单。
