# snippet（片段）输出方式

## 产物

两种输出：
1. **markdown 响应**：按端/平台分组展示代码片段（方便阅读）
2. **代码文件**：`.ae-tracking/output/<platform>-sdk.<ext>`（方便复制使用）

---

## markdown 响应结构

### 0. 前置 checklist

展示配置信息：
```
配置信息：
- SDK 类型：<platform>（如 android / java / javascript）
- SERVER_URL：<用户提供的上报地址>
- appId：<用户提供的 APP_ID>
- 用户体系：account_id=<字段名>，distinct_id=<策略>
```

### 1. SDK 初始化片段

```
## <平台> SDK 初始化

```<lang>
// 复制到你的入口文件
<init 代码 with SERVER_URL / appId>
```

### 1.1 自动采集事件开启（如需要）

**重要**：自动采集事件（如页面浏览、控件点击）在 SDK 初始化后默认关闭，
需要在初始化时或初始化后调用对应 API **开启**才会生效。

⚠️ **必须先读 `references/autotrack-enum.md`**，查询对应 SDK 的正确枚举值和枚举名。
**不允许在未查表的情况下直接写自动采集代码**（枚举名写错用户会直接报错）。

```
## 自动采集事件开关

```<lang>
// 根据选择的 SDK 类型，添加对应的自动采集开关
<autoTrackEnable 代码>
```
```

### 2. 公共属性片段

```
## 公共属性

```<lang>
// 在 SDK init 后调用
<setSuperProperties 代码>
```
```

### 3. 用户属性片段

```
## 用户属性

```<lang>
// 用户登录后调用
<user_set / user_setOnce 代码>
```
```

### 4. 按事件组

```
## 事件组：<tag>

### <event_name>
触发时机：<event_desc>

```<lang>
// 放到 <触发位置>
ta.track('<event_name>', {
    <prop_name>: <示例值>,
    ...
});
```
```

逐事件展示，每个独立成 block，方便复制。

### 5. 验证步骤（snippet 模式专用）

```
## 验证步骤

1. **集成 SDK**（按集成方式选择对应步骤）
   - **npm / 包管理器集成**：执行 `npm install` 或对应包管理器命令安装 SDK
   - **构建工具集成**：通过 Package Manager 或 Epic Games Launcher 添加 SDK 依赖
   - **依赖管理集成**：在 `build.gradle` / `Podfile` 中添加 SDK 依赖
   - **手动下载集成**：下载 SDK 文件并放置到项目对应目录

   参考 `references/sdk-index.md` 中对应 SDK 的官方文档

2. **Debug 模式设备绑定**（AE Debug 需要先添加设备 ID）
   - 获取设备 ID：SDK 初始化后控制台打印的 DeviceId，或调用 SDK 的获取设备 ID 方法
   - 添加设备：AE 系统 → 埋点管理 → Debug 模式 → 添加设备 → 粘贴设备 ID

3. **操作应用触发事件**，在 AE Debug 页面查看数据上报
```

---

## 代码文件生成

### 文件路径规则

| 端/平台 | 文件路径 |
|---|---|
| 客户端 android (java) | `.ae-tracking/output/android-sdk-java.java` |
| 客户端 android (kotlin) | `.ae-tracking/output/android-sdk-kotlin.kt` |
| 客户端 ios (objc) | `.ae-tracking/output/ios-sdk-objectivec.m` |
| 客户端 ios (swift) | `.ae-tracking/output/ios-sdk-swift.swift` |
| 客户端 openharmony | `.ae-tracking/output/openharmony-sdk.ets` |
| 客户端 javascript | `.ae-tracking/output/javascript-sdk.js` |
| 客户端 miniprogram | `.ae-tracking/output/miniprogram-sdk.js` |
| 客户端 unity | `.ae-tracking/output/unity-sdk.cs` |
| 服务端 java | `.ae-tracking/output/java-sdk.java` |
| 服务端 python | `.ae-tracking/output/python-sdk.py` |
| 服务端 go | `.ae-tracking/output/go-sdk.go` |
| 服务端 nodejs | `.ae-tracking/output/nodejs-sdk.ts` |
| 服务端 php | `.ae-tracking/output/php-sdk.php` |

**多语言平台输出规则**：
- Android SDK：若选择 Java + Kotlin，生成两个文件（android-sdk-java.java 和 android-sdk-kotlin.kt）
- iOS SDK：若选择 Objective-C + Swift，生成两个文件（ios-sdk-objectivec.m 和 ios-sdk-swift.swift）

### 文件内容结构

```<ext>
/**
 * AE 埋点代码片段 - <平台>
 * 
* 使用说明：
 * 1. 复制 SDK 初始化代码到入口文件
 * 2. 复制公共属性代码到 init 后
 * 3. 复制事件代码到对应触发位置
 * 
 * 配置：
 * - SERVER_URL: <数据上报地址>
 * - APPID: <APP_ID>
 */

// ==================== SDK 初始化 ====================
<init 代码>

// ==================== 公共属性 ====================
<setSuperProperties 代码>

// ==================== 用户属性 ====================
<user_set 代码>

// ==================== 事件组：<tag> ====================
// <event_name> - <event_desc>
<track 代码>

// ... 其他事件
```

### README 文件

生成 `.ae-tracking/output/README.md`：

```
# AE 埋点代码片段

## 文件清单

| 文件 | 说明 |
|---|---|
| <platform>-sdk.<ext> | SDK 初始化 + 事件调用代码 |
| daemon.json | LogBus2 配置（仅服务端） |

## 使用步骤

### 客户端

1. 复制 SDK 初始化代码到入口文件
2. 配置 SERVER_URL 和 appId
3. 在对应触发位置复制事件代码

### 服务端

1. 复制 LoggerConsumer 代码到项目
2. 配置日志文件路径
3. **下载 LogBus2**：https://docs-v2.thinkingdata.cn/?version=latest&code=logbus2_installation（下载地址在官方文档「二、下载 LogBus2」章节）
4. 将 daemon.json 复制到 LogBus2 的 conf/ 目录
5. 启动 LogBus2：`./logbus start`

## LogBus2 官方文档

| 文档 | 链接 |
|---|---|
| 使用指南 | https://docs-v2.thinkingdata.cn/?version=latest&code=logbus2_installation |

注意：LogBus v1 已过时，请使用 LogBus2。

## 验证

运行 `te-debug.<ext>`，打开 AE Debug 页面验证数据上报。

### Debug 模式设备绑定（仅 snippet 模式需要）

AE Debug 模式需要先在 AE 系统添加设备 ID 才能查看数据：

1. **获取设备 ID**（二选一）：
   - 客户端日志：SDK 初始化后会打印设备 DeviceId
   - 代码获取：`ta.getDeviceId()` 获取当前设备的设备 ID

2. **在 AE 系统添加设备**：
   - 路径：AE 系统 → 埋点管理 → Debug 模式 → 添加设备
   - 粘贴设备 ID 并确认

3. **验证数据**：
   - 操作应用触发事件
   - 在 AE Debug 页面查看数据上报
```

---

## 生成流程

### ⚠️ 前置强制检查（不得跳过）

**生成代码前，必须按以下顺序执行，每步完成后才能进入下一步：**

#### Step 0：读取 SDK 主文档

1. **读 `references/sdk-index.md`** 找到对应 SDK 的主文档路径
2. **读 SDK 主文档**，必须包含以下章节：
   - **集成 SDK 章节**（npm 包名 / CDN URL / 集成方式）
   - **初始化章节**（init API 签名和示例代码）
   - **track 章节**（事件上报 API 和示例）
   - **用户属性章节**（user_set / user_setOnce / user_add API）

3. **⚠️ 禁止跳过 Step 0** — 即使之前生成过同平台代码，每次生成都必须重新读取最新文档

#### Step 1：从 plan 读取配置

1. 从 plan 读取 `meta.user_identity`（用户体系配置）
2. **从 plan 读取事件列表，按 platform 筛选**：
   - 客户端 snippet：只读 `platform === "client"` 或 `platform === "both"` 的事件
     - xlsx 中 "采集端" 为 `客户端` 或 `client` → `platform: "client"`
     - xlsx 中 "采集端" 为 `客户端,服务端` 或 `client,server` → `platform: "both"`
   - 服务端 snippet：只读 `platform === "server"` 或 `platform === "both"` 的事件
     - xlsx 中 "采集端" 为 `服务端` 或 `server` → `platform: "server"`
     - xlsx 中 "采集端" 为 `客户端,服务端` 或 `client,server` → `platform: "both"`
   - 若事件无 platform 字段（早期 xlsx 无此列），则读取全部事件（向后兼容）
3. 从 plan 读取用户属性列表（用户属性无 platform 字段，两端都生成）

#### Step 2：复制 wiki 代码示例

**必须从 Step 0 读取的 wiki 主文档中直接复制代码示例**，不得手动编写：

- **初始化代码**：直接复制 wiki 文档中的完整代码块（包括 import 语句、包名、init 参数）
- **⚠️ 语言严格匹配**：
  - 用户选择 **Java** → 只复制 `::: el-tab-pane label=Java` 或 ````java` 块的代码
  - 用户选择 **Kotlin** → 只复制 `::: el-tab-pane label=Kotlin` 或 ` ```kotlin` 块的代码
  - 用户选择 **Objective-C** → 只复制 `::: el-tab-pane label=Objective-C` 或 ````objc` 块的代码
  - 用户选择 **Swift** → 只复制 `::: el-tab-pane label=Swift` 或 ` ```swift` 块的代码
  - **禁止**从 Java 块复制到 Kotlin 文件，或反过来
  - **禁止**从 Objective-C 块复制到 Swift 文件，或反过来
  - 若 wiki 文档使用 el-tabs 格式，先确认当前语言 tab，再切换到正确 tab 复制
- **自动采集代码**：⚠️ **必须先读 `references/autotrack-enum.md`** 查询对应 SDK 的正确枚举名和枚举值，再从 wiki 复制 `EnableAutoTrack` 调用示例。**不得猜测枚举名或遗漏枚举值**
- **事件 track 代码**：属性名和事件名来自 plan，结构直接复用 wiki 示例
- **用户属性代码**：直接复制 wiki 文档中的 API 调用方式
- **公共属性代码**：直接复制 wiki 文档中的 setSuperProperties 调用方式

#### Step 3：生成代码片段

生成代码片段（markdown + 文件）

#### Step 3.5：⚠️ 语言风格校验（不得跳过）

生成完成后，对每个输出文件做语言风格检查：

| 语言 | 检查规则 |
|---|---|
| **Kotlin** | 文件中不得包含 `;`（行尾分号），如发现立即修正并告知用户 |
| **Java** | 每条语句必须以 `;` 结尾，如缺失立即修正 |
| **Swift** | 不得使用 `var` 声明可选类型，应使用 `let` + `?`；不得出现 `@objc` 除非必要 |
| **Objective-C** | 方法调用必须使用 `[self method]` 语法，不得出现 `.` 调用；必须使用 `nil` 而非 `null` |

**若发现语言风格错误**：
1. 立即用正确语法替换
2. 在响应中标注 `⚠️ 已自动修正：<错误描述> → <正确写法>`

#### Step 4：服务端额外处理

1. 服务端额外生成 `daemon.json`
2. **必须同步提供 LogBus2 官方文档链接** 

---

## 代码风格

- 事件名 / 属性名严格来自 plan，不改
- 每个代码块上方加注释说明触发时机
- 不加 try/catch
- 用户体系调用：`login()` / `identify()` 根据 `meta.user_identity` 生成

### 属性占位值规则（根据 plan type 字段）

生成属性值占位时，**必须根据 plan 中属性的 `type` 字段选择对应类型的占位值**，不得对所有类型统一使用字符串占位：

| plan `type` | 占位值 | 说明 |
|---|---|---|
| `string` | `"\"<实际值>\""` | 字符串占位 |
| `number` | `0` | 数值占位（**`userAdd` 必须用数值**，字符串会导致运行时报错） |
| `bool` | `true` / `false` | 布尔占位（**不得用字符串 `"\"<实际值>\""`**，否则数据类型不匹配） |
| `datetime` | `new Date()` / 平台对应语法 | 时间占位 |
| `array_string` | `["示例"]` | 列表占位 |
| `array_row` | `[{"key":"示例"}]` | 对象组占位 |

**各平台 datetime 占位值语法**：

| 平台 | 语法 |
|---|---|
| Android Java | `new Date()` |
| Android Kotlin | `Date()` |
| iOS Objective-C | `[NSDate date]` |
| iOS Swift | `Date()` |
| JavaScript | `new Date()` |
| Java Server | `new Date()` |
| Python | `datetime.datetime.now()` |
| Go | `time.Now()` |
| Node.js | `new Date()` |

### ⚠️ 服务端 SDK track() 参数顺序（各 SDK 不同，不得统一假设）

各服务端 SDK 的 `track()` 调用中，**accountId 和 distinctId 的参数位置不同**，生成代码时必须按对应 SDK 的实际顺序：

| SDK | 位置参数顺序 | 建议写法 |
|---|---|---|
| Java | `track(accountId, distinctId, eventName, properties)` | 位置参数 ✅ |
| Go | `Track(accountId, distinctId, eventName, properties)` | 位置参数 ✅ |
| **Python** | **`track(distinct_id, account_id, eventName, properties)`** — **distinct_id 在前** | **优先用 keyword args** ✅ |
| Node.js | 单个 object 参数 `{accountId, distinctId, event, ...}` | object arg ✅ |

**Python SDK 强烈建议使用 keyword args**，避免位置参数顺序混淆：
```python
te.track(account_id="<账号ID>", distinct_id="<访客ID>", event_name="order_create", properties={...})
```

同理，Python `user_set` / `user_setOnce` / `user_add` 也应使用 keyword args：
```python
te.user_set(account_id="<账号ID>", distinct_id="<访客ID>", properties={...})
```

### ⚠️ JavaScript SDK 示例（正确 vs 错误对比）

**从 wiki 直接复制的 init 代码**（包含正确的 import 和包名）：

```js
// ✅ 正确：从 wiki javascript.md 直接复制
import ta from "thinkingdata-browser";
ta.init({
  appId: "YOUR_APP_ID",
  serverUrl: "https://YOUR_SERVER_URL/sync_js",
  autoTrack: {
    pageShow: true,
    pageHide: true,
  }
});
```

**❌ 错误示范（绝对禁止）**：

```js
// ❌ 错误：猜测的包名和 import 写法
import TDAnalytics from "@thinkingdata/web-sdk";  // 包名猜错了
TDAnalytics.init({...});  // 类名猜错了

// ❌ 错误：用 te 或 analytics 等通用变量名
import te from "thinkingdata-browser";  // wiki 没用过 te
te.init({...});
```

**关键点**：
- npm 包名：`thinkingdata-browser`（不是 `@thinkingdata/web-sdk`、不是 `thinkingdata`）
- import 变量名：`ta`（不是 `TDAnalytics`、不是 `te`）
- init 方法：`ta.init({...})`（不是 `TDAnalytics.init()`）