【修改】提交未提交代码

This commit is contained in:
huababa1
2025-08-26 23:28:05 +08:00
parent aaa49e7f71
commit 24ec8a3c9a
6 changed files with 422 additions and 0 deletions

View File

@@ -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;
/**
* 试题内容(带样式:<p>下列表格123</p>\n<table style=\"border-collapse: collapse; width: 100%;\" border=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 31.4907%;\">1</td>\)
*/
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<ExamQuestionAnswer> answerList;
/**
* 试题文件
*/
@TableField(exist = false)
private List<ExamQuestionFile> fileUploads;
/**
* 试题判分
*/
@TableField(exist = false)
private ExamQuestionScore questionScores;
/**
* 试题关键字
*/
@TableField(exist = false)
private List<ExamQuestionKeyword> questionKeywords;
private LocalDateTime createTime;
private String creator;
/**
* 租户id
*/
private long tenantId;
}

View File

@@ -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<ExamErrorQuestion>
{
default ExamErrorQuestion selectByIdAndUserId(String quId, String userId, String tenantId) {
LambdaQueryWrapper<ExamErrorQuestion> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ExamErrorQuestion::getQuId, quId);
wrapper.eq(ExamErrorQuestion::getCreator, userId);
wrapper.eq(ExamErrorQuestion::getTenantId, tenantId);
return selectOne(wrapper);
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,180 @@
package com.example.exam.exam.utils.snowflake;
import java.time.Instant;
/**
* 雪花算法 ID 生成器64bit
* 结构1bit 符号位 | 41bit 时间戳(ms) | 5bit 数据中心 | 5bit 工作节点 | 12bit 序列
* 默认 epoch2025-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)));
}
}
}