dev-tmp1
luotaiqian 2026-08-17 16:06:23 +08:00
parent 33d58e63c7
commit 4d14e528cf
25 changed files with 1119 additions and 0 deletions

View File

@ -23,6 +23,7 @@
<module>safety-eval-client</module>
<module>safety-eval-domain</module>
<module>safety-eval-infrastructure</module>
<module>safety-cedu-service</module>
</modules>
<properties>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.qinan</groupId>
<artifactId>safety-eval-service</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>safety-cedu-service</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- Swagger 注解 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -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<Paper> 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<Long> questionIds) {
return paperService.bindQuestions(paperId, questionIds);
}
@ApiOperation("查询试卷试题id列表")
@GetMapping("/{id}/questionIds")
public List<Long> questionIds(@PathVariable("id") Long paperId) {
return paperService.getQuestionIds(paperId);
}
}

View File

@ -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<Question> 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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<QuestionOption> options;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Paper> {
}

View File

@ -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<PaperQuestionRel> {
}

View File

@ -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<Question> {
}

View File

@ -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<QuestionOption> {
}

View File

@ -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<PaperQuestionRel> {
}

View File

@ -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<Paper> {
/**
*
*
* @param current
* @param size
* @param paperName
* @param status
* @return
*/
IPage<Paper> 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<Long> questionIds);
/**
* id
*
* @param paperId id
* @return id
*/
List<Long> getQuestionIds(Long paperId);
}

View File

@ -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<QuestionOption> {
}

View File

@ -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<Question> {
/**
*
*
* @param current
* @param size
* @param title
* @param questionType
* @return
*/
IPage<Question> 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<QuestionOption> options);
/**
*
*
* @param question
* @param options
* @return
*/
boolean updateWithOptions(Question question, List<QuestionOption> options);
/**
*
*
* @param id id
* @return
*/
boolean deleteById(Long id);
}

View File

@ -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<PaperQuestionRelMapper, PaperQuestionRel> implements PaperQuestionRelService {
}

View File

@ -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<PaperMapper, Paper> implements PaperService {
@Autowired
private PaperQuestionRelService paperQuestionRelService;
@Override
public IPage<Paper> 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<Long> questionIds) {
paperQuestionRelService.remove(Wrappers.<PaperQuestionRel>lambdaQuery()
.eq(PaperQuestionRel::getPaperId, paperId));
if (CollectionUtils.isEmpty(questionIds)) {
return true;
}
LocalDateTime now = LocalDateTime.now();
List<PaperQuestionRel> 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<Long> getQuestionIds(Long paperId) {
return paperQuestionRelService.lambdaQuery()
.eq(PaperQuestionRel::getPaperId, paperId)
.eq(PaperQuestionRel::getDeleteEnum, DeleteEnum.FALSE.getCode())
.list()
.stream()
.map(PaperQuestionRel::getQuestionId)
.collect(Collectors.toList());
}
}

View File

@ -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<QuestionOptionMapper, QuestionOption> implements QuestionOptionService {
}

View File

@ -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<QuestionMapper, Question> implements QuestionService {
@Autowired
private QuestionOptionService questionOptionService;
@Override
public IPage<Question> 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<QuestionOption> 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<QuestionOption> 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<QuestionOption> options) {
LocalDateTime now = LocalDateTime.now();
question.setUpdateTime(now);
boolean updated = this.updateById(question);
if (updated) {
questionOptionService.remove(Wrappers.<QuestionOption>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<QuestionOption> 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);
}
}

View File

@ -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;