diff --git a/src/main/java/com/sztzjy/linkCommerce/controller/stu/TrainingTaskController.java b/src/main/java/com/sztzjy/linkCommerce/controller/stu/TrainingTaskController.java index 7a2db1d..765b015 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/TrainingTaskController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/TrainingTaskController.java @@ -169,6 +169,14 @@ public class TrainingTaskController { } } + @PostMapping("/knowledge/reindex-case-materials") + @ApiOperation("Reindex existing case materials for AI training knowledge") + public ResultEntity reindexCaseMaterials(HttpServletRequest request) { + requireAdmin(request); + int count = trainingTaskService.reindexCaseMaterials(); + return new ResultEntity<>(HttpStatus.OK, "Case material indexing submitted", count); + } + @DeleteMapping("/{id}") @ApiOperation("Delete training task") public ResultEntity delete(@PathVariable String id, HttpServletRequest request) { diff --git a/src/main/java/com/sztzjy/linkCommerce/mapper/TrainingTaskClassConfigMapper.java b/src/main/java/com/sztzjy/linkCommerce/mapper/TrainingTaskClassConfigMapper.java index aabd4f0..2e6cfc0 100644 --- a/src/main/java/com/sztzjy/linkCommerce/mapper/TrainingTaskClassConfigMapper.java +++ b/src/main/java/com/sztzjy/linkCommerce/mapper/TrainingTaskClassConfigMapper.java @@ -20,6 +20,8 @@ public interface TrainingTaskClassConfigMapper { List selectListByTeachingClass(@Param("teachingClassId") String teachingClassId, @Param("enabled") Boolean enabled); + List selectAll(); + int deleteByTeachingClassAndTaskKey(@Param("teachingClassId") String teachingClassId, @Param("taskKey") String taskKey); diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TrainingTaskService.java b/src/main/java/com/sztzjy/linkCommerce/service/TrainingTaskService.java index f81d2b7..fda9bbf 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/TrainingTaskService.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/TrainingTaskService.java @@ -37,6 +37,14 @@ public interface TrainingTaskService { void restoreClassTasksDefaults(String teachingClassId, String operatorId); + /** + * Rebuilds AI knowledge entries for all saved administrator and teaching-class case materials. + * Existing entries with an unchanged file hash are reused. + * + * @return number of effective case-material configurations submitted for indexing + */ + int reindexCaseMaterials(); + TrainingTask getStudentTaskByTaskKey(String taskKey, String studentUserId); TrainingTask getStudentTaskByTaskKey(String taskKey, JwtUser student); diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java index df678f1..5a02bad 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java @@ -304,6 +304,40 @@ public class TrainingTaskServiceImpl implements TrainingTaskService { } } + @Override + public int reindexCaseMaterials() { + if (trainingKnowledgeService == null) return 0; + + List tasks = trainingTaskMapper.selectList(null, null, null); + Map taskByKey = new LinkedHashMap<>(); + int indexed = 0; + for (TrainingTask task : tasks) { + if (task == null || StringUtils.isBlank(task.getTaskKey())) continue; + taskByKey.put(task.getTaskKey(), task); + EffectiveTrainingTaskContent content = EffectiveTrainingTaskContent.resolve(task, null); + if (StringUtils.isNotBlank(content.getMaterialUrl())) { + indexCaseMaterial(task, content, null); + indexed++; + } + } + + List classConfigs = trainingTaskClassConfigMapper.selectAll(); + if (classConfigs == null) return indexed; + for (TrainingTaskClassConfig config : classConfigs) { + if (config == null || !Boolean.TRUE.equals(config.getEnabled()) + || StringUtils.isBlank(config.getTeachingClassId()) || StringUtils.isBlank(config.getTaskKey())) { + continue; + } + TrainingTask task = taskByKey.get(config.getTaskKey()); + if (task == null) continue; + EffectiveTrainingTaskContent content = EffectiveTrainingTaskContent.resolve(task, config); + if (StringUtils.isBlank(content.getMaterialUrl())) continue; + indexCaseMaterial(task, content, config.getTeachingClassId()); + indexed++; + } + return indexed; + } + @Override @Transactional(rollbackFor = Exception.class) public void restoreClassTaskDefault(String teachingClassId, String taskKey, String operatorId) { diff --git a/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml b/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml index 42ee4f8..65d414c 100644 --- a/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml +++ b/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml @@ -50,6 +50,12 @@ order by sort asc, create_time asc + + delete from training_task_class_config where teaching_class_id = #{teachingClassId,jdbcType=VARCHAR} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImplTest.java index 7292bb4..df8ff4f 100644 --- a/src/test/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImplTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImplTest.java @@ -12,6 +12,8 @@ import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper; import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper; import com.sztzjy.linkCommerce.mapper.TrainingTaskClassConfigMapper; import com.sztzjy.linkCommerce.mapper.TrainingTaskMapper; +import com.sztzjy.linkCommerce.service.TrainingKnowledgeService; +import com.sztzjy.linkCommerce.training.knowledge.EffectiveTrainingTaskContent; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.jdbc.core.JdbcTemplate; @@ -52,6 +54,37 @@ class TrainingTaskServiceImplTest { verify(service.jdbcTemplate, never()).execute(contains("task_allocation")); } + @Test + void reindexCaseMaterialsKeepsDefaultAndTeachingClassCasesSeparated() { + TrainingTaskServiceImpl service = new TrainingTaskServiceImpl(); + service.trainingTaskMapper = mock(TrainingTaskMapper.class); + service.trainingTaskClassConfigMapper = mock(TrainingTaskClassConfigMapper.class); + service.trainingKnowledgeService = mock(TrainingKnowledgeService.class); + + TrainingTask task = storedTask("task-1", "new-product-survey"); + task.setMaterialName("管理员案例.docx"); + task.setMaterialUrl("/file/admin-case.docx"); + TrainingTaskClassConfig teacherConfig = overrideTask("config-1", "class-1", "new-product-survey"); + teacherConfig.setMaterialName("教师案例.docx"); + teacherConfig.setMaterialUrl("/file/teacher-case.docx"); + when(service.trainingTaskMapper.selectList(null, null, null)).thenReturn(List.of(task)); + when(service.trainingTaskClassConfigMapper.selectAll()).thenReturn(List.of(teacherConfig)); + + int indexed = service.reindexCaseMaterials(); + + assertEquals(2, indexed); + ArgumentCaptor contentCaptor = ArgumentCaptor.forClass(EffectiveTrainingTaskContent.class); + ArgumentCaptor classCaptor = ArgumentCaptor.forClass(String.class); + verify(service.trainingKnowledgeService, times(2)).indexCaseMaterial(org.mockito.ArgumentMatchers.eq(task), + contentCaptor.capture(), classCaptor.capture()); + assertEquals("ADMIN", contentCaptor.getAllValues().get(0).getScope()); + assertEquals("task-1", contentCaptor.getAllValues().get(0).getEffectiveConfigId()); + assertEquals("TEACHER", contentCaptor.getAllValues().get(1).getScope()); + assertEquals("config-1", contentCaptor.getAllValues().get(1).getEffectiveConfigId()); + assertNull(classCaptor.getAllValues().get(0)); + assertEquals("class-1", classCaptor.getAllValues().get(1)); + } + @Test void splitListSupportsCommonSeparators() { TrainingTaskServiceImpl service = new TrainingTaskServiceImpl();