279 lines
7.2 KiB
JavaScript
279 lines
7.2 KiB
JavaScript
/**
|
||
* 学习时长追踪服务
|
||
* 自动记录用户在各页面的学习时长
|
||
*/
|
||
|
||
class LearningTimeTracker {
|
||
constructor() {
|
||
this.currentPage = null;
|
||
this.startTime = null;
|
||
this.sessionData = {};
|
||
}
|
||
|
||
/**
|
||
* 开始追踪页面
|
||
* @param {String} pageName - 页面名称
|
||
*/
|
||
startTracking(pageName) {
|
||
// 如果之前有页面在追踪,先结束它
|
||
if (this.currentPage && this.startTime) {
|
||
this.endTracking();
|
||
}
|
||
|
||
this.currentPage = pageName;
|
||
this.startTime = Date.now();
|
||
|
||
console.log(`[学习追踪] 开始: ${pageName}`);
|
||
}
|
||
|
||
/**
|
||
* 结束追踪
|
||
*/
|
||
endTracking() {
|
||
if (!this.currentPage || !this.startTime) {
|
||
return;
|
||
}
|
||
|
||
const endTime = Date.now();
|
||
const duration = Math.floor((endTime - this.startTime) / 1000); // 秒
|
||
|
||
// 只记录超过5秒的有效学习时长(过滤快速切换)
|
||
if (duration >= 5) {
|
||
this.recordDuration(this.currentPage, duration);
|
||
console.log(`[学习追踪] 结束: ${this.currentPage}, 时长: ${duration}秒`);
|
||
}
|
||
|
||
this.currentPage = null;
|
||
this.startTime = null;
|
||
}
|
||
|
||
/**
|
||
* 记录学习时长
|
||
* @param {String} pageName - 页面名称
|
||
* @param {Number} duration - 时长(秒)
|
||
*/
|
||
recordDuration(pageName, duration) {
|
||
const today = this.getTodayString();
|
||
const now = new Date();
|
||
|
||
// 1. 更新总学习数据
|
||
const learningData = wx.getStorageSync('learning_data') || {
|
||
totalHours: 0,
|
||
totalSeconds: 0,
|
||
dailyRecords: []
|
||
};
|
||
|
||
learningData.totalSeconds = (learningData.totalSeconds || 0) + duration;
|
||
learningData.totalHours = parseFloat((learningData.totalSeconds / 3600).toFixed(1));
|
||
|
||
// 更新每日记录
|
||
let todayRecord = learningData.dailyRecords.find(r => r.date === today);
|
||
if (!todayRecord) {
|
||
todayRecord = {
|
||
date: today,
|
||
seconds: 0,
|
||
hours: 0,
|
||
sessions: []
|
||
};
|
||
learningData.dailyRecords.push(todayRecord);
|
||
}
|
||
|
||
todayRecord.seconds += duration;
|
||
todayRecord.hours = parseFloat((todayRecord.seconds / 3600).toFixed(2));
|
||
todayRecord.sessions.push({
|
||
page: pageName,
|
||
duration: duration,
|
||
timestamp: now.getTime()
|
||
});
|
||
|
||
// 只保留最近365天的记录
|
||
if (learningData.dailyRecords.length > 365) {
|
||
learningData.dailyRecords = learningData.dailyRecords
|
||
.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||
.slice(0, 365);
|
||
}
|
||
|
||
wx.setStorageSync('learning_data', learningData);
|
||
|
||
// 2. 更新每日活跃度(用于热力图)
|
||
const dailyActivity = wx.getStorageSync('daily_activity') || {};
|
||
dailyActivity[today] = (dailyActivity[today] || 0) + Math.floor(duration / 60); // 转为分钟
|
||
wx.setStorageSync('daily_activity', dailyActivity);
|
||
|
||
// 3. 更新模块使用时长
|
||
const moduleMapping = {
|
||
'courses': 'course',
|
||
'course-detail': 'course',
|
||
'forum': 'forum',
|
||
'forum-detail': 'forum',
|
||
'post': 'forum',
|
||
'gpa': 'tools',
|
||
'schedule': 'tools',
|
||
'countdown': 'tools',
|
||
'tools': 'tools',
|
||
'ai-assistant': 'ai',
|
||
'dashboard': 'tools'
|
||
};
|
||
|
||
const module = moduleMapping[pageName] || 'other';
|
||
const moduleUsage = wx.getStorageSync('module_usage') || {
|
||
course: 0,
|
||
forum: 0,
|
||
tools: 0,
|
||
ai: 0
|
||
};
|
||
|
||
moduleUsage[module] = (moduleUsage[module] || 0) + parseFloat((duration / 3600).toFixed(2));
|
||
wx.setStorageSync('module_usage', moduleUsage);
|
||
|
||
// 4. 更新学习画像
|
||
this.updateLearningProfile(pageName, duration);
|
||
}
|
||
|
||
/**
|
||
* 更新学习画像
|
||
*/
|
||
updateLearningProfile(pageName, duration) {
|
||
const profile = wx.getStorageSync('learning_profile') || {
|
||
focus: 0, // 专注度
|
||
activity: 0, // 活跃度
|
||
duration: 0, // 学习时长
|
||
breadth: 0, // 知识广度
|
||
interaction: 0, // 互动性
|
||
persistence: 0 // 坚持度
|
||
};
|
||
|
||
// 专注度:基于单次学习时长(超过30分钟+10,超过1小时+20)
|
||
if (duration >= 3600) {
|
||
profile.focus = Math.min(100, profile.focus + 2);
|
||
} else if (duration >= 1800) {
|
||
profile.focus = Math.min(100, profile.focus + 1);
|
||
}
|
||
|
||
// 活跃度:每次学习+1
|
||
profile.activity = Math.min(100, profile.activity + 0.5);
|
||
|
||
// 学习时长:每小时+5
|
||
profile.duration = Math.min(100, profile.duration + (duration / 3600) * 5);
|
||
|
||
// 知识广度:访问课程相关页面+1
|
||
if (pageName.includes('course')) {
|
||
profile.breadth = Math.min(100, profile.breadth + 0.3);
|
||
}
|
||
|
||
// 互动性:论坛相关+2
|
||
if (pageName.includes('forum') || pageName.includes('post')) {
|
||
profile.interaction = Math.min(100, profile.interaction + 1);
|
||
}
|
||
|
||
// 坚持度:每天学习+3
|
||
const learningData = wx.getStorageSync('learning_data') || {};
|
||
const continuousDays = this.calculateContinuousDays(learningData.dailyRecords || []);
|
||
profile.persistence = Math.min(100, continuousDays * 3);
|
||
|
||
wx.setStorageSync('learning_profile', profile);
|
||
}
|
||
|
||
/**
|
||
* 计算连续学习天数
|
||
*/
|
||
calculateContinuousDays(records) {
|
||
if (!records || records.length === 0) return 0;
|
||
|
||
const sortedRecords = records.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||
let continuousDays = 0;
|
||
const today = new Date();
|
||
today.setHours(0, 0, 0, 0);
|
||
|
||
for (let i = 0; i < sortedRecords.length; i++) {
|
||
const recordDate = new Date(sortedRecords[i].date);
|
||
recordDate.setHours(0, 0, 0, 0);
|
||
const daysDiff = Math.floor((today - recordDate) / (1000 * 60 * 60 * 24));
|
||
|
||
if (daysDiff === i) {
|
||
continuousDays++;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return continuousDays;
|
||
}
|
||
|
||
/**
|
||
* 获取今天的日期字符串
|
||
*/
|
||
getTodayString() {
|
||
const now = new Date();
|
||
const year = now.getFullYear();
|
||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||
const day = String(now.getDate()).padStart(2, '0');
|
||
return `${year}-${month}-${day}`;
|
||
}
|
||
|
||
/**
|
||
* 获取学习统计
|
||
*/
|
||
getStats() {
|
||
const learningData = wx.getStorageSync('learning_data') || {
|
||
totalHours: 0,
|
||
totalSeconds: 0,
|
||
dailyRecords: []
|
||
};
|
||
|
||
const continuousDays = this.calculateContinuousDays(learningData.dailyRecords);
|
||
|
||
return {
|
||
totalHours: learningData.totalHours || 0,
|
||
totalSeconds: learningData.totalSeconds || 0,
|
||
continuousDays: continuousDays,
|
||
todayHours: this.getTodayHours()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取今天的学习时长
|
||
*/
|
||
getTodayHours() {
|
||
const today = this.getTodayString();
|
||
const learningData = wx.getStorageSync('learning_data') || { dailyRecords: [] };
|
||
const todayRecord = learningData.dailyRecords.find(r => r.date === today);
|
||
return todayRecord ? todayRecord.hours : 0;
|
||
}
|
||
}
|
||
|
||
// 创建全局实例
|
||
const tracker = new LearningTimeTracker();
|
||
|
||
module.exports = {
|
||
tracker,
|
||
|
||
/**
|
||
* 页面onShow时调用
|
||
*/
|
||
onPageShow(pageName) {
|
||
tracker.startTracking(pageName);
|
||
},
|
||
|
||
/**
|
||
* 页面onHide时调用
|
||
*/
|
||
onPageHide() {
|
||
tracker.endTracking();
|
||
},
|
||
|
||
/**
|
||
* 页面onUnload时调用
|
||
*/
|
||
onPageUnload() {
|
||
tracker.endTracking();
|
||
},
|
||
|
||
/**
|
||
* 获取统计数据
|
||
*/
|
||
getStats() {
|
||
return tracker.getStats();
|
||
}
|
||
};
|