用户行为埋点 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 请求头 |
响应
{
"code": 202,
"message": "accepted",
"data": {
"status": "accepted"
}
}
注意:接口返回 202,写入是异步的(fire-and-forget),前端无需等待或处理结果。
示例
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 |
brand_recommend |
品牌推荐匹配命中 |
{recommendations: [{brand_name, matched_keyword, tier}]} |
品牌推荐匹配由后端在 SSE 流中自动注入 brand_recommend 事件,前端收到后渲染推荐卡片。事件数据详见 HANDOVER.md 第十六章节。
管理端——品牌管理 API
| 方法 |
路径 |
说明 |
| GET |
/api/v1/admin/knowledge/brands |
品牌列表(分页+搜索 keyword/name) |
| POST |
/api/v1/admin/knowledge/brands |
新增品牌(name + 10个长文本字段 + jump_url + sort_order) |
| PUT |
/api/v1/admin/knowledge/brands/{id} |
修改品牌 |
| DELETE |
/api/v1/admin/knowledge/brands/{id} |
删除品牌 |
| POST |
/api/v1/admin/knowledge/brands/import |
品牌 CSV 导入 |
| GET |
/api/v1/admin/knowledge/brands/export |
品牌 CSV 导出 |
| GET |
/api/v1/admin/knowledge/brands/template |
下载品牌导入模板 |
管理端——品牌推荐规则 API
| 方法 |
路径 |
说明 |
| GET |
/api/v1/admin/knowledge/brand-recommend-rules |
规则列表(分页+搜索 keyword) |
| POST |
/api/v1/admin/knowledge/brand-recommend-rules |
新增规则(keyword + brand_id + tier) |
| PUT |
/api/v1/admin/knowledge/brand-recommend-rules/{id} |
修改规则 |
| PATCH |
/api/v1/admin/knowledge/brand-recommend-rules/{id}/toggle |
启用/停用规则 |
| DELETE |
/api/v1/admin/knowledge/brand-recommend-rules/{id} |
删除规则 |
| POST |
/api/v1/admin/knowledge/brand-recommend-rules/import |
规则 CSV 导入 |
| GET |
/api/v1/admin/knowledge/brand-recommend-rules/export |
规则 CSV 导出 |
| GET |
/api/v1/admin/knowledge/brand-recommend-rules/template |
下载规则导入模板 |
品牌实体字段
{
"id": 1,
"name": "芬必得布洛芬缓释胶囊",
"function_indication": "用于缓解轻至中度疼痛...",
"usage_dosage": "口服。成人一次1粒,一日2次...",
"contraindication": "对本品过敏者禁用...",
"ingredients": "每粒含布洛芬300mg",
"properties": "本品为胶囊剂,内容物为白色粉末",
"specification": "0.3g×20粒/盒",
"adverse_reactions": "偶见恶心、呕吐...",
"precautions": "孕妇及哺乳期妇女慎用...",
"execution_standard": "《中国药典》2025年版二部",
"storage": "密封,在阴凉干燥处保存",
"jump_url": "https://example.com/product/123",
"description": "知名止痛品牌",
"sort_order": 0,
"is_active": true
}
匹配规则实体字段
{
"id": 1,
"keyword": "布洛芬",
"brand_id": 1,
"tier": 1,
"is_active": true
}
接口调用(自动埋点)
| 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 函数)
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) {}
}
使用示例
// 页面浏览
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 调用)
(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;
});
};
})();
四、注意事项
- fire-and-forget — 埋点接口返回 202,异步写入,前端不要 await,不要阻塞主流程
- keepalive — fetch 带上
keepalive: true,确保页面关闭/跳转时也能发出
- 容错 —
track() 必须放在 try/catch 里,埋点失败不能影响正常功能
- user_key — 建议前端传 JWT Token(
Authorization: Bearer xxx),后端自动提取用户标识;不传则记为 anonymous
- event_data — 建议始终传
{} 而非 null,方便后端统一处理
- page_url — 建议传完整 URL,方便分析不同页面的行为分布