25 changed files with 861 additions and 1 deletions
@ -0,0 +1,18 @@ |
|||||
|
package com.project.task.application; |
||||
|
|
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.task.domain.dto.TaskDTO; |
||||
|
import com.project.task.domain.param.TaskParam; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface TaskApplicationService { |
||||
|
Result<TaskDTO> saveOrUpdate(TaskDTO dto) throws Exception; |
||||
|
|
||||
|
Result<String> batchDelete(List<Long> idList) throws Exception; |
||||
|
|
||||
|
Result<PageResult<TaskDTO>> search(TaskParam param) throws Exception; |
||||
|
|
||||
|
Result<TaskDTO> getDetail(Long id) throws Exception; |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package com.project.task.application.impl; |
||||
|
|
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.task.application.TaskApplicationService; |
||||
|
import com.project.task.domain.dto.TaskDTO; |
||||
|
import com.project.task.domain.param.TaskParam; |
||||
|
import com.project.task.domain.service.DeleteTaskDomainService; |
||||
|
import com.project.task.domain.service.SaveOrUpdateTaskDomainService; |
||||
|
import com.project.task.domain.service.SearchTaskDomainService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
|
||||
|
@Service |
||||
|
public class TaskApplicationServiceImpl implements TaskApplicationService { |
||||
|
@Autowired |
||||
|
private SaveOrUpdateTaskDomainService saveOrUpdateTaskDomainService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DeleteTaskDomainService deleteTaskDomainService; |
||||
|
|
||||
|
@Autowired |
||||
|
private SearchTaskDomainService searchTaskDomainService; |
||||
|
|
||||
|
@Override |
||||
|
public Result<TaskDTO> saveOrUpdate(TaskDTO dto) throws Exception { |
||||
|
return saveOrUpdateTaskDomainService.saveOrUpdate(dto); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public Result<String> batchDelete(List<Long> idList) throws Exception { |
||||
|
return deleteTaskDomainService.batchDelete(idList); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<PageResult<TaskDTO>> search(TaskParam param) throws Exception { |
||||
|
return searchTaskDomainService.search(param); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<TaskDTO> getDetail(Long id) throws Exception { |
||||
|
return searchTaskDomainService.getDetail(id); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
package com.project.task.config; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Data |
||||
|
@Component |
||||
|
@ConfigurationProperties(prefix = "exam.score-ratio") |
||||
|
public class ExamScoreRatioConfig { |
||||
|
/** |
||||
|
* 单选题权重,默认 2 |
||||
|
*/ |
||||
|
private Integer single = 2; |
||||
|
|
||||
|
/** |
||||
|
* 多选题权重,默认 3 |
||||
|
*/ |
||||
|
private Integer multiple = 3; |
||||
|
|
||||
|
/** |
||||
|
* 判断题权重,默认 1 |
||||
|
*/ |
||||
|
private Integer trueFalse = 1; |
||||
|
|
||||
|
/** |
||||
|
* 总分固定 100 |
||||
|
*/ |
||||
|
private Integer totalScore = 100; |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
package com.project.task.controller; |
||||
|
|
||||
|
|
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.task.application.TaskApplicationService; |
||||
|
import com.project.task.domain.dto.TaskDTO; |
||||
|
import com.project.task.domain.param.TaskParam; |
||||
|
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/task") |
||||
|
public class TaskController { |
||||
|
@Autowired |
||||
|
private TaskApplicationService taskApplicationService; |
||||
|
|
||||
|
@GetMapping("/search") |
||||
|
public Result<PageResult<TaskDTO>> search(TaskParam param) throws Exception { |
||||
|
return taskApplicationService.search(param); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/getDetail") |
||||
|
public Result<TaskDTO> getDetail(Long id) throws Exception { |
||||
|
return taskApplicationService.getDetail(id); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/saveOrUpdate") |
||||
|
public Result<TaskDTO> save(TaskDTO dto) throws Exception { |
||||
|
return taskApplicationService.saveOrUpdate(dto); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@PostMapping("/batchDelete") |
||||
|
public Result<String> batchDelete(List<Long> idList) throws Exception { |
||||
|
return taskApplicationService.batchDelete(idList); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
package com.project.task.domain.dto; |
||||
|
|
||||
|
import com.project.base.domain.dto.BaseDTO; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class TaskDTO extends BaseDTO { |
||||
|
|
||||
|
private Long id; |
||||
|
private String name; |
||||
|
private Date startTime; |
||||
|
private Date endTime; |
||||
|
private Long lineId; |
||||
|
private String lineName; |
||||
|
private Long subLineId; |
||||
|
private String subLineName; |
||||
|
private Integer accurateGraspNum; |
||||
|
private Integer vagueGraspNum; |
||||
|
private Integer duration; |
||||
|
private Integer passScore; |
||||
|
private String note; |
||||
|
private List<Long> relatedDocumentList; |
||||
|
private Integer singleChoiceNum; |
||||
|
private Double singleChoiceScore; |
||||
|
private Integer multipleChoiceNum; |
||||
|
private Double multipleChoiceScore; |
||||
|
private Integer trueFalseNum; |
||||
|
private Double trueFalseScore; |
||||
|
private Double totalScore; |
||||
|
private Integer participantNum; |
||||
|
private Integer passNum; |
||||
|
private Boolean needAiGrading; |
||||
|
|
||||
|
private List<String> participantUserIdList; |
||||
|
|
||||
|
private String statusText; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
package com.project.task.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.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
||||
|
import com.project.base.domain.entity.BaseEntity; |
||||
|
import jakarta.persistence.*; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.hibernate.annotations.Comment; |
||||
|
import org.hibernate.annotations.JdbcTypeCode; |
||||
|
import org.hibernate.type.SqlTypes; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
@Data |
||||
|
@Table(name = "evaluator_exam_record" , indexes = {@Index(name = "Idx_parentId", columnList = "parent_id")}) |
||||
|
@Entity |
||||
|
@TableName(value = "evaluator_exam_record" , autoResultMap = true) |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class ExamRecordEntity extends BaseEntity { |
||||
|
@TableId(type = IdType.ASSIGN_ID) |
||||
|
@Id |
||||
|
private Long id; |
||||
|
|
||||
|
@TableField("task_user_id") |
||||
|
@Column(name = "task_user_id" , columnDefinition="bigint(20) comment '关联TaskUserEntity'") |
||||
|
private Long taskUserId; |
||||
|
|
||||
|
@Comment("分数") |
||||
|
@Column(name = "score") |
||||
|
private Double score; |
||||
|
|
||||
|
@Column(name = "pass") |
||||
|
@Comment("是否通过") |
||||
|
private Boolean pass; |
||||
|
|
||||
|
|
||||
|
@Comment("开始时间") |
||||
|
@Column(name = "start_time") |
||||
|
@TableField("start_time") |
||||
|
private Date startTime; |
||||
|
|
||||
|
@Comment("提交时间") |
||||
|
@Column(name = "submit_time") |
||||
|
@TableField("submit_time") |
||||
|
private Date submitTime; |
||||
|
/** |
||||
|
* 考试快照(包含:题干、当时乱序后的选项内容、用户答案、正确答案、解析、是否正确) |
||||
|
*/ |
||||
|
@TableField(value = "answer_snapshot" , typeHandler = JacksonTypeHandler.class) |
||||
|
@JdbcTypeCode(SqlTypes.JSON) |
||||
|
@Column(name = "answer_snapshot", columnDefinition = "json") |
||||
|
private List<QuestionSnapshot> answerSnapshot; |
||||
|
|
||||
|
@Data |
||||
|
public static class QuestionSnapshot { |
||||
|
private Long questionId; |
||||
|
// 题干原文
|
||||
|
private String questionContent; |
||||
|
// 题型
|
||||
|
private Integer type; |
||||
|
// 用户看的选项:{"A":"xxx", "B":"yyy"}
|
||||
|
private Map<String, String> options; |
||||
|
// 正确项
|
||||
|
private String rightAnswer; |
||||
|
// 用户选的
|
||||
|
private String userAnswer; |
||||
|
// 判定正确
|
||||
|
private Boolean isRight; |
||||
|
// AI解析
|
||||
|
private String analysis; |
||||
|
// 该题是否已发起申诉
|
||||
|
private Boolean hasAppealed = false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,144 @@ |
|||||
|
package com.project.task.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.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
||||
|
import com.project.base.domain.entity.BaseEntity; |
||||
|
import jakarta.persistence.*; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.hibernate.annotations.Comment; |
||||
|
import org.hibernate.annotations.JdbcTypeCode; |
||||
|
import org.hibernate.type.SqlTypes; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
@Table(name = "evaluator_task" , |
||||
|
indexes = {@Index(name = "Idx_startTime_endTime" , columnList = "start_time,end_time") , |
||||
|
@Index(name = "Idx_endTime" , columnList = "end_time") , |
||||
|
@Index(name = "Idx_subLineId" , columnList = "sub_line_id") |
||||
|
}) |
||||
|
@Entity |
||||
|
@TableName(value = "evaluator_task", autoResultMap = true) |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
|
||||
|
public class TaskEntity extends BaseEntity { |
||||
|
@TableId(value = "id" , type = IdType.ASSIGN_ID) |
||||
|
@Id |
||||
|
private Long id; |
||||
|
|
||||
|
@Column(name = "name" , columnDefinition="varchar(50) comment '考试任务名称'") |
||||
|
private String name; |
||||
|
|
||||
|
@Comment("开始时间") |
||||
|
@Column(name = "start_time") |
||||
|
@TableField("start_time") |
||||
|
private Date startTime; |
||||
|
|
||||
|
@Comment("结束时间") |
||||
|
@Column(name = "end_time") |
||||
|
@TableField("end_time") |
||||
|
private Date endTime; |
||||
|
|
||||
|
@Column(name = "line_id") |
||||
|
@Comment("产品线ID") |
||||
|
@TableField("line_id") |
||||
|
private Long lineId; |
||||
|
|
||||
|
@Column(name = "line_name" , columnDefinition="varchar(550) comment '所属产品线名称'") |
||||
|
@Comment("产品线名称") |
||||
|
@TableField("line_name") |
||||
|
private String lineName; |
||||
|
|
||||
|
@Column(name = "sub_line_id") |
||||
|
@Comment("所属子产品线ID") |
||||
|
@TableField("sub_line_id") |
||||
|
private Long subLineId; |
||||
|
|
||||
|
@Column(name = "sub_line_name" , columnDefinition="varchar(550) comment '所属子产品线名称'") |
||||
|
@Comment("所属子产品线名称") |
||||
|
@TableField("sub_line_name") |
||||
|
private String subLineName; |
||||
|
|
||||
|
@Column(name = "accurate_grasp_num") |
||||
|
@Comment("精准掌握知识点数量") |
||||
|
private Integer accurateGraspNum; |
||||
|
|
||||
|
@Column(name = "vague_grasp_num") |
||||
|
@Comment("模糊掌握知识点数量") |
||||
|
private Integer vagueGraspNum; |
||||
|
|
||||
|
|
||||
|
@Column(name = "duration") |
||||
|
@Comment("考试时长") |
||||
|
private Integer duration; |
||||
|
|
||||
|
@Column(name = "pass_score") |
||||
|
@Comment("通过考试分数线") |
||||
|
private Integer passScore; |
||||
|
|
||||
|
@Column(name = "note" , columnDefinition="TEXT comment '注意事项'") |
||||
|
private String note; |
||||
|
|
||||
|
/** |
||||
|
* 关联文档idList |
||||
|
* 存储为 JSON 数组: [1, 10, 101] |
||||
|
*/ |
||||
|
@TableField(value = "related_document_list" , typeHandler = JacksonTypeHandler.class) |
||||
|
@Column(name = "related_document_list", columnDefinition = "json comment '关联文档列表'") |
||||
|
@JdbcTypeCode(SqlTypes.JSON) |
||||
|
private List<Long> relatedDocumentList; |
||||
|
|
||||
|
|
||||
|
@Column(name = "single_choice_num") |
||||
|
@TableField("single_choice_num") |
||||
|
@Comment("单选题数量") |
||||
|
private Integer singleChoiceNum; |
||||
|
|
||||
|
@Column(name = "single_choice_score") |
||||
|
@TableField("single_choice_score") |
||||
|
@Comment("单选题分数") |
||||
|
private Double singleChoiceScore; |
||||
|
|
||||
|
|
||||
|
@Column(name = "multiple_choice_num") |
||||
|
@TableField("multiple_choice_num") |
||||
|
@Comment("多选题数量") |
||||
|
private Integer multipleChoiceNum; |
||||
|
|
||||
|
@Column(name = "multiple_choice_score") |
||||
|
@TableField("multiple_choice_score") |
||||
|
@Comment("多选题分数") |
||||
|
private Double multipleChoiceScore; |
||||
|
|
||||
|
@Column(name = "true_false_num") |
||||
|
@TableField("true_false_num") |
||||
|
@Comment("判断题数量") |
||||
|
private Integer trueFalseNum; |
||||
|
|
||||
|
@Column(name = "true_false_score") |
||||
|
@TableField("true_false_score") |
||||
|
@Comment("判断题分数") |
||||
|
private Double trueFalseScore; |
||||
|
|
||||
|
@Column(name = "total_score") |
||||
|
@Comment("总分") |
||||
|
private Double totalScore; |
||||
|
|
||||
|
@Column(name = "participant_num") |
||||
|
@Comment("参与用户人数") |
||||
|
private Integer participantNum; |
||||
|
|
||||
|
@Column(name = "pass_num") |
||||
|
@Comment("及格人数") |
||||
|
private Integer passNum = 0; |
||||
|
|
||||
|
@Column(name = "need_ai_grading") |
||||
|
@Comment("是否包含需要AI判分的题目") |
||||
|
private Boolean needAiGrading = false; |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
package com.project.task.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 com.project.task.domain.enums.TaskUserStatusEnum; |
||||
|
import jakarta.persistence.*; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.hibernate.annotations.Comment; |
||||
|
|
||||
|
@Data |
||||
|
@Table(name = "evaluator_task_user" , indexes = {@Index(name = "Idx_parentId", columnList = "parent_id")}) |
||||
|
@Entity |
||||
|
@TableName(value = "evaluator_task_user" , autoResultMap = true) |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class TaskUserEntity extends BaseEntity { |
||||
|
@TableId(type = IdType.ASSIGN_ID) |
||||
|
@Id |
||||
|
private Long id; |
||||
|
|
||||
|
@TableField("task_id") |
||||
|
@Column(name = "task_id" , columnDefinition="bigint(20) comment '考试任务id'") |
||||
|
private Long taskId; |
||||
|
|
||||
|
@TableField("user_id") |
||||
|
@Column(name = "user_id" , columnDefinition="varchar(255) comment '用户id'") |
||||
|
private String userId; |
||||
|
|
||||
|
@Column(name = "status") |
||||
|
@Comment("考试状态:0-未考, 1-不通过, 2-已通过") |
||||
|
private Integer status = TaskUserStatusEnum.Not_Start.getValue(); |
||||
|
|
||||
|
@Comment("最近一次考试记录ID") |
||||
|
@Column(name = "last_record_id") |
||||
|
@TableField("last_record_id") |
||||
|
private Long lastRecordId; |
||||
|
|
||||
|
|
||||
|
@Comment("累计考试次数") |
||||
|
@Column(name = "attempt_num") |
||||
|
@TableField("attempt_num") |
||||
|
private Integer attemptNum = 0; |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
package com.project.task.domain.enums; |
||||
|
|
||||
|
|
||||
|
import com.project.base.domain.enums.HasValueEnum; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
@Getter |
||||
|
@RequiredArgsConstructor |
||||
|
public enum QuestionTypeEnum implements HasValueEnum<Integer> { |
||||
|
SINGLE_CHOICE(1, "单选题"), // 对应黄色背景
|
||||
|
MULTIPLE_CHOICE(2, "多选题"), // 对应黄色背景
|
||||
|
TRUE_FALSE(3, "判断题"); // 对应绿色背景
|
||||
|
|
||||
|
private final Integer value; |
||||
|
private final String description; |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
package com.project.task.domain.enums; |
||||
|
|
||||
|
import com.project.base.domain.enums.HasValueEnum; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
public enum TaskStatusEnum implements HasValueEnum<String> { |
||||
|
Not_Start("未开考") , |
||||
|
In_Progress("已开考") , |
||||
|
|
||||
|
Cut_Off("已截止"); |
||||
|
@Getter |
||||
|
private final String value; |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package com.project.task.domain.enums; |
||||
|
|
||||
|
import com.project.base.domain.enums.HasValueEnum; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
@Getter |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
public enum TaskUserStatusEnum implements HasValueEnum<Integer> { |
||||
|
Not_Start(0 , "未参加") , |
||||
|
Fail(1 , "未通过") , |
||||
|
Pass(2 ,"已通过"); |
||||
|
|
||||
|
private final Integer value; |
||||
|
|
||||
|
private final String description; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
package com.project.task.domain.param; |
||||
|
|
||||
|
import com.project.base.domain.param.BaseParam; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class TaskParam extends BaseParam { |
||||
|
private String name; |
||||
|
|
||||
|
private Long lineId; |
||||
|
|
||||
|
private Long subLineId; |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.project.task.domain.service; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface DeleteTaskDomainService { |
||||
|
|
||||
|
Result<String> batchDelete(List<Long> idList) throws Exception; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package com.project.task.domain.service; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.task.domain.dto.TaskDTO; |
||||
|
|
||||
|
public interface SaveOrUpdateTaskDomainService { |
||||
|
Result<TaskDTO> saveOrUpdate(TaskDTO dto) throws Exception; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
package com.project.task.domain.service; |
||||
|
|
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.task.domain.dto.TaskDTO; |
||||
|
import com.project.task.domain.param.TaskParam; |
||||
|
|
||||
|
public interface SearchTaskDomainService { |
||||
|
Result<PageResult<TaskDTO>> search(TaskParam param) throws Exception; |
||||
|
|
||||
|
Result<TaskDTO> getDetail(Long id) throws Exception; |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
package com.project.task.domain.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
import com.project.task.domain.entity.TaskEntity; |
||||
|
|
||||
|
public interface TaskBaseService extends IService<TaskEntity> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
package com.project.task.domain.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
import com.project.task.domain.entity.TaskUserEntity; |
||||
|
|
||||
|
public interface TaskUserBaseService extends IService<TaskUserEntity> { |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
package com.project.task.domain.service.impl; |
||||
|
|
||||
|
import cn.hutool.core.date.DateUtil; |
||||
|
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.task.domain.entity.TaskEntity; |
||||
|
import com.project.task.domain.service.DeleteTaskDomainService; |
||||
|
import com.project.task.domain.service.TaskBaseService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Service |
||||
|
public class DeleteTaskDomainServiceImpl implements DeleteTaskDomainService { |
||||
|
@Autowired |
||||
|
private TaskBaseService taskBaseService; |
||||
|
@Override |
||||
|
public Result<String> batchDelete(List<Long> idList) throws Exception { |
||||
|
long count = taskBaseService.count(new LambdaQueryWrapper<TaskEntity>() |
||||
|
.in(TaskEntity::getId, idList) |
||||
|
.ge(TaskEntity::getStartTime, DateUtil.beginOfDay(new Date()))); |
||||
|
if (count > 0) { |
||||
|
throw new BusinessErrorException("已开考 、 已截止的考试任务不允许删除"); |
||||
|
} |
||||
|
taskBaseService.removeBatchByIds(idList); |
||||
|
return Result.success("删除成功"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
package com.project.task.domain.service.impl; |
||||
|
|
||||
|
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.entity.ProductLineEntity; |
||||
|
import com.project.information.domain.service.ProductLineBaseService; |
||||
|
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.entity.TaskUserEntity; |
||||
|
import com.project.task.domain.service.SaveOrUpdateTaskDomainService; |
||||
|
import com.project.task.domain.service.TaskBaseService; |
||||
|
import com.project.task.domain.service.TaskUserBaseService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Objects; |
||||
|
|
||||
|
@Service |
||||
|
public class SaveOrUpdateTaskDomainServiceImpl implements SaveOrUpdateTaskDomainService { |
||||
|
|
||||
|
@Autowired |
||||
|
private ExamScoreRatioConfig examScoreRatioConfig; |
||||
|
|
||||
|
@Autowired |
||||
|
private ProductLineBaseService productLineBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private TaskUserBaseService taskUserBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private TaskBaseService taskBaseService; |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Result<TaskDTO> saveOrUpdate(TaskDTO dto) throws Exception { |
||||
|
checkDTO(dto); |
||||
|
// 计算各题型赋分
|
||||
|
// 1. 计算总权重份额
|
||||
|
int totalUnit = dto.getSingleChoiceNum() * examScoreRatioConfig.getSingle() |
||||
|
+ dto.getMultipleChoiceNum() * examScoreRatioConfig.getMultiple() |
||||
|
+ dto.getTrueFalseNum() * examScoreRatioConfig.getTrueFalse(); |
||||
|
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); |
||||
|
|
||||
|
// 3. 余数平差:从配置中读取总分进行逆向补齐,确保总分 100% 对应配置
|
||||
|
double configTotal = examScoreRatioConfig.getTotalScore().doubleValue(); |
||||
|
|
||||
|
if (dto.getTrueFalseNum() > 0) { |
||||
|
dto.setTrueFalseScore(Math.round((configTotal - dto.getSingleChoiceNum() * dto.getSingleChoiceScore() - dto.getMultipleChoiceNum() * dto.getMultipleChoiceScore()) / dto.getTrueFalseNum() * 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(configTotal); |
||||
|
// 补全冗余字段
|
||||
|
ProductLineEntity subProductLine = productLineBaseService.getById(dto.getSubLineId()); |
||||
|
dto.setSubLineName(subProductLine.getName()); |
||||
|
dto.setLineId(subProductLine.getParentId()); |
||||
|
dto.setLineName(subProductLine.getParentName()); |
||||
|
// todo 知识点分簇,预出题
|
||||
|
|
||||
|
// 保存
|
||||
|
TaskEntity saveEntity = dto.toEntity(TaskEntity::new); |
||||
|
taskBaseService.saveOrUpdate(saveEntity); |
||||
|
|
||||
|
// 保存关联表 TaskUserEntity
|
||||
|
taskUserBaseService.remove(new LambdaQueryWrapper<TaskUserEntity>() |
||||
|
.eq(TaskUserEntity::getTaskId , saveEntity.getId())); |
||||
|
|
||||
|
List<TaskUserEntity> taskUserEntityList = new ArrayList<>(); |
||||
|
for (String userId : dto.getParticipantUserIdList()) { |
||||
|
TaskUserEntity taskUserEntity = new TaskUserEntity(); |
||||
|
taskUserEntity.setTaskId(saveEntity.getId()); |
||||
|
taskUserEntity.setUserId(userId); |
||||
|
taskUserEntityList.add(taskUserEntity); |
||||
|
} |
||||
|
taskUserBaseService.saveBatch(taskUserEntityList); |
||||
|
return Result.success(saveEntity.toDTO(TaskDTO::new)); |
||||
|
} |
||||
|
|
||||
|
private void checkDTO(TaskDTO dto) throws Exception { |
||||
|
if (StrUtil.isBlank(dto.getName())) { |
||||
|
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.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 (!Validator.isBetween(dto.getSingleChoiceNum() + |
||||
|
dto.getMultipleChoiceNum() + |
||||
|
dto.getTrueFalseNum() , 1, 100)) { |
||||
|
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("模糊掌握知识点数量不能为空"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
package com.project.task.domain.service.impl; |
||||
|
|
||||
|
import cn.hutool.core.date.DateUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
import com.project.base.domain.result.PageResult; |
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.base.domain.utils.PageConverter; |
||||
|
import com.project.task.domain.dto.TaskDTO; |
||||
|
import com.project.task.domain.entity.TaskEntity; |
||||
|
import com.project.task.domain.enums.TaskStatusEnum; |
||||
|
import com.project.task.domain.param.TaskParam; |
||||
|
import com.project.task.domain.service.SearchTaskDomainService; |
||||
|
import com.project.task.mapper.TaskMapper; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.Objects; |
||||
|
|
||||
|
@Service |
||||
|
public class SearchTaskDomainServiceImpl implements SearchTaskDomainService { |
||||
|
|
||||
|
@Autowired |
||||
|
private TaskMapper taskMapper; |
||||
|
@Override |
||||
|
public Result<PageResult<TaskDTO>> search(TaskParam param) throws Exception { |
||||
|
LambdaQueryWrapper<TaskEntity> queryWrapper = new LambdaQueryWrapper<>(); |
||||
|
if (StrUtil.isNotBlank(param.getName())) { |
||||
|
queryWrapper.like(TaskEntity::getName , param.getName()); |
||||
|
} |
||||
|
if (Objects.nonNull(param.getLineId())) { |
||||
|
queryWrapper.eq(TaskEntity::getLineId , param.getLineId()); |
||||
|
} |
||||
|
if (Objects.nonNull(param.getSubLineId())) { |
||||
|
queryWrapper.eq(TaskEntity::getSubLineId , param.getSubLineId()); |
||||
|
} |
||||
|
queryWrapper.orderByDesc(TaskEntity::getId); |
||||
|
IPage<TaskEntity> taskEntityPage = taskMapper.selectPage( |
||||
|
PageConverter.toMpPage(param), |
||||
|
queryWrapper); |
||||
|
IPage<TaskDTO> taskDTOIPage = taskEntityPage.convert(this::buildDTO); |
||||
|
return Result.page(taskDTOIPage); |
||||
|
} |
||||
|
|
||||
|
private TaskDTO buildDTO(TaskEntity entity) { |
||||
|
TaskDTO dto = entity.toDTO(TaskDTO::new); |
||||
|
// 开始时间晚于当前
|
||||
|
if (dto.getStartTime().after(new Date())) { |
||||
|
dto.setStatusText(TaskStatusEnum.Not_Start.getValue()); |
||||
|
} else if (DateUtil.isIn(new Date() , dto.getStartTime() , dto.getEndTime())) { |
||||
|
dto.setStatusText(TaskStatusEnum.In_Progress.getValue()); |
||||
|
} else { |
||||
|
dto.setStatusText(TaskStatusEnum.Cut_Off.getValue()); |
||||
|
} |
||||
|
return dto; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Result<TaskDTO> getDetail(Long id) throws Exception { |
||||
|
TaskEntity taskEntity = taskMapper.selectById(id); |
||||
|
return Result.success(buildDTO(taskEntity)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
package com.project.task.domain.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import com.project.task.domain.entity.TaskEntity; |
||||
|
import com.project.task.domain.service.TaskBaseService; |
||||
|
import com.project.task.mapper.TaskMapper; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
|
||||
|
@Service |
||||
|
public class TaskBaseServiceImpl extends ServiceImpl<TaskMapper, TaskEntity> implements TaskBaseService { |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.project.task.domain.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import com.project.task.domain.entity.TaskUserEntity; |
||||
|
import com.project.task.domain.service.TaskUserBaseService; |
||||
|
import com.project.task.mapper.TaskUserMapper; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
public class TaskUserBaseServiceImpl extends ServiceImpl<TaskUserMapper, TaskUserEntity> implements TaskUserBaseService { |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package com.project.task.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.project.task.domain.entity.TaskEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface TaskMapper extends BaseMapper<TaskEntity> { |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package com.project.task.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.project.task.domain.entity.TaskUserEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface TaskUserMapper extends BaseMapper<TaskUserEntity> { |
||||
|
} |
||||
Loading…
Reference in new issue