16 changed files with 622 additions and 134 deletions
@ -0,0 +1,15 @@ |
|||
package com.project.classicpaper.application; |
|||
|
|||
import com.project.base.domain.result.Result; |
|||
import com.project.classicpaper.domain.dto.ClassicCategoryDTO; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface ClassicCategoryApplicationService { |
|||
|
|||
Result<List<ClassicCategoryDTO>> treeList(); |
|||
|
|||
Result<ClassicCategoryDTO> save(ClassicCategoryDTO dto) throws Exception; |
|||
|
|||
Result<String> delete(Long id) throws Exception; |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
package com.project.classicpaper.application.impl; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
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.base.domain.utils.TreeUtils; |
|||
import com.project.classicpaper.application.ClassicCategoryApplicationService; |
|||
import com.project.classicpaper.domain.dto.ClassicCategoryDTO; |
|||
import com.project.classicpaper.domain.entity.ClassicCategoryEntity; |
|||
import com.project.classicpaper.domain.service.ClassicCategoryBaseService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Service |
|||
public class ClassicCategoryApplicationServiceImpl implements ClassicCategoryApplicationService { |
|||
|
|||
@Autowired |
|||
private ClassicCategoryBaseService classicCategoryBaseService; |
|||
|
|||
@Override |
|||
public Result<List<ClassicCategoryDTO>> treeList() { |
|||
List<ClassicCategoryDTO> list = classicCategoryBaseService.list( |
|||
new LambdaQueryWrapper<ClassicCategoryEntity>().orderByAsc(ClassicCategoryEntity::getSort) |
|||
).stream().map(entity -> entity.toDTO(ClassicCategoryDTO::new)) |
|||
.collect(Collectors.toList()); |
|||
return Result.success(TreeUtils.buildLongTree(list, |
|||
ClassicCategoryDTO::getId, |
|||
ClassicCategoryDTO::getParentId, |
|||
ClassicCategoryDTO::setChildrenList)); |
|||
} |
|||
|
|||
@Override |
|||
public Result<ClassicCategoryDTO> save(ClassicCategoryDTO dto) throws Exception { |
|||
if (StrUtil.isBlank(dto.getName())) { |
|||
throw new BusinessErrorException("分类名称不能为空"); |
|||
} |
|||
if (StrUtil.length(dto.getName()) > 50) { |
|||
throw new BusinessErrorException("分类名称过长"); |
|||
} |
|||
// 新增时校验层级
|
|||
if (dto.getId() == null) { |
|||
if (dto.getLevel() == null || (dto.getLevel() != 1 && dto.getLevel() != 2)) { |
|||
throw new BusinessErrorException("层级参数错误"); |
|||
} |
|||
if (dto.getLevel() == 2) { |
|||
if (dto.getParentId() == null || dto.getParentId() == 0) { |
|||
throw new BusinessErrorException("二级分类必须选择父级"); |
|||
} |
|||
ClassicCategoryEntity parent = classicCategoryBaseService.getById(dto.getParentId()); |
|||
if (parent == null || !Objects.equals(parent.getLevel(), 1)) { |
|||
throw new BusinessErrorException("父级分类不存在或不是一级分类"); |
|||
} |
|||
} else { |
|||
dto.setParentId(0L); |
|||
} |
|||
} |
|||
// 名称去重(同父级下不可重名)
|
|||
LambdaQueryWrapper<ClassicCategoryEntity> duplicateQuery = new LambdaQueryWrapper<ClassicCategoryEntity>() |
|||
.eq(ClassicCategoryEntity::getName, dto.getName()) |
|||
.eq(ClassicCategoryEntity::getParentId, dto.getParentId()); |
|||
if (dto.getId() != null) { |
|||
duplicateQuery.ne(ClassicCategoryEntity::getId, dto.getId()); |
|||
} |
|||
if (classicCategoryBaseService.count(duplicateQuery) > 0) { |
|||
throw new BusinessErrorException("同级下已存在同名分类"); |
|||
} |
|||
|
|||
ClassicCategoryEntity entity = dto.toEntity(ClassicCategoryEntity::new); |
|||
classicCategoryBaseService.saveOrUpdate(entity); |
|||
return Result.success(entity.toDTO(ClassicCategoryDTO::new)); |
|||
} |
|||
|
|||
@Override |
|||
public Result<String> delete(Long id) throws Exception { |
|||
ClassicCategoryEntity category = classicCategoryBaseService.getById(id); |
|||
if (category == null) { |
|||
throw new BusinessErrorException("分类不存在"); |
|||
} |
|||
// 如果是一级分类,校验是否有子分类
|
|||
if (Objects.equals(category.getLevel(), 1)) { |
|||
long childCount = classicCategoryBaseService.count( |
|||
new LambdaQueryWrapper<ClassicCategoryEntity>().eq(ClassicCategoryEntity::getParentId, id)); |
|||
if (childCount > 0) { |
|||
throw new BusinessErrorException("该分类下存在子分类,无法删除"); |
|||
} |
|||
} |
|||
classicCategoryBaseService.removeById(id); |
|||
return Result.success("删除成功"); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
package com.project.classicpaper.controller; |
|||
|
|||
import com.project.base.domain.result.Result; |
|||
import com.project.classicpaper.application.ClassicCategoryApplicationService; |
|||
import com.project.classicpaper.domain.dto.ClassicCategoryDTO; |
|||
import com.project.operation.annotation.OperationLog; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.util.List; |
|||
|
|||
@RestController |
|||
@Slf4j |
|||
@RequestMapping("/api/admin/classicCategory") |
|||
public class ClassicCategoryController { |
|||
|
|||
@Autowired |
|||
private ClassicCategoryApplicationService classicCategoryApplicationService; |
|||
|
|||
@GetMapping("/treeList") |
|||
public Result<List<ClassicCategoryDTO>> treeList() { |
|||
return classicCategoryApplicationService.treeList(); |
|||
} |
|||
|
|||
@PostMapping("/save") |
|||
@OperationLog(module = "经典分类") |
|||
public Result<ClassicCategoryDTO> save(ClassicCategoryDTO dto) throws Exception { |
|||
return classicCategoryApplicationService.save(dto); |
|||
} |
|||
|
|||
@PostMapping("/delete") |
|||
@OperationLog(module = "经典分类") |
|||
public Result<String> delete(Long id) throws Exception { |
|||
return classicCategoryApplicationService.delete(id); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
package com.project.classicpaper.domain.dto; |
|||
|
|||
import com.project.base.domain.dto.BaseDTO; |
|||
import lombok.Data; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class ClassicCategoryDTO extends BaseDTO { |
|||
private Long id; |
|||
|
|||
private String name; |
|||
|
|||
private Long parentId; |
|||
|
|||
private Integer level; |
|||
|
|||
private Integer sort = 0; |
|||
|
|||
private List<ClassicCategoryDTO> childrenList = new ArrayList<>(); |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
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_category", |
|||
indexes = {@Index(name = "Idx_parent_id", columnList = "parent_id")}) |
|||
@Entity |
|||
@TableName(value = "evaluator_classic_category") |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class ClassicCategoryEntity 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 = "parent_id") |
|||
@TableField("parent_id") |
|||
@Comment("父级ID,一级分类为0") |
|||
private Long parentId; |
|||
|
|||
@Column(name = "level") |
|||
@TableField("level") |
|||
@Comment("层级:1-一级分类,2-二级分类") |
|||
private Integer level; |
|||
|
|||
@Column(name = "sort") |
|||
@TableField("sort") |
|||
@Comment("排序权重") |
|||
private Integer sort = 0; |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
package com.project.classicpaper.domain.service; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.project.classicpaper.domain.entity.ClassicCategoryEntity; |
|||
|
|||
public interface ClassicCategoryBaseService extends IService<ClassicCategoryEntity> { |
|||
} |
|||
@ -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.ClassicCategoryEntity; |
|||
import com.project.classicpaper.domain.service.ClassicCategoryBaseService; |
|||
import com.project.classicpaper.mapper.ClassicCategoryMapper; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
public class ClassicCategoryBaseServiceImpl extends ServiceImpl<ClassicCategoryMapper, ClassicCategoryEntity> implements ClassicCategoryBaseService { |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
package com.project.classicpaper.mapper; |
|||
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.project.classicpaper.domain.entity.ClassicCategoryEntity; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
@Mapper |
|||
public interface ClassicCategoryMapper extends BaseMapper<ClassicCategoryEntity> { |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
package com.project.task.domain.service.strategy; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.date.DateUnit; |
|||
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.project.base.domain.exception.BusinessErrorException; |
|||
import com.project.task.domain.dto.TaskDTO; |
|||
import com.project.task.domain.entity.TaskEntity; |
|||
import com.project.task.domain.service.TaskBaseService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* 经典套题模式:校验 + 字段默认值 |
|||
* 赋分来自套卷本身,此处不做赋分计算 |
|||
*/ |
|||
@Component |
|||
public class ClassicTaskConfigStrategy implements TaskConfigStrategy { |
|||
|
|||
@Autowired |
|||
private TaskBaseService taskBaseService; |
|||
|
|||
@Override |
|||
public void validateAndConfigure(TaskDTO dto) throws Exception { |
|||
validate(dto); |
|||
fillDefaults(dto); |
|||
} |
|||
|
|||
/** |
|||
* 经典模式校验:通用参数 + classicPaperId 必填 |
|||
*/ |
|||
private void validate(TaskDTO dto) throws Exception { |
|||
if (StrUtil.isBlank(dto.getName())) { |
|||
throw new BusinessErrorException("考试任务名称不能为空"); |
|||
} |
|||
if (dto.getId() == null) { |
|||
long count = taskBaseService.count(new LambdaQueryWrapper<TaskEntity>().eq(TaskEntity::getName, dto.getName())); |
|||
if (count > 0) { |
|||
throw new BusinessErrorException("考试任务名称已存在"); |
|||
} |
|||
} |
|||
if (StrUtil.length(dto.getName()) > 10) { |
|||
throw new BusinessErrorException("考试任务名称过长"); |
|||
} |
|||
if (Objects.isNull(dto.getStartTime())) { |
|||
throw new BusinessErrorException("开始时间不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getEndTime())) { |
|||
throw new BusinessErrorException("截止时间不能为空"); |
|||
} |
|||
if (DateUtil.between(dto.getStartTime(), dto.getEndTime(), DateUnit.DAY) < 0) { |
|||
throw new BusinessErrorException("截止时间不能早于开始时间"); |
|||
} |
|||
// 经典模式不关联产品线,subLineId 非必填
|
|||
if (Objects.isNull(dto.getDuration())) { |
|||
throw new BusinessErrorException("考试时长不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getRemindTimePoint())) { |
|||
throw new BusinessErrorException("剩余时间提示点不能为空"); |
|||
} |
|||
if (dto.getRemindTimePoint() > dto.getDuration()) { |
|||
throw new BusinessErrorException("剩余时间提示点不能大于考试时长"); |
|||
} |
|||
if (Objects.isNull(dto.getPassScore())) { |
|||
throw new BusinessErrorException("通过考试分数线不能为空"); |
|||
} |
|||
if (!Validator.isBetween(dto.getPassScore(), 1, 100)) { |
|||
throw new BusinessErrorException("通过考试分数线设置错误"); |
|||
} |
|||
if (StrUtil.isBlank(dto.getNote())) { |
|||
throw new BusinessErrorException("注意事项不能为空"); |
|||
} |
|||
if (CollUtil.isEmpty(dto.getParticipantUserIdList())) { |
|||
throw new BusinessErrorException("参与用户不能为空"); |
|||
} |
|||
// 经典模式特有校验
|
|||
if (Objects.isNull(dto.getClassicPaperId())) { |
|||
throw new BusinessErrorException("经典套卷不能为空"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 经典模式下题目数量和分数来自套卷,DTO 中可能为 null,赋默认值 0 避免 NPE |
|||
*/ |
|||
private void fillDefaults(TaskDTO dto) { |
|||
if (dto.getSingleChoiceNum() == null) dto.setSingleChoiceNum(0); |
|||
if (dto.getMultipleChoiceNum() == null) dto.setMultipleChoiceNum(0); |
|||
if (dto.getTrueFalseNum() == null) dto.setTrueFalseNum(0); |
|||
if (dto.getShortAnswerNum() == null) dto.setShortAnswerNum(0); |
|||
if (dto.getSingleChoiceScore() == null) dto.setSingleChoiceScore(0.0); |
|||
if (dto.getMultipleChoiceScore() == null) dto.setMultipleChoiceScore(0.0); |
|||
if (dto.getTrueFalseScore() == null) dto.setTrueFalseScore(0.0); |
|||
if (dto.getShortAnswerScore() == null) dto.setShortAnswerScore(0.0); |
|||
if (dto.getAccurateGraspNum() == null) dto.setAccurateGraspNum(0); |
|||
if (dto.getVagueGraspNum() == null) dto.setVagueGraspNum(0); |
|||
if (dto.getTotalScore() == null) dto.setTotalScore(0); |
|||
} |
|||
} |
|||
@ -0,0 +1,168 @@ |
|||
package com.project.task.domain.service.strategy; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.date.DateUnit; |
|||
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.project.base.domain.exception.BusinessErrorException; |
|||
import com.project.base.domain.result.Result; |
|||
import com.project.information.domain.dto.KnowledgePointStatisticsDTO; |
|||
import com.project.information.domain.service.GetStatisticsKnowledgePointDomainService; |
|||
import com.project.task.config.ExamScoreRatioConfig; |
|||
import com.project.task.domain.dto.TaskDTO; |
|||
import com.project.task.domain.entity.TaskEntity; |
|||
import com.project.task.domain.service.TaskBaseService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* 生成式套题模式:校验 + 赋分 |
|||
* 校验逻辑从 SaveOrUpdateTaskDomainServiceImpl.checkDTO() 原样搬出,新增简答题赋分 |
|||
*/ |
|||
@Component |
|||
public class GenerativeTaskConfigStrategy implements TaskConfigStrategy { |
|||
|
|||
@Autowired |
|||
private ExamScoreRatioConfig examScoreRatioConfig; |
|||
|
|||
@Autowired |
|||
private TaskBaseService taskBaseService; |
|||
|
|||
@Autowired |
|||
private GetStatisticsKnowledgePointDomainService getStatisticsKnowledgePointDomainService; |
|||
|
|||
@Override |
|||
public void validateAndConfigure(TaskDTO dto) throws Exception { |
|||
validate(dto); |
|||
configureScore(dto); |
|||
} |
|||
|
|||
/** |
|||
* 校验任务参数(原 SaveOrUpdateTaskDomainServiceImpl.checkDTO 逻辑) |
|||
*/ |
|||
private void validate(TaskDTO dto) throws Exception { |
|||
if (StrUtil.isBlank(dto.getName())) { |
|||
throw new BusinessErrorException("考试任务名称不能为空"); |
|||
} |
|||
if (dto.getId() == null) { |
|||
long count = taskBaseService.count(new LambdaQueryWrapper<TaskEntity>().eq(TaskEntity::getName, dto.getName())); |
|||
if (count > 0) { |
|||
throw new BusinessErrorException("考试任务名称已存在"); |
|||
} |
|||
} |
|||
if (StrUtil.length(dto.getName()) > 10) { |
|||
throw new BusinessErrorException("考试任务名称过长"); |
|||
} |
|||
if (Objects.isNull(dto.getStartTime())) { |
|||
throw new BusinessErrorException("开始时间不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getEndTime())) { |
|||
throw new BusinessErrorException("截止时间不能为空"); |
|||
} |
|||
if (DateUtil.between(dto.getStartTime(), dto.getEndTime(), DateUnit.DAY) < 0) { |
|||
throw new BusinessErrorException("截止时间不能早于开始时间"); |
|||
} |
|||
if (Objects.isNull(dto.getSubLineId())) { |
|||
throw new BusinessErrorException("关联产品线不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getDuration())) { |
|||
throw new BusinessErrorException("考试时长不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getRemindTimePoint())) { |
|||
throw new BusinessErrorException("剩余时间提示点不能为空"); |
|||
} |
|||
if (dto.getRemindTimePoint() > dto.getDuration()) { |
|||
throw new BusinessErrorException("剩余时间提示点不能大于考试时长"); |
|||
} |
|||
if (Objects.isNull(dto.getPassScore())) { |
|||
throw new BusinessErrorException("通过考试分数线不能为空"); |
|||
} |
|||
if (!Validator.isBetween(dto.getPassScore(), 1, 100)) { |
|||
throw new BusinessErrorException("通过考试分数线设置错误"); |
|||
} |
|||
if (StrUtil.isBlank(dto.getNote())) { |
|||
throw new BusinessErrorException("注意事项不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getSingleChoiceNum())) { |
|||
throw new BusinessErrorException("单选题数量不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getMultipleChoiceNum())) { |
|||
throw new BusinessErrorException("多选题数量不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getTrueFalseNum())) { |
|||
throw new BusinessErrorException("判断题数量不能为空"); |
|||
} |
|||
if (CollUtil.isEmpty(dto.getParticipantUserIdList())) { |
|||
throw new BusinessErrorException("参与用户不能为空"); |
|||
} |
|||
if (CollUtil.isEmpty(dto.getRelatedDocumentList())) { |
|||
throw new BusinessErrorException("关联文档列表不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getAccurateGraspNum())) { |
|||
throw new BusinessErrorException("精准掌握知识点数量不能为空"); |
|||
} |
|||
if (Objects.isNull(dto.getVagueGraspNum())) { |
|||
throw new BusinessErrorException("模糊掌握知识点数量不能为空"); |
|||
} |
|||
Result<KnowledgePointStatisticsDTO> statistics = getStatisticsKnowledgePointDomainService.getStatistics(dto.getSubLineId()); |
|||
if (!Objects.equals(statistics.getData().getAccurateGraspNum(), dto.getAccurateGraspNum()) || |
|||
!Objects.equals(statistics.getData().getVagueGraspNum(), dto.getVagueGraspNum())) { |
|||
throw new BusinessErrorException("知识点数有变化,请重新选择关联产品线"); |
|||
} |
|||
|
|||
int totalQuestions = dto.getSingleChoiceNum() + dto.getMultipleChoiceNum() + dto.getTrueFalseNum() |
|||
+ (dto.getShortAnswerNum() != null ? dto.getShortAnswerNum() : 0); |
|||
int totalKnowledgePoints = dto.getAccurateGraspNum() + dto.getVagueGraspNum(); |
|||
if (totalQuestions > totalKnowledgePoints) { |
|||
throw new BusinessErrorException("题目总数" + totalQuestions + ",不能超过知识点总数" + totalKnowledgePoints); |
|||
} |
|||
if (totalQuestions > 100) { |
|||
throw new BusinessErrorException("题目总数不能超过 100 道,当前为:" + totalQuestions); |
|||
} |
|||
if (totalQuestions < 1) { |
|||
throw new BusinessErrorException("题目总数不能少于 1 道"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 各题型赋分计算(原逻辑 + 简答题权重) |
|||
*/ |
|||
private void configureScore(TaskDTO dto) { |
|||
int shortAnswerNum = dto.getShortAnswerNum() != null ? dto.getShortAnswerNum() : 0; |
|||
|
|||
// 1. 计算总权重份额
|
|||
int totalUnit = dto.getSingleChoiceNum() * examScoreRatioConfig.getSingle() |
|||
+ dto.getMultipleChoiceNum() * examScoreRatioConfig.getMultiple() |
|||
+ dto.getTrueFalseNum() * examScoreRatioConfig.getTrueFalse() |
|||
+ shortAnswerNum * examScoreRatioConfig.getShortAnswer(); |
|||
double base = examScoreRatioConfig.getTotalScore() * 1.0 / totalUnit; |
|||
|
|||
// 2. 基础赋值:按比例初步分配并保留2位小数
|
|||
dto.setSingleChoiceScore(dto.getSingleChoiceNum() > 0 ? Math.round(base * examScoreRatioConfig.getSingle() * 100) / 100.0 : 0.0); |
|||
dto.setMultipleChoiceScore(dto.getMultipleChoiceNum() > 0 ? Math.round(base * examScoreRatioConfig.getMultiple() * 100) / 100.0 : 0.0); |
|||
dto.setTrueFalseScore(dto.getTrueFalseNum() > 0 ? Math.round(base * examScoreRatioConfig.getTrueFalse() * 100) / 100.0 : 0.0); |
|||
dto.setShortAnswerScore(shortAnswerNum > 0 ? Math.round(base * examScoreRatioConfig.getShortAnswer() * 100) / 100.0 : 0.0); |
|||
|
|||
// 3. 余数平差:从配置中读取总分进行逆向补齐,确保总分 100% 对应配置
|
|||
double configTotal = examScoreRatioConfig.getTotalScore(); |
|||
|
|||
if (dto.getTrueFalseNum() > 0) { |
|||
dto.setTrueFalseScore(Math.round((configTotal - dto.getSingleChoiceNum() * dto.getSingleChoiceScore() |
|||
- dto.getMultipleChoiceNum() * dto.getMultipleChoiceScore() |
|||
- shortAnswerNum * dto.getShortAnswerScore()) / dto.getTrueFalseNum() * 100) / 100.0); |
|||
} else if (shortAnswerNum > 0) { |
|||
dto.setShortAnswerScore(Math.round((configTotal - dto.getSingleChoiceNum() * dto.getSingleChoiceScore() |
|||
- dto.getMultipleChoiceNum() * dto.getMultipleChoiceScore()) / shortAnswerNum * 100) / 100.0); |
|||
} else if (dto.getMultipleChoiceNum() > 0) { |
|||
dto.setMultipleChoiceScore(Math.round((configTotal - dto.getSingleChoiceNum() * dto.getSingleChoiceScore()) / dto.getMultipleChoiceNum() * 100) / 100.0); |
|||
} else if (dto.getSingleChoiceNum() > 0) { |
|||
dto.setSingleChoiceScore(Math.round(configTotal / dto.getSingleChoiceNum() * 100) / 100.0); |
|||
} |
|||
|
|||
dto.setTotalScore((int) configTotal); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
package com.project.task.domain.service.strategy; |
|||
|
|||
import com.project.task.domain.dto.TaskDTO; |
|||
|
|||
/** |
|||
* 任务配置策略接口 |
|||
* 不同考试模式(生成式/经典套题)的校验与赋分逻辑分离 |
|||
*/ |
|||
public interface TaskConfigStrategy { |
|||
|
|||
/** |
|||
* 校验任务参数并完成赋分配置(校验不通过直接抛异常) |
|||
* |
|||
* @param dto 任务DTO |
|||
*/ |
|||
void validateAndConfigure(TaskDTO dto) throws Exception; |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
package com.project.task.domain.service.strategy; |
|||
|
|||
import com.project.task.domain.enums.ExamModeEnum; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* 任务配置策略工厂,按考试模式返回对应策略 |
|||
*/ |
|||
@Component |
|||
public class TaskConfigStrategyFactory { |
|||
|
|||
@Autowired |
|||
private GenerativeTaskConfigStrategy generativeTaskConfigStrategy; |
|||
|
|||
@Autowired |
|||
private ClassicTaskConfigStrategy classicTaskConfigStrategy; |
|||
|
|||
public TaskConfigStrategy getStrategy(Integer examMode) { |
|||
if (ExamModeEnum.CLASSIC.getValue().equals(examMode)) { |
|||
return classicTaskConfigStrategy; |
|||
} |
|||
return generativeTaskConfigStrategy; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue