【修改】提交未提交代码
This commit is contained in:
126
src/main/java/com/example/exam/exam/dal/ExamErrorQuestion.java
Normal file
126
src/main/java/com/example/exam/exam/dal/ExamErrorQuestion.java
Normal 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;
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user