/** * 学习时长追踪服务 * 自动记录用户在各页面的学习时长 */ 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(); } };