diff --git a/pom.xml b/pom.xml index f7a55ef1..9c58c8f3 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ safety-eval-client safety-eval-domain safety-eval-infrastructure + safety-cedu-service diff --git a/safety-cedu-service/pom.xml b/safety-cedu-service/pom.xml new file mode 100644 index 00000000..a7570224 --- /dev/null +++ b/safety-cedu-service/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + org.qinan + safety-eval-service + 1.0-SNAPSHOT + + + safety-cedu-service + + + 8 + 8 + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + io.swagger + swagger-annotations + + + + + org.projectlombok + lombok + provided + + + + \ No newline at end of file diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/controller/PaperController.java b/safety-cedu-service/src/main/java/org/qinan/cedu/controller/PaperController.java new file mode 100644 index 00000000..ae5a05dc --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/controller/PaperController.java @@ -0,0 +1,77 @@ +package org.qinan.cedu.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.qinan.cedu.entity.Paper; +import org.qinan.cedu.service.PaperService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 试卷管理 + */ +@Api(tags = "试卷管理") +@RestController +@RequestMapping("/safetyEval/paper") +public class PaperController { + + @Autowired + private PaperService paperService; + + @ApiOperation("分页查询试卷") + @GetMapping("/page") + public IPage page(@RequestParam(defaultValue = "1") long current, + @RequestParam(defaultValue = "10") long size, + @RequestParam(required = false) String paperName, + @RequestParam(required = false) String status) { + return paperService.pageQuery(current, size, paperName, status); + } + + @ApiOperation("查询试卷详情") + @GetMapping("/{id}") + public Paper getById(@PathVariable Long id) { + return paperService.getById(id); + } + + @ApiOperation("新增试卷") + @PostMapping + public boolean save(@RequestBody Paper paper) { + return paperService.savePaper(paper); + } + + @ApiOperation("修改试卷") + @PutMapping + public boolean update(@RequestBody Paper paper) { + return paperService.updatePaper(paper); + } + + @ApiOperation("删除试卷") + @DeleteMapping("/{id}") + public boolean delete(@PathVariable Long id) { + return paperService.deleteById(id); + } + + @ApiOperation("绑定试卷试题") + @PostMapping("/{id}/questions") + public boolean bindQuestions(@PathVariable("id") Long paperId, + @RequestBody List questionIds) { + return paperService.bindQuestions(paperId, questionIds); + } + + @ApiOperation("查询试卷试题id列表") + @GetMapping("/{id}/questionIds") + public List questionIds(@PathVariable("id") Long paperId) { + return paperService.getQuestionIds(paperId); + } +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/controller/QuestionController.java b/safety-cedu-service/src/main/java/org/qinan/cedu/controller/QuestionController.java new file mode 100644 index 00000000..167cef19 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/controller/QuestionController.java @@ -0,0 +1,62 @@ +package org.qinan.cedu.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.qinan.cedu.entity.Question; +import org.qinan.cedu.service.QuestionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 试题管理 + */ +@Api(tags = "试题管理") +@RestController +@RequestMapping("/safetyEval/question") +public class QuestionController { + + @Autowired + private QuestionService questionService; + + @ApiOperation("分页查询试题") + @GetMapping("/page") + public IPage page(@RequestParam(defaultValue = "1") long current, + @RequestParam(defaultValue = "10") long size, + @RequestParam(required = false) String title, + @RequestParam(required = false) String questionType) { + return questionService.pageQuery(current, size, title, questionType); + } + + @ApiOperation("查询试题详情(含选项)") + @GetMapping("/{id}") + public Question getDetail(@PathVariable Long id) { + return questionService.getDetail(id); + } + + @ApiOperation("新增试题(含选项)") + @PostMapping + public boolean save(@RequestBody Question question) { + return questionService.saveWithOptions(question, question.getOptions()); + } + + @ApiOperation("修改试题(含选项)") + @PutMapping + public boolean update(@RequestBody Question question) { + return questionService.updateWithOptions(question, question.getOptions()); + } + + @ApiOperation("删除试题") + @DeleteMapping("/{id}") + public boolean delete(@PathVariable Long id) { + return questionService.deleteById(id); + } +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/entity/BaseEntity.java b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/BaseEntity.java new file mode 100644 index 00000000..6f5d5e60 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/BaseEntity.java @@ -0,0 +1,50 @@ +package org.qinan.cedu.entity; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.qinan.cedu.enums.DeleteEnum; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 实体公共基类(各表公共字段) + */ +@Data +public class BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("删除标识 true/false") + private String deleteEnum = DeleteEnum.FALSE.getCode(); + + @ApiModelProperty("备注") + private String remarks; + + @ApiModelProperty("创建人姓名") + private String createName; + + @ApiModelProperty("更新人姓名") + private String updateName; + + @ApiModelProperty("租户id") + private Long tenantId; + + @ApiModelProperty("版本") + private Integer version; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("修改时间") + private LocalDateTime updateTime; + + @ApiModelProperty("创建人id") + private Long createId; + + @ApiModelProperty("修改人id") + private Long updateId; + + @ApiModelProperty("环境") + private String env; +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/entity/Paper.java b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/Paper.java new file mode 100644 index 00000000..3e3af466 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/Paper.java @@ -0,0 +1,39 @@ +package org.qinan.cedu.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * 试卷 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("paper") +@ApiModel(value = "Paper", description = "试卷") +public class Paper extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("试卷名称") + private String paperName; + + @ApiModelProperty("试卷总分") + private BigDecimal paperTotalScore; + + @ApiModelProperty("合格分数") + private BigDecimal paperPassScore; + + @ApiModelProperty("试卷状态 normal 正常") + private String status; +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/entity/PaperQuestionRel.java b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/PaperQuestionRel.java new file mode 100644 index 00000000..ca4285e0 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/PaperQuestionRel.java @@ -0,0 +1,31 @@ +package org.qinan.cedu.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 试卷试题关系 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("paper_question_rel") +@ApiModel(value = "PaperQuestionRel", description = "试卷试题关系") +public class PaperQuestionRel extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("试卷id") + private Long paperId; + + @ApiModelProperty("试题id") + private Long questionId; +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/entity/Question.java b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/Question.java new file mode 100644 index 00000000..59412806 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/Question.java @@ -0,0 +1,60 @@ +package org.qinan.cedu.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 io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 试题 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("question") +@ApiModel(value = "Question", description = "试题") +public class Question extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + @ApiModelProperty("试题ID") + private Long id; + + @ApiModelProperty("题目内容") + private String title; + + @ApiModelProperty("题型:single单选/multiple多选/judge判断/fill填空/short简答") + private String questionType; + + @ApiModelProperty("参考答案:逗号隔开") + private String answer; + + @ApiModelProperty("答案选项id 多个逗号隔开") + private String answerQuestionOptionIds; + + @ApiModelProperty("分值") + private BigDecimal score; + + @ApiModelProperty("答案解析") + private String analysis; + + @ApiModelProperty("标签类型") + private String tagType; + + @ApiModelProperty("试题号") + private Integer questionNum; + + @ApiModelProperty("关联课件") + private Long coursewareId; + + @ApiModelProperty("试题选项列表") + @TableField(exist = false) + private List options; +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/entity/QuestionOption.java b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/QuestionOption.java new file mode 100644 index 00000000..4d56a3e7 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/entity/QuestionOption.java @@ -0,0 +1,43 @@ +package org.qinan.cedu.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 试题选项 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("question_option") +@ApiModel(value = "QuestionOption", description = "试题选项") +public class QuestionOption extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + @ApiModelProperty("选项ID") + private Long id; + + @ApiModelProperty("所属试题ID") + private Long questionId; + + @ApiModelProperty("选项标识:A,B,C,D,E,F") + private String optionKey; + + @ApiModelProperty("选项标志名称") + private String optionName; + + @ApiModelProperty("选项内容") + private String optionText; + + @ApiModelProperty("是否正确答案:1是,0否") + private Boolean isCorrect; + + @ApiModelProperty("选项显示顺序") + private Integer sortOrder; +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/enums/DeleteEnum.java b/safety-cedu-service/src/main/java/org/qinan/cedu/enums/DeleteEnum.java new file mode 100644 index 00000000..15f29cff --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/enums/DeleteEnum.java @@ -0,0 +1,39 @@ +package org.qinan.cedu.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 删除标识枚举(delete_enum 字段) + */ +@Getter +@AllArgsConstructor +public enum DeleteEnum { + + /** + * 已删除 + */ + TRUE("true"), + + /** + * 未删除 + */ + FALSE("false"); + + /** + * 编码 + */ + private final String code; + + /** + * 根据编码获取枚举 + */ + public static DeleteEnum fromCode(String code) { + for (DeleteEnum deleteEnum : values()) { + if (deleteEnum.code.equals(code)) { + return deleteEnum; + } + } + return null; + } +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/enums/PaperStatusEnum.java b/safety-cedu-service/src/main/java/org/qinan/cedu/enums/PaperStatusEnum.java new file mode 100644 index 00000000..2af93f35 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/enums/PaperStatusEnum.java @@ -0,0 +1,39 @@ +package org.qinan.cedu.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 试卷状态枚举 + */ +@Getter +@AllArgsConstructor +public enum PaperStatusEnum { + + /** + * 正常 + */ + NORMAL("normal", "正常"); + + /** + * 编码 + */ + private final String code; + + /** + * 描述 + */ + private final String desc; + + /** + * 根据编码获取枚举 + */ + public static PaperStatusEnum fromCode(String code) { + for (PaperStatusEnum status : values()) { + if (status.code.equals(code)) { + return status; + } + } + return null; + } +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/enums/QuestionTypeEnum.java b/safety-cedu-service/src/main/java/org/qinan/cedu/enums/QuestionTypeEnum.java new file mode 100644 index 00000000..a5f9bc35 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/enums/QuestionTypeEnum.java @@ -0,0 +1,59 @@ +package org.qinan.cedu.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 题型枚举 + */ +@Getter +@AllArgsConstructor +public enum QuestionTypeEnum { + + /** + * 单选 + */ + SINGLE("single", "单选"), + + /** + * 多选 + */ + MULTIPLE("multiple", "多选"), + + /** + * 判断 + */ + JUDGE("judge", "判断"), + + /** + * 填空 + */ + FILL("fill", "填空"), + + /** + * 简答 + */ + SHORT("short", "简答"); + + /** + * 编码 + */ + private final String code; + + /** + * 描述 + */ + private final String desc; + + /** + * 根据编码获取枚举 + */ + public static QuestionTypeEnum fromCode(String code) { + for (QuestionTypeEnum type : values()) { + if (type.code.equals(code)) { + return type; + } + } + return null; + } +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/PaperMapper.java b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/PaperMapper.java new file mode 100644 index 00000000..e2107848 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/PaperMapper.java @@ -0,0 +1,12 @@ +package org.qinan.cedu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.qinan.cedu.entity.Paper; + +/** + * 试卷 Mapper + */ +@Mapper +public interface PaperMapper extends BaseMapper { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/PaperQuestionRelMapper.java b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/PaperQuestionRelMapper.java new file mode 100644 index 00000000..fc99ad88 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/PaperQuestionRelMapper.java @@ -0,0 +1,12 @@ +package org.qinan.cedu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.qinan.cedu.entity.PaperQuestionRel; + +/** + * 试卷试题关系 Mapper + */ +@Mapper +public interface PaperQuestionRelMapper extends BaseMapper { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/QuestionMapper.java b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/QuestionMapper.java new file mode 100644 index 00000000..729c024f --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/QuestionMapper.java @@ -0,0 +1,12 @@ +package org.qinan.cedu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.qinan.cedu.entity.Question; + +/** + * 试题 Mapper + */ +@Mapper +public interface QuestionMapper extends BaseMapper { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/QuestionOptionMapper.java b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/QuestionOptionMapper.java new file mode 100644 index 00000000..71b33e6e --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/mapper/QuestionOptionMapper.java @@ -0,0 +1,12 @@ +package org.qinan.cedu.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.qinan.cedu.entity.QuestionOption; + +/** + * 试题选项 Mapper + */ +@Mapper +public interface QuestionOptionMapper extends BaseMapper { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/PaperQuestionRelService.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/PaperQuestionRelService.java new file mode 100644 index 00000000..ae77cd13 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/PaperQuestionRelService.java @@ -0,0 +1,10 @@ +package org.qinan.cedu.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.qinan.cedu.entity.PaperQuestionRel; + +/** + * 试卷试题关系服务接口 + */ +public interface PaperQuestionRelService extends IService { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/PaperService.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/PaperService.java new file mode 100644 index 00000000..30a28eb3 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/PaperService.java @@ -0,0 +1,65 @@ +package org.qinan.cedu.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.qinan.cedu.entity.Paper; + +import java.util.List; + +/** + * 试卷服务接口 + */ +public interface PaperService extends IService { + + /** + * 分页查询试卷 + * + * @param current 当前页 + * @param size 每页条数 + * @param paperName 试卷名称(模糊) + * @param status 试卷状态 + * @return 分页结果 + */ + IPage pageQuery(long current, long size, String paperName, String status); + + /** + * 新增试卷 + * + * @param paper 试卷 + * @return 是否成功 + */ + boolean savePaper(Paper paper); + + /** + * 修改试卷 + * + * @param paper 试卷 + * @return 是否成功 + */ + boolean updatePaper(Paper paper); + + /** + * 逻辑删除试卷(同时逻辑删除试卷试题关系) + * + * @param id 试卷id + * @return 是否成功 + */ + boolean deleteById(Long id); + + /** + * 绑定试卷试题(全量覆盖) + * + * @param paperId 试卷id + * @param questionIds 试题id列表 + * @return 是否成功 + */ + boolean bindQuestions(Long paperId, List questionIds); + + /** + * 查询试卷下的试题id列表 + * + * @param paperId 试卷id + * @return 试题id列表 + */ + List getQuestionIds(Long paperId); +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/QuestionOptionService.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/QuestionOptionService.java new file mode 100644 index 00000000..a71f27e5 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/QuestionOptionService.java @@ -0,0 +1,10 @@ +package org.qinan.cedu.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.qinan.cedu.entity.QuestionOption; + +/** + * 试题选项服务接口 + */ +public interface QuestionOptionService extends IService { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/QuestionService.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/QuestionService.java new file mode 100644 index 00000000..109566e0 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/QuestionService.java @@ -0,0 +1,59 @@ +package org.qinan.cedu.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.qinan.cedu.entity.Question; +import org.qinan.cedu.entity.QuestionOption; + +import java.util.List; + +/** + * 试题服务接口 + */ +public interface QuestionService extends IService { + + /** + * 分页查询试题 + * + * @param current 当前页 + * @param size 每页条数 + * @param title 题目内容(模糊) + * @param questionType 题型 + * @return 分页结果 + */ + IPage pageQuery(long current, long size, String title, String questionType); + + /** + * 查询试题详情(含选项) + * + * @param id 试题id + * @return 试题详情 + */ + Question getDetail(Long id); + + /** + * 新增试题(含选项) + * + * @param question 试题 + * @param options 选项列表 + * @return 是否成功 + */ + boolean saveWithOptions(Question question, List options); + + /** + * 修改试题(选项全量覆盖) + * + * @param question 试题 + * @param options 选项列表 + * @return 是否成功 + */ + boolean updateWithOptions(Question question, List options); + + /** + * 逻辑删除试题(同时逻辑删除其选项) + * + * @param id 试题id + * @return 是否成功 + */ + boolean deleteById(Long id); +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/PaperQuestionRelServiceImpl.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/PaperQuestionRelServiceImpl.java new file mode 100644 index 00000000..53c041c2 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/PaperQuestionRelServiceImpl.java @@ -0,0 +1,14 @@ +package org.qinan.cedu.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.qinan.cedu.entity.PaperQuestionRel; +import org.qinan.cedu.mapper.PaperQuestionRelMapper; +import org.qinan.cedu.service.PaperQuestionRelService; +import org.springframework.stereotype.Service; + +/** + * 试卷试题关系服务实现 + */ +@Service +public class PaperQuestionRelServiceImpl extends ServiceImpl implements PaperQuestionRelService { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/PaperServiceImpl.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/PaperServiceImpl.java new file mode 100644 index 00000000..921b44c4 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/PaperServiceImpl.java @@ -0,0 +1,110 @@ +package org.qinan.cedu.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.qinan.cedu.entity.Paper; +import org.qinan.cedu.entity.PaperQuestionRel; +import org.qinan.cedu.enums.DeleteEnum; +import org.qinan.cedu.enums.PaperStatusEnum; +import org.qinan.cedu.mapper.PaperMapper; +import org.qinan.cedu.service.PaperQuestionRelService; +import org.qinan.cedu.service.PaperService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 试卷服务实现 + */ +@Service +public class PaperServiceImpl extends ServiceImpl implements PaperService { + + @Autowired + private PaperQuestionRelService paperQuestionRelService; + + @Override + public IPage pageQuery(long current, long size, String paperName, String status) { + return this.lambdaQuery() + .eq(Paper::getDeleteEnum, DeleteEnum.FALSE.getCode()) + .like(StringUtils.hasText(paperName), Paper::getPaperName, paperName) + .eq(StringUtils.hasText(status), Paper::getStatus, status) + .orderByDesc(Paper::getCreateTime) + .page(new Page<>(current, size)); + } + + @Override + public boolean savePaper(Paper paper) { + LocalDateTime now = LocalDateTime.now(); + paper.setCreateTime(now); + paper.setUpdateTime(now); + paper.setVersion(0); + if (!StringUtils.hasText(paper.getStatus())) { + paper.setStatus(PaperStatusEnum.NORMAL.getCode()); + } + return this.save(paper); + } + + @Override + public boolean updatePaper(Paper paper) { + paper.setUpdateTime(LocalDateTime.now()); + return this.updateById(paper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteById(Long id) { + boolean deleted = this.lambdaUpdate() + .set(Paper::getDeleteEnum, DeleteEnum.TRUE.getCode()) + .set(Paper::getUpdateTime, LocalDateTime.now()) + .eq(Paper::getId, id) + .eq(Paper::getDeleteEnum, DeleteEnum.FALSE.getCode()) + .update(); + if (deleted) { + paperQuestionRelService.lambdaUpdate() + .set(PaperQuestionRel::getDeleteEnum, DeleteEnum.TRUE.getCode()) + .set(PaperQuestionRel::getUpdateTime, LocalDateTime.now()) + .eq(PaperQuestionRel::getPaperId, id) + .update(); + } + return deleted; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean bindQuestions(Long paperId, List questionIds) { + paperQuestionRelService.remove(Wrappers.lambdaQuery() + .eq(PaperQuestionRel::getPaperId, paperId)); + if (CollectionUtils.isEmpty(questionIds)) { + return true; + } + LocalDateTime now = LocalDateTime.now(); + List rels = questionIds.stream().map(questionId -> { + PaperQuestionRel rel = new PaperQuestionRel(); + rel.setPaperId(paperId); + rel.setQuestionId(questionId); + rel.setCreateTime(now); + rel.setUpdateTime(now); + return rel; + }).collect(Collectors.toList()); + return paperQuestionRelService.saveBatch(rels); + } + + @Override + public List getQuestionIds(Long paperId) { + return paperQuestionRelService.lambdaQuery() + .eq(PaperQuestionRel::getPaperId, paperId) + .eq(PaperQuestionRel::getDeleteEnum, DeleteEnum.FALSE.getCode()) + .list() + .stream() + .map(PaperQuestionRel::getQuestionId) + .collect(Collectors.toList()); + } +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/QuestionOptionServiceImpl.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/QuestionOptionServiceImpl.java new file mode 100644 index 00000000..92a452c1 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/QuestionOptionServiceImpl.java @@ -0,0 +1,14 @@ +package org.qinan.cedu.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.qinan.cedu.entity.QuestionOption; +import org.qinan.cedu.mapper.QuestionOptionMapper; +import org.qinan.cedu.service.QuestionOptionService; +import org.springframework.stereotype.Service; + +/** + * 试题选项服务实现 + */ +@Service +public class QuestionOptionServiceImpl extends ServiceImpl implements QuestionOptionService { +} diff --git a/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/QuestionServiceImpl.java b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/QuestionServiceImpl.java new file mode 100644 index 00000000..1c7f9c39 --- /dev/null +++ b/safety-cedu-service/src/main/java/org/qinan/cedu/service/impl/QuestionServiceImpl.java @@ -0,0 +1,117 @@ +package org.qinan.cedu.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.qinan.cedu.entity.Question; +import org.qinan.cedu.entity.QuestionOption; +import org.qinan.cedu.enums.DeleteEnum; +import org.qinan.cedu.mapper.QuestionMapper; +import org.qinan.cedu.service.QuestionOptionService; +import org.qinan.cedu.service.QuestionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 试题服务实现 + */ +@Service +public class QuestionServiceImpl extends ServiceImpl implements QuestionService { + + @Autowired + private QuestionOptionService questionOptionService; + + @Override + public IPage pageQuery(long current, long size, String title, String questionType) { + return this.lambdaQuery() + .eq(Question::getDeleteEnum, DeleteEnum.FALSE.getCode()) + .like(StringUtils.hasText(title), Question::getTitle, title) + .eq(StringUtils.hasText(questionType), Question::getQuestionType, questionType) + .orderByDesc(Question::getCreateTime) + .page(new Page<>(current, size)); + } + + @Override + public Question getDetail(Long id) { + Question question = this.getById(id); + if (question == null) { + return null; + } + List options = questionOptionService.lambdaQuery() + .eq(QuestionOption::getQuestionId, id) + .eq(QuestionOption::getDeleteEnum, DeleteEnum.FALSE.getCode()) + .orderByAsc(QuestionOption::getSortOrder) + .list(); + question.setOptions(options); + return question; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean saveWithOptions(Question question, List options) { + LocalDateTime now = LocalDateTime.now(); + question.setCreateTime(now); + question.setUpdateTime(now); + boolean saved = this.save(question); + if (saved) { + saveOptions(question.getId(), options, now); + } + return saved; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateWithOptions(Question question, List options) { + LocalDateTime now = LocalDateTime.now(); + question.setUpdateTime(now); + boolean updated = this.updateById(question); + if (updated) { + questionOptionService.remove(Wrappers.lambdaQuery() + .eq(QuestionOption::getQuestionId, question.getId())); + saveOptions(question.getId(), options, now); + } + return updated; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteById(Long id) { + boolean deleted = this.lambdaUpdate() + .set(Question::getDeleteEnum, DeleteEnum.TRUE.getCode()) + .set(Question::getUpdateTime, LocalDateTime.now()) + .eq(Question::getId, id) + .eq(Question::getDeleteEnum, DeleteEnum.FALSE.getCode()) + .update(); + if (deleted) { + questionOptionService.lambdaUpdate() + .set(QuestionOption::getDeleteEnum, DeleteEnum.TRUE.getCode()) + .set(QuestionOption::getUpdateTime, LocalDateTime.now()) + .eq(QuestionOption::getQuestionId, id) + .update(); + } + return deleted; + } + + /** + * 保存试题选项 + */ + private void saveOptions(Long questionId, List options, LocalDateTime now) { + if (CollectionUtils.isEmpty(options)) { + return; + } + options.forEach(option -> { + option.setId(null); + option.setQuestionId(questionId); + option.setCreateTime(now); + option.setUpdateTime(now); + }); + questionOptionService.saveBatch(options); + } +} diff --git a/safety-cedu-service/src/test/java/db.sql b/safety-cedu-service/src/test/java/db.sql new file mode 100644 index 00000000..cbf9e383 --- /dev/null +++ b/safety-cedu-service/src/test/java/db.sql @@ -0,0 +1,125 @@ +/* + Navicat Premium Dump SQL + + Source Server : dev + Source Server Type : MySQL + Source Server Version : 80035 (8.0.35) + Source Host : 192.168.20.100:33080 + Source Schema : cedu-service + + Target Server Type : MySQL + Target Server Version : 80035 (8.0.35) + File Encoding : 65001 + + Date: 17/08/2026 15:51:24 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for paper +-- ---------------------------- +DROP TABLE IF EXISTS `paper`; +CREATE TABLE `paper` ( + `id` bigint NOT NULL COMMENT 'id', + `paper_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷名称', + `paper_total_score` decimal(6, 2) NOT NULL DEFAULT 0.00 COMMENT '试卷总分', + `paper_pass_score` decimal(6, 2) NOT NULL DEFAULT 0.00 COMMENT '合格分数', + `status` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '试卷试卷状态 normal 正常', + `delete_enum` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标识true false', + `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `create_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人姓名', + `update_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人姓名', + `tenant_id` bigint NULL DEFAULT NULL COMMENT '租户id', + `version` int NULL DEFAULT NULL COMMENT '版本', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间', + `create_id` bigint NULL DEFAULT NULL COMMENT '创建人id', + `update_id` bigint NULL DEFAULT NULL COMMENT '修改人id', + `env` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '环境', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '试卷' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for paper_question_rel +-- ---------------------------- +DROP TABLE IF EXISTS `paper_question_rel`; +CREATE TABLE `paper_question_rel` ( + `id` bigint NOT NULL COMMENT 'id', + `paper_id` bigint NOT NULL COMMENT '试卷id', + `question_id` bigint NOT NULL COMMENT '试题id', + `delete_enum` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标识true false', + `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `create_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人姓名', + `update_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人姓名', + `tenant_id` bigint NULL DEFAULT NULL COMMENT '租户id', + `version` int NULL DEFAULT NULL COMMENT '版本', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间', + `create_id` bigint NULL DEFAULT NULL COMMENT '创建人id', + `update_id` bigint NULL DEFAULT NULL COMMENT '修改人id', + `env` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '环境', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_paper_id`(`paper_id` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '试卷试题关系表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for question +-- ---------------------------- +DROP TABLE IF EXISTS `question`; +CREATE TABLE `question` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '试题ID', + `title` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '题目内容', + `question_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'single' COMMENT '题型:single单选/multiple多选/judge判断/fill填空/short简答', + `answer` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '参考答案:逗号隔开', + `answer_question_option_ids` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '答案选项id 多个逗号隔开', + `score` decimal(5, 2) NOT NULL DEFAULT 0.00 COMMENT '分值', + `analysis` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '答案解析', + `tag_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '标签类型', + `question_num` int NULL DEFAULT NULL COMMENT '试题号', + `courseware_id` bigint NULL DEFAULT NULL COMMENT '关联课件', + `delete_enum` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标识true false', + `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `create_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人姓名', + `update_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人姓名', + `tenant_id` bigint NULL DEFAULT NULL COMMENT '租户id', + `version` int NULL DEFAULT NULL COMMENT '版本', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间', + `create_id` bigint NULL DEFAULT NULL COMMENT '创建人id', + `update_id` bigint NULL DEFAULT NULL COMMENT '修改人id', + `env` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '环境', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_courseware_id`(`courseware_id` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '试题表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for question_option +-- ---------------------------- +DROP TABLE IF EXISTS `question_option`; +CREATE TABLE `question_option` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '选项ID', + `question_id` bigint UNSIGNED NOT NULL COMMENT '所属试题ID', + `option_key` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '选项标识:A,B,C,D,E,F', + `option_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '选项标志名称', + `option_text` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '选项内容', + `is_correct` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否正确答案:1是,0否', + `sort_order` int NOT NULL DEFAULT 0 COMMENT '选项显示顺序', + `delete_enum` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标识true false', + `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `create_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人姓名', + `update_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人姓名', + `tenant_id` bigint NULL DEFAULT NULL COMMENT '租户id', + `version` int NULL DEFAULT NULL COMMENT '版本', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间', + `create_id` bigint NULL DEFAULT NULL COMMENT '创建人id', + `update_id` bigint NULL DEFAULT NULL COMMENT '修改人id', + `env` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '环境', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_question_id`(`question_id` ASC) USING BTREE, + CONSTRAINT `fk_question_option_question` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '试题选项表' ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1;