优化问卷回答界面及功能
This commit is contained in:
1 parent
1ab9f9c458
commit
ed5eb9ca52
13 files changed
+6122
-86
No files matched your search
+47
-1
@@ -19,8 +19,10 @@ import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.zhxy.domain.vo.HqSatSurveyVo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatSurveyBo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatAnswerSubmitBo;
|
||||
import org.dromara.zhxy.service.IHqSatSurveyService;
|
||||
import org.dromara.zhxy.domain.vo.HqSatSurveyQuestionVo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatRecordVo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
@@ -130,7 +132,7 @@ public class HqSatSurveyController extends BaseController {
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
*/
|
||||
@SaCheckPermission("hq:satSurvey:question")
|
||||
// @SaCheckPermission("hq:satSurvey:question")
|
||||
@GetMapping("/{surveyId}/questions")
|
||||
public R<List<HqSatSurveyQuestionVo>> getSurveyQuestions(
|
||||
@NotNull(message = "问卷ID不能为空") @PathVariable Long surveyId) {
|
||||
@@ -177,4 +179,48 @@ public class HqSatSurveyController extends BaseController {
|
||||
public R<Void> unpublishSurvey(@NotNull(message = "问卷ID不能为空") @PathVariable Long id) {
|
||||
return toAjax(hqSatSurveyService.unpublishSurvey(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交问卷答案
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
*/
|
||||
@Log(title = "提交问卷答案", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/answer/submit")
|
||||
public R<HqSatRecordVo> submitSurveyAnswer(@Validated @RequestBody HqSatAnswerSubmitBo submitBo) {
|
||||
return R.ok(hqSatSurveyService.submitSurveyAnswer(submitBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存问卷答案草稿
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
*/
|
||||
@Log(title = "保存问卷答案草稿", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/answer/draft")
|
||||
public R<HqSatRecordVo> saveSurveyDraft(@Validated @RequestBody HqSatAnswerSubmitBo submitBo) {
|
||||
return R.ok(hqSatSurveyService.saveSurveyDraft(submitBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已答过问卷
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
*/
|
||||
@GetMapping("/{surveyId}/answered")
|
||||
public R<Boolean> checkUserAnswered(@NotNull(message = "问卷ID不能为空") @PathVariable Long surveyId) {
|
||||
return R.ok(hqSatSurveyService.checkUserAnswered(surveyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户问卷答题记录
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
*/
|
||||
@GetMapping("/{surveyId}/answer")
|
||||
public R<HqSatRecordVo> getUserSurveyAnswer(@NotNull(message = "问卷ID不能为空") @PathVariable Long surveyId) {
|
||||
return R.ok(hqSatSurveyService.getUserSurveyAnswer(surveyId));
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package org.dromara.zhxy.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 答题提交BO
|
||||
*
|
||||
* @author wuzilong
|
||||
* @date 2025-01-26
|
||||
*/
|
||||
@Data
|
||||
public class HqSatAnswerSubmitBo {
|
||||
|
||||
/**
|
||||
* 问卷ID
|
||||
*/
|
||||
@NotNull(message = "问卷ID不能为空")
|
||||
private Long surveyId;
|
||||
|
||||
/**
|
||||
* 答题开始时间
|
||||
*/
|
||||
@NotNull(message = "答题开始时间不能为空")
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 答题结束时间
|
||||
*/
|
||||
@NotNull(message = "答题结束时间不能为空")
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 是否匿名(1是 0否)
|
||||
*/
|
||||
@NotNull(message = "是否匿名不能为空")
|
||||
private Integer isAnonymous;
|
||||
|
||||
/**
|
||||
* 答案列表
|
||||
*/
|
||||
@NotEmpty(message = "答案列表不能为空")
|
||||
private List<AnswerItem> answers;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 单个答案项
|
||||
*/
|
||||
@Data
|
||||
public static class AnswerItem {
|
||||
/**
|
||||
* 题目ID
|
||||
*/
|
||||
@NotNull(message = "题目ID不能为空")
|
||||
private Long questionId;
|
||||
|
||||
/**
|
||||
* 题目类型(1单选题 2多选题 3问答题)
|
||||
*/
|
||||
@NotNull(message = "题目类型不能为空")
|
||||
private String questionType;
|
||||
|
||||
/**
|
||||
* 答案值
|
||||
*/
|
||||
@NotNull(message = "答案值不能为空")
|
||||
private String answerValue;
|
||||
|
||||
/**
|
||||
* 选项详情(单选题和多选题需要)
|
||||
*/
|
||||
private List<OptionDetail> options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项详情
|
||||
*/
|
||||
@Data
|
||||
public static class OptionDetail {
|
||||
/**
|
||||
* 选项ID
|
||||
*/
|
||||
private Long optionId;
|
||||
|
||||
/**
|
||||
* 选项值
|
||||
*/
|
||||
private String optionValue;
|
||||
|
||||
/**
|
||||
* 选项文本
|
||||
*/
|
||||
private String optionText;
|
||||
|
||||
/**
|
||||
* 是否选中
|
||||
*/
|
||||
private Boolean isSelected;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@ package org.dromara.zhxy.service;
|
||||
|
||||
import org.dromara.zhxy.domain.vo.HqSatSurveyVo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatSurveyBo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatAnswerSubmitBo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatSurveyQuestionVo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatRecordVo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
@@ -99,4 +101,36 @@ public interface IHqSatSurveyService {
|
||||
* @return 是否撤销成功
|
||||
*/
|
||||
Boolean unpublishSurvey(Long id);
|
||||
|
||||
/**
|
||||
* 提交问卷答案
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
* @return 答题记录
|
||||
*/
|
||||
HqSatRecordVo submitSurveyAnswer(HqSatAnswerSubmitBo submitBo);
|
||||
|
||||
/**
|
||||
* 保存问卷答案草稿
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
* @return 答题记录
|
||||
*/
|
||||
HqSatRecordVo saveSurveyDraft(HqSatAnswerSubmitBo submitBo);
|
||||
|
||||
/**
|
||||
* 检查用户是否已答过问卷
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
* @return 是否已答过
|
||||
*/
|
||||
Boolean checkUserAnswered(Long surveyId);
|
||||
|
||||
/**
|
||||
* 获取用户问卷答题记录
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
* @return 答题记录
|
||||
*/
|
||||
HqSatRecordVo getUserSurveyAnswer(Long surveyId);
|
||||
}
|
||||
+664
@@ -12,17 +12,33 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.zhxy.domain.bo.HqSatSurveyBo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatAnswerSubmitBo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatRecordBo;
|
||||
import org.dromara.zhxy.domain.bo.HqSatDetailBo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatSurveyVo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatRecordVo;
|
||||
import org.dromara.zhxy.domain.HqSatSurvey;
|
||||
import org.dromara.zhxy.mapper.HqSatSurveyMapper;
|
||||
import org.dromara.zhxy.service.IHqSatSurveyService;
|
||||
import org.dromara.zhxy.service.IHqSatSurveyQuestionService;
|
||||
import org.dromara.zhxy.service.IHqSatRecordService;
|
||||
import org.dromara.zhxy.service.IHqSatDetailService;
|
||||
import org.dromara.zhxy.domain.bo.HqSatSurveyQuestionBo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatSurveyQuestionVo;
|
||||
import org.dromara.zhxy.domain.vo.HqSatDetailVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* 问卷Service业务层处理
|
||||
@@ -37,6 +53,11 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
|
||||
|
||||
private final HqSatSurveyMapper baseMapper;
|
||||
private final IHqSatSurveyQuestionService surveyQuestionService;
|
||||
private final IHqSatRecordService recordService;
|
||||
private final IHqSatDetailService detailService;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 查询问卷
|
||||
@@ -271,4 +292,647 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交问卷答案
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
* @return 答题记录
|
||||
*/
|
||||
@Override
|
||||
public HqSatRecordVo submitSurveyAnswer(HqSatAnswerSubmitBo submitBo) {
|
||||
log.info("=== 收到前端答题提交请求 ===");
|
||||
log.info("问卷ID: {}, 答案数量: {}", submitBo.getSurveyId(),
|
||||
submitBo.getAnswers() != null ? submitBo.getAnswers().size() : 0);
|
||||
|
||||
if (submitBo.getAnswers() != null) {
|
||||
for (int i = 0; i < submitBo.getAnswers().size(); i++) {
|
||||
HqSatAnswerSubmitBo.AnswerItem answer = submitBo.getAnswers().get(i);
|
||||
log.info("答案{}: 题目ID={}, 题目类型={}, 答案值={}, 选项数量={}",
|
||||
i + 1, answer.getQuestionId(), answer.getQuestionType(), answer.getAnswerValue(),
|
||||
answer.getOptions() != null ? answer.getOptions().size() : 0);
|
||||
|
||||
if (answer.getOptions() != null) {
|
||||
for (int j = 0; j < answer.getOptions().size(); j++) {
|
||||
HqSatAnswerSubmitBo.OptionDetail option = answer.getOptions().get(j);
|
||||
log.info(" 选项{}: ID={}, 值={}, 文本={}, 选中={}",
|
||||
j + 1, option.getOptionId(), option.getOptionValue(),
|
||||
option.getOptionText(), option.getIsSelected());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveAnswerInternal(submitBo, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存问卷答案草稿
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
* @return 答题记录
|
||||
*/
|
||||
@Override
|
||||
public HqSatRecordVo saveSurveyDraft(HqSatAnswerSubmitBo submitBo) {
|
||||
return saveAnswerInternal(submitBo, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部方法:保存答题数据
|
||||
*
|
||||
* @param submitBo 答题提交数据
|
||||
* @param isDraft 是否为草稿
|
||||
* @return 答题记录
|
||||
*/
|
||||
private HqSatRecordVo saveAnswerInternal(HqSatAnswerSubmitBo submitBo, boolean isDraft) {
|
||||
// 验证问卷是否存在且可以答题
|
||||
HqSatSurveyVo survey = queryById(submitBo.getSurveyId());
|
||||
if (survey == null) {
|
||||
throw new ServiceException("问卷不存在");
|
||||
}
|
||||
|
||||
// 检查问卷状态
|
||||
if (!isDraft && !"1".equals(survey.getStatus())) {
|
||||
throw new ServiceException("问卷未在进行中状态,无法提交答案");
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
Long userId = LoginHelper.getUserId();
|
||||
String userTypeString = LoginHelper.getLoginUser().getUserType();
|
||||
|
||||
// 将角色字符串转换为数字编码
|
||||
String userType = convertUserTypeToCode(userTypeString);
|
||||
|
||||
// 检查用户提交次数是否超过限制(非草稿模式下)
|
||||
if (!isDraft) {
|
||||
int submitCount = getUserSubmitCount(submitBo.getSurveyId(), userId);
|
||||
int maxSubmitTimes = survey.getMaxSubmitTimes() != null ? survey.getMaxSubmitTimes().intValue() : 1;
|
||||
|
||||
log.info("用户提交次数检查: 用户ID={}, 问卷ID={}, 已提交次数={}, 最大允许次数={}",
|
||||
userId, submitBo.getSurveyId(), submitCount, maxSubmitTimes);
|
||||
|
||||
// maxSubmitTimes为0表示不限制提交次数
|
||||
if (maxSubmitTimes > 0 && submitCount >= maxSubmitTimes) {
|
||||
throw new ServiceException("您已达到该问卷的最大提交次数限制(" + maxSubmitTimes + "次)");
|
||||
}
|
||||
}
|
||||
|
||||
// 在保存新答题数据之前,将用户之前的答题记录标记为已删除
|
||||
markPreviousAnswersAsDeleted(submitBo.getSurveyId(), userId);
|
||||
|
||||
// 创建答题记录
|
||||
HqSatRecordBo recordBo = new HqSatRecordBo();
|
||||
recordBo.setSurveyId(submitBo.getSurveyId());
|
||||
recordBo.setUserId(userId);
|
||||
recordBo.setUserType(userType);
|
||||
recordBo.setSubmitTime(new Date());
|
||||
recordBo.setTotalScore(0L); // 暂时设为0,后续可以计算
|
||||
recordBo.setAvgScore(0L);
|
||||
recordBo.setRemark(submitBo.getRemark());
|
||||
|
||||
// 保存答题记录
|
||||
boolean recordSaved = recordService.insertByBo(recordBo);
|
||||
if (!recordSaved) {
|
||||
throw new ServiceException("保存答题记录失败");
|
||||
}
|
||||
|
||||
// 保存答题详情
|
||||
for (HqSatAnswerSubmitBo.AnswerItem answerItem : submitBo.getAnswers()) {
|
||||
HqSatDetailBo detailBo = new HqSatDetailBo();
|
||||
detailBo.setRecordId(recordBo.getId());
|
||||
detailBo.setQuestionId(answerItem.getQuestionId());
|
||||
detailBo.setAnswerScore(0L); // 暂时设为0,后续可以计算分数
|
||||
|
||||
// 将答案内容临时存储在remark字段中
|
||||
String answerContent = buildAnswerContent(answerItem);
|
||||
detailBo.setRemark(answerContent);
|
||||
|
||||
boolean detailSaved = detailService.insertByBo(detailBo);
|
||||
if (!detailSaved) {
|
||||
log.error("保存答题详情失败,题目ID: {}", answerItem.getQuestionId());
|
||||
}
|
||||
|
||||
// 保存具体的答案信息到 hq_sat_answer 表
|
||||
saveAnswerDetails(recordBo.getId(), answerItem);
|
||||
}
|
||||
|
||||
// 返回答题记录
|
||||
return recordService.queryById(recordBo.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户角色字符串转换为数字编码
|
||||
*
|
||||
* @param userTypeString 用户角色字符串
|
||||
* @return 数字编码字符串
|
||||
*/
|
||||
private String convertUserTypeToCode(String userTypeString) {
|
||||
if (userTypeString == null) {
|
||||
return "1"; // 默认为学生
|
||||
}
|
||||
|
||||
// 转换为小写进行比较
|
||||
String lowerCaseType = userTypeString.toLowerCase();
|
||||
|
||||
// 学生角色判断
|
||||
if (lowerCaseType.contains("student") || lowerCaseType.contains("学生") || lowerCaseType.contains("stu")) {
|
||||
return "1";
|
||||
}
|
||||
|
||||
// 教师角色判断
|
||||
if (lowerCaseType.contains("teacher") || lowerCaseType.contains("老师") ||
|
||||
lowerCaseType.contains("教师") || lowerCaseType.contains("教职工") || lowerCaseType.contains("tea")) {
|
||||
return "2";
|
||||
}
|
||||
|
||||
// 管理员角色判断
|
||||
if (lowerCaseType.contains("admin") || lowerCaseType.contains("管理员") ||
|
||||
lowerCaseType.contains("超级管理员") || lowerCaseType.contains("administrator")) {
|
||||
return "3";
|
||||
}
|
||||
|
||||
// 默认返回学生
|
||||
log.warn("未识别的用户角色类型: {}, 默认设置为学生角色", userTypeString);
|
||||
return "1";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建答案内容字符串
|
||||
*
|
||||
* @param answerItem 答案项
|
||||
* @return 答案内容字符串
|
||||
*/
|
||||
private String buildAnswerContent(HqSatAnswerSubmitBo.AnswerItem answerItem) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
content.append("题目类型:").append(answerItem.getQuestionType()).append(";");
|
||||
content.append("答案值:").append(answerItem.getAnswerValue()).append(";");
|
||||
|
||||
if (answerItem.getOptions() != null && !answerItem.getOptions().isEmpty()) {
|
||||
content.append("选项详情:");
|
||||
for (HqSatAnswerSubmitBo.OptionDetail option : answerItem.getOptions()) {
|
||||
if (Boolean.TRUE.equals(option.getIsSelected())) {
|
||||
content.append(option.getOptionValue()).append("(")
|
||||
.append(option.getOptionText()).append("),");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户之前的答题记录标记为已删除
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
private void markPreviousAnswersAsDeleted(Long surveyId, Long userId) {
|
||||
try {
|
||||
log.info("=== 开始标记用户之前的答题记录为已删除 ===");
|
||||
log.info("问卷ID: {}, 用户ID: {}", surveyId, userId);
|
||||
|
||||
// 1. 查找该用户对该问卷的所有历史答题记录ID
|
||||
String findRecordsSql = """
|
||||
SELECT id FROM hq_sat_record
|
||||
WHERE survey_id = ? AND user_id = ? AND deleted = 0
|
||||
""";
|
||||
|
||||
List<Map<String, Object>> recordResults = jdbcTemplate.queryForList(findRecordsSql, surveyId, userId);
|
||||
|
||||
if (recordResults.isEmpty()) {
|
||||
log.info("用户没有历史答题记录,无需标记删除");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("找到 {} 条历史答题记录", recordResults.size());
|
||||
|
||||
// 2. 标记答题记录表为已删除
|
||||
String updateRecordSql = """
|
||||
UPDATE hq_sat_record
|
||||
SET deleted = 1, update_by = ?, update_time = NOW()
|
||||
WHERE survey_id = ? AND user_id = ? AND deleted = 0
|
||||
""";
|
||||
|
||||
int recordUpdated = jdbcTemplate.update(updateRecordSql,
|
||||
LoginHelper.getUserId(), surveyId, userId);
|
||||
log.info("标记答题记录为已删除,影响行数: {}", recordUpdated);
|
||||
|
||||
// 3. 标记答题详情表为已删除(通过record_id关联)
|
||||
for (Map<String, Object> record : recordResults) {
|
||||
Long recordId = Long.valueOf(record.get("id").toString());
|
||||
|
||||
String updateDetailSql = """
|
||||
UPDATE hq_sat_detail
|
||||
SET deleted = 1, update_by = ?, update_time = NOW()
|
||||
WHERE record_id = ? AND deleted = 0
|
||||
""";
|
||||
|
||||
int detailUpdated = jdbcTemplate.update(updateDetailSql,
|
||||
LoginHelper.getUserId(), recordId);
|
||||
log.info("标记答题详情为已删除,记录ID: {}, 影响行数: {}", recordId, detailUpdated);
|
||||
|
||||
// 4. 标记答案表为已删除(通过record_id关联)
|
||||
String updateAnswerSql = """
|
||||
UPDATE hq_sat_answer
|
||||
SET deleted = 1, update_by = ?, update_time = NOW()
|
||||
WHERE record_id = ? AND deleted = 0
|
||||
""";
|
||||
|
||||
int answerUpdated = jdbcTemplate.update(updateAnswerSql,
|
||||
LoginHelper.getUserId(), recordId);
|
||||
log.info("标记答案数据为已删除,记录ID: {}, 影响行数: {}", recordId, answerUpdated);
|
||||
}
|
||||
|
||||
log.info("=== 用户历史答题记录标记删除完成 ===");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("标记用户历史答题记录为已删除失败,问卷ID: {}, 用户ID: {}, 错误详情: {}",
|
||||
surveyId, userId, e.getMessage(), e);
|
||||
// 这里不抛出异常,避免影响新答题数据的保存
|
||||
log.warn("历史数据删除失败不影响新答题数据保存,继续执行后续流程");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已答过问卷
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
* @return 是否已答过
|
||||
*/
|
||||
@Override
|
||||
public Boolean checkUserAnswered(Long surveyId) {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
return getUserSubmitCount(surveyId, userId) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户对指定问卷的提交次数(只统计未删除的记录)
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
* @param userId 用户ID
|
||||
* @return 提交次数
|
||||
*/
|
||||
private int getUserSubmitCount(Long surveyId, Long userId) {
|
||||
try {
|
||||
// 使用SQL直接查询,确保只统计未删除的记录
|
||||
String countSql = """
|
||||
SELECT COUNT(*) FROM hq_sat_record
|
||||
WHERE survey_id = ? AND user_id = ? AND deleted = 0
|
||||
""";
|
||||
|
||||
Integer count = jdbcTemplate.queryForObject(countSql, Integer.class, surveyId, userId);
|
||||
int submitCount = count != null ? count : 0;
|
||||
|
||||
log.debug("用户{}对问卷{}的有效提交次数: {}", userId, surveyId, submitCount);
|
||||
return submitCount;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询用户提交次数失败,问卷ID: {}, 用户ID: {}, 错误: {}",
|
||||
surveyId, userId, e.getMessage(), e);
|
||||
|
||||
// 发生异常时使用原有方式查询作为降级方案
|
||||
log.warn("使用降级方案查询提交次数");
|
||||
HqSatRecordBo queryBo = new HqSatRecordBo();
|
||||
queryBo.setSurveyId(surveyId);
|
||||
queryBo.setUserId(userId);
|
||||
|
||||
List<HqSatRecordVo> records = recordService.queryList(queryBo);
|
||||
int count = records.size();
|
||||
|
||||
log.debug("降级方案统计用户{}对问卷{}的提交次数: {}", userId, surveyId, count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户问卷答题记录(只返回未删除的最新记录)
|
||||
*
|
||||
* @param surveyId 问卷ID
|
||||
* @return 答题记录(包含答题详情)
|
||||
*/
|
||||
@Override
|
||||
public HqSatRecordVo getUserSurveyAnswer(Long surveyId) {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
HqSatRecordVo record = null;
|
||||
|
||||
try {
|
||||
// 使用SQL直接查询,确保只获取未删除的最新记录
|
||||
String findLatestRecordSql = """
|
||||
SELECT * FROM hq_sat_record
|
||||
WHERE survey_id = ? AND user_id = ? AND deleted = 0
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 1
|
||||
""";
|
||||
|
||||
List<Map<String, Object>> recordResults = jdbcTemplate.queryForList(findLatestRecordSql, surveyId, userId);
|
||||
|
||||
if (recordResults.isEmpty()) {
|
||||
throw new ServiceException("未找到有效的答题记录", 404);
|
||||
}
|
||||
|
||||
Map<String, Object> recordMap = recordResults.get(0);
|
||||
Long recordId = Long.valueOf(recordMap.get("id").toString());
|
||||
|
||||
log.info("找到用户最新有效答题记录,记录ID: {}", recordId);
|
||||
|
||||
// 通过recordId获取完整的记录对象
|
||||
record = recordService.queryById(recordId);
|
||||
if (record == null) {
|
||||
throw new ServiceException("答题记录数据异常", 500);
|
||||
}
|
||||
|
||||
} catch (ServiceException e) {
|
||||
throw e; // 重新抛出业务异常
|
||||
} catch (Exception e) {
|
||||
log.error("查询用户答题记录失败,使用降级方案,问卷ID: {}, 用户ID: {}, 错误: {}",
|
||||
surveyId, userId, e.getMessage(), e);
|
||||
|
||||
// 降级方案:使用原有方式查询
|
||||
HqSatRecordBo queryBo = new HqSatRecordBo();
|
||||
queryBo.setSurveyId(surveyId);
|
||||
queryBo.setUserId(userId);
|
||||
|
||||
List<HqSatRecordVo> records = recordService.queryList(queryBo);
|
||||
if (records.isEmpty()) {
|
||||
throw new ServiceException("未找到答题记录", 404);
|
||||
}
|
||||
|
||||
// 获取最新的一条记录
|
||||
record = records.get(0);
|
||||
}
|
||||
|
||||
// 直接从 hq_sat_answer 表查询答案详情(更准确的数据源)
|
||||
List<Map<String, Object>> answers = getAnswersFromSatAnswerTable(record.getId());
|
||||
log.debug("从 hq_sat_answer 表查询到答案记录数: {}", answers.size());
|
||||
|
||||
// 构造包含答案详情的记录对象
|
||||
HqSatRecordVo recordWithAnswers = new HqSatRecordVo();
|
||||
recordWithAnswers.setId(record.getId());
|
||||
recordWithAnswers.setSurveyId(record.getSurveyId());
|
||||
recordWithAnswers.setUserId(record.getUserId());
|
||||
recordWithAnswers.setUserType(record.getUserType());
|
||||
recordWithAnswers.setUserDept(record.getUserDept());
|
||||
recordWithAnswers.setSubmitTime(record.getSubmitTime());
|
||||
recordWithAnswers.setTotalScore(record.getTotalScore());
|
||||
recordWithAnswers.setAvgScore(record.getAvgScore());
|
||||
recordWithAnswers.setRemark(record.getRemark());
|
||||
|
||||
// 将answers数组作为扩展属性设置到记录对象
|
||||
// 由于HqSatRecordVo没有answers字段,我们使用一个包装类
|
||||
ExtendedSatRecordVo extendedRecord = new ExtendedSatRecordVo();
|
||||
extendedRecord.setRecord(recordWithAnswers);
|
||||
extendedRecord.setAnswers(answers);
|
||||
|
||||
// 临时使用remark字段传递answers数据
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
String answersJson = mapper.writeValueAsString(answers);
|
||||
recordWithAnswers.setRemark("ANSWERS_DATA:" + answersJson);
|
||||
log.debug("答案数据序列化成功,数据长度: {}", answersJson.length());
|
||||
} catch (Exception e) {
|
||||
log.error("序列化答案数据失败", e);
|
||||
}
|
||||
|
||||
return recordWithAnswers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 hq_sat_answer 表查询答案详情
|
||||
*
|
||||
* @param recordId 答题记录ID
|
||||
* @return 答案详情列表
|
||||
*/
|
||||
private List<Map<String, Object>> getAnswersFromSatAnswerTable(Long recordId) {
|
||||
try {
|
||||
log.debug("从 hq_sat_answer 表查询答案,记录ID: {}", recordId);
|
||||
|
||||
// 查询该记录的所有答案
|
||||
String answerSql = """
|
||||
SELECT question_id, question_type, answer_value,
|
||||
option_id, option_value, option_text, answer_score
|
||||
FROM hq_sat_answer
|
||||
WHERE record_id = ? AND deleted = 0
|
||||
ORDER BY question_id, option_id
|
||||
""";
|
||||
|
||||
List<Map<String, Object>> answerResults = jdbcTemplate.queryForList(answerSql, recordId);
|
||||
log.debug("查询到原始答案数据: {} 条", answerResults.size());
|
||||
|
||||
// 按题目ID分组聚合答案
|
||||
Map<Long, Map<String, Object>> questionAnswerMap = new HashMap<>();
|
||||
|
||||
for (Map<String, Object> answerRow : answerResults) {
|
||||
Long questionId = Long.valueOf(answerRow.get("question_id").toString());
|
||||
String questionType = answerRow.get("question_type").toString();
|
||||
String answerValue = (String) answerRow.get("answer_value");
|
||||
|
||||
// 获取或创建题目答案对象
|
||||
Map<String, Object> questionAnswer = questionAnswerMap.computeIfAbsent(questionId, k -> {
|
||||
Map<String, Object> answer = new HashMap<>();
|
||||
answer.put("questionId", questionId);
|
||||
answer.put("questionType", questionType);
|
||||
answer.put("answerValue", answerValue);
|
||||
return answer;
|
||||
});
|
||||
|
||||
log.debug("处理题目{}的答案: questionType={}, answerValue={}",
|
||||
questionId, questionType, answerValue);
|
||||
}
|
||||
|
||||
// 转换为列表并返回
|
||||
List<Map<String, Object>> result = new ArrayList<>(questionAnswerMap.values());
|
||||
log.debug("聚合后的答案数据: {} 个题目", result.size());
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("从 hq_sat_answer 表查询答案失败,记录ID: {}, 错误: {}",
|
||||
recordId, e.getMessage(), e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从remark字段解析答案数据
|
||||
*
|
||||
* @param detail 答题详情
|
||||
* @return 解析后的答案数据
|
||||
*/
|
||||
private Map<String, Object> parseAnswerFromRemark(HqSatDetailVo detail) {
|
||||
if (detail.getRemark() == null || detail.getRemark().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> answer = new HashMap<>();
|
||||
answer.put("questionId", detail.getQuestionId());
|
||||
|
||||
String remark = detail.getRemark();
|
||||
log.debug("解析答题详情,题目ID: {}, remark: {}", detail.getQuestionId(), remark);
|
||||
|
||||
// 解析remark中的数据格式: "题目类型:1;答案值:A;选项详情:A(选项文本),"
|
||||
try {
|
||||
String[] parts = remark.split(";");
|
||||
String questionType = "";
|
||||
String answerValue = "";
|
||||
|
||||
for (String part : parts) {
|
||||
if (part.startsWith("题目类型:")) {
|
||||
questionType = part.substring("题目类型:".length());
|
||||
answer.put("questionType", questionType);
|
||||
} else if (part.startsWith("答案值:")) {
|
||||
answerValue = part.substring("答案值:".length());
|
||||
answer.put("answerValue", answerValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置其他默认值
|
||||
answer.put("id", detail.getId());
|
||||
answer.put("recordId", detail.getRecordId());
|
||||
answer.put("answerScore", detail.getAnswerScore() != null ? detail.getAnswerScore() : 0);
|
||||
answer.put("remark", detail.getRemark());
|
||||
|
||||
log.debug("解析答案完成: questionId={}, questionType={}, answerValue={}",
|
||||
detail.getQuestionId(), questionType, answerValue);
|
||||
|
||||
return answer;
|
||||
} catch (Exception e) {
|
||||
log.error("解析答题详情失败,题目ID: {}, remark: {}", detail.getQuestionId(), remark, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存答案详情到 hq_sat_answer 表
|
||||
*
|
||||
* @param recordId 答题记录ID
|
||||
* @param answerItem 答案项
|
||||
*/
|
||||
private void saveAnswerDetails(Long recordId, HqSatAnswerSubmitBo.AnswerItem answerItem) {
|
||||
try {
|
||||
String questionType = answerItem.getQuestionType();
|
||||
String answerValue = answerItem.getAnswerValue();
|
||||
|
||||
log.info("=== 开始保存答案详情到 hq_sat_answer 表 ===");
|
||||
log.info("记录ID: {}, 题目ID: {}, 题目类型: {}, 答案值: {}",
|
||||
recordId, answerItem.getQuestionId(), questionType, answerValue);
|
||||
log.info("选项数据: {}", answerItem.getOptions());
|
||||
|
||||
if (questionType.equals("1") || questionType.equals("2")) {
|
||||
// 单选题或多选题:保存选项信息
|
||||
log.info("处理单选题或多选题,题目类型: {}", questionType);
|
||||
|
||||
if (answerItem.getOptions() != null && !answerItem.getOptions().isEmpty()) {
|
||||
log.info("选项数据不为空,选项数量: {}", answerItem.getOptions().size());
|
||||
|
||||
for (HqSatAnswerSubmitBo.OptionDetail option : answerItem.getOptions()) {
|
||||
log.info("处理选项: optionId={}, optionValue={}, optionText={}, isSelected={}",
|
||||
option.getOptionId(), option.getOptionValue(), option.getOptionText(),
|
||||
option.getIsSelected());
|
||||
|
||||
if (Boolean.TRUE.equals(option.getIsSelected())) {
|
||||
log.info("选项已选中,准备插入数据库");
|
||||
|
||||
// 根据选项ID查询真实的分值
|
||||
Long realScore = 0L;
|
||||
try {
|
||||
String scoreSql = "SELECT option_value FROM hq_sat_option WHERE id = ? AND deleted = 0";
|
||||
List<Map<String, Object>> scoreResult = jdbcTemplate.queryForList(scoreSql,
|
||||
option.getOptionId());
|
||||
if (!scoreResult.isEmpty()) {
|
||||
Object scoreValue = scoreResult.get(0).get("option_value");
|
||||
realScore = scoreValue != null ? Long.valueOf(scoreValue.toString()) : 0L;
|
||||
log.info("查询到选项{}的真实分值: {}", option.getOptionId(), realScore);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询选项分值失败,使用默认值0,选项ID: {}, 错误: {}", option.getOptionId(), e.getMessage());
|
||||
}
|
||||
|
||||
String sql = """
|
||||
INSERT INTO hq_sat_answer (
|
||||
tenant_id, create_by, create_time,
|
||||
record_id, question_id, question_type,
|
||||
option_id, option_value, option_text,
|
||||
answer_value, answer_score, deleted
|
||||
) VALUES (?, ?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
||||
""";
|
||||
|
||||
int rowsAffected = jdbcTemplate.update(sql,
|
||||
LoginHelper.getTenantId(), // tenant_id
|
||||
LoginHelper.getUserId(), // create_by
|
||||
recordId, // record_id
|
||||
answerItem.getQuestionId(), // question_id
|
||||
questionType, // question_type
|
||||
option.getOptionId(), // option_id
|
||||
option.getOptionValue(), // option_value(A、B、C、D)
|
||||
option.getOptionText(), // option_text
|
||||
answerValue, // answer_value
|
||||
realScore // answer_score(真实分值)
|
||||
);
|
||||
|
||||
log.info("选择题答案插入成功,影响行数: {}, 选项标识: {}, 分值: {}",
|
||||
rowsAffected, option.getOptionValue(), realScore);
|
||||
} else {
|
||||
log.info("选项未选中,跳过: {}", option.getOptionValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("选择题选项数据为空或null,题目ID: {}, 题目类型: {}",
|
||||
answerItem.getQuestionId(), questionType);
|
||||
}
|
||||
} else if (questionType.equals("3")) {
|
||||
// 问答题:直接保存文本内容
|
||||
log.info("处理问答题,答案值: {}", answerValue);
|
||||
|
||||
String sql = """
|
||||
INSERT INTO hq_sat_answer (
|
||||
tenant_id, create_by, create_time,
|
||||
record_id, question_id, question_type,
|
||||
option_id, option_value, option_text,
|
||||
answer_value, answer_score, deleted
|
||||
) VALUES (?, ?, NOW(), ?, ?, ?, NULL, NULL, NULL, ?, 0, 0)
|
||||
""";
|
||||
|
||||
int rowsAffected = jdbcTemplate.update(sql,
|
||||
LoginHelper.getTenantId(), // tenant_id
|
||||
LoginHelper.getUserId(), // create_by
|
||||
recordId, // record_id
|
||||
answerItem.getQuestionId(), // question_id
|
||||
questionType, // question_type
|
||||
answerValue // answer_value
|
||||
);
|
||||
|
||||
log.info("问答题答案插入成功,影响行数: {}", rowsAffected);
|
||||
}
|
||||
|
||||
log.info("=== 答案详情保存完成 ===");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("保存答案到 hq_sat_answer 表失败,题目ID: {}, 题目类型: {}, 错误详情: {}",
|
||||
answerItem.getQuestionId(), answerItem.getQuestionType(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展的答题记录VO,包含答案列表
|
||||
*/
|
||||
private static class ExtendedSatRecordVo {
|
||||
private HqSatRecordVo record;
|
||||
private List<Map<String, Object>> answers;
|
||||
|
||||
public HqSatRecordVo getRecord() {
|
||||
return record;
|
||||
}
|
||||
|
||||
public void setRecord(HqSatRecordVo record) {
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getAnswers() {
|
||||
return answers;
|
||||
}
|
||||
|
||||
public void setAnswers(List<Map<String, Object>> answers) {
|
||||
this.answers = answers;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user