Files
ZhiQiXiaoYuan/utils/analytics.js
ChuXun eaab9a762a 1
2025-10-19 20:28:31 +08:00

351 lines
7.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 数据统计和分析工具
*/
const { Storage } = require('./storage.js')
const { logger } = require('./logger.js')
class Analytics {
/**
* 获取学习统计数据
*/
static getStudyStats() {
const gpaCourses = Storage.get('gpaCourses', [])
const countdowns = Storage.get('countdowns', [])
const favoriteCourses = Storage.get('favoriteCourses', [])
const stats = {
// GPA统计
gpa: {
total: gpaCourses.length,
average: this._calculateAverageGPA(gpaCourses),
highest: this._getHighestScore(gpaCourses),
lowest: this._getLowestScore(gpaCourses),
trend: this._calculateGPATrend(gpaCourses)
},
// 课程统计
courses: {
total: favoriteCourses.length,
byCategory: this._groupByCategory(gpaCourses),
completionRate: this._calculateCompletionRate(gpaCourses)
},
// 考试倒计时
exams: {
total: countdowns.length,
upcoming: countdowns.filter(c => c.days > 0).length,
thisWeek: countdowns.filter(c => c.days <= 7 && c.days > 0).length
},
// 学习时长统计(模拟数据)
studyTime: {
today: 0,
week: 0,
month: 0
}
}
return stats
}
/**
* 计算平均GPA
*/
static _calculateAverageGPA(courses) {
if (courses.length === 0) return 0
let totalPoints = 0
let totalCredits = 0
courses.forEach(course => {
const gradePoint = this._scoreToGradePoint(course.score)
totalPoints += gradePoint * course.credit
totalCredits += course.credit
})
return totalCredits > 0 ? (totalPoints / totalCredits).toFixed(2) : 0
}
/**
* 分数转绩点
*/
static _scoreToGradePoint(score) {
if (score >= 60) {
return parseFloat((score / 10 - 5).toFixed(2))
}
return 0
}
/**
* 获取最高分
*/
static _getHighestScore(courses) {
if (courses.length === 0) return 0
return Math.max(...courses.map(c => c.score))
}
/**
* 获取最低分
*/
static _getLowestScore(courses) {
if (courses.length === 0) return 0
return Math.min(...courses.map(c => c.score))
}
/**
* 计算GPA趋势
*/
static _calculateGPATrend(courses) {
if (courses.length < 2) return []
// 按时间排序假设ID越大时间越新
const sorted = [...courses].sort((a, b) => a.id - b.id)
const trend = []
let runningTotal = 0
let runningCredits = 0
sorted.forEach(course => {
const gradePoint = this._scoreToGradePoint(course.score)
runningTotal += gradePoint * course.credit
runningCredits += course.credit
const gpa = runningCredits > 0 ? (runningTotal / runningCredits).toFixed(2) : 0
trend.push({
name: course.name,
gpa: parseFloat(gpa),
score: course.score
})
})
return trend
}
/**
* 按分类分组
*/
static _groupByCategory(courses) {
const grouped = {}
courses.forEach(course => {
const category = course.category || '其他'
if (!grouped[category]) {
grouped[category] = {
count: 0,
totalScore: 0,
avgScore: 0
}
}
grouped[category].count++
grouped[category].totalScore += course.score
})
// 计算平均分
Object.keys(grouped).forEach(key => {
grouped[key].avgScore = (grouped[key].totalScore / grouped[key].count).toFixed(1)
})
return grouped
}
/**
* 计算完成率
*/
static _calculateCompletionRate(courses) {
if (courses.length === 0) return 0
const passed = courses.filter(c => c.score >= 60).length
return ((passed / courses.length) * 100).toFixed(1)
}
/**
* 生成学习报告
*/
static generateReport() {
const stats = this.getStudyStats()
const report = {
summary: {
gpa: stats.gpa.average,
totalCourses: stats.courses.total,
passRate: stats.courses.completionRate
},
highlights: [],
suggestions: []
}
// 添加亮点
if (parseFloat(stats.gpa.average) >= 3.5) {
report.highlights.push('GPA优秀继续保持')
}
if (parseFloat(stats.courses.completionRate) === 100) {
report.highlights.push('所有课程全部通过,非常棒!')
}
// 添加建议
if (parseFloat(stats.gpa.average) < 2.5) {
report.suggestions.push('建议加强学习提高GPA')
}
if (stats.exams.thisWeek > 0) {
report.suggestions.push(`本周有${stats.exams.thisWeek}场考试,请做好准备`)
}
logger.info('生成学习报告', report)
return report
}
/**
* 导出数据
*/
static exportData() {
const data = {
gpaCourses: Storage.get('gpaCourses', []),
favoriteCourses: Storage.get('favoriteCourses', []),
schedule: Storage.get('schedule', {}),
countdowns: Storage.get('countdowns', []),
exportTime: new Date().toISOString()
}
logger.info('导出数据', { recordCount: data.gpaCourses.length })
return data
}
/**
* 导入数据
*/
static importData(data) {
try {
if (data.gpaCourses) {
Storage.set('gpaCourses', data.gpaCourses)
}
if (data.favoriteCourses) {
Storage.set('favoriteCourses', data.favoriteCourses)
}
if (data.schedule) {
Storage.set('schedule', data.schedule)
}
if (data.countdowns) {
Storage.set('countdowns', data.countdowns)
}
logger.info('导入数据成功', { recordCount: data.gpaCourses?.length || 0 })
return { success: true }
} catch (error) {
logger.error('导入数据失败', error)
return { success: false, error }
}
}
}
/**
* 智能推荐系统
*/
class RecommendationEngine {
/**
* 推荐课程
*/
static recommendCourses(allCourses, userCourses) {
const recommendations = []
// 基于用户已选课程推荐相关课程
const userCategories = [...new Set(userCourses.map(c => c.category))]
allCourses.forEach(course => {
let score = 0
// 相同分类加分
if (userCategories.includes(course.category)) {
score += 3
}
// 热门课程加分
if (course.enrolled / course.capacity > 0.8) {
score += 2
}
// 高评分课程加分
if (course.rating && course.rating >= 4.5) {
score += 2
}
// 还有名额的课程加分
if (course.enrolled < course.capacity) {
score += 1
}
if (score > 0) {
recommendations.push({
course,
score,
reason: this._getRecommendReason(score)
})
}
})
// 按分数排序
recommendations.sort((a, b) => b.score - a.score)
return recommendations.slice(0, 5)
}
/**
* 获取推荐理由
*/
static _getRecommendReason(score) {
if (score >= 6) return '强烈推荐'
if (score >= 4) return '推荐'
return '可以考虑'
}
/**
* 学习建议
*/
static getStudySuggestions(stats) {
const suggestions = []
// GPA建议
if (parseFloat(stats.gpa.average) < 2.0) {
suggestions.push({
type: 'gpa',
level: 'warning',
message: '当前GPA较低建议加强学习',
action: '查看学习计划'
})
} else if (parseFloat(stats.gpa.average) >= 3.5) {
suggestions.push({
type: 'gpa',
level: 'success',
message: 'GPA优秀继续保持',
action: '分享经验'
})
}
// 考试提醒
if (stats.exams.thisWeek > 0) {
suggestions.push({
type: 'exam',
level: 'info',
message: `本周有${stats.exams.thisWeek}场考试`,
action: '查看倒计时'
})
}
return suggestions
}
}
module.exports = {
Analytics,
RecommendationEngine
}