|
@@ -63,6 +63,9 @@ public class StudentPaperServiceImpl implements StudentPaperService {
|
|
|
private static final TypeReference<List<StudentAnswerBean>> STUDENT_ANSWER_LIST_TYPE_REFERENCE =
|
|
|
new TypeReference<List<StudentAnswerBean>>() {};
|
|
|
|
|
|
+ private final ThreadLocal<PaperBean> threadLocalPaper = new ThreadLocal<>();
|
|
|
+
|
|
|
+
|
|
|
@Autowired
|
|
|
public StudentPaperServiceImpl(RestTemplate restTemplate, PdfConfig pdfConfig, ThreadPoolTaskExecutor taskExecutor) {
|
|
|
this.httpClient = new HttpClientUtil(restTemplate);
|
|
@@ -141,16 +144,16 @@ public class StudentPaperServiceImpl implements StudentPaperService {
|
|
|
|
|
|
private void processStudent(StudentBean studentBean, Long examId) {
|
|
|
String subjectCode = studentBean.getSubjectCode();
|
|
|
- PaperBean paperBean;
|
|
|
+ PaperBean cachedPaperBean;
|
|
|
|
|
|
- // 从缓存中获取试卷结构
|
|
|
- if ((paperBean = paperCache.getIfPresent(subjectCode)) == null) {
|
|
|
+ // 从缓存中获取原始试卷模板
|
|
|
+ if ((cachedPaperBean = paperCache.getIfPresent(subjectCode)) == null) {
|
|
|
synchronized (this) {
|
|
|
- if ((paperBean = paperCache.getIfPresent(subjectCode)) == null) {
|
|
|
+ if ((cachedPaperBean = paperCache.getIfPresent(subjectCode)) == null) {
|
|
|
try {
|
|
|
- paperBean = fetchPaperStructure(examId, subjectCode);
|
|
|
- if (paperBean != null) {
|
|
|
- paperCache.put(subjectCode, paperBean);
|
|
|
+ cachedPaperBean = fetchPaperStructure(examId, subjectCode);
|
|
|
+ if (cachedPaperBean != null) {
|
|
|
+ paperCache.put(subjectCode, cachedPaperBean);
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
log.error("从远程获取试卷结构失败, examId:{}, subjectCode:{}", examId, subjectCode, e);
|
|
@@ -159,11 +162,28 @@ public class StudentPaperServiceImpl implements StudentPaperService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (paperBean == null) {
|
|
|
+ if (cachedPaperBean == null) {
|
|
|
log.warn("找不到该科目对应的试卷, examId:{}, subjectCode:{}", examId, subjectCode);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 从 ThreadLocal 获取当前线程的 paperBean 副本
|
|
|
+ PaperBean paperBean = threadLocalPaper.get();
|
|
|
+ if (paperBean == null) {
|
|
|
+ try {
|
|
|
+ // 深拷贝原始试卷结构到当前线程私有对象
|
|
|
+ paperBean = deepCopy(cachedPaperBean);
|
|
|
+ threadLocalPaper.set(paperBean);
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("深拷贝试卷结构失败", e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充考生信息(不会影响其他线程)
|
|
|
+ paperBean.setStudentName(studentBean.getName());
|
|
|
+ paperBean.setStudentCode(studentBean.getStudentCode());
|
|
|
+
|
|
|
//试卷的大题集合
|
|
|
List<DetailBean> details = paperBean.getDetails();
|
|
|
if (CollectionUtils.isEmpty(details)) {
|
|
@@ -189,8 +209,16 @@ public class StudentPaperServiceImpl implements StudentPaperService {
|
|
|
|
|
|
// 将填充后的试卷写到文件中
|
|
|
outputToFile(studentBean, paperBean, examId);
|
|
|
+
|
|
|
+ // 清理 ThreadLocal 避免内存泄漏
|
|
|
+ threadLocalPaper.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ private PaperBean deepCopy(PaperBean original) throws IOException {
|
|
|
+ return objectMapper.readValue(objectMapper.writeValueAsBytes(original), PaperBean.class);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
private void outputToFile(StudentBean studentBean, PaperBean paperBean, Long examId) {
|
|
|
try {
|
|
|
String json = objectMapper.writeValueAsString(paperBean);
|