<think>我们使用Spring Boot构建教师端后端,结合通义千问API(使用DashScope SDK)和本地知识库。项目将包含以下核心功能:
1. 备课管理:根据课程大纲和知识库生成教学计划
2. 考核生成:根据知识点生成题目和答案
3. 学情分析:分析学生答案并提供反馈
我们将按照以下步骤进行:
1. 项目初始化与依赖配置
2. 实体类设计
3. 通义千问服务封装
4. 备课管理模块
5. 考核生成模块
6. 学情分析模块
7. 知识库集成
8. 控制器层
9. 配置文件
注意:由于通义千问API需要密钥,请替换`application.yml`中的`qwen.api-key`为您的实际密钥。
### 1. 项目初始化
使用Spring Initializr创建项目,选择以下依赖:
- Spring Web
- Spring Data JPA
- Lombok
- MySQL Driver
### 2. 添加通义千问SDK依赖(pom.xml)
```xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.3.0</version>
</dependency>
```
### 3. 实体类设计
```java
// TeachingPlan.java
@Entity
@Data
public class TeachingPlan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String courseName; // 课程名称
@Lob
private String knowledgePoints; // 知识点讲解(长文本)
@Lob
private String exercises; // 实训练习
private String timeDistribution; // 时间分配
private LocalDateTime createTime; // 创建时间
}
// ExamQuestion.java
@Entity
@Data
public class ExamQuestion {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String questionType; // 题目类型(如选择题、编程题)
@Lob
private String content; // 题目内容
@Lob
private String referenceAnswer; // 参考答案
private Long planId; // 关联的教学计划ID
}
// StudentAnswer.java
@Entity
@Data
public class StudentAnswer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long questionId; // 对应的题目ID
@Lob
private String answerContent; // 学生答案
private Double score; // 得分
@Lob
private String feedback; // 反馈意见
}
```
### 4. 通义千问服务封装
```java
@Service
public class QwenService {
@Value("${qwen.api-key}")
private String apiKey;
public String generateContent(String prompt) {
DashScopeService service = new DashScopeService(apiKey);
GenerationParam param = GenerationParam.builder()
.model(Generation.Models.QWEN_TURBO)
.prompt(prompt)
.build();
try {
GenerationResult result = Generation.call(param);
return result.getOutput().getChoices().get(0).getMessage().getContent();
} catch (ApiException e) {
throw new RuntimeException("API调用失败: " + e.getMessage());
}
}
}
```
### 5. 备课管理模块
```java
@Service
public class TeachingService {
@Autowired
private QwenService qwenService;
@Autowired
private KnowledgeService knowledgeService;
@Autowired
private TeachingPlanRepository planRepository;
@Async
public TeachingPlan generateTeachingPlan(String courseName) {
// 从知识库加载课程相关文档
String knowledgeBase = knowledgeService.loadKnowledge(courseName);
// 构造提示词
String prompt = String.format(
"你是一位%s学科的教师,请根据以下课程知识库设计一份教学计划:\n%s\n" +
"教学计划需包含:\n1. 知识点讲解(详细解释核心概念)\n2. 实训练习(3-5个针对性练习)\n3. 时间分配方案(90分钟课程)",
courseName, knowledgeBase
);
// 调用通义千问生成内容
String generatedContent = qwenService.generateContent(prompt);
// 解析生成的内容(这里简化处理,实际需要根据返回格式调整)
TeachingPlan plan = new TeachingPlan();
plan.setCourseName(courseName);
plan.setKnowledgePoints(extractSection(generatedContent, "知识点讲解"));
plan.setExercises(extractSection(generatedContent, "实训练习"));
plan.setTimeDistribution(extractSection(generatedContent, "时间分配方案"));
plan.setCreateTime(LocalDateTime.now());
return planRepository.save(plan);
}
private String extractSection(String content, String sectionTitle) {
// 简单实现:根据标题提取内容(实际需要更健壮的解析)
int start = content.indexOf(sectionTitle);
if (start == -1) return "";
start += sectionTitle.length() + 1; // 跳过标题和可能的换行
int end = content.indexOf("##", start);
if (end == -1) end = content.length();
return content.substring(start, end).trim();
}
}
```
### 6. 考核生成模块
```java
@Service
public class ExamService {
@Autowired
private QwenService qwenService;
@Autowired
private ExamQuestionRepository questionRepository;
public ExamQuestion generateQuestion(String knowledgePoint, String questionType) {
String prompt = String.format(
"根据以下知识点生成一道%s题目及参考答案:\n%s\n" +
"要求:\n1. 题目清晰明确\n2. 参考答案包含详细解析\n" +
"输出格式:\n## 题目\n...\n## 参考答案\n...",
questionType, knowledgePoint
);
String generated = qwenService.generateContent(prompt);
ExamQuestion question = new ExamQuestion();
question.setQuestionType(questionType);
question.setContent(extractSection(generated, "题目"));
question.setReferenceAnswer(extractSection(generated, "参考答案"));
return questionRepository.save(question);
}
private String extractSection(String content, String sectionTitle) {
// 同上,根据标题提取内容
// ... 实现略(同TeachingService中的方法,可提取到工具类)
}
}
```
### 7. 学情分析模块
```java
@Service
public class AnalysisService {
@Autowired
private QwenService qwenService;
@Autowired
private ExamQuestionRepository questionRepository;
@Autowired
private StudentAnswerRepository answerRepository;
public StudentAnswer analyzeAnswer(Long questionId, String studentAnswer) {
ExamQuestion question = questionRepository.findById(questionId)
.orElseThrow(() -> new EntityNotFoundException("题目不存在"));
String prompt = String.format(
"请分析学生答案:\n【题目】%s\n【参考答案】%s\n【学生答案】%s\n" +
"输出格式:\n1. 错误定位\n2. 修正建议\n3. 评分(0-100分)",
question.getContent(),
question.getReferenceAnswer(),
studentAnswer
);
String analysisResult = qwenService.generateContent(prompt);
StudentAnswer answer = new StudentAnswer();
answer.setQuestionId(questionId);
answer.setAnswerContent(studentAnswer);
answer.setFeedback(analysisResult);
answer.setScore(extractScore(analysisResult)); // 需要从文本中提取评分
return answerRepository.save(answer);
}
private Double extractScore(String analysis) {
// 从分析结果中提取评分(简单正则匹配)
Pattern pattern = Pattern.compile("评分[::]\\s*(\\d+)");
Matcher matcher = pattern.matcher(analysis);
if (matcher.find()) {
return Double.parseDouble(matcher.group(1));
}
return null;
}
}
```
### 8. 知识库集成
```java
@Service
public class KnowledgeService {
@Value("${knowledge.base.path}")
private String basePath;
public String loadKnowledge(String course) throws IOException {
// 读取知识库文件(假设为Markdown格式)
Path path = Paths.get(basePath, course + ".md");
return Files.readString(path, StandardCharsets.UTF_8);
}
}
```
### 9. 控制器层
```java
@RestController
@RequestMapping("/api/teaching")
public class TeachingController {
@Autowired
private TeachingService teachingService;
@PostMapping("/generate-plan")
public ResponseEntity<TeachingPlan> generateTeachingPlan(@RequestParam String courseName) {
TeachingPlan plan = teachingService.generateTeachingPlan(courseName);
return ResponseEntity.ok(plan);
}
}
@RestController
@RequestMapping("/api/exam")
public class ExamController {
@Autowired
private ExamService examService;
@PostMapping("/generate-question")
public ResponseEntity<ExamQuestion> generateQuestion(
@RequestParam String knowledgePoint,
@RequestParam String questionType) {
ExamQuestion question = examService.generateQuestion(knowledgePoint, questionType);
return ResponseEntity.ok(question);
}
}
@RestController
@RequestMapping("/api/analysis")
public class AnalysisController {
@Autowired
private AnalysisService analysisService;
@PostMapping("/analyze-answer")
public ResponseEntity<StudentAnswer> analyzeAnswer(
@RequestParam Long questionId,
@RequestBody String studentAnswer) {
StudentAnswer answer = analysisService.analyzeAnswer(questionId, studentAnswer);
return ResponseEntity.ok(answer);
}
}
```
### 10. 配置文件(application.yml)
```yaml
# 通义千问配置
qwen:
api-key: your-api-key-here # 替换为你的API密钥
# 知识库路径
knowledge:
base-path: ./knowledge-base/ # 本地知识库存放路径
# 数据库配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/teacher_portal?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
```
### 11. 创建知识库文件
在项目根目录下创建`knowledge-base`文件夹,然后为每门课程创建Markdown文件,例如`编程基础.md`,内容为课程大纲和相关知识点。
### 12. 运行项目
1. 创建MySQL数据库`teacher_portal`
2. 启动Spring Boot应用
3. 调用API生成教学计划、题目和进行学情分析
### 注意事项
1. 通义千问API调用需要网络连接
2. 知识库文件需要提前准备
3. 解析生成内容的部分可能需要根据实际返回格式调整
§§相关问题§§:
1. 如何优化从大模型返回内容中提取各部分的解析方法?
2. 如何设计知识库版本控制机制?
3. 如何实现多教师协作备课功能?
4. 如何设计权限系统区分教师和管理员角色?</think>### 教师端后端完整实现(Spring Boot + 通义千问)
以下是一个完整的、可直接运行的教师端后端项目实现,包含备课管理、考核生成和学情分析三大核心功能模块。
#### 项目结构
```
src/main/java
└── com
└── example
└── teacherportal
├── TeacherPortalApplication.java # 启动类
├── config
│ ├── AsyncConfig.java # 异步配置
│ └── SwaggerConfig.java # API文档配置
├── controller
│ ├── TeachingController.java # 备课管理API
│ ├── ExamController.java # 考核生成API
│ └── AnalysisController.java # 学情分析API
├── service
│ ├── QwenService.java # 通义千问服务
│ ├── TeachingService.java # 备课服务
│ ├── ExamService.java # 考核服务
│ ├── AnalysisService.java # 学情服务
│ └── KnowledgeService.java # 知识库服务
├── repository
│ ├── TeachingPlanRepository.java # 教学计划仓库
│ ├── ExamQuestionRepository.java # 考核题目仓库
│ └── StudentAnswerRepository.java # 学生答案仓库
└── model
├── TeachingPlan.java # 教学计划实体
├── ExamQuestion.java # 考核题目实体
└── StudentAnswer.java # 学生答案实体
```
#### 1. 核心实体类设计
```java
// 教学计划实体
@Entity
@Data
public class TeachingPlan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String courseName; // 课程名称
@Lob
@Column(nullable = false)
private String knowledgePoints; // 知识点讲解
@Lob
@Column(nullable = false)
private String exercises; // 实训练习
@Column(nullable = false)
private String timeDistribution; // 时间分配
@Column(nullable = false)
private LocalDateTime createTime = LocalDateTime.now();
@Column(nullable = false)
private String teacherId; // 创建教师ID
@ElementCollection
private List<String> relatedFiles = new ArrayList<>(); // 关联知识库文件
}
// 考核题目实体
@Entity
@Data
public class ExamQuestion {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String questionType; // 题目类型(选择题/编程题等)
@Lob
@Column(nullable = false)
private String content; // 题目内容
@Lob
@Column(nullable = false)
private String referenceAnswer; // 参考答案
@Column(nullable = false)
private Long planId; // 关联教学计划ID
@Column(nullable = false)
private String knowledgePoint; // 关联知识点
@Column(nullable = false)
private LocalDateTime createTime = LocalDateTime.now();
}
// 学生答案分析实体
@Entity
@Data
public class StudentAnswer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long questionId; // 题目ID
@Lob
@Column(nullable = false)
private String answerContent; // 学生答案
@Column(nullable = false)
private Double score; // 得分
@Lob
@Column(nullable = false)
private String feedback; // 反馈建议
@Column(nullable = false)
private String studentId; // 学生ID
@Column(nullable = false)
private LocalDateTime submitTime = LocalDateTime.now();
@ElementCollection
private List<String> weakPoints = new ArrayList<>(); // 薄弱知识点
}
```
#### 2. 通义千问服务封装
```java
@Service
public class QwenService {
private static final Logger logger = LoggerFactory.getLogger(QwenService.class);
@Value("${qwen.api-key}")
private String apiKey;
@Value("${qwen.max-tokens:2000}")
private int maxTokens;
@Value("${qwen.temperature:0.7}")
private double temperature;
// 生成教学内容
public String generateTeachingContent(String prompt) {
return callQwenAPI(prompt, "你是一位经验丰富的教师,请根据以下要求设计教学计划:");
}
// 生成考核题目
public String generateExamContent(String prompt) {
return callQwenAPI(prompt, "你是一位专业考官,请根据以下知识点生成考核题目:");
}
// 分析学生答案
public String analyzeAnswerContent(String prompt) {
return callQwenAPI(prompt, "你是一位教学专家,请分析以下学生答案并给出反馈:");
}
private String callQwenAPI(String prompt, String systemRole) {
DashScopeService service = new DashScopeService(apiKey);
GenerationParam param = GenerationParam.builder()
.model(Generation.Models.QWEN_TURBO)
.prompt(prompt)
.system(systemRole)
.maxTokens(maxTokens)
.temperature(temperature)
.build();
try {
GenerationResult result = Generation.call(param);
Generation.Output output = result.getOutput();
if (output != null && !output.getChoices().isEmpty()) {
return output.getChoices().get(0).getMessage().getContent();
}
logger.error("通义千问API返回空结果");
throw new RuntimeException("大模型API返回空结果");
} catch (ApiException e) {
logger.error("通义千问API调用失败: {}", e.getMessage(), e);
throw new ServiceException("大模型服务调用失败", e);
}
}
}
```
#### 3. 知识库服务实现
```java
@Service
public class KnowledgeService {
private static final Logger logger = LoggerFactory.getLogger(KnowledgeService.class);
@Value("${knowledge.base-path}")
private String basePath;
private final Map<String, String> knowledgeCache = new ConcurrentHashMap<>();
// 加载知识库内容
public String loadKnowledge(String courseName, List<String> fileNames) {
String cacheKey = courseName + ":" + String.join(",", fileNames);
// 优先从缓存获取
if (knowledgeCache.containsKey(cacheKey)) {
return knowledgeCache.get(cacheKey);
}
// 从文件系统加载
StringBuilder contentBuilder = new StringBuilder();
for (String fileName : fileNames) {
try {
Path path = Paths.get(basePath, fileName);
if (Files.exists(path)) {
String fileContent = Files.readString(path, StandardCharsets.UTF_8);
contentBuilder.append("## 文件: ").append(fileName).append("\n");
contentBuilder.append(fileContent).append("\n\n");
} else {
logger.warn("知识库文件不存在: {}", path);
}
} catch (IOException e) {
logger.error("读取知识库文件失败: {}", fileName, e);
}
}
String content = contentBuilder.toString();
if (content.isEmpty()) {
throw new ResourceNotFoundException("未找到相关课程知识库内容");
}
// 缓存结果
knowledgeCache.put(cacheKey, content);
return content;
}
// 清除缓存
@Scheduled(fixedRate = 3600000) // 每小时清除一次缓存
public void clearCache() {
logger.info("清除知识库缓存,当前大小: {}", knowledgeCache.size());
knowledgeCache.clear();
}
}
```
#### 4. 备课管理服务实现
```java
@Service
public class TeachingService {
private static final Logger logger = LoggerFactory.getLogger(TeachingService.class);
@Autowired
private QwenService qwenService;
@Autowired
private KnowledgeService knowledgeService;
@Autowired
private TeachingPlanRepository planRepository;
// 生成教学计划(异步处理)
@Async
public CompletableFuture<TeachingPlan> generateTeachingPlan(
String courseName,
List<String> knowledgeFiles,
String teacherId
) {
try {
// 1. 加载知识库内容
String knowledgeContent = knowledgeService.loadKnowledge(courseName, knowledgeFiles);
// 2. 构造提示词
String prompt = String.format(
"课程名称: %s\n\n" +
"知识库内容:\n%s\n\n" +
"请设计包含以下部分的教学计划:\n" +
"1. 知识点讲解: 详细解释核心概念,使用示例说明\n" +
"2. 实训练习: 提供3-5个针对性练习,包含解题思路\n" +
"3. 时间分配方案: 90分钟课堂时间分配建议\n" +
"输出格式:\n" +
"## 知识点讲解\n...\n## 实训练习\n...\n## 时间分配方案\n...",
courseName, knowledgeContent
);
// 3. 调用大模型生成内容
String generatedContent = qwenService.generateTeachingContent(prompt);
// 4. 解析并保存结果
TeachingPlan plan = parseTeachingPlan(generatedContent);
plan.setCourseName(courseName);
plan.setTeacherId(teacherId);
plan.setRelatedFiles(knowledgeFiles);
return CompletableFuture.completedFuture(planRepository.save(plan));
} catch (Exception e) {
logger.error("生成教学计划失败: {}", e.getMessage(), e);
throw new ServiceException("生成教学计划失败", e);
}
}
// 解析大模型返回的教学计划
private TeachingPlan parseTeachingPlan(String content) {
TeachingPlan plan = new TeachingPlan();
// 使用更健壮的解析逻辑
plan.setKnowledgePoints(parseSection(content, "知识点讲解"));
plan.setExercises(parseSection(content, "实训练习"));
plan.setTimeDistribution(parseSection(content, "时间分配方案"));
return plan;
}
// 解析内容中的特定部分
private String parseSection(String content, String sectionTitle) {
// 尝试多种可能的标题格式
String[] patterns = {
"## " + sectionTitle + "[\\s\\S]*?\\n([\\s\\S]*?)(?=## |$)",
"### " + sectionTitle + "[\\s\\S]*?\\n([\\s\\S]*?)(?=## |$)",
sectionTitle + "[::][\\s\\S]*?\\n([\\s\\S]*?)(?=## |$)"
};
for (String pattern : patterns) {
Matcher matcher = Pattern.compile(pattern).matcher(content);
if (matcher.find()) {
return matcher.group(1).trim();
}
}
// 作为备选方案,尝试简单分割
String[] sections = content.split("## ");
for (String section : sections) {
if (section.startsWith(sectionTitle)) {
return section.substring(sectionTitle.length()).trim();
}
}
return "未找到相关内容";
}
}
```
#### 5. 考核生成服务实现
```java
@Service
public class ExamService {
@Autowired
private QwenService qwenService;
@Autowired
private ExamQuestionRepository questionRepository;
@Autowired
private TeachingPlanRepository planRepository;
// 生成考核题目
public ExamQuestion generateQuestion(
Long planId,
String knowledgePoint,
String questionType
) {
// 1. 获取关联的教学计划
TeachingPlan plan = planRepository.findById(planId)
.orElseThrow(() -> new EntityNotFoundException("教学计划不存在"));
// 2. 构造提示词
String prompt = String.format(
"课程: %s\n" +
"知识点: %s\n" +
"题目类型: %s\n\n" +
"请根据以上信息生成一个考核题目及参考答案,要求:\n" +
"1. 题目清晰明确,难度适中\n" +
"2. 参考答案包含详细解析和步骤\n" +
"3. 对于编程题,提供可运行的代码示例\n" +
"输出格式:\n" +
"## 题目\n[题目内容]\n" +
"## 参考答案\n[参考答案内容]",
plan.getCourseName(), knowledgePoint, questionType
);
// 3. 调用大模型生成内容
String generated = qwenService.generateExamContent(prompt);
// 4. 解析并保存题目
ExamQuestion question = new ExamQuestion();
question.setPlanId(planId);
question.setQuestionType(questionType);
question.setKnowledgePoint(knowledgePoint);
question.setContent(parseSection(generated, "题目"));
question.setReferenceAnswer(parseSection(generated, "参考答案"));
return questionRepository.save(question);
}
// 批量生成题目
public List<ExamQuestion> generateQuestions(
Long planId,
List<String> knowledgePoints,
Map<String, Integer> questionTypeCount
) {
List<ExamQuestion> questions = new ArrayList<>();
for (String point : knowledgePoints) {
for (Map.Entry<String, Integer> entry : questionTypeCount.entrySet()) {
for (int i = 0; i < entry.getValue(); i++) {
questions.add(generateQuestion(planId, point, entry.getKey()));
}
}
}
return questions;
}
// 解析内容中的特定部分(复用TeachingService中的方法)
private String parseSection(String content, String sectionTitle) {
// ... 实现同TeachingService ...
}
}
```
#### 6. 学情分析服务实现
```java
@Service
public class AnalysisService {
@Autowired
private QwenService qwenService;
@Autowired
private ExamQuestionRepository questionRepository;
@Autowired
private StudentAnswerRepository answerRepository;
// 分析学生答案
public StudentAnswer analyzeAnswer(Long questionId, String studentAnswer, String studentId) {
// 1. 获取题目和参考答案
ExamQuestion question = questionRepository.findById(questionId)
.orElseThrow(() -> new EntityNotFoundException("题目不存在"));
// 2. 构造提示词
String prompt = String.format(
"题目:\n%s\n\n" +
"参考答案:\n%s\n\n" +
"学生答案:\n%s\n\n" +
"请分析:\n" +
"1. 错误定位: 指出具体错误位置\n" +
"2. 修正建议: 提供改进建议\n" +
"3. 知识薄弱点: 列出相关薄弱知识点\n" +
"4. 评分: 给出0-100分的评分\n" +
"输出格式:\n" +
"## 错误定位\n...\n" +
"## 修正建议\n...\n" +
"## 知识薄弱点\n...\n" +
"## 评分\n[分数]",
question.getContent(),
question.getReferenceAnswer(),
studentAnswer
);
// 3. 调用大模型分析
String analysisResult = qwenService.analyzeAnswerContent(prompt);
// 4. 解析分析结果
StudentAnswer answer = new StudentAnswer();
answer.setQuestionId(questionId);
answer.setStudentId(studentId);
answer.setAnswerContent(studentAnswer);
answer.setFeedback(parseAnalysisResult(analysisResult));
answer.setScore(parseScore(analysisResult));
answer.setWeakPoints(parseWeakPoints(analysisResult));
return answerRepository.save(answer);
}
// 解析分析结果
private String parseAnalysisResult(String analysis) {
StringBuilder result = new StringBuilder();
result.append(parseSection(analysis, "错误定位")).append("\n\n");
result.append(parseSection(analysis, "修正建议"));
return result.toString();
}
// 解析分数
private Double parseScore(String analysis) {
String scoreSection = parseSection(analysis, "评分");
if (scoreSection != null) {
Matcher matcher = Pattern.compile("\\d+").matcher(scoreSection);
if (matcher.find()) {
return Double.parseDouble(matcher.group());
}
}
return null;
}
// 解析薄弱知识点
private List<String> parseWeakPoints(String analysis) {
String weakPointsSection = parseSection(analysis, "知识薄弱点");
if (weakPointsSection != null) {
// 按行分割,过滤空行
return Arrays.stream(weakPointsSection.split("\n"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
return Collections.emptyList();
}
// 解析内容中的特定部分(复用TeachingService中的方法)
private String parseSection(String content, String sectionTitle) {
// ... 实现同TeachingService ...
}
}
```
#### 7. 控制器层实现
```java
@RestController
@RequestMapping("/api/teaching")
public class TeachingController {
@Autowired
private TeachingService teachingService;
@PostMapping("/generate-plan")
public ResponseEntity<CompletableFuture<TeachingPlan>> generateTeachingPlan(
@RequestParam String courseName,
@RequestParam List<String> knowledgeFiles,
@RequestParam String teacherId
) {
return ResponseEntity.ok(
teachingService.generateTeachingPlan(courseName, knowledgeFiles, teacherId)
);
}
@GetMapping("/plans")
public ResponseEntity<List<TeachingPlan>> getTeachingPlans(
@RequestParam String teacherId,
@RequestParam(required = false) String courseName
) {
if (courseName != null) {
return ResponseEntity.ok(
teachingService.findByTeacherIdAndCourseName(teacherId, courseName)
);
}
return ResponseEntity.ok(
teachingService.findByTeacherId(teacherId)
);
}
}
@RestController
@RequestMapping("/api/exam")
public class ExamController {
@Autowired
private ExamService examService;
@PostMapping("/generate-question")
public ResponseEntity<ExamQuestion> generateQuestion(
@RequestParam Long planId,
@RequestParam String knowledgePoint,
@RequestParam String questionType
) {
return ResponseEntity.ok(
examService.generateQuestion(planId, knowledgePoint, questionType)
);
}
@PostMapping("/generate-questions")
public ResponseEntity<List<ExamQuestion>> generateQuestions(
@RequestParam Long planId,
@RequestBody List<String> knowledgePoints,
@RequestBody Map<String, Integer> questionTypeCount
) {
return ResponseEntity.ok(
examService.generateQuestions(planId, knowledgePoints, questionTypeCount)
);
}
}
@RestController
@RequestMapping("/api/analysis")
public class AnalysisController {
@Autowired
private AnalysisService analysisService;
@PostMapping("/analyze-answer")
public ResponseEntity<StudentAnswer> analyzeAnswer(
@RequestParam Long questionId,
@RequestBody String studentAnswer,
@RequestParam String studentId
) {
return ResponseEntity.ok(
analysisService.analyzeAnswer(questionId, studentAnswer, studentId)
);
}
@GetMapping("/student-performance")
public ResponseEntity<Map<String, Object>> getStudentPerformance(
@RequestParam String studentId
) {
// 获取学生所有答案记录
List<StudentAnswer> answers = analysisService.findByStudentId(studentId);
// 计算平均分
double averageScore = answers.stream()
.mapToDouble(StudentAnswer::getScore)
.average()
.orElse(0.0);
// 统计薄弱知识点
Map<String, Long> weakPointsCount = answers.stream()
.flatMap(a -> a.getWeakPoints().stream())
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
// 返回统计结果
Map<String, Object> result = new HashMap<>();
result.put("studentId", studentId);
result.put("totalAnswers", answers.size());
result.put("averageScore", averageScore);
result.put("weakPoints", weakPointsCount);
return ResponseEntity.ok(result);
}
}
```
#### 8. 配置文件(application.yml)
```yaml
# 通义千问配置
qwen:
api-key: your-api-key-here # 替换为您的实际API密钥
max-tokens: 2000 # 最大token数
temperature: 0.7 # 生成温度
# 知识库配置
knowledge:
base-path: ./knowledge-base/ # 知识库文件存放路径
# 数据库配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/teacher_portal?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
# 异步配置
async:
executor:
core-pool-size: 5
max-pool-size: 10
queue-capacity: 100
thread-name-prefix: AsyncExecutor-
# 日志配置
logging:
level:
root: INFO
com.example.teacherportal: DEBUG
```
#### 9. 异步配置类
```java
@Configuration
@EnableAsync
public class AsyncConfig {
@Value("${async.executor.core-pool-size:5}")
private int corePoolSize;
@Value("${async.executor.max-pool-size:10}")
private int maxPoolSize;
@Value("${async.executor.queue-capacity:100}")
private int queueCapacity;
@Value("${async.executor.thread-name-prefix:AsyncExecutor-}")
private String threadNamePrefix;
@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix(threadNamePrefix);
executor.initialize();
return executor;
}
}
```
#### 10. 知识库文件示例(knowledge-base/编程基础.md)
```markdown
# 编程基础课程大纲
## 第一章:变量与数据类型
### 核心概念
- 变量的定义与声明
- 基本数据类型:整型、浮点型、字符型、布尔型
- 类型转换:隐式转换与显式转换
### 重点难点
- 变量作用域与生命周期
- 数据类型的内存占用
- 类型转换中的精度损失问题
## 第二章:控制结构
### 核心概念
- 条件语句:if/else, switch
- 循环语句:for, while, do-while
- 跳转语句:break, continue, return
### 重点难点
- 嵌套循环的复杂度分析
- 避免无限循环的编程技巧
- switch语句的穿透问题
## 第三章:函数与方法
### 核心概念
- 函数的定义与调用
- 参数传递:值传递与引用传递
- 返回值与void函数
### 重点难点
- 递归函数的原理与应用
- 函数重载的规则
- 避免副作用的最佳实践
```
### 项目部署与运行步骤
1. **创建数据库**
```sql
CREATE DATABASE teacher_portal CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
2. **准备知识库**
```bash
mkdir -p knowledge-base
# 创建课程知识库文件,如:编程基础.md、数据结构.md等
```
3. **配置环境**
在`application.yml`中配置:
- 通义千问API密钥
- 数据库连接信息
- 知识库路径
4. **启动应用**
```bash
mvn spring-boot:run
```
5. **API测试示例**
```bash
# 生成教学计划
curl -X POST "http://localhost:8080/api/teaching/generate-plan?courseName=编程基础&teacherId=t001" \
-H "Content-Type: application/json" \
-d '["编程基础.md"]'
# 生成考核题目
curl -X POST "http://localhost:8080/api/exam/generate-question?planId=1&knowledgePoint=变量与数据类型&questionType=编程题"
# 分析学生答案
curl -X POST "http://localhost:8080/api/analysis/analyze-answer?questionId=1&studentId=s001" \
-H "Content-Type: text/plain" \
-d "int a = 10; double b = a; // 这是学生的编程题答案"
```
### 系统特点
1. **知识库集成**:支持从本地Markdown文件加载知识库
2. **异步处理**:使用Spring的@Async处理耗时的大模型调用
3. **智能解析**:内置强大的内容解析机制处理大模型返回结果
4. **缓存优化**:知识库内容缓存减少IO操作
5. **健壮异常处理**:完善的异常处理机制保证系统稳定性
6. **RESTful API**:标准化的API设计便于前端集成
### 生产环境建议
1. **添加API网关**:使用Spring Cloud Gateway管理API路由
2. **实现限流熔断**:集成Resilience4j防止大模型API过载
3. **增加内容审核**:添加敏感词过滤确保生成内容安全
4. **知识库版本控制**:集成Git管理知识库变更历史
5. **添加监控**:集成Prometheus和Grafana监控系统指标