|
|
@@ -0,0 +1,195 @@
|
|
|
+# 用户行为埋点 API 文档
|
|
|
+
|
|
|
+## 基本信息
|
|
|
+
|
|
|
+| 项目 | 说明 |
|
|
|
+|------|------|
|
|
|
+| 接口路径 | `POST /api/v1/analytics/events` |
|
|
|
+| 请求方式 | POST |
|
|
|
+| Content-Type | application/json |
|
|
|
+| 返回格式 | `{code, message, data}` |
|
|
|
+| 认证 | 无需认证(有 Token 时自动记录用户标识,无 Token 记录为 anonymous) |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 一、上报行为埋点
|
|
|
+
|
|
|
+### 请求
|
|
|
+
|
|
|
+```
|
|
|
+POST /api/v1/analytics/events
|
|
|
+Content-Type: application/json
|
|
|
+Authorization: Bearer <token> (可选)
|
|
|
+```
|
|
|
+
|
|
|
+### 请求体
|
|
|
+
|
|
|
+| 字段 | 类型 | 必填 | 说明 |
|
|
|
+|------|------|------|------|
|
|
|
+| event_type | string | 否 | 事件类型,不传则默认为 `unknown` |
|
|
|
+| event_data | object | 否 | 事件附加数据,JSON 对象 |
|
|
|
+| page_url | string | 否 | 触发事件的页面 URL(建议传完整地址) |
|
|
|
+| referrer | string | 否 | 来源页面 URL |
|
|
|
+
|
|
|
+### 服务端自动记录
|
|
|
+
|
|
|
+以下字段由服务端从请求中自动提取,**无需前端传入**:
|
|
|
+
|
|
|
+| 字段 | 来源 |
|
|
|
+|------|------|
|
|
|
+| user_key | JWT Token 中的用户标识,未登录为 `anonymous` |
|
|
|
+| ip | 请求来源 IP |
|
|
|
+| user_agent | `User-Agent` 请求头 |
|
|
|
+
|
|
|
+### 响应
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 202,
|
|
|
+ "message": "accepted",
|
|
|
+ "data": {
|
|
|
+ "status": "accepted"
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> 注意:接口返回 202,写入是异步的(fire-and-forget),前端无需等待或处理结果。
|
|
|
+
|
|
|
+### 示例
|
|
|
+
|
|
|
+```bash
|
|
|
+curl -X POST https://pharmacopoeia.kailin.com.cn/api/v1/analytics/events \
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
+ -d '{
|
|
|
+ "event_type": "page_view",
|
|
|
+ "event_data": {},
|
|
|
+ "page_url": "https://pharmacopoeia.kailin.com.cn/?token=xxx"
|
|
|
+ }'
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 二、标准事件类型定义
|
|
|
+
|
|
|
+### 页面行为
|
|
|
+
|
|
|
+| event_type | 触发时机 | event_data |
|
|
|
+|-----------|---------|------------|
|
|
|
+| `page_view` | 页面加载/进入 | `{}` 或 `{referrer: "来源URL"}` |
|
|
|
+
|
|
|
+### 用户交互
|
|
|
+
|
|
|
+| event_type | 触发时机 | event_data |
|
|
|
+|-----------|---------|------------|
|
|
|
+| `click` | 点击快捷提问标签 | `{label: "标签名称"}` |
|
|
|
+| `switch_tab` | 切换导航标签页 | `{tab: "chat" \| "drugs"}` |
|
|
|
+
|
|
|
+### 对话行为
|
|
|
+
|
|
|
+| event_type | 触发时机 | event_data |
|
|
|
+|-----------|---------|------------|
|
|
|
+| `chat` | 用户发送提问 | `{query: "用户输入的问题", has_media: true \| false}` |
|
|
|
+
|
|
|
+### 搜索行为
|
|
|
+
|
|
|
+| event_type | 触发时机 | event_data |
|
|
|
+|-----------|---------|------------|
|
|
|
+| `search` | 搜索药品 | `{keyword: "搜索关键词"}` |
|
|
|
+
|
|
|
+### 浏览行为
|
|
|
+
|
|
|
+| event_type | 触发时机 | event_data |
|
|
|
+|-----------|---------|------------|
|
|
|
+| `drug_detail` | 查看药品详情 | `{drug_id: "H20000001", drug_name: "阿莫西林"}` |
|
|
|
+
|
|
|
+### 接口调用(自动埋点)
|
|
|
+
|
|
|
+| event_type | 触发时机 | event_data |
|
|
|
+|-----------|---------|------------|
|
|
|
+| `api_call` | 任意 API 调用完成 | `{url: "/api/v1/chat/stream", method: "POST", status: 200, elapsed_ms: 342}` |
|
|
|
+| `api_call` | API 调用失败 | `{url: "/api/v1/drug/search", method: "GET", status: 0, elapsed_ms: 5000, error: "network_error"}` |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 三、前端接入示例
|
|
|
+
|
|
|
+### 最小接入(推荐封装 track 函数)
|
|
|
+
|
|
|
+```javascript
|
|
|
+function track(eventType, eventData) {
|
|
|
+ var payload = {
|
|
|
+ event_type: eventType,
|
|
|
+ event_data: eventData || {},
|
|
|
+ page_url: window.location.href,
|
|
|
+ referrer: document.referrer || ''
|
|
|
+ };
|
|
|
+ try {
|
|
|
+ fetch('https://pharmacopoeia.kailin.com.cn/api/v1/analytics/events', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify(payload),
|
|
|
+ keepalive: true // 页面关闭时也能发出
|
|
|
+ }).catch(function(){});
|
|
|
+ } catch(e) {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 使用示例
|
|
|
+
|
|
|
+```javascript
|
|
|
+// 页面浏览
|
|
|
+track('page_view');
|
|
|
+
|
|
|
+// 搜索
|
|
|
+track('search', { keyword: '阿莫西林' });
|
|
|
+
|
|
|
+// 点击
|
|
|
+track('click', { label: '感冒药推荐', position: 1 });
|
|
|
+
|
|
|
+// 提问
|
|
|
+track('chat', { query: '布洛芬怎么吃', has_media: false });
|
|
|
+
|
|
|
+// 自定义事件
|
|
|
+track('custom_event', { key1: 'value1', key2: 'value2' });
|
|
|
+```
|
|
|
+
|
|
|
+### 自动拦截 fetch(可选,自动记录所有 API 调用)
|
|
|
+
|
|
|
+```javascript
|
|
|
+(function(){
|
|
|
+ var _fetch = window.fetch;
|
|
|
+ window.fetch = function(url, opts){
|
|
|
+ var t0 = Date.now();
|
|
|
+ var method = (opts && opts.method) || 'GET';
|
|
|
+ return _fetch.apply(this, arguments).then(function(r){
|
|
|
+ track('api_call', {
|
|
|
+ url: typeof url === 'string' ? url : '',
|
|
|
+ method: method,
|
|
|
+ status: r.status,
|
|
|
+ elapsed_ms: Date.now() - t0
|
|
|
+ });
|
|
|
+ return r;
|
|
|
+ }).catch(function(e){
|
|
|
+ track('api_call', {
|
|
|
+ url: typeof url === 'string' ? url : '',
|
|
|
+ method: method,
|
|
|
+ status: 0,
|
|
|
+ elapsed_ms: Date.now() - t0,
|
|
|
+ error: e.message || 'network_error'
|
|
|
+ });
|
|
|
+ throw e;
|
|
|
+ });
|
|
|
+ };
|
|
|
+})();
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 四、注意事项
|
|
|
+
|
|
|
+1. **fire-and-forget** — 埋点接口返回 202,异步写入,前端不要 await,不要阻塞主流程
|
|
|
+2. **keepalive** — fetch 带上 `keepalive: true`,确保页面关闭/跳转时也能发出
|
|
|
+3. **容错** — `track()` 必须放在 try/catch 里,埋点失败不能影响正常功能
|
|
|
+4. **user_key** — 建议前端传 JWT Token(`Authorization: Bearer xxx`),后端自动提取用户标识;不传则记为 `anonymous`
|
|
|
+5. **event_data** — 建议始终传 `{}` 而非 `null`,方便后端统一处理
|
|
|
+6. **page_url** — 建议传完整 URL,方便分析不同页面的行为分布
|