feat: Phase 1-3 全部完成 — 沙盒增强、学情分析、学习路径

This commit is contained in:
yuzhiran-dev
2026-05-18 09:48:51 +08:00
commit 11bb86854c
277 changed files with 37755 additions and 0 deletions
@@ -0,0 +1,22 @@
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
import { LearningService } from './learning.service';
@ApiTags('学习分析')
@Controller('learning')
@UseGuards(AuthGuard('jwt'))
@ApiBearerAuth()
export class LearningController {
constructor(private learningService: LearningService) {}
@Get('analytics')
async getAnalytics(@Req() req: any) {
return this.learningService.getAnalytics(req.user.userId);
}
@Get('path')
async getLearningPath(@Req() req: any) {
return this.learningService.getLearningPath(req.user.userId);
}
}
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { LearningController } from './learning.controller';
import { LearningService } from './learning.service';
@Module({
controllers: [LearningController],
providers: [LearningService],
exports: [LearningService],
})
export class LearningModule {}
@@ -0,0 +1,192 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
const KNOWLEDGE_DOMAINS = [
{ id: 'ai-basics', name: 'AI 基础知识', keywords: ['AI', '人工智能', '大模型', 'chatgpt', 'gpt', '大语言模型', 'llm', '深度学习', '神经网络', 'machine learning', '机器学习'] },
{ id: 'prompt-engineering', name: '提示词工程', keywords: ['提示词', 'prompt', 'system prompt', 'role', 'few-shot', 'chain-of-thought', 'cot'] },
{ id: 'programming', name: '编程开发', keywords: ['python', 'javascript', 'typescript', 'java', '代码', '函数', '算法', 'debug', 'bug', '编程', '开发', 'react', 'vue', 'node'] },
{ id: 'writing', name: '写作创作', keywords: ['写作', '文章', '文案', '润色', '作文', '创作', '故事', '小说', '博客'] },
{ id: 'english', name: '英语学习', keywords: ['英语', 'english', '翻译', '语法', 'grammar', 'vocabulary', '口语', '写作', '阅读'] },
{ id: 'data-science', name: '数据分析', keywords: ['数据', '分析', '统计', '图表', '可视化', 'sql', 'excel', 'pandas', 'numpy', '数据分析'] },
{ id: 'office', name: '办公效率', keywords: ['ppt', 'excel', 'word', '办公', '邮件', '报告', '文档', '会议', '总结'] },
{ id: 'career', name: '职业发展', keywords: ['简历', '面试', '求职', '职业', '工作', '升职', '薪资'] },
];
const COURSE_RECOMMENDATIONS: Record<string, { title: string; url: string }[]> = {
'ai-basics': [
{ title: 'AI 通识:零基础入门', url: '/courses' },
{ title: '大模型原理与应用', url: '/courses' },
],
'prompt-engineering': [
{ title: '提示词工程从入门到精通', url: '/courses' },
{ title: '高级 Prompt 技巧', url: '/prompts' },
],
'programming': [
{ title: '用 Python 入门 AI 编程', url: '/courses' },
{ title: 'AI 辅助编程实战', url: '/sandbox' },
],
'writing': [
{ title: 'AI 写作实战指南', url: '/prompts/workshop' },
{ title: '内容创作与润色技巧', url: '/courses' },
],
'english': [
{ title: 'AI 辅助英语学习', url: '/sandbox' },
{ title: '英语写作提升课程', url: '/courses' },
],
'data-science': [
{ title: '数据分析入门', url: '/courses' },
{ title: 'Python 数据分析', url: '/courses' },
],
'office': [
{ title: '用 AI 提升 10 倍办公效率', url: '/courses' },
{ title: 'AI 办公自动化', url: '/courses' },
],
'career': [
{ title: 'AI 时代职业规划', url: '/courses' },
{ title: '面试技巧与简历优化', url: '/sandbox' },
],
};
@Injectable()
export class LearningService {
constructor(private prisma: PrismaService) {}
async getAnalytics(userId: number) {
const sessions = await this.prisma.sandboxSession.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
take: 100,
});
const domainCounts: Record<string, number> = {};
const domainDates: Record<string, string> = {};
const totalSessions = sessions.length;
for (const domain of KNOWLEDGE_DOMAINS) {
domainCounts[domain.id] = 0;
}
for (const session of sessions) {
const searchText = `${session.title} ${session.messages || ''}`.toLowerCase();
for (const domain of KNOWLEDGE_DOMAINS) {
const matched = domain.keywords.some(kw => searchText.includes(kw));
if (matched) {
domainCounts[domain.id] = (domainCounts[domain.id] || 0) + 1;
if (!domainDates[domain.id] || session.createdAt.toISOString() > domainDates[domain.id]) {
domainDates[domain.id] = session.createdAt.toISOString();
}
}
}
}
const domains = KNOWLEDGE_DOMAINS.map(d => {
const count = domainCounts[d.id] || 0;
const totalSessionsForUser = Math.max(totalSessions, 1);
const mastery = Math.min(Math.round((count / Math.max(totalSessionsForUser * 0.3, 1)) * 100), 100);
return {
id: d.id,
name: d.name,
sessionCount: count,
mastery,
lastActive: domainDates[d.id] || null,
weak: mastery < 30,
};
});
const weakDomains = domains.filter(d => d.weak);
const recommendations = weakDomains.length > 0
? weakDomains.slice(0, 3).flatMap(d => (COURSE_RECOMMENDATIONS[d.id] || []).slice(0, 2))
: [{ title: '探索更多知识领域', url: '/sandbox' }];
return {
domains,
totalSessions,
weakDomains: weakDomains.map(d => d.name),
recommendations: [...new Map(recommendations.map(r => [r.title, r])).values()],
};
}
async getLearningPath(userId: number) {
const sessions = await this.prisma.sandboxSession.findMany({
where: { userId },
select: { title: true, messages: true },
});
const allText = sessions.map(s => `${s.title} ${s.messages || ''}`.toLowerCase()).join(' ');
const stages = [
{
id: 'basics',
title: '认识大模型',
icon: '🤖',
description: '了解 AI 和大型语言模型的基本概念,学会如何使用 AI 工具。',
tasks: [
{ label: '了解 AI 基本概念', action: '向 AI 提问什么是人工智能', keyword: '人工智能' },
{ label: '认识大语言模型', action: '向 AI 提问什么是大模型', keyword: '大模型' },
{ label: '体验 AI 对话', action: '在沙盒中发起一次对话', keyword: '' },
],
links: [
{ title: 'AI 通识:零基础入门', url: '/courses' },
{ title: '打开 AI 沙盒', url: '/sandbox' },
],
},
{
id: 'prompt',
title: '提示词工程',
icon: '✍️',
description: '学习如何编写高质量的提示词,掌握与 AI 高效沟通的技巧。',
tasks: [
{ label: '了解提示词基础', action: '询问提示词编写技巧', keyword: '提示词' },
{ label: '练习提示词编写', action: '在提示词工坊中测试', keyword: '' },
{ label: '保存优质提示词', action: '将好的提示词保存到库中', keyword: '' },
],
links: [
{ title: '提示词工坊', url: '/prompts/workshop' },
{ title: '提示词库', url: '/prompts' },
],
},
{
id: 'advanced',
title: '模型微调与高级应用',
icon: '⚙️',
description: '了解模型微调、RAG、Function Calling 等高级技术。',
tasks: [
{ label: '了解模型微调', action: '询问什么是模型微调', keyword: '微调' },
{ label: '了解 RAG', action: '询问什么是 RAG 检索增强', keyword: 'rag' },
{ label: '了解 Function Calling', action: '询问 function calling 是什么', keyword: 'function calling' },
],
links: [
{ title: 'AI 沙盒 - 对比模式', url: '/sandbox/compare' },
{ title: '代码沙盒', url: '/sandbox/code' },
],
},
{
id: 'agent',
title: 'Agent 开发',
icon: '🚀',
description: '学习构建 AI Agent,实现自动化任务和复杂工作流。',
tasks: [
{ label: '了解 AI Agent', action: '询问什么是 AI Agent', keyword: 'agent' },
{ label: '学习工具调用', action: '询问 AI 工具调用机制', keyword: 'tool' },
{ label: '实践项目', action: '尝试用 AI 构建一个小项目', keyword: '' },
],
links: [
{ title: '代码沙盒 - 运行项目', url: '/sandbox/code' },
{ title: '对比实验室', url: '/sandbox/compare' },
],
},
];
return stages.map(stage => {
const completedCount = stage.tasks.filter(t => !t.keyword || allText.includes(t.keyword)).length;
const progress = stage.tasks.length > 0 ? Math.round((completedCount / stage.tasks.length) * 100) : 0;
return {
...stage,
completedCount,
totalTasks: stage.tasks.length,
progress,
unlocked: true,
};
});
}
}