<think>我们根据项目结构,为新增的实体(KnowledgeDoc, StudentAnswer, KnowledgePoint)创建对应的Repository接口,同时为已有的实体(如Courseware, Exercise, User)补充Repository接口(如果已有则忽略,这里我们按照项目结构重新提供)。
注意:项目中使用的是Spring Data JPA,因此每个Repository接口需要继承JpaRepository。
同时,为了调试,我们可以在关键方法上添加日志(通常Repository接口本身不包含实现,所以日志一般放在Service层。但我们可以通过自定义Repository实现或使用AOP记录日志,这里我们按照常规做法,不在Repository接口中直接写日志,而是在Service层记录)。
不过,我们可以利用Spring Data JPA的查询方法,并假设在Service调用时会记录日志。
根据项目结构,我们需要创建:
1. KnowledgeRepo.java:用于KnowledgeDoc实体的持久化操作
2. AnswerRepo.java:用于StudentAnswer实体的持久化操作
3. 另外,原有的CoursewareRepository, ExerciseRepository, UserRepository已经存在,但为了完整性,我们一并提供(如果已有,可以跳过,但这里我们按照要求提供全部)。
注意:由于项目升级到Spring Boot 3.x,我们使用Jakarta EE 9+,所以导包使用jakarta.persistence。
我们将为每个Repository接口提供基本的CRUD操作,以及根据业务需要自定义的查询方法。
代码实现如下:</think>### Repository 层实现(包含调试日志)
以下是针对项目结构的 Repository 层实现,包含必要的调试日志:
#### KnowledgeRepo.java
```java
package com.teacher.repository;
import com.teacher.model.entity.KnowledgeDoc;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Optional;
@Repository
public interface KnowledgeRepo extends JpaRepository<KnowledgeDoc, Long> {
Logger logger = LoggerFactory.getLogger(KnowledgeRepo.class);
// 根据状态查找文档
List<KnowledgeDoc> findByStatus(String status);
// 根据用户ID查找文档
List<KnowledgeDoc> findByUserId(Long userId);
// 更新文档状态
@Modifying
@Query("UPDATE KnowledgeDoc k SET k.status = :status WHERE k.id = :id")
default int updateStatus(@Param("id") Long id, @Param("status") String status) {
logger.debug("更新知识库文档状态: id={}, status={}", id, status);
return executeUpdateStatus(id, status);
}
@Modifying
@Query("UPDATE KnowledgeDoc k SET k.status = :status WHERE k.id = :id")
int executeUpdateStatus(@Param("id") Long id, @Param("status") String status);
// 设置向量ID
@Modifying
@Query("UPDATE KnowledgeDoc k SET k.vectorId = :vectorId WHERE k.id = :id")
default int setVectorId(@Param("id") Long id, @Param("vectorId") String vectorId) {
logger.debug("设置知识库文档向量ID: id={}, vectorId={}", id, vectorId);
return executeSetVectorId(id, vectorId);
}
@Modifying
@Query("UPDATE KnowledgeDoc k SET k.vectorId = :vectorId WHERE k.id = :id")
int executeSetVectorId(@Param("id") Long id, @Param("vectorId") String vectorId);
// 重写保存方法添加日志
@Override
default <S extends KnowledgeDoc> S save(S entity) {
logger.debug("保存知识库文档: title={}, userId={}", entity.getTitle(), entity.getUserId());
return saveEntity(entity);
}
@Override
<S extends KnowledgeDoc> S save(S entity);
// 重写删除方法添加日志
@Override
default void deleteById(Long id) {
logger.debug("删除知识库文档: id={}", id);
deleteEntityById(id);
}
void deleteEntityById(Long id);
// 自定义查询:根据标题关键词搜索
@Query("SELECT k FROM KnowledgeDoc k WHERE k.title LIKE %:keyword%")
List<KnowledgeDoc> searchByTitleKeyword(@Param("keyword") String keyword);
}
```
#### AnswerRepo.java
```java
package com.teacher.repository;
import com.teacher.model.entity.StudentAnswer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
@Repository
public interface AnswerRepo extends JpaRepository<StudentAnswer, Long> {
Logger logger = LoggerFactory.getLogger(AnswerRepo.class);
// 根据学生ID查找答案
List<StudentAnswer> findByStudentId(Long studentId);
// 根据习题ID查找答案
List<StudentAnswer> findByExerciseId(Long exerciseId);
// 查找某个学生的所有正确/错误答案
List<StudentAnswer> findByStudentIdAndIsCorrect(Long studentId, Boolean isCorrect);
// 重写保存方法添加日志
@Override
default <S extends StudentAnswer> S save(S entity) {
logger.debug("保存学生答题: studentId={}, exerciseId={}",
entity.getStudentId(), entity.getExerciseId());
return saveEntity(entity);
}
@Override
<S extends StudentAnswer> S save(S entity);
// 自定义查询:计算学生平均分
@Query("SELECT AVG(s.score) FROM StudentAnswer s WHERE s.studentId = :studentId")
default Double calculateAverageScore(@Param("studentId") Long studentId) {
logger.debug("计算学生平均分: studentId={}", studentId);
return executeCalculateAverageScore(studentId);
}
@Query("SELECT AVG(s.score) FROM StudentAnswer s WHERE s.studentId = :studentId")
Double executeCalculateAverageScore(@Param("studentId") Long studentId);
// 自定义查询:查找某个知识点的所有答题
@Query("SELECT s FROM StudentAnswer s JOIN Exercise e ON s.exerciseId = e.id " +
"WHERE e.knowledgePoint = :knowledgePoint")
List<StudentAnswer> findByKnowledgePoint(@Param("knowledgePoint") String knowledgePoint);
}
```
#### CoursewareRepository.java
```java
package com.teacher.repository;
import com.teacher.model.entity.Courseware;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
@Repository
public interface CoursewareRepository extends JpaRepository<Courseware, Long> {
Logger logger = LoggerFactory.getLogger(CoursewareRepository.class);
// 根据用户ID查找课件
List<Courseware> findByUserId(Long userId);
// 根据标题查找课件
List<Courseware> findByTitleContaining(String keyword);
// 重写保存方法添加日志
@Override
default <S extends Courseware> S save(S entity) {
logger.debug("保存课件: title={}, userId={}", entity.getTitle(), entity.getUserId());
return saveEntity(entity);
}
@Override
<S extends Courseware> S save(S entity);
// 重写删除方法添加日志
@Override
default void deleteById(Long id) {
logger.debug("删除课件: id={}", id);
deleteEntityById(id);
}
void deleteEntityById(Long id);
}
```
#### ExerciseRepository.java
```java
package com.teacher.repository;
import com.teacher.model.entity.Exercise;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
@Repository
public interface ExerciseRepository extends JpaRepository<Exercise, Long> {
Logger logger = LoggerFactory.getLogger(ExerciseRepository.class);
// 根据知识点查找习题
List<Exercise> findByKnowledgePoint(String knowledgePoint);
// 根据难度级别查找习题
List<Exercise> findByDifficultyLevel(String difficultyLevel);
// 根据类型查找习题
List<Exercise> findByExerciseType(String exerciseType);
// 重写保存方法添加日志
@Override
default <S extends Exercise> S save(S entity) {
logger.debug("保存习题: type={}, knowledgePoint={}",
entity.getExerciseType(), entity.getKnowledgePoint());
return saveEntity(entity);
}
@Override
<S extends Exercise> S save(S entity);
// 自定义查询:随机获取指定数量的习题
@Query(value = "SELECT * FROM exercises ORDER BY RAND() LIMIT :count", nativeQuery = true)
List<Exercise> findRandomExercises(@Param("count") int count);
// 自定义查询:根据知识点和难度获取习题
@Query("SELECT e FROM Exercise e WHERE e.knowledgePoint = :knowledgePoint AND e.difficultyLevel = :difficulty")
List<Exercise> findByKnowledgeAndDifficulty(
@Param("knowledgePoint") String knowledgePoint,
@Param("difficulty") String difficulty
);
}
```
#### UserRepository.java
```java
package com.teacher.repository;
import com.teacher.model.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Logger logger = LoggerFactory.getLogger(UserRepository.class);
// 根据用户名查找用户
Optional<User> findByUsername(String username);
// 根据邮箱查找用户
Optional<User> findByEmail(String email);
// 根据角色查找用户
List<User> findByRole(String role);
// 重写保存方法添加日志
@Override
default <S extends User> S save(S entity) {
logger.debug("保存用户: username={}, role={}", entity.getUsername(), entity.getRole());
return saveEntity(entity);
}
@Override
<S extends User> S save(S entity);
// 重写删除方法添加日志
@Override
default void deleteById(Long id) {
logger.debug("删除用户: id={}", id);
deleteEntityById(id);
}
void deleteEntityById(Long id);
}
```
### Repository 设计说明
#### 日志策略
1. **接口级日志**:
- 每个 Repository 接口包含静态 Logger 实例
- 通过默认方法(default method)添加日志记录
- 重写 JpaRepository 的 save/delete 方法添加日志
2. **日志级别**:
- `DEBUG`:记录关键操作(保存、删除、更新)
- `TRACE`:详细查询参数(开发环境使用)
3. **方法级日志**:
```java
@Override
default <S extends Entity> S save(S entity) {
logger.debug("保存操作: {}", entity);
return saveEntity(entity);
}
```
#### 自定义查询方法
1. **派生查询**:
```java
List<KnowledgeDoc> findByStatus(String status);
```
2. **JPQL 查询**:
```java
@Query("SELECT k FROM KnowledgeDoc k WHERE k.title LIKE %:keyword%")
List<KnowledgeDoc> searchByTitleKeyword(@Param("keyword") String keyword);
```
3. **原生 SQL 查询**:
```java
@Query(value = "SELECT * FROM exercises ORDER BY RAND() LIMIT :count", nativeQuery = true)
List<Exercise> findRandomExercises(@Param("count") int count);
```
#### 更新操作处理
1. **使用 `@Modifying` 注解**:
```java
@Modifying
@Query("UPDATE KnowledgeDoc k SET k.status = :status WHERE k.id = :id")
int updateStatus(@Param("id") Long id, @Param("status") String status);
```
2. **日志包装方法**:
```java
default int updateStatus(Long id, String status) {
logger.debug("更新状态: id={}, status={}", id, status);
return executeUpdateStatus(id, status);
}
```
### 服务层使用示例
#### KnowledgeServiceImpl.java
```java
package com.teacher.service.impl;
import com.teacher.model.entity.KnowledgeDoc;
import com.teacher.repository.KnowledgeRepo;
import com.teacher.service.KnowledgeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class KnowledgeServiceImpl implements KnowledgeService {
private static final Logger logger = LoggerFactory.getLogger(KnowledgeServiceImpl.class);
private final KnowledgeRepo knowledgeRepo;
@Autowired
public KnowledgeServiceImpl(KnowledgeRepo knowledgeRepo) {
this.knowledgeRepo = knowledgeRepo;
}
@Override
@Transactional
public KnowledgeDoc uploadDocument(KnowledgeDoc doc) {
KnowledgeDoc savedDoc = knowledgeRepo.save(doc);
logger.info("知识库文档已保存: id={}, title={}", savedDoc.getId(), savedDoc.getTitle());
return savedDoc;
}
@Override
@Transactional
public void updateDocumentStatus(Long docId, String status) {
int updated = knowledgeRepo.updateStatus(docId, status);
if (updated > 0) {
logger.info("文档状态更新成功: id={}, status={}", docId, status);
} else {
logger.warn("文档状态更新失败: id={}, status={}", docId, status);
}
}
@Override
public List<KnowledgeDoc> getPendingDocuments() {
List<KnowledgeDoc> pendingDocs = knowledgeRepo.findByStatus("PENDING");
logger.debug("获取待处理文档: 数量={}", pendingDocs.size());
return pendingDocs;
}
@Override
public List<KnowledgeDoc> searchDocuments(String keyword) {
List<KnowledgeDoc> results = knowledgeRepo.searchByTitleKeyword(keyword);
logger.debug("知识库搜索: keyword={}, 结果数量={}", keyword, results.size());
return results;
}
}
```
#### AssessmentServiceImpl.java
```java
package com.teacher.service.impl;
import com.teacher.model.entity.Exercise;
import com.teacher.model.entity.StudentAnswer;
import com.teacher.repository.AnswerRepo;
import com.teacher.repository.ExerciseRepository;
import com.teacher.service.AssessmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class AssessmentServiceImpl implements AssessmentService {
private static final Logger logger = LoggerFactory.getLogger(AssessmentServiceImpl.class);
private final ExerciseRepository exerciseRepository;
private final AnswerRepo answerRepo;
@Autowired
public AssessmentServiceImpl(ExerciseRepository exerciseRepository,
AnswerRepo answerRepo) {
this.exerciseRepository = exerciseRepository;
this.answerRepo = answerRepo;
}
@Override
public List<Exercise> generateExercises(String knowledgePoint, int count) {
List<Exercise> exercises = exerciseRepository.findByKnowledgePoint(knowledgePoint);
logger.info("生成习题: 知识点={}, 总数={}", knowledgePoint, exercises.size());
// 如果数量不足,补充随机习题
if (exercises.size() < count) {
int needed = count - exercises.size();
List<Exercise> randomExercises = exerciseRepository.findRandomExercises(needed);
exercises.addAll(randomExercises);
logger.debug("补充随机习题: 数量={}", randomExercises.size());
}
return exercises.subList(0, Math.min(count, exercises.size()));
}
@Override
@Transactional
public void saveStudentAnswers(List<StudentAnswer> answers) {
List<StudentAnswer> savedAnswers = answerRepo.saveAll(answers);
logger.info("保存学生答题: 数量={}", savedAnswers.size());
}
@Override
public double calculateStudentScore(Long studentId) {
Double averageScore = answerRepo.calculateAverageScore(studentId);
double score = averageScore != null ? averageScore : 0.0;
logger.info("计算学生平均分: studentId={}, score={}", studentId, score);
return score;
}
}
```
### 日志输出示例
1. **知识库文档操作**:
```
DEBUG c.t.r.KnowledgeRepo - 保存知识库文档: title=微积分基础, userId=1001
INFO c.t.s.i.KnowledgeServiceImpl - 知识库文档已保存: id=15, title=微积分基础
DEBUG c.t.r.KnowledgeRepo - 更新知识库文档状态: id=15, status=INDEXED
INFO c.t.s.i.KnowledgeServiceImpl - 文档状态更新成功: id=15, status=INDEXED
DEBUG c.t.r.KnowledgeRepo - 知识库搜索: keyword=积分, 结果数量=8
```
2. **学生答题处理**:
```
DEBUG c.t.r.AnswerRepo - 保存学生答题: studentId=2001, exerciseId=3005
INFO c.t.s.i.AssessmentServiceImpl - 保存学生答题: 数量=10
DEBUG c.t.r.AnswerRepo - 计算学生平均分: studentId=2001
INFO c.t.s.i.AssessmentServiceImpl - 计算学生平均分: studentId=2001, score=85.5
```
3. **习题生成**:
```
INFO c.t.s.i.AssessmentServiceImpl - 生成习题: 知识点=微分方程, 总数=15
DEBUG c.t.s.i.AssessmentServiceImpl - 补充随机习题: 数量=5
```
### 调试技巧
1. **SQL 日志输出**:
```properties
# application.properties
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
```
2. **事务调试**:
```java
@Transactional
public void processDocument(Long docId) {
KnowledgeDoc doc = knowledgeRepo.findById(docId).orElseThrow();
logger.debug("开始处理文档: {}", doc.getTitle());
// 复杂处理逻辑
doc.setStatus("PROCESSING");
knowledgeRepo.save(doc);
logger.debug("文档处理完成: {}", doc.getTitle());
}
```
3. **批量操作优化**:
```java
@Transactional
public void bulkUpdateStatus(List<Long> ids, String status) {
ids.forEach(id -> {
knowledgeRepo.updateStatus(id, status);
logger.trace("更新文档状态: id={}", id);
});
logger.info("批量更新状态完成: 数量={}", ids.size());
}
```
4. **异常处理**:
```java
public KnowledgeDoc getDocument(Long id) {
try {
return knowledgeRepo.findById(id)
.orElseThrow(() -> new DocumentNotFoundException(id));
} catch (EmptyResultDataAccessException e) {
logger.error("文档不存在: id={}", id, e);
throw new DocumentNotFoundException(id);
}
}
```
这些 Repository 实现符合 Spring Data JPA 规范,包含必要的日志记录,便于开发调试和生产问题排查。