558 lines
16 KiB
JavaScript
558 lines
16 KiB
JavaScript
/**
|
||
* API服务类 - 与后端交互
|
||
*/
|
||
class ApiService {
|
||
constructor() {
|
||
this.baseUrl = 'http://localhost:8080/api';
|
||
this.currentUser = null;
|
||
}
|
||
|
||
/**
|
||
* 设置当前用户
|
||
*/
|
||
setCurrentUser(user) {
|
||
this.currentUser = user;
|
||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||
}
|
||
|
||
/**
|
||
* 获取当前用户
|
||
*/
|
||
getCurrentUser() {
|
||
if (!this.currentUser) {
|
||
const userStr = localStorage.getItem('currentUser');
|
||
if (userStr) {
|
||
this.currentUser = JSON.parse(userStr);
|
||
}
|
||
}
|
||
return this.currentUser;
|
||
}
|
||
|
||
/**
|
||
* 清除当前用户
|
||
*/
|
||
clearCurrentUser() {
|
||
this.currentUser = null;
|
||
localStorage.removeItem('currentUser');
|
||
}
|
||
|
||
/**
|
||
* 通用请求方法
|
||
*/
|
||
async request(url, options = {}) {
|
||
const defaultOptions = {
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
};
|
||
|
||
const finalOptions = { ...defaultOptions, ...options };
|
||
|
||
try {
|
||
const response = await fetch(`${this.baseUrl}${url}`, finalOptions);
|
||
const data = await response.json();
|
||
|
||
if (data.code === 200) {
|
||
return data;
|
||
} else {
|
||
throw new Error(data.message || '请求失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('API请求错误:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* GET请求
|
||
*/
|
||
async get(url) {
|
||
return this.request(url, { method: 'GET' });
|
||
}
|
||
|
||
/**
|
||
* POST请求
|
||
*/
|
||
async post(url, data) {
|
||
return this.request(url, {
|
||
method: 'POST',
|
||
body: JSON.stringify(data),
|
||
});
|
||
}
|
||
|
||
/**
|
||
* PUT请求
|
||
*/
|
||
async put(url, data) {
|
||
return this.request(url, {
|
||
method: 'PUT',
|
||
body: JSON.stringify(data),
|
||
});
|
||
}
|
||
|
||
/**
|
||
* DELETE请求
|
||
*/
|
||
async delete(url) {
|
||
return this.request(url, { method: 'DELETE' });
|
||
}
|
||
|
||
// ========== 用户相关API ==========
|
||
|
||
/**
|
||
* 用户登录
|
||
*/
|
||
async login(username, password) {
|
||
const result = await this.post('/users/login', { username, password });
|
||
if (result.data) {
|
||
this.setCurrentUser(result.data);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取所有用户
|
||
*/
|
||
async getUsers() {
|
||
return this.get('/users');
|
||
}
|
||
|
||
/**
|
||
* 创建用户
|
||
*/
|
||
async createUser(userData) {
|
||
return this.post('/users', userData);
|
||
}
|
||
|
||
// ========== 课程相关API ==========
|
||
|
||
/**
|
||
* 获取所有课程
|
||
*/
|
||
async getCourses() {
|
||
return this.get('/courses');
|
||
}
|
||
|
||
/**
|
||
* 根据课程ID获取课程
|
||
*/
|
||
async getCourseByCourseId(courseId) {
|
||
return this.get(`/courses/courseId/${courseId}`);
|
||
}
|
||
|
||
/**
|
||
* 根据分类获取课程
|
||
*/
|
||
async getCoursesByCategory(categoryId) {
|
||
return this.get(`/courses/category/${categoryId}`);
|
||
}
|
||
|
||
/**
|
||
* 根据年级和学期获取课程
|
||
*/
|
||
async getCoursesByGradeAndSemester(gradeLevel, semester) {
|
||
return this.get(`/courses/grade/${gradeLevel}/semester/${semester}`);
|
||
}
|
||
|
||
// ========== 游戏相关API ==========
|
||
|
||
/**
|
||
* 获取所有游戏
|
||
*/
|
||
async getGames() {
|
||
return this.get('/games');
|
||
}
|
||
|
||
/**
|
||
* 根据课程ID获取游戏
|
||
*/
|
||
async getGamesByCourseId(courseId) {
|
||
return this.get(`/games/course/${courseId}`);
|
||
}
|
||
|
||
/**
|
||
* 根据分类获取游戏
|
||
*/
|
||
async getGamesByCategory(categoryId) {
|
||
return this.get(`/games/category/${categoryId}`);
|
||
}
|
||
|
||
// ========== 数据同步API ==========
|
||
|
||
/**
|
||
* 获取完整数据结构
|
||
*/
|
||
async getCompleteData() {
|
||
return this.get('/data/complete');
|
||
}
|
||
|
||
/**
|
||
* 获取课程完整数据
|
||
*/
|
||
async getCourseCompleteData(courseId) {
|
||
return this.get(`/data/course/${courseId}/complete`);
|
||
}
|
||
|
||
// ========== 记录相关API ==========
|
||
|
||
/**
|
||
* 记录用户访问
|
||
*/
|
||
async recordAccess(courseId, gameId, accessType, durationSeconds = 0) {
|
||
const user = this.getCurrentUser();
|
||
if (!user) {
|
||
console.warn('用户未登录,无法记录访问');
|
||
return { success: false, message: '用户未登录' };
|
||
}
|
||
|
||
const data = {
|
||
userId: user.id,
|
||
courseId: courseId ? parseInt(courseId) : null,
|
||
gameId: gameId ? parseInt(gameId) : null,
|
||
accessType: accessType,
|
||
durationSeconds: parseInt(durationSeconds) || 0
|
||
};
|
||
|
||
try {
|
||
const result = await this.post('/records/access', data);
|
||
return result;
|
||
} catch (error) {
|
||
console.error('记录访问失败:', error);
|
||
return { success: false, message: error.message };
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 记录游戏结果
|
||
*/
|
||
async recordGameResult(gameId, gameResult, score = 0, playTimeSeconds = 0, attemptsCount = 1, gameData = null) {
|
||
const user = this.getCurrentUser();
|
||
if (!user) {
|
||
console.warn('用户未登录,无法记录游戏结果');
|
||
return { success: false, message: '用户未登录' };
|
||
}
|
||
|
||
const data = {
|
||
userId: user.id,
|
||
gameId: gameId ? parseInt(gameId) : null,
|
||
gameResult: gameResult,
|
||
score: parseInt(score) || 0,
|
||
playTimeSeconds: parseInt(playTimeSeconds) || 0,
|
||
attemptsCount: parseInt(attemptsCount) || 1,
|
||
gameData: gameData
|
||
};
|
||
|
||
try {
|
||
const result = await this.post('/records/game', data);
|
||
return result;
|
||
} catch (error) {
|
||
console.error('记录游戏结果失败:', error);
|
||
return { success: false, message: error.message };
|
||
}
|
||
}
|
||
|
||
// ========== 统计相关API ==========
|
||
|
||
/**
|
||
* 获取用户学习统计
|
||
*/
|
||
async getUserStats(userId) {
|
||
return this.get(`/stats/user/${userId}`);
|
||
}
|
||
|
||
/**
|
||
* 获取课程访问统计
|
||
*/
|
||
async getCourseStats(courseId) {
|
||
return this.get(`/stats/course/${courseId}`);
|
||
}
|
||
|
||
/**
|
||
* 获取游戏统计
|
||
*/
|
||
async getGameStats(gameId) {
|
||
return this.get(`/stats/game/${gameId}`);
|
||
}
|
||
|
||
/**
|
||
* 获取用户游戏统计
|
||
*/
|
||
async getUserGameStats(userId, gameId) {
|
||
return this.get(`/stats/user/${userId}/game/${gameId}`);
|
||
}
|
||
|
||
// ========== 数据分析相关API ==========
|
||
|
||
/**
|
||
* 获取学生统计数据
|
||
*/
|
||
async getStudentStats(studentId, days = 30, categoryId = null) {
|
||
let url = `/analytics/student/${studentId}/stats?days=${days}`;
|
||
if (categoryId) {
|
||
url += `&categoryId=${categoryId}`;
|
||
}
|
||
return this.get(url);
|
||
}
|
||
|
||
/**
|
||
* 获取学生课程进度
|
||
*/
|
||
async getStudentCourseProgress(studentId, days = 30, categoryId = null) {
|
||
let url = `/analytics/student/${studentId}/courses?days=${days}`;
|
||
if (categoryId) {
|
||
url += `&categoryId=${categoryId}`;
|
||
}
|
||
return this.get(url);
|
||
}
|
||
|
||
/**
|
||
* 获取学生游戏统计
|
||
*/
|
||
async getStudentGameStats(studentId, days = 30, categoryId = null) {
|
||
let url = `/analytics/student/${studentId}/games?days=${days}`;
|
||
if (categoryId) {
|
||
url += `&categoryId=${categoryId}`;
|
||
}
|
||
return this.get(url);
|
||
}
|
||
|
||
/**
|
||
* 获取学生最近活动
|
||
*/
|
||
async getStudentRecentActivities(studentId, limit = 10) {
|
||
return this.get(`/analytics/student/${studentId}/activities?limit=${limit}`);
|
||
}
|
||
|
||
/**
|
||
* 获取AI视频推荐
|
||
*/
|
||
async getAIRecommendations(studentId, gameStats, courseStats) {
|
||
try {
|
||
// 构建推荐请求数据
|
||
const recommendationData = {
|
||
studentId: studentId,
|
||
gameStats: gameStats,
|
||
courseStats: courseStats,
|
||
timestamp: new Date().toISOString()
|
||
};
|
||
|
||
// 调用AI推荐API
|
||
const response = await this.post('/analytics/ai/recommendations', recommendationData);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取AI推荐失败:', error);
|
||
// 返回模拟推荐数据
|
||
return this.getMockRecommendations(gameStats, courseStats);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取模拟推荐数据(当AI服务不可用时)
|
||
*/
|
||
getMockRecommendations(gameStats, courseStats) {
|
||
const recommendations = [];
|
||
|
||
// 基于游戏统计生成推荐
|
||
if (gameStats && gameStats.games) {
|
||
gameStats.games.forEach(game => {
|
||
if (game.accuracy < 70) {
|
||
recommendations.push({
|
||
id: `rec_${game.gameId}_${Date.now()}`,
|
||
type: 'video',
|
||
title: `${game.gameTitle} - 基础讲解视频`,
|
||
reason: `根据您的答题情况,在${game.gameTitle}中正确率为${game.accuracy}%,建议观看相关基础讲解视频来提升理解。`,
|
||
tags: ['基础讲解', '数学概念', '提升理解'],
|
||
videoUrl: this.getVideoUrlByGameName(game.gameTitle, 'basic'),
|
||
priority: 'high'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 基于课程统计生成推荐
|
||
if (courseStats && courseStats.courses) {
|
||
courseStats.courses.forEach(course => {
|
||
if (course.progress < 50) {
|
||
recommendations.push({
|
||
id: `rec_course_${course.id}_${Date.now()}`,
|
||
type: 'course',
|
||
title: `${course.name} - 深入学习`,
|
||
reason: `您在${course.name}课程中的学习进度为${course.progress}%,建议继续深入学习相关概念。`,
|
||
tags: ['深入学习', '课程内容', '巩固知识'],
|
||
videoUrl: this.getVideoUrlByCourseName(course.name),
|
||
priority: 'medium'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 如果没有特定推荐,提供通用推荐
|
||
if (recommendations.length === 0) {
|
||
recommendations.push({
|
||
id: `rec_general_${Date.now()}`,
|
||
type: 'general',
|
||
title: '数学基础巩固视频',
|
||
reason: '基于您的学习情况,建议观看数学基础概念视频来巩固知识。',
|
||
tags: ['数学基础', '概念理解', '知识巩固'],
|
||
videoUrl: 'video/数学-2下-视频--认识数字/认识数字课.mp4',
|
||
priority: 'low'
|
||
});
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
recommendations: recommendations.slice(0, 5), // 最多返回5个推荐
|
||
generatedAt: new Date().toISOString(),
|
||
source: 'mock'
|
||
}
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 根据游戏名称获取对应的视频URL
|
||
*/
|
||
getVideoUrlByGameName(gameName, type) {
|
||
// 根据游戏名称匹配对应的视频文件
|
||
if (gameName.includes('数字') || gameName.includes('识数')) {
|
||
return 'video/数学-2下-视频--认识数字/认识数字课.mp4';
|
||
} else if (gameName.includes('比大小') || gameName.includes('大小')) {
|
||
return 'video/生活-比大小/比大小.mp4';
|
||
} else if (gameName.includes('加法') || gameName.includes('计算')) {
|
||
if (type === 'basic') {
|
||
return 'video/数学-2下-视频--饮品(和是4的加法)/教学片段.mp4';
|
||
} else {
|
||
return 'video/数学-2下-视频--饮品(和是4的加法)/交互1(1+3).mp4';
|
||
}
|
||
} else if (gameName.includes('时钟') || gameName.includes('时间')) {
|
||
return 'video/数学-2下-视频--认识时钟/认识时钟.mp4';
|
||
} else if (gameName.includes('情绪') || gameName.includes('情感')) {
|
||
return 'video/生活-什么是情绪/happy.mp4';
|
||
} else if (gameName.includes('垃圾') || gameName.includes('环保')) {
|
||
return 'video/生活-捡垃圾/捡垃圾.mp4';
|
||
}
|
||
|
||
// 默认返回数字认识视频
|
||
return 'video/数学-2下-视频--认识数字/认识数字课.mp4';
|
||
}
|
||
|
||
/**
|
||
* 根据课程名称获取对应的视频URL
|
||
*/
|
||
getVideoUrlByCourseName(courseName) {
|
||
// 根据课程名称匹配对应的视频文件
|
||
if (courseName.includes('数学') || courseName.includes('数字')) {
|
||
return 'video/数学-2下-视频--认识数字/认识数字课.mp4';
|
||
} else if (courseName.includes('语文') || courseName.includes('语言')) {
|
||
return 'video/生活-什么是情绪/happy.mp4';
|
||
} else if (courseName.includes('生活') || courseName.includes('常识')) {
|
||
return 'video/生活-捡垃圾/捡垃圾.mp4';
|
||
} else if (courseName.includes('时间') || courseName.includes('时钟')) {
|
||
return 'video/数学-2下-视频--认识时钟/认识时钟.mp4';
|
||
} else if (courseName.includes('加法') || courseName.includes('计算')) {
|
||
return 'video/数学-2下-视频--饮品(和是4的加法)/教学片段.mp4';
|
||
}
|
||
|
||
// 默认返回数字认识视频
|
||
return 'video/数学-2下-视频--认识数字/认识数字课.mp4';
|
||
}
|
||
|
||
/**
|
||
* 获取班级整体统计
|
||
*/
|
||
async getClassOverview(classId, days = 30) {
|
||
return this.get(`/analytics/class/${classId}/overview?days=${days}`);
|
||
}
|
||
|
||
/**
|
||
* 获取课程学习统计
|
||
*/
|
||
async getCourseStats(courseId, days = 30) {
|
||
return this.get(`/analytics/course/${courseId}/stats?days=${days}`);
|
||
}
|
||
|
||
/**
|
||
* 获取游戏统计
|
||
*/
|
||
async getGameStats(gameId, days = 30) {
|
||
return this.get(`/analytics/game/${gameId}/stats?days=${days}`);
|
||
}
|
||
|
||
/**
|
||
* 导出学生数据报告
|
||
*/
|
||
async exportStudentReport(studentId, days = 30) {
|
||
return this.get(`/analytics/student/${studentId}/export?days=${days}`);
|
||
}
|
||
|
||
// ==================== AI游戏生成器相关方法 ====================
|
||
|
||
/**
|
||
* 生成AI个性化游戏
|
||
*/
|
||
async generateAIGame(studentId, subject, gameType, difficulty, questionCount) {
|
||
try {
|
||
const response = await this.post('/api/ai/game/generate', {
|
||
studentId: studentId,
|
||
subject: subject,
|
||
gameType: gameType,
|
||
difficulty: difficulty,
|
||
questionCount: questionCount
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('生成AI游戏失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取学生游戏统计
|
||
*/
|
||
async getStudentGameStats(studentId) {
|
||
try {
|
||
const response = await this.get(`/api/ai/game/stats/${studentId}`);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取游戏统计失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新游戏结果
|
||
*/
|
||
async updateGameResult(studentId, gameId, correct, total, timeSpent, hintsUsed) {
|
||
try {
|
||
const response = await this.post('/api/ai/game/result', {
|
||
studentId: studentId,
|
||
gameId: gameId,
|
||
correct: correct,
|
||
total: total,
|
||
timeSpent: timeSpent,
|
||
hintsUsed: hintsUsed
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新游戏结果失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取推荐难度
|
||
*/
|
||
async getRecommendedDifficulty(studentId) {
|
||
try {
|
||
const response = await this.get(`/api/ai/game/difficulty/${studentId}`);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取推荐难度失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建全局API服务实例
|
||
window.apiService = new ApiService();
|