diff --git a/src/main/java/com/project/ding/config/SecurityConfig.java b/src/main/java/com/project/ding/config/SecurityConfig.java index 6f1c36a..8e72ae2 100644 --- a/src/main/java/com/project/ding/config/SecurityConfig.java +++ b/src/main/java/com/project/ding/config/SecurityConfig.java @@ -28,7 +28,8 @@ public class SecurityConfig { .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth // 登录放行 - .requestMatchers("/api/login/**").permitAll() + .requestMatchers("/api/login/**" , + "/api/interaction/**").permitAll() // 管理端锁定 .requestMatchers("/api/admin/**").hasRole("ADMIN") // 其余(考生端)需登录 diff --git a/src/main/java/com/project/information/domain/dto/KnowledgePointDTO.java b/src/main/java/com/project/information/domain/dto/KnowledgePointDTO.java index 9f20f5b..555fbe8 100644 --- a/src/main/java/com/project/information/domain/dto/KnowledgePointDTO.java +++ b/src/main/java/com/project/information/domain/dto/KnowledgePointDTO.java @@ -1,7 +1,12 @@ package com.project.information.domain.dto; +import com.project.base.domain.dto.BaseDTO; import lombok.Data; @Data -public class KnowledgePointDTO { +public class KnowledgePointDTO extends BaseDTO { + private Long id; + + private String content; + } diff --git a/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java b/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java index c7b66d9..7a7d70d 100644 --- a/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java +++ b/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java @@ -29,4 +29,5 @@ public class CheckDuplicatesParam { // 处理解析异常 throw new BusinessErrorException("Json解析失败"); } - }} + } +} diff --git a/src/main/java/com/project/interaction/application/AlgorithmApplicationService.java b/src/main/java/com/project/interaction/application/AlgorithmApplicationService.java new file mode 100644 index 0000000..2213301 --- /dev/null +++ b/src/main/java/com/project/interaction/application/AlgorithmApplicationService.java @@ -0,0 +1,24 @@ +package com.project.interaction.application; + +import com.project.information.domain.dto.KnowledgePointDTO; +import com.project.interaction.domain.dto.ClusterCallbackDTO; +import com.project.question.domain.dto.TaskKnowledgePointDTO; +import com.project.task.domain.enums.QuestionTypeEnum; + +import java.util.List; + +public interface AlgorithmApplicationService { + + /** + * 知识点分簇 + * @param taskId + * @param kpList + */ + + void postToClustering(Long taskId, List kpList); + + + void generateQuestion(List dtoList , QuestionTypeEnum questionType , int num); + + void saveCluster(Long taskId, List clusters) throws Exception; +} diff --git a/src/main/java/com/project/interaction/application/impl/AlgorithmApplicationServiceImpl.java b/src/main/java/com/project/interaction/application/impl/AlgorithmApplicationServiceImpl.java new file mode 100644 index 0000000..32932be --- /dev/null +++ b/src/main/java/com/project/interaction/application/impl/AlgorithmApplicationServiceImpl.java @@ -0,0 +1,47 @@ +package com.project.interaction.application.impl; + +import com.project.information.domain.dto.KnowledgePointDTO; +import com.project.interaction.application.AlgorithmApplicationService; +import com.project.interaction.domain.dto.ClusterCallbackDTO; +import com.project.interaction.domain.service.PostToClusteringDomainService; +import com.project.interaction.domain.service.SaveClusterDomainService; +import com.project.question.domain.dto.TaskKnowledgePointDTO; +import com.project.question.domain.enums.QuestionSourceTypeEnum; +import com.project.task.domain.enums.QuestionTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class AlgorithmApplicationServiceImpl implements AlgorithmApplicationService { + @Autowired + private PostToClusteringDomainService postToClusteringDomainService; + + @Autowired + private SaveClusterDomainService saveClusterDomainService; + @Override + public void postToClustering(Long taskId, List kpList) { + postToClusteringDomainService.postToClustering(taskId , kpList); + } + + + @Override + public void generateQuestion(List dtoList, QuestionTypeEnum questionType, int num) { + System.out.println(String.format("<=======生成了%d道%s, 结合了知识点【%s】属于%s=======>" , + num , + questionType.getDescription() , + String.join("," , dtoList.stream().map(dto -> dto.getId().toString()).toList()) , + dtoList.size() > 1 ? QuestionSourceTypeEnum.Multi_Concept.getDesc() : QuestionSourceTypeEnum.Single_Concept.getDesc())); + } + + + @Override + public void saveCluster(Long taskId, List clusters) throws Exception { + try { + saveClusterDomainService.saveCluster(taskId , clusters); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/project/interactor/config/WebClientConfig.java b/src/main/java/com/project/interaction/config/WebClientConfig.java similarity index 95% rename from src/main/java/com/project/interactor/config/WebClientConfig.java rename to src/main/java/com/project/interaction/config/WebClientConfig.java index 553275a..9c3665b 100644 --- a/src/main/java/com/project/interactor/config/WebClientConfig.java +++ b/src/main/java/com/project/interaction/config/WebClientConfig.java @@ -1,4 +1,4 @@ -package com.project.interactor.config; +package com.project.interaction.config; import io.netty.channel.ChannelOption; import io.netty.handler.timeout.ReadTimeoutHandler; @@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit; @Configuration public class WebClientConfig { - @Value("${algo.baseUrl}") + @Value("${algo.baseUrl:''}") private String baseUrl; @Bean(name = "algorithmWebClient") diff --git a/src/main/java/com/project/interaction/controller/InteractionController.java b/src/main/java/com/project/interaction/controller/InteractionController.java new file mode 100644 index 0000000..be9bd98 --- /dev/null +++ b/src/main/java/com/project/interaction/controller/InteractionController.java @@ -0,0 +1,29 @@ +package com.project.interaction.controller; + + + +import com.project.base.domain.result.Result; +import com.project.interaction.application.AlgorithmApplicationService; +import com.project.interaction.domain.dto.ClusterCallbackDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/interaction") +public class InteractionController { + + @Autowired + private AlgorithmApplicationService algorithmApplicationService; + + + + /** + * 手动触发同步接口 + * 强刷:/sync/all?force=true + */ + @PostMapping("/saveCluster") + public Result saveCluster(ClusterCallbackDTO dto) throws Exception{ + algorithmApplicationService.saveCluster(dto.getTaskId(), dto.getClusters()); + return Result.success("保存成功"); + } +} diff --git a/src/main/java/com/project/interaction/domain/dto/ClusterCallbackDTO.java b/src/main/java/com/project/interaction/domain/dto/ClusterCallbackDTO.java new file mode 100644 index 0000000..e02c50c --- /dev/null +++ b/src/main/java/com/project/interaction/domain/dto/ClusterCallbackDTO.java @@ -0,0 +1,40 @@ +package com.project.interaction.domain.dto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.project.base.domain.exception.BusinessErrorException; +import com.project.information.domain.param.FileCheckItem; +import lombok.Data; +import org.springframework.util.StringUtils; + +import java.util.Collections; +import java.util.List; + +@Data +public class ClusterCallbackDTO { + private Long taskId; + + private String clusters; + @Data + public static class ClusterItem { + private String topic; + private List atomIdList; + } + + // 手动解析为List的方法 + public List getClusters() { + if (StringUtils.isEmpty(clusters)) { + return Collections.emptyList(); + } + try { + return new ObjectMapper().readValue( + clusters, + new TypeReference>() {}); + } catch (JsonProcessingException e) { + // 处理解析异常 + throw new BusinessErrorException("Json解析失败"); + } + } + +} diff --git a/src/main/java/com/project/interactor/domain/dto/ClusterQueryDTO.java b/src/main/java/com/project/interaction/domain/dto/ClusterQueryDTO.java similarity index 83% rename from src/main/java/com/project/interactor/domain/dto/ClusterQueryDTO.java rename to src/main/java/com/project/interaction/domain/dto/ClusterQueryDTO.java index 176de9f..7bd96e6 100644 --- a/src/main/java/com/project/interactor/domain/dto/ClusterQueryDTO.java +++ b/src/main/java/com/project/interaction/domain/dto/ClusterQueryDTO.java @@ -1,4 +1,4 @@ -package com.project.interactor.domain.dto; +package com.project.interaction.domain.dto; import com.project.information.domain.dto.KnowledgePointDTO; diff --git a/src/main/java/com/project/interactor/domain/dto/ClusterResultDTO.java b/src/main/java/com/project/interaction/domain/dto/ClusterResultDTO.java similarity index 73% rename from src/main/java/com/project/interactor/domain/dto/ClusterResultDTO.java rename to src/main/java/com/project/interaction/domain/dto/ClusterResultDTO.java index db351a7..3c2b84c 100644 --- a/src/main/java/com/project/interactor/domain/dto/ClusterResultDTO.java +++ b/src/main/java/com/project/interaction/domain/dto/ClusterResultDTO.java @@ -1,4 +1,4 @@ -package com.project.interactor.domain.dto; +package com.project.interaction.domain.dto; import lombok.Data; diff --git a/src/main/java/com/project/interactor/domain/service/PostToClusteringDomainService.java b/src/main/java/com/project/interaction/domain/service/PostToClusteringDomainService.java similarity index 81% rename from src/main/java/com/project/interactor/domain/service/PostToClusteringDomainService.java rename to src/main/java/com/project/interaction/domain/service/PostToClusteringDomainService.java index bd777fb..f9621eb 100644 --- a/src/main/java/com/project/interactor/domain/service/PostToClusteringDomainService.java +++ b/src/main/java/com/project/interaction/domain/service/PostToClusteringDomainService.java @@ -1,4 +1,4 @@ -package com.project.interactor.domain.service; +package com.project.interaction.domain.service; import com.project.information.domain.dto.KnowledgePointDTO; diff --git a/src/main/java/com/project/interaction/domain/service/SaveClusterDomainService.java b/src/main/java/com/project/interaction/domain/service/SaveClusterDomainService.java new file mode 100644 index 0000000..57e3bcd --- /dev/null +++ b/src/main/java/com/project/interaction/domain/service/SaveClusterDomainService.java @@ -0,0 +1,9 @@ +package com.project.interaction.domain.service; + +import com.project.interaction.domain.dto.ClusterCallbackDTO; + +import java.util.List; + +public interface SaveClusterDomainService { + void saveCluster(Long taskId, List clusters) throws Exception; +} diff --git a/src/main/java/com/project/interactor/domain/service/impl/PostToClusteringDomainServiceImpl.java b/src/main/java/com/project/interaction/domain/service/impl/PostToClusteringDomainServiceImpl.java similarity index 88% rename from src/main/java/com/project/interactor/domain/service/impl/PostToClusteringDomainServiceImpl.java rename to src/main/java/com/project/interaction/domain/service/impl/PostToClusteringDomainServiceImpl.java index d9cd40c..686962e 100644 --- a/src/main/java/com/project/interactor/domain/service/impl/PostToClusteringDomainServiceImpl.java +++ b/src/main/java/com/project/interaction/domain/service/impl/PostToClusteringDomainServiceImpl.java @@ -1,9 +1,9 @@ -package com.project.interactor.domain.service.impl; +package com.project.interaction.domain.service.impl; import com.project.information.domain.dto.KnowledgePointDTO; -import com.project.interactor.domain.dto.ClusterQueryDTO; -import com.project.interactor.domain.dto.ClusterResultDTO; -import com.project.interactor.domain.service.PostToClusteringDomainService; +import com.project.interaction.domain.dto.ClusterQueryDTO; +import com.project.interaction.domain.dto.ClusterResultDTO; +import com.project.interaction.domain.service.PostToClusteringDomainService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/project/interaction/domain/service/impl/SaveClusterDomainServiceImpl.java b/src/main/java/com/project/interaction/domain/service/impl/SaveClusterDomainServiceImpl.java new file mode 100644 index 0000000..2303d66 --- /dev/null +++ b/src/main/java/com/project/interaction/domain/service/impl/SaveClusterDomainServiceImpl.java @@ -0,0 +1,90 @@ +package com.project.interaction.domain.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.project.information.domain.dto.KnowledgePointDTO; +import com.project.information.domain.entity.KnowledgePointEntity; +import com.project.information.domain.service.KnowledgePointBaseService; +import com.project.interaction.domain.dto.ClusterCallbackDTO; +import com.project.interaction.domain.service.SaveClusterDomainService; +import com.project.question.domain.entity.TaskKnowledgeClusterEntity; +import com.project.question.domain.entity.TaskKnowledgePointEntity; +import com.project.question.domain.enums.QuestionSourceTypeEnum; +import com.project.question.domain.service.GenerateQuestionDomainService; +import com.project.question.domain.service.TaskKnowledgeClusterBaseService; +import com.project.question.domain.service.TaskKnowledgePointBaseService; +import com.project.task.domain.entity.TaskEntity; +import com.project.task.domain.enums.QuestionTypeEnum; +import com.project.task.domain.service.TaskBaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +@Service +@Slf4j +public class SaveClusterDomainServiceImpl implements SaveClusterDomainService { + @Autowired + private TaskBaseService taskBaseService; + + @Autowired + private TaskKnowledgeClusterBaseService taskKnowledgeClusterBaseService; + + @Autowired + private TaskKnowledgePointBaseService taskKnowledgePointBaseService; + + + @Autowired + private KnowledgePointBaseService knowledgePointBaseService; + + @Autowired + private GenerateQuestionDomainService generateQuestionDomainService; + + @Override + public void saveCluster(Long taskId, List clusters) throws Exception { + TaskEntity taskEntity = taskBaseService.getById(taskId); + int u = 0; + if (Objects.isNull(taskEntity) || Objects.isNull(taskEntity.getParticipantNum()) || taskEntity.getParticipantNum() <= 0) { + return; + } + u = taskEntity.getParticipantNum(); + log.info(">>> [回调处理] 开始持久化任务[{}]聚类结果,计划人数U={}", taskId, u); + for (ClusterCallbackDTO.ClusterItem cluster : clusters) { + // 1.保存任务知识点簇 + TaskKnowledgeClusterEntity clusterEntity = new TaskKnowledgeClusterEntity(); + clusterEntity.setTaskId(taskId); + clusterEntity.setClusterTopic(cluster.getTopic()); + clusterEntity.setClusterSize(cluster.getAtomIdList().size()); + taskKnowledgeClusterBaseService.save(clusterEntity); + // 2.提取该簇下的原子知识点,并保存任务知识点 + List kpList = knowledgePointBaseService.list(new LambdaQueryWrapper() + .in(KnowledgePointEntity::getId, cluster.getAtomIdList())) + .stream().map(entity -> entity.toDTO(KnowledgePointDTO::new)).toList(); + + List taskKpList = kpList.stream().map(atomKp -> { + TaskKnowledgePointEntity taskKp = new TaskKnowledgePointEntity(); + taskKp.setTaskId(taskId); + taskKp.setClusterId(clusterEntity.getId()); + taskKp.setAtomId(atomKp.getId()); + taskKp.setContent(atomKp.getContent()); + return taskKp; + }).toList(); + // 批量插入 + taskKnowledgePointBaseService.saveBatch(taskKpList); + // 生成目标量 + int targetNum = (int) Math.ceil(1.2 * u); + for (TaskKnowledgePointEntity kp : taskKpList) { + // 调用题目生成方法 + for (QuestionTypeEnum value : QuestionTypeEnum.values()) { + generateQuestionDomainService.produce(kp.getId(), value , QuestionSourceTypeEnum.Single_Concept , targetNum); + } + } + if (clusterEntity.getClusterSize() > 1) { + // 簇生成目标量 + int clusterTargetNum = (int) Math.ceil(1.2 * u * clusterEntity.getClusterSize()); + generateQuestionDomainService.produce(clusterEntity.getId(), QuestionTypeEnum.MULTIPLE_CHOICE ,QuestionSourceTypeEnum.Multi_Concept , clusterTargetNum); + } + } + } +} diff --git a/src/main/java/com/project/interactor/application/AlgorithmApplicationService.java b/src/main/java/com/project/interactor/application/AlgorithmApplicationService.java deleted file mode 100644 index e0f5637..0000000 --- a/src/main/java/com/project/interactor/application/AlgorithmApplicationService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.project.interactor.application; - -import com.project.information.domain.dto.KnowledgePointDTO; - -import java.util.List; - -public interface AlgorithmApplicationService { - - void postToClustering(Long taskId, List kpList); -} diff --git a/src/main/java/com/project/interactor/application/impl/AlgorithmApplicationServiceImpl.java b/src/main/java/com/project/interactor/application/impl/AlgorithmApplicationServiceImpl.java deleted file mode 100644 index 9d3978a..0000000 --- a/src/main/java/com/project/interactor/application/impl/AlgorithmApplicationServiceImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.project.interactor.application.impl; - -import com.project.information.domain.dto.KnowledgePointDTO; -import com.project.interactor.application.AlgorithmApplicationService; -import com.project.interactor.domain.service.PostToClusteringDomainService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public class AlgorithmApplicationServiceImpl implements AlgorithmApplicationService { - @Autowired - private PostToClusteringDomainService postToClusteringDomainService; - @Override - public void postToClustering(Long taskId, List kpList) { - postToClusteringDomainService.postToClustering(taskId , kpList); - } -} diff --git a/src/main/java/com/project/question/application/QuestionApplicationService.java b/src/main/java/com/project/question/application/QuestionApplicationService.java new file mode 100644 index 0000000..e7832f1 --- /dev/null +++ b/src/main/java/com/project/question/application/QuestionApplicationService.java @@ -0,0 +1,4 @@ +package com.project.question.application; + +public interface QuestionApplicationService { +} diff --git a/src/main/java/com/project/question/application/TaskKnowledgeClusterApplicationService.java b/src/main/java/com/project/question/application/TaskKnowledgeClusterApplicationService.java new file mode 100644 index 0000000..4aeec24 --- /dev/null +++ b/src/main/java/com/project/question/application/TaskKnowledgeClusterApplicationService.java @@ -0,0 +1,4 @@ +package com.project.question.application; + +public interface TaskKnowledgeClusterApplicationService { +} diff --git a/src/main/java/com/project/question/application/impl/QuestionApplicationServiceImpl.java b/src/main/java/com/project/question/application/impl/QuestionApplicationServiceImpl.java new file mode 100644 index 0000000..2bf0348 --- /dev/null +++ b/src/main/java/com/project/question/application/impl/QuestionApplicationServiceImpl.java @@ -0,0 +1,8 @@ +package com.project.question.application.impl; + +import com.project.question.application.QuestionApplicationService; +import org.springframework.stereotype.Service; + +@Service +public class QuestionApplicationServiceImpl implements QuestionApplicationService { +} diff --git a/src/main/java/com/project/question/application/impl/TaskKnowledgeClusterApplicationServiceImpl.java b/src/main/java/com/project/question/application/impl/TaskKnowledgeClusterApplicationServiceImpl.java new file mode 100644 index 0000000..c4beafb --- /dev/null +++ b/src/main/java/com/project/question/application/impl/TaskKnowledgeClusterApplicationServiceImpl.java @@ -0,0 +1,8 @@ +package com.project.question.application.impl; + +import com.project.question.application.TaskKnowledgeClusterApplicationService; +import org.springframework.stereotype.Service; + +@Service +public class TaskKnowledgeClusterApplicationServiceImpl implements TaskKnowledgeClusterApplicationService { +} diff --git a/src/main/java/com/project/question/config/QuestionAsyncThreadPoolConfig.java b/src/main/java/com/project/question/config/QuestionAsyncThreadPoolConfig.java new file mode 100644 index 0000000..55f54a1 --- /dev/null +++ b/src/main/java/com/project/question/config/QuestionAsyncThreadPoolConfig.java @@ -0,0 +1,29 @@ +package com.project.question.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +public class QuestionAsyncThreadPoolConfig { + + /** + * 题目生成池 (高并发、IO密集型) + * 核心:允许并行处理不同的知识点 + */ + @Bean(name = "questionGenExecutor") + public Executor questionGenExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + // 允许 5-10 个知识点同时进行 AI 出题 + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(500); + executor.setThreadNamePrefix("question-gen-"); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/com/project/question/domain/dto/QuestionDTO.java b/src/main/java/com/project/question/domain/dto/QuestionDTO.java new file mode 100644 index 0000000..3627c45 --- /dev/null +++ b/src/main/java/com/project/question/domain/dto/QuestionDTO.java @@ -0,0 +1,9 @@ +package com.project.question.domain.dto; + +import com.project.base.domain.dto.BaseDTO; +import lombok.Data; + +@Data +public class QuestionDTO extends BaseDTO { + private Long id; +} diff --git a/src/main/java/com/project/question/domain/dto/TaskKnowledgePointDTO.java b/src/main/java/com/project/question/domain/dto/TaskKnowledgePointDTO.java new file mode 100644 index 0000000..85d0b8e --- /dev/null +++ b/src/main/java/com/project/question/domain/dto/TaskKnowledgePointDTO.java @@ -0,0 +1,10 @@ +package com.project.question.domain.dto; + +import com.project.base.domain.dto.BaseDTO; +import lombok.Data; + +@Data +public class TaskKnowledgePointDTO extends BaseDTO { + private Long id; + +} diff --git a/src/main/java/com/project/question/domain/entity/QuestionEntity.java b/src/main/java/com/project/question/domain/entity/QuestionEntity.java index 3f482fb..1cef48e 100644 --- a/src/main/java/com/project/question/domain/entity/QuestionEntity.java +++ b/src/main/java/com/project/question/domain/entity/QuestionEntity.java @@ -53,8 +53,9 @@ public class QuestionEntity extends BaseEntity { @TableField("unique_hash") private String uniqueHash; - @Column(name = "question_type", columnDefinition = "comment '题目类型'") + @Column(name = "question_type") @TableField("question_type") + @Comment("题目类型") private Integer questionType; @TableField(value = "question_detail" , typeHandler = JacksonTypeHandler.class) diff --git a/src/main/java/com/project/question/domain/entity/QuestionKpRelEntity.java b/src/main/java/com/project/question/domain/entity/QuestionKpRelEntity.java new file mode 100644 index 0000000..6117799 --- /dev/null +++ b/src/main/java/com/project/question/domain/entity/QuestionKpRelEntity.java @@ -0,0 +1,50 @@ +package com.project.question.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.project.base.domain.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Comment; + +@Data +@Entity +@Table(name = "evaluator_question_kp_rel", indexes = { + @Index(name = "idx_kp_id", columnList = "kp_id"), + @Index(name = "idx_question_id", columnList = "question_id"), + @Index(name = "idx_task_id", columnList = "task_id") +}) +@TableName("evaluator_question_kp_rel") +@EqualsAndHashCode(callSuper = true) +public class QuestionKpRelEntity extends BaseEntity { + @TableId(type = IdType.ASSIGN_ID) + @Id + private Long id; + + @Column(name = "question_id") + @Comment("题目ID") + @TableField("question_id") + private Long questionId; + + @Column(name = "kp_id") + @Comment("考试任务知识点ID") + private Long kpId; + + @Column(name = "cluster_id") + @TableField("cluster_id") + @Comment("所属知识点簇ID") + private Long clusterId; + + @Column(name = "task_id") + @Comment("所属考试任务ID (冗余字段,方便清理及统计)") + @TableField("task_id") + private Long taskId; + + @Column(name = "question_type") + @TableField("question_type") + @Comment("题目类型") + private Integer questionType; +} diff --git a/src/main/java/com/project/task/domain/entity/TaskKnowledgeClusterEntity.java b/src/main/java/com/project/question/domain/entity/TaskKnowledgeClusterEntity.java similarity index 96% rename from src/main/java/com/project/task/domain/entity/TaskKnowledgeClusterEntity.java rename to src/main/java/com/project/question/domain/entity/TaskKnowledgeClusterEntity.java index 5aeefb4..6c1a8c2 100644 --- a/src/main/java/com/project/task/domain/entity/TaskKnowledgeClusterEntity.java +++ b/src/main/java/com/project/question/domain/entity/TaskKnowledgeClusterEntity.java @@ -1,4 +1,4 @@ -package com.project.task.domain.entity; +package com.project.question.domain.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; diff --git a/src/main/java/com/project/task/domain/entity/TaskKnowledgePointEntity.java b/src/main/java/com/project/question/domain/entity/TaskKnowledgePointEntity.java similarity index 96% rename from src/main/java/com/project/task/domain/entity/TaskKnowledgePointEntity.java rename to src/main/java/com/project/question/domain/entity/TaskKnowledgePointEntity.java index 9dbb7c5..be4d448 100644 --- a/src/main/java/com/project/task/domain/entity/TaskKnowledgePointEntity.java +++ b/src/main/java/com/project/question/domain/entity/TaskKnowledgePointEntity.java @@ -1,4 +1,4 @@ -package com.project.task.domain.entity; +package com.project.question.domain.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; diff --git a/src/main/java/com/project/question/domain/enums/QuestionUseStatusEnum.java b/src/main/java/com/project/question/domain/enums/QuestionUseStatusEnum.java index 21bb3c3..19c4d35 100644 --- a/src/main/java/com/project/question/domain/enums/QuestionUseStatusEnum.java +++ b/src/main/java/com/project/question/domain/enums/QuestionUseStatusEnum.java @@ -4,8 +4,8 @@ import com.project.base.domain.enums.HasValueEnum; import lombok.Getter; import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor @Getter +@RequiredArgsConstructor public enum QuestionUseStatusEnum implements HasValueEnum { Not_Use(Boolean.FALSE) , Used(Boolean.TRUE); diff --git a/src/main/java/com/project/question/domain/service/GenerateQuestionDomainService.java b/src/main/java/com/project/question/domain/service/GenerateQuestionDomainService.java new file mode 100644 index 0000000..6e8b3bc --- /dev/null +++ b/src/main/java/com/project/question/domain/service/GenerateQuestionDomainService.java @@ -0,0 +1,9 @@ +package com.project.question.domain.service; + +import com.project.question.domain.enums.QuestionSourceTypeEnum; +import com.project.task.domain.enums.QuestionTypeEnum; + +public interface GenerateQuestionDomainService { + + void produce(Long sourceId, QuestionTypeEnum questionType , QuestionSourceTypeEnum sourceType, int needCount); +} diff --git a/src/main/java/com/project/question/domain/service/SamplingStrategy.java b/src/main/java/com/project/question/domain/service/SamplingStrategy.java new file mode 100644 index 0000000..037d983 --- /dev/null +++ b/src/main/java/com/project/question/domain/service/SamplingStrategy.java @@ -0,0 +1,10 @@ +package com.project.question.domain.service; + +import com.project.question.domain.dto.TaskKnowledgePointDTO; + +import java.util.List; + +public interface SamplingStrategy { + + List sample(Long clusterId); +} diff --git a/src/main/java/com/project/question/domain/service/TaskKnowledgeClusterBaseService.java b/src/main/java/com/project/question/domain/service/TaskKnowledgeClusterBaseService.java new file mode 100644 index 0000000..2446a08 --- /dev/null +++ b/src/main/java/com/project/question/domain/service/TaskKnowledgeClusterBaseService.java @@ -0,0 +1,7 @@ +package com.project.question.domain.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.project.question.domain.entity.TaskKnowledgeClusterEntity; + +public interface TaskKnowledgeClusterBaseService extends IService { +} diff --git a/src/main/java/com/project/question/domain/service/TaskKnowledgePointBaseService.java b/src/main/java/com/project/question/domain/service/TaskKnowledgePointBaseService.java new file mode 100644 index 0000000..b23e43a --- /dev/null +++ b/src/main/java/com/project/question/domain/service/TaskKnowledgePointBaseService.java @@ -0,0 +1,7 @@ +package com.project.question.domain.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.project.question.domain.entity.TaskKnowledgePointEntity; + +public interface TaskKnowledgePointBaseService extends IService { +} diff --git a/src/main/java/com/project/question/domain/service/impl/GenerateQuestionDomainServiceImpl.java b/src/main/java/com/project/question/domain/service/impl/GenerateQuestionDomainServiceImpl.java new file mode 100644 index 0000000..c8013ff --- /dev/null +++ b/src/main/java/com/project/question/domain/service/impl/GenerateQuestionDomainServiceImpl.java @@ -0,0 +1,61 @@ +package com.project.question.domain.service.impl; + +import com.project.question.domain.dto.TaskKnowledgePointDTO; +import com.project.question.domain.enums.QuestionSourceTypeEnum; +import com.project.question.domain.service.GenerateQuestionDomainService; +import com.project.question.domain.service.SamplingStrategy; +import com.project.question.domain.service.TaskKnowledgePointBaseService; +import com.project.task.domain.enums.QuestionTypeEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +@Service +@Slf4j +public class GenerateQuestionDomainServiceImpl implements GenerateQuestionDomainService { + + @Resource(name = "questionGenExecutor") + private Executor questionGenExecutor; + + @Autowired + private TaskKnowledgePointBaseService taskKnowledgePointBaseService; + + @Autowired + private SamplingStrategy samplingStrategy; + + @Override + public void produce(Long sourceId, QuestionTypeEnum questionType , QuestionSourceTypeEnum sourceType, int needCount) { + if (needCount <= 0) { + return; + } + CompletableFuture.runAsync(() -> { + log.info(">>> [题库预生成] 准备生产任务: SourceID={}, Type={}, Count={}", sourceId, sourceType, needCount); + + processBatch(sourceId,questionType , sourceType , needCount); + + }, questionGenExecutor); + + } + + private void processBatch(Long sourceId, QuestionTypeEnum questionType , QuestionSourceTypeEnum sourceType, int count) { + // 如果是簇复合题,执行随机采样 2-4 个知识点 + List kpList; + if (QuestionSourceTypeEnum.Multi_Concept.equals(sourceType)) { + kpList = samplingStrategy.sample(sourceId); // 获取随机组合的 KPs + } else { + // 单 KP 逻辑:直接查出对应 KP 的内容 + kpList = Collections.singletonList(taskKnowledgePointBaseService.getById(sourceId) + .toDTO(TaskKnowledgePointDTO::new)); + } + + // 2. 调用大模型并保存题目及其多对多关系 (QuestionPool & QuestionKpRel) + // + //aiLlmService.generateAndBatchSave(context, sourceType, count); + } +} diff --git a/src/main/java/com/project/question/domain/service/impl/RandomSamplingStrategy.java b/src/main/java/com/project/question/domain/service/impl/RandomSamplingStrategy.java new file mode 100644 index 0000000..9faf86f --- /dev/null +++ b/src/main/java/com/project/question/domain/service/impl/RandomSamplingStrategy.java @@ -0,0 +1,39 @@ +package com.project.question.domain.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.project.question.domain.dto.TaskKnowledgePointDTO; +import com.project.question.domain.entity.TaskKnowledgePointEntity; +import com.project.question.domain.service.SamplingStrategy; +import com.project.question.domain.service.TaskKnowledgePointBaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Service +public class RandomSamplingStrategy implements SamplingStrategy { + @Autowired + private TaskKnowledgePointBaseService taskKnowledgePointBaseService; + + @Override + public List sample(Long clusterId) { + List dtoList = taskKnowledgePointBaseService.list(new LambdaQueryWrapper() + .eq(TaskKnowledgePointEntity::getClusterId, clusterId)) + .stream().map(entity -> entity.toDTO(TaskKnowledgePointDTO::new)).toList(); + + if (CollUtil.isEmpty(dtoList)) { + return Collections.emptyList(); + } + // 确定采样数量:2 到 4 之间的随机数 + int totalSize = dtoList.size(); + int min = Math.min(totalSize, 2); + int max = Math.min(totalSize, 4); + int targetSampleSize = (min == max) ? min : RandomUtil.randomInt(min, max + 1); + Collections.shuffle(dtoList); + + return dtoList.subList(0 , targetSampleSize); + } +} diff --git a/src/main/java/com/project/question/domain/service/impl/TaskKnowledgeClusterBaseServiceImpl.java b/src/main/java/com/project/question/domain/service/impl/TaskKnowledgeClusterBaseServiceImpl.java new file mode 100644 index 0000000..f1b37cb --- /dev/null +++ b/src/main/java/com/project/question/domain/service/impl/TaskKnowledgeClusterBaseServiceImpl.java @@ -0,0 +1,12 @@ +package com.project.question.domain.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.project.question.domain.entity.TaskKnowledgeClusterEntity; +import com.project.question.domain.service.TaskKnowledgeClusterBaseService; +import com.project.question.mapper.TaskKnowledgeClusterMapper; +import org.springframework.stereotype.Service; + + +@Service +public class TaskKnowledgeClusterBaseServiceImpl extends ServiceImpl implements TaskKnowledgeClusterBaseService { +} diff --git a/src/main/java/com/project/question/domain/service/impl/TaskKnowledgePointBaseServiceImpl.java b/src/main/java/com/project/question/domain/service/impl/TaskKnowledgePointBaseServiceImpl.java new file mode 100644 index 0000000..c8c8bc4 --- /dev/null +++ b/src/main/java/com/project/question/domain/service/impl/TaskKnowledgePointBaseServiceImpl.java @@ -0,0 +1,12 @@ +package com.project.question.domain.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.project.question.domain.entity.TaskKnowledgePointEntity; +import com.project.question.domain.service.TaskKnowledgePointBaseService; +import com.project.question.mapper.TaskKnowledgePointMapper; +import org.springframework.stereotype.Service; + + +@Service +public class TaskKnowledgePointBaseServiceImpl extends ServiceImpl implements TaskKnowledgePointBaseService { +} diff --git a/src/main/java/com/project/task/mapper/TaskKnowledgeClusterMapper.java b/src/main/java/com/project/question/mapper/TaskKnowledgeClusterMapper.java similarity index 65% rename from src/main/java/com/project/task/mapper/TaskKnowledgeClusterMapper.java rename to src/main/java/com/project/question/mapper/TaskKnowledgeClusterMapper.java index 0419de4..5a741af 100644 --- a/src/main/java/com/project/task/mapper/TaskKnowledgeClusterMapper.java +++ b/src/main/java/com/project/question/mapper/TaskKnowledgeClusterMapper.java @@ -1,6 +1,6 @@ -package com.project.task.mapper; +package com.project.question.mapper; -import com.project.task.domain.entity.TaskKnowledgeClusterEntity; +import com.project.question.domain.entity.TaskKnowledgeClusterEntity; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; diff --git a/src/main/java/com/project/task/mapper/TaskKnowledgePointMapper.java b/src/main/java/com/project/question/mapper/TaskKnowledgePointMapper.java similarity index 65% rename from src/main/java/com/project/task/mapper/TaskKnowledgePointMapper.java rename to src/main/java/com/project/question/mapper/TaskKnowledgePointMapper.java index 9b6a218..a31c833 100644 --- a/src/main/java/com/project/task/mapper/TaskKnowledgePointMapper.java +++ b/src/main/java/com/project/question/mapper/TaskKnowledgePointMapper.java @@ -1,7 +1,7 @@ -package com.project.task.mapper; +package com.project.question.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.project.task.domain.entity.TaskKnowledgePointEntity; +import com.project.question.domain.entity.TaskKnowledgePointEntity; import org.apache.ibatis.annotations.Mapper; @Mapper diff --git a/src/main/java/com/project/task/config/TaskAsyncThreadPoolConfig.java b/src/main/java/com/project/task/config/TaskAsyncThreadPoolConfig.java index 9d278e7..b441813 100644 --- a/src/main/java/com/project/task/config/TaskAsyncThreadPoolConfig.java +++ b/src/main/java/com/project/task/config/TaskAsyncThreadPoolConfig.java @@ -11,7 +11,7 @@ import java.util.concurrent.ThreadPoolExecutor; public class TaskAsyncThreadPoolConfig { /** - * 1. 任务初始化池 (低频、轮询专用) + * 任务初始化池 (低频、轮询专用) */ @Bean(name = "taskInternalExecutor") public Executor taskInternalExecutor() { @@ -25,21 +25,6 @@ public class TaskAsyncThreadPoolConfig { return executor; } - /** - * 2. 题目生成池 (高并发、IO密集型) - * 核心:允许并行处理不同的知识点 - */ - @Bean(name = "questionGenExecutor") - public Executor questionGenExecutor() { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - // 允许 5-10 个知识点同时进行 AI 出题 - executor.setCorePoolSize(5); - executor.setMaxPoolSize(10); - executor.setQueueCapacity(500); - executor.setThreadNamePrefix("question-gen-"); - executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); - executor.initialize(); - return executor; - } + } diff --git a/src/main/java/com/project/task/domain/entity/ExamRecordEntity.java b/src/main/java/com/project/task/domain/entity/ExamRecordEntity.java index afe393b..d59f061 100644 --- a/src/main/java/com/project/task/domain/entity/ExamRecordEntity.java +++ b/src/main/java/com/project/task/domain/entity/ExamRecordEntity.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Map; @Data -@Table(name = "evaluator_exam_record" , indexes = {@Index(name = "Idx_parentId", columnList = "parent_id")}) +@Table(name = "evaluator_exam_record") @Entity @TableName(value = "evaluator_exam_record" , autoResultMap = true) @EqualsAndHashCode(callSuper = true) diff --git a/src/main/java/com/project/task/domain/entity/TaskUserEntity.java b/src/main/java/com/project/task/domain/entity/TaskUserEntity.java index d2da481..307298b 100644 --- a/src/main/java/com/project/task/domain/entity/TaskUserEntity.java +++ b/src/main/java/com/project/task/domain/entity/TaskUserEntity.java @@ -12,7 +12,7 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Comment; @Data -@Table(name = "evaluator_task_user" , indexes = {@Index(name = "Idx_parentId", columnList = "parent_id")}) +@Table(name = "evaluator_task_user") @Entity @TableName(value = "evaluator_task_user" , autoResultMap = true) @EqualsAndHashCode(callSuper = true) diff --git a/src/main/java/com/project/task/domain/service/impl/InitTaskDomainServiceImpl.java b/src/main/java/com/project/task/domain/service/impl/InitTaskDomainServiceImpl.java index 76e37a3..d033fbf 100644 --- a/src/main/java/com/project/task/domain/service/impl/InitTaskDomainServiceImpl.java +++ b/src/main/java/com/project/task/domain/service/impl/InitTaskDomainServiceImpl.java @@ -7,7 +7,7 @@ import com.project.information.domain.entity.KnowledgePointEntity; import com.project.information.domain.enums.InformationParseStatusEnum; import com.project.information.domain.service.InformationBaseService; import com.project.information.domain.service.KnowledgePointBaseService; -import com.project.interactor.application.AlgorithmApplicationService; +import com.project.interaction.application.AlgorithmApplicationService; import com.project.task.domain.service.InitTaskDomainService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j;