diff --git a/src/main/java/com/example/exam/exam/dal/ExamErrorQuestion.java b/src/main/java/com/example/exam/exam/dal/ExamErrorQuestion.java
new file mode 100644
index 0000000..b813800
--- /dev/null
+++ b/src/main/java/com/example/exam/exam/dal/ExamErrorQuestion.java
@@ -0,0 +1,126 @@
+package com.example.exam.exam.dal;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 错题集
+ *
+ * @author pengchen
+ */
+
+@TableName(value = "exam_error_question", autoResultMap = true)
+@Accessors(chain = true)
+@Data
+public class ExamErrorQuestion {
+
+
+ @TableId
+ private String id;
+ /**
+ * 试题id
+ */
+ private String quId;
+
+ /**
+ * 任务ID
+ */
+ private String taskId;
+
+ /**
+ * 章节名称
+ */
+ private String chapteridDictText;
+ /**
+ * 题型名称
+ */
+ private String subjectName;
+
+ /**
+ * 题型难度(0:简单,1:一般,2:困难)
+ */
+ private Integer quLevel;
+
+ /**
+ * 试题内容(带样式:
下列表格123
\n\n\n\n1 | \)
+ */
+ private String content;
+
+ /**
+ * 试题内容(纯文本)
+ */
+ private String contentText;
+
+ /**
+ * 解析(带样式)
+ */
+ private String analysis;
+
+ /**
+ * c语言参考答案
+ */
+ private String answer;
+
+
+ /**
+ * 知识点
+ */
+ private String pointNames;
+ /**
+ * 关键字
+ */
+ private String keywords;
+
+ /**
+ * 课程类别
+ */
+ private String courseName;
+
+ /**
+ * 专业分类
+ */
+ private String specialtyName;
+ /**
+ * 数据库名
+ */
+ private String tname;
+
+ /**
+ * 试题答案
+ */
+ @TableField(exist = false)
+ private List answerList;
+
+ /**
+ * 试题文件
+ */
+ @TableField(exist = false)
+ private List fileUploads;
+
+ /**
+ * 试题判分
+ */
+ @TableField(exist = false)
+ private ExamQuestionScore questionScores;
+
+ /**
+ * 试题关键字
+ */
+ @TableField(exist = false)
+ private List questionKeywords;
+
+ private LocalDateTime createTime;
+
+ private String creator;
+ /**
+ * 租户id
+ */
+ private long tenantId;
+
+}
diff --git a/src/main/java/com/example/exam/exam/mapper/ExamErrorQuestionMapper.java b/src/main/java/com/example/exam/exam/mapper/ExamErrorQuestionMapper.java
new file mode 100644
index 0000000..bd011d4
--- /dev/null
+++ b/src/main/java/com/example/exam/exam/mapper/ExamErrorQuestionMapper.java
@@ -0,0 +1,27 @@
+package com.example.exam.exam.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.exam.exam.dal.ExamErrorQuestion;
+import com.example.exam.exam.dal.ExamQuestion;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 错题集Mapper接口
+ *
+ * @author pengchen
+ */
+@Mapper
+public interface ExamErrorQuestionMapper extends BaseMapper
+{
+
+ default ExamErrorQuestion selectByIdAndUserId(String quId, String userId, String tenantId) {
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(ExamErrorQuestion::getQuId, quId);
+ wrapper.eq(ExamErrorQuestion::getCreator, userId);
+ wrapper.eq(ExamErrorQuestion::getTenantId, tenantId);
+ return selectOne(wrapper);
+ }
+}
diff --git a/src/main/java/com/example/exam/exam/service/error/ExamErrorQuestionServiceImpl.java b/src/main/java/com/example/exam/exam/service/error/ExamErrorQuestionServiceImpl.java
new file mode 100644
index 0000000..b101158
--- /dev/null
+++ b/src/main/java/com/example/exam/exam/service/error/ExamErrorQuestionServiceImpl.java
@@ -0,0 +1,32 @@
+package com.example.exam.exam.service.error;
+
+
+import com.example.exam.exam.dal.*;
+import com.example.exam.exam.mapper.*;
+import jakarta.annotation.Resource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 错题集Service业务层处理
+ *
+ * @author pengchen
+ */
+@Service
+public class ExamErrorQuestionServiceImpl implements IExamErrorQuestionService
+{
+ @Resource
+ ExamErrorQuestionMapper examErrorQuestionMapper;
+
+ @Override
+ public int insertExamErrorQuestion(ExamErrorQuestion examErrorQuestion) {
+ return examErrorQuestionMapper.insert(examErrorQuestion);
+ }
+
+ @Override
+ public boolean isExamErrorQuestion(String quId, String userId, String tenantId) {
+ return examErrorQuestionMapper.selectByIdAndUserId(quId, userId, tenantId) != null;
+ }
+}
diff --git a/src/main/java/com/example/exam/exam/service/error/IExamErrorQuestionService.java b/src/main/java/com/example/exam/exam/service/error/IExamErrorQuestionService.java
new file mode 100644
index 0000000..9f87b31
--- /dev/null
+++ b/src/main/java/com/example/exam/exam/service/error/IExamErrorQuestionService.java
@@ -0,0 +1,21 @@
+package com.example.exam.exam.service.error;
+
+
+import com.example.exam.exam.dal.ExamErrorQuestion;
+
+/**
+ * 错题集Service接口
+ *
+ * @author pengchen
+ */
+public interface IExamErrorQuestionService {
+ /**
+ * 新增错题集数据
+ */
+ public int insertExamErrorQuestion(ExamErrorQuestion examErrorQuestion);
+
+ /**
+ * 判断错题是否存在
+ */
+ boolean isExamErrorQuestion(String quId, String userId, String tenantId);
+}
diff --git a/src/main/java/com/example/exam/exam/utils/error_question/ErrorQuestion.java b/src/main/java/com/example/exam/exam/utils/error_question/ErrorQuestion.java
new file mode 100644
index 0000000..fb46986
--- /dev/null
+++ b/src/main/java/com/example/exam/exam/utils/error_question/ErrorQuestion.java
@@ -0,0 +1,36 @@
+package com.example.exam.exam.utils.error_question;
+
+import com.example.exam.exam.controller.auto.vo.StuInfoVo;
+import com.example.exam.exam.dal.ExamErrorQuestion;
+import com.example.exam.exam.dal.ExamQuestion;
+import com.example.exam.exam.dal.SystemTenant;
+import com.example.exam.exam.utils.snowflake.SnowflakeId;
+import org.springframework.beans.BeanUtils;
+
+import java.time.LocalDateTime;
+
+public class ErrorQuestion {
+
+ public static ExamErrorQuestion insertErrorQuestion(String questionId,
+ ExamQuestion examQuestion,
+ String taskId,
+ SystemTenant systemTenant,
+ StuInfoVo stuInfoVo,
+ int isError) {
+ // 判断是否进入错题集
+ if (isError != 0) {
+ // 如果不是全对得话进入错题集
+ System.out.println("进入错题集" + questionId);
+ ExamErrorQuestion examErrorQuestion = new ExamErrorQuestion();
+ BeanUtils.copyProperties(examQuestion, examErrorQuestion);
+ examErrorQuestion.setTaskId(taskId);
+ SnowflakeId idWorker = new SnowflakeId(0, 31);
+ examErrorQuestion.setId(idWorker.nextIdStr());
+ examErrorQuestion.setTenantId(systemTenant.getId());
+ examErrorQuestion.setCreator(String.valueOf(stuInfoVo.getStuId()));
+ examErrorQuestion.setCreateTime(LocalDateTime.now());
+ return examErrorQuestion;
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/example/exam/exam/utils/snowflake/SnowflakeId.java b/src/main/java/com/example/exam/exam/utils/snowflake/SnowflakeId.java
new file mode 100644
index 0000000..1564697
--- /dev/null
+++ b/src/main/java/com/example/exam/exam/utils/snowflake/SnowflakeId.java
@@ -0,0 +1,180 @@
+package com.example.exam.exam.utils.snowflake;
+
+import java.time.Instant;
+
+/**
+ * 雪花算法 ID 生成器(64bit)
+ * 结构:1bit 符号位 | 41bit 时间戳(ms) | 5bit 数据中心 | 5bit 工作节点 | 12bit 序列
+ * 默认 epoch:2025-01-01T00:00:00Z
+ */
+public class SnowflakeId {
+
+ // ---- 位宽定义 ----
+ private static final long WORKER_ID_BITS = 5L; // 0-31
+ private static final long DATACENTER_ID_BITS = 5L; // 0-31
+ private static final long SEQUENCE_BITS = 12L; // 0-4095
+
+ private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); // 31
+ private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); // 31
+ private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); // 4095
+
+ private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; // 12
+ private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; // 17
+ private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; // 22
+
+ // ---- 配置 ----
+ private final long epoch; // 自定义纪元(毫秒)
+ private final long datacenterId; // 0..31
+ private final long workerId; // 0..31
+
+ // ---- 运行时状态 ----
+ private long lastTimestamp = -1L; // 上次生成的时间戳
+ private long sequence = 0L; // 当前毫秒内的序列
+
+ /**
+ * 构造器
+ * @param datacenterId 0..31
+ * @param workerId 0..31
+ * @param epochMillis 自定义纪元(毫秒),建议固定且小于当前时间
+ */
+ public SnowflakeId(long datacenterId, long workerId, long epochMillis) {
+ if (datacenterId < 0 || datacenterId > MAX_DATACENTER_ID) {
+ throw new IllegalArgumentException("datacenterId out of range: 0.." + MAX_DATACENTER_ID);
+ }
+ if (workerId < 0 || workerId > MAX_WORKER_ID) {
+ throw new IllegalArgumentException("workerId out of range: 0.." + MAX_WORKER_ID);
+ }
+ this.datacenterId = datacenterId;
+ this.workerId = workerId;
+ this.epoch = epochMillis;
+ }
+
+ /**
+ * 使用默认纪元(2020-01-01T00:00:00Z)
+ */
+ public SnowflakeId(long datacenterId, long workerId) {
+ this(datacenterId, workerId, Instant.parse("2020-01-01T00:00:00Z").toEpochMilli());
+ }
+
+ /**
+ * 生成下一个 ID(线程安全)
+ */
+ public synchronized long nextId() {
+ long now = currentTime();
+
+ // 处理时钟回拨
+ if (now < lastTimestamp) {
+ long offset = lastTimestamp - now;
+ // 小回拨:尝试等待一个回拨差值的时长
+ if (offset <= 5) {
+ sleepMs(offset);
+ now = currentTime();
+ if (now < lastTimestamp) {
+ // 仍小于:采用“借用序列”的容错策略(把时间当作 lastTimestamp)
+ now = lastTimestamp;
+ }
+ } else {
+ // 大回拨:直接使用 lastTimestamp(保序)或抛出异常
+ // 为了高可用,这里选择保序;如需严格,可改为抛出异常
+ now = lastTimestamp;
+ }
+ }
+
+ if (now == lastTimestamp) {
+ // 同一毫秒内自增序列
+ sequence = (sequence + 1) & SEQUENCE_MASK;
+ if (sequence == 0) {
+ // 序列溢出,等待到下一毫秒
+ now = waitUntilNextMillis(lastTimestamp);
+ }
+ } else {
+ // 新毫秒,序列重置为 0(也可随机起步以降低热点)
+ sequence = 0L;
+ }
+
+ lastTimestamp = now;
+
+ long timestampPart = (now - epoch) << TIMESTAMP_SHIFT;
+ long datacenterPart = (datacenterId << DATACENTER_ID_SHIFT);
+ long workerPart = (workerId << WORKER_ID_SHIFT);
+ long sequencePart = sequence;
+
+ return timestampPart | datacenterPart | workerPart | sequencePart;
+ }
+
+ /**
+ * 生成字符串形式的 ID(十进制)
+ */
+ public String nextIdStr() {
+ return Long.toUnsignedString(nextId());
+ }
+
+ /**
+ * 解析:从 ID 中还原出时间戳(绝对毫秒)
+ */
+ public long extractTimestamp(long id) {
+ long ts = (id >>> TIMESTAMP_SHIFT) + epoch;
+ return ts;
+ }
+
+ /**
+ * 解析:数据中心 ID
+ */
+ public long extractDatacenterId(long id) {
+ return (id >>> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
+ }
+
+ /**
+ * 解析:工作节点 ID
+ */
+ public long extractWorkerId(long id) {
+ return (id >>> WORKER_ID_SHIFT) & MAX_WORKER_ID;
+ }
+
+ /**
+ * 解析:序列
+ */
+ public long extractSequence(long id) {
+ return id & SEQUENCE_MASK;
+ }
+
+ private long waitUntilNextMillis(long lastTs) {
+ long ts = currentTime();
+ while (ts <= lastTs) {
+ ts = currentTime();
+ }
+ return ts;
+ }
+
+ private long currentTime() {
+ return System.currentTimeMillis();
+ }
+
+ private void sleepMs(long ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ // ---- 简易单例 ----
+ private static class Holder {
+ // 默认:数据中心 0,工作节点 0,可按需修改
+ private static final SnowflakeId INSTANCE = new SnowflakeId(0, 0);
+ }
+
+ /** 获取全局单例(如项目只需一个生成器时使用) */
+ public static SnowflakeId getInstance() {
+ return Holder.INSTANCE;
+ }
+
+ // ---- DEMO ----
+ public static void main(String[] args) {
+ SnowflakeId gen = new SnowflakeId(1, 3); // 数据中心=1,工作节点=3
+ for (int i = 0; i < 5; i++) {
+ long id = gen.nextId();
+ System.out.println(id + " ts=" + Instant.ofEpochMilli(gen.extractTimestamp(id)));
+ }
+ }
+}