This commit is contained in:
ChuXun
2025-10-19 20:28:31 +08:00
parent c81f8a8b03
commit eaab9a762a
100 changed files with 23416 additions and 0 deletions

350
utils/analytics.js Normal file
View File

@@ -0,0 +1,350 @@
/**
* 数据统计和分析工具
*/
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
}