22 changed files with 1092 additions and 0 deletions
@ -0,0 +1,42 @@ |
|||||
|
package com.project.classicpaper.application; |
||||
|
|
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperDTO; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; |
||||
|
import com.project.classicpaper.domain.dto.GenerateClassicPaperRequestDTO; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface ClassicPaperApplicationService { |
||||
|
|
||||
|
/** |
||||
|
* 批量生成经典套题(N个版本) |
||||
|
*/ |
||||
|
Result<List<ClassicPaperDTO>> generate(GenerateClassicPaperRequestDTO request) throws Exception; |
||||
|
|
||||
|
/** |
||||
|
* 单题重抽 |
||||
|
*/ |
||||
|
Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long questionId) throws Exception; |
||||
|
|
||||
|
/** |
||||
|
* 套卷列表 |
||||
|
*/ |
||||
|
Result<PageResult<ClassicPaperDTO>> list(Long subLineId, Long classicCategoryId, Integer status, Integer pageNum, Integer pageSize); |
||||
|
|
||||
|
/** |
||||
|
* 套卷详情(含题目列表) |
||||
|
*/ |
||||
|
Result<ClassicPaperDTO> getDetail(Long id); |
||||
|
|
||||
|
/** |
||||
|
* 套题组详情(查一组N个版本) |
||||
|
*/ |
||||
|
Result<List<ClassicPaperDTO>> getGroupDetail(Long paperGroupId); |
||||
|
|
||||
|
/** |
||||
|
* 废弃套卷 |
||||
|
*/ |
||||
|
Result<String> delete(Long id) throws Exception; |
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
package com.project.classicpaper.application.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.project.base.domain.exception.BusinessErrorException; |
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.base.domain.result.ResultCodeEnum; |
||||
|
import com.project.classicpaper.application.ClassicPaperApplicationService; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperDTO; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; |
||||
|
import com.project.classicpaper.domain.dto.GenerateClassicPaperRequestDTO; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperEntity; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; |
||||
|
import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum; |
||||
|
import com.project.classicpaper.domain.service.*; |
||||
|
import com.project.question.domain.entity.QuestionEntity; |
||||
|
import com.project.question.domain.service.QuestionBaseService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Service |
||||
|
public class ClassicPaperApplicationServiceImpl implements ClassicPaperApplicationService { |
||||
|
|
||||
|
@Autowired |
||||
|
private GenerateClassicPaperDomainService generateClassicPaperDomainService; |
||||
|
|
||||
|
@Autowired |
||||
|
private RegenerateQuestionDomainService regenerateQuestionDomainService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperBaseService classicPaperBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperQuestionBaseService classicPaperQuestionBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private QuestionBaseService questionBaseService; |
||||
|
|
||||
|
@Override |
||||
|
public Result<List<ClassicPaperDTO>> generate(GenerateClassicPaperRequestDTO request) throws Exception { |
||||
|
return generateClassicPaperDomainService.generate(request); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long questionId) throws Exception { |
||||
|
return regenerateQuestionDomainService.regenerate(paperId, questionId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<PageResult<ClassicPaperDTO>> list(Long subLineId, Long classicCategoryId, |
||||
|
Integer status, Integer pageNum, Integer pageSize) { |
||||
|
Page<ClassicPaperEntity> page = new Page<>(pageNum == null ? 1 : pageNum, pageSize == null ? 10 : pageSize); |
||||
|
|
||||
|
LambdaQueryWrapper<ClassicPaperEntity> wrapper = new LambdaQueryWrapper<>(); |
||||
|
if (subLineId != null) { |
||||
|
wrapper.eq(ClassicPaperEntity::getSubLineId, subLineId); |
||||
|
} |
||||
|
if (classicCategoryId != null) { |
||||
|
wrapper.eq(ClassicPaperEntity::getClassicCategoryId, classicCategoryId); |
||||
|
} |
||||
|
if (status != null) { |
||||
|
wrapper.eq(ClassicPaperEntity::getStatus, status); |
||||
|
} |
||||
|
wrapper.orderByDesc(ClassicPaperEntity::getCreateTime); |
||||
|
|
||||
|
classicPaperBaseService.page(page, wrapper); |
||||
|
|
||||
|
PageResult<ClassicPaperDTO> pageResult = new PageResult<>(page.convert(entity -> { |
||||
|
ClassicPaperDTO dto = entity.toDTO(ClassicPaperDTO::new); |
||||
|
dto.setStatusText(ClassicPaperStatusEnum.findByValue(entity.getStatus())); |
||||
|
return dto; |
||||
|
})); |
||||
|
return Result.success(pageResult); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<ClassicPaperDTO> getDetail(Long id) { |
||||
|
ClassicPaperEntity paper = classicPaperBaseService.getById(id); |
||||
|
if (paper == null) { |
||||
|
return Result.fail(ResultCodeEnum.RESOURCE_NOT_EXIST, "套卷不存在"); |
||||
|
} |
||||
|
|
||||
|
ClassicPaperDTO dto = paper.toDTO(ClassicPaperDTO::new); |
||||
|
dto.setStatusText(ClassicPaperStatusEnum.findByValue(paper.getStatus())); |
||||
|
|
||||
|
// 查询关联题目
|
||||
|
List<ClassicPaperQuestionEntity> paperQuestions = classicPaperQuestionBaseService.list( |
||||
|
new LambdaQueryWrapper<ClassicPaperQuestionEntity>() |
||||
|
.eq(ClassicPaperQuestionEntity::getPaperId, id) |
||||
|
.orderByAsc(ClassicPaperQuestionEntity::getSortOrder) |
||||
|
); |
||||
|
|
||||
|
if (!paperQuestions.isEmpty()) { |
||||
|
List<Long> questionIds = paperQuestions.stream() |
||||
|
.map(ClassicPaperQuestionEntity::getQuestionId).collect(Collectors.toList()); |
||||
|
List<QuestionEntity> questions = questionBaseService.listByIds(questionIds); |
||||
|
|
||||
|
List<ClassicPaperQuestionDTO> questionDTOs = new ArrayList<>(); |
||||
|
for (ClassicPaperQuestionEntity pq : paperQuestions) { |
||||
|
ClassicPaperQuestionDTO qDto = new ClassicPaperQuestionDTO(); |
||||
|
qDto.setId(pq.getId()); |
||||
|
qDto.setPaperId(pq.getPaperId()); |
||||
|
qDto.setQuestionId(pq.getQuestionId()); |
||||
|
qDto.setSortOrder(pq.getSortOrder()); |
||||
|
qDto.setQuestionType(pq.getQuestionType()); |
||||
|
questions.stream() |
||||
|
.filter(q -> q.getId().equals(pq.getQuestionId())) |
||||
|
.findFirst() |
||||
|
.ifPresent(q -> qDto.setQuestionDetail(q.getQuestionDetail())); |
||||
|
questionDTOs.add(qDto); |
||||
|
} |
||||
|
dto.setQuestionList(questionDTOs); |
||||
|
} |
||||
|
|
||||
|
return Result.success(dto); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<List<ClassicPaperDTO>> getGroupDetail(Long paperGroupId) { |
||||
|
List<ClassicPaperEntity> papers = classicPaperBaseService.list( |
||||
|
new LambdaQueryWrapper<ClassicPaperEntity>() |
||||
|
.eq(ClassicPaperEntity::getPaperGroupId, paperGroupId) |
||||
|
.orderByAsc(ClassicPaperEntity::getSetIndex) |
||||
|
); |
||||
|
if (papers.isEmpty()) { |
||||
|
return Result.fail(ResultCodeEnum.RESOURCE_NOT_EXIST, "套题组不存在"); |
||||
|
} |
||||
|
|
||||
|
List<ClassicPaperDTO> dtoList = papers.stream() |
||||
|
.map(entity -> { |
||||
|
ClassicPaperDTO dto = entity.toDTO(ClassicPaperDTO::new); |
||||
|
dto.setStatusText(ClassicPaperStatusEnum.findByValue(entity.getStatus())); |
||||
|
return dto; |
||||
|
}) |
||||
|
.collect(Collectors.toList()); |
||||
|
return Result.success(dtoList); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<String> delete(Long id) throws Exception { |
||||
|
ClassicPaperEntity paper = classicPaperBaseService.getById(id); |
||||
|
if (paper == null) { |
||||
|
throw new BusinessErrorException("套卷不存在"); |
||||
|
} |
||||
|
if (ClassicPaperStatusEnum.CONFIRMED.getValue().equals(paper.getStatus())) { |
||||
|
throw new BusinessErrorException("已固化的套卷不能废弃"); |
||||
|
} |
||||
|
paper.setStatus(ClassicPaperStatusEnum.DISCARDED.getValue()); |
||||
|
classicPaperBaseService.updateById(paper); |
||||
|
return Result.success("废弃成功"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
package com.project.classicpaper.controller; |
||||
|
|
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.classicpaper.application.ClassicPaperApplicationService; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperDTO; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; |
||||
|
import com.project.classicpaper.domain.dto.GenerateClassicPaperRequestDTO; |
||||
|
import com.project.operation.annotation.OperationLog; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@RestController |
||||
|
@Slf4j |
||||
|
@RequestMapping("/api/admin/classicPaper") |
||||
|
public class ClassicPaperController { |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperApplicationService classicPaperApplicationService; |
||||
|
|
||||
|
@PostMapping("/generate") |
||||
|
@OperationLog(module = "经典套题") |
||||
|
public Result<List<ClassicPaperDTO>> generate(@RequestBody GenerateClassicPaperRequestDTO request) throws Exception { |
||||
|
return classicPaperApplicationService.generate(request); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/regenerateQuestion") |
||||
|
@OperationLog(module = "经典套题") |
||||
|
public Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long questionId) throws Exception { |
||||
|
return classicPaperApplicationService.regenerateQuestion(paperId, questionId); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/list") |
||||
|
public Result<PageResult<ClassicPaperDTO>> list(Long subLineId, Long classicCategoryId, |
||||
|
Integer status, Integer pageNum, Integer pageSize) { |
||||
|
return classicPaperApplicationService.list(subLineId, classicCategoryId, status, pageNum, pageSize); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/detail") |
||||
|
public Result<ClassicPaperDTO> detail(Long id) { |
||||
|
return classicPaperApplicationService.getDetail(id); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/groupDetail") |
||||
|
public Result<List<ClassicPaperDTO>> groupDetail(Long paperGroupId) { |
||||
|
return classicPaperApplicationService.getGroupDetail(paperGroupId); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/delete") |
||||
|
@OperationLog(module = "经典套题") |
||||
|
public Result<String> delete(Long id) throws Exception { |
||||
|
return classicPaperApplicationService.delete(id); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
package com.project.classicpaper.domain.dto; |
||||
|
|
||||
|
import com.project.base.domain.dto.BaseDTO; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class ClassicPaperDTO extends BaseDTO { |
||||
|
private Long id; |
||||
|
|
||||
|
private String name; |
||||
|
|
||||
|
private String description; |
||||
|
|
||||
|
private Integer sourceType; |
||||
|
|
||||
|
private Long subLineId; |
||||
|
|
||||
|
private String subLineName; |
||||
|
|
||||
|
private Long classicCategoryId; |
||||
|
|
||||
|
private String classicCategoryName; |
||||
|
|
||||
|
private Long informationId; |
||||
|
|
||||
|
private Integer status; |
||||
|
|
||||
|
private String statusText; |
||||
|
|
||||
|
private Integer questionCount; |
||||
|
|
||||
|
private Long paperGroupId; |
||||
|
|
||||
|
private Integer setIndex; |
||||
|
|
||||
|
private List<ClassicPaperQuestionDTO> questionList; |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.project.classicpaper.domain.dto; |
||||
|
|
||||
|
import com.project.question.domain.entity.QuestionEntity; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class ClassicPaperQuestionDTO { |
||||
|
private Long id; |
||||
|
|
||||
|
private Long paperId; |
||||
|
|
||||
|
private Long questionId; |
||||
|
|
||||
|
private Integer sortOrder; |
||||
|
|
||||
|
private Integer questionType; |
||||
|
|
||||
|
/** |
||||
|
* 题目详情(从 QuestionEntity 关联查询) |
||||
|
*/ |
||||
|
private QuestionEntity.QuestionDetail questionDetail; |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
package com.project.classicpaper.domain.dto; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class GenerateClassicPaperRequestDTO { |
||||
|
|
||||
|
/** |
||||
|
* 子产品线ID(必填) |
||||
|
*/ |
||||
|
private Long subLineId; |
||||
|
|
||||
|
/** |
||||
|
* 经典分类ID(必填) |
||||
|
*/ |
||||
|
private Long classicCategoryId; |
||||
|
|
||||
|
/** |
||||
|
* 生成版本数 N,1~5(必填) |
||||
|
*/ |
||||
|
private Integer setCount; |
||||
|
|
||||
|
/** |
||||
|
* 套卷名称前缀(必填) |
||||
|
*/ |
||||
|
private String namePrefix; |
||||
|
|
||||
|
/** |
||||
|
* 套卷描述(可选) |
||||
|
*/ |
||||
|
private String description; |
||||
|
|
||||
|
/** |
||||
|
* 单选题数量 |
||||
|
*/ |
||||
|
private Integer singleChoiceNum; |
||||
|
|
||||
|
/** |
||||
|
* 多选题数量 |
||||
|
*/ |
||||
|
private Integer multipleChoiceNum; |
||||
|
|
||||
|
/** |
||||
|
* 判断题数量 |
||||
|
*/ |
||||
|
private Integer trueFalseNum; |
||||
|
|
||||
|
/** |
||||
|
* 简答题数量 |
||||
|
*/ |
||||
|
private Integer shortAnswerNum; |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
package com.project.classicpaper.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 |
||||
|
@Table(name = "evaluator_classic_paper", |
||||
|
indexes = {@Index(name = "Idx_sub_line_id", columnList = "sub_line_id"), |
||||
|
@Index(name = "Idx_status", columnList = "status"), |
||||
|
@Index(name = "Idx_paper_group_id", columnList = "paper_group_id")}) |
||||
|
@Entity |
||||
|
@TableName(value = "evaluator_classic_paper") |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class ClassicPaperEntity extends BaseEntity { |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
@Id |
||||
|
private Long id; |
||||
|
|
||||
|
@Column(name = "name", columnDefinition = "varchar(200) comment '套卷名称'") |
||||
|
@Comment("套卷名称") |
||||
|
private String name; |
||||
|
|
||||
|
@Column(name = "description", columnDefinition = "TEXT comment '套卷描述'") |
||||
|
@Comment("套卷描述") |
||||
|
private String description; |
||||
|
|
||||
|
@Column(name = "source_type") |
||||
|
@TableField("source_type") |
||||
|
@Comment("来源:0-Word导入,1-AI生成") |
||||
|
private Integer sourceType; |
||||
|
|
||||
|
@Column(name = "sub_line_id") |
||||
|
@TableField("sub_line_id") |
||||
|
@Comment("来源子产品线ID") |
||||
|
private Long subLineId; |
||||
|
|
||||
|
@Column(name = "information_id") |
||||
|
@TableField("information_id") |
||||
|
@Comment("关联资料ID") |
||||
|
private Long informationId; |
||||
|
|
||||
|
@Column(name = "status") |
||||
|
@TableField("status") |
||||
|
@Comment("状态:0-生成中,1-草稿,2-已固化,3-已废弃") |
||||
|
private Integer status; |
||||
|
|
||||
|
@Column(name = "question_count") |
||||
|
@TableField("question_count") |
||||
|
@Comment("题目总数") |
||||
|
private Integer questionCount; |
||||
|
|
||||
|
@Column(name = "paper_group_id") |
||||
|
@TableField("paper_group_id") |
||||
|
@Comment("套题组ID,N个版本共享") |
||||
|
private Long paperGroupId; |
||||
|
|
||||
|
@Column(name = "set_index") |
||||
|
@TableField("set_index") |
||||
|
@Comment("组内序号(1~N)") |
||||
|
private Integer setIndex; |
||||
|
|
||||
|
@Column(name = "classic_category_id") |
||||
|
@TableField("classic_category_id") |
||||
|
@Comment("经典分类ID") |
||||
|
private Long classicCategoryId; |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
package com.project.classicpaper.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 |
||||
|
@Table(name = "evaluator_classic_paper_question", |
||||
|
indexes = {@Index(name = "Idx_paper_id", columnList = "paper_id"), |
||||
|
@Index(name = "Idx_question_id", columnList = "question_id")}) |
||||
|
@Entity |
||||
|
@TableName(value = "evaluator_classic_paper_question") |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class ClassicPaperQuestionEntity extends BaseEntity { |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
@Id |
||||
|
private Long id; |
||||
|
|
||||
|
@Column(name = "paper_id") |
||||
|
@TableField("paper_id") |
||||
|
@Comment("套卷ID") |
||||
|
private Long paperId; |
||||
|
|
||||
|
@Column(name = "question_id") |
||||
|
@TableField("question_id") |
||||
|
@Comment("题目ID") |
||||
|
private Long questionId; |
||||
|
|
||||
|
@Column(name = "sort_order") |
||||
|
@TableField("sort_order") |
||||
|
@Comment("排序序号") |
||||
|
private Integer sortOrder; |
||||
|
|
||||
|
@Column(name = "question_type") |
||||
|
@TableField("question_type") |
||||
|
@Comment("题型") |
||||
|
private Integer questionType; |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
package com.project.classicpaper.domain.enums; |
||||
|
|
||||
|
import com.project.base.domain.enums.HasValueEnum; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
@Getter |
||||
|
public enum ClassicPaperSourceTypeEnum implements HasValueEnum<Integer> { |
||||
|
WORD_IMPORT(0, "Word导入"), |
||||
|
AI_GENERATED(1, "AI生成"); |
||||
|
|
||||
|
private final Integer value; |
||||
|
private final String desc; |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.project.classicpaper.domain.enums; |
||||
|
|
||||
|
import com.project.base.domain.enums.HasValueEnum; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
@Getter |
||||
|
public enum ClassicPaperStatusEnum implements HasValueEnum<Integer> { |
||||
|
GENERATING(0, "生成中"), |
||||
|
DRAFT(1, "草稿"), |
||||
|
CONFIRMED(2, "已固化"), |
||||
|
DISCARDED(3, "已废弃"); |
||||
|
|
||||
|
private final Integer value; |
||||
|
private final String desc; |
||||
|
|
||||
|
public static String findByValue(Integer value) { |
||||
|
for (ClassicPaperStatusEnum e : values()) { |
||||
|
if (e.value.equals(value)) { |
||||
|
return e.desc; |
||||
|
} |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
package com.project.classicpaper.domain.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperEntity; |
||||
|
|
||||
|
public interface ClassicPaperBaseService extends IService<ClassicPaperEntity> { |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
package com.project.classicpaper.domain.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; |
||||
|
|
||||
|
public interface ClassicPaperQuestionBaseService extends IService<ClassicPaperQuestionEntity> { |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.project.classicpaper.domain.service; |
||||
|
|
||||
|
import com.project.information.domain.entity.KnowledgePointEntity; |
||||
|
import com.project.question.domain.entity.QuestionEntity; |
||||
|
import com.project.task.domain.enums.QuestionTypeEnum; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 经典套卷题目生成器接口 |
||||
|
* 当前使用降级实现,后续替换为算法服务新接口实现 |
||||
|
*/ |
||||
|
public interface ClassicPaperQuestionGenerator { |
||||
|
|
||||
|
/** |
||||
|
* 为经典套卷生成题目 |
||||
|
* |
||||
|
* @param knowledgePoints 知识点列表(素材) |
||||
|
* @param questionType 题型 |
||||
|
* @param count 生成数量 |
||||
|
* @return 生成的题目实体列表(已保存到数据库) |
||||
|
*/ |
||||
|
List<QuestionEntity> generate(List<KnowledgePointEntity> knowledgePoints, |
||||
|
QuestionTypeEnum questionType, int count) throws Exception; |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
package com.project.classicpaper.domain.service; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperDTO; |
||||
|
import com.project.classicpaper.domain.dto.GenerateClassicPaperRequestDTO; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface GenerateClassicPaperDomainService { |
||||
|
|
||||
|
/** |
||||
|
* 批量生成经典套题(N个版本) |
||||
|
*/ |
||||
|
Result<List<ClassicPaperDTO>> generate(GenerateClassicPaperRequestDTO request) throws Exception; |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package com.project.classicpaper.domain.service; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; |
||||
|
|
||||
|
public interface RegenerateQuestionDomainService { |
||||
|
|
||||
|
/** |
||||
|
* 单题重抽:替换套卷中的某道题 |
||||
|
* |
||||
|
* @param paperId 套卷ID |
||||
|
* @param questionId 原题目ID |
||||
|
* @return 新题目信息 |
||||
|
*/ |
||||
|
Result<ClassicPaperQuestionDTO> regenerate(Long paperId, Long questionId) throws Exception; |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.project.classicpaper.domain.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperEntity; |
||||
|
import com.project.classicpaper.domain.service.ClassicPaperBaseService; |
||||
|
import com.project.classicpaper.mapper.ClassicPaperMapper; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
public class ClassicPaperBaseServiceImpl extends ServiceImpl<ClassicPaperMapper, ClassicPaperEntity> implements ClassicPaperBaseService { |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.project.classicpaper.domain.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; |
||||
|
import com.project.classicpaper.domain.service.ClassicPaperQuestionBaseService; |
||||
|
import com.project.classicpaper.mapper.ClassicPaperQuestionMapper; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
public class ClassicPaperQuestionBaseServiceImpl extends ServiceImpl<ClassicPaperQuestionMapper, ClassicPaperQuestionEntity> implements ClassicPaperQuestionBaseService { |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
package com.project.classicpaper.domain.service.impl; |
||||
|
|
||||
|
import cn.hutool.core.util.RandomUtil; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.information.domain.entity.KnowledgePointEntity; |
||||
|
import com.project.question.domain.dto.QuestionDTO; |
||||
|
import com.project.question.domain.entity.QuestionEntity; |
||||
|
import com.project.question.domain.service.SaveQuestionDomainService; |
||||
|
import com.project.task.domain.enums.QuestionTypeEnum; |
||||
|
import com.project.classicpaper.domain.service.ClassicPaperQuestionGenerator; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.*; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
/** |
||||
|
* 降级实现:生成占位题目 |
||||
|
* 当算法服务新接口未就绪时使用,后续替换为真正的算法调用 |
||||
|
*/ |
||||
|
@Component |
||||
|
@Slf4j |
||||
|
public class FallbackClassicPaperQuestionGenerator implements ClassicPaperQuestionGenerator { |
||||
|
|
||||
|
@Autowired |
||||
|
private SaveQuestionDomainService saveQuestionDomainService; |
||||
|
|
||||
|
private final String[] optionListStr = {"A", "B", "C", "D"}; |
||||
|
private final String[] trueFalseListStr = {"A", "B"}; |
||||
|
private final List<Integer> list = Arrays.asList(0, 1, 2, 3); |
||||
|
|
||||
|
@Override |
||||
|
public List<QuestionEntity> generate(List<KnowledgePointEntity> knowledgePoints, |
||||
|
QuestionTypeEnum questionType, int count) throws Exception { |
||||
|
log.info(">>> [经典套题-降级] 开始生成占位题目, 题型: {}, 数量: {}, 知识点数: {}", |
||||
|
questionType, count, knowledgePoints.size()); |
||||
|
|
||||
|
List<QuestionEntity> result = new ArrayList<>(); |
||||
|
for (int i = 0; i < count; i++) { |
||||
|
KnowledgePointEntity kp = knowledgePoints.get(i % knowledgePoints.size()); |
||||
|
|
||||
|
QuestionDTO questionDTO = new QuestionDTO(); |
||||
|
questionDTO.setKpIdList(Collections.singletonList(kp.getId())); |
||||
|
questionDTO.setQuestionType(questionType.getValue()); |
||||
|
questionDTO.setSourceType(0); |
||||
|
|
||||
|
QuestionDTO.QuestionDetailDTO detailDTO = new QuestionDTO.QuestionDetailDTO(); |
||||
|
questionDTO.setQuestionDetailDTO(detailDTO); |
||||
|
detailDTO.setQuestionContent(String.format("【经典套题】%s - 第%d题(%s)", |
||||
|
kp.getParseName(), i + 1, questionType.getDescription())); |
||||
|
detailDTO.setType(questionType.getValue()); |
||||
|
|
||||
|
if (QuestionTypeEnum.SINGLE_CHOICE.equals(questionType)) { |
||||
|
int rightAnswerNo = RandomUtil.randomInt(0, 4); |
||||
|
TreeMap<String, String> optionList = new TreeMap<>(); |
||||
|
for (int j = 0; j < optionListStr.length; j++) { |
||||
|
optionList.put(optionListStr[j], String.format("选项%s", optionListStr[j])); |
||||
|
if (j == rightAnswerNo) { |
||||
|
detailDTO.setRightAnswer(optionListStr[j]); |
||||
|
detailDTO.setAnalysis(String.format("正确答案是%s", optionListStr[j])); |
||||
|
} |
||||
|
} |
||||
|
detailDTO.setOptions(optionList); |
||||
|
} else if (QuestionTypeEnum.MULTIPLE_CHOICE.equals(questionType)) { |
||||
|
Collections.shuffle(list); |
||||
|
Set<Integer> resultIdx = list.subList(0, 2).stream().collect(Collectors.toSet()); |
||||
|
TreeMap<String, String> optionList = new TreeMap<>(); |
||||
|
List<String> rightAnswerList = new ArrayList<>(); |
||||
|
for (int j = 0; j < optionListStr.length; j++) { |
||||
|
optionList.put(optionListStr[j], String.format("选项%s", optionListStr[j])); |
||||
|
if (resultIdx.contains(j)) { |
||||
|
rightAnswerList.add(optionListStr[j]); |
||||
|
} |
||||
|
} |
||||
|
detailDTO.setRightAnswer(String.join(",", rightAnswerList)); |
||||
|
detailDTO.setAnalysis(String.format("正确答案是%s", detailDTO.getRightAnswer())); |
||||
|
detailDTO.setOptions(optionList); |
||||
|
} else if (QuestionTypeEnum.TRUE_FALSE.equals(questionType)) { |
||||
|
int rightAnswerNo = RandomUtil.randomInt(0, 2); |
||||
|
TreeMap<String, String> optionList = new TreeMap<>(); |
||||
|
for (int j = 0; j < trueFalseListStr.length; j++) { |
||||
|
optionList.put(trueFalseListStr[j], j == 0 ? "对" : "错"); |
||||
|
if (j == rightAnswerNo) { |
||||
|
detailDTO.setRightAnswer(trueFalseListStr[j]); |
||||
|
detailDTO.setAnalysis(String.format("正确答案是%s", trueFalseListStr[j])); |
||||
|
} |
||||
|
} |
||||
|
detailDTO.setOptions(optionList); |
||||
|
} else { |
||||
|
// 简答题
|
||||
|
detailDTO.setRightAnswer("参考答案"); |
||||
|
detailDTO.setAnalysis("解析"); |
||||
|
} |
||||
|
|
||||
|
Result<QuestionDTO> saveResult = saveQuestionDomainService.save(questionDTO); |
||||
|
QuestionDTO saved = saveResult.getData(); |
||||
|
|
||||
|
QuestionEntity savedEntity = new QuestionEntity(); |
||||
|
savedEntity.setId(saved.getId()); |
||||
|
savedEntity.setQuestionType(questionType.getValue()); |
||||
|
savedEntity.setKpIdList(saved.getKpIdList()); |
||||
|
result.add(savedEntity); |
||||
|
} |
||||
|
|
||||
|
log.info(">>> [经典套题-降级] 生成完成, 共{}题", result.size()); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,222 @@ |
|||||
|
package com.project.classicpaper.domain.service.impl; |
||||
|
|
||||
|
import cn.hutool.core.collection.CollUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.project.base.config.SnowflakeIdWorker; |
||||
|
import com.project.base.domain.exception.BusinessErrorException; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperDTO; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; |
||||
|
import com.project.classicpaper.domain.dto.GenerateClassicPaperRequestDTO; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperEntity; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; |
||||
|
import com.project.classicpaper.domain.enums.ClassicPaperSourceTypeEnum; |
||||
|
import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum; |
||||
|
import com.project.classicpaper.domain.service.*; |
||||
|
import com.project.information.domain.entity.InformationEntity; |
||||
|
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.question.domain.entity.QuestionEntity; |
||||
|
import com.project.task.domain.enums.QuestionTypeEnum; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.util.*; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class GenerateClassicPaperDomainServiceImpl implements GenerateClassicPaperDomainService { |
||||
|
|
||||
|
@Autowired |
||||
|
private InformationBaseService informationBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private KnowledgePointBaseService knowledgePointBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperBaseService classicPaperBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperQuestionBaseService classicPaperQuestionBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperQuestionGenerator classicPaperQuestionGenerator; |
||||
|
|
||||
|
private final SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(0, 0); |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Result<List<ClassicPaperDTO>> generate(GenerateClassicPaperRequestDTO request) throws Exception { |
||||
|
// 1. 校验参数
|
||||
|
validateRequest(request); |
||||
|
|
||||
|
// 2. 查询子产品线下已解析成功的资料
|
||||
|
List<InformationEntity> informations = informationBaseService.list( |
||||
|
new LambdaQueryWrapper<InformationEntity>() |
||||
|
.eq(InformationEntity::getSubLineId, request.getSubLineId()) |
||||
|
.eq(InformationEntity::getParseStatus, InformationParseStatusEnum.Success.getValue()) |
||||
|
); |
||||
|
if (CollUtil.isEmpty(informations)) { |
||||
|
throw new BusinessErrorException("该子产品线下没有已解析成功的资料"); |
||||
|
} |
||||
|
|
||||
|
// 3. 查询这些资料的所有知识点
|
||||
|
List<Long> informationIds = informations.stream() |
||||
|
.map(InformationEntity::getId).collect(Collectors.toList()); |
||||
|
List<KnowledgePointEntity> allKps = knowledgePointBaseService.list( |
||||
|
new LambdaQueryWrapper<KnowledgePointEntity>() |
||||
|
.in(KnowledgePointEntity::getInformationId, informationIds) |
||||
|
); |
||||
|
if (CollUtil.isEmpty(allKps)) { |
||||
|
throw new BusinessErrorException("该子产品线下没有知识点数据"); |
||||
|
} |
||||
|
|
||||
|
// 4. 计算总题目数并校验知识点数量
|
||||
|
int totalPerSet = Optional.ofNullable(request.getSingleChoiceNum()).orElse(0) |
||||
|
+ Optional.ofNullable(request.getMultipleChoiceNum()).orElse(0) |
||||
|
+ Optional.ofNullable(request.getTrueFalseNum()).orElse(0) |
||||
|
+ Optional.ofNullable(request.getShortAnswerNum()).orElse(0); |
||||
|
if (totalPerSet <= 0) { |
||||
|
throw new BusinessErrorException("至少需要一种题型的数量大于0"); |
||||
|
} |
||||
|
int setCount = request.getSetCount(); |
||||
|
if (allKps.size() < totalPerSet) { |
||||
|
throw new BusinessErrorException("知识点数量(" + allKps.size() + ")不足以生成每套 " + totalPerSet + " 道题"); |
||||
|
} |
||||
|
|
||||
|
// 5. 创建 paperGroupId
|
||||
|
Long paperGroupId = snowflakeIdWorker.nextId(); |
||||
|
|
||||
|
// 6. 构建题型配置列表
|
||||
|
List<QuestionTypeConfig> typeConfigs = buildTypeConfigs(request); |
||||
|
|
||||
|
// 7. 循环生成 N 个版本
|
||||
|
List<ClassicPaperDTO> paperDTOs = new ArrayList<>(); |
||||
|
for (int i = 1; i <= setCount; i++) { |
||||
|
ClassicPaperDTO paperDTO = generateOnePaper(paperGroupId, i, request, allKps, typeConfigs); |
||||
|
paperDTOs.add(paperDTO); |
||||
|
} |
||||
|
|
||||
|
log.info(">>> [经典套题] 批量生成完成, paperGroupId={}, 版本数={}, 每套题数={}", |
||||
|
paperGroupId, setCount, totalPerSet); |
||||
|
return Result.success(paperDTOs); |
||||
|
} |
||||
|
|
||||
|
private void validateRequest(GenerateClassicPaperRequestDTO request) { |
||||
|
if (request.getSubLineId() == null) { |
||||
|
throw new BusinessErrorException("子产品线ID不能为空"); |
||||
|
} |
||||
|
if (request.getClassicCategoryId() == null) { |
||||
|
throw new BusinessErrorException("经典分类不能为空"); |
||||
|
} |
||||
|
if (request.getSetCount() == null || request.getSetCount() < 1 || request.getSetCount() > 5) { |
||||
|
throw new BusinessErrorException("版本数必须在1~5之间"); |
||||
|
} |
||||
|
if (StrUtil.isBlank(request.getNamePrefix())) { |
||||
|
throw new BusinessErrorException("套卷名称前缀不能为空"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<QuestionTypeConfig> buildTypeConfigs(GenerateClassicPaperRequestDTO request) { |
||||
|
List<QuestionTypeConfig> configs = new ArrayList<>(); |
||||
|
if (Optional.ofNullable(request.getSingleChoiceNum()).orElse(0) > 0) { |
||||
|
configs.add(new QuestionTypeConfig(QuestionTypeEnum.SINGLE_CHOICE, request.getSingleChoiceNum())); |
||||
|
} |
||||
|
if (Optional.ofNullable(request.getMultipleChoiceNum()).orElse(0) > 0) { |
||||
|
configs.add(new QuestionTypeConfig(QuestionTypeEnum.MULTIPLE_CHOICE, request.getMultipleChoiceNum())); |
||||
|
} |
||||
|
if (Optional.ofNullable(request.getTrueFalseNum()).orElse(0) > 0) { |
||||
|
configs.add(new QuestionTypeConfig(QuestionTypeEnum.TRUE_FALSE, request.getTrueFalseNum())); |
||||
|
} |
||||
|
if (Optional.ofNullable(request.getShortAnswerNum()).orElse(0) > 0) { |
||||
|
configs.add(new QuestionTypeConfig(QuestionTypeEnum.SHORT_ANSWER, request.getShortAnswerNum())); |
||||
|
} |
||||
|
return configs; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成一个版本的套卷 |
||||
|
*/ |
||||
|
private ClassicPaperDTO generateOnePaper(Long paperGroupId, int setIndex, |
||||
|
GenerateClassicPaperRequestDTO request, |
||||
|
List<KnowledgePointEntity> allKps, |
||||
|
List<QuestionTypeConfig> typeConfigs) throws Exception { |
||||
|
// 创建套卷实体
|
||||
|
ClassicPaperEntity paper = new ClassicPaperEntity(); |
||||
|
paper.setName(request.getNamePrefix() + "-V" + setIndex); |
||||
|
paper.setDescription(request.getDescription()); |
||||
|
paper.setSourceType(ClassicPaperSourceTypeEnum.AI_GENERATED.getValue()); |
||||
|
paper.setSubLineId(request.getSubLineId()); |
||||
|
paper.setClassicCategoryId(request.getClassicCategoryId()); |
||||
|
paper.setStatus(ClassicPaperStatusEnum.DRAFT.getValue()); |
||||
|
paper.setPaperGroupId(paperGroupId); |
||||
|
paper.setSetIndex(setIndex); |
||||
|
paper.setQuestionCount(0); |
||||
|
classicPaperBaseService.save(paper); |
||||
|
|
||||
|
// 打乱知识点顺序,每个版本用不同的排列
|
||||
|
List<KnowledgePointEntity> shuffledKps = new ArrayList<>(allKps); |
||||
|
Collections.shuffle(shuffledKps); |
||||
|
|
||||
|
int totalQuestions = 0; |
||||
|
int sortOrder = 1; |
||||
|
List<ClassicPaperQuestionEntity> paperQuestionEntities = new ArrayList<>(); |
||||
|
|
||||
|
// 对每种题型生成题目
|
||||
|
for (QuestionTypeConfig config : typeConfigs) { |
||||
|
// 从知识点池中采样(不同版本使用不同组合)
|
||||
|
List<KnowledgePointEntity> sampledKps = sampleKps(shuffledKps, config.count); |
||||
|
|
||||
|
List<QuestionEntity> questions = classicPaperQuestionGenerator.generate( |
||||
|
sampledKps, config.questionType, config.count); |
||||
|
|
||||
|
for (QuestionEntity question : questions) { |
||||
|
ClassicPaperQuestionEntity pq = new ClassicPaperQuestionEntity(); |
||||
|
pq.setPaperId(paper.getId()); |
||||
|
pq.setQuestionId(question.getId()); |
||||
|
pq.setSortOrder(sortOrder++); |
||||
|
pq.setQuestionType(config.questionType.getValue()); |
||||
|
paperQuestionEntities.add(pq); |
||||
|
totalQuestions++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 批量保存关联关系
|
||||
|
classicPaperQuestionBaseService.saveBatch(paperQuestionEntities); |
||||
|
|
||||
|
// 更新题目总数
|
||||
|
paper.setQuestionCount(totalQuestions); |
||||
|
classicPaperBaseService.updateById(paper); |
||||
|
|
||||
|
// 构建返回 DTO
|
||||
|
ClassicPaperDTO dto = paper.toDTO(ClassicPaperDTO::new); |
||||
|
dto.setStatusText(ClassicPaperStatusEnum.DRAFT.getDesc()); |
||||
|
return dto; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从知识点池中采样指定数量的知识点 |
||||
|
*/ |
||||
|
private List<KnowledgePointEntity> sampleKps(List<KnowledgePointEntity> pool, int count) { |
||||
|
if (pool.size() <= count) { |
||||
|
return new ArrayList<>(pool); |
||||
|
} |
||||
|
return new ArrayList<>(pool.subList(0, count)); |
||||
|
} |
||||
|
|
||||
|
private static class QuestionTypeConfig { |
||||
|
final QuestionTypeEnum questionType; |
||||
|
final int count; |
||||
|
|
||||
|
QuestionTypeConfig(QuestionTypeEnum questionType, int count) { |
||||
|
this.questionType = questionType; |
||||
|
this.count = count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,124 @@ |
|||||
|
package com.project.classicpaper.domain.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.project.base.domain.exception.BusinessErrorException; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperEntity; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; |
||||
|
import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum; |
||||
|
import com.project.classicpaper.domain.service.ClassicPaperBaseService; |
||||
|
import com.project.classicpaper.domain.service.ClassicPaperQuestionBaseService; |
||||
|
import com.project.classicpaper.domain.service.ClassicPaperQuestionGenerator; |
||||
|
import com.project.classicpaper.domain.service.RegenerateQuestionDomainService; |
||||
|
import com.project.information.domain.entity.KnowledgePointEntity; |
||||
|
import com.project.information.domain.service.KnowledgePointBaseService; |
||||
|
import com.project.question.domain.entity.QuestionEntity; |
||||
|
import com.project.question.domain.entity.QuestionKpRelEntity; |
||||
|
import com.project.question.domain.service.QuestionBaseService; |
||||
|
import com.project.question.domain.service.QuestionKpRelBaseService; |
||||
|
import com.project.task.domain.enums.QuestionTypeEnum; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDomainService { |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperBaseService classicPaperBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperQuestionBaseService classicPaperQuestionBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private QuestionBaseService questionBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private QuestionKpRelBaseService questionKpRelBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private KnowledgePointBaseService knowledgePointBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ClassicPaperQuestionGenerator classicPaperQuestionGenerator; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Result<ClassicPaperQuestionDTO> regenerate(Long paperId, Long questionId) throws Exception { |
||||
|
// 1. 校验套卷存在且为草稿状态
|
||||
|
ClassicPaperEntity paper = classicPaperBaseService.getById(paperId); |
||||
|
if (paper == null) { |
||||
|
throw new BusinessErrorException("套卷不存在"); |
||||
|
} |
||||
|
if (!ClassicPaperStatusEnum.DRAFT.getValue().equals(paper.getStatus())) { |
||||
|
throw new BusinessErrorException("只有草稿状态的套卷才能重抽题目"); |
||||
|
} |
||||
|
|
||||
|
// 2. 校验题目属于该套卷
|
||||
|
ClassicPaperQuestionEntity paperQuestion = classicPaperQuestionBaseService.getOne( |
||||
|
new LambdaQueryWrapper<ClassicPaperQuestionEntity>() |
||||
|
.eq(ClassicPaperQuestionEntity::getPaperId, paperId) |
||||
|
.eq(ClassicPaperQuestionEntity::getQuestionId, questionId) |
||||
|
); |
||||
|
if (paperQuestion == null) { |
||||
|
throw new BusinessErrorException("该题目不属于此套卷"); |
||||
|
} |
||||
|
|
||||
|
// 3. 查询原题目信息
|
||||
|
QuestionEntity originalQuestion = questionBaseService.getById(questionId); |
||||
|
if (originalQuestion == null) { |
||||
|
throw new BusinessErrorException("原题目不存在"); |
||||
|
} |
||||
|
|
||||
|
// 4. 查询原题目的知识点关联
|
||||
|
List<QuestionKpRelEntity> kpRels = questionKpRelBaseService.list( |
||||
|
new LambdaQueryWrapper<QuestionKpRelEntity>() |
||||
|
.eq(QuestionKpRelEntity::getQuestionId, questionId) |
||||
|
); |
||||
|
if (kpRels.isEmpty()) { |
||||
|
throw new BusinessErrorException("原题目没有关联知识点,无法重抽"); |
||||
|
} |
||||
|
List<Long> kpIds = kpRels.stream() |
||||
|
.map(QuestionKpRelEntity::getKpId).collect(Collectors.toList()); |
||||
|
|
||||
|
// 5. 查询知识点详情
|
||||
|
List<KnowledgePointEntity> knowledgePoints = knowledgePointBaseService.listByIds(kpIds); |
||||
|
if (knowledgePoints.isEmpty()) { |
||||
|
throw new BusinessErrorException("关联知识点数据不存在"); |
||||
|
} |
||||
|
|
||||
|
// 6. 调用生成器生成新题
|
||||
|
QuestionTypeEnum questionType = QuestionTypeEnum.findByValue(originalQuestion.getQuestionType()); |
||||
|
List<QuestionEntity> newQuestions = classicPaperQuestionGenerator.generate( |
||||
|
knowledgePoints, questionType, 1); |
||||
|
if (newQuestions.isEmpty()) { |
||||
|
throw new BusinessErrorException("生成新题失败"); |
||||
|
} |
||||
|
QuestionEntity newQuestion = newQuestions.get(0); |
||||
|
|
||||
|
// 7. 更新关联关系,指向新题
|
||||
|
paperQuestion.setQuestionId(newQuestion.getId()); |
||||
|
classicPaperQuestionBaseService.updateById(paperQuestion); |
||||
|
|
||||
|
log.info(">>> [经典套题-重抽] 套卷{} 题目{} -> 新题目{}", paperId, questionId, newQuestion.getId()); |
||||
|
|
||||
|
// 8. 构建返回
|
||||
|
ClassicPaperQuestionDTO dto = new ClassicPaperQuestionDTO(); |
||||
|
dto.setId(paperQuestion.getId()); |
||||
|
dto.setPaperId(paperId); |
||||
|
dto.setQuestionId(newQuestion.getId()); |
||||
|
dto.setSortOrder(paperQuestion.getSortOrder()); |
||||
|
dto.setQuestionType(paperQuestion.getQuestionType()); |
||||
|
if (newQuestion.getQuestionDetail() != null) { |
||||
|
dto.setQuestionDetail(newQuestion.getQuestionDetail()); |
||||
|
} |
||||
|
return Result.success(dto); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package com.project.classicpaper.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface ClassicPaperMapper extends BaseMapper<ClassicPaperEntity> { |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package com.project.classicpaper.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface ClassicPaperQuestionMapper extends BaseMapper<ClassicPaperQuestionEntity> { |
||||
|
} |
||||
Loading…
Reference in new issue