diff --git a/frontend-sdk/API_EXAMPLES.md b/frontend-sdk/API_EXAMPLES.md deleted file mode 100644 index ec03878..0000000 --- a/frontend-sdk/API_EXAMPLES.md +++ /dev/null @@ -1,284 +0,0 @@ -# 数据埋点 API 调用示例 - -## 基础信息 - -- **接口路径**: `/doc_ai/v1/analytics/track` -- **请求方法**: `POST` -- **Content-Type**: `application/json` -- **认证**: 可选(Bearer Token) - -## 1. 基础埋点事件(最小参数) - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{ - "user_id": 12345, - "event_name": "button_click" - }' -``` - -## 2. 完整埋点事件(包含所有字段) - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -d '{ - "user_id": 12345, - "event_name": "formula_recognition_start", - "properties": { - "file_name": "math_formula.png", - "file_size": 102400, - "file_type": "image/png", - "upload_method": "drag_drop" - }, - "device_info": { - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", - "screen_width": 1920, - "screen_height": 1080, - "language": "zh-CN", - "timezone": "Asia/Shanghai", - "platform": "MacIntel" - }, - "meta_data": { - "task_id": "task_123456", - "timestamp": 1706342400000 - } - }' -``` - -## 3. 页面浏览事件 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{ - "user_id": 12345, - "event_name": "page_view", - "properties": { - "page_url": "https://example.com/home", - "page_title": "首页", - "page_name": "home", - "referrer": "https://example.com/login" - }, - "device_info": { - "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", - "screen_width": 1920, - "screen_height": 1080 - } - }' -``` - -## 4. 任务相关事件 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -d '{ - "user_id": 12345, - "event_name": "task_create", - "properties": { - "task_type": "formula_recognition", - "file_name": "equation.png", - "file_size": 204800 - }, - "meta_data": { - "task_id": "task_789012" - } - }' -``` - -## 5. 任务完成事件 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{ - "user_id": 12345, - "event_name": "task_complete", - "properties": { - "duration_seconds": 5.2, - "success": true, - "result_type": "latex" - }, - "meta_data": { - "task_id": "task_789012" - } - }' -``` - -## 6. 表单提交事件 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{ - "user_id": 12345, - "event_name": "form_submit", - "properties": { - "form_name": "user_registration", - "form_fields": ["email", "password", "phone"], - "success": true, - "validation_errors": 0 - } - }' -``` - -## 7. 文件上传事件 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{ - "user_id": 12345, - "event_name": "file_upload", - "properties": { - "file_name": "document.pdf", - "file_size": 5242880, - "file_type": "application/pdf", - "upload_source": "drag_drop", - "upload_duration_ms": 1200 - } - }' -``` - -## 8. 错误追踪事件 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{ - "user_id": 12345, - "event_name": "error_occurred", - "properties": { - "error_type": "network_error", - "error_message": "Failed to fetch data", - "error_code": "NET_001", - "page_url": "https://example.com/tasks", - "user_action": "click_submit_button" - } - }' -``` - -## 9. 使用环境变量(推荐) - -```bash -# 设置环境变量 -export API_BASE_URL="http://localhost:8080" -export JWT_TOKEN="YOUR_JWT_TOKEN" -export USER_ID=12345 - -# 调用接口 -curl -X POST ${API_BASE_URL}/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${JWT_TOKEN}" \ - -d "{ - \"user_id\": ${USER_ID}, - \"event_name\": \"button_click\", - \"properties\": { - \"button_name\": \"submit\", - \"button_position\": \"bottom\" - } - }" -``` - -## 10. 使用 JSON 文件 - -创建 `event.json` 文件: -```json -{ - "user_id": 12345, - "event_name": "custom_event", - "properties": { - "action": "click", - "element": "button", - "value": "submit" - }, - "device_info": { - "user_agent": "Mozilla/5.0", - "screen_width": 1920, - "screen_height": 1080 - }, - "meta_data": { - "task_id": "task_123" - } -} -``` - -然后执行: -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d @event.json -``` - -## 11. 批量埋点接口 - -```bash -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track/batch \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer YOUR_JWT_TOKEN" \ - -d '{ - "events": [ - { - "user_id": 12345, - "event_name": "page_view", - "properties": { - "page_name": "home" - } - }, - { - "user_id": 12345, - "event_name": "button_click", - "properties": { - "button_name": "start" - } - } - ] - }' -``` - -## 响应示例 - -### 成功响应 -```json -{ - "code": 200, - "message": "success", - "data": null -} -``` - -### 错误响应 -```json -{ - "code": 400, - "message": "invalid request", - "data": null -} -``` - -## 注意事项 - -1. **user_id** 和 **event_name** 是必填字段 -2. **properties**、**device_info**、**meta_data** 都是可选字段,类型为 JSON 对象 -3. 如果提供了 Authorization header,token 中的 user_id 会被设置到上下文中,但请求体中的 user_id 仍然需要提供 -4. 建议在生产环境中始终使用 HTTPS -5. 批量接口最多支持 100 个事件 - -## 测试命令(本地开发) - -```bash -# 最简单的测试 -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{"user_id": 1, "event_name": "test_event"}' - -# 查看响应详情 -curl -X POST http://localhost:8080/doc_ai/v1/analytics/track \ - -H "Content-Type: application/json" \ - -d '{"user_id": 1, "event_name": "test_event"}' \ - -v -``` diff --git a/frontend-sdk/analytics.ts b/frontend-sdk/analytics.ts deleted file mode 100644 index 187d080..0000000 --- a/frontend-sdk/analytics.ts +++ /dev/null @@ -1,308 +0,0 @@ -// Analytics SDK for Frontend -// 前端数据埋点 SDK - -interface EventProperties { - [key: string]: any; -} - -interface DeviceInfo { - user_agent?: string; - screen_width?: number; - screen_height?: number; - language?: string; - timezone?: string; - platform?: string; -} - -interface MetaData { - task_id?: string | number; - [key: string]: any; -} - -interface TrackEventParams { - event_name: string; - properties?: EventProperties; - device_info?: DeviceInfo; - meta_data?: MetaData; -} - -interface AnalyticsConfig { - apiUrl: string; - token?: string; - userId?: number | string; - enableAutoTrack?: boolean; - debug?: boolean; -} - -class Analytics { - private config: AnalyticsConfig; - private userId: number | string | null = null; - private eventQueue: TrackEventParams[] = []; - private isSending: boolean = false; - - constructor(config: AnalyticsConfig) { - this.config = { - enableAutoTrack: true, - debug: false, - ...config, - }; - - if (this.config.userId) { - this.userId = this.config.userId; - } - - // 自动收集设备信息 - if (this.config.enableAutoTrack) { - this.initAutoTrack(); - } - } - - /** - * 设置用户ID - */ - setUserId(userId: number | string) { - this.userId = userId; - } - - /** - * 获取设备信息 - */ - private getDeviceInfo(): DeviceInfo { - return { - user_agent: navigator.userAgent, - screen_width: window.screen.width, - screen_height: window.screen.height, - language: navigator.language, - timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, - platform: navigator.platform, - }; - } - - /** - * 记录单个事件 - */ - async track(params: TrackEventParams): Promise { - if (!this.userId) { - console.warn('Analytics: userId not set, event will not be tracked'); - return; - } - - const eventData = { - user_id: this.userId, - event_name: params.event_name, - properties: params.properties || {}, - device_info: { - ...this.getDeviceInfo(), - ...params.device_info, - }, - meta_data: { - timestamp: Date.now(), - ...params.meta_data, - }, - }; - - if (this.config.debug) { - console.log('Analytics Track:', eventData); - } - - try { - const response = await fetch(`${this.config.apiUrl}/track`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(this.config.token && { Authorization: `Bearer ${this.config.token}` }), - }, - body: JSON.stringify(eventData), - }); - - if (!response.ok) { - throw new Error(`Failed to track event: ${response.statusText}`); - } - - if (this.config.debug) { - console.log('Analytics: Event tracked successfully'); - } - } catch (error) { - console.error('Analytics: Failed to track event', error); - // 失败时加入队列,稍后重试 - this.eventQueue.push(params); - } - } - - /** - * 批量记录事件 - */ - async trackBatch(events: TrackEventParams[]): Promise { - if (!this.userId) { - console.warn('Analytics: userId not set, events will not be tracked'); - return; - } - - const batchData = { - events: events.map((params) => ({ - user_id: this.userId, - event_name: params.event_name, - properties: params.properties || {}, - device_info: { - ...this.getDeviceInfo(), - ...params.device_info, - }, - meta_data: { - timestamp: Date.now(), - ...params.meta_data, - }, - })), - }; - - if (this.config.debug) { - console.log('Analytics Track Batch:', batchData); - } - - try { - const response = await fetch(`${this.config.apiUrl}/track/batch`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(this.config.token && { Authorization: `Bearer ${this.config.token}` }), - }, - body: JSON.stringify(batchData), - }); - - if (!response.ok) { - throw new Error(`Failed to track batch events: ${response.statusText}`); - } - - if (this.config.debug) { - console.log('Analytics: Batch events tracked successfully'); - } - } catch (error) { - console.error('Analytics: Failed to track batch events', error); - } - } - - /** - * 页面浏览事件 - */ - trackPageView(pageName?: string) { - this.track({ - event_name: 'page_view', - properties: { - page_url: window.location.href, - page_title: document.title, - page_name: pageName || document.title, - referrer: document.referrer, - }, - }); - } - - /** - * 点击事件 - */ - trackClick(elementName: string, properties?: EventProperties) { - this.track({ - event_name: 'click', - properties: { - element_name: elementName, - page_url: window.location.href, - ...properties, - }, - }); - } - - /** - * 表单提交事件 - */ - trackFormSubmit(formName: string, properties?: EventProperties) { - this.track({ - event_name: 'form_submit', - properties: { - form_name: formName, - page_url: window.location.href, - ...properties, - }, - }); - } - - /** - * 任务相关事件 - */ - trackTask(taskId: string | number, action: string, properties?: EventProperties) { - this.track({ - event_name: `task_${action}`, - properties: { - action, - ...properties, - }, - meta_data: { - task_id: taskId, - }, - }); - } - - /** - * 初始化自动埋点 - */ - private initAutoTrack() { - // 页面加载完成时记录 - if (document.readyState === 'complete') { - this.trackPageView(); - } else { - window.addEventListener('load', () => this.trackPageView()); - } - - // 页面离开前发送队列中的事件 - window.addEventListener('beforeunload', () => { - if (this.eventQueue.length > 0) { - this.flushQueue(); - } - }); - - // 页面可见性变化 - document.addEventListener('visibilitychange', () => { - if (document.hidden && this.eventQueue.length > 0) { - this.flushQueue(); - } - }); - } - - /** - * 刷新队列中的事件 - */ - private flushQueue() { - if (this.isSending || this.eventQueue.length === 0) { - return; - } - - this.isSending = true; - const eventsToSend = [...this.eventQueue]; - this.eventQueue = []; - - this.trackBatch(eventsToSend).finally(() => { - this.isSending = false; - }); - } - - /** - * 手动刷新队列 - */ - flush() { - this.flushQueue(); - } -} - -// 导出单例实例 -let analyticsInstance: Analytics | null = null; - -export function initAnalytics(config: AnalyticsConfig): Analytics { - analyticsInstance = new Analytics(config); - return analyticsInstance; -} - -export function getAnalytics(): Analytics { - if (!analyticsInstance) { - throw new Error('Analytics not initialized. Call initAnalytics first.'); - } - return analyticsInstance; -} - -export default Analytics; diff --git a/frontend-sdk/usage-examples.ts b/frontend-sdk/usage-examples.ts deleted file mode 100644 index cc67b0c..0000000 --- a/frontend-sdk/usage-examples.ts +++ /dev/null @@ -1,217 +0,0 @@ -// Analytics SDK 使用示例 - -import { initAnalytics, getAnalytics } from './analytics'; - -// 1. 初始化 SDK -const analytics = initAnalytics({ - apiUrl: 'https://your-api-domain.com/doc_ai/v1/analytics', - token: 'your-auth-token', // 从登录后获取 - userId: 12345, // 用户ID - enableAutoTrack: true, // 启用自动埋点(页面浏览等) - debug: true, // 开发环境下启用调试 -}); - -// 2. 设置用户ID(登录后) -analytics.setUserId(12345); - -// 3. 记录页面浏览 -analytics.trackPageView('首页'); - -// 4. 记录点击事件 -const handleButtonClick = () => { - analytics.trackClick('提交按钮', { - button_text: '提交', - button_position: 'bottom', - }); -}; - -// 5. 记录表单提交 -const handleFormSubmit = (formData: any) => { - analytics.trackFormSubmit('用户注册表单', { - form_fields: Object.keys(formData), - success: true, - }); -}; - -// 6. 记录任务相关事件 -const handleTaskCreate = (taskId: string) => { - analytics.trackTask(taskId, 'create', { - task_type: 'formula_recognition', - file_type: 'image/png', - }); -}; - -const handleTaskComplete = (taskId: string) => { - analytics.trackTask(taskId, 'complete', { - duration_seconds: 5.2, - success: true, - }); -}; - -// 7. 记录自定义事件 -const handleFileUpload = (file: File) => { - analytics.track({ - event_name: 'file_upload', - properties: { - file_name: file.name, - file_size: file.size, - file_type: file.type, - }, - meta_data: { - upload_source: 'drag_drop', - }, - }); -}; - -// 8. 批量记录事件 -const handleBatchActions = () => { - analytics.trackBatch([ - { - event_name: 'button_click', - properties: { button_name: 'save' }, - }, - { - event_name: 'data_export', - properties: { format: 'pdf' }, - }, - ]); -}; - -// 9. React 组件中使用 -import React, { useEffect } from 'react'; - -function HomePage() { - useEffect(() => { - // 页面加载时记录 - getAnalytics().trackPageView('首页'); - }, []); - - const handleClick = () => { - getAnalytics().trackClick('首页-开始按钮'); - }; - - return ( -
-

首页

- -
- ); -} - -// 10. Vue 组件中使用 -export default { - name: 'HomePage', - mounted() { - getAnalytics().trackPageView('首页'); - }, - methods: { - handleClick() { - getAnalytics().trackClick('首页-开始按钮'); - }, - }, -}; - -// 11. 记录用户行为流程 -class FormulaRecognitionFlow { - private analytics = getAnalytics(); - private taskId: string | null = null; - - // 开始识别流程 - startRecognition(file: File) { - this.analytics.track({ - event_name: 'formula_recognition_start', - properties: { - file_name: file.name, - file_size: file.size, - }, - }); - } - - // 上传成功 - uploadSuccess(taskId: string) { - this.taskId = taskId; - this.analytics.trackTask(taskId, 'upload_success', { - step: 'upload', - }); - } - - // 识别进行中 - recognitionProcessing() { - if (this.taskId) { - this.analytics.trackTask(this.taskId, 'processing', { - step: 'recognition', - }); - } - } - - // 识别完成 - recognitionComplete(result: any) { - if (this.taskId) { - this.analytics.trackTask(this.taskId, 'complete', { - step: 'complete', - has_result: !!result, - }); - } - } - - // 识别失败 - recognitionFailed(error: string) { - if (this.taskId) { - this.analytics.trackTask(this.taskId, 'failed', { - step: 'error', - error_message: error, - }); - } - } - - // 查看结果 - viewResult() { - if (this.taskId) { - this.analytics.trackTask(this.taskId, 'view_result', { - step: 'view', - }); - } - } - - // 导出结果 - exportResult(format: string) { - if (this.taskId) { - this.analytics.trackTask(this.taskId, 'export', { - step: 'export', - export_format: format, - }); - } - } -} - -// 12. 错误追踪 -window.addEventListener('error', (event) => { - getAnalytics().track({ - event_name: 'javascript_error', - properties: { - error_message: event.message, - error_filename: event.filename, - error_line: event.lineno, - error_column: event.colno, - }, - }); -}); - -// 13. 性能追踪 -window.addEventListener('load', () => { - const perfData = performance.timing; - const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart; - - getAnalytics().track({ - event_name: 'page_performance', - properties: { - page_load_time: pageLoadTime, - dns_time: perfData.domainLookupEnd - perfData.domainLookupStart, - tcp_time: perfData.connectEnd - perfData.connectStart, - request_time: perfData.responseEnd - perfData.requestStart, - dom_parse_time: perfData.domComplete - perfData.domLoading, - }, - }); -}); - -export {};