diff --git a/api/router.go b/api/router.go index 476d38d..41a8c7f 100644 --- a/api/router.go +++ b/api/router.go @@ -1,11 +1,12 @@ package api import ( - "gitea.com/bitwsd/document_ai/api/v1/formula" - "gitea.com/bitwsd/document_ai/api/v1/oss" - "gitea.com/bitwsd/document_ai/api/v1/task" - "gitea.com/bitwsd/document_ai/api/v1/user" - "gitea.com/bitwsd/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/api/v1/analytics" + "gitea.com/texpixel/document_ai/api/v1/formula" + "gitea.com/texpixel/document_ai/api/v1/oss" + "gitea.com/texpixel/document_ai/api/v1/task" + "gitea.com/texpixel/document_ai/api/v1/user" + "gitea.com/texpixel/document_ai/pkg/common" "github.com/gin-gonic/gin" ) @@ -47,6 +48,13 @@ func SetupRouter(engine *gin.RouterGroup) { userRouter.GET("/info", common.MustAuthMiddleware(), userEndpoint.GetUserInfo) } } + + // 数据埋点路由 + analyticsRouter := v1.Group("/analytics", common.GetAuthMiddleware()) + { + analyticsHandler := analytics.NewAnalyticsHandler() + analyticsRouter.POST("/track", analyticsHandler.TrackEvent) + } } } diff --git a/api/v1/analytics/handler.go b/api/v1/analytics/handler.go new file mode 100644 index 0000000..e767f6d --- /dev/null +++ b/api/v1/analytics/handler.go @@ -0,0 +1,47 @@ +package analytics + +import ( + "net/http" + + "gitea.com/texpixel/document_ai/internal/model/analytics" + "gitea.com/texpixel/document_ai/internal/service" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/log" + "github.com/gin-gonic/gin" +) + +type AnalyticsHandler struct { + analyticsService *service.AnalyticsService +} + +func NewAnalyticsHandler() *AnalyticsHandler { + return &AnalyticsHandler{ + analyticsService: service.NewAnalyticsService(), + } +} + +// TrackEvent 记录单个事件 +// @Summary 记录单个埋点事件 +// @Description 记录用户行为埋点事件 +// @Tags Analytics +// @Accept json +// @Produce json +// @Param request body analytics.TrackEventRequest true "事件信息" +// @Success 200 {object} common.Response +// @Router /api/v1/analytics/track [post] +func (h *AnalyticsHandler) TrackEvent(c *gin.Context) { + var req analytics.TrackEventRequest + if err := c.ShouldBindJSON(&req); err != nil { + log.Error(c.Request.Context(), "bind request failed", "error", err) + c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeParamError, "invalid request")) + return + } + + if err := h.analyticsService.TrackEvent(c.Request.Context(), &req); err != nil { + log.Error(c.Request.Context(), "track event failed", "error", err) + c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeSystemError, "failed to track event")) + return + } + + c.JSON(http.StatusOK, common.SuccessResponse(c, "success")) +} diff --git a/api/v1/formula/handler.go b/api/v1/formula/handler.go index e4a6687..02605c2 100644 --- a/api/v1/formula/handler.go +++ b/api/v1/formula/handler.go @@ -5,12 +5,12 @@ import ( "path/filepath" "strings" - "gitea.com/bitwsd/document_ai/internal/model/formula" - "gitea.com/bitwsd/document_ai/internal/service" - "gitea.com/bitwsd/document_ai/internal/storage/dao" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/constant" - "gitea.com/bitwsd/document_ai/pkg/utils" + "gitea.com/texpixel/document_ai/internal/model/formula" + "gitea.com/texpixel/document_ai/internal/service" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/constant" + "gitea.com/texpixel/document_ai/pkg/utils" "github.com/gin-gonic/gin" ) diff --git a/api/v1/oss/handler.go b/api/v1/oss/handler.go index 8ba31f2..a36e756 100644 --- a/api/v1/oss/handler.go +++ b/api/v1/oss/handler.go @@ -8,11 +8,11 @@ import ( "strings" "time" - "gitea.com/bitwsd/document_ai/config" - "gitea.com/bitwsd/document_ai/internal/storage/dao" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/oss" - "gitea.com/bitwsd/document_ai/pkg/utils" + "gitea.com/texpixel/document_ai/config" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/oss" + "gitea.com/texpixel/document_ai/pkg/utils" "github.com/gin-gonic/gin" "gorm.io/gorm" ) diff --git a/api/v1/task/handler.go b/api/v1/task/handler.go index 62aaaa4..820d0b0 100644 --- a/api/v1/task/handler.go +++ b/api/v1/task/handler.go @@ -3,10 +3,10 @@ package task import ( "net/http" - "gitea.com/bitwsd/document_ai/internal/model/task" - "gitea.com/bitwsd/document_ai/internal/service" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/internal/model/task" + "gitea.com/texpixel/document_ai/internal/service" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/log" "github.com/gin-gonic/gin" ) diff --git a/api/v1/user/handler.go b/api/v1/user/handler.go index c6df12f..5e98e21 100644 --- a/api/v1/user/handler.go +++ b/api/v1/user/handler.go @@ -3,13 +3,13 @@ package user import ( "net/http" - "gitea.com/bitwsd/document_ai/config" - model "gitea.com/bitwsd/document_ai/internal/model/user" - "gitea.com/bitwsd/document_ai/internal/service" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/constant" - "gitea.com/bitwsd/document_ai/pkg/jwt" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/config" + model "gitea.com/texpixel/document_ai/internal/model/user" + "gitea.com/texpixel/document_ai/internal/service" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/constant" + "gitea.com/texpixel/document_ai/pkg/jwt" + "gitea.com/texpixel/document_ai/pkg/log" "github.com/gin-gonic/gin" ) diff --git a/cmd/migrate/main.go b/cmd/migrate/main.go index 77838c6..d90843f 100644 --- a/cmd/migrate/main.go +++ b/cmd/migrate/main.go @@ -7,8 +7,8 @@ import ( "log" "time" - "gitea.com/bitwsd/document_ai/config" - "gitea.com/bitwsd/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/config" + "gitea.com/texpixel/document_ai/internal/storage/dao" "github.com/spf13/viper" "gorm.io/driver/mysql" "gorm.io/gorm" @@ -69,26 +69,26 @@ func migrateData(testDB, prodDB *gorm.DB) error { // 从测试数据库读取所有任务数据(包含结果) type TaskWithResult struct { // Task 字段 - TaskID int64 `gorm:"column:id"` - UserID int64 `gorm:"column:user_id"` - TaskUUID string `gorm:"column:task_uuid"` - FileName string `gorm:"column:file_name"` - FileHash string `gorm:"column:file_hash"` - FileURL string `gorm:"column:file_url"` - TaskType string `gorm:"column:task_type"` - Status int `gorm:"column:status"` - CompletedAt time.Time `gorm:"column:completed_at"` - Remark string `gorm:"column:remark"` - IP string `gorm:"column:ip"` + TaskID int64 `gorm:"column:id"` + UserID int64 `gorm:"column:user_id"` + TaskUUID string `gorm:"column:task_uuid"` + FileName string `gorm:"column:file_name"` + FileHash string `gorm:"column:file_hash"` + FileURL string `gorm:"column:file_url"` + TaskType string `gorm:"column:task_type"` + Status int `gorm:"column:status"` + CompletedAt time.Time `gorm:"column:completed_at"` + Remark string `gorm:"column:remark"` + IP string `gorm:"column:ip"` TaskCreatedAt time.Time `gorm:"column:created_at"` TaskUpdatedAt time.Time `gorm:"column:updated_at"` // Result 字段 - ResultID *int64 `gorm:"column:result_id"` - ResultTaskID *int64 `gorm:"column:result_task_id"` - ResultTaskType *string `gorm:"column:result_task_type"` - Latex *string `gorm:"column:latex"` - Markdown *string `gorm:"column:markdown"` - MathML *string `gorm:"column:mathml"` + ResultID *int64 `gorm:"column:result_id"` + ResultTaskID *int64 `gorm:"column:result_task_id"` + ResultTaskType *string `gorm:"column:result_task_type"` + Latex *string `gorm:"column:latex"` + Markdown *string `gorm:"column:markdown"` + MathML *string `gorm:"column:mathml"` ResultCreatedAt *time.Time `gorm:"column:result_created_at"` ResultUpdatedAt *time.Time `gorm:"column:result_updated_at"` } diff --git a/config/config.go b/config/config.go index fe61de6..34fcdb8 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,7 @@ package config import ( - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/log" "github.com/spf13/viper" ) diff --git a/frontend-sdk/analytics.ts b/frontend-sdk/analytics.ts new file mode 100644 index 0000000..187d080 --- /dev/null +++ b/frontend-sdk/analytics.ts @@ -0,0 +1,308 @@ +// 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 new file mode 100644 index 0000000..cc67b0c --- /dev/null +++ b/frontend-sdk/usage-examples.ts @@ -0,0 +1,217 @@ +// 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 {}; diff --git a/go.mod b/go.mod index 6fb2350..af65a6c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ -module gitea.com/bitwsd/document_ai +module gitea.com/texpixel/document_ai -go 1.20 +go 1.21 require ( github.com/alibabacloud-go/darabonba-openapi v0.2.1 @@ -17,13 +17,15 @@ require ( github.com/spf13/viper v1.19.0 golang.org/x/crypto v0.23.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 + gorm.io/datatypes v1.2.7 gorm.io/driver/mysql v1.5.7 - gorm.io/gorm v1.25.12 + gorm.io/gorm v1.30.0 ) -require github.com/go-sql-driver/mysql v1.7.0 // indirect +require github.com/go-sql-driver/mysql v1.8.1 // indirect require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect @@ -74,7 +76,7 @@ require ( golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 57e0b0b..27d6d2e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc= @@ -31,7 +33,9 @@ github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9 github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= @@ -49,11 +53,13 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -63,18 +69,25 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -83,6 +96,14 @@ github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -97,9 +118,11 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -110,6 +133,10 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -126,9 +153,11 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -199,6 +228,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -211,8 +242,8 @@ golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -237,10 +268,18 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk= +gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= +gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc= +gorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOzehntWw= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/model/analytics/request.go b/internal/model/analytics/request.go new file mode 100644 index 0000000..89f87e9 --- /dev/null +++ b/internal/model/analytics/request.go @@ -0,0 +1,33 @@ +package analytics + +import "time" + +// TrackEventRequest 埋点事件请求 +type TrackEventRequest struct { + UserID int64 `json:"user_id" binding:"required"` + EventName string `json:"event_name" binding:"required"` + Properties map[string]interface{} `json:"properties"` + DeviceInfo map[string]interface{} `json:"device_info"` + MetaData map[string]interface{} `json:"meta_data"` +} + +// BatchTrackEventRequest 批量埋点事件请求 +type BatchTrackEventRequest struct { + Events []TrackEventRequest `json:"events" binding:"required,min=1,max=100"` +} + +// QueryEventsRequest 查询事件请求 +type QueryEventsRequest struct { + UserID *int64 `json:"user_id" form:"user_id"` + EventName string `json:"event_name" form:"event_name"` + StartTime *time.Time `json:"start_time" form:"start_time"` + EndTime *time.Time `json:"end_time" form:"end_time"` + Page int `json:"page" form:"page" binding:"required,min=1"` + PageSize int `json:"page_size" form:"page_size" binding:"required,min=1,max=100"` +} + +// EventStatsRequest 事件统计请求 +type EventStatsRequest struct { + StartTime time.Time `json:"start_time" form:"start_time" binding:"required"` + EndTime time.Time `json:"end_time" form:"end_time" binding:"required"` +} diff --git a/internal/model/analytics/response.go b/internal/model/analytics/response.go new file mode 100644 index 0000000..eb2b72e --- /dev/null +++ b/internal/model/analytics/response.go @@ -0,0 +1,36 @@ +package analytics + +import "time" + +// EventResponse 事件响应 +type EventResponse struct { + ID int64 `json:"id"` + UserID int64 `json:"user_id"` + EventName string `json:"event_name"` + Properties map[string]interface{} `json:"properties"` + DeviceInfo map[string]interface{} `json:"device_info"` + MetaData map[string]interface{} `json:"meta_data"` + CreatedAt time.Time `json:"created_at"` +} + +// EventListResponse 事件列表响应 +type EventListResponse struct { + Events []*EventResponse `json:"events"` + Total int64 `json:"total"` + Page int `json:"page"` + Size int `json:"size"` +} + +// EventStatsResponse 事件统计响应 +type EventStatsResponse struct { + EventName string `json:"event_name"` + Count int64 `json:"count"` + UniqueUsers int64 `json:"unique_users"` +} + +// EventStatsListResponse 事件统计列表响应 +type EventStatsListResponse struct { + Stats []*EventStatsResponse `json:"stats"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time"` +} diff --git a/internal/service/analytics_service.go b/internal/service/analytics_service.go new file mode 100644 index 0000000..f061fe7 --- /dev/null +++ b/internal/service/analytics_service.go @@ -0,0 +1,234 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "gitea.com/texpixel/document_ai/internal/model/analytics" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/log" + "gorm.io/datatypes" + "gorm.io/gorm" +) + +type AnalyticsService struct { + db *gorm.DB + eventDao *dao.AnalyticsEventDao +} + +func NewAnalyticsService() *AnalyticsService { + return &AnalyticsService{ + eventDao: dao.NewAnalyticsEventDao(), + } +} + +// TrackEvent 记录单个事件 +func (s *AnalyticsService) TrackEvent(ctx context.Context, req *analytics.TrackEventRequest) error { + // 将 map 转换为 JSON + propertiesJSON, err := json.Marshal(req.Properties) + if err != nil { + log.Error(ctx, "marshal properties failed", "error", err) + return fmt.Errorf("invalid properties format") + } + + deviceInfoJSON, err := json.Marshal(req.DeviceInfo) + if err != nil { + log.Error(ctx, "marshal device_info failed", "error", err) + return fmt.Errorf("invalid device_info format") + } + + metaDataJSON, err := json.Marshal(req.MetaData) + if err != nil { + log.Error(ctx, "marshal meta_data failed", "error", err) + return fmt.Errorf("invalid meta_data format") + } + + event := &dao.AnalyticsEvent{ + UserID: req.UserID, + EventName: req.EventName, + Properties: datatypes.JSON(propertiesJSON), + DeviceInfo: datatypes.JSON(deviceInfoJSON), + MetaData: datatypes.JSON(metaDataJSON), + CreatedAt: time.Now(), + } + + if err := s.eventDao.Create(s.db, event); err != nil { + log.Error(ctx, "create analytics event failed", "error", err) + return fmt.Errorf("failed to track event") + } + + log.Info(ctx, "event tracked successfully", + "event_id", event.ID, + "user_id", req.UserID, + "event_name", req.EventName) + + return nil +} + +// BatchTrackEvents 批量记录事件 +func (s *AnalyticsService) BatchTrackEvents(ctx context.Context, req *analytics.BatchTrackEventRequest) error { + events := make([]*dao.AnalyticsEvent, 0, len(req.Events)) + + for _, eventReq := range req.Events { + propertiesJSON, err := json.Marshal(eventReq.Properties) + if err != nil { + log.Error(ctx, "marshal properties failed", "error", err) + continue + } + + deviceInfoJSON, err := json.Marshal(eventReq.DeviceInfo) + if err != nil { + log.Error(ctx, "marshal device_info failed", "error", err) + continue + } + + metaDataJSON, err := json.Marshal(eventReq.MetaData) + if err != nil { + log.Error(ctx, "marshal meta_data failed", "error", err) + continue + } + + event := &dao.AnalyticsEvent{ + UserID: eventReq.UserID, + EventName: eventReq.EventName, + Properties: datatypes.JSON(propertiesJSON), + DeviceInfo: datatypes.JSON(deviceInfoJSON), + MetaData: datatypes.JSON(metaDataJSON), + CreatedAt: time.Now(), + } + events = append(events, event) + } + + if len(events) == 0 { + return fmt.Errorf("no valid events to track") + } + + if err := s.eventDao.BatchCreate(s.db, events); err != nil { + log.Error(ctx, "batch create analytics events failed", "error", err) + return fmt.Errorf("failed to batch track events") + } + + log.Info(ctx, "batch events tracked successfully", "count", len(events)) + return nil +} + +// QueryEvents 查询事件 +func (s *AnalyticsService) QueryEvents(ctx context.Context, req *analytics.QueryEventsRequest) (*analytics.EventListResponse, error) { + var events []*dao.AnalyticsEvent + var total int64 + var err error + + // 根据不同条件查询 + if req.UserID != nil && req.EventName != "" { + // 查询用户的指定事件 + events, total, err = s.eventDao.GetUserEventsByName(s.db, *req.UserID, req.EventName, req.Page, req.PageSize) + } else if req.UserID != nil { + // 查询用户的所有事件 + events, total, err = s.eventDao.GetUserEvents(s.db, *req.UserID, req.Page, req.PageSize) + } else if req.EventName != "" { + // 查询指定事件 + events, total, err = s.eventDao.GetEventsByName(s.db, req.EventName, req.Page, req.PageSize) + } else if req.StartTime != nil && req.EndTime != nil { + // 查询时间范围内的事件 + events, total, err = s.eventDao.GetEventsByTimeRange(s.db, *req.StartTime, *req.EndTime, req.Page, req.PageSize) + } else { + return nil, fmt.Errorf("invalid query parameters") + } + + if err != nil { + log.Error(ctx, "query events failed", "error", err) + return nil, fmt.Errorf("failed to query events") + } + + // 转换为响应格式 + eventResponses := make([]*analytics.EventResponse, 0, len(events)) + for _, event := range events { + var properties, deviceInfo, metaData map[string]interface{} + + if len(event.Properties) > 0 { + json.Unmarshal(event.Properties, &properties) + } + if len(event.DeviceInfo) > 0 { + json.Unmarshal(event.DeviceInfo, &deviceInfo) + } + if len(event.MetaData) > 0 { + json.Unmarshal(event.MetaData, &metaData) + } + + eventResponses = append(eventResponses, &analytics.EventResponse{ + ID: event.ID, + UserID: event.UserID, + EventName: event.EventName, + Properties: properties, + DeviceInfo: deviceInfo, + MetaData: metaData, + CreatedAt: event.CreatedAt, + }) + } + + return &analytics.EventListResponse{ + Events: eventResponses, + Total: total, + Page: req.Page, + Size: req.PageSize, + }, nil +} + +// GetEventStats 获取事件统计 +func (s *AnalyticsService) GetEventStats(ctx context.Context, req *analytics.EventStatsRequest) (*analytics.EventStatsListResponse, error) { + results, err := s.eventDao.GetEventStats(s.db, req.StartTime, req.EndTime) + if err != nil { + log.Error(ctx, "get event stats failed", "error", err) + return nil, fmt.Errorf("failed to get event stats") + } + + stats := make([]*analytics.EventStatsResponse, 0, len(results)) + for _, result := range results { + stats = append(stats, &analytics.EventStatsResponse{ + EventName: result["event_name"].(string), + Count: result["count"].(int64), + UniqueUsers: result["unique_users"].(int64), + }) + } + + return &analytics.EventStatsListResponse{ + Stats: stats, + StartTime: req.StartTime, + EndTime: req.EndTime, + }, nil +} + +// CountUserEvents 统计用户事件数量 +func (s *AnalyticsService) CountUserEvents(ctx context.Context, userID int64) (int64, error) { + count, err := s.eventDao.CountUserEvents(s.db, userID) + if err != nil { + log.Error(ctx, "count user events failed", "error", err, "user_id", userID) + return 0, fmt.Errorf("failed to count user events") + } + return count, nil +} + +// CountEventsByName 统计指定事件的数量 +func (s *AnalyticsService) CountEventsByName(ctx context.Context, eventName string) (int64, error) { + count, err := s.eventDao.CountEventsByName(s.db, eventName) + if err != nil { + log.Error(ctx, "count events by name failed", "error", err, "event_name", eventName) + return 0, fmt.Errorf("failed to count events") + } + return count, nil +} + +// CleanOldEvents 清理旧数据(可以定时执行) +func (s *AnalyticsService) CleanOldEvents(ctx context.Context, retentionDays int) error { + beforeTime := time.Now().AddDate(0, 0, -retentionDays) + + if err := s.eventDao.DeleteOldEvents(s.db, beforeTime); err != nil { + log.Error(ctx, "clean old events failed", "error", err, "before_time", beforeTime) + return fmt.Errorf("failed to clean old events") + } + + log.Info(ctx, "old events cleaned successfully", "retention_days", retentionDays) + return nil +} diff --git a/internal/service/recognition_service.go b/internal/service/recognition_service.go index be755be..0e44015 100644 --- a/internal/service/recognition_service.go +++ b/internal/service/recognition_service.go @@ -12,18 +12,18 @@ import ( "strings" "time" - "gitea.com/bitwsd/document_ai/config" - "gitea.com/bitwsd/document_ai/internal/model/formula" - "gitea.com/bitwsd/document_ai/internal/storage/cache" - "gitea.com/bitwsd/document_ai/internal/storage/dao" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/config" + "gitea.com/texpixel/document_ai/internal/model/formula" + "gitea.com/texpixel/document_ai/internal/storage/cache" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/log" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/constant" - "gitea.com/bitwsd/document_ai/pkg/httpclient" - "gitea.com/bitwsd/document_ai/pkg/oss" - "gitea.com/bitwsd/document_ai/pkg/requestid" - "gitea.com/bitwsd/document_ai/pkg/utils" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/constant" + "gitea.com/texpixel/document_ai/pkg/httpclient" + "gitea.com/texpixel/document_ai/pkg/oss" + "gitea.com/texpixel/document_ai/pkg/requestid" + "gitea.com/texpixel/document_ai/pkg/utils" "gorm.io/gorm" ) diff --git a/internal/service/task.go b/internal/service/task.go index 04c08e7..8d5e8c2 100644 --- a/internal/service/task.go +++ b/internal/service/task.go @@ -10,10 +10,10 @@ import ( "net/http" "strings" - "gitea.com/bitwsd/document_ai/internal/model/task" - "gitea.com/bitwsd/document_ai/internal/storage/dao" - "gitea.com/bitwsd/document_ai/pkg/log" - "gitea.com/bitwsd/document_ai/pkg/oss" + "gitea.com/texpixel/document_ai/internal/model/task" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/oss" ) type TaskService struct { diff --git a/internal/service/user_service.go b/internal/service/user_service.go index a40e8a4..4627c2e 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -6,11 +6,11 @@ import ( "fmt" "math/rand" - "gitea.com/bitwsd/document_ai/internal/storage/cache" - "gitea.com/bitwsd/document_ai/internal/storage/dao" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/log" - "gitea.com/bitwsd/document_ai/pkg/sms" + "gitea.com/texpixel/document_ai/internal/storage/cache" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/sms" "golang.org/x/crypto/bcrypt" ) diff --git a/internal/storage/cache/engine.go b/internal/storage/cache/engine.go index a682609..66aecd5 100644 --- a/internal/storage/cache/engine.go +++ b/internal/storage/cache/engine.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "gitea.com/bitwsd/document_ai/config" + "gitea.com/texpixel/document_ai/config" "github.com/redis/go-redis/v9" ) diff --git a/internal/storage/dao/analytics_event.go b/internal/storage/dao/analytics_event.go new file mode 100644 index 0000000..f4db890 --- /dev/null +++ b/internal/storage/dao/analytics_event.go @@ -0,0 +1,170 @@ +package dao + +import ( + "time" + + "gorm.io/datatypes" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +// AnalyticsEvent 数据埋点事件表 +type AnalyticsEvent struct { + ID int64 `gorm:"bigint;primaryKey;autoIncrement;column:id;comment:主键ID" json:"id"` + UserID int64 `gorm:"column:user_id;not null;index:idx_user_id;comment:用户ID" json:"user_id"` + EventName string `gorm:"column:event_name;varchar(128);not null;index:idx_event_name;comment:事件名称" json:"event_name"` + Properties datatypes.JSON `gorm:"column:properties;type:json;comment:事件属性(JSON)" json:"properties"` + DeviceInfo datatypes.JSON `gorm:"column:device_info;type:json;comment:设备信息(JSON)" json:"device_info"` + MetaData datatypes.JSON `gorm:"column:meta_data;type:json;comment:元数据(JSON,包含task_id等)" json:"meta_data"` + CreatedAt time.Time `gorm:"column:created_at;comment:创建时间;not null;default:current_timestamp;index:idx_created_at" json:"created_at"` +} + +func (e *AnalyticsEvent) TableName() string { + return "analytics_events" +} + +// AnalyticsEventDao 数据埋点事件DAO +type AnalyticsEventDao struct{} + +func NewAnalyticsEventDao() *AnalyticsEventDao { + return &AnalyticsEventDao{} +} + +// Create 创建事件记录 +func (dao *AnalyticsEventDao) Create(tx *gorm.DB, event *AnalyticsEvent) error { + return tx.Create(event).Error +} + +// BatchCreate 批量创建事件记录 +func (dao *AnalyticsEventDao) BatchCreate(tx *gorm.DB, events []*AnalyticsEvent) error { + if len(events) == 0 { + return nil + } + return tx.CreateInBatches(events, 100).Error +} + +// GetByID 根据ID获取事件 +func (dao *AnalyticsEventDao) GetByID(tx *gorm.DB, id int64) (*AnalyticsEvent, error) { + event := &AnalyticsEvent{} + err := tx.Where("id = ?", id).First(event).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, err + } + return event, nil +} + +// GetUserEvents 获取用户的事件列表 +func (dao *AnalyticsEventDao) GetUserEvents(tx *gorm.DB, userID int64, page, pageSize int) ([]*AnalyticsEvent, int64, error) { + var events []*AnalyticsEvent + var total int64 + + offset := (page - 1) * pageSize + query := tx.Model(&AnalyticsEvent{}).Where("user_id = ?", userID) + + err := query.Count(&total).Error + if err != nil { + return nil, 0, err + } + + err = query.Offset(offset).Limit(pageSize). + Order(clause.OrderByColumn{Column: clause.Column{Name: "created_at"}, Desc: true}). + Find(&events).Error + + return events, total, err +} + +// GetEventsByName 根据事件名称获取事件列表 +func (dao *AnalyticsEventDao) GetEventsByName(tx *gorm.DB, eventName string, page, pageSize int) ([]*AnalyticsEvent, int64, error) { + var events []*AnalyticsEvent + var total int64 + + offset := (page - 1) * pageSize + query := tx.Model(&AnalyticsEvent{}).Where("event_name = ?", eventName) + + err := query.Count(&total).Error + if err != nil { + return nil, 0, err + } + + err = query.Offset(offset).Limit(pageSize). + Order(clause.OrderByColumn{Column: clause.Column{Name: "created_at"}, Desc: true}). + Find(&events).Error + + return events, total, err +} + +// GetUserEventsByName 获取用户指定事件的列表 +func (dao *AnalyticsEventDao) GetUserEventsByName(tx *gorm.DB, userID int64, eventName string, page, pageSize int) ([]*AnalyticsEvent, int64, error) { + var events []*AnalyticsEvent + var total int64 + + offset := (page - 1) * pageSize + query := tx.Model(&AnalyticsEvent{}).Where("user_id = ? AND event_name = ?", userID, eventName) + + err := query.Count(&total).Error + if err != nil { + return nil, 0, err + } + + err = query.Offset(offset).Limit(pageSize). + Order(clause.OrderByColumn{Column: clause.Column{Name: "created_at"}, Desc: true}). + Find(&events).Error + + return events, total, err +} + +// GetEventsByTimeRange 根据时间范围获取事件列表 +func (dao *AnalyticsEventDao) GetEventsByTimeRange(tx *gorm.DB, startTime, endTime time.Time, page, pageSize int) ([]*AnalyticsEvent, int64, error) { + var events []*AnalyticsEvent + var total int64 + + offset := (page - 1) * pageSize + query := tx.Model(&AnalyticsEvent{}).Where("created_at BETWEEN ? AND ?", startTime, endTime) + + err := query.Count(&total).Error + if err != nil { + return nil, 0, err + } + + err = query.Offset(offset).Limit(pageSize). + Order(clause.OrderByColumn{Column: clause.Column{Name: "created_at"}, Desc: true}). + Find(&events).Error + + return events, total, err +} + +// CountEventsByName 统计指定事件的数量 +func (dao *AnalyticsEventDao) CountEventsByName(tx *gorm.DB, eventName string) (int64, error) { + var count int64 + err := tx.Model(&AnalyticsEvent{}).Where("event_name = ?", eventName).Count(&count).Error + return count, err +} + +// CountUserEvents 统计用户的事件数量 +func (dao *AnalyticsEventDao) CountUserEvents(tx *gorm.DB, userID int64) (int64, error) { + var count int64 + err := tx.Model(&AnalyticsEvent{}).Where("user_id = ?", userID).Count(&count).Error + return count, err +} + +// GetEventStats 获取事件统计信息(按事件名称分组) +func (dao *AnalyticsEventDao) GetEventStats(tx *gorm.DB, startTime, endTime time.Time) ([]map[string]interface{}, error) { + var results []map[string]interface{} + + err := tx.Model(&AnalyticsEvent{}). + Select("event_name, COUNT(*) as count, COUNT(DISTINCT user_id) as unique_users"). + Where("created_at BETWEEN ? AND ?", startTime, endTime). + Group("event_name"). + Order("count DESC"). + Find(&results).Error + + return results, err +} + +// DeleteOldEvents 删除旧事件(数据清理) +func (dao *AnalyticsEventDao) DeleteOldEvents(tx *gorm.DB, beforeTime time.Time) error { + return tx.Where("created_at < ?", beforeTime).Delete(&AnalyticsEvent{}).Error +} diff --git a/internal/storage/dao/engine.go b/internal/storage/dao/engine.go index 9bd1fcd..bef7c6c 100644 --- a/internal/storage/dao/engine.go +++ b/internal/storage/dao/engine.go @@ -3,7 +3,7 @@ package dao import ( "fmt" - "gitea.com/bitwsd/document_ai/config" + "gitea.com/texpixel/document_ai/config" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" diff --git a/main.go b/main.go index 6a41973..d249d6b 100644 --- a/main.go +++ b/main.go @@ -10,15 +10,15 @@ import ( "syscall" "time" - "gitea.com/bitwsd/document_ai/api" - "gitea.com/bitwsd/document_ai/config" - "gitea.com/bitwsd/document_ai/internal/storage/cache" - "gitea.com/bitwsd/document_ai/internal/storage/dao" - "gitea.com/bitwsd/document_ai/pkg/common" - "gitea.com/bitwsd/document_ai/pkg/cors" - "gitea.com/bitwsd/document_ai/pkg/log" - "gitea.com/bitwsd/document_ai/pkg/middleware" - "gitea.com/bitwsd/document_ai/pkg/sms" + "gitea.com/texpixel/document_ai/api" + "gitea.com/texpixel/document_ai/config" + "gitea.com/texpixel/document_ai/internal/storage/cache" + "gitea.com/texpixel/document_ai/internal/storage/dao" + "gitea.com/texpixel/document_ai/pkg/common" + "gitea.com/texpixel/document_ai/pkg/cors" + "gitea.com/texpixel/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/middleware" + "gitea.com/texpixel/document_ai/pkg/sms" "github.com/gin-gonic/gin" ) @@ -53,7 +53,7 @@ func main() { // 使用中间件 r.Use(gin.Recovery(), middleware.RequestID(), middleware.AccessLog(), cors.Cors(cors.DefaultConfig()), common.MiddlewareContext) router := r.Group("doc_ai") - api.SetupRouter(router) + api.SetupRouter(router, dao.DB) // 启动服务器 addr := fmt.Sprintf(":%d", config.GlobalConfig.Server.Port) diff --git a/migrations/analytics_events.sql b/migrations/analytics_events.sql new file mode 100644 index 0000000..b818ff8 --- /dev/null +++ b/migrations/analytics_events.sql @@ -0,0 +1,18 @@ +-- 数据埋点事件表 +CREATE TABLE IF NOT EXISTS `analytics_events` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `event_name` VARCHAR(128) NOT NULL COMMENT '事件名称', + `properties` JSON DEFAULT NULL COMMENT '事件属性(JSON)', + `device_info` JSON DEFAULT NULL COMMENT '设备信息(JSON)', + `meta_data` JSON DEFAULT NULL COMMENT '元数据(JSON,包含task_id等)', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + INDEX `idx_user_id` (`user_id`), + INDEX `idx_event_name` (`event_name`), + INDEX `idx_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据埋点事件表'; + +-- 创建复合索引以提高查询性能 +CREATE INDEX `idx_user_event` ON `analytics_events` (`user_id`, `event_name`); +CREATE INDEX `idx_event_time` ON `analytics_events` (`event_name`, `created_at`); diff --git a/pkg/common/middleware.go b/pkg/common/middleware.go index 8423db4..1e97077 100644 --- a/pkg/common/middleware.go +++ b/pkg/common/middleware.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "gitea.com/bitwsd/document_ai/pkg/constant" - "gitea.com/bitwsd/document_ai/pkg/jwt" + "gitea.com/texpixel/document_ai/pkg/constant" + "gitea.com/texpixel/document_ai/pkg/jwt" "github.com/gin-gonic/gin" ) diff --git a/pkg/common/response.go b/pkg/common/response.go index b65e063..0e23349 100644 --- a/pkg/common/response.go +++ b/pkg/common/response.go @@ -3,7 +3,7 @@ package common import ( "context" - "gitea.com/bitwsd/document_ai/pkg/constant" + "gitea.com/texpixel/document_ai/pkg/constant" ) type Response struct { diff --git a/pkg/httpclient/client.go b/pkg/httpclient/client.go index d07715f..c868042 100644 --- a/pkg/httpclient/client.go +++ b/pkg/httpclient/client.go @@ -10,7 +10,7 @@ import ( "net/http" "time" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/log" ) // RetryConfig 重试配置 diff --git a/pkg/log/logger.go b/pkg/log/logger.go index 6bfc3f3..e28aaf2 100644 --- a/pkg/log/logger.go +++ b/pkg/log/logger.go @@ -8,7 +8,7 @@ import ( "runtime" "time" - "gitea.com/bitwsd/document_ai/pkg/requestid" + "gitea.com/texpixel/document_ai/pkg/requestid" "github.com/rs/zerolog" "gopkg.in/natefinch/lumberjack.v2" diff --git a/pkg/middleware/access.go b/pkg/middleware/access.go index b13e772..861d779 100644 --- a/pkg/middleware/access.go +++ b/pkg/middleware/access.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/log" "github.com/gin-gonic/gin" ) @@ -72,4 +72,3 @@ func AccessLog() gin.HandlerFunc { ) } } - diff --git a/pkg/middleware/requestid.go b/pkg/middleware/requestid.go index 0f060b1..d9edcef 100644 --- a/pkg/middleware/requestid.go +++ b/pkg/middleware/requestid.go @@ -1,7 +1,7 @@ package middleware import ( - "gitea.com/bitwsd/document_ai/pkg/requestid" + "gitea.com/texpixel/document_ai/pkg/requestid" "github.com/gin-gonic/gin" "github.com/google/uuid" diff --git a/pkg/oss/policy.go b/pkg/oss/policy.go index 1ff0650..a286eed 100644 --- a/pkg/oss/policy.go +++ b/pkg/oss/policy.go @@ -12,8 +12,8 @@ import ( "strings" "time" - "gitea.com/bitwsd/document_ai/config" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/config" + "gitea.com/texpixel/document_ai/pkg/log" "github.com/aliyun/aliyun-oss-go-sdk/oss" ) diff --git a/pkg/sms/sms.go b/pkg/sms/sms.go index fba75a9..a595a60 100644 --- a/pkg/sms/sms.go +++ b/pkg/sms/sms.go @@ -4,7 +4,7 @@ import ( "errors" "sync" - "gitea.com/bitwsd/document_ai/config" + "gitea.com/texpixel/document_ai/config" openapi "github.com/alibabacloud-go/darabonba-openapi/client" dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v2/client" aliutil "github.com/alibabacloud-go/tea-utils/service" diff --git a/pkg/utils/routine.go b/pkg/utils/routine.go index 5ed9ca1..c36e05d 100644 --- a/pkg/utils/routine.go +++ b/pkg/utils/routine.go @@ -3,7 +3,7 @@ package utils import ( "context" - "gitea.com/bitwsd/document_ai/pkg/log" + "gitea.com/texpixel/document_ai/pkg/log" ) func SafeGo(fn func()) {