hookehuyr

feat(埋点): 添加用户行为追踪功能

在CompleteInfoPage和PosterPage中添加用户行为埋点
新增trackingAPI接口用于发送埋点数据
移除login页面中旧的埋点逻辑
1 /* 1 /*
2 * @Date: 2025-12-19 10:43:09 2 * @Date: 2025-12-19 10:43:09
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-26 11:44:09 4 + * @LastEditTime: 2025-12-26 16:00:49
5 * @FilePath: /mlaj/src/api/recall_users.js 5 * @FilePath: /mlaj/src/api/recall_users.js
6 * @Description: 引入外部接口, 召回旧用户相关接口 6 * @Description: 引入外部接口, 召回旧用户相关接口
7 */ 7 */
8 import { fn, fetch } from './fn'; 8 import { fn, fetch } from './fn';
9 9
10 const Api = { 10 const Api = {
11 - USER_LOGIN: '/srv/?a=user_login', 11 + USER_LOGIN: '/srv/?a=desk_calendar&t=login',
12 USER_INFO: '/srv/?a=desk_calendar&t=user_info', 12 USER_INFO: '/srv/?a=desk_calendar&t=user_info',
13 USER_SEARCH_OLD_ACTIVITY: '/srv/?a=desk_calendar&t=search_old_activity', 13 USER_SEARCH_OLD_ACTIVITY: '/srv/?a=desk_calendar&t=search_old_activity',
14 USER_GET_POSTER: '/srv/?a=desk_calendar&t=get_poster', 14 USER_GET_POSTER: '/srv/?a=desk_calendar&t=get_poster',
15 USER_EDIT_POSTER: '/srv/?a=desk_calendar&t=edit_poster', 15 USER_EDIT_POSTER: '/srv/?a=desk_calendar&t=edit_poster',
16 USER_GET_SUPPLEMENT: '/srv/?a=desk_calendar&t=get_supplement', 16 USER_GET_SUPPLEMENT: '/srv/?a=desk_calendar&t=get_supplement',
17 USER_EDIT_SUPPLEMENT: '/srv/?a=desk_calendar&t=edit_supplement', 17 USER_EDIT_SUPPLEMENT: '/srv/?a=desk_calendar&t=edit_supplement',
18 + USER_TRACKING: '/srv/?a=desk_calendar&t=tracking',
18 } 19 }
19 20
20 /** 21 /**
21 * @description: 用户登录 22 * @description: 用户登录
22 * @param: mobile 手机号 23 * @param: mobile 手机号
23 - * @param: password 用户密码 24 + * @param: sms_code 短信验证码
25 + * @param: entry 进入H5的入口。qrcode=扫二维码进入, poster=通过海报进入
26 + * @param: referrer_user_id 海报创建人ID
24 */ 27 */
25 export const loginAPI = (params) => fn(fetch.post(Api.USER_LOGIN, params)); 28 export const loginAPI = (params) => fn(fetch.post(Api.USER_LOGIN, params));
26 29
...@@ -69,3 +72,11 @@ export const getSupplementAPI = (params) => fn(fetch.get(Api.USER_GET_SUPPLEMENT ...@@ -69,3 +72,11 @@ export const getSupplementAPI = (params) => fn(fetch.get(Api.USER_GET_SUPPLEMENT
69 * @param: note 补充说明 72 * @param: note 补充说明
70 */ 73 */
71 export const editSupplementAPI = (params) => fn(fetch.post(Api.USER_EDIT_SUPPLEMENT, params)); 74 export const editSupplementAPI = (params) => fn(fetch.post(Api.USER_EDIT_SUPPLEMENT, params));
75 +
76 +/**
77 + * @description: 埋点
78 + * @param: event_type 事件类型。edit_user=完善用户信息, share_poster=转发海报
79 + * @param: campaign_id 活动ID(转发海报时填写)
80 + * @param: stu_uid 学员ID(转发海报时填写)
81 + */
82 +export const trackingAPI = (params) => fn(fetch.post(Api.USER_TRACKING, params));
......
...@@ -83,7 +83,7 @@ import { useRoute, useRouter } from 'vue-router' ...@@ -83,7 +83,7 @@ import { useRoute, useRouter } from 'vue-router'
83 import { useTitle } from '@vueuse/core' 83 import { useTitle } from '@vueuse/core'
84 import { showToast } from 'vant' 84 import { showToast } from 'vant'
85 import { updateUserInfoAPI } from '@/api/users' 85 import { updateUserInfoAPI } from '@/api/users'
86 -import { searchOldActivityAPI } from '@/api/recall_users' 86 +import { searchOldActivityAPI, trackingAPI } from '@/api/recall_users'
87 87
88 const starImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/xing@2x.png' 88 const starImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/xing@2x.png'
89 89
...@@ -127,6 +127,10 @@ const handleConfirm = async () => { ...@@ -127,6 +127,10 @@ const handleConfirm = async () => {
127 idcard: form.idCard 127 idcard: form.idCard
128 }) 128 })
129 if (res.code) { 129 if (res.code) {
130 + // 埋点
131 + trackingAPI({
132 + event_type: 'edit_user'
133 + })
130 const activityRes = await searchOldActivityAPI({ 134 const activityRes = await searchOldActivityAPI({
131 name: form.name, 135 name: form.name,
132 mobile: form.phone, 136 mobile: form.phone,
......
1 <!-- 1 <!--
2 * @Date: 2025-12-23 15:50:59 2 * @Date: 2025-12-23 15:50:59
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-25 13:22:42 4 + * @LastEditTime: 2025-12-26 16:06:36
5 * @FilePath: /mlaj/src/views/recall/PosterPage.vue 5 * @FilePath: /mlaj/src/views/recall/PosterPage.vue
6 * @Description: 分享海报页面 6 * @Description: 分享海报页面
7 --> 7 -->
...@@ -46,7 +46,7 @@ import { showToast, showLoadingToast } from 'vant' ...@@ -46,7 +46,7 @@ import { showToast, showLoadingToast } from 'vant'
46 import { qiniuFileHash } from '@/utils/qiniuFileHash' 46 import { qiniuFileHash } from '@/utils/qiniuFileHash'
47 import { useAuth } from '@/contexts/auth' 47 import { useAuth } from '@/contexts/auth'
48 48
49 -import { getPosterAPI, editPosterAPI } from '@/api/recall_users' 49 +import { getPosterAPI, editPosterAPI, trackingAPI } from '@/api/recall_users'
50 50
51 const $route = useRoute(); 51 const $route = useRoute();
52 const $router = useRouter(); 52 const $router = useRouter();
...@@ -167,6 +167,13 @@ const campaign_id = ref($route.query.campaign_id || '') ...@@ -167,6 +167,13 @@ const campaign_id = ref($route.query.campaign_id || '')
167 const title = ref($route.query.title || '活动海报') 167 const title = ref($route.query.title || '活动海报')
168 168
169 onMounted(async () => { 169 onMounted(async () => {
170 + // 埋点
171 + trackingAPI({
172 + event_type: 'share_poster',
173 + campaign_id: campaign_id.value,
174 + stu_uid: stu_uid.value
175 + })
176 +
170 if (stu_uid.value && campaign_id.value) { 177 if (stu_uid.value && campaign_id.value) {
171 const { code, data } = await getPosterAPI({ 178 const { code, data } = await getPosterAPI({
172 stu_uid: stu_uid.value, 179 stu_uid: stu_uid.value,
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
50 点击“下一步”,即表示您同意我们的 50 点击“下一步”,即表示您同意我们的
51 <br /> 51 <br />
52 <span class="text-[#FFDD01] cursor-pointer hover:opacity-80 transition-opacity" 52 <span class="text-[#FFDD01] cursor-pointer hover:opacity-80 transition-opacity"
53 - @click="showAgreement = true; trackClick('view_agreement_link')">《美乐爱觉宇宙用户协议》</span> 53 + @click="showAgreement = true">《美乐爱觉宇宙用户协议》</span>
54 </div> 54 </div>
55 </FrostedGlass> 55 </FrostedGlass>
56 </div> 56 </div>
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
74 </div> 74 </div>
75 <div class="p-4 border-t border-gray-100 bg-white safe-area-bottom"> 75 <div class="p-4 border-t border-gray-100 bg-white safe-area-bottom">
76 <van-button block round type="primary" color="linear-gradient(to right, #FFDD01, #E5C600)" 76 <van-button block round type="primary" color="linear-gradient(to right, #FFDD01, #E5C600)"
77 - class="!text-white !font-bold !h-[44px]" @click="showAgreement = false; trackClick('agreement_confirm_btn')"> 77 + class="!text-white !font-bold !h-[44px]" @click="showAgreement = false">
78 我已阅读并同意 78 我已阅读并同意
79 </van-button> 79 </van-button>
80 </div> 80 </div>
...@@ -94,7 +94,7 @@ import VideoBackground from '@/components/ui/VideoBackground.vue' ...@@ -94,7 +94,7 @@ import VideoBackground from '@/components/ui/VideoBackground.vue'
94 // 导入接口 94 // 导入接口
95 import { smsAPI } from '@/api/common' 95 import { smsAPI } from '@/api/common'
96 import { loginAPI, userInfoAPI } from '@/api/recall_users' 96 import { loginAPI, userInfoAPI } from '@/api/recall_users'
97 -import { useTracking } from '@/composables/useTracking' 97 +
98 // 导入图片 98 // 导入图片
99 const titleImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/title01@2x.png' 99 const titleImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/title01@2x.png'
100 100
...@@ -103,18 +103,15 @@ const $route = useRoute() ...@@ -103,18 +103,15 @@ const $route = useRoute()
103 const $router = useRouter() 103 const $router = useRouter()
104 useTitle('登陆') 104 useTitle('登陆')
105 105
106 -// TAG: 埋点
107 -const { trackPageView, trackClick } = useTracking()
108 -
109 onMounted(() => { 106 onMounted(() => {
110 - // TAG: 埋点, 登录页面加载
111 - trackPageView('login_page')
112 }) 107 })
113 108
114 // Form Data 109 // Form Data
115 const phone = ref('') 110 const phone = ref('')
116 const code = ref('') 111 const code = ref('')
117 const showAgreement = ref(false) 112 const showAgreement = ref(false)
113 +const entry = ref($route.query.entry || '')
114 +const referrer_user_id = ref($route.query.referrer_user_id || '')
118 115
119 // Countdown Logic 116 // Countdown Logic
120 const counting = ref(false) 117 const counting = ref(false)
...@@ -125,9 +122,6 @@ let timer = null ...@@ -125,9 +122,6 @@ let timer = null
125 * @description 发送验证码 122 * @description 发送验证码
126 */ 123 */
127 const handleSendCode = async () => { 124 const handleSendCode = async () => {
128 - // TAG: 埋点, 获取验证码按钮点击
129 - trackClick('send_code_btn', { phone: phone.value })
130 -
131 if (!phone.value) { 125 if (!phone.value) {
132 showToast('请输入手机号') 126 showToast('请输入手机号')
133 return 127 return
...@@ -166,9 +160,6 @@ const handleSendCode = async () => { ...@@ -166,9 +160,6 @@ const handleSendCode = async () => {
166 * @description 登录/下一步 160 * @description 登录/下一步
167 */ 161 */
168 const handleLogin = async () => { 162 const handleLogin = async () => {
169 - // TAG: 埋点, 登录/下一步按钮点击
170 - trackClick('login_next_btn', { phone: phone.value })
171 -
172 if (!phone.value) { 163 if (!phone.value) {
173 showToast('请输入手机号') 164 showToast('请输入手机号')
174 return 165 return
...@@ -179,7 +170,7 @@ const handleLogin = async () => { ...@@ -179,7 +170,7 @@ const handleLogin = async () => {
179 } 170 }
180 171
181 try { 172 try {
182 - const res = await loginAPI({ mobile: phone.value, sms_code: code.value }) 173 + const res = await loginAPI({ mobile: phone.value, sms_code: code.value, entry: entry.value, referrer_user_id: referrer_user_id.value })
183 if (res.code) { 174 if (res.code) {
184 // 获取data里面的 user_id, HTTP_USER_TOKEN, 并设置到后面所有的请求头里面,headers.User-Id, headers.User-Token 175 // 获取data里面的 user_id, HTTP_USER_TOKEN, 并设置到后面所有的请求头里面,headers.User-Id, headers.User-Token
185 const { user_id, HTTP_USER_TOKEN } = res?.data?.user_info || {}; 176 const { user_id, HTTP_USER_TOKEN } = res?.data?.user_info || {};
......