diff --git a/src/main/java/com/project/classicpaper/application/ClassicPaperSetApplicationService.java b/src/main/java/com/project/classicpaper/application/ClassicPaperSetApplicationService.java index 152d84d..df814fe 100644 --- a/src/main/java/com/project/classicpaper/application/ClassicPaperSetApplicationService.java +++ b/src/main/java/com/project/classicpaper/application/ClassicPaperSetApplicationService.java @@ -6,8 +6,6 @@ import com.project.classicpaper.domain.dto.ClassicPaperSetDTO; import com.project.classicpaper.domain.param.ClassicPaperSetParam; import org.springframework.web.multipart.MultipartFile; -import java.util.List; - public interface ClassicPaperSetApplicationService { /** @@ -30,7 +28,7 @@ public interface ClassicPaperSetApplicationService { /** * 废弃套题组(连带所有版本) */ - Result delete(List ids) throws Exception; + Result delete(String ids) throws Exception; /** * 更新分值比例,同时计算每道题的分值 diff --git a/src/main/java/com/project/classicpaper/application/impl/ClassicPaperSetApplicationServiceImpl.java b/src/main/java/com/project/classicpaper/application/impl/ClassicPaperSetApplicationServiceImpl.java index 4b2f61a..68fee9b 100644 --- a/src/main/java/com/project/classicpaper/application/impl/ClassicPaperSetApplicationServiceImpl.java +++ b/src/main/java/com/project/classicpaper/application/impl/ClassicPaperSetApplicationServiceImpl.java @@ -22,6 +22,7 @@ import com.project.classicpaper.domain.entity.ClassicCategoryEntity; import com.project.classicpaper.domain.service.*; import com.project.question.domain.entity.QuestionEntity; import com.project.question.domain.service.QuestionBaseService; +import org.apache.commons.lang3.StringUtils; import org.springframework.web.multipart.MultipartFile; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -229,8 +230,13 @@ public class ClassicPaperSetApplicationServiceImpl implements ClassicPaperSetApp @Override @Transactional(rollbackFor = Exception.class) - public Result delete(List ids) throws Exception { - classicPaperSetBaseService.removeBatchByIds(ids); + public Result delete(String ids) throws Exception { + + if (StringUtils.isBlank(ids)) { + throw new BusinessErrorException("ids is empty"); + } + List idList = Arrays.asList(ids.split(",")).stream().map(id -> Long.valueOf(id)).collect(Collectors.toList()); + classicPaperSetBaseService.removeBatchByIds(idList); return Result.success("删除成功"); } diff --git a/src/main/java/com/project/classicpaper/controller/ClassicPaperSetController.java b/src/main/java/com/project/classicpaper/controller/ClassicPaperSetController.java index 0ddd55b..2c2251b 100644 --- a/src/main/java/com/project/classicpaper/controller/ClassicPaperSetController.java +++ b/src/main/java/com/project/classicpaper/controller/ClassicPaperSetController.java @@ -52,7 +52,7 @@ public class ClassicPaperSetController { } @PostMapping("/delete") - public Result delete(List ids) throws Exception { + public Result delete(String ids) throws Exception { return classicPaperSetApplicationService.delete(ids); } diff --git a/src/main/java/com/project/classicpaper/domain/service/impl/ImportClassicPaperDomainServiceImpl.java b/src/main/java/com/project/classicpaper/domain/service/impl/ImportClassicPaperDomainServiceImpl.java index 9c5cb7e..51af7cd 100644 --- a/src/main/java/com/project/classicpaper/domain/service/impl/ImportClassicPaperDomainServiceImpl.java +++ b/src/main/java/com/project/classicpaper/domain/service/impl/ImportClassicPaperDomainServiceImpl.java @@ -1,5 +1,6 @@ package com.project.classicpaper.domain.service.impl; +import com.fasterxml.jackson.databind.ObjectMapper; import com.project.classicpaper.domain.dto.ClassicPaperDTO; import com.project.classicpaper.domain.dto.ClassicPaperQuestionDTO; import com.project.classicpaper.domain.dto.ClassicPaperSetDTO; @@ -19,6 +20,7 @@ import com.project.question.domain.entity.QuestionEntity; import com.project.question.domain.enums.QuestionSourceTypeEnum; import com.project.question.domain.service.QuestionBaseService; import com.project.question.domain.service.SaveQuestionDomainService; +import com.project.task.config.ExamScoreRatioConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -27,6 +29,7 @@ import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -62,6 +65,11 @@ public class ImportClassicPaperDomainServiceImpl implements ImportClassicPaperDo @Autowired private MinIoUtils minIoUtils; + @Autowired + private ExamScoreRatioConfig examScoreRatioConfig; + + private final ObjectMapper objectMapper = new ObjectMapper(); + @Override @Transactional(rollbackFor = Exception.class) public ClassicPaperSetDTO importFromWord(MultipartFile file, Long classicCategoryId, String paperName) throws Exception { @@ -115,6 +123,21 @@ public class ImportClassicPaperDomainServiceImpl implements ImportClassicPaperDo paperSet.setTrueFalseNum(trueFalseNum); paperSet.setShortAnswerNum(shortAnswerNum); paperSet.setStatus(ClassicPaperStatusEnum.DRAFT.getValue()); + + // 设置默认分值比例(只包含数量 > 0 的题型) + Map ratioMap = new LinkedHashMap<>(); + if (singleChoiceNum > 0) ratioMap.put("single", examScoreRatioConfig.getSingle()); + if (multipleChoiceNum > 0) ratioMap.put("multiple", examScoreRatioConfig.getMultiple()); + if (trueFalseNum > 0) ratioMap.put("trueFalse", examScoreRatioConfig.getTrueFalse()); + if (shortAnswerNum > 0) ratioMap.put("shortAnswer", examScoreRatioConfig.getShortAnswer()); + if (!ratioMap.isEmpty()) { + try { + paperSet.setScoreRatio(objectMapper.writeValueAsString(ratioMap)); + } catch (Exception e) { + log.warn(">>> [历史导入] scoreRatio 序列化失败", e); + } + } + classicPaperSetBaseService.save(paperSet); // 3. 创建套卷(版本1) diff --git a/src/main/java/com/project/task/domain/service/strategy/ClassicTaskConfigStrategy.java b/src/main/java/com/project/task/domain/service/strategy/ClassicTaskConfigStrategy.java index 094b227..b90d46d 100644 --- a/src/main/java/com/project/task/domain/service/strategy/ClassicTaskConfigStrategy.java +++ b/src/main/java/com/project/task/domain/service/strategy/ClassicTaskConfigStrategy.java @@ -6,25 +6,37 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; import com.project.base.domain.exception.BusinessErrorException; +import com.project.classicpaper.domain.entity.ClassicPaperSetEntity; +import com.project.classicpaper.domain.service.ClassicPaperSetBaseService; import com.project.task.domain.dto.TaskDTO; import com.project.task.domain.entity.TaskEntity; import com.project.task.domain.service.TaskBaseService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; import java.util.Objects; /** * 经典套题模式:校验 + 字段默认值 - * 赋分来自套卷本身,此处不做赋分计算 */ +@Slf4j @Component public class ClassicTaskConfigStrategy implements TaskConfigStrategy { @Autowired private TaskBaseService taskBaseService; + @Autowired + private ClassicPaperSetBaseService classicPaperSetBaseService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + @Override public void validateAndConfigure(TaskDTO dto) throws Exception { validate(dto); @@ -85,7 +97,7 @@ public class ClassicTaskConfigStrategy implements TaskConfigStrategy { } /** - * 经典模式下题目数量和分数来自套卷,DTO 中可能为 null,赋默认值 0 避免 NPE + * 经典模式下题目数量和分数来自套卷,DTO 中可能为 null,赋默认值避免 NPE */ private void fillDefaults(TaskDTO dto) { if (dto.getSingleChoiceNum() == null) { @@ -100,6 +112,10 @@ public class ClassicTaskConfigStrategy implements TaskConfigStrategy { if (dto.getShortAnswerNum() == null) { dto.setShortAnswerNum(0); } + + // 从套题组的分值比例计算每题分值(保留2位小数) + calculateScoresFromRatio(dto); + if (dto.getSingleChoiceScore() == null) { dto.setSingleChoiceScore(0.0); } @@ -122,4 +138,43 @@ public class ClassicTaskConfigStrategy implements TaskConfigStrategy { dto.setTotalScore(100); } } + + /** + * 从 ClassicPaperSet.scoreRatio 解析比例,计算每题分值 + * 公式:每题分值 = 100 × 题型比例 / 总比例 / 该题型数量 + */ + private void calculateScoresFromRatio(TaskDTO dto) { + if (dto.getClassicPaperSetId() == null) { + return; + } + ClassicPaperSetEntity paperSet = classicPaperSetBaseService.getById(dto.getClassicPaperSetId()); + if (paperSet == null || StrUtil.isBlank(paperSet.getScoreRatio())) { + return; + } + try { + Map ratioMap = objectMapper.readValue(paperSet.getScoreRatio(), Map.class); + int totalRatio = ratioMap.values().stream().mapToInt(Integer::intValue).sum(); + if (totalRatio <= 0) { + return; + } + if (paperSet.getSingleChoiceNum() != null && paperSet.getSingleChoiceNum() > 0) { + double perScore = 100.0 * ratioMap.getOrDefault("single", 0) / totalRatio / paperSet.getSingleChoiceNum(); + dto.setSingleChoiceScore(BigDecimal.valueOf(perScore).setScale(2, RoundingMode.HALF_UP).doubleValue()); + } + if (paperSet.getMultipleChoiceNum() != null && paperSet.getMultipleChoiceNum() > 0) { + double perScore = 100.0 * ratioMap.getOrDefault("multiple", 0) / totalRatio / paperSet.getMultipleChoiceNum(); + dto.setMultipleChoiceScore(BigDecimal.valueOf(perScore).setScale(2, RoundingMode.HALF_UP).doubleValue()); + } + if (paperSet.getTrueFalseNum() != null && paperSet.getTrueFalseNum() > 0) { + double perScore = 100.0 * ratioMap.getOrDefault("trueFalse", 0) / totalRatio / paperSet.getTrueFalseNum(); + dto.setTrueFalseScore(BigDecimal.valueOf(perScore).setScale(2, RoundingMode.HALF_UP).doubleValue()); + } + if (paperSet.getShortAnswerNum() != null && paperSet.getShortAnswerNum() > 0) { + double perScore = 100.0 * ratioMap.getOrDefault("shortAnswer", 0) / totalRatio / paperSet.getShortAnswerNum(); + dto.setShortAnswerScore(BigDecimal.valueOf(perScore).setScale(2, RoundingMode.HALF_UP).doubleValue()); + } + } catch (Exception e) { + log.warn(">>> [经典模式] scoreRatio 解析失败, paperSetId={}", dto.getClassicPaperSetId(), e); + } + } }