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;