Browse Source

重新生题改成异步

master
luoweijian 3 months ago
parent
commit
71506ffcb9
  1. 17
      src/main/java/com/project/classicpaper/application/ClassicPaperApplicationService.java
  2. 2
      src/main/java/com/project/classicpaper/application/ClassicPaperSetApplicationService.java
  3. 12
      src/main/java/com/project/classicpaper/application/impl/ClassicPaperApplicationServiceImpl.java
  4. 31
      src/main/java/com/project/classicpaper/application/impl/ClassicPaperSetApplicationServiceImpl.java
  5. 24
      src/main/java/com/project/classicpaper/controller/ClassicPaperController.java
  6. 4
      src/main/java/com/project/classicpaper/controller/ClassicPaperSetController.java
  7. 8
      src/main/java/com/project/classicpaper/domain/dto/ClassicPaperQuestionDTO.java
  8. 5
      src/main/java/com/project/classicpaper/domain/entity/ClassicPaperQuestionEntity.java
  9. 54
      src/main/java/com/project/classicpaper/domain/entity/RegenerateQuestionTaskEntity.java
  10. 21
      src/main/java/com/project/classicpaper/domain/service/RegenerateQuestionDomainService.java
  11. 7
      src/main/java/com/project/classicpaper/domain/service/RegenerateQuestionTaskBaseService.java
  12. 2
      src/main/java/com/project/classicpaper/domain/service/SavePaperSetDomainService.java
  13. 6
      src/main/java/com/project/classicpaper/domain/service/impl/DraftPaperDomainServiceImpl.java
  14. 166
      src/main/java/com/project/classicpaper/domain/service/impl/RegenerateQuestionDomainServiceImpl.java
  15. 11
      src/main/java/com/project/classicpaper/domain/service/impl/RegenerateQuestionTaskBaseServiceImpl.java
  16. 156
      src/main/java/com/project/classicpaper/domain/service/impl/SavePaperSetDomainServiceImpl.java
  17. 9
      src/main/java/com/project/classicpaper/mapper/RegenerateQuestionTaskMapper.java
  18. 16
      src/main/java/com/project/operation/config/AsyncConfig.java
  19. 10
      src/main/resources/application-dev.yml
  20. 8
      src/main/resources/application-prod.yml
  21. 8
      src/main/resources/application-test.yml

17
src/main/java/com/project/classicpaper/application/ClassicPaperApplicationService.java

@ -4,13 +4,28 @@ import com.project.base.domain.result.Result;
import com.project.classicpaper.domain.dto.ClassicPaperDTO; import com.project.classicpaper.domain.dto.ClassicPaperDTO;
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO;
import java.util.Map;
public interface ClassicPaperApplicationService { public interface ClassicPaperApplicationService {
/** /**
* 单题重 DRAFT 状态的版本可操作 * 单题重新生题同步
*/ */
Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long paperQuestionId) throws Exception; Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long paperQuestionId) throws Exception;
/**
* 单题重新生题异步提交任务后立即返回前端通过轮询状态接口获取结果
*/
void regenerateQuestionAsync(Long paperQuestionId) throws Exception;
/**
* 查询重新生题任务状态
*
* @param paperQuestionId 套卷题目关联ID
* @return 状态信息status(0进行中/1成功/2失败), newQuestionId, errorMessage无任务返回 null
*/
Map<String, Object> getRegenerateStatus(Long paperQuestionId);
/** /**
* 切tab暂存保存单个版本的编辑名称描述题目排序/替换 * 切tab暂存保存单个版本的编辑名称描述题目排序/替换
*/ */

2
src/main/java/com/project/classicpaper/application/ClassicPaperSetApplicationService.java

@ -30,7 +30,7 @@ public interface ClassicPaperSetApplicationService {
Result<String> delete(Long setId) throws Exception; Result<String> delete(Long setId) throws Exception;
/** /**
* 保存固化全量保存套题组所有版本校验通过后锁定为CONFIRMED * 更新分值比例同时计算每道题的分值
*/ */
Result<ClassicPaperSetDTO> save(ClassicPaperSetDTO request) throws Exception; Result<ClassicPaperSetDTO> save(ClassicPaperSetDTO request) throws Exception;
} }

12
src/main/java/com/project/classicpaper/application/impl/ClassicPaperApplicationServiceImpl.java

@ -9,6 +9,8 @@ import com.project.classicpaper.domain.service.RegenerateQuestionDomainService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Map;
@Service @Service
public class ClassicPaperApplicationServiceImpl implements ClassicPaperApplicationService { public class ClassicPaperApplicationServiceImpl implements ClassicPaperApplicationService {
@ -23,6 +25,16 @@ public class ClassicPaperApplicationServiceImpl implements ClassicPaperApplicati
return regenerateQuestionDomainService.regenerate(paperId, paperQuestionId); return regenerateQuestionDomainService.regenerate(paperId, paperQuestionId);
} }
@Override
public void regenerateQuestionAsync(Long paperQuestionId) throws Exception {
regenerateQuestionDomainService.regenerateAsync(paperQuestionId);
}
@Override
public Map<String, Object> getRegenerateStatus(Long paperQuestionId) {
return regenerateQuestionDomainService.getTaskStatus(paperQuestionId);
}
@Override @Override
public Result<ClassicPaperDTO> draft(ClassicPaperDTO request) throws Exception { public Result<ClassicPaperDTO> draft(ClassicPaperDTO request) throws Exception {
return draftPaperDomainService.draft(request); return draftPaperDomainService.draft(request);

31
src/main/java/com/project/classicpaper/application/impl/ClassicPaperSetApplicationServiceImpl.java

@ -15,6 +15,7 @@ import com.project.classicpaper.domain.dto.ClassicPaperSetDTO;
import com.project.classicpaper.domain.entity.ClassicPaperEntity; import com.project.classicpaper.domain.entity.ClassicPaperEntity;
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity;
import com.project.classicpaper.domain.entity.ClassicPaperSetEntity; import com.project.classicpaper.domain.entity.ClassicPaperSetEntity;
import com.project.classicpaper.domain.entity.RegenerateQuestionTaskEntity;
import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum; import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum;
import com.project.classicpaper.domain.param.ClassicPaperSetParam; import com.project.classicpaper.domain.param.ClassicPaperSetParam;
import com.project.classicpaper.domain.service.*; import com.project.classicpaper.domain.service.*;
@ -24,10 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -53,6 +51,9 @@ public class ClassicPaperSetApplicationServiceImpl implements ClassicPaperSetApp
@Autowired @Autowired
private SavePaperSetDomainService savePaperSetDomainService; private SavePaperSetDomainService savePaperSetDomainService;
@Autowired
private RegenerateQuestionTaskBaseService regenerateQuestionTaskBaseService;
@Autowired @Autowired
private com.project.task.config.ExamScoreRatioConfig examScoreRatioConfig; private com.project.task.config.ExamScoreRatioConfig examScoreRatioConfig;
@ -146,6 +147,20 @@ public class ClassicPaperSetApplicationServiceImpl implements ClassicPaperSetApp
.map(ClassicPaperQuestionEntity::getQuestionId).collect(Collectors.toList()); .map(ClassicPaperQuestionEntity::getQuestionId).collect(Collectors.toList());
List<QuestionEntity> questions = questionBaseService.listByIds(questionIds); List<QuestionEntity> questions = questionBaseService.listByIds(questionIds);
// 查询正在进行中的重新生题任务
List<Long> paperQuestionIds = paperQuestions.stream()
.map(ClassicPaperQuestionEntity::getId).collect(Collectors.toList());
Set<Long> regeneratingIds = new HashSet<>();
if (!paperQuestionIds.isEmpty()) {
List<RegenerateQuestionTaskEntity> inProgressTasks = regenerateQuestionTaskBaseService.list(
new LambdaQueryWrapper<RegenerateQuestionTaskEntity>()
.in(RegenerateQuestionTaskEntity::getPaperQuestionId, paperQuestionIds)
.eq(RegenerateQuestionTaskEntity::getStatus, 0));
regeneratingIds = inProgressTasks.stream()
.map(RegenerateQuestionTaskEntity::getPaperQuestionId)
.collect(Collectors.toSet());
}
List<ClassicPaperQuestionDTO> questionDTOs = new ArrayList<>(); List<ClassicPaperQuestionDTO> questionDTOs = new ArrayList<>();
for (ClassicPaperQuestionEntity pq : paperQuestions) { for (ClassicPaperQuestionEntity pq : paperQuestions) {
ClassicPaperQuestionDTO qDto = new ClassicPaperQuestionDTO(); ClassicPaperQuestionDTO qDto = new ClassicPaperQuestionDTO();
@ -154,6 +169,8 @@ public class ClassicPaperSetApplicationServiceImpl implements ClassicPaperSetApp
qDto.setQuestionId(pq.getQuestionId()); qDto.setQuestionId(pq.getQuestionId());
qDto.setSortOrder(pq.getSortOrder()); qDto.setSortOrder(pq.getSortOrder());
qDto.setQuestionType(pq.getQuestionType()); qDto.setQuestionType(pq.getQuestionType());
qDto.setScore(pq.getScore());
qDto.setRegenerating(regeneratingIds.contains(pq.getId()));
questions.stream() questions.stream()
.filter(q -> q.getId().equals(pq.getQuestionId())) .filter(q -> q.getId().equals(pq.getQuestionId()))
.findFirst() .findFirst()
@ -173,9 +190,6 @@ public class ClassicPaperSetApplicationServiceImpl implements ClassicPaperSetApp
if (paperSet == null) { if (paperSet == null) {
throw new BusinessErrorException("套题组不存在"); throw new BusinessErrorException("套题组不存在");
} }
if (ClassicPaperStatusEnum.CONFIRMED.getValue().equals(paperSet.getStatus())) {
throw new BusinessErrorException("已固化的套题组不能废弃");
}
paperSet.setStatus(ClassicPaperStatusEnum.DISCARDED.getValue()); paperSet.setStatus(ClassicPaperStatusEnum.DISCARDED.getValue());
classicPaperSetBaseService.updateById(paperSet); classicPaperSetBaseService.updateById(paperSet);
@ -188,6 +202,9 @@ public class ClassicPaperSetApplicationServiceImpl implements ClassicPaperSetApp
return Result.success("废弃成功"); return Result.success("废弃成功");
} }
/**
* 更新分值比例同时计算每道题的分值
*/
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Result<ClassicPaperSetDTO> save(ClassicPaperSetDTO request) throws Exception { public Result<ClassicPaperSetDTO> save(ClassicPaperSetDTO request) throws Exception {

24
src/main/java/com/project/classicpaper/controller/ClassicPaperController.java

@ -9,6 +9,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController @RestController
@Slf4j @Slf4j
@RequestMapping("/api/admin/classicPaper") @RequestMapping("/api/admin/classicPaper")
@ -17,12 +19,34 @@ public class ClassicPaperController {
@Autowired @Autowired
private ClassicPaperApplicationService classicPaperApplicationService; private ClassicPaperApplicationService classicPaperApplicationService;
/**
* 单题重新生题同步等待算法返回结果适合响应快的场景
*/
@PostMapping("/regenerateQuestion") @PostMapping("/regenerateQuestion")
@OperationLog(module = "经典套题") @OperationLog(module = "经典套题")
public Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long paperQuestionId) throws Exception { public Result<ClassicPaperQuestionDTO> regenerateQuestion(Long paperId, Long paperQuestionId) throws Exception {
return classicPaperApplicationService.regenerateQuestion(paperId, paperQuestionId); return classicPaperApplicationService.regenerateQuestion(paperId, paperQuestionId);
} }
/**
* 单题重新生题异步立即返回前端通过轮询状态接口获取结果
*/
@PostMapping("/regenerateQuestion/async")
@OperationLog(module = "经典套题")
public Result<String> regenerateQuestionAsync(Long paperQuestionId) throws Exception {
classicPaperApplicationService.regenerateQuestionAsync(paperQuestionId);
return Result.success("接收成功");
}
/**
* 查询重新生题任务状态前端轮询
*/
@GetMapping("/regenerateQuestion/status")
public Result<Map<String, Object>> getRegenerateStatus(Long paperQuestionId) {
Map<String, Object> status = classicPaperApplicationService.getRegenerateStatus(paperQuestionId);
return Result.success(status);
}
/** /**
* 切tab暂存保存当前版本的编辑名称描述题目排序/替换 * 切tab暂存保存当前版本的编辑名称描述题目排序/替换
*/ */

4
src/main/java/com/project/classicpaper/controller/ClassicPaperSetController.java

@ -41,10 +41,10 @@ public class ClassicPaperSetController {
} }
/** /**
* 保存固化全量保存套题组所有版本校验通过后锁定为CONFIRMED * 更新分值比例同时计算每道题的分值
*/ */
@PostMapping("/save") @PostMapping("/save")
public Result<ClassicPaperSetDTO> save(@RequestBody ClassicPaperSetDTO request) throws Exception { public Result<ClassicPaperSetDTO> save(ClassicPaperSetDTO request) throws Exception {
return classicPaperSetApplicationService.save(request); return classicPaperSetApplicationService.save(request);
} }
} }

8
src/main/java/com/project/classicpaper/domain/dto/ClassicPaperQuestionDTO.java

@ -11,7 +11,7 @@ public class ClassicPaperQuestionDTO {
/** 套卷ID */ /** 套卷ID */
private Long paperId; private Long paperId;
/** 题目ID(QuestionEntity 主键),标识具体题目内容,重抽题目时传入此字段 */ /** 题目ID(QuestionEntity 主键),标识具体题目内容,重新生题时传入此字段 */
private Long questionId; private Long questionId;
/** 题目在套卷中的排序序号 */ /** 题目在套卷中的排序序号 */
@ -20,6 +20,12 @@ public class ClassicPaperQuestionDTO {
/** 题型:0-单选,1-多选,2-判断,4-简答 */ /** 题型:0-单选,1-多选,2-判断,4-简答 */
private Integer questionType; private Integer questionType;
/** 该题分值(固化时由分值比例计算得出) */
private Double score;
/** 是否正在重新生题中:true-显示loading,false-显示题目 */
private Boolean regenerating;
/** /**
* 题目详情 QuestionEntity 关联查询 * 题目详情 QuestionEntity 关联查询
*/ */

5
src/main/java/com/project/classicpaper/domain/entity/ClassicPaperQuestionEntity.java

@ -41,4 +41,9 @@ public class ClassicPaperQuestionEntity extends BaseEntity {
@TableField("question_type") @TableField("question_type")
@Comment("题型") @Comment("题型")
private Integer questionType; private Integer questionType;
@Column(name = "score")
@TableField("score")
@Comment("该题分值(固化时由分值比例计算得出)")
private Double score;
} }

54
src/main/java/com/project/classicpaper/domain/entity/RegenerateQuestionTaskEntity.java

@ -0,0 +1,54 @@
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_regenerate_question_task",
indexes = {@Index(name = "Idx_task_id", columnList = "task_id")})
@Entity
@TableName(value = "evaluator_regenerate_question_task")
@EqualsAndHashCode(callSuper = true)
public class RegenerateQuestionTaskEntity extends BaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
@Id
private Long id;
@Column(name = "task_id", columnDefinition = "varchar(64) comment '任务ID(UUID)'")
@TableField("task_id")
@Comment("任务ID(UUID)")
private String taskId;
@Column(name = "paper_id")
@TableField("paper_id")
@Comment("套卷ID")
private Long paperId;
@Column(name = "paper_question_id")
@TableField("paper_question_id")
@Comment("套卷题目关联ID")
private Long paperQuestionId;
@Column(name = "status")
@TableField("status")
@Comment("状态:0-进行中,1-成功,2-失败")
private Integer status;
@Column(name = "new_question_id")
@TableField("new_question_id")
@Comment("新题目ID")
private Long newQuestionId;
@Column(name = "error_message", columnDefinition = "TEXT")
@TableField("error_message")
@Comment("失败原因")
private String errorMessage;
}

21
src/main/java/com/project/classicpaper/domain/service/RegenerateQuestionDomainService.java

@ -3,14 +3,31 @@ package com.project.classicpaper.domain.service;
import com.project.base.domain.result.Result; import com.project.base.domain.result.Result;
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO;
import java.util.Map;
public interface RegenerateQuestionDomainService { public interface RegenerateQuestionDomainService {
/** /**
* 单题重替换套卷中的某道题 * 单题重新生题同步替换套卷中的某道题等待算法返回结果
* *
* @param paperId 套卷ID * @param paperId 套卷ID
* @param paperQuestionId 关联记录IDClassicPaperQuestionEntity 主键 * @param paperQuestionId 关联记录IDClassicPaperQuestionEntity 主键
* @return 新题目信息 * @return 新题目信息
*/ */
Result<ClassicPaperQuestionDTO> regenerate(Long paperId, Long paperQuestionId) throws Exception; Result<ClassicPaperQuestionDTO> regenerate(Long paperId, Long paperQuestionId) throws Exception;
/**
* 单题重新生题异步提交任务后立即返回前端通过轮询状态接口获取结果
*
* @param paperQuestionId 关联记录IDClassicPaperQuestionEntity 主键
*/
void regenerateAsync(Long paperQuestionId) throws Exception;
/**
* 查询重新生题任务状态 paperQuestionId 查询
*
* @param paperQuestionId 套卷题目关联ID
* @return 状态信息status(0进行中/1成功/2失败), newQuestionId, errorMessage无任务返回 null
*/
Map<String, Object> getTaskStatus(Long paperQuestionId);
} }

7
src/main/java/com/project/classicpaper/domain/service/RegenerateQuestionTaskBaseService.java

@ -0,0 +1,7 @@
package com.project.classicpaper.domain.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.project.classicpaper.domain.entity.RegenerateQuestionTaskEntity;
public interface RegenerateQuestionTaskBaseService extends IService<RegenerateQuestionTaskEntity> {
}

2
src/main/java/com/project/classicpaper/domain/service/SavePaperSetDomainService.java

@ -6,7 +6,7 @@ import com.project.classicpaper.domain.dto.ClassicPaperSetDTO;
public interface SavePaperSetDomainService { public interface SavePaperSetDomainService {
/** /**
* 保存固化全量保存套题组所有版本校验通过后锁定为CONFIRMED * 更新分值比例同时计算每道题的分值
* 返回 setId由调用方自行查询详情组装返回 * 返回 setId由调用方自行查询详情组装返回
*/ */
Result<Long> save(ClassicPaperSetDTO request) throws Exception; Result<Long> save(ClassicPaperSetDTO request) throws Exception;

6
src/main/java/com/project/classicpaper/domain/service/impl/DraftPaperDomainServiceImpl.java

@ -39,14 +39,11 @@ public class DraftPaperDomainServiceImpl implements DraftPaperDomainService {
@Override @Override
public Result<ClassicPaperDTO> draft(ClassicPaperDTO request) throws Exception { public Result<ClassicPaperDTO> draft(ClassicPaperDTO request) throws Exception {
Long paperId = request.getId(); Long paperId = request.getId();
// 1. 校验套卷存在且为草稿状态 // 1. 校验套卷存在
ClassicPaperEntity paper = classicPaperBaseService.getById(paperId); ClassicPaperEntity paper = classicPaperBaseService.getById(paperId);
if (paper == null) { if (paper == null) {
throw new BusinessErrorException("套卷不存在"); throw new BusinessErrorException("套卷不存在");
} }
if (!ClassicPaperStatusEnum.DRAFT.getValue().equals(paper.getStatus())) {
throw new BusinessErrorException("只有草稿状态的套卷才能编辑");
}
// 2. 更新套卷名称和描述 // 2. 更新套卷名称和描述
if (StrUtil.isNotBlank(request.getName())) { if (StrUtil.isNotBlank(request.getName())) {
@ -99,6 +96,7 @@ public class DraftPaperDomainServiceImpl implements DraftPaperDomainService {
qDto.setQuestionId(rel.getQuestionId()); qDto.setQuestionId(rel.getQuestionId());
qDto.setSortOrder(rel.getSortOrder()); qDto.setSortOrder(rel.getSortOrder());
qDto.setQuestionType(rel.getQuestionType()); qDto.setQuestionType(rel.getQuestionType());
qDto.setScore(rel.getScore());
questions.stream() questions.stream()
.filter(q -> q.getId().equals(rel.getQuestionId())) .filter(q -> q.getId().equals(rel.getQuestionId()))
.findFirst() .findFirst()

166
src/main/java/com/project/classicpaper/domain/service/impl/RegenerateQuestionDomainServiceImpl.java

@ -6,11 +6,9 @@ import com.project.base.domain.result.Result;
import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO;
import com.project.classicpaper.domain.entity.ClassicPaperEntity; import com.project.classicpaper.domain.entity.ClassicPaperEntity;
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity; import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity;
import com.project.classicpaper.domain.entity.RegenerateQuestionTaskEntity;
import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum; import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum;
import com.project.classicpaper.domain.service.ClassicPaperBaseService; import com.project.classicpaper.domain.service.*;
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.entity.KnowledgePointEntity;
import com.project.information.domain.service.KnowledgePointBaseService; import com.project.information.domain.service.KnowledgePointBaseService;
import com.project.question.domain.entity.QuestionEntity; import com.project.question.domain.entity.QuestionEntity;
@ -20,17 +18,28 @@ import com.project.question.domain.service.QuestionKpRelBaseService;
import com.project.task.domain.enums.QuestionTypeEnum; import com.project.task.domain.enums.QuestionTypeEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@Slf4j @Slf4j
public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDomainService { public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDomainService {
@Autowired
@Qualifier("regenerateExecutor")
private Executor regenerateExecutor;
@Autowired @Autowired
private ClassicPaperBaseService classicPaperBaseService; private ClassicPaperBaseService classicPaperBaseService;
@ -49,17 +58,23 @@ public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDo
@Autowired @Autowired
private ClassicPaperQuestionGenerator classicPaperQuestionGenerator; private ClassicPaperQuestionGenerator classicPaperQuestionGenerator;
@Autowired
private RegenerateQuestionTaskBaseService regenerateQuestionTaskBaseService;
@Value("${classicpaper.mock.regenerate-delay-ms:4000}")
private long mockDelayMin;
@Value("${classicpaper.mock.regenerate-delay-max-ms:5000}")
private long mockDelayMax;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Result<ClassicPaperQuestionDTO> regenerate(Long paperId, Long paperQuestionId) throws Exception { public Result<ClassicPaperQuestionDTO> regenerate(Long paperId, Long paperQuestionId) throws Exception {
// 1. 校验套卷存在且为草稿状态 // 1. 校验套卷存在
ClassicPaperEntity paper = classicPaperBaseService.getById(paperId); ClassicPaperEntity paper = classicPaperBaseService.getById(paperId);
if (paper == null) { if (paper == null) {
throw new BusinessErrorException("套卷不存在"); throw new BusinessErrorException("套卷不存在");
} }
if (!ClassicPaperStatusEnum.DRAFT.getValue().equals(paper.getStatus())) {
throw new BusinessErrorException("只有草稿状态的套卷才能重抽题目");
}
// 2. 直接用 paperQuestionId 查关联记录 // 2. 直接用 paperQuestionId 查关联记录
ClassicPaperQuestionEntity paperQuestion = classicPaperQuestionBaseService.getById(paperQuestionId); ClassicPaperQuestionEntity paperQuestion = classicPaperQuestionBaseService.getById(paperQuestionId);
@ -84,7 +99,7 @@ public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDo
.eq(QuestionKpRelEntity::getQuestionId, questionId) .eq(QuestionKpRelEntity::getQuestionId, questionId)
); );
if (kpRels.isEmpty()) { if (kpRels.isEmpty()) {
throw new BusinessErrorException("原题目没有关联知识点,无法重"); throw new BusinessErrorException("原题目没有关联知识点,无法重新生题");
} }
List<Long> kpIds = kpRels.stream() List<Long> kpIds = kpRels.stream()
.map(QuestionKpRelEntity::getKpId).collect(Collectors.toList()); .map(QuestionKpRelEntity::getKpId).collect(Collectors.toList());
@ -95,7 +110,10 @@ public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDo
throw new BusinessErrorException("关联知识点数据不存在"); throw new BusinessErrorException("关联知识点数据不存在");
} }
// 6. 调用生成器生成新题 // 6. Mock 延迟:模拟算法服务返回耗时
mockSleep();
// 7. 调用生成器生成新题
QuestionTypeEnum questionType = QuestionTypeEnum.findByValue(originalQuestion.getQuestionType()); QuestionTypeEnum questionType = QuestionTypeEnum.findByValue(originalQuestion.getQuestionType());
List<QuestionEntity> newQuestions = classicPaperQuestionGenerator.generate( List<QuestionEntity> newQuestions = classicPaperQuestionGenerator.generate(
knowledgePoints, questionType, 1); knowledgePoints, questionType, 1);
@ -104,13 +122,13 @@ public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDo
} }
QuestionEntity newQuestion = newQuestions.get(0); QuestionEntity newQuestion = newQuestions.get(0);
// 7. 更新关联关系,指向新题 // 8. 更新关联关系,指向新题
paperQuestion.setQuestionId(newQuestion.getId()); paperQuestion.setQuestionId(newQuestion.getId());
classicPaperQuestionBaseService.updateById(paperQuestion); classicPaperQuestionBaseService.updateById(paperQuestion);
log.info(">>> [经典套题-重] 套卷{} 关联记录{} 原题{} -> 新题{}", paperId, paperQuestionId, questionId, newQuestion.getId()); log.info(">>> [经典套题-重新生题] 套卷{} 关联记录{} 原题{} -> 新题{}", paperId, paperQuestionId, questionId, newQuestion.getId());
// 8. 构建返回 // 9. 构建返回
ClassicPaperQuestionDTO dto = new ClassicPaperQuestionDTO(); ClassicPaperQuestionDTO dto = new ClassicPaperQuestionDTO();
dto.setId(paperQuestion.getId()); dto.setId(paperQuestion.getId());
dto.setPaperId(paperId); dto.setPaperId(paperId);
@ -122,4 +140,126 @@ public class RegenerateQuestionDomainServiceImpl implements RegenerateQuestionDo
} }
return Result.success(dto); return Result.success(dto);
} }
@Override
public void regenerateAsync(Long paperQuestionId) throws Exception {
// 1. 前置校验(快速失败,不占用异步线程)
ClassicPaperQuestionEntity paperQuestion = classicPaperQuestionBaseService.getById(paperQuestionId);
if (paperQuestion == null) {
throw new BusinessErrorException("该题目不存在");
}
Long paperId = paperQuestion.getPaperId();
ClassicPaperEntity paper = classicPaperBaseService.getById(paperId);
if (paper == null) {
throw new BusinessErrorException("套卷不存在");
}
// 2. 检查是否已有进行中的任务
RegenerateQuestionTaskEntity existingTask = regenerateQuestionTaskBaseService.getOne(
new LambdaQueryWrapper<RegenerateQuestionTaskEntity>()
.eq(RegenerateQuestionTaskEntity::getPaperQuestionId, paperQuestionId)
.eq(RegenerateQuestionTaskEntity::getStatus, 0));
if (existingTask != null) {
throw new BusinessErrorException("该题目正在重新生题中,请稍后再试");
}
// 3. 创建任务记录
String taskId = UUID.randomUUID().toString().replace("-", "");
RegenerateQuestionTaskEntity taskEntity = new RegenerateQuestionTaskEntity();
taskEntity.setTaskId(taskId);
taskEntity.setPaperId(paperId);
taskEntity.setPaperQuestionId(paperQuestionId);
taskEntity.setStatus(0); // 进行中
regenerateQuestionTaskBaseService.save(taskEntity);
// 4. 异步执行重新生题逻辑
CompletableFuture.runAsync(() -> doRegenerateAsync(taskId, paperQuestionId), regenerateExecutor);
log.info(">>> [经典套题-重新生题-异步] 任务已提交, taskId={}, paperId={}, paperQuestionId={}", taskId, paperId, paperQuestionId);
}
private void doRegenerateAsync(String taskId, Long paperQuestionId) {
try {
// 1. 查询关联记录和原题
ClassicPaperQuestionEntity paperQuestion = classicPaperQuestionBaseService.getById(paperQuestionId);
Long questionId = paperQuestion.getQuestionId();
QuestionEntity originalQuestion = questionBaseService.getById(questionId);
// 2. 查询知识点
List<QuestionKpRelEntity> kpRels = questionKpRelBaseService.list(
new LambdaQueryWrapper<QuestionKpRelEntity>()
.eq(QuestionKpRelEntity::getQuestionId, questionId));
List<Long> kpIds = kpRels.stream()
.map(QuestionKpRelEntity::getKpId).collect(Collectors.toList());
List<KnowledgePointEntity> knowledgePoints = knowledgePointBaseService.listByIds(kpIds);
// 3. Mock 延迟:模拟算法服务返回耗时
mockSleep();
// 4. 调用生成器生成新题
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);
// 5. 更新关联关系
paperQuestion.setQuestionId(newQuestion.getId());
classicPaperQuestionBaseService.updateById(paperQuestion);
// 6. 更新任务状态为成功
RegenerateQuestionTaskEntity taskEntity = regenerateQuestionTaskBaseService.getOne(
new LambdaQueryWrapper<RegenerateQuestionTaskEntity>()
.eq(RegenerateQuestionTaskEntity::getTaskId, taskId));
taskEntity.setStatus(1);
taskEntity.setNewQuestionId(newQuestion.getId());
regenerateQuestionTaskBaseService.updateById(taskEntity);
log.info(">>> [经典套题-重新生题-异步] 任务成功, taskId={}, 原题{} -> 新题{}", taskId, questionId, newQuestion.getId());
} catch (Exception e) {
log.error(">>> [经典套题-重新生题-异步] 任务失败, taskId={}", taskId, e);
// 更新任务状态为失败
RegenerateQuestionTaskEntity taskEntity = regenerateQuestionTaskBaseService.getOne(
new LambdaQueryWrapper<RegenerateQuestionTaskEntity>()
.eq(RegenerateQuestionTaskEntity::getTaskId, taskId));
if (taskEntity != null) {
taskEntity.setStatus(2);
taskEntity.setErrorMessage(e.getMessage());
regenerateQuestionTaskBaseService.updateById(taskEntity);
}
}
}
@Override
public Map<String, Object> getTaskStatus(Long paperQuestionId) {
RegenerateQuestionTaskEntity taskEntity = regenerateQuestionTaskBaseService.getOne(
new LambdaQueryWrapper<RegenerateQuestionTaskEntity>()
.eq(RegenerateQuestionTaskEntity::getPaperQuestionId, paperQuestionId)
.orderByDesc(RegenerateQuestionTaskEntity::getCreateTime)
.last("LIMIT 1"));
if (taskEntity == null) {
return null;
}
Map<String, Object> result = new java.util.HashMap<>();
result.put("status", taskEntity.getStatus());
result.put("newQuestionId", taskEntity.getNewQuestionId());
result.put("errorMessage", taskEntity.getErrorMessage());
return result;
}
/**
* Mock 延迟模拟算法服务返回耗时4~5秒随机
*/
private void mockSleep() {
long delay = ThreadLocalRandom.current().nextLong(mockDelayMin, mockDelayMax + 1);
log.info(">>> [经典套题-重新生题] Mock 延迟 {}ms", delay);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn(">>> [经典套题-重新生题] Mock 延迟被中断");
}
}
} }

11
src/main/java/com/project/classicpaper/domain/service/impl/RegenerateQuestionTaskBaseServiceImpl.java

@ -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.RegenerateQuestionTaskEntity;
import com.project.classicpaper.domain.service.RegenerateQuestionTaskBaseService;
import com.project.classicpaper.mapper.RegenerateQuestionTaskMapper;
import org.springframework.stereotype.Service;
@Service
public class RegenerateQuestionTaskBaseServiceImpl extends ServiceImpl<RegenerateQuestionTaskMapper, RegenerateQuestionTaskEntity> implements RegenerateQuestionTaskBaseService {
}

156
src/main/java/com/project/classicpaper/domain/service/impl/SavePaperSetDomainServiceImpl.java

@ -1,27 +1,26 @@
package com.project.classicpaper.domain.service.impl; 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.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.project.base.domain.exception.BusinessErrorException; import com.project.base.domain.exception.BusinessErrorException;
import com.project.base.domain.result.Result; 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.ClassicPaperSetDTO; import com.project.classicpaper.domain.dto.ClassicPaperSetDTO;
import com.project.classicpaper.domain.entity.ClassicPaperEntity; import com.project.classicpaper.domain.entity.ClassicPaperEntity;
import com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity;
import com.project.classicpaper.domain.entity.ClassicPaperSetEntity; import com.project.classicpaper.domain.entity.ClassicPaperSetEntity;
import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum; import com.project.classicpaper.domain.enums.ClassicPaperStatusEnum;
import com.project.classicpaper.domain.service.ClassicPaperBaseService; import com.project.classicpaper.domain.service.ClassicPaperBaseService;
import com.project.classicpaper.domain.service.ClassicPaperQuestionBaseService; import com.project.classicpaper.domain.service.ClassicPaperQuestionBaseService;
import com.project.classicpaper.domain.service.ClassicPaperSetBaseService; import com.project.classicpaper.domain.service.ClassicPaperSetBaseService;
import com.project.classicpaper.domain.service.SavePaperSetDomainService; import com.project.classicpaper.domain.service.SavePaperSetDomainService;
import com.project.task.config.ExamScoreRatioConfig;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map; import java.util.Map;
@Service @Service
@ -37,94 +36,115 @@ public class SavePaperSetDomainServiceImpl implements SavePaperSetDomainService
@Autowired @Autowired
private ClassicPaperQuestionBaseService classicPaperQuestionBaseService; private ClassicPaperQuestionBaseService classicPaperQuestionBaseService;
@Autowired
private ExamScoreRatioConfig examScoreRatioConfig;
private final ObjectMapper objectMapper = new ObjectMapper();
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Result<Long> save(ClassicPaperSetDTO request) throws Exception { public Result<Long> save(ClassicPaperSetDTO request) throws Exception {
Long setId = request.getId(); Long setId = request.getId();
// 1. 校验套题组存在且为草稿状态
// 1. 校验套题组存在
ClassicPaperSetEntity paperSet = classicPaperSetBaseService.getById(setId); ClassicPaperSetEntity paperSet = classicPaperSetBaseService.getById(setId);
if (paperSet == null) { if (paperSet == null) {
throw new BusinessErrorException("套题组不存在"); throw new BusinessErrorException("套题组不存在");
} }
if (!ClassicPaperStatusEnum.DRAFT.getValue().equals(paperSet.getStatus())) {
throw new BusinessErrorException("只有草稿状态的套题组才能保存固化");
}
// 2. 更新套题组基本信息 // 2. 更新分值比例
if (StrUtil.isNotBlank(request.getName())) {
paperSet.setName(request.getName());
}
if (StrUtil.isNotBlank(request.getDescription())) {
paperSet.setDescription(request.getDescription());
}
if (request.getClassicCategoryId() != null) {
paperSet.setClassicCategoryId(request.getClassicCategoryId());
}
// 从四个独立字段重建 scoreRatio JSON
String scoreRatio = buildScoreRatioFromFields(request); String scoreRatio = buildScoreRatioFromFields(request);
if (scoreRatio != null) { if (scoreRatio != null) {
paperSet.setScoreRatio(scoreRatio); paperSet.setScoreRatio(scoreRatio);
} }
classicPaperSetBaseService.updateById(paperSet); classicPaperSetBaseService.updateById(paperSet);
// 3. 全量更新各版本及其题目关联 // 3. 根据分值比例计算每道题的分值,写入关联表
if (CollUtil.isNotEmpty(request.getPaperList())) { calculateAndSaveQuestionScores(paperSet);
for (ClassicPaperDTO paperDto : request.getPaperList()) {
if (paperDto.getId() != null) { log.info(">>> [经典套题] 分值比例已更新, setId={}", setId);
// 更新已有套卷
ClassicPaperEntity paper = classicPaperBaseService.getById(paperDto.getId()); return Result.success(setId);
if (paper != null) { }
if (StrUtil.isNotBlank(paperDto.getName())) {
paper.setName(paperDto.getName()); /**
} * 根据分值比例和题型数量计算每道题的分值并写入 ClassicPaperQuestionEntity.score
if (StrUtil.isNotBlank(paperDto.getDescription())) { * 计算公式每题分值 = (题型权重 / 总权重) × 总分 / 该题型数量
paper.setDescription(paperDto.getDescription()); */
} private void calculateAndSaveQuestionScores(ClassicPaperSetEntity paperSet) throws Exception {
classicPaperBaseService.updateById(paper); // 解析分值比例
} Map<String, Integer> ratioMap = objectMapper.readValue(
paperSet.getScoreRatio(), new TypeReference<Map<String, Integer>>() {});
// 更新题目关联(排序、替换的题目ID)
if (CollUtil.isNotEmpty(paperDto.getQuestionList())) { // 各题型数量
for (ClassicPaperQuestionDTO qDto : paperDto.getQuestionList()) { int singleCount = paperSet.getSingleChoiceNum() != null ? paperSet.getSingleChoiceNum() : 0;
if (qDto.getId() != null) { int multipleCount = paperSet.getMultipleChoiceNum() != null ? paperSet.getMultipleChoiceNum() : 0;
com.project.classicpaper.domain.entity.ClassicPaperQuestionEntity relation = int trueFalseCount = paperSet.getTrueFalseNum() != null ? paperSet.getTrueFalseNum() : 0;
classicPaperQuestionBaseService.getById(qDto.getId()); int shortAnswerCount = paperSet.getShortAnswerNum() != null ? paperSet.getShortAnswerNum() : 0;
if (relation != null) {
if (qDto.getSortOrder() != null) { // 各题型权重(从 scoreRatio 读取,不存在则用配置默认值)
relation.setSortOrder(qDto.getSortOrder()); int singleWeight = ratioMap.getOrDefault("single", examScoreRatioConfig.getSingle());
} int multipleWeight = ratioMap.getOrDefault("multiple", examScoreRatioConfig.getMultiple());
if (qDto.getQuestionId() != null) { int trueFalseWeight = ratioMap.getOrDefault("trueFalse", examScoreRatioConfig.getTrueFalse());
relation.setQuestionId(qDto.getQuestionId()); int shortAnswerWeight = ratioMap.getOrDefault("shortAnswer", examScoreRatioConfig.getShortAnswer());
}
classicPaperQuestionBaseService.updateById(relation); // 总权重 = 各题型数量 × 权重
} int totalWeight = singleCount * singleWeight
} + multipleCount * multipleWeight
} + trueFalseCount * trueFalseWeight
} + shortAnswerCount * shortAnswerWeight;
}
} if (totalWeight == 0) {
return;
} }
// 4. 全部校验通过,锁定为 CONFIRMED double totalScore = examScoreRatioConfig.getTotalScore();
classicPaperBaseService.lambdaUpdate()
.eq(ClassicPaperEntity::getSetId, setId)
.set(ClassicPaperEntity::getStatus, ClassicPaperStatusEnum.CONFIRMED.getValue())
.update();
paperSet.setStatus(ClassicPaperStatusEnum.CONFIRMED.getValue()); // 计算每题分值(保留2位小数)
classicPaperSetBaseService.updateById(paperSet); double singleScore = singleCount > 0 ? Math.round(totalScore * singleWeight / totalWeight * 100) / 100.0 : 0;
double multipleScore = multipleCount > 0 ? Math.round(totalScore * multipleWeight / totalWeight * 100) / 100.0 : 0;
double trueFalseScore = trueFalseCount > 0 ? Math.round(totalScore * trueFalseWeight / totalWeight * 100) / 100.0 : 0;
double shortAnswerScore = shortAnswerCount > 0 ? Math.round(totalScore * shortAnswerWeight / totalWeight * 100) / 100.0 : 0;
// 查询该套题组下所有套卷
List<ClassicPaperEntity> papers = classicPaperBaseService.list(
new LambdaQueryWrapper<ClassicPaperEntity>()
.eq(ClassicPaperEntity::getSetId, paperSet.getId()));
if (papers.isEmpty()) {
return;
}
log.info(">>> [经典套题] 套题组已固化, setId={}", setId); // 查询所有关联题目
List<Long> paperIds = papers.stream().map(ClassicPaperEntity::getId).toList();
List<ClassicPaperQuestionEntity> allQuestions = classicPaperQuestionBaseService.list(
new LambdaQueryWrapper<ClassicPaperQuestionEntity>()
.in(ClassicPaperQuestionEntity::getPaperId, paperIds));
return Result.success(setId); // 按题型设置分值
for (ClassicPaperQuestionEntity q : allQuestions) {
if (q.getQuestionType() == null) continue;
switch (q.getQuestionType()) {
case 1 -> q.setScore(singleScore); // 单选
case 2 -> q.setScore(multipleScore); // 多选
case 3 -> q.setScore(trueFalseScore); // 判断
case 4 -> q.setScore(shortAnswerScore); // 简答
}
}
// 批量更新
if (!allQuestions.isEmpty()) {
classicPaperQuestionBaseService.updateBatchById(allQuestions);
}
log.info(">>> [经典套题] 分值计算完成, setId={}, 单选{}分, 多选{}分, 判断{}分, 简答{}分",
paperSet.getId(), singleScore, multipleScore, trueFalseScore, shortAnswerScore);
} }
/** /**
* DTO 的四个独立分值比例字段构建 JSON * DTO 的四个独立分值比例字段构建 JSON
* 只包含非 null 的字段对应数量 > 0 的题型
*/ */
private String buildScoreRatioFromFields(ClassicPaperSetDTO request) { private String buildScoreRatioFromFields(ClassicPaperSetDTO request) {
Map<String, Integer> ratioMap = new LinkedHashMap<>(); java.util.Map<String, Integer> ratioMap = new java.util.LinkedHashMap<>();
if (request.getSingleChoiceScoreRatio() != null) { if (request.getSingleChoiceScoreRatio() != null) {
ratioMap.put("single", request.getSingleChoiceScoreRatio()); ratioMap.put("single", request.getSingleChoiceScoreRatio());
} }
@ -141,7 +161,7 @@ public class SavePaperSetDomainServiceImpl implements SavePaperSetDomainService
return null; return null;
} }
try { try {
return new ObjectMapper().writeValueAsString(ratioMap); return objectMapper.writeValueAsString(ratioMap);
} catch (Exception e) { } catch (Exception e) {
log.warn(">>> [经典套题] scoreRatio 序列化失败", e); log.warn(">>> [经典套题] scoreRatio 序列化失败", e);
return null; return null;

9
src/main/java/com/project/classicpaper/mapper/RegenerateQuestionTaskMapper.java

@ -0,0 +1,9 @@
package com.project.classicpaper.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.project.classicpaper.domain.entity.RegenerateQuestionTaskEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RegenerateQuestionTaskMapper extends BaseMapper<RegenerateQuestionTaskEntity> {
}

16
src/main/java/com/project/operation/config/AsyncConfig.java

@ -37,4 +37,20 @@ public class AsyncConfig {
executor.initialize(); executor.initialize();
return executor; return executor;
} }
/**
* 重抽题异步线程池
*/
@Bean(name = "regenerateExecutor")
public Executor regenerateExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("regenerate-");
executor.setKeepAliveSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
} }

10
src/main/resources/application-dev.yml

@ -92,4 +92,12 @@ question:
# 重试间隔(秒) # 重试间隔(秒)
retry-interval: 60 retry-interval: 60
scheduled-task: scheduled-task:
owner: test owner: test
# 经典套题 Mock 配置
classicpaper:
mock:
# 重抽题最小延迟(毫秒)
regenerate-delay-ms: 4000
# 重抽题最大延迟(毫秒)
regenerate-delay-max-ms: 5000

8
src/main/resources/application-prod.yml

@ -94,4 +94,10 @@ question:
retry-interval: 60 retry-interval: 60
scheduled-task: scheduled-task:
owner: test owner: test
# 经典套题 Mock 配置(生产环境关闭 mock 延迟)
classicpaper:
mock:
regenerate-delay-ms: 0
regenerate-delay-max-ms: 0

8
src/main/resources/application-test.yml

@ -94,4 +94,10 @@ question:
retry-interval: 60 retry-interval: 60
scheduled-task: scheduled-task:
owner: test owner: test
# 经典套题 Mock 配置
classicpaper:
mock:
regenerate-delay-ms: 4000
regenerate-delay-max-ms: 5000
Loading…
Cancel
Save