mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 03:36:36 +08:00
Add posthog
This commit is contained in:
parent
897458fbe1
commit
2b8be2c869
@ -3,6 +3,7 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import * as amplitude from '@amplitude/analytics-browser'
|
import * as amplitude from '@amplitude/analytics-browser'
|
||||||
|
import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser'
|
||||||
|
|
||||||
export type IAmplitudeProps = {
|
export type IAmplitudeProps = {
|
||||||
apiKey?: string
|
apiKey?: string
|
||||||
@ -18,7 +19,12 @@ const AmplitudeProvider: FC<IAmplitudeProps> = ({
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Initialize Amplitude with proxy configuration to bypass CSP
|
// Create Session Replay plugin instance
|
||||||
|
const sessionReplay = sessionReplayPlugin({
|
||||||
|
sampleRate: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Initialize Amplitude with proxy configuration to bypass CSP and Session Replay
|
||||||
amplitude.init(apiKey, {
|
amplitude.init(apiKey, {
|
||||||
defaultTracking: {
|
defaultTracking: {
|
||||||
sessions: true,
|
sessions: true,
|
||||||
@ -31,10 +37,10 @@ const AmplitudeProvider: FC<IAmplitudeProps> = ({
|
|||||||
// Use Next.js proxy to bypass CSP restrictions
|
// Use Next.js proxy to bypass CSP restrictions
|
||||||
serverUrl: '/api/amplitude/2/httpapi',
|
serverUrl: '/api/amplitude/2/httpapi',
|
||||||
})
|
})
|
||||||
|
amplitude.add(sessionReplay)
|
||||||
// Log initialization success in development
|
// Log initialization success in development
|
||||||
if (process.env.NODE_ENV === 'development')
|
if (process.env.NODE_ENV === 'development')
|
||||||
console.log('[Amplitude] Initialized successfully, API Key:', apiKey)
|
console.log('[Amplitude] Initialized successfully with Session Replay, API Key:', apiKey)
|
||||||
}, [apiKey])
|
}, [apiKey])
|
||||||
|
|
||||||
// This is a client component that renders nothing
|
// This is a client component that renders nothing
|
||||||
|
|||||||
57
web/app/components/posthog-provider.tsx
Normal file
57
web/app/components/posthog-provider.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { usePathname, useSearchParams } from 'next/navigation'
|
||||||
|
import posthog from 'posthog-js'
|
||||||
|
|
||||||
|
const PostHogProvider: FC = () => {
|
||||||
|
const pathname = usePathname()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
posthog.init('phc_Nqsm7RqSX7ZUbH9C47ZRfiUsVBCiLZIrapbWjHAYTBV', {
|
||||||
|
api_host: 'https://us.i.posthog.com',
|
||||||
|
person_profiles: 'identified_only', // 为已识别用户创建 profile 并存储 UTM
|
||||||
|
autocapture: true, // 自动捕获用户交互
|
||||||
|
capture_exceptions: true, // 捕获异常
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化后,确保捕获 UTM 参数
|
||||||
|
// PostHog 会自动将首次访问的 UTM 保存为 Initial UTM
|
||||||
|
if (typeof window !== 'undefined' && window.location.search) {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
const utmParams: Record<string, string> = {}
|
||||||
|
|
||||||
|
// 提取所有 UTM 参数
|
||||||
|
const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
|
||||||
|
utmKeys.forEach((key) => {
|
||||||
|
const value = urlParams.get(key)
|
||||||
|
if (value)
|
||||||
|
utmParams[key] = value
|
||||||
|
})
|
||||||
|
|
||||||
|
// 如果有 UTM 参数,注册为持久属性
|
||||||
|
if (Object.keys(utmParams).length > 0)
|
||||||
|
posthog.register(utmParams)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// 追踪页面浏览
|
||||||
|
useEffect(() => {
|
||||||
|
if (pathname && posthog.__loaded) {
|
||||||
|
let url = window.origin + pathname
|
||||||
|
if (searchParams.toString())
|
||||||
|
url = `${url}?${searchParams.toString()}`
|
||||||
|
|
||||||
|
posthog.capture('$pageview', {
|
||||||
|
$current_url: url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [pathname, searchParams])
|
||||||
|
|
||||||
|
// This is a client component that renders nothing
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(PostHogProvider)
|
||||||
@ -3,6 +3,7 @@ import type { Viewport } from 'next'
|
|||||||
import I18nServer from './components/i18n-server'
|
import I18nServer from './components/i18n-server'
|
||||||
import BrowserInitializer from './components/browser-initializer'
|
import BrowserInitializer from './components/browser-initializer'
|
||||||
import SentryInitializer from './components/sentry-initializer'
|
import SentryInitializer from './components/sentry-initializer'
|
||||||
|
import PostHogProvider from './components/posthog-provider'
|
||||||
import { getLocaleOnServer } from '@/i18n-config/server'
|
import { getLocaleOnServer } from '@/i18n-config/server'
|
||||||
import { TanstackQueryInitializer } from '@/context/query-client'
|
import { TanstackQueryInitializer } from '@/context/query-client'
|
||||||
import { ThemeProvider } from 'next-themes'
|
import { ThemeProvider } from 'next-themes'
|
||||||
@ -93,15 +94,18 @@ const LocaleLayout = async ({
|
|||||||
enableColorScheme={false}
|
enableColorScheme={false}
|
||||||
>
|
>
|
||||||
<BrowserInitializer>
|
<BrowserInitializer>
|
||||||
<SentryInitializer>
|
<>
|
||||||
<TanstackQueryInitializer>
|
<PostHogProvider />
|
||||||
<I18nServer>
|
<SentryInitializer>
|
||||||
<GlobalPublicStoreProvider>
|
<TanstackQueryInitializer>
|
||||||
{children}
|
<I18nServer>
|
||||||
</GlobalPublicStoreProvider>
|
<GlobalPublicStoreProvider>
|
||||||
</I18nServer>
|
{children}
|
||||||
</TanstackQueryInitializer>
|
</GlobalPublicStoreProvider>
|
||||||
</SentryInitializer>
|
</I18nServer>
|
||||||
|
</TanstackQueryInitializer>
|
||||||
|
</SentryInitializer>
|
||||||
|
</>
|
||||||
</BrowserInitializer>
|
</BrowserInitializer>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
<RoutePrefixHandle />
|
<RoutePrefixHandle />
|
||||||
|
|||||||
@ -131,6 +131,8 @@ const nextConfig = {
|
|||||||
'@heroicons/react'
|
'@heroicons/react'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
// This is required to support PostHog trailing slash API requests
|
||||||
|
skipTrailingSlashRedirect: true,
|
||||||
// fix all before production. Now it slow the develop speed.
|
// fix all before production. Now it slow the develop speed.
|
||||||
eslint: {
|
eslint: {
|
||||||
// Warning: This allows production builds to successfully complete even if
|
// Warning: This allows production builds to successfully complete even if
|
||||||
|
|||||||
@ -44,6 +44,7 @@
|
|||||||
"knip": "knip"
|
"knip": "knip"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@amplitude/plugin-session-replay-browser": "^1.23.2",
|
||||||
"@amplitude/unified": "1.0.0-beta.9",
|
"@amplitude/unified": "1.0.0-beta.9",
|
||||||
"@emoji-mart/data": "^1.2.1",
|
"@emoji-mart/data": "^1.2.1",
|
||||||
"@floating-ui/react": "^0.26.28",
|
"@floating-ui/react": "^0.26.28",
|
||||||
@ -106,6 +107,8 @@
|
|||||||
"next-pwa": "^5.6.0",
|
"next-pwa": "^5.6.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"pinyin-pro": "^3.27.0",
|
"pinyin-pro": "^3.27.0",
|
||||||
|
"posthog-js": "^1.288.0",
|
||||||
|
"posthog-node": "^5.11.1",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"qs": "^6.14.0",
|
"qs": "^6.14.0",
|
||||||
"react": "19.1.1",
|
"react": "19.1.1",
|
||||||
@ -131,9 +134,9 @@
|
|||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"scheduler": "^0.26.0",
|
"scheduler": "^0.26.0",
|
||||||
"socket.io-client": "^4.8.1",
|
|
||||||
"semver": "^7.7.3",
|
"semver": "^7.7.3",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
"swr": "^2.3.6",
|
"swr": "^2.3.6",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
|
|||||||
54
web/pnpm-lock.yaml
generated
54
web/pnpm-lock.yaml
generated
@ -60,6 +60,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@amplitude/plugin-session-replay-browser':
|
||||||
|
specifier: ^1.23.2
|
||||||
|
version: 1.23.2(@amplitude/rrweb@2.0.0-alpha.33)(rollup@2.79.2)
|
||||||
'@amplitude/unified':
|
'@amplitude/unified':
|
||||||
specifier: 1.0.0-beta.9
|
specifier: 1.0.0-beta.9
|
||||||
version: 1.0.0-beta.9(@amplitude/rrweb@2.0.0-alpha.33)(rollup@2.79.2)
|
version: 1.0.0-beta.9(@amplitude/rrweb@2.0.0-alpha.33)(rollup@2.79.2)
|
||||||
@ -246,6 +249,12 @@ importers:
|
|||||||
pinyin-pro:
|
pinyin-pro:
|
||||||
specifier: ^3.27.0
|
specifier: ^3.27.0
|
||||||
version: 3.27.0
|
version: 3.27.0
|
||||||
|
posthog-js:
|
||||||
|
specifier: ^1.288.0
|
||||||
|
version: 1.288.0
|
||||||
|
posthog-node:
|
||||||
|
specifier: ^5.11.1
|
||||||
|
version: 5.11.1
|
||||||
qrcode.react:
|
qrcode.react:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0(react@19.1.1)
|
version: 4.2.0(react@19.1.1)
|
||||||
@ -2619,6 +2628,9 @@ packages:
|
|||||||
'@polka/url@1.0.0-next.29':
|
'@polka/url@1.0.0-next.29':
|
||||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||||
|
|
||||||
|
'@posthog/core@1.5.1':
|
||||||
|
resolution: {integrity: sha512-8fdEzfvdStr45iIncTD+gnqp45UBTUpRK/bwB4shP5usCKytnPIeilU8rIpNBOVjJPwfW+2N8yWhQ0l14x191Q==}
|
||||||
|
|
||||||
'@preact/signals-core@1.12.1':
|
'@preact/signals-core@1.12.1':
|
||||||
resolution: {integrity: sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA==}
|
resolution: {integrity: sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA==}
|
||||||
|
|
||||||
@ -4304,6 +4316,9 @@ packages:
|
|||||||
core-js-pure@3.46.0:
|
core-js-pure@3.46.0:
|
||||||
resolution: {integrity: sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw==}
|
resolution: {integrity: sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw==}
|
||||||
|
|
||||||
|
core-js@3.46.0:
|
||||||
|
resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==}
|
||||||
|
|
||||||
core-util-is@1.0.3:
|
core-util-is@1.0.3:
|
||||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||||
|
|
||||||
@ -7064,6 +7079,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
posthog-js@1.288.0:
|
||||||
|
resolution: {integrity: sha512-KOeF8PK/zxBuFB4b3FVkj5JxSWAfSOrfDVvWj5VrJNBGYqr8igDbAl10huFv9NB4/K9XeIWQ7AzPPGV4D3lbEA==}
|
||||||
|
|
||||||
|
posthog-node@5.11.1:
|
||||||
|
resolution: {integrity: sha512-P6rtzdCVvS718r011x0W0cwmJo7gfP5YWXiWh0/S3OL+pnHtcqbWHDjrtRxN/IrMkjZWzMU4xDze5vRK/cZ23w==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
preact@10.27.2:
|
||||||
|
resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==}
|
||||||
|
|
||||||
prebuild-install@7.1.3:
|
prebuild-install@7.1.3:
|
||||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -8387,6 +8412,9 @@ packages:
|
|||||||
web-namespaces@2.0.1:
|
web-namespaces@2.0.1:
|
||||||
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
||||||
|
|
||||||
|
web-vitals@4.2.4:
|
||||||
|
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
|
||||||
|
|
||||||
web-vitals@5.1.0:
|
web-vitals@5.1.0:
|
||||||
resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==}
|
resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==}
|
||||||
|
|
||||||
@ -8752,7 +8780,7 @@ snapshots:
|
|||||||
|
|
||||||
'@amplitude/rrweb-packer@2.0.0-alpha.32':
|
'@amplitude/rrweb-packer@2.0.0-alpha.32':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@amplitude/rrweb-types': 2.0.0-alpha.32
|
'@amplitude/rrweb-types': 2.0.0-alpha.33
|
||||||
fflate: 0.4.8
|
fflate: 0.4.8
|
||||||
|
|
||||||
'@amplitude/rrweb-plugin-console-record@2.0.0-alpha.32(@amplitude/rrweb@2.0.0-alpha.33)':
|
'@amplitude/rrweb-plugin-console-record@2.0.0-alpha.32(@amplitude/rrweb@2.0.0-alpha.33)':
|
||||||
@ -8762,7 +8790,7 @@ snapshots:
|
|||||||
'@amplitude/rrweb-record@2.0.0-alpha.32':
|
'@amplitude/rrweb-record@2.0.0-alpha.32':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@amplitude/rrweb': 2.0.0-alpha.33
|
'@amplitude/rrweb': 2.0.0-alpha.33
|
||||||
'@amplitude/rrweb-types': 2.0.0-alpha.32
|
'@amplitude/rrweb-types': 2.0.0-alpha.33
|
||||||
|
|
||||||
'@amplitude/rrweb-snapshot@2.0.0-alpha.33':
|
'@amplitude/rrweb-snapshot@2.0.0-alpha.33':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11127,6 +11155,10 @@ snapshots:
|
|||||||
|
|
||||||
'@polka/url@1.0.0-next.29': {}
|
'@polka/url@1.0.0-next.29': {}
|
||||||
|
|
||||||
|
'@posthog/core@1.5.1':
|
||||||
|
dependencies:
|
||||||
|
cross-spawn: 7.0.6
|
||||||
|
|
||||||
'@preact/signals-core@1.12.1': {}
|
'@preact/signals-core@1.12.1': {}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.1.3': {}
|
'@radix-ui/primitive@1.1.3': {}
|
||||||
@ -13059,6 +13091,8 @@ snapshots:
|
|||||||
|
|
||||||
core-js-pure@3.46.0: {}
|
core-js-pure@3.46.0: {}
|
||||||
|
|
||||||
|
core-js@3.46.0: {}
|
||||||
|
|
||||||
core-util-is@1.0.3: {}
|
core-util-is@1.0.3: {}
|
||||||
|
|
||||||
cose-base@1.0.3:
|
cose-base@1.0.3:
|
||||||
@ -16627,6 +16661,20 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
posthog-js@1.288.0:
|
||||||
|
dependencies:
|
||||||
|
'@posthog/core': 1.5.1
|
||||||
|
core-js: 3.46.0
|
||||||
|
fflate: 0.4.8
|
||||||
|
preact: 10.27.2
|
||||||
|
web-vitals: 4.2.4
|
||||||
|
|
||||||
|
posthog-node@5.11.1:
|
||||||
|
dependencies:
|
||||||
|
'@posthog/core': 1.5.1
|
||||||
|
|
||||||
|
preact@10.27.2: {}
|
||||||
|
|
||||||
prebuild-install@7.1.3:
|
prebuild-install@7.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
detect-libc: 2.1.2
|
detect-libc: 2.1.2
|
||||||
@ -18108,6 +18156,8 @@ snapshots:
|
|||||||
|
|
||||||
web-namespaces@2.0.1: {}
|
web-namespaces@2.0.1: {}
|
||||||
|
|
||||||
|
web-vitals@4.2.4: {}
|
||||||
|
|
||||||
web-vitals@5.1.0: {}
|
web-vitals@5.1.0: {}
|
||||||
|
|
||||||
webidl-conversions@4.0.2: {}
|
webidl-conversions@4.0.2: {}
|
||||||
|
|||||||
65
web/utils/posthog.ts
Normal file
65
web/utils/posthog.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import posthog from 'posthog-js'
|
||||||
|
import type { UserProfileResponse } from '@/models/common'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 URL 中提取 UTM 参数
|
||||||
|
*/
|
||||||
|
function getUTMParams(): Record<string, string> {
|
||||||
|
const utmParams: Record<string, string> = {}
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
|
||||||
|
const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
|
||||||
|
|
||||||
|
utmKeys.forEach((key) => {
|
||||||
|
const value = urlParams.get(key)
|
||||||
|
if (value)
|
||||||
|
utmParams[key] = value
|
||||||
|
})
|
||||||
|
|
||||||
|
return utmParams
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 识别用户身份并上报到 PostHog
|
||||||
|
* @param userProfile - 完整的用户资料对象
|
||||||
|
*/
|
||||||
|
export function identifyUser(userProfile: UserProfileResponse) {
|
||||||
|
// 检查 PostHog 是否已加载
|
||||||
|
if (!posthog.__loaded)
|
||||||
|
return
|
||||||
|
|
||||||
|
// 检查是否有有效的用户 ID
|
||||||
|
if (!userProfile?.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const properties: Record<string, any> = {}
|
||||||
|
|
||||||
|
// 用户基本信息
|
||||||
|
if (userProfile.email)
|
||||||
|
properties.email = userProfile.email
|
||||||
|
if (userProfile.name)
|
||||||
|
properties.name = userProfile.name
|
||||||
|
if (userProfile.created_at)
|
||||||
|
properties.created_at = userProfile.created_at
|
||||||
|
if (userProfile.interface_language)
|
||||||
|
properties.interface_language = userProfile.interface_language
|
||||||
|
if (userProfile.interface_theme)
|
||||||
|
properties.interface_theme = userProfile.interface_theme
|
||||||
|
if (userProfile.timezone)
|
||||||
|
properties.timezone = userProfile.timezone
|
||||||
|
|
||||||
|
// 添加当前 URL 的 UTM 参数(如果有)
|
||||||
|
const utmParams = getUTMParams()
|
||||||
|
if (Object.keys(utmParams).length > 0) {
|
||||||
|
Object.entries(utmParams).forEach(([key, value]) => {
|
||||||
|
properties[key] = value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
posthog.identify(userProfile.id, properties)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('[PostHog] identify 失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user