sa2kit 1.6.30 → 1.6.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AliyunOSSProvider-4W47OFEK.mjs +6 -0
- package/dist/{AliyunOSSProvider-KJYRIZES.mjs.map → AliyunOSSProvider-4W47OFEK.mjs.map} +1 -1
- package/dist/AliyunOSSProvider-HCNGDJL7.js +15 -0
- package/dist/{AliyunOSSProvider-FWAKUB2T.js.map → AliyunOSSProvider-HCNGDJL7.js.map} +1 -1
- package/dist/ConfigService-3DIC6C3Q.js +21 -0
- package/dist/{ConfigService-7MEZXKJ5.js.map → ConfigService-3DIC6C3Q.js.map} +1 -1
- package/dist/ConfigService-V6ZK273Z.mjs +4 -0
- package/dist/{ConfigService-BV57YYFW.mjs.map → ConfigService-V6ZK273Z.mjs.map} +1 -1
- package/dist/LocalStorageProvider-3RVPCQB3.mjs +6 -0
- package/dist/{LocalStorageProvider-RTPMUOZ2.mjs.map → LocalStorageProvider-3RVPCQB3.mjs.map} +1 -1
- package/dist/LocalStorageProvider-PP7MA5OT.js +15 -0
- package/dist/{LocalStorageProvider-XSRCUXOU.js.map → LocalStorageProvider-PP7MA5OT.js.map} +1 -1
- package/dist/PMXParser-2VTA737I.js +13 -0
- package/dist/{PMXParser-YBS3B6HM.js.map → PMXParser-2VTA737I.js.map} +1 -1
- package/dist/PMXParser-RNVQL76A.mjs +4 -0
- package/dist/{PMXParser-L6IWHL4I.mjs.map → PMXParser-RNVQL76A.mjs.map} +1 -1
- package/dist/analytics/index.js +46 -45
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +45 -44
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/analytics/server/index.js +4 -4
- package/dist/analytics/server/index.js.map +1 -1
- package/dist/analytics/server/index.mjs +4 -4
- package/dist/analytics/server/index.mjs.map +1 -1
- package/dist/api/index.js +5 -5
- package/dist/api/index.js.map +1 -1
- package/dist/api/index.mjs +5 -5
- package/dist/api/index.mjs.map +1 -1
- package/dist/audioDetection/index.js +17 -16
- package/dist/audioDetection/index.js.map +1 -1
- package/dist/audioDetection/index.mjs +17 -16
- package/dist/audioDetection/index.mjs.map +1 -1
- package/dist/auth/client/index.js +4 -4
- package/dist/auth/client/index.mjs +1 -1
- package/dist/auth/components/index.js +3 -3
- package/dist/auth/components/index.js.map +1 -1
- package/dist/auth/components/index.mjs +3 -3
- package/dist/auth/components/index.mjs.map +1 -1
- package/dist/auth/index.js +29 -29
- package/dist/auth/index.mjs +5 -5
- package/dist/auth/middleware/index.js +3 -3
- package/dist/auth/middleware/index.mjs +2 -2
- package/dist/auth/routes/index.js +14 -14
- package/dist/auth/routes/index.mjs +2 -2
- package/dist/auth/services/index.js +7 -7
- package/dist/auth/services/index.mjs +1 -1
- package/dist/calendar/index.js +146 -182
- package/dist/calendar/index.js.map +1 -1
- package/dist/calendar/index.mjs +139 -175
- package/dist/calendar/index.mjs.map +1 -1
- package/dist/calendar/routes/index.js +1 -1
- package/dist/calendar/routes/index.js.map +1 -1
- package/dist/calendar/routes/index.mjs +1 -1
- package/dist/calendar/routes/index.mjs.map +1 -1
- package/dist/{chunk-5YQ5B7IZ.js → chunk-24HGREE6.js} +5 -5
- package/dist/{chunk-5YQ5B7IZ.js.map → chunk-24HGREE6.js.map} +1 -1
- package/dist/{chunk-6PRFP5EG.js → chunk-25OFOKNF.js} +6 -6
- package/dist/chunk-25OFOKNF.js.map +1 -0
- package/dist/{chunk-KQGP6BTS.mjs → chunk-3DXPQ4YV.mjs} +6 -6
- package/dist/chunk-3DXPQ4YV.mjs.map +1 -0
- package/dist/{chunk-3BGPZN4X.mjs → chunk-3NHAT7D4.mjs} +12 -12
- package/dist/chunk-3NHAT7D4.mjs.map +1 -0
- package/dist/{chunk-MW4BCIZC.mjs → chunk-4HC6M7FK.mjs} +3 -3
- package/dist/chunk-4HC6M7FK.mjs.map +1 -0
- package/dist/{chunk-ESRCX5TQ.mjs → chunk-52TN2QSS.mjs} +3 -3
- package/dist/{chunk-ESRCX5TQ.mjs.map → chunk-52TN2QSS.mjs.map} +1 -1
- package/dist/{chunk-CNTILN5J.mjs → chunk-5YQ62BKX.mjs} +20 -19
- package/dist/chunk-5YQ62BKX.mjs.map +1 -0
- package/dist/{chunk-6W5BMXJG.js → chunk-6OWNMJKG.js} +4 -4
- package/dist/{chunk-6W5BMXJG.js.map → chunk-6OWNMJKG.js.map} +1 -1
- package/dist/{chunk-DUHZ7VZP.js → chunk-7VRT55ZD.js} +3 -3
- package/dist/chunk-7VRT55ZD.js.map +1 -0
- package/dist/{chunk-3WOAPLEG.mjs → chunk-EB4NR623.mjs} +27 -26
- package/dist/chunk-EB4NR623.mjs.map +1 -0
- package/dist/chunk-EI27JKND.mjs +1988 -0
- package/dist/chunk-EI27JKND.mjs.map +1 -0
- package/dist/{chunk-CD77U7LZ.js → chunk-GBPLX42J.js} +9 -9
- package/dist/chunk-GBPLX42J.js.map +1 -0
- package/dist/{chunk-TFQF2HDO.mjs → chunk-HDEOCX2L.mjs} +12 -12
- package/dist/chunk-HDEOCX2L.mjs.map +1 -0
- package/dist/{chunk-TV3VKRJK.mjs → chunk-HDMIOOZY.mjs} +38 -68
- package/dist/chunk-HDMIOOZY.mjs.map +1 -0
- package/dist/{chunk-OPPF3326.js → chunk-HJ6MH7J7.js} +39 -69
- package/dist/chunk-HJ6MH7J7.js.map +1 -0
- package/dist/chunk-KO73EBUT.js +80 -0
- package/dist/chunk-KO73EBUT.js.map +1 -0
- package/dist/{chunk-6YKMCPQI.mjs → chunk-KZKIH4AS.mjs} +4 -4
- package/dist/chunk-KZKIH4AS.mjs.map +1 -0
- package/dist/{chunk-LX4XX6W7.js → chunk-L47ZOYHL.js} +15 -89
- package/dist/chunk-L47ZOYHL.js.map +1 -0
- package/dist/{chunk-6MQUBPKB.mjs → chunk-LJ4CCSSY.mjs} +3 -3
- package/dist/{chunk-6MQUBPKB.mjs.map → chunk-LJ4CCSSY.mjs.map} +1 -1
- package/dist/{chunk-TOC5FSHP.js → chunk-NJ2SNXBJ.js} +12 -12
- package/dist/chunk-NJ2SNXBJ.js.map +1 -0
- package/dist/{chunk-OCR5DS4C.mjs → chunk-PE5EAHZK.mjs} +3 -3
- package/dist/chunk-PE5EAHZK.mjs.map +1 -0
- package/dist/{chunk-LZHMNOED.js → chunk-Q5EDCKQA.js} +26 -26
- package/dist/chunk-Q5EDCKQA.js.map +1 -0
- package/dist/{chunk-CLKKZSPZ.js → chunk-RBKGYWME.js} +20 -19
- package/dist/chunk-RBKGYWME.js.map +1 -0
- package/dist/{chunk-VRTRSEEH.mjs → chunk-RSJSZ7QH.mjs} +11 -11
- package/dist/chunk-RSJSZ7QH.mjs.map +1 -0
- package/dist/{chunk-E7RGBAYJ.js → chunk-TDCDEBGP.js} +30 -29
- package/dist/chunk-TDCDEBGP.js.map +1 -0
- package/dist/{chunk-JZXJQMVE.js → chunk-UIFFDRTE.js} +11 -11
- package/dist/chunk-UIFFDRTE.js.map +1 -0
- package/dist/{chunk-T5OZHYVM.mjs → chunk-UKT3PLON.mjs} +13 -85
- package/dist/chunk-UKT3PLON.mjs.map +1 -0
- package/dist/{chunk-UOFTHYIH.js → chunk-UL6XJGUZ.js} +4 -4
- package/dist/chunk-UL6XJGUZ.js.map +1 -0
- package/dist/chunk-VVWQTO4Y.mjs +77 -0
- package/dist/chunk-VVWQTO4Y.mjs.map +1 -0
- package/dist/{chunk-A3UP56MS.js → chunk-WA67GZSZ.js} +3 -3
- package/dist/chunk-WA67GZSZ.js.map +1 -0
- package/dist/{chunk-OLHGZXN3.mjs → chunk-WEEXCPSE.mjs} +5 -5
- package/dist/chunk-WEEXCPSE.mjs.map +1 -0
- package/dist/chunk-XGBE4SUV.js +2093 -0
- package/dist/chunk-XGBE4SUV.js.map +1 -0
- package/dist/{chunk-QU5OT4DF.js → chunk-XJ7ZAGC5.js} +5 -5
- package/dist/chunk-XJ7ZAGC5.js.map +1 -0
- package/dist/{chunk-ZI25QCHD.mjs → chunk-YOTQG4NP.mjs} +25 -25
- package/dist/chunk-YOTQG4NP.mjs.map +1 -0
- package/dist/{chunk-QAT2RWAO.mjs → chunk-Z36R3P62.mjs} +7 -7
- package/dist/chunk-Z36R3P62.mjs.map +1 -0
- package/dist/{chunk-7Z5LLJ3A.js → chunk-ZWQJSZEY.js} +13 -13
- package/dist/chunk-ZWQJSZEY.js.map +1 -0
- package/dist/config/index.js +6 -6
- package/dist/config/index.js.map +1 -1
- package/dist/config/index.mjs +6 -6
- package/dist/config/index.mjs.map +1 -1
- package/dist/config/server/index.js +37 -37
- package/dist/config/server/index.js.map +1 -1
- package/dist/config/server/index.mjs +37 -37
- package/dist/config/server/index.mjs.map +1 -1
- package/dist/i18n/index.d.mts +2 -2
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +16 -17
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/index.mjs +16 -17
- package/dist/i18n/index.mjs.map +1 -1
- package/dist/imageCrop/index.js +11 -10
- package/dist/imageCrop/index.js.map +1 -1
- package/dist/imageCrop/index.mjs +11 -10
- package/dist/imageCrop/index.mjs.map +1 -1
- package/dist/index.d.mts +185 -100
- package/dist/index.d.ts +185 -100
- package/dist/index.js +225 -249
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +80 -104
- package/dist/index.mjs.map +1 -1
- package/dist/logger/index.js +6 -6
- package/dist/logger/index.mjs +1 -1
- package/dist/mikuFusionGame/index.d.mts +112 -0
- package/dist/mikuFusionGame/index.d.ts +112 -0
- package/dist/mikuFusionGame/index.js +680 -0
- package/dist/mikuFusionGame/index.js.map +1 -0
- package/dist/mikuFusionGame/index.mjs +667 -0
- package/dist/mikuFusionGame/index.mjs.map +1 -0
- package/dist/mmd/admin/index.js +11 -10
- package/dist/mmd/admin/index.js.map +1 -1
- package/dist/mmd/admin/index.mjs +11 -10
- package/dist/mmd/admin/index.mjs.map +1 -1
- package/dist/mmd/index.js +223 -241
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +220 -238
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/mmd/server/index.js +6 -6
- package/dist/mmd/server/index.js.map +1 -1
- package/dist/mmd/server/index.mjs +6 -6
- package/dist/mmd/server/index.mjs.map +1 -1
- package/dist/music/index.js +16 -16
- package/dist/music/index.mjs +2 -2
- package/dist/music/server/index.js +8 -8
- package/dist/music/server/index.mjs +1 -1
- package/dist/request/index.js +2 -2
- package/dist/request/index.js.map +1 -1
- package/dist/request/index.mjs +2 -2
- package/dist/request/index.mjs.map +1 -1
- package/dist/storage/index.js +15 -14
- package/dist/storage/index.mjs +3 -2
- package/dist/testYourself/admin/index.js +3 -3
- package/dist/testYourself/admin/index.mjs +1 -1
- package/dist/testYourself/index.js +22 -22
- package/dist/testYourself/index.js.map +1 -1
- package/dist/testYourself/index.mjs +14 -14
- package/dist/testYourself/index.mjs.map +1 -1
- package/dist/testYourself/server/index.js +4 -4
- package/dist/testYourself/server/index.mjs +1 -1
- package/dist/universalExport/index.d.mts +3 -3
- package/dist/universalExport/index.d.ts +3 -3
- package/dist/universalExport/index.js +48 -47
- package/dist/universalExport/index.js.map +1 -1
- package/dist/universalExport/index.mjs +48 -47
- package/dist/universalExport/index.mjs.map +1 -1
- package/dist/universalExport/server/index.js +29 -29
- package/dist/universalExport/server/index.js.map +1 -1
- package/dist/universalExport/server/index.mjs +28 -28
- package/dist/universalExport/server/index.mjs.map +1 -1
- package/dist/universalFile/index.d.mts +3 -3
- package/dist/universalFile/index.d.ts +3 -3
- package/dist/universalFile/index.js +73 -72
- package/dist/universalFile/index.js.map +1 -1
- package/dist/universalFile/index.mjs +73 -72
- package/dist/universalFile/index.mjs.map +1 -1
- package/dist/universalFile/server/index.js +258 -260
- package/dist/universalFile/server/index.js.map +1 -1
- package/dist/universalFile/server/index.mjs +244 -246
- package/dist/universalFile/server/index.mjs.map +1 -1
- package/dist/utils/index.js +11 -11
- package/dist/utils/index.mjs +2 -2
- package/package.json +25 -31
- package/dist/AliyunOSSProvider-FWAKUB2T.js +0 -15
- package/dist/AliyunOSSProvider-KJYRIZES.mjs +0 -6
- package/dist/ConfigService-7MEZXKJ5.js +0 -21
- package/dist/ConfigService-BV57YYFW.mjs +0 -4
- package/dist/LocalStorageProvider-RTPMUOZ2.mjs +0 -6
- package/dist/LocalStorageProvider-XSRCUXOU.js +0 -15
- package/dist/PMXParser-L6IWHL4I.mjs +0 -4
- package/dist/PMXParser-YBS3B6HM.js +0 -13
- package/dist/chunk-3BGPZN4X.mjs.map +0 -1
- package/dist/chunk-3WOAPLEG.mjs.map +0 -1
- package/dist/chunk-6PRFP5EG.js.map +0 -1
- package/dist/chunk-6YKMCPQI.mjs.map +0 -1
- package/dist/chunk-7Z5LLJ3A.js.map +0 -1
- package/dist/chunk-A3UP56MS.js.map +0 -1
- package/dist/chunk-CD77U7LZ.js.map +0 -1
- package/dist/chunk-CLKKZSPZ.js.map +0 -1
- package/dist/chunk-CNTILN5J.mjs.map +0 -1
- package/dist/chunk-DUHZ7VZP.js.map +0 -1
- package/dist/chunk-DW2ZTOCV.js +0 -1727
- package/dist/chunk-DW2ZTOCV.js.map +0 -1
- package/dist/chunk-E7RGBAYJ.js.map +0 -1
- package/dist/chunk-JZXJQMVE.js.map +0 -1
- package/dist/chunk-KQGP6BTS.mjs.map +0 -1
- package/dist/chunk-LFG6FPM5.mjs +0 -1597
- package/dist/chunk-LFG6FPM5.mjs.map +0 -1
- package/dist/chunk-LX4XX6W7.js.map +0 -1
- package/dist/chunk-LZHMNOED.js.map +0 -1
- package/dist/chunk-MW4BCIZC.mjs.map +0 -1
- package/dist/chunk-OCR5DS4C.mjs.map +0 -1
- package/dist/chunk-OLHGZXN3.mjs.map +0 -1
- package/dist/chunk-OPPF3326.js.map +0 -1
- package/dist/chunk-QAT2RWAO.mjs.map +0 -1
- package/dist/chunk-QU5OT4DF.js.map +0 -1
- package/dist/chunk-T5OZHYVM.mjs.map +0 -1
- package/dist/chunk-TFQF2HDO.mjs.map +0 -1
- package/dist/chunk-TOC5FSHP.js.map +0 -1
- package/dist/chunk-TV3VKRJK.mjs.map +0 -1
- package/dist/chunk-UOFTHYIH.js.map +0 -1
- package/dist/chunk-VRTRSEEH.mjs.map +0 -1
- package/dist/chunk-ZI25QCHD.mjs.map +0 -1
|
@@ -45,8 +45,8 @@ var analyticsEvents = pgCore.pgTable(
|
|
|
45
45
|
]
|
|
46
46
|
);
|
|
47
47
|
var defaultLogger = {
|
|
48
|
-
info: (message, data) => console.log(
|
|
49
|
-
error: (message, error) => console.error(
|
|
48
|
+
info: (message, data) => console.log("[Analytics] " + message, data || ""),
|
|
49
|
+
error: (message, error) => console.error("[Analytics] " + message, error)
|
|
50
50
|
};
|
|
51
51
|
function createAnalyticsService(db, analyticsEventsTable, logger = defaultLogger) {
|
|
52
52
|
const analyticsEvents2 = analyticsEventsTable;
|
|
@@ -113,8 +113,8 @@ function createAnalyticsService(db, analyticsEventsTable, logger = defaultLogger
|
|
|
113
113
|
const events = await db.select().from(analyticsEvents2).where(whereClause).orderBy(orderFn(orderByField)).limit(params.limit || 100).offset(params.offset || 0);
|
|
114
114
|
const formattedEvents = events.map((event) => ({
|
|
115
115
|
...event,
|
|
116
|
-
timestamp: event.timestamp.endsWith("Z") ? event.timestamp :
|
|
117
|
-
createdAt: event.createdAt.endsWith("Z") ? event.createdAt :
|
|
116
|
+
timestamp: event.timestamp.endsWith("Z") ? event.timestamp : event.timestamp + "Z",
|
|
117
|
+
createdAt: event.createdAt.endsWith("Z") ? event.createdAt : event.createdAt + "Z"
|
|
118
118
|
}));
|
|
119
119
|
return {
|
|
120
120
|
events: formattedEvents,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/analytics/server/schema.ts","../../../src/analytics/server/service.ts","../../../src/analytics/server/handlers.ts"],"names":["pgTable","text","timestamp","integer","jsonb","sql","index","analyticsEvents","gte","lte","eq","inArray","and","count","asc","desc"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 埋点系统数据库 Schema\n * Analytics Database Schema\n *\n * 使用方式:\n * 在 backend/drizzle/migrations/schema.ts 中导入并导出:\n * export { analyticsEvents } from '@lyricnote/shared/analytics/server/schema'\n */\n\nimport { pgTable, text, timestamp, integer, jsonb, index } from 'drizzle-orm/pg-core';\nimport { sql } from 'drizzle-orm';\n\n/**\n * 埋点事件表\n */\nexport const analyticsEvents = pgTable(\n 'analytics_events',\n {\n id: text().primaryKey().notNull(),\n eventType: text('event_type').notNull(),\n eventName: text('event_name').notNull(),\n timestamp: timestamp({ precision: 3, mode: 'string' }).notNull(),\n priority: integer().notNull(),\n\n userId: text('user_id'),\n sessionId: text('session_id').notNull(),\n deviceId: text('device_id').notNull(),\n\n pageUrl: text('page_url'),\n pageTitle: text('page_title'),\n referrer: text(),\n\n properties: jsonb(),\n\n platform: text().notNull(),\n appVersion: text('app_version').notNull(),\n sdkVersion: text('sdk_version').notNull(),\n\n createdAt: timestamp({ precision: 3, mode: 'string' })\n .default(sql`CURRENT_TIMESTAMP`)\n .notNull(),\n },\n (table) => [\n index('analytics_events_user_id_idx').using(\n 'btree',\n table.userId.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_event_type_idx').using(\n 'btree',\n table.eventType.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_platform_idx').using(\n 'btree',\n table.platform.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_timestamp_idx').using('btree', table.timestamp.desc().nullsLast()),\n index('analytics_events_session_id_idx').using(\n 'btree',\n table.sessionId.asc().nullsLast().op('text_ops')\n ),\n ]\n);\n\n/**\n * 创建外键关系(需要在使用时手动添加)\n *\n * 在你的 schema.ts 中添加:\n *\n * foreignKey({\n * columns: [analyticsEvents.userId],\n * foreignColumns: [user.id],\n * name: \"analytics_events_userId_fkey\"\n * }).onUpdate(\"cascade\").onDelete(\"set null\")\n */\n","/**\n * 埋点分析服务\n * Analytics Service\n *\n * 使用方式:\n * import { createAnalyticsService } from '@lyricnote/shared/analytics/server'\n * const service = createAnalyticsService(db, analyticsEvents, logger)\n */\n\nimport { eq, and, gte, lte, sql, desc, asc, count, inArray } from 'drizzle-orm';\n\n// 导出类型\nexport * from './types';\nimport type {\n AnalyticsEvent,\n AnalyticsQueryParams,\n AnalyticsStats,\n UserBehavior,\n SessionAnalytics,\n DatabaseInstance,\n} from './types';\n\n// Logger 接口\ninterface Logger {\n info: (message: string, data?: any) => void;\n error: (message: string, error: Error) => void;\n}\n\n// 默认 logger(如果未提供)\nconst defaultLogger: Logger = {\n info: (message, data) => console.log(`[Analytics] ${message}`, data || ''),\n error: (message, error) => console.error(`[Analytics] ${message}`, error),\n};\n\n/**\n * 创建埋点服务实例\n * @param db - Drizzle 数据库实例\n * @param analyticsEventsTable - analytics_events 表定义\n * @param logger - 日志实例(可选)\n */\nexport function createAnalyticsService(\n db: DatabaseInstance,\n analyticsEventsTable: any,\n logger: Logger = defaultLogger\n) {\n const analyticsEvents = analyticsEventsTable;\n\n return {\n /**\n * 批量插入埋点事件\n */\n async insertAnalyticsEvents(events: AnalyticsEvent[]): Promise<void> {\n try {\n if (events.length === 0) return;\n\n // 使用 onConflictDoNothing 来忽略重复的事件 ID\n // 这样可以避免前端重试或离线队列导致的重复插入\n await db.insert(analyticsEvents).values(events).onConflictDoNothing();\n\n logger.info('Analytics events inserted', { count: events.length });\n } catch (error) {\n logger.error('Failed to insert analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 查询埋点事件\n */\n async queryAnalyticsEvents(\n params: AnalyticsQueryParams\n ): Promise<{ events: AnalyticsEvent[]; total: number }> {\n try {\n const conditions: any[] = [];\n\n // 时间范围过滤\n if (params.startDate) {\n conditions.push(gte(analyticsEvents.timestamp, params.startDate));\n }\n if (params.endDate) {\n conditions.push(lte(analyticsEvents.timestamp, params.endDate));\n }\n\n // 事件类型过滤\n if (params.eventType) {\n conditions.push(eq(analyticsEvents.eventType, params.eventType));\n }\n if (params.eventTypes && params.eventTypes.length > 0) {\n conditions.push(inArray(analyticsEvents.eventType, params.eventTypes));\n }\n\n // 用户过滤\n if (params.userId) {\n conditions.push(eq(analyticsEvents.userId, params.userId));\n }\n\n // 平台过滤\n if (params.platform) {\n conditions.push(eq(analyticsEvents.platform, params.platform));\n }\n if (params.platforms && params.platforms.length > 0) {\n conditions.push(inArray(analyticsEvents.platform, params.platforms));\n }\n\n // 会话过滤\n if (params.sessionId) {\n conditions.push(eq(analyticsEvents.sessionId, params.sessionId));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 查询总数\n const [{ total }] = await db\n .select({ total: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 排序\n const orderByColumn = params.orderBy || 'timestamp';\n const orderDirection = params.orderDirection || 'desc';\n const orderFn = orderDirection === 'asc' ? asc : desc;\n\n let orderByField;\n switch (orderByColumn) {\n case 'eventType':\n orderByField = analyticsEvents.eventType;\n break;\n case 'platform':\n orderByField = analyticsEvents.platform;\n break;\n default:\n orderByField = analyticsEvents.timestamp;\n }\n\n // 查询数据\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(whereClause)\n .orderBy(orderFn(orderByField))\n .limit(params.limit || 100)\n .offset(params.offset || 0);\n\n // 修复时间戳格式:添加 'Z' 后缀表示 UTC 时间\n const formattedEvents = events.map((event: any) => ({\n ...event,\n timestamp: event.timestamp.endsWith('Z') ? event.timestamp : `${event.timestamp}Z`,\n createdAt: event.createdAt.endsWith('Z') ? event.createdAt : `${event.createdAt}Z`,\n }));\n\n return {\n events: formattedEvents as AnalyticsEvent[],\n total: Number(total),\n };\n } catch (error) {\n logger.error('Failed to query analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取统计数据\n */\n async getAnalyticsStats(\n startDate?: string,\n endDate?: string,\n platform?: string\n ): Promise<AnalyticsStats> {\n try {\n const conditions: any[] = [];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n if (platform) {\n conditions.push(eq(analyticsEvents.platform, platform));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 总事件数\n const [{ totalEvents }] = await db\n .select({ totalEvents: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一用户数\n const [{ uniqueUsers }] = await db\n .select({ uniqueUsers: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一会话数\n const [{ uniqueSessions }] = await db\n .select({ uniqueSessions: sql<number>`COUNT(DISTINCT ${analyticsEvents.sessionId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一设备数\n const [{ uniqueDevices }] = await db\n .select({ uniqueDevices: sql<number>`COUNT(DISTINCT ${analyticsEvents.deviceId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 按事件类型统计\n const eventsByType = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(10);\n\n // 按平台统计\n const eventsByPlatform = await db\n .select({\n platform: analyticsEvents.platform,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform)\n .orderBy(desc(count()));\n\n // 热门页面\n const topPages = await db\n .select({\n pageUrl: analyticsEvents.pageUrl,\n count: count(),\n })\n .from(analyticsEvents)\n .where(and(whereClause, sql`${analyticsEvents.pageUrl} IS NOT NULL`))\n .groupBy(analyticsEvents.pageUrl)\n .orderBy(desc(count()))\n .limit(10);\n\n return {\n totalEvents: Number(totalEvents),\n uniqueUsers: Number(uniqueUsers),\n uniqueSessions: Number(uniqueSessions),\n uniqueDevices: Number(uniqueDevices),\n eventsByType: eventsByType.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n eventsByPlatform: eventsByPlatform.map((e: any) => ({\n platform: e.platform,\n count: Number(e.count),\n })),\n topPages: topPages\n .filter((p: any) => p.pageUrl)\n .map((p: any) => ({\n pageUrl: p.pageUrl!,\n count: Number(p.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get analytics stats', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取用户行为分析\n */\n async getUserBehavior(\n userId: string,\n startDate?: string,\n endDate?: string\n ): Promise<UserBehavior | null> {\n try {\n const conditions: any[] = [eq(analyticsEvents.userId, userId)];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n // 事件总数\n const [{ eventCount }] = await db\n .select({ eventCount: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n if (eventCount === 0) return null;\n\n // 最后活跃时间\n const [{ lastActive }] = await db\n .select({ lastActive: sql<string>`MAX(${analyticsEvents.timestamp})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 使用的平台\n const platformsResult = await db\n .select({ platform: analyticsEvents.platform })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform);\n\n // 热门事件\n const topEvents = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(5);\n\n return {\n userId,\n eventCount: Number(eventCount),\n lastActive,\n platforms: platformsResult.map((p: any) => p.platform),\n topEvents: topEvents.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get user behavior', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取会话分析\n */\n async getSessionAnalytics(sessionId: string): Promise<SessionAnalytics | null> {\n try {\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(eq(analyticsEvents.sessionId, sessionId))\n .orderBy(asc(analyticsEvents.timestamp));\n\n if (events.length === 0) return null;\n\n const firstEvent = events[0];\n const lastEvent = events[events.length - 1];\n\n const startTime = new Date(firstEvent.timestamp).getTime();\n const endTime = new Date(lastEvent.timestamp).getTime();\n const duration = endTime - startTime;\n\n return {\n sessionId,\n userId: firstEvent.userId || undefined,\n deviceId: firstEvent.deviceId,\n platform: firstEvent.platform,\n startTime: firstEvent.timestamp,\n endTime: lastEvent.timestamp,\n duration,\n eventCount: events.length,\n events: events as AnalyticsEvent[],\n };\n } catch (error) {\n logger.error('Failed to get session analytics', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取漏斗分析\n */\n async getFunnelAnalysis(\n steps: string[],\n startDate?: string,\n endDate?: string\n ): Promise<{ step: string; count: number; conversionRate: number }[]> {\n try {\n const results = [];\n let previousCount = 0;\n\n for (let i = 0; i < steps.length; i++) {\n const conditions: any[] = [eq(analyticsEvents.eventType, steps[i])];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n const [{ count: stepCount }] = await db\n .select({ count: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n const conversionRate = i === 0 ? 100 : (Number(stepCount) / previousCount) * 100;\n\n results.push({\n step: steps[i] || 'unknown',\n count: Number(stepCount),\n conversionRate: Math.round(conversionRate * 100) / 100,\n });\n\n previousCount = Number(stepCount);\n }\n\n return results;\n } catch (error) {\n logger.error('Failed to get funnel analysis', error as Error);\n throw error;\n }\n },\n\n /**\n * 删除旧数据(数据清理)\n */\n async cleanOldAnalyticsEvents(daysToKeep: number = 90): Promise<number> {\n try {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);\n\n await db\n .delete(analyticsEvents)\n .where(lte(analyticsEvents.timestamp, cutoffDate.toISOString()));\n\n logger.info('Old analytics events cleaned', { daysToKeep });\n\n return 0; // Drizzle doesn't return affected rows count in delete\n } catch (error) {\n logger.error('Failed to clean old analytics events', error as Error);\n throw error;\n }\n },\n };\n}\n","/**\n * 埋点 API 路由处理器\n * Analytics API Route Handlers\n *\n * 使用方式(Next.js):\n * import { createAnalyticsHandlers } from '@lyricnote/shared/analytics/server'\n * const handlers = createAnalyticsHandlers(analyticsService)\n * export const POST = handlers.handleEventsPost\n */\n\nimport type { AnalyticsEvent, AnalyticsQueryParams } from './types';\n\n// 服务接口类型\ninterface AnalyticsService {\n insertAnalyticsEvents: (events: AnalyticsEvent[]) => Promise<void>;\n queryAnalyticsEvents: (\n params: AnalyticsQueryParams\n ) => Promise<{ events: AnalyticsEvent[]; total: number }>;\n getAnalyticsStats: (startDate?: string, endDate?: string, platform?: string) => Promise<any>;\n getUserBehavior: (userId: string, startDate?: string, endDate?: string) => Promise<any>;\n getSessionAnalytics: (sessionId: string) => Promise<any>;\n getFunnelAnalysis: (steps: string[], startDate?: string, endDate?: string) => Promise<any>;\n}\n\n// Request/Response 接口(兼容多种框架)\ninterface Request {\n json: () => Promise<any>;\n url?: string;\n nextUrl?: { searchParams: URLSearchParams };\n}\n\ninterface ResponseInit {\n status?: number;\n headers?: Record<string, string>;\n}\n\n/**\n * 创建埋点 API 处理器\n */\nexport function createAnalyticsHandlers(\n service: AnalyticsService,\n ResponseClass?: any // Next.js NextResponse 或其他响应类\n) {\n // 默认使用标准响应格式\n const createResponse = ResponseClass\n ? (data: any, init?: ResponseInit) => ResponseClass.json(data, init)\n : (data: any, init?: ResponseInit) => ({ body: data, status: init?.status || 200 });\n\n return {\n /**\n * POST /api/analytics/events\n * 处理事件上报\n */\n async handleEventsPost(request: Request) {\n try {\n const body = await request.json();\n const { events } = body;\n\n // 验证数据\n if (!Array.isArray(events)) {\n return createResponse(\n { success: false, message: 'Invalid events format' },\n { status: 400 }\n );\n }\n\n if (events.length === 0) {\n return createResponse(\n { success: true, message: 'No events to process' },\n { status: 200 }\n );\n }\n\n // 验证每个事件的必填字段\n for (const event of events) {\n if (!event.event_id || !event.event_type || !event.event_name) {\n return createResponse(\n { success: false, message: 'Missing required event fields' },\n { status: 400 }\n );\n }\n }\n\n // 转换字段名(前端使用 snake_case,数据库使用 camelCase)\n const formattedEvents = events.map((event) => ({\n id: event.event_id,\n eventType: event.event_type,\n eventName: event.event_name,\n timestamp: new Date(event.timestamp).toISOString(),\n priority: event.priority,\n userId: event.user_id || null,\n sessionId: event.session_id,\n deviceId: event.device_id,\n pageUrl: event.page_url || null,\n pageTitle: event.page_title || null,\n referrer: event.referrer || null,\n properties: event.properties || {},\n platform: event.platform,\n appVersion: event.app_version,\n sdkVersion: event.sdk_version,\n }));\n\n // 插入数据库\n await service.insertAnalyticsEvents(formattedEvents);\n\n return createResponse({\n success: true,\n message: 'Events received successfully',\n count: events.length,\n });\n } catch (error) {\n console.error('Failed to process analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/query\n * 查询埋点事件\n */\n async handleQueryGet(request: Request) {\n try {\n // 解析查询参数\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const params: AnalyticsQueryParams = {\n startDate: searchParams.get('startDate') || undefined,\n endDate: searchParams.get('endDate') || undefined,\n eventType: searchParams.get('eventType') || undefined,\n eventTypes: searchParams.get('eventTypes')?.split(',') || undefined,\n userId: searchParams.get('userId') || undefined,\n platform: searchParams.get('platform') || undefined,\n platforms: searchParams.get('platforms')?.split(',') || undefined,\n sessionId: searchParams.get('sessionId') || undefined,\n limit: parseInt(searchParams.get('limit') || '100'),\n offset: parseInt(searchParams.get('offset') || '0'),\n orderBy: (searchParams.get('orderBy') || 'timestamp') as any,\n orderDirection: (searchParams.get('orderDirection') || 'desc') as any,\n };\n\n // 查询数据\n const result = await service.queryAnalyticsEvents(params);\n\n return createResponse({\n success: true,\n data: result.events,\n total: result.total,\n limit: params.limit,\n offset: params.offset,\n });\n } catch (error) {\n console.error('Failed to query analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/stats\n * 获取统计数据\n */\n async handleStatsGet(request: Request) {\n try {\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n const platform = searchParams.get('platform') || undefined;\n\n // 获取统计数据\n const stats = await service.getAnalyticsStats(startDate, endDate, platform);\n\n return createResponse({\n success: true,\n data: stats,\n });\n } catch (error) {\n console.error('Failed to get analytics stats', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/user/[userId]\n * 获取用户行为分析\n */\n async handleUserBehaviorGet(request: Request, params: { userId: string }) {\n try {\n const { userId } = params;\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n\n // 获取用户行为数据\n const behavior = await service.getUserBehavior(userId, startDate, endDate);\n\n if (!behavior) {\n return createResponse(\n { success: false, message: 'No data found for this user' },\n { status: 404 }\n );\n }\n\n return createResponse({\n success: true,\n data: behavior,\n });\n } catch (error) {\n console.error('Failed to get user behavior', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/session/[sessionId]\n * 获取会话分析\n */\n async handleSessionAnalyticsGet(_request: Request, params: { sessionId: string }) {\n try {\n const { sessionId } = params;\n\n // 获取会话分析数据\n const session = await service.getSessionAnalytics(sessionId);\n\n if (!session) {\n return createResponse({ success: false, message: 'Session not found' }, { status: 404 });\n }\n\n return createResponse({\n success: true,\n data: session,\n });\n } catch (error) {\n console.error('Failed to get session analytics', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * POST /api/analytics/funnel\n * 漏斗分析\n */\n async handleFunnelPost(request: Request) {\n try {\n const body = await request.json();\n const { steps, startDate, endDate } = body;\n\n // 验证数据\n if (!Array.isArray(steps) || steps.length === 0) {\n return createResponse(\n { success: false, message: 'Invalid steps format' },\n { status: 400 }\n );\n }\n\n // 获取漏斗分析数据\n const funnel = await service.getFunnelAnalysis(steps, startDate, endDate);\n\n return createResponse({\n success: true,\n data: funnel,\n });\n } catch (error) {\n console.error('Failed to get funnel analysis', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/analytics/server/schema.ts","../../../src/analytics/server/service.ts","../../../src/analytics/server/handlers.ts"],"names":["pgTable","text","timestamp","integer","jsonb","sql","index","analyticsEvents","gte","lte","eq","inArray","and","count","asc","desc"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 埋点系统数据库 Schema\n * Analytics Database Schema\n *\n * 使用方式:\n * 在 backend/drizzle/migrations/schema.ts 中导入并导出:\n * export { analyticsEvents } from '@lyricnote/shared/analytics/server/schema'\n */\n\nimport { pgTable, text, timestamp, integer, jsonb, index } from 'drizzle-orm/pg-core';\nimport { sql } from 'drizzle-orm';\n\n/**\n * 埋点事件表\n */\nexport const analyticsEvents = pgTable(\n 'analytics_events',\n {\n id: text().primaryKey().notNull(),\n eventType: text('event_type').notNull(),\n eventName: text('event_name').notNull(),\n timestamp: timestamp({ precision: 3, mode: 'string' }).notNull(),\n priority: integer().notNull(),\n\n userId: text('user_id'),\n sessionId: text('session_id').notNull(),\n deviceId: text('device_id').notNull(),\n\n pageUrl: text('page_url'),\n pageTitle: text('page_title'),\n referrer: text(),\n\n properties: jsonb(),\n\n platform: text().notNull(),\n appVersion: text('app_version').notNull(),\n sdkVersion: text('sdk_version').notNull(),\n\n createdAt: timestamp({ precision: 3, mode: 'string' })\n .default(sql`CURRENT_TIMESTAMP`)\n .notNull(),\n },\n (table) => [\n index('analytics_events_user_id_idx').using(\n 'btree',\n table.userId.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_event_type_idx').using(\n 'btree',\n table.eventType.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_platform_idx').using(\n 'btree',\n table.platform.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_timestamp_idx').using('btree', table.timestamp.desc().nullsLast()),\n index('analytics_events_session_id_idx').using(\n 'btree',\n table.sessionId.asc().nullsLast().op('text_ops')\n ),\n ]\n);\n\n/**\n * 创建外键关系(需要在使用时手动添加)\n *\n * 在你的 schema.ts 中添加:\n *\n * foreignKey({\n * columns: [analyticsEvents.userId],\n * foreignColumns: [user.id],\n * name: \"analytics_events_userId_fkey\"\n * }).onUpdate(\"cascade\").onDelete(\"set null\")\n */\n","/**\n * 埋点分析服务\n * Analytics Service\n *\n * 使用方式:\n * import { createAnalyticsService } from '@lyricnote/shared/analytics/server'\n * const service = createAnalyticsService(db, analyticsEvents, logger)\n */\n\nimport { eq, and, gte, lte, sql, desc, asc, count, inArray } from 'drizzle-orm';\n\n// 导出类型\nexport * from './types';\nimport type {\n AnalyticsEvent,\n AnalyticsQueryParams,\n AnalyticsStats,\n UserBehavior,\n SessionAnalytics,\n DatabaseInstance,\n} from './types';\n\n// Logger 接口\ninterface Logger {\n info: (message: string, data?: any) => void;\n error: (message: string, error: Error) => void;\n}\n\n// 默认 logger(如果未提供)\nconst defaultLogger: Logger = {\n info: (message, data) => console.log('[Analytics] ' + (message), data || ''),\n error: (message, error) => console.error('[Analytics] ' + (message), error),\n};\n\n/**\n * 创建埋点服务实例\n * @param db - Drizzle 数据库实例\n * @param analyticsEventsTable - analytics_events 表定义\n * @param logger - 日志实例(可选)\n */\nexport function createAnalyticsService(\n db: DatabaseInstance,\n analyticsEventsTable: any,\n logger: Logger = defaultLogger\n) {\n const analyticsEvents = analyticsEventsTable;\n\n return {\n /**\n * 批量插入埋点事件\n */\n async insertAnalyticsEvents(events: AnalyticsEvent[]): Promise<void> {\n try {\n if (events.length === 0) return;\n\n // 使用 onConflictDoNothing 来忽略重复的事件 ID\n // 这样可以避免前端重试或离线队列导致的重复插入\n await db.insert(analyticsEvents).values(events).onConflictDoNothing();\n\n logger.info('Analytics events inserted', { count: events.length });\n } catch (error) {\n logger.error('Failed to insert analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 查询埋点事件\n */\n async queryAnalyticsEvents(\n params: AnalyticsQueryParams\n ): Promise<{ events: AnalyticsEvent[]; total: number }> {\n try {\n const conditions: any[] = [];\n\n // 时间范围过滤\n if (params.startDate) {\n conditions.push(gte(analyticsEvents.timestamp, params.startDate));\n }\n if (params.endDate) {\n conditions.push(lte(analyticsEvents.timestamp, params.endDate));\n }\n\n // 事件类型过滤\n if (params.eventType) {\n conditions.push(eq(analyticsEvents.eventType, params.eventType));\n }\n if (params.eventTypes && params.eventTypes.length > 0) {\n conditions.push(inArray(analyticsEvents.eventType, params.eventTypes));\n }\n\n // 用户过滤\n if (params.userId) {\n conditions.push(eq(analyticsEvents.userId, params.userId));\n }\n\n // 平台过滤\n if (params.platform) {\n conditions.push(eq(analyticsEvents.platform, params.platform));\n }\n if (params.platforms && params.platforms.length > 0) {\n conditions.push(inArray(analyticsEvents.platform, params.platforms));\n }\n\n // 会话过滤\n if (params.sessionId) {\n conditions.push(eq(analyticsEvents.sessionId, params.sessionId));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 查询总数\n const [{ total }] = await db\n .select({ total: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 排序\n const orderByColumn = params.orderBy || 'timestamp';\n const orderDirection = params.orderDirection || 'desc';\n const orderFn = orderDirection === 'asc' ? asc : desc;\n\n let orderByField;\n switch (orderByColumn) {\n case 'eventType':\n orderByField = analyticsEvents.eventType;\n break;\n case 'platform':\n orderByField = analyticsEvents.platform;\n break;\n default:\n orderByField = analyticsEvents.timestamp;\n }\n\n // 查询数据\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(whereClause)\n .orderBy(orderFn(orderByField))\n .limit(params.limit || 100)\n .offset(params.offset || 0);\n\n // 修复时间戳格式:添加 'Z' 后缀表示 UTC 时间\n const formattedEvents = events.map((event: any) => ({\n ...event,\n timestamp: event.timestamp.endsWith('Z') ? event.timestamp : (event.timestamp) + 'Z',\n createdAt: event.createdAt.endsWith('Z') ? event.createdAt : (event.createdAt) + 'Z',\n }));\n\n return {\n events: formattedEvents as AnalyticsEvent[],\n total: Number(total),\n };\n } catch (error) {\n logger.error('Failed to query analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取统计数据\n */\n async getAnalyticsStats(\n startDate?: string,\n endDate?: string,\n platform?: string\n ): Promise<AnalyticsStats> {\n try {\n const conditions: any[] = [];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n if (platform) {\n conditions.push(eq(analyticsEvents.platform, platform));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 总事件数\n const [{ totalEvents }] = await db\n .select({ totalEvents: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一用户数\n const [{ uniqueUsers }] = await db\n .select({ uniqueUsers: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一会话数\n const [{ uniqueSessions }] = await db\n .select({ uniqueSessions: sql<number>`COUNT(DISTINCT ${analyticsEvents.sessionId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一设备数\n const [{ uniqueDevices }] = await db\n .select({ uniqueDevices: sql<number>`COUNT(DISTINCT ${analyticsEvents.deviceId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 按事件类型统计\n const eventsByType = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(10);\n\n // 按平台统计\n const eventsByPlatform = await db\n .select({\n platform: analyticsEvents.platform,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform)\n .orderBy(desc(count()));\n\n // 热门页面\n const topPages = await db\n .select({\n pageUrl: analyticsEvents.pageUrl,\n count: count(),\n })\n .from(analyticsEvents)\n .where(and(whereClause, sql`${analyticsEvents.pageUrl} IS NOT NULL`))\n .groupBy(analyticsEvents.pageUrl)\n .orderBy(desc(count()))\n .limit(10);\n\n return {\n totalEvents: Number(totalEvents),\n uniqueUsers: Number(uniqueUsers),\n uniqueSessions: Number(uniqueSessions),\n uniqueDevices: Number(uniqueDevices),\n eventsByType: eventsByType.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n eventsByPlatform: eventsByPlatform.map((e: any) => ({\n platform: e.platform,\n count: Number(e.count),\n })),\n topPages: topPages\n .filter((p: any) => p.pageUrl)\n .map((p: any) => ({\n pageUrl: p.pageUrl!,\n count: Number(p.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get analytics stats', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取用户行为分析\n */\n async getUserBehavior(\n userId: string,\n startDate?: string,\n endDate?: string\n ): Promise<UserBehavior | null> {\n try {\n const conditions: any[] = [eq(analyticsEvents.userId, userId)];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n // 事件总数\n const [{ eventCount }] = await db\n .select({ eventCount: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n if (eventCount === 0) return null;\n\n // 最后活跃时间\n const [{ lastActive }] = await db\n .select({ lastActive: sql<string>`MAX(${analyticsEvents.timestamp})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 使用的平台\n const platformsResult = await db\n .select({ platform: analyticsEvents.platform })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform);\n\n // 热门事件\n const topEvents = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(5);\n\n return {\n userId,\n eventCount: Number(eventCount),\n lastActive,\n platforms: platformsResult.map((p: any) => p.platform),\n topEvents: topEvents.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get user behavior', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取会话分析\n */\n async getSessionAnalytics(sessionId: string): Promise<SessionAnalytics | null> {\n try {\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(eq(analyticsEvents.sessionId, sessionId))\n .orderBy(asc(analyticsEvents.timestamp));\n\n if (events.length === 0) return null;\n\n const firstEvent = events[0];\n const lastEvent = events[events.length - 1];\n\n const startTime = new Date(firstEvent.timestamp).getTime();\n const endTime = new Date(lastEvent.timestamp).getTime();\n const duration = endTime - startTime;\n\n return {\n sessionId,\n userId: firstEvent.userId || undefined,\n deviceId: firstEvent.deviceId,\n platform: firstEvent.platform,\n startTime: firstEvent.timestamp,\n endTime: lastEvent.timestamp,\n duration,\n eventCount: events.length,\n events: events as AnalyticsEvent[],\n };\n } catch (error) {\n logger.error('Failed to get session analytics', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取漏斗分析\n */\n async getFunnelAnalysis(\n steps: string[],\n startDate?: string,\n endDate?: string\n ): Promise<{ step: string; count: number; conversionRate: number }[]> {\n try {\n const results = [];\n let previousCount = 0;\n\n for (let i = 0; i < steps.length; i++) {\n const conditions: any[] = [eq(analyticsEvents.eventType, steps[i])];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n const [{ count: stepCount }] = await db\n .select({ count: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n const conversionRate = i === 0 ? 100 : (Number(stepCount) / previousCount) * 100;\n\n results.push({\n step: steps[i] || 'unknown',\n count: Number(stepCount),\n conversionRate: Math.round(conversionRate * 100) / 100,\n });\n\n previousCount = Number(stepCount);\n }\n\n return results;\n } catch (error) {\n logger.error('Failed to get funnel analysis', error as Error);\n throw error;\n }\n },\n\n /**\n * 删除旧数据(数据清理)\n */\n async cleanOldAnalyticsEvents(daysToKeep: number = 90): Promise<number> {\n try {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);\n\n await db\n .delete(analyticsEvents)\n .where(lte(analyticsEvents.timestamp, cutoffDate.toISOString()));\n\n logger.info('Old analytics events cleaned', { daysToKeep });\n\n return 0; // Drizzle doesn't return affected rows count in delete\n } catch (error) {\n logger.error('Failed to clean old analytics events', error as Error);\n throw error;\n }\n },\n };\n}\n","/**\n * 埋点 API 路由处理器\n * Analytics API Route Handlers\n *\n * 使用方式(Next.js):\n * import { createAnalyticsHandlers } from '@lyricnote/shared/analytics/server'\n * const handlers = createAnalyticsHandlers(analyticsService)\n * export const POST = handlers.handleEventsPost\n */\n\nimport type { AnalyticsEvent, AnalyticsQueryParams } from './types';\n\n// 服务接口类型\ninterface AnalyticsService {\n insertAnalyticsEvents: (events: AnalyticsEvent[]) => Promise<void>;\n queryAnalyticsEvents: (\n params: AnalyticsQueryParams\n ) => Promise<{ events: AnalyticsEvent[]; total: number }>;\n getAnalyticsStats: (startDate?: string, endDate?: string, platform?: string) => Promise<any>;\n getUserBehavior: (userId: string, startDate?: string, endDate?: string) => Promise<any>;\n getSessionAnalytics: (sessionId: string) => Promise<any>;\n getFunnelAnalysis: (steps: string[], startDate?: string, endDate?: string) => Promise<any>;\n}\n\n// Request/Response 接口(兼容多种框架)\ninterface Request {\n json: () => Promise<any>;\n url?: string;\n nextUrl?: { searchParams: URLSearchParams };\n}\n\ninterface ResponseInit {\n status?: number;\n headers?: Record<string, string>;\n}\n\n/**\n * 创建埋点 API 处理器\n */\nexport function createAnalyticsHandlers(\n service: AnalyticsService,\n ResponseClass?: any // Next.js NextResponse 或其他响应类\n) {\n // 默认使用标准响应格式\n const createResponse = ResponseClass\n ? (data: any, init?: ResponseInit) => ResponseClass.json(data, init)\n : (data: any, init?: ResponseInit) => ({ body: data, status: init?.status || 200 });\n\n return {\n /**\n * POST /api/analytics/events\n * 处理事件上报\n */\n async handleEventsPost(request: Request) {\n try {\n const body = await request.json();\n const { events } = body;\n\n // 验证数据\n if (!Array.isArray(events)) {\n return createResponse(\n { success: false, message: 'Invalid events format' },\n { status: 400 }\n );\n }\n\n if (events.length === 0) {\n return createResponse(\n { success: true, message: 'No events to process' },\n { status: 200 }\n );\n }\n\n // 验证每个事件的必填字段\n for (const event of events) {\n if (!event.event_id || !event.event_type || !event.event_name) {\n return createResponse(\n { success: false, message: 'Missing required event fields' },\n { status: 400 }\n );\n }\n }\n\n // 转换字段名(前端使用 snake_case,数据库使用 camelCase)\n const formattedEvents = events.map((event) => ({\n id: event.event_id,\n eventType: event.event_type,\n eventName: event.event_name,\n timestamp: new Date(event.timestamp).toISOString(),\n priority: event.priority,\n userId: event.user_id || null,\n sessionId: event.session_id,\n deviceId: event.device_id,\n pageUrl: event.page_url || null,\n pageTitle: event.page_title || null,\n referrer: event.referrer || null,\n properties: event.properties || {},\n platform: event.platform,\n appVersion: event.app_version,\n sdkVersion: event.sdk_version,\n }));\n\n // 插入数据库\n await service.insertAnalyticsEvents(formattedEvents);\n\n return createResponse({\n success: true,\n message: 'Events received successfully',\n count: events.length,\n });\n } catch (error) {\n console.error('Failed to process analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/query\n * 查询埋点事件\n */\n async handleQueryGet(request: Request) {\n try {\n // 解析查询参数\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const params: AnalyticsQueryParams = {\n startDate: searchParams.get('startDate') || undefined,\n endDate: searchParams.get('endDate') || undefined,\n eventType: searchParams.get('eventType') || undefined,\n eventTypes: searchParams.get('eventTypes')?.split(',') || undefined,\n userId: searchParams.get('userId') || undefined,\n platform: searchParams.get('platform') || undefined,\n platforms: searchParams.get('platforms')?.split(',') || undefined,\n sessionId: searchParams.get('sessionId') || undefined,\n limit: parseInt(searchParams.get('limit') || '100'),\n offset: parseInt(searchParams.get('offset') || '0'),\n orderBy: (searchParams.get('orderBy') || 'timestamp') as any,\n orderDirection: (searchParams.get('orderDirection') || 'desc') as any,\n };\n\n // 查询数据\n const result = await service.queryAnalyticsEvents(params);\n\n return createResponse({\n success: true,\n data: result.events,\n total: result.total,\n limit: params.limit,\n offset: params.offset,\n });\n } catch (error) {\n console.error('Failed to query analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/stats\n * 获取统计数据\n */\n async handleStatsGet(request: Request) {\n try {\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n const platform = searchParams.get('platform') || undefined;\n\n // 获取统计数据\n const stats = await service.getAnalyticsStats(startDate, endDate, platform);\n\n return createResponse({\n success: true,\n data: stats,\n });\n } catch (error) {\n console.error('Failed to get analytics stats', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/user/[userId]\n * 获取用户行为分析\n */\n async handleUserBehaviorGet(request: Request, params: { userId: string }) {\n try {\n const { userId } = params;\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n\n // 获取用户行为数据\n const behavior = await service.getUserBehavior(userId, startDate, endDate);\n\n if (!behavior) {\n return createResponse(\n { success: false, message: 'No data found for this user' },\n { status: 404 }\n );\n }\n\n return createResponse({\n success: true,\n data: behavior,\n });\n } catch (error) {\n console.error('Failed to get user behavior', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/session/[sessionId]\n * 获取会话分析\n */\n async handleSessionAnalyticsGet(_request: Request, params: { sessionId: string }) {\n try {\n const { sessionId } = params;\n\n // 获取会话分析数据\n const session = await service.getSessionAnalytics(sessionId);\n\n if (!session) {\n return createResponse({ success: false, message: 'Session not found' }, { status: 404 });\n }\n\n return createResponse({\n success: true,\n data: session,\n });\n } catch (error) {\n console.error('Failed to get session analytics', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * POST /api/analytics/funnel\n * 漏斗分析\n */\n async handleFunnelPost(request: Request) {\n try {\n const body = await request.json();\n const { steps, startDate, endDate } = body;\n\n // 验证数据\n if (!Array.isArray(steps) || steps.length === 0) {\n return createResponse(\n { success: false, message: 'Invalid steps format' },\n { status: 400 }\n );\n }\n\n // 获取漏斗分析数据\n const funnel = await service.getFunnelAnalysis(steps, startDate, endDate);\n\n return createResponse({\n success: true,\n data: funnel,\n });\n } catch (error) {\n console.error('Failed to get funnel analysis', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n };\n}\n"]}
|
|
@@ -43,8 +43,8 @@ var analyticsEvents = pgTable(
|
|
|
43
43
|
]
|
|
44
44
|
);
|
|
45
45
|
var defaultLogger = {
|
|
46
|
-
info: (message, data) => console.log(
|
|
47
|
-
error: (message, error) => console.error(
|
|
46
|
+
info: (message, data) => console.log("[Analytics] " + message, data || ""),
|
|
47
|
+
error: (message, error) => console.error("[Analytics] " + message, error)
|
|
48
48
|
};
|
|
49
49
|
function createAnalyticsService(db, analyticsEventsTable, logger = defaultLogger) {
|
|
50
50
|
const analyticsEvents2 = analyticsEventsTable;
|
|
@@ -111,8 +111,8 @@ function createAnalyticsService(db, analyticsEventsTable, logger = defaultLogger
|
|
|
111
111
|
const events = await db.select().from(analyticsEvents2).where(whereClause).orderBy(orderFn(orderByField)).limit(params.limit || 100).offset(params.offset || 0);
|
|
112
112
|
const formattedEvents = events.map((event) => ({
|
|
113
113
|
...event,
|
|
114
|
-
timestamp: event.timestamp.endsWith("Z") ? event.timestamp :
|
|
115
|
-
createdAt: event.createdAt.endsWith("Z") ? event.createdAt :
|
|
114
|
+
timestamp: event.timestamp.endsWith("Z") ? event.timestamp : event.timestamp + "Z",
|
|
115
|
+
createdAt: event.createdAt.endsWith("Z") ? event.createdAt : event.createdAt + "Z"
|
|
116
116
|
}));
|
|
117
117
|
return {
|
|
118
118
|
events: formattedEvents,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/analytics/server/schema.ts","../../../src/analytics/server/service.ts","../../../src/analytics/server/handlers.ts"],"names":["analyticsEvents","sql"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * 埋点系统数据库 Schema\n * Analytics Database Schema\n *\n * 使用方式:\n * 在 backend/drizzle/migrations/schema.ts 中导入并导出:\n * export { analyticsEvents } from '@lyricnote/shared/analytics/server/schema'\n */\n\nimport { pgTable, text, timestamp, integer, jsonb, index } from 'drizzle-orm/pg-core';\nimport { sql } from 'drizzle-orm';\n\n/**\n * 埋点事件表\n */\nexport const analyticsEvents = pgTable(\n 'analytics_events',\n {\n id: text().primaryKey().notNull(),\n eventType: text('event_type').notNull(),\n eventName: text('event_name').notNull(),\n timestamp: timestamp({ precision: 3, mode: 'string' }).notNull(),\n priority: integer().notNull(),\n\n userId: text('user_id'),\n sessionId: text('session_id').notNull(),\n deviceId: text('device_id').notNull(),\n\n pageUrl: text('page_url'),\n pageTitle: text('page_title'),\n referrer: text(),\n\n properties: jsonb(),\n\n platform: text().notNull(),\n appVersion: text('app_version').notNull(),\n sdkVersion: text('sdk_version').notNull(),\n\n createdAt: timestamp({ precision: 3, mode: 'string' })\n .default(sql`CURRENT_TIMESTAMP`)\n .notNull(),\n },\n (table) => [\n index('analytics_events_user_id_idx').using(\n 'btree',\n table.userId.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_event_type_idx').using(\n 'btree',\n table.eventType.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_platform_idx').using(\n 'btree',\n table.platform.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_timestamp_idx').using('btree', table.timestamp.desc().nullsLast()),\n index('analytics_events_session_id_idx').using(\n 'btree',\n table.sessionId.asc().nullsLast().op('text_ops')\n ),\n ]\n);\n\n/**\n * 创建外键关系(需要在使用时手动添加)\n *\n * 在你的 schema.ts 中添加:\n *\n * foreignKey({\n * columns: [analyticsEvents.userId],\n * foreignColumns: [user.id],\n * name: \"analytics_events_userId_fkey\"\n * }).onUpdate(\"cascade\").onDelete(\"set null\")\n */\n","/**\n * 埋点分析服务\n * Analytics Service\n *\n * 使用方式:\n * import { createAnalyticsService } from '@lyricnote/shared/analytics/server'\n * const service = createAnalyticsService(db, analyticsEvents, logger)\n */\n\nimport { eq, and, gte, lte, sql, desc, asc, count, inArray } from 'drizzle-orm';\n\n// 导出类型\nexport * from './types';\nimport type {\n AnalyticsEvent,\n AnalyticsQueryParams,\n AnalyticsStats,\n UserBehavior,\n SessionAnalytics,\n DatabaseInstance,\n} from './types';\n\n// Logger 接口\ninterface Logger {\n info: (message: string, data?: any) => void;\n error: (message: string, error: Error) => void;\n}\n\n// 默认 logger(如果未提供)\nconst defaultLogger: Logger = {\n info: (message, data) => console.log(`[Analytics] ${message}`, data || ''),\n error: (message, error) => console.error(`[Analytics] ${message}`, error),\n};\n\n/**\n * 创建埋点服务实例\n * @param db - Drizzle 数据库实例\n * @param analyticsEventsTable - analytics_events 表定义\n * @param logger - 日志实例(可选)\n */\nexport function createAnalyticsService(\n db: DatabaseInstance,\n analyticsEventsTable: any,\n logger: Logger = defaultLogger\n) {\n const analyticsEvents = analyticsEventsTable;\n\n return {\n /**\n * 批量插入埋点事件\n */\n async insertAnalyticsEvents(events: AnalyticsEvent[]): Promise<void> {\n try {\n if (events.length === 0) return;\n\n // 使用 onConflictDoNothing 来忽略重复的事件 ID\n // 这样可以避免前端重试或离线队列导致的重复插入\n await db.insert(analyticsEvents).values(events).onConflictDoNothing();\n\n logger.info('Analytics events inserted', { count: events.length });\n } catch (error) {\n logger.error('Failed to insert analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 查询埋点事件\n */\n async queryAnalyticsEvents(\n params: AnalyticsQueryParams\n ): Promise<{ events: AnalyticsEvent[]; total: number }> {\n try {\n const conditions: any[] = [];\n\n // 时间范围过滤\n if (params.startDate) {\n conditions.push(gte(analyticsEvents.timestamp, params.startDate));\n }\n if (params.endDate) {\n conditions.push(lte(analyticsEvents.timestamp, params.endDate));\n }\n\n // 事件类型过滤\n if (params.eventType) {\n conditions.push(eq(analyticsEvents.eventType, params.eventType));\n }\n if (params.eventTypes && params.eventTypes.length > 0) {\n conditions.push(inArray(analyticsEvents.eventType, params.eventTypes));\n }\n\n // 用户过滤\n if (params.userId) {\n conditions.push(eq(analyticsEvents.userId, params.userId));\n }\n\n // 平台过滤\n if (params.platform) {\n conditions.push(eq(analyticsEvents.platform, params.platform));\n }\n if (params.platforms && params.platforms.length > 0) {\n conditions.push(inArray(analyticsEvents.platform, params.platforms));\n }\n\n // 会话过滤\n if (params.sessionId) {\n conditions.push(eq(analyticsEvents.sessionId, params.sessionId));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 查询总数\n const [{ total }] = await db\n .select({ total: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 排序\n const orderByColumn = params.orderBy || 'timestamp';\n const orderDirection = params.orderDirection || 'desc';\n const orderFn = orderDirection === 'asc' ? asc : desc;\n\n let orderByField;\n switch (orderByColumn) {\n case 'eventType':\n orderByField = analyticsEvents.eventType;\n break;\n case 'platform':\n orderByField = analyticsEvents.platform;\n break;\n default:\n orderByField = analyticsEvents.timestamp;\n }\n\n // 查询数据\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(whereClause)\n .orderBy(orderFn(orderByField))\n .limit(params.limit || 100)\n .offset(params.offset || 0);\n\n // 修复时间戳格式:添加 'Z' 后缀表示 UTC 时间\n const formattedEvents = events.map((event: any) => ({\n ...event,\n timestamp: event.timestamp.endsWith('Z') ? event.timestamp : `${event.timestamp}Z`,\n createdAt: event.createdAt.endsWith('Z') ? event.createdAt : `${event.createdAt}Z`,\n }));\n\n return {\n events: formattedEvents as AnalyticsEvent[],\n total: Number(total),\n };\n } catch (error) {\n logger.error('Failed to query analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取统计数据\n */\n async getAnalyticsStats(\n startDate?: string,\n endDate?: string,\n platform?: string\n ): Promise<AnalyticsStats> {\n try {\n const conditions: any[] = [];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n if (platform) {\n conditions.push(eq(analyticsEvents.platform, platform));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 总事件数\n const [{ totalEvents }] = await db\n .select({ totalEvents: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一用户数\n const [{ uniqueUsers }] = await db\n .select({ uniqueUsers: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一会话数\n const [{ uniqueSessions }] = await db\n .select({ uniqueSessions: sql<number>`COUNT(DISTINCT ${analyticsEvents.sessionId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一设备数\n const [{ uniqueDevices }] = await db\n .select({ uniqueDevices: sql<number>`COUNT(DISTINCT ${analyticsEvents.deviceId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 按事件类型统计\n const eventsByType = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(10);\n\n // 按平台统计\n const eventsByPlatform = await db\n .select({\n platform: analyticsEvents.platform,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform)\n .orderBy(desc(count()));\n\n // 热门页面\n const topPages = await db\n .select({\n pageUrl: analyticsEvents.pageUrl,\n count: count(),\n })\n .from(analyticsEvents)\n .where(and(whereClause, sql`${analyticsEvents.pageUrl} IS NOT NULL`))\n .groupBy(analyticsEvents.pageUrl)\n .orderBy(desc(count()))\n .limit(10);\n\n return {\n totalEvents: Number(totalEvents),\n uniqueUsers: Number(uniqueUsers),\n uniqueSessions: Number(uniqueSessions),\n uniqueDevices: Number(uniqueDevices),\n eventsByType: eventsByType.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n eventsByPlatform: eventsByPlatform.map((e: any) => ({\n platform: e.platform,\n count: Number(e.count),\n })),\n topPages: topPages\n .filter((p: any) => p.pageUrl)\n .map((p: any) => ({\n pageUrl: p.pageUrl!,\n count: Number(p.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get analytics stats', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取用户行为分析\n */\n async getUserBehavior(\n userId: string,\n startDate?: string,\n endDate?: string\n ): Promise<UserBehavior | null> {\n try {\n const conditions: any[] = [eq(analyticsEvents.userId, userId)];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n // 事件总数\n const [{ eventCount }] = await db\n .select({ eventCount: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n if (eventCount === 0) return null;\n\n // 最后活跃时间\n const [{ lastActive }] = await db\n .select({ lastActive: sql<string>`MAX(${analyticsEvents.timestamp})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 使用的平台\n const platformsResult = await db\n .select({ platform: analyticsEvents.platform })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform);\n\n // 热门事件\n const topEvents = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(5);\n\n return {\n userId,\n eventCount: Number(eventCount),\n lastActive,\n platforms: platformsResult.map((p: any) => p.platform),\n topEvents: topEvents.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get user behavior', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取会话分析\n */\n async getSessionAnalytics(sessionId: string): Promise<SessionAnalytics | null> {\n try {\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(eq(analyticsEvents.sessionId, sessionId))\n .orderBy(asc(analyticsEvents.timestamp));\n\n if (events.length === 0) return null;\n\n const firstEvent = events[0];\n const lastEvent = events[events.length - 1];\n\n const startTime = new Date(firstEvent.timestamp).getTime();\n const endTime = new Date(lastEvent.timestamp).getTime();\n const duration = endTime - startTime;\n\n return {\n sessionId,\n userId: firstEvent.userId || undefined,\n deviceId: firstEvent.deviceId,\n platform: firstEvent.platform,\n startTime: firstEvent.timestamp,\n endTime: lastEvent.timestamp,\n duration,\n eventCount: events.length,\n events: events as AnalyticsEvent[],\n };\n } catch (error) {\n logger.error('Failed to get session analytics', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取漏斗分析\n */\n async getFunnelAnalysis(\n steps: string[],\n startDate?: string,\n endDate?: string\n ): Promise<{ step: string; count: number; conversionRate: number }[]> {\n try {\n const results = [];\n let previousCount = 0;\n\n for (let i = 0; i < steps.length; i++) {\n const conditions: any[] = [eq(analyticsEvents.eventType, steps[i])];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n const [{ count: stepCount }] = await db\n .select({ count: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n const conversionRate = i === 0 ? 100 : (Number(stepCount) / previousCount) * 100;\n\n results.push({\n step: steps[i] || 'unknown',\n count: Number(stepCount),\n conversionRate: Math.round(conversionRate * 100) / 100,\n });\n\n previousCount = Number(stepCount);\n }\n\n return results;\n } catch (error) {\n logger.error('Failed to get funnel analysis', error as Error);\n throw error;\n }\n },\n\n /**\n * 删除旧数据(数据清理)\n */\n async cleanOldAnalyticsEvents(daysToKeep: number = 90): Promise<number> {\n try {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);\n\n await db\n .delete(analyticsEvents)\n .where(lte(analyticsEvents.timestamp, cutoffDate.toISOString()));\n\n logger.info('Old analytics events cleaned', { daysToKeep });\n\n return 0; // Drizzle doesn't return affected rows count in delete\n } catch (error) {\n logger.error('Failed to clean old analytics events', error as Error);\n throw error;\n }\n },\n };\n}\n","/**\n * 埋点 API 路由处理器\n * Analytics API Route Handlers\n *\n * 使用方式(Next.js):\n * import { createAnalyticsHandlers } from '@lyricnote/shared/analytics/server'\n * const handlers = createAnalyticsHandlers(analyticsService)\n * export const POST = handlers.handleEventsPost\n */\n\nimport type { AnalyticsEvent, AnalyticsQueryParams } from './types';\n\n// 服务接口类型\ninterface AnalyticsService {\n insertAnalyticsEvents: (events: AnalyticsEvent[]) => Promise<void>;\n queryAnalyticsEvents: (\n params: AnalyticsQueryParams\n ) => Promise<{ events: AnalyticsEvent[]; total: number }>;\n getAnalyticsStats: (startDate?: string, endDate?: string, platform?: string) => Promise<any>;\n getUserBehavior: (userId: string, startDate?: string, endDate?: string) => Promise<any>;\n getSessionAnalytics: (sessionId: string) => Promise<any>;\n getFunnelAnalysis: (steps: string[], startDate?: string, endDate?: string) => Promise<any>;\n}\n\n// Request/Response 接口(兼容多种框架)\ninterface Request {\n json: () => Promise<any>;\n url?: string;\n nextUrl?: { searchParams: URLSearchParams };\n}\n\ninterface ResponseInit {\n status?: number;\n headers?: Record<string, string>;\n}\n\n/**\n * 创建埋点 API 处理器\n */\nexport function createAnalyticsHandlers(\n service: AnalyticsService,\n ResponseClass?: any // Next.js NextResponse 或其他响应类\n) {\n // 默认使用标准响应格式\n const createResponse = ResponseClass\n ? (data: any, init?: ResponseInit) => ResponseClass.json(data, init)\n : (data: any, init?: ResponseInit) => ({ body: data, status: init?.status || 200 });\n\n return {\n /**\n * POST /api/analytics/events\n * 处理事件上报\n */\n async handleEventsPost(request: Request) {\n try {\n const body = await request.json();\n const { events } = body;\n\n // 验证数据\n if (!Array.isArray(events)) {\n return createResponse(\n { success: false, message: 'Invalid events format' },\n { status: 400 }\n );\n }\n\n if (events.length === 0) {\n return createResponse(\n { success: true, message: 'No events to process' },\n { status: 200 }\n );\n }\n\n // 验证每个事件的必填字段\n for (const event of events) {\n if (!event.event_id || !event.event_type || !event.event_name) {\n return createResponse(\n { success: false, message: 'Missing required event fields' },\n { status: 400 }\n );\n }\n }\n\n // 转换字段名(前端使用 snake_case,数据库使用 camelCase)\n const formattedEvents = events.map((event) => ({\n id: event.event_id,\n eventType: event.event_type,\n eventName: event.event_name,\n timestamp: new Date(event.timestamp).toISOString(),\n priority: event.priority,\n userId: event.user_id || null,\n sessionId: event.session_id,\n deviceId: event.device_id,\n pageUrl: event.page_url || null,\n pageTitle: event.page_title || null,\n referrer: event.referrer || null,\n properties: event.properties || {},\n platform: event.platform,\n appVersion: event.app_version,\n sdkVersion: event.sdk_version,\n }));\n\n // 插入数据库\n await service.insertAnalyticsEvents(formattedEvents);\n\n return createResponse({\n success: true,\n message: 'Events received successfully',\n count: events.length,\n });\n } catch (error) {\n console.error('Failed to process analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/query\n * 查询埋点事件\n */\n async handleQueryGet(request: Request) {\n try {\n // 解析查询参数\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const params: AnalyticsQueryParams = {\n startDate: searchParams.get('startDate') || undefined,\n endDate: searchParams.get('endDate') || undefined,\n eventType: searchParams.get('eventType') || undefined,\n eventTypes: searchParams.get('eventTypes')?.split(',') || undefined,\n userId: searchParams.get('userId') || undefined,\n platform: searchParams.get('platform') || undefined,\n platforms: searchParams.get('platforms')?.split(',') || undefined,\n sessionId: searchParams.get('sessionId') || undefined,\n limit: parseInt(searchParams.get('limit') || '100'),\n offset: parseInt(searchParams.get('offset') || '0'),\n orderBy: (searchParams.get('orderBy') || 'timestamp') as any,\n orderDirection: (searchParams.get('orderDirection') || 'desc') as any,\n };\n\n // 查询数据\n const result = await service.queryAnalyticsEvents(params);\n\n return createResponse({\n success: true,\n data: result.events,\n total: result.total,\n limit: params.limit,\n offset: params.offset,\n });\n } catch (error) {\n console.error('Failed to query analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/stats\n * 获取统计数据\n */\n async handleStatsGet(request: Request) {\n try {\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n const platform = searchParams.get('platform') || undefined;\n\n // 获取统计数据\n const stats = await service.getAnalyticsStats(startDate, endDate, platform);\n\n return createResponse({\n success: true,\n data: stats,\n });\n } catch (error) {\n console.error('Failed to get analytics stats', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/user/[userId]\n * 获取用户行为分析\n */\n async handleUserBehaviorGet(request: Request, params: { userId: string }) {\n try {\n const { userId } = params;\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n\n // 获取用户行为数据\n const behavior = await service.getUserBehavior(userId, startDate, endDate);\n\n if (!behavior) {\n return createResponse(\n { success: false, message: 'No data found for this user' },\n { status: 404 }\n );\n }\n\n return createResponse({\n success: true,\n data: behavior,\n });\n } catch (error) {\n console.error('Failed to get user behavior', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/session/[sessionId]\n * 获取会话分析\n */\n async handleSessionAnalyticsGet(_request: Request, params: { sessionId: string }) {\n try {\n const { sessionId } = params;\n\n // 获取会话分析数据\n const session = await service.getSessionAnalytics(sessionId);\n\n if (!session) {\n return createResponse({ success: false, message: 'Session not found' }, { status: 404 });\n }\n\n return createResponse({\n success: true,\n data: session,\n });\n } catch (error) {\n console.error('Failed to get session analytics', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * POST /api/analytics/funnel\n * 漏斗分析\n */\n async handleFunnelPost(request: Request) {\n try {\n const body = await request.json();\n const { steps, startDate, endDate } = body;\n\n // 验证数据\n if (!Array.isArray(steps) || steps.length === 0) {\n return createResponse(\n { success: false, message: 'Invalid steps format' },\n { status: 400 }\n );\n }\n\n // 获取漏斗分析数据\n const funnel = await service.getFunnelAnalysis(steps, startDate, endDate);\n\n return createResponse({\n success: true,\n data: funnel,\n });\n } catch (error) {\n console.error('Failed to get funnel analysis', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/analytics/server/schema.ts","../../../src/analytics/server/service.ts","../../../src/analytics/server/handlers.ts"],"names":["analyticsEvents","sql"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * 埋点系统数据库 Schema\n * Analytics Database Schema\n *\n * 使用方式:\n * 在 backend/drizzle/migrations/schema.ts 中导入并导出:\n * export { analyticsEvents } from '@lyricnote/shared/analytics/server/schema'\n */\n\nimport { pgTable, text, timestamp, integer, jsonb, index } from 'drizzle-orm/pg-core';\nimport { sql } from 'drizzle-orm';\n\n/**\n * 埋点事件表\n */\nexport const analyticsEvents = pgTable(\n 'analytics_events',\n {\n id: text().primaryKey().notNull(),\n eventType: text('event_type').notNull(),\n eventName: text('event_name').notNull(),\n timestamp: timestamp({ precision: 3, mode: 'string' }).notNull(),\n priority: integer().notNull(),\n\n userId: text('user_id'),\n sessionId: text('session_id').notNull(),\n deviceId: text('device_id').notNull(),\n\n pageUrl: text('page_url'),\n pageTitle: text('page_title'),\n referrer: text(),\n\n properties: jsonb(),\n\n platform: text().notNull(),\n appVersion: text('app_version').notNull(),\n sdkVersion: text('sdk_version').notNull(),\n\n createdAt: timestamp({ precision: 3, mode: 'string' })\n .default(sql`CURRENT_TIMESTAMP`)\n .notNull(),\n },\n (table) => [\n index('analytics_events_user_id_idx').using(\n 'btree',\n table.userId.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_event_type_idx').using(\n 'btree',\n table.eventType.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_platform_idx').using(\n 'btree',\n table.platform.asc().nullsLast().op('text_ops')\n ),\n index('analytics_events_timestamp_idx').using('btree', table.timestamp.desc().nullsLast()),\n index('analytics_events_session_id_idx').using(\n 'btree',\n table.sessionId.asc().nullsLast().op('text_ops')\n ),\n ]\n);\n\n/**\n * 创建外键关系(需要在使用时手动添加)\n *\n * 在你的 schema.ts 中添加:\n *\n * foreignKey({\n * columns: [analyticsEvents.userId],\n * foreignColumns: [user.id],\n * name: \"analytics_events_userId_fkey\"\n * }).onUpdate(\"cascade\").onDelete(\"set null\")\n */\n","/**\n * 埋点分析服务\n * Analytics Service\n *\n * 使用方式:\n * import { createAnalyticsService } from '@lyricnote/shared/analytics/server'\n * const service = createAnalyticsService(db, analyticsEvents, logger)\n */\n\nimport { eq, and, gte, lte, sql, desc, asc, count, inArray } from 'drizzle-orm';\n\n// 导出类型\nexport * from './types';\nimport type {\n AnalyticsEvent,\n AnalyticsQueryParams,\n AnalyticsStats,\n UserBehavior,\n SessionAnalytics,\n DatabaseInstance,\n} from './types';\n\n// Logger 接口\ninterface Logger {\n info: (message: string, data?: any) => void;\n error: (message: string, error: Error) => void;\n}\n\n// 默认 logger(如果未提供)\nconst defaultLogger: Logger = {\n info: (message, data) => console.log('[Analytics] ' + (message), data || ''),\n error: (message, error) => console.error('[Analytics] ' + (message), error),\n};\n\n/**\n * 创建埋点服务实例\n * @param db - Drizzle 数据库实例\n * @param analyticsEventsTable - analytics_events 表定义\n * @param logger - 日志实例(可选)\n */\nexport function createAnalyticsService(\n db: DatabaseInstance,\n analyticsEventsTable: any,\n logger: Logger = defaultLogger\n) {\n const analyticsEvents = analyticsEventsTable;\n\n return {\n /**\n * 批量插入埋点事件\n */\n async insertAnalyticsEvents(events: AnalyticsEvent[]): Promise<void> {\n try {\n if (events.length === 0) return;\n\n // 使用 onConflictDoNothing 来忽略重复的事件 ID\n // 这样可以避免前端重试或离线队列导致的重复插入\n await db.insert(analyticsEvents).values(events).onConflictDoNothing();\n\n logger.info('Analytics events inserted', { count: events.length });\n } catch (error) {\n logger.error('Failed to insert analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 查询埋点事件\n */\n async queryAnalyticsEvents(\n params: AnalyticsQueryParams\n ): Promise<{ events: AnalyticsEvent[]; total: number }> {\n try {\n const conditions: any[] = [];\n\n // 时间范围过滤\n if (params.startDate) {\n conditions.push(gte(analyticsEvents.timestamp, params.startDate));\n }\n if (params.endDate) {\n conditions.push(lte(analyticsEvents.timestamp, params.endDate));\n }\n\n // 事件类型过滤\n if (params.eventType) {\n conditions.push(eq(analyticsEvents.eventType, params.eventType));\n }\n if (params.eventTypes && params.eventTypes.length > 0) {\n conditions.push(inArray(analyticsEvents.eventType, params.eventTypes));\n }\n\n // 用户过滤\n if (params.userId) {\n conditions.push(eq(analyticsEvents.userId, params.userId));\n }\n\n // 平台过滤\n if (params.platform) {\n conditions.push(eq(analyticsEvents.platform, params.platform));\n }\n if (params.platforms && params.platforms.length > 0) {\n conditions.push(inArray(analyticsEvents.platform, params.platforms));\n }\n\n // 会话过滤\n if (params.sessionId) {\n conditions.push(eq(analyticsEvents.sessionId, params.sessionId));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 查询总数\n const [{ total }] = await db\n .select({ total: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 排序\n const orderByColumn = params.orderBy || 'timestamp';\n const orderDirection = params.orderDirection || 'desc';\n const orderFn = orderDirection === 'asc' ? asc : desc;\n\n let orderByField;\n switch (orderByColumn) {\n case 'eventType':\n orderByField = analyticsEvents.eventType;\n break;\n case 'platform':\n orderByField = analyticsEvents.platform;\n break;\n default:\n orderByField = analyticsEvents.timestamp;\n }\n\n // 查询数据\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(whereClause)\n .orderBy(orderFn(orderByField))\n .limit(params.limit || 100)\n .offset(params.offset || 0);\n\n // 修复时间戳格式:添加 'Z' 后缀表示 UTC 时间\n const formattedEvents = events.map((event: any) => ({\n ...event,\n timestamp: event.timestamp.endsWith('Z') ? event.timestamp : (event.timestamp) + 'Z',\n createdAt: event.createdAt.endsWith('Z') ? event.createdAt : (event.createdAt) + 'Z',\n }));\n\n return {\n events: formattedEvents as AnalyticsEvent[],\n total: Number(total),\n };\n } catch (error) {\n logger.error('Failed to query analytics events', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取统计数据\n */\n async getAnalyticsStats(\n startDate?: string,\n endDate?: string,\n platform?: string\n ): Promise<AnalyticsStats> {\n try {\n const conditions: any[] = [];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n if (platform) {\n conditions.push(eq(analyticsEvents.platform, platform));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // 总事件数\n const [{ totalEvents }] = await db\n .select({ totalEvents: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一用户数\n const [{ uniqueUsers }] = await db\n .select({ uniqueUsers: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一会话数\n const [{ uniqueSessions }] = await db\n .select({ uniqueSessions: sql<number>`COUNT(DISTINCT ${analyticsEvents.sessionId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 唯一设备数\n const [{ uniqueDevices }] = await db\n .select({ uniqueDevices: sql<number>`COUNT(DISTINCT ${analyticsEvents.deviceId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 按事件类型统计\n const eventsByType = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(10);\n\n // 按平台统计\n const eventsByPlatform = await db\n .select({\n platform: analyticsEvents.platform,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform)\n .orderBy(desc(count()));\n\n // 热门页面\n const topPages = await db\n .select({\n pageUrl: analyticsEvents.pageUrl,\n count: count(),\n })\n .from(analyticsEvents)\n .where(and(whereClause, sql`${analyticsEvents.pageUrl} IS NOT NULL`))\n .groupBy(analyticsEvents.pageUrl)\n .orderBy(desc(count()))\n .limit(10);\n\n return {\n totalEvents: Number(totalEvents),\n uniqueUsers: Number(uniqueUsers),\n uniqueSessions: Number(uniqueSessions),\n uniqueDevices: Number(uniqueDevices),\n eventsByType: eventsByType.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n eventsByPlatform: eventsByPlatform.map((e: any) => ({\n platform: e.platform,\n count: Number(e.count),\n })),\n topPages: topPages\n .filter((p: any) => p.pageUrl)\n .map((p: any) => ({\n pageUrl: p.pageUrl!,\n count: Number(p.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get analytics stats', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取用户行为分析\n */\n async getUserBehavior(\n userId: string,\n startDate?: string,\n endDate?: string\n ): Promise<UserBehavior | null> {\n try {\n const conditions: any[] = [eq(analyticsEvents.userId, userId)];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n // 事件总数\n const [{ eventCount }] = await db\n .select({ eventCount: count() })\n .from(analyticsEvents)\n .where(whereClause);\n\n if (eventCount === 0) return null;\n\n // 最后活跃时间\n const [{ lastActive }] = await db\n .select({ lastActive: sql<string>`MAX(${analyticsEvents.timestamp})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n // 使用的平台\n const platformsResult = await db\n .select({ platform: analyticsEvents.platform })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.platform);\n\n // 热门事件\n const topEvents = await db\n .select({\n eventType: analyticsEvents.eventType,\n count: count(),\n })\n .from(analyticsEvents)\n .where(whereClause)\n .groupBy(analyticsEvents.eventType)\n .orderBy(desc(count()))\n .limit(5);\n\n return {\n userId,\n eventCount: Number(eventCount),\n lastActive,\n platforms: platformsResult.map((p: any) => p.platform),\n topEvents: topEvents.map((e: any) => ({\n eventType: e.eventType,\n count: Number(e.count),\n })),\n };\n } catch (error) {\n logger.error('Failed to get user behavior', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取会话分析\n */\n async getSessionAnalytics(sessionId: string): Promise<SessionAnalytics | null> {\n try {\n const events = await db\n .select()\n .from(analyticsEvents)\n .where(eq(analyticsEvents.sessionId, sessionId))\n .orderBy(asc(analyticsEvents.timestamp));\n\n if (events.length === 0) return null;\n\n const firstEvent = events[0];\n const lastEvent = events[events.length - 1];\n\n const startTime = new Date(firstEvent.timestamp).getTime();\n const endTime = new Date(lastEvent.timestamp).getTime();\n const duration = endTime - startTime;\n\n return {\n sessionId,\n userId: firstEvent.userId || undefined,\n deviceId: firstEvent.deviceId,\n platform: firstEvent.platform,\n startTime: firstEvent.timestamp,\n endTime: lastEvent.timestamp,\n duration,\n eventCount: events.length,\n events: events as AnalyticsEvent[],\n };\n } catch (error) {\n logger.error('Failed to get session analytics', error as Error);\n throw error;\n }\n },\n\n /**\n * 获取漏斗分析\n */\n async getFunnelAnalysis(\n steps: string[],\n startDate?: string,\n endDate?: string\n ): Promise<{ step: string; count: number; conversionRate: number }[]> {\n try {\n const results = [];\n let previousCount = 0;\n\n for (let i = 0; i < steps.length; i++) {\n const conditions: any[] = [eq(analyticsEvents.eventType, steps[i])];\n\n if (startDate) {\n conditions.push(gte(analyticsEvents.timestamp, startDate));\n }\n if (endDate) {\n conditions.push(lte(analyticsEvents.timestamp, endDate));\n }\n\n const whereClause = and(...conditions);\n\n const [{ count: stepCount }] = await db\n .select({ count: sql<number>`COUNT(DISTINCT ${analyticsEvents.userId})` })\n .from(analyticsEvents)\n .where(whereClause);\n\n const conversionRate = i === 0 ? 100 : (Number(stepCount) / previousCount) * 100;\n\n results.push({\n step: steps[i] || 'unknown',\n count: Number(stepCount),\n conversionRate: Math.round(conversionRate * 100) / 100,\n });\n\n previousCount = Number(stepCount);\n }\n\n return results;\n } catch (error) {\n logger.error('Failed to get funnel analysis', error as Error);\n throw error;\n }\n },\n\n /**\n * 删除旧数据(数据清理)\n */\n async cleanOldAnalyticsEvents(daysToKeep: number = 90): Promise<number> {\n try {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);\n\n await db\n .delete(analyticsEvents)\n .where(lte(analyticsEvents.timestamp, cutoffDate.toISOString()));\n\n logger.info('Old analytics events cleaned', { daysToKeep });\n\n return 0; // Drizzle doesn't return affected rows count in delete\n } catch (error) {\n logger.error('Failed to clean old analytics events', error as Error);\n throw error;\n }\n },\n };\n}\n","/**\n * 埋点 API 路由处理器\n * Analytics API Route Handlers\n *\n * 使用方式(Next.js):\n * import { createAnalyticsHandlers } from '@lyricnote/shared/analytics/server'\n * const handlers = createAnalyticsHandlers(analyticsService)\n * export const POST = handlers.handleEventsPost\n */\n\nimport type { AnalyticsEvent, AnalyticsQueryParams } from './types';\n\n// 服务接口类型\ninterface AnalyticsService {\n insertAnalyticsEvents: (events: AnalyticsEvent[]) => Promise<void>;\n queryAnalyticsEvents: (\n params: AnalyticsQueryParams\n ) => Promise<{ events: AnalyticsEvent[]; total: number }>;\n getAnalyticsStats: (startDate?: string, endDate?: string, platform?: string) => Promise<any>;\n getUserBehavior: (userId: string, startDate?: string, endDate?: string) => Promise<any>;\n getSessionAnalytics: (sessionId: string) => Promise<any>;\n getFunnelAnalysis: (steps: string[], startDate?: string, endDate?: string) => Promise<any>;\n}\n\n// Request/Response 接口(兼容多种框架)\ninterface Request {\n json: () => Promise<any>;\n url?: string;\n nextUrl?: { searchParams: URLSearchParams };\n}\n\ninterface ResponseInit {\n status?: number;\n headers?: Record<string, string>;\n}\n\n/**\n * 创建埋点 API 处理器\n */\nexport function createAnalyticsHandlers(\n service: AnalyticsService,\n ResponseClass?: any // Next.js NextResponse 或其他响应类\n) {\n // 默认使用标准响应格式\n const createResponse = ResponseClass\n ? (data: any, init?: ResponseInit) => ResponseClass.json(data, init)\n : (data: any, init?: ResponseInit) => ({ body: data, status: init?.status || 200 });\n\n return {\n /**\n * POST /api/analytics/events\n * 处理事件上报\n */\n async handleEventsPost(request: Request) {\n try {\n const body = await request.json();\n const { events } = body;\n\n // 验证数据\n if (!Array.isArray(events)) {\n return createResponse(\n { success: false, message: 'Invalid events format' },\n { status: 400 }\n );\n }\n\n if (events.length === 0) {\n return createResponse(\n { success: true, message: 'No events to process' },\n { status: 200 }\n );\n }\n\n // 验证每个事件的必填字段\n for (const event of events) {\n if (!event.event_id || !event.event_type || !event.event_name) {\n return createResponse(\n { success: false, message: 'Missing required event fields' },\n { status: 400 }\n );\n }\n }\n\n // 转换字段名(前端使用 snake_case,数据库使用 camelCase)\n const formattedEvents = events.map((event) => ({\n id: event.event_id,\n eventType: event.event_type,\n eventName: event.event_name,\n timestamp: new Date(event.timestamp).toISOString(),\n priority: event.priority,\n userId: event.user_id || null,\n sessionId: event.session_id,\n deviceId: event.device_id,\n pageUrl: event.page_url || null,\n pageTitle: event.page_title || null,\n referrer: event.referrer || null,\n properties: event.properties || {},\n platform: event.platform,\n appVersion: event.app_version,\n sdkVersion: event.sdk_version,\n }));\n\n // 插入数据库\n await service.insertAnalyticsEvents(formattedEvents);\n\n return createResponse({\n success: true,\n message: 'Events received successfully',\n count: events.length,\n });\n } catch (error) {\n console.error('Failed to process analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/query\n * 查询埋点事件\n */\n async handleQueryGet(request: Request) {\n try {\n // 解析查询参数\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const params: AnalyticsQueryParams = {\n startDate: searchParams.get('startDate') || undefined,\n endDate: searchParams.get('endDate') || undefined,\n eventType: searchParams.get('eventType') || undefined,\n eventTypes: searchParams.get('eventTypes')?.split(',') || undefined,\n userId: searchParams.get('userId') || undefined,\n platform: searchParams.get('platform') || undefined,\n platforms: searchParams.get('platforms')?.split(',') || undefined,\n sessionId: searchParams.get('sessionId') || undefined,\n limit: parseInt(searchParams.get('limit') || '100'),\n offset: parseInt(searchParams.get('offset') || '0'),\n orderBy: (searchParams.get('orderBy') || 'timestamp') as any,\n orderDirection: (searchParams.get('orderDirection') || 'desc') as any,\n };\n\n // 查询数据\n const result = await service.queryAnalyticsEvents(params);\n\n return createResponse({\n success: true,\n data: result.events,\n total: result.total,\n limit: params.limit,\n offset: params.offset,\n });\n } catch (error) {\n console.error('Failed to query analytics events', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/stats\n * 获取统计数据\n */\n async handleStatsGet(request: Request) {\n try {\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n const platform = searchParams.get('platform') || undefined;\n\n // 获取统计数据\n const stats = await service.getAnalyticsStats(startDate, endDate, platform);\n\n return createResponse({\n success: true,\n data: stats,\n });\n } catch (error) {\n console.error('Failed to get analytics stats', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/user/[userId]\n * 获取用户行为分析\n */\n async handleUserBehaviorGet(request: Request, params: { userId: string }) {\n try {\n const { userId } = params;\n const searchParams =\n request.nextUrl?.searchParams || new URLSearchParams(request.url?.split('?')[1]);\n\n const startDate = searchParams.get('startDate') || undefined;\n const endDate = searchParams.get('endDate') || undefined;\n\n // 获取用户行为数据\n const behavior = await service.getUserBehavior(userId, startDate, endDate);\n\n if (!behavior) {\n return createResponse(\n { success: false, message: 'No data found for this user' },\n { status: 404 }\n );\n }\n\n return createResponse({\n success: true,\n data: behavior,\n });\n } catch (error) {\n console.error('Failed to get user behavior', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * GET /api/analytics/session/[sessionId]\n * 获取会话分析\n */\n async handleSessionAnalyticsGet(_request: Request, params: { sessionId: string }) {\n try {\n const { sessionId } = params;\n\n // 获取会话分析数据\n const session = await service.getSessionAnalytics(sessionId);\n\n if (!session) {\n return createResponse({ success: false, message: 'Session not found' }, { status: 404 });\n }\n\n return createResponse({\n success: true,\n data: session,\n });\n } catch (error) {\n console.error('Failed to get session analytics', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n\n /**\n * POST /api/analytics/funnel\n * 漏斗分析\n */\n async handleFunnelPost(request: Request) {\n try {\n const body = await request.json();\n const { steps, startDate, endDate } = body;\n\n // 验证数据\n if (!Array.isArray(steps) || steps.length === 0) {\n return createResponse(\n { success: false, message: 'Invalid steps format' },\n { status: 400 }\n );\n }\n\n // 获取漏斗分析数据\n const funnel = await service.getFunnelAnalysis(steps, startDate, endDate);\n\n return createResponse({\n success: true,\n data: funnel,\n });\n } catch (error) {\n console.error('Failed to get funnel analysis', error);\n\n return createResponse(\n {\n success: false,\n message: 'Internal server error',\n error: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined,\n },\n { status: 500 }\n );\n }\n },\n };\n}\n"]}
|
package/dist/api/index.js
CHANGED
|
@@ -17,9 +17,9 @@ var DEFAULT_API_ROUTES = {
|
|
|
17
17
|
},
|
|
18
18
|
users: {
|
|
19
19
|
list: "/users",
|
|
20
|
-
detail: (id) =>
|
|
21
|
-
update: (id) =>
|
|
22
|
-
delete: (id) =>
|
|
20
|
+
detail: (id) => "/users/" + id,
|
|
21
|
+
update: (id) => "/users/" + id,
|
|
22
|
+
delete: (id) => "/users/" + id
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
|
|
@@ -124,11 +124,11 @@ var BaseApiClient = class {
|
|
|
124
124
|
...config.headers || {}
|
|
125
125
|
};
|
|
126
126
|
if (this.token) {
|
|
127
|
-
headers["Authorization"] =
|
|
127
|
+
headers["Authorization"] = "Bearer " + this.token;
|
|
128
128
|
}
|
|
129
129
|
const response = await this.request.request({
|
|
130
130
|
...config,
|
|
131
|
-
url:
|
|
131
|
+
url: this.baseUrl + config.url,
|
|
132
132
|
headers
|
|
133
133
|
});
|
|
134
134
|
return response;
|