From 30f5a5687645cef5e55ad0de17729228d52fa6c5 Mon Sep 17 00:00:00 2001 From: xiao12feng Date: Fri, 21 Nov 2025 13:39:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AF=BC=E5=87=BA=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E5=92=8C=E7=94=A8=E6=88=B7=E6=A1=A3=E6=A1=88=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PsyAssessmentReportController.java | 31 +++ .../PsyQuestionnaireAnswerController.java | 23 +- .../PsyQuestionnaireController.java | 6 +- .../src/main/resources/application-druid.yml | 14 +- ry-xinli-system/pom.xml | 2 +- .../domain/psychology/PsyUserProfile.java | 225 +++++++++--------- .../psychology/PsyAssessmentServiceImpl.java | 26 +- .../PsyQuestionnaireAnswerServiceImpl.java | 90 +++++-- .../impl/psychology/SasReportServiceImpl.java | 12 +- .../psychology/PsyQuestionnaireMapper.xml | 26 +- .../psychology/PsyUserProfileMapper.xml | 74 +++--- .../services/report/DynamicContentService.js | 122 +++++++--- .../src/services/report/SASReportGenerator.js | 71 ++++-- xinli-ui/src/utils/request.js | 4 +- .../src/views/psychology/assessment/start.vue | 67 +++++- .../src/views/psychology/profile/index.vue | 141 ++++++++--- .../psychology/questionnaire/scoring.vue | 36 +-- .../views/psychology/questionnaire/start.vue | 19 +- .../src/views/psychology/report/index.vue | 140 ++++++++--- 19 files changed, 772 insertions(+), 357 deletions(-) diff --git a/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyAssessmentReportController.java b/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyAssessmentReportController.java index a34d6610..ceab0b99 100644 --- a/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyAssessmentReportController.java +++ b/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyAssessmentReportController.java @@ -83,6 +83,9 @@ public class PsyAssessmentReportController extends BaseController @Autowired private ISasReportService sasReportService; + @Autowired + private com.ddnai.system.service.psychology.IPsyUserProfileService profileService; + /** * 获取报告列表(包含测评报告和问卷报告) */ @@ -125,6 +128,17 @@ public class PsyAssessmentReportController extends BaseController vo.setIsGenerated(ar.getIsGenerated()); vo.setGenerateTime(ar.getGenerateTime()); vo.setCreateTime(ar.getCreateTime()); + + // 获取用户信息 + PsyAssessment assessment = assessmentService.selectAssessmentById(ar.getAssessmentId()); + if (assessment != null) { + vo.setUserId(assessment.getUserId()); + // 获取用户档案信息编号 + com.ddnai.system.domain.psychology.PsyUserProfile profile = profileService.selectProfileByUserId(assessment.getUserId()); + if (profile != null) { + vo.setInfoNumber(profile.getInfoNumber()); + } + } allReports.add(vo); } @@ -141,6 +155,17 @@ public class PsyAssessmentReportController extends BaseController vo.setIsGenerated(qr.getIsGenerated()); vo.setGenerateTime(qr.getGenerateTime()); vo.setCreateTime(qr.getCreateTime()); + + // 获取用户信息 + com.ddnai.system.domain.psychology.PsyQuestionnaireAnswer answer = questionnaireAnswerService.selectAnswerById(qr.getAnswerId()); + if (answer != null) { + vo.setUserId(answer.getUserId()); + // 获取用户档案信息编号 + com.ddnai.system.domain.psychology.PsyUserProfile profile = profileService.selectProfileByUserId(answer.getUserId()); + if (profile != null) { + vo.setInfoNumber(profile.getInfoNumber()); + } + } allReports.add(vo); System.out.println("添加问卷报告: reportId=" + qr.getReportId() + ", answerId=" + qr.getAnswerId() + ", title=" + qr.getReportTitle()); } @@ -184,6 +209,8 @@ public class PsyAssessmentReportController extends BaseController private Long reportId; private String sourceType; // "assessment" 或 "questionnaire" private Long sourceId; // assessmentId 或 answerId + private Long userId; // 用户ID + private String infoNumber; // 信息编号 private String reportTitle; private String reportType; private String reportContent; @@ -199,6 +226,10 @@ public class PsyAssessmentReportController extends BaseController public void setSourceType(String sourceType) { this.sourceType = sourceType; } public Long getSourceId() { return sourceId; } public void setSourceId(Long sourceId) { this.sourceId = sourceId; } + public Long getUserId() { return userId; } + public void setUserId(Long userId) { this.userId = userId; } + public String getInfoNumber() { return infoNumber; } + public void setInfoNumber(String infoNumber) { this.infoNumber = infoNumber; } public String getReportTitle() { return reportTitle; } public void setReportTitle(String reportTitle) { this.reportTitle = reportTitle; } public String getReportType() { return reportType; } diff --git a/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireAnswerController.java b/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireAnswerController.java index b184c7e9..887a2c62 100644 --- a/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireAnswerController.java +++ b/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireAnswerController.java @@ -61,7 +61,6 @@ public class PsyQuestionnaireAnswerController extends BaseController /** * 获取问卷答题列表 */ - @PreAuthorize("@ss.hasPermi('psychology:questionnaire:list')") @GetMapping("/list") public TableDataInfo list(PsyQuestionnaireAnswer answer) { @@ -88,9 +87,12 @@ public class PsyQuestionnaireAnswerController extends BaseController Long questionnaireId = Long.valueOf(params.get("questionnaireId").toString()); String respondentName = params.get("respondentName") != null ? params.get("respondentName").toString() : null; + // 获取被测试用户ID:如果前端传递了userId(管理员代测),则使用传递的userId;否则使用当前登录用户ID + Long userId = params.get("userId") != null ? Long.valueOf(params.get("userId").toString()) : SecurityUtils.getUserId(); + PsyQuestionnaireAnswer answer = new PsyQuestionnaireAnswer(); answer.setQuestionnaireId(questionnaireId); - answer.setUserId(SecurityUtils.getUserId()); + answer.setUserId(userId); // 使用被测试用户ID answer.setRespondentName(respondentName); answer.setStatus("0"); // 进行中 answer.setStartTime(new Date()); @@ -164,28 +166,39 @@ public class PsyQuestionnaireAnswerController extends BaseController { try { + System.out.println("开始提交问卷,answerId: " + answerId + ", 用户: " + SecurityUtils.getUsername()); + PsyQuestionnaireAnswer answer = answerService.selectAnswerById(answerId); if (answer == null) { + System.err.println("答题记录不存在,answerId: " + answerId); return error("答题记录不存在"); } if (!"0".equals(answer.getStatus())) { + System.err.println("问卷状态异常,answerId: " + answerId + ", status: " + answer.getStatus()); return error("问卷已完成或已作废"); } // 自动评分并提交 + long startTime = System.currentTimeMillis(); int result = answerService.submitAnswer(answerId); + long endTime = System.currentTimeMillis(); + + System.out.println("问卷提交完成,answerId: " + answerId + ", 耗时: " + (endTime - startTime) + "ms, 结果: " + result); if (result > 0) { return success("提交成功"); } + System.err.println("提交失败,answerId: " + answerId + ", result: " + result); return error("提交失败"); } catch (Exception e) { + System.err.println("提交问卷异常,answerId: " + answerId + ", 错误: " + e.getMessage()); + e.printStackTrace(); return error("提交失败:" + e.getMessage()); } } @@ -290,9 +303,8 @@ public class PsyQuestionnaireAnswerController extends BaseController { Long detailId = Long.valueOf(params.get("detailId").toString()); java.math.BigDecimal score = new java.math.BigDecimal(params.get("score").toString()); - String comment = params.get("comment") != null ? params.get("comment").toString() : null; - int result = answerService.submitScoring(detailId, score, comment); + int result = answerService.submitScoring(detailId, score, null); if (result > 0) { return success("评分成功"); @@ -317,9 +329,8 @@ public class PsyQuestionnaireAnswerController extends BaseController { Long detailId = Long.valueOf(params.get("detailId").toString()); java.math.BigDecimal score = new java.math.BigDecimal(params.get("score").toString()); - String comment = params.get("comment") != null ? params.get("comment").toString() : null; - int result = answerService.submitScoring(detailId, score, comment); + int result = answerService.submitScoring(detailId, score, null); if (result > 0) { successCount++; diff --git a/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java b/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java index df21cbdb..009a7802 100644 --- a/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java +++ b/ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java @@ -34,9 +34,8 @@ public class PsyQuestionnaireController extends BaseController private IPsyQuestionnaireService questionnaireService; /** - * 获取问卷列表 + * 获取问卷列表(答题用户可访问) */ - @PreAuthorize("@ss.hasPermi('psychology:questionnaire:list')") @GetMapping("/list") public TableDataInfo list(PsyQuestionnaire questionnaire) { @@ -46,9 +45,8 @@ public class PsyQuestionnaireController extends BaseController } /** - * 根据问卷ID获取详细信息 + * 根据问卷ID获取详细信息(答题用户可访问) */ - @PreAuthorize("@ss.hasPermi('psychology:questionnaire:query')") @GetMapping(value = "/{questionnaireId}") public AjaxResult getInfo(@PathVariable Long questionnaireId) { diff --git a/ry-xinli-admin/src/main/resources/application-druid.yml b/ry-xinli-admin/src/main/resources/application-druid.yml index 4cf41fe8..6f7ff468 100644 --- a/ry-xinli-admin/src/main/resources/application-druid.yml +++ b/ry-xinli-admin/src/main/resources/application-druid.yml @@ -17,17 +17,17 @@ spring: username: password: # 初始连接数 - initialSize: 5 + initialSize: 10 # 最小连接池数量 - minIdle: 10 + minIdle: 20 # 最大连接池数量 - maxActive: 20 - # 配置获取连接等待超时的时间 - maxWait: 60000 + maxActive: 100 + # 配置获取连接等待超时的时间(减少到20秒,快速失败) + maxWait: 20000 # 配置连接超时时间 - connectTimeout: 30000 + connectTimeout: 10000 # 配置网络超时时间 - socketTimeout: 60000 + socketTimeout: 30000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 diff --git a/ry-xinli-system/pom.xml b/ry-xinli-system/pom.xml index 604a1de8..50673d93 100644 --- a/ry-xinli-system/pom.xml +++ b/ry-xinli-system/pom.xml @@ -1,4 +1,4 @@ - + diff --git a/ry-xinli-system/src/main/java/com/ddnai/system/domain/psychology/PsyUserProfile.java b/ry-xinli-system/src/main/java/com/ddnai/system/domain/psychology/PsyUserProfile.java index 84734503..41a00404 100644 --- a/ry-xinli-system/src/main/java/com/ddnai/system/domain/psychology/PsyUserProfile.java +++ b/ry-xinli-system/src/main/java/com/ddnai/system/domain/psychology/PsyUserProfile.java @@ -48,28 +48,48 @@ public class PsyUserProfile extends BaseEntity @Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd") private java.util.Date birthday; - /** 学历 */ - @Excel(name = "学历") - private String education; + /** 监狱 */ + @Excel(name = "监狱") + private String prison; - /** 职业 */ - @Excel(name = "职业") - private String occupation; + /** 监区 */ + @Excel(name = "监区") + private String prisonArea; - /** 地址 */ - @Excel(name = "地址") - private String address; + /** 性别 */ + @Excel(name = "性别", readConverterExp = "0=男,1=女,2=未知") + private String gender; - /** 紧急联系人 */ - @Excel(name = "紧急联系人") - private String emergencyContact; + /** 民族 */ + @Excel(name = "民族") + private String nation; - /** 紧急联系电话 */ - @Excel(name = "紧急联系电话") - private String emergencyPhone; + /** 文化程度 */ + @Excel(name = "文化程度") + private String educationLevel; - /** 病史 */ - private String medicalHistory; + /** 罪名 */ + @Excel(name = "罪名") + private String crimeName; + + /** 刑期 */ + @Excel(name = "刑期") + private String sentenceTerm; + + /** 刑期起日 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "刑期起日", width = 30, dateFormat = "yyyy-MM-dd") + private java.util.Date sentenceStartDate; + + /** 刑期止日 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "刑期止日", width = 30, dateFormat = "yyyy-MM-dd") + private java.util.Date sentenceEndDate; + + /** 入监时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "入监时间", width = 30, dateFormat = "yyyy-MM-dd") + private java.util.Date entryDate; /** 用户状态(0正常 1停用) */ private String status; @@ -154,64 +174,104 @@ public class PsyUserProfile extends BaseEntity this.birthday = birthday; } - public String getEducation() + public String getPrison() { - return education; + return prison; } - public void setEducation(String education) + public void setPrison(String prison) { - this.education = education; + this.prison = prison; } - public String getOccupation() + public String getPrisonArea() { - return occupation; + return prisonArea; } - public void setOccupation(String occupation) + public void setPrisonArea(String prisonArea) { - this.occupation = occupation; + this.prisonArea = prisonArea; } - public String getAddress() + public String getGender() { - return address; + return gender; } - public void setAddress(String address) + public void setGender(String gender) { - this.address = address; + this.gender = gender; } - public String getEmergencyContact() + public String getNation() { - return emergencyContact; + return nation; } - public void setEmergencyContact(String emergencyContact) + public void setNation(String nation) { - this.emergencyContact = emergencyContact; + this.nation = nation; } - public String getEmergencyPhone() + public String getEducationLevel() { - return emergencyPhone; + return educationLevel; } - public void setEmergencyPhone(String emergencyPhone) + public void setEducationLevel(String educationLevel) { - this.emergencyPhone = emergencyPhone; + this.educationLevel = educationLevel; } - public String getMedicalHistory() + public String getCrimeName() { - return medicalHistory; + return crimeName; } - public void setMedicalHistory(String medicalHistory) + public void setCrimeName(String crimeName) { - this.medicalHistory = medicalHistory; + this.crimeName = crimeName; + } + + public String getSentenceTerm() + { + return sentenceTerm; + } + + public void setSentenceTerm(String sentenceTerm) + { + this.sentenceTerm = sentenceTerm; + } + + public java.util.Date getSentenceStartDate() + { + return sentenceStartDate; + } + + public void setSentenceStartDate(java.util.Date sentenceStartDate) + { + this.sentenceStartDate = sentenceStartDate; + } + + public java.util.Date getSentenceEndDate() + { + return sentenceEndDate; + } + + public void setSentenceEndDate(java.util.Date sentenceEndDate) + { + this.sentenceEndDate = sentenceEndDate; + } + + public java.util.Date getEntryDate() + { + return entryDate; + } + + public void setEntryDate(java.util.Date entryDate) + { + this.entryDate = entryDate; } public String getUserName() @@ -274,74 +334,6 @@ public class PsyUserProfile extends BaseEntity this.infoNumber = infoNumber; } - // 以下方法用于兼容现有代码,这些字段可能存储在 profileData JSON 中 - // 如果需要,可以从 profileData 中解析这些字段 - - public String getPrisonerName() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getPrisonName() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getPrisonAreaName() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getCustodyStatus() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getNation() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getEducationLevel() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getCrimeName() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getSentenceTerm() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getSentenceStartDate() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getSentenceEndDate() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } - - public String getEntryDate() - { - // 可以从 profileData JSON 中解析,暂时返回 null - return null; - } @Override public String toString() { @@ -354,11 +346,16 @@ public class PsyUserProfile extends BaseEntity .append("userName", getUserName()) .append("phone", getPhone()) .append("birthday", getBirthday()) - .append("education", getEducation()) - .append("occupation", getOccupation()) - .append("address", getAddress()) - .append("emergencyContact", getEmergencyContact()) - .append("emergencyPhone", getEmergencyPhone()) + .append("prison", getPrison()) + .append("prisonArea", getPrisonArea()) + .append("gender", getGender()) + .append("nation", getNation()) + .append("educationLevel", getEducationLevel()) + .append("crimeName", getCrimeName()) + .append("sentenceTerm", getSentenceTerm()) + .append("sentenceStartDate", getSentenceStartDate()) + .append("sentenceEndDate", getSentenceEndDate()) + .append("entryDate", getEntryDate()) .append("infoNumber", getInfoNumber()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) diff --git a/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyAssessmentServiceImpl.java b/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyAssessmentServiceImpl.java index ac3fbc5b..e9730a57 100644 --- a/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyAssessmentServiceImpl.java +++ b/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyAssessmentServiceImpl.java @@ -156,22 +156,30 @@ public class PsyAssessmentServiceImpl implements IPsyAssessmentService PsyUserProfile profile = userProfileService.selectProfileByUserId(userId); if (profile != null) { - if (StringUtils.isNotEmpty(profile.getPrisonerName())) + if (StringUtils.isNotEmpty(profile.getUserName())) { - summaryVO.setUserName(profile.getPrisonerName()); - summaryVO.setNickName(profile.getPrisonerName()); + summaryVO.setUserName(profile.getUserName()); + summaryVO.setNickName(profile.getUserName()); } summaryVO.setInfoNumber(profile.getInfoNumber()); - summaryVO.setPrisonName(profile.getPrisonName()); - summaryVO.setPrisonAreaName(profile.getPrisonAreaName()); - summaryVO.setCustodyStatus(profile.getCustodyStatus()); + summaryVO.setPrisonName(profile.getPrison()); + summaryVO.setPrisonAreaName(profile.getPrisonArea()); + // 监管状态字段已删除,可以根据需要设置默认值或删除此行 + // summaryVO.setCustodyStatus(profile.getCustodyStatus()); summaryVO.setNation(profile.getNation()); summaryVO.setEducationLevel(profile.getEducationLevel()); summaryVO.setCrimeName(profile.getCrimeName()); summaryVO.setSentenceTerm(profile.getSentenceTerm()); - summaryVO.setSentenceStartDate(profile.getSentenceStartDate()); - summaryVO.setSentenceEndDate(profile.getSentenceEndDate()); - summaryVO.setEntryDate(profile.getEntryDate()); + // 日期字段需要转换为字符串格式 + if (profile.getSentenceStartDate() != null) { + summaryVO.setSentenceStartDate(new java.text.SimpleDateFormat("yyyy-MM-dd").format(profile.getSentenceStartDate())); + } + if (profile.getSentenceEndDate() != null) { + summaryVO.setSentenceEndDate(new java.text.SimpleDateFormat("yyyy-MM-dd").format(profile.getSentenceEndDate())); + } + if (profile.getEntryDate() != null) { + summaryVO.setEntryDate(new java.text.SimpleDateFormat("yyyy-MM-dd").format(profile.getEntryDate())); + } } List assessments = assessmentMapper.selectAssessmentListByUserId(userId); diff --git a/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyQuestionnaireAnswerServiceImpl.java b/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyQuestionnaireAnswerServiceImpl.java index 939b365a..9ec5dfee 100644 --- a/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyQuestionnaireAnswerServiceImpl.java +++ b/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyQuestionnaireAnswerServiceImpl.java @@ -3,6 +3,7 @@ package com.ddnai.system.service.impl.psychology; import java.math.BigDecimal; import java.util.Date; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -217,25 +218,37 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe // 判断是否及格(需要从问卷表获取及格分数) // 这里暂时不判断,后续可以完善 - // 更新排名(需要重新计算所有答题记录的排名) - updateRank(answer.getQuestionnaireId()); - int result = answerMapper.updateAnswer(answer); - // 生成问卷报告 - if (result > 0) { + // 异步更新排名(避免阻塞提交流程) + final Long questionnaireIdForRank = answer.getQuestionnaireId(); + CompletableFuture.runAsync(() -> { try { - generateQuestionnaireReport(answerId); - System.out.println("问卷报告生成完成,answerId: " + answerId); + // 延迟50ms确保事务提交完成 + Thread.sleep(50); + updateRank(questionnaireIdForRank); + System.out.println("异步排名更新完成,questionnaireId: " + questionnaireIdForRank); } catch (Exception e) { - // 报告生成失败不影响提交,但记录详细错误 - System.err.println("生成问卷报告失败,answerId: " + answerId); - System.err.println("错误信息: " + e.getMessage()); + System.err.println("异步更新排名失败,questionnaireId: " + questionnaireIdForRank); e.printStackTrace(); - // 不抛出异常,让提交流程继续 } - } else { - System.err.println("更新答题记录失败,无法生成报告,answerId: " + answerId); + }); + + // 异步生成问卷报告(避免阻塞提交流程) + if (result > 0) { + // 使用异步任务生成报告,不阻塞当前事务 + CompletableFuture.runAsync(() -> { + try { + // 延迟100ms确保事务提交完成 + Thread.sleep(100); + generateQuestionnaireReport(answerId); + System.out.println("异步问卷报告生成完成,answerId: " + answerId); + } catch (Exception e) { + System.err.println("异步生成问卷报告失败,answerId: " + answerId); + System.err.println("错误信息: " + e.getMessage()); + e.printStackTrace(); + } + }); } return result; @@ -379,10 +392,35 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe reportContent.append(""); reportContent.append(""); + // 检查是否有未评分的主观题 + boolean hasUnscoredSubjective = false; + int unscoredCount = 0; + for (PsyQuestionnaireAnswerDetail detail : details) { + PsyQuestionnaireItem item = itemMapper.selectItemById(detail.getItemId()); + if (item != null) { + boolean isSubjective = isSubjectiveType(item.getItemType()); + String isScored = detail.getIsScored(); + + // 如果是主观题且未评分 + if (isSubjective && !"1".equals(isScored)) { + hasUnscoredSubjective = true; + unscoredCount++; + } + } + } + + System.out.println("评分检查结果 - 总题目数: " + details.size() + + ", 未评分主观题数: " + unscoredCount + + ", 是否有未评分主观题: " + hasUnscoredSubjective); + // 生成报告摘要 - String summary = String.format("本次答题共完成%d道题目,总得分%.2f分", + String summary = String.format("本次答题共完成%d道题目,总得分%.2f分%s", details.size(), - answer.getTotalScore() != null ? answer.getTotalScore().doubleValue() : 0.0); + answer.getTotalScore() != null ? answer.getTotalScore().doubleValue() : 0.0, + hasUnscoredSubjective ? "(有" + unscoredCount + "道主观题待评分)" : ""); + + // 根据是否有未评分的主观题决定报告状态 + String isGenerated = hasUnscoredSubjective ? "0" : "1"; // 保存报告到数据库 PsyQuestionnaireReport existingReport = reportMapper.selectReportByAnswerId(answerId); @@ -394,13 +432,13 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe if (existingReport != null) { // 更新现有报告 - System.out.println("更新已存在的报告,reportId: " + existingReport.getReportId()); + System.out.println("更新已存在的报告,reportId: " + existingReport.getReportId() + ", 报告状态: " + isGenerated); report = existingReport; report.setReportType("standard"); report.setReportTitle(reportTitle); report.setReportContent(reportContent.toString()); report.setSummary(summary); - report.setIsGenerated("1"); + report.setIsGenerated(isGenerated); // 根据评分状态设置 report.setGenerateTime(DateUtils.getNowDate()); report.setUpdateBy(SecurityUtils.getUsername()); report.setUpdateTime(DateUtils.getNowDate()); @@ -414,12 +452,12 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe report.setReportTitle(reportTitle); report.setReportContent(reportContent.toString()); report.setSummary(summary); - report.setIsGenerated("1"); + report.setIsGenerated(isGenerated); // 根据评分状态设置 report.setGenerateTime(DateUtils.getNowDate()); report.setCreateBy(SecurityUtils.getUsername()); report.setCreateTime(DateUtils.getNowDate()); int insertResult = reportMapper.insertReport(report); - System.out.println("创建新报告结果: " + insertResult + ", reportId: " + report.getReportId()); + System.out.println("创建新报告结果: " + insertResult + ", reportId: " + report.getReportId() + ", 报告状态: " + isGenerated); if (insertResult <= 0) { System.err.println("警告:报告插入失败,insertResult: " + insertResult); } @@ -1008,8 +1046,18 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe answer.setUpdateBy(SecurityUtils.getUsername()); answerMapper.updateAnswer(answer); - // 重新计算排名 - updateRank(answer.getQuestionnaireId()); + // 异步重新计算排名 + final Long questionnaireIdForRank = answer.getQuestionnaireId(); + CompletableFuture.runAsync(() -> { + try { + Thread.sleep(50); + updateRank(questionnaireIdForRank); + System.out.println("评分后异步排名更新完成,questionnaireId: " + questionnaireIdForRank); + } catch (Exception e) { + System.err.println("评分后异步更新排名失败,questionnaireId: " + questionnaireIdForRank); + e.printStackTrace(); + } + }); // 重新生成报告,以反映最新的评分 // 注意:由于在同一个事务中,MyBatis一级缓存可能返回旧数据 diff --git a/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/SasReportServiceImpl.java b/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/SasReportServiceImpl.java index fd702445..57c08dba 100644 --- a/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/SasReportServiceImpl.java +++ b/ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/SasReportServiceImpl.java @@ -92,7 +92,8 @@ public class SasReportServiceImpl implements ISasReportService info.setUnitName(firstNonBlank(summary.getPrisonName(), summary.getDeptName(), resolveDefaultUnitName())); info.setPrisonAreaName(summary.getPrisonAreaName()); info.setDeptName(summary.getDeptName()); - info.setCustodyStatus(summary.getCustodyStatus()); + // 监管状态字段已删除,可以根据需要设置默认值或删除此行 + // info.setCustodyStatus(summary.getCustodyStatus()); } else { @@ -181,10 +182,11 @@ public class SasReportServiceImpl implements ISasReportService } if (data.getUserInfo() != null) { - if (StringUtils.isNotBlank(data.getUserInfo().getCustodyStatus())) - { - keywords.add(data.getUserInfo().getCustodyStatus()); - } + // 监管状态字段已删除 + // if (StringUtils.isNotBlank(data.getUserInfo().getCustodyStatus())) + // { + // keywords.add(data.getUserInfo().getCustodyStatus()); + // } if (StringUtils.isNotBlank(data.getUserInfo().getPrisonAreaName())) { keywords.add(data.getUserInfo().getPrisonAreaName()); diff --git a/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyQuestionnaireMapper.xml b/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyQuestionnaireMapper.xml index ad28ee4d..36b008fb 100644 --- a/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyQuestionnaireMapper.xml +++ b/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyQuestionnaireMapper.xml @@ -25,31 +25,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select questionnaire_id, questionnaire_code, questionnaire_name, questionnaire_type, paper_type, - item_count, total_score, pass_score, estimated_time, description, status, sort_order, - create_by, create_time, update_by, update_time, remark - from psy_questionnaire + select q.questionnaire_id, q.questionnaire_code, q.questionnaire_name, q.questionnaire_type, q.paper_type, + COALESCE(COUNT(i.item_id), 0) as item_count, q.total_score, q.pass_score, q.estimated_time, q.description, q.status, q.sort_order, + q.create_by, q.create_time, q.update_by, q.update_time, q.remark + from psy_questionnaire q + left join psy_questionnaire_item i on q.questionnaire_id = i.questionnaire_id diff --git a/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyUserProfileMapper.xml b/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyUserProfileMapper.xml index 58e9a2de..bea2b240 100644 --- a/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyUserProfileMapper.xml +++ b/ry-xinli-system/src/main/resources/mapper/system/psychology/PsyUserProfileMapper.xml @@ -14,12 +14,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - - - - - + + + + + + + + + + @@ -33,8 +37,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select p.profile_id, p.user_id, p.profile_type, p.profile_data, p.avatar, p.id_card, p.birthday, - p.education, p.occupation, p.address, p.emergency_contact, p.emergency_phone, - p.medical_history, p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark, + p.prison, p.prison_area, p.gender, p.nation, p.education_level, p.crime_name, + p.sentence_term, p.sentence_start_date, p.sentence_end_date, p.entry_date, + p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark, u.user_name, u.phonenumber as phone, u.nick_name, u.email, u.sex, u.status, u.dept_id, u.create_time as user_create_time @@ -64,8 +69,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar as user_avatar, u.phonenumber as phone, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by as user_create_by, u.create_time as user_create_time, u.remark as user_remark, d.dept_name, d.leader, p.profile_id, p.profile_type, p.profile_data, p.avatar, p.id_card, p.birthday, - p.education, p.occupation, p.address, p.emergency_contact, p.emergency_phone, - p.medical_history, p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark + p.prison, p.prison_area, p.gender, p.nation, p.education_level, p.crime_name, + p.sentence_term, p.sentence_start_date, p.sentence_end_date, p.entry_date, + p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark from sys_user u left join sys_dept d on u.dept_id = d.dept_id left join psy_user_profile p on u.user_id = p.user_id @@ -113,12 +119,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" avatar, id_card, birthday, - education, - occupation, - address, - emergency_contact, - emergency_phone, - medical_history, + prison, + prison_area, + gender, + nation, + education_level, + crime_name, + sentence_term, + sentence_start_date, + sentence_end_date, + entry_date, info_number, create_by, remark, @@ -130,12 +140,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{avatar}, #{idCard}, #{birthday}, - #{education}, - #{occupation}, - #{address}, - #{emergencyContact}, - #{emergencyPhone}, - #{medicalHistory}, + #{prison}, + #{prisonArea}, + #{gender}, + #{nation}, + #{educationLevel}, + #{crimeName}, + #{sentenceTerm}, + #{sentenceStartDate}, + #{sentenceEndDate}, + #{entryDate}, #{infoNumber}, #{createBy}, #{remark}, @@ -151,12 +165,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" avatar = #{avatar}, id_card = #{idCard}, birthday = #{birthday}, - education = #{education}, - occupation = #{occupation}, - address = #{address}, - emergency_contact = #{emergencyContact}, - emergency_phone = #{emergencyPhone}, - medical_history = #{medicalHistory}, + prison = #{prison}, + prison_area = #{prisonArea}, + gender = #{gender}, + nation = #{nation}, + education_level = #{educationLevel}, + crime_name = #{crimeName}, + sentence_term = #{sentenceTerm}, + sentence_start_date = #{sentenceStartDate}, + sentence_end_date = #{sentenceEndDate}, + entry_date = #{entryDate}, info_number = #{infoNumber}, update_by = #{updateBy}, remark = #{remark}, diff --git a/xinli-ui/src/services/report/DynamicContentService.js b/xinli-ui/src/services/report/DynamicContentService.js index f3796198..0b968491 100644 --- a/xinli-ui/src/services/report/DynamicContentService.js +++ b/xinli-ui/src/services/report/DynamicContentService.js @@ -45,39 +45,97 @@ function buildKeywordAnalysis(reportData, severityMeta) { } export function buildChartOption(reportData) { - const chartData = reportData.chartData || {} - const score = safeScore(reportData.calculatedResults?.rawScore) - const xAxis = chartData.xAxis || [reportData.testRecord?.projectDate || '当前'] - const seriesData = chartData.series || [{ name: '总分', value: reportData.calculatedResults?.rawScore || 0 }] - return { - title: { - text: chartData.title || 'SAS总分趋势', - left: 'center' - }, - tooltip: { trigger: 'axis' }, - xAxis: { - type: 'category', - data: xAxis - }, - yAxis: { type: 'value' }, - series: [ - { - name: '总分', - type: 'line', - smooth: true, - areaStyle: { opacity: 0.15 }, - data: seriesData.map(item => item.value) - } - ], - graphic: { - type: 'text', - left: 'center', - top: '10%', - style: { - text: `当前得分:${score}`, - fontSize: 14 - } + try { + const chartData = reportData.chartData || {} + const score = safeScore(reportData.calculatedResults?.rawScore) + const rawScoreValue = reportData.calculatedResults?.rawScore || 0 + + // 构建X轴数据 + let xAxis = [] + if (chartData.xAxis && Array.isArray(chartData.xAxis) && chartData.xAxis.length > 0) { + xAxis = chartData.xAxis + } else if (reportData.testRecord?.projectDate) { + xAxis = [formatDateLabel(reportData.testRecord.projectDate)] + } else { + xAxis = ['当前'] } + + // 构建系列数据 + let seriesDataValues = [] + if (chartData.series && Array.isArray(chartData.series) && chartData.series.length > 0) { + seriesDataValues = chartData.series.map(item => { + const val = item.value !== undefined ? item.value : rawScoreValue + return typeof val === 'number' ? val : parseFloat(val) || 0 + }) + } else { + seriesDataValues = [typeof rawScoreValue === 'number' ? rawScoreValue : parseFloat(rawScoreValue) || 0] + } + + return { + title: { + text: chartData.title || '测评分数趋势', + left: 'center', + top: 10, + textStyle: { + fontSize: 16, + fontWeight: 'normal' + } + }, + tooltip: { + trigger: 'axis', + formatter: '{b}: {c}分' + }, + grid: { + left: '10%', + right: '10%', + bottom: '15%', + top: '20%', + containLabel: true + }, + xAxis: { + type: 'category', + data: xAxis, + axisLine: { + lineStyle: { color: '#999' } + } + }, + yAxis: { + type: 'value', + name: '分数', + axisLine: { + lineStyle: { color: '#999' } + } + }, + series: [ + { + name: '总分', + type: 'line', + smooth: true, + symbol: 'circle', + symbolSize: 8, + itemStyle: { + color: '#409EFF' + }, + lineStyle: { + width: 2, + color: '#409EFF' + }, + areaStyle: { + opacity: 0.2, + color: '#409EFF' + }, + data: seriesDataValues, + label: { + show: true, + position: 'top', + formatter: '{c}分' + } + } + ] + } + } catch (error) { + console.error('构建图表配置失败:', error) + throw new Error('图表配置构建失败: ' + error.message) } } diff --git a/xinli-ui/src/services/report/SASReportGenerator.js b/xinli-ui/src/services/report/SASReportGenerator.js index bc626b46..3119097b 100644 --- a/xinli-ui/src/services/report/SASReportGenerator.js +++ b/xinli-ui/src/services/report/SASReportGenerator.js @@ -3,10 +3,8 @@ import { buildChartOption, formatDateLabel } from './DynamicContentService' class SASReportGenerator { async buildHtml(reportData, includeChart = true) { + // 不再使用图表功能 let chartImage = '' - if (includeChart) { - chartImage = await this.renderChartImage(buildChartOption(reportData)) - } const recommendations = (reportData.calculatedResults.recommendations || []).map(item => `
  • ${item}
  • `).join('') const keywords = (reportData.keywordAnalysis || reportData.keywords || []).map(item => `${item}`).join('') const printScopes = (reportData.printScopes || []).map(scope => ` @@ -43,7 +41,7 @@ class SASReportGenerator {
    基本信息
    - + @@ -58,7 +56,6 @@ class SASReportGenerator {
    编码${reportData.userInfo.infoNumber || reportData.testRecord.assessmentId || '-'}样本${reportData.userInfo.name || '-'}
    姓名${reportData.userInfo.name || '-'}
    测试单位${reportData.userInfo.unitName || '-'} 性别${reportData.userInfo.gender || '-'}
    所属科室/监区${reportData.userInfo.prisonAreaName || reportData.userInfo.deptName || '-'}
    原始分数${reportData.calculatedResults.rawScore}
    功能评级${reportData.calculatedResults.functionalRating}
    - ${chartImage ? `
    chart
    ` : ''}
    @@ -106,18 +103,58 @@ class SASReportGenerator { } async renderChartImage(option) { - const container = document.createElement('div') - container.style.width = '640px' - container.style.height = '320px' - container.style.position = 'fixed' - container.style.left = '-9999px' - document.body.appendChild(container) - const chart = echarts.init(container) - chart.setOption(option) - const dataUrl = chart.getDataURL({ pixelRatio: 2, backgroundColor: '#fff' }) - chart.dispose() - document.body.removeChild(container) - return dataUrl + let container = null + let chart = null + try { + console.log('开始渲染图表,配置:', option) + + // 创建容器 + container = document.createElement('div') + container.style.width = '640px' + container.style.height = '320px' + container.style.position = 'fixed' + container.style.left = '-9999px' + container.style.top = '0' + document.body.appendChild(container) + + // 初始化echarts + chart = echarts.init(container) + if (!chart) { + throw new Error('echarts初始化失败') + } + + // 设置图表选项 + chart.setOption(option) + + // 等待渲染完成 + await new Promise(resolve => setTimeout(resolve, 100)) + + // 获取图表图像 + const dataUrl = chart.getDataURL({ + type: 'png', + pixelRatio: 2, + backgroundColor: '#fff' + }) + + console.log('图表渲染成功,图像长度:', dataUrl?.length || 0) + return dataUrl + + } catch (error) { + console.error('图表渲染失败:', error) + throw error + } finally { + // 清理资源 + try { + if (chart) { + chart.dispose() + } + if (container && container.parentNode) { + document.body.removeChild(container) + } + } catch (cleanupError) { + console.warn('清理图表资源时出错:', cleanupError) + } + } } resolveReportTitle(reportData) { diff --git a/xinli-ui/src/utils/request.js b/xinli-ui/src/utils/request.js index 827aa207..11908135 100644 --- a/xinli-ui/src/utils/request.js +++ b/xinli-ui/src/utils/request.js @@ -16,8 +16,8 @@ axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: process.env.VUE_APP_BASE_API, - // 超时 - timeout: 10000 + // 超时(增加到30秒以支持连续测评操作) + timeout: 30000 }) // request拦截器 diff --git a/xinli-ui/src/views/psychology/assessment/start.vue b/xinli-ui/src/views/psychology/assessment/start.vue index 06e2cd44..c171b3fd 100644 --- a/xinli-ui/src/views/psychology/assessment/start.vue +++ b/xinli-ui/src/views/psychology/assessment/start.vue @@ -95,6 +95,7 @@ export default { scaleList: [], profileList: [], pausedList: [], + targetUserId: null, // 目标用户ID(从URL参数获取) form: { scaleId: undefined, profileId: undefined @@ -110,11 +111,23 @@ export default { }; }, created() { - // 检查URL参数中是否有scaleId + // 检查URL参数中是否有scaleId和profileId/userId const scaleId = this.$route.query.scaleId; + const profileId = this.$route.query.profileId; + const userId = this.$route.query.userId; + if (scaleId) { this.form.scaleId = parseInt(scaleId); } + if (profileId) { + this.form.profileId = parseInt(profileId); + } + + // 保存userId(如果有)用于后续跳转 + if (userId) { + this.targetUserId = parseInt(userId); + } + this.loadScales(); this.loadProfiles(); this.loadPaused(); @@ -140,9 +153,17 @@ export default { if (selectedScale && selectedScale.sourceType === 'questionnaire') { // 如果是问卷,直接跳转到问卷开始页面 const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId); + const queryParams = { questionnaireId: questionnaireId }; + + // 检查URL中是否有userId参数(管理员代测) + const urlUserId = this.$route.query.userId; + if (urlUserId) { + queryParams.userId = urlUserId; + } + this.$router.replace({ path: '/psychology/questionnaire/start', - query: { questionnaireId: questionnaireId } + query: queryParams }); } } @@ -164,9 +185,17 @@ export default { if (selectedScale && selectedScale.sourceType === 'questionnaire') { // 如果是问卷,直接跳转到问卷开始页面 const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId); + const queryParams = { questionnaireId: questionnaireId }; + + // 检查URL中是否有userId参数(管理员代测) + const urlUserId = this.$route.query.userId; + if (urlUserId) { + queryParams.userId = urlUserId; + } + this.$router.replace({ path: '/psychology/questionnaire/start', - query: { questionnaireId: questionnaireId } + query: queryParams }); } } @@ -197,9 +226,17 @@ export default { if (selectedScale && selectedScale.sourceType === 'questionnaire') { // 如果是问卷,直接跳转到问卷开始页面 const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId); + const queryParams = { questionnaireId: questionnaireId }; + + // 检查URL中是否有userId参数(管理员代测) + const urlUserId = this.$route.query.userId; + if (urlUserId) { + queryParams.userId = urlUserId; + } + this.$router.replace({ path: '/psychology/questionnaire/start', - query: { questionnaireId: questionnaireId } + query: queryParams }); } } @@ -269,13 +306,29 @@ export default { return; } - // 如果是问卷,直接跳转到问卷答题页面(不需要用户档案) + // 如果是问卷,直接跳转到问卷答题页面 if (selectedScale.sourceType === 'questionnaire') { const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId); - // 问卷不需要用户档案,直接跳转到问卷开始页面 + + // 构建查询参数 + const queryParams = { questionnaireId: questionnaireId }; + + // 优先使用targetUserId(从URL获取),其次使用选择的用户档案 + if (this.targetUserId) { + queryParams.userId = this.targetUserId; + console.log('使用URL参数的userId:', this.targetUserId); + } else if (this.form.profileId) { + const selectedProfile = this.profileList.find(p => p.profileId === this.form.profileId); + if (selectedProfile && selectedProfile.userId) { + queryParams.userId = selectedProfile.userId; + console.log('管理员代测,传递userId:', selectedProfile.userId); + } + } + + // 跳转到问卷开始页面 this.$router.push({ path: '/psychology/questionnaire/start', - query: { questionnaireId: questionnaireId } + query: queryParams }); return; } diff --git a/xinli-ui/src/views/psychology/profile/index.vue b/xinli-ui/src/views/psychology/profile/index.vue index 75a68d9b..3e22f3ea 100644 --- a/xinli-ui/src/views/psychology/profile/index.vue +++ b/xinli-ui/src/views/psychology/profile/index.vue @@ -122,10 +122,25 @@ {{ parseTime(scope.row.birthday, '{y}-{m}-{d}') }} - - - - + + + + + + + + + + + + - - -