useStudyRecordTracker.js
4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { ref, onUnmounted } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import { addStudyRecordAPI } from '@/api/record';
/**
* 学习记录埋点跟踪器
* @module useStudyRecordTracker
* @description
* 自动追踪用户的学习行为(视频播放、音频播放),定期上报学习进度。
*
* @param {Object} params - 参数对象
* @param {import('vue').Ref<Object>} params.course - 课程对象
* @param {import('vue').Ref<string|number>} params.courseId - 课程ID
* @param {import('vue').Ref<Object>} params.videoPlayerRef - 视频播放器组件引用
* @param {import('vue').Ref<Object>} params.audioPlayerRef - 音频播放器组件引用
* @returns {Object} 包含开始/结束追踪和手动添加记录的方法
*/
export const useStudyRecordTracker = ({ course, courseId, videoPlayerRef, audioPlayerRef }) => {
/** @type {import('vue').Ref<number|null>} 定时器ID */
const action_timer = ref(null);
/** @type {import('vue').Ref<string>} 当前播放会话ID (UUID) */
const playback_id = ref('');
/**
* 清除定时器
*/
const clear_timer = () => {
if (action_timer.value) {
clearInterval(action_timer.value);
action_timer.value = null;
}
};
/**
* 手动上报学习记录
* @param {Object} paramsObj - 上报参数
* @returns {Promise<Object>} API响应
*/
const addRecord = async (paramsObj) => {
if (!paramsObj || !paramsObj.schedule_id) return;
return await addStudyRecordAPI(paramsObj);
};
/**
* 获取规范化的课程ID
* @returns {string|number}
*/
const get_schedule_id = () => {
if (typeof courseId === 'object' && courseId && 'value' in courseId) return courseId.value;
return courseId || '';
};
/**
* 获取视频播放状态载荷
* @description 从 videoPlayerRef 获取当前播放时间、总时长和 meta_id
* @returns {Object|null}
*/
const get_video_payload = () => {
const player = videoPlayerRef?.value?.getPlayer?.();
if (!player) return null;
const duration = typeof player.duration === 'function' ? player.duration() : (player.duration || 0);
const position = typeof player.currentTime === 'function' ? player.currentTime() : (player.currentTime || 0);
const meta_id = videoPlayerRef?.value?.getId?.();
if (!meta_id) return null;
return {
meta_id,
media_duration: duration,
playback_position: position,
playback_id: playback_id.value,
};
};
/**
* 获取音频播放状态载荷
* @param {Object} item - 音频项数据
* @returns {Object|null}
*/
const get_audio_payload = (item) => {
const player = audioPlayerRef?.value?.getPlayer?.();
if (!player) return null;
const meta_id = item?.meta_id;
if (!meta_id) return null;
return {
meta_id,
media_duration: player.duration || 0,
playback_position: player.currentTime || 0,
playback_id: playback_id.value,
};
};
/**
* 开始追踪
* @param {Object} [item] - 当前播放项(用于音频)
* @description
* 1. 生成新的 playback_id
* 2. 启动定时器 (3秒间隔)
* 3. 根据课程类型 (video/audio) 获取对应的播放状态
* 4. 调用 addRecord 上报
*/
const startAction = (item) => {
clear_timer();
const schedule_id = get_schedule_id();
if (!schedule_id) return;
playback_id.value = uuidv4();
action_timer.value = setInterval(() => {
const is_video = course?.value?.course_type === 'video';
const is_audio = course?.value?.course_type === 'audio';
let payload = null;
if (is_video) payload = get_video_payload();
if (is_audio) payload = get_audio_payload(item);
// 兜底尝试
if (!payload) payload = get_video_payload() || get_audio_payload(item);
if (!payload) return;
addRecord({
schedule_id,
...payload,
});
}, 3000);
};
/**
* 结束追踪
*/
const endAction = () => {
clear_timer();
};
// 组件卸载时自动清理
onUnmounted(() => {
clear_timer();
});
return {
startAction,
endAction,
addRecord,
};
};