优化问卷回答界面及功能

This commit is contained in:
zlwu committed 2025-06-27 11:47:00 +08:00
1 parent 1ab9f9c458
commit ed5eb9ca52
13 files changed
+6122 -86

No files matched your search

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