提交代码修改

This commit is contained in:
zlwu committed 2025-07-18 22:08:03 +08:00
1 parent 68d470391f
commit 9562e32697
5 files changed
+502 -83

No files matched your search

@@ -26,6 +26,9 @@ import org.dromara.hq.service.IHqSatDetailService;
import org.dromara.hq.domain.bo.HqSatSurveyQuestionBo;
import org.dromara.hq.domain.vo.HqSatSurveyQuestionVo;
import org.dromara.hq.domain.vo.HqSatDetailVo;
import org.dromara.hq.domain.bo.HqSatOptionBo;
import org.dromara.hq.domain.vo.HqSatOptionVo;
import org.dromara.hq.service.IHqSatOptionService;
import java.util.List;
import java.util.Map;
@@ -58,6 +61,7 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
private final IHqSatSurveyQuestionService surveyQuestionService;
private final IHqSatRecordService recordService;
private final IHqSatDetailService detailService;
private final IHqSatOptionService optionService;
@Autowired
private JdbcTemplate jdbcTemplate;
@@ -858,6 +862,7 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
// 构建返回数据
statistics.put("surveyId", surveyId);
statistics.put("score", survey.getScore());
statistics.put("surveyTitle", survey.getSurveyTitle());
statistics.put("surveyCode", survey.getSurveyCode());
statistics.put("totalParticipants", totalParticipants != null ? totalParticipants : 0);
@@ -953,15 +958,15 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
String optionStatsSql = """
SELECT
o.id as option_id,
o.option_value,
o.option_text,
o.option_score,
o.option_content,
COUNT(DISTINCT a.record_id) as select_count
FROM hq_sat_question_option o
FROM hq_sat_option o
LEFT JOIN hq_sat_answer a ON o.id = a.option_id AND a.deleted = 0
LEFT JOIN hq_sat_record r ON a.record_id = r.id AND r.survey_id = ?
WHERE o.question_id = ? AND o.deleted = 0
GROUP BY o.id, o.option_value, o.option_text
ORDER BY o.option_value
GROUP BY o.id, o.option_score, o.option_content
ORDER BY o.option_score DESC
""";
java.util.List<java.util.Map<String, Object>> optionStats = jdbcTemplate.queryForList(optionStatsSql, surveyId, questionId);
@@ -979,13 +984,13 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
String textAnswersSql = """
SELECT
a.id,
a.answer_value as answer_text,
LENGTH(a.answer_value) as text_length,
a.remark as answer_text,
LENGTH(a.remark) as text_length,
DATE_FORMAT(r.submit_time, '%Y-%m-%d %H:%i:%s') as answer_time
FROM hq_sat_answer a
JOIN hq_sat_record r ON a.record_id = r.id
WHERE a.question_id = ? AND r.survey_id = ? AND a.answer_value IS NOT NULL
AND a.deleted = 0 AND LENGTH(a.answer_value) > 0
AND a.deleted = 0 AND LENGTH(a.remark) > 0
ORDER BY r.submit_time DESC
LIMIT 10
""";
@@ -1040,13 +1045,38 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
String status = (String) query.get("status");
String userName = (String) query.get("userName");
// 构建查询SQL
// 查询问卷信息,获取目标群体类型
String targetGroupSql = "SELECT target_group FROM hq_sat_survey WHERE id = ? AND deleted = 0";
String targetGroup = jdbcTemplate.queryForObject(targetGroupSql, String.class, surveyId);
if (targetGroup == null) {
throw new ServiceException("问卷不存在或已被删除");
}
// 构建查询SQL - 从用户表查询符合条件的用户,并关联答题记录表
StringBuilder sql = new StringBuilder();
sql.append("SELECT r.*, u.nick_name as userName, d.dept_name as deptName ");
sql.append("FROM hq_sat_record r ");
sql.append("LEFT JOIN sys_user u ON r.user_id = u.user_id ");
sql.append("SELECT u.user_id, u.nick_name as userName, d.dept_name as deptName, ");
sql.append("rec.id, rec.survey_id, rec.submit_time, rec.total_score, rec.create_time ");
sql.append("FROM sys_user u ");
sql.append("LEFT JOIN sys_dept d ON u.dept_id = d.dept_id ");
sql.append("WHERE r.survey_id = ? AND r.deleted = 0 ");
sql.append("LEFT JOIN sys_user_role ur ON u.user_id = ur.user_id ");
sql.append("LEFT JOIN sys_role r ON ur.role_id = r.role_id ");
sql.append("LEFT JOIN hq_sat_record rec ON u.user_id = rec.user_id AND rec.survey_id = ? AND rec.deleted = 0 ");
sql.append("WHERE u.del_flag = '0' ");
// 根据目标群体过滤用户
sql.append("AND (");
if ("1".equals(targetGroup)) {
// 学生群体
sql.append("r.role_key = 'student'");
} else if ("2".equals(targetGroup)) {
// 教职工群体
sql.append("r.role_key = 'teacher'");
} else if ("3".equals(targetGroup)) {
// 全体群体(学生和教职工)
sql.append("(r.role_key = 'student' OR r.role_key = 'teacher')");
}
sql.append(") ");
java.util.List<Object> params = new java.util.ArrayList<>();
params.add(surveyId);
@@ -1054,9 +1084,9 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
// 添加状态过滤
if (status != null && !status.isEmpty()) {
if ("1".equals(status)) {
sql.append("AND r.submit_time IS NOT NULL ");
sql.append("AND rec.submit_time IS NOT NULL ");
} else if ("0".equals(status)) {
sql.append("AND r.submit_time IS NULL ");
sql.append("AND (rec.submit_time IS NULL OR rec.id IS NULL) ");
}
}
@@ -1066,30 +1096,40 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
params.add("%" + userName + "%");
}
sql.append("ORDER BY r.create_time DESC ");
// 分页查询
int offset = (pageNum - 1) * pageSize;
sql.append("LIMIT ? OFFSET ?");
params.add(pageSize);
params.add(offset);
java.util.List<java.util.Map<String, Object>> records = jdbcTemplate.queryForList(sql.toString(), params.toArray());
sql.append("GROUP BY u.user_id, rec.id ");
sql.append("ORDER BY CASE WHEN rec.submit_time IS NOT NULL THEN 0 ELSE 1 END, rec.submit_time DESC, u.user_id ");
// 查询总数
StringBuilder countSql = new StringBuilder();
countSql.append("SELECT COUNT(*) FROM hq_sat_record r ");
countSql.append("LEFT JOIN sys_user u ON r.user_id = u.user_id ");
countSql.append("WHERE r.survey_id = ? AND r.deleted = 0 ");
countSql.append("SELECT COUNT(DISTINCT u.user_id) ");
countSql.append("FROM sys_user u ");
countSql.append("LEFT JOIN sys_user_role ur ON u.user_id = ur.user_id ");
countSql.append("LEFT JOIN sys_role r ON ur.role_id = r.role_id ");
countSql.append("LEFT JOIN hq_sat_record rec ON u.user_id = rec.user_id AND rec.survey_id = ? AND rec.deleted = 0 ");
countSql.append("WHERE u.del_flag = '0' ");
// 根据目标群体过滤用户
countSql.append("AND (");
if ("1".equals(targetGroup)) {
// 学生群体
countSql.append("r.role_key = 'student'");
} else if ("2".equals(targetGroup)) {
// 教职工群体
countSql.append("r.role_key = 'teacher'");
} else if ("3".equals(targetGroup)) {
// 全体群体(学生和教职工)
countSql.append("(r.role_key = 'student' OR r.role_key = 'teacher')");
}
countSql.append(") ");
java.util.List<Object> countParams = new java.util.ArrayList<>();
countParams.add(surveyId);
if (status != null && !status.isEmpty()) {
if ("1".equals(status)) {
countSql.append("AND r.submit_time IS NOT NULL ");
countSql.append("AND rec.submit_time IS NOT NULL ");
} else if ("0".equals(status)) {
countSql.append("AND r.submit_time IS NULL ");
countSql.append("AND (rec.submit_time IS NULL OR rec.id IS NULL) ");
}
}
@@ -1100,13 +1140,42 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
Integer total = jdbcTemplate.queryForObject(countSql.toString(), Integer.class, countParams.toArray());
// 处理记录数据,添加状态和持续时间字段
for (java.util.Map<String, Object> record : records) {
// 设置状态
record.put("status", record.get("submit_time") != null ? "1" : "0");
// 分页查询
int offset = (pageNum - 1) * pageSize;
sql.append("LIMIT ? OFFSET ?");
params.add(pageSize);
params.add(offset);
// 计算持续时间(暂时使用随机值,后续可以实现真实计算)
record.put("duration", 300 + (int)(Math.random() * 600)); // 5-15分钟
java.util.List<java.util.Map<String, Object>> records = jdbcTemplate.queryForList(sql.toString(), params.toArray());
// 查询问卷题目信息,用于前端显示
String questionsSql = "SELECT q.id, q.question_title, q.question_type FROM hq_sat_question q " +
"JOIN hq_sat_survey_question sq ON q.id = sq.question_id " +
"WHERE sq.survey_id = ? AND q.deleted = 0 AND sq.deleted = 0 " +
"ORDER BY sq.sort_order";
List<Map<String, Object>> questionsList = jdbcTemplate.queryForList(questionsSql, surveyId);
Map<Long, Map<String, Object>> questionsMap = new HashMap<>();
// 构建题目ID到题目信息的映射
for (Map<String, Object> question : questionsList) {
Long questionId = Long.valueOf(question.get("id").toString());
questionsMap.put(questionId, question);
}
// 处理记录数据,添加状态、持续时间和答题详情字段
for (java.util.Map<String, Object> record : records) {
// 设置状态 - 已提交或未提交
boolean hasSubmitted = record.get("submit_time") != null;
record.put("status", hasSubmitted ? "1" : "0");
// 设置得分 - 已提交显示实际得分,未提交显示0
if (!hasSubmitted) {
record.put("total_score", 0);
}
// 计算持续时间(已提交的才计算,未提交显示0)
record.put("duration", hasSubmitted ? (300 + (int)(Math.random() * 600)) : 0); // 已提交的记录显示5-15分钟,未提交的显示0
// 格式化时间
if (record.get("create_time") != null) {
@@ -1115,18 +1184,114 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
if (record.get("submit_time") != null) {
record.put("endTime", record.get("submit_time").toString());
}
// 获取答题详情(仅对已提交的记录)
if (hasSubmitted && record.get("id") != null) {
Long recordId = Long.valueOf(record.get("id").toString());
List<Map<String, Object>> answerDetails = getAnswersFromSatAnswerTable(recordId);
// 将答案与题目信息关联,组织成前端需要的格式
List<Map<String, Object>> formattedAnswers = new ArrayList<>();
Map<Long, List<Map<String, Object>>> questionAnswers = new HashMap<>();
// 先按题目ID分组答案
for (Map<String, Object> answer : answerDetails) {
Long questionId = Long.valueOf(answer.get("questionId").toString());
if (!questionAnswers.containsKey(questionId)) {
questionAnswers.put(questionId, new ArrayList<>());
}
questionAnswers.get(questionId).add(answer);
}
// 为每个题目创建一个答案记录
for (Long questionId : questionAnswers.keySet()) {
List<Map<String, Object>> answers = questionAnswers.get(questionId);
// 获取题目基本信息
Map<String, Object> questionInfo = questionsMap.get(questionId);
if (questionInfo == null) continue;
Map<String, Object> formattedAnswer = new HashMap<>();
formattedAnswer.put("id", questionId);
formattedAnswer.put("questionId", questionId);
formattedAnswer.put("questionTitle", questionInfo.get("question_title"));
formattedAnswer.put("questionType", String.valueOf(questionInfo.get("question_type")));
// 设置分数(汇总所有选项的分数)
double totalScore = 0;
for (Map<String, Object> answer : answers) {
String scoreStr = (String)answer.get("answerScore");
if (scoreStr != null) {
try {
totalScore += Double.parseDouble(scoreStr);
} catch (NumberFormatException e) {
log.warn("解析答题分数失败: {}", scoreStr);
}
}
}
formattedAnswer.put("score", totalScore);
String questionType = (String)formattedAnswer.get("questionType");
// 处理问答题
if ("3".equals(questionType)) {
// 取第一个答案的值作为问答题答案
if (!answers.isEmpty()) {
formattedAnswer.put("answerValue", answers.get(0).get("remark"));
}
}
// 处理单选题和多选题
else if ("1".equals(questionType) || "2".equals(questionType)) {
List<Map<String, Object>> options = new ArrayList<>();
// 获取所有选中的选项
for (Map<String, Object> answer : answers) {
// 只处理有选项ID的答案
if (answer.get("optionId") != null) {
Map<String, Object> option = new HashMap<>();
log.info("answer-zlwu12: {}", answer);
option.put("optionId", answer.get("optionId"));
option.put("optionValue", answer.get("optionValue"));
option.put("optionText", answer.get("optionText"));
// option.put("optionScore", answer.get("answerScore"));
option.put("isSelected", true);
options.add(option);
}
}
formattedAnswer.put("options", options);
// 设置answerValue为所有选中选项值的拼接
StringBuilder answerValue = new StringBuilder();
for (Map<String, Object> option : options) {
if (answerValue.length() > 0) {
answerValue.append(",");
}
answerValue.append(option.get("optionValue"));
}
formattedAnswer.put("answerValue", answerValue.toString());
}
formattedAnswers.add(formattedAnswer);
}
// 将答题详情添加到记录中
record.put("answers", formattedAnswers);
}
}
java.util.Map<String, Object> result = new java.util.HashMap<>();
result.put("rows", records);
result.put("total", total != null ? total : 0);
log.info("获取答题记录完成,总数: {}, 当前页: {}", total, records.size());
log.info("获取符合问卷面向群体的所有用户信息完成,总数: {}, 当前页: {}", total, records.size());
return result;
} catch (Exception e) {
log.error("获取答题记录失败,问卷ID: {}, 错误: {}", surveyId, e.getMessage(), e);
throw new ServiceException("获取答题记录失败: " + e.getMessage());
log.error("获取问卷用户记录失败,问卷ID: {}, 错误: {}", surveyId, e.getMessage(), e);
throw new ServiceException("获取问卷用户记录失败: " + e.getMessage());
}
}
@@ -1139,7 +1304,6 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
@Override
public java.util.Map<String, Object> getAnswerRecordDetail(Long recordId) {
log.info("获取答题记录详情,记录ID: {}", recordId);
try {
// 查询答题记录基本信息
String recordSql = "SELECT r.*, u.nick_name as userName, d.dept_name as deptName " +
@@ -1147,57 +1311,107 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
"LEFT JOIN sys_user u ON r.user_id = u.user_id " +
"LEFT JOIN sys_dept d ON u.dept_id = d.dept_id " +
"WHERE r.id = ? AND r.deleted = 0";
java.util.List<java.util.Map<String, Object>> recordList = jdbcTemplate.queryForList(recordSql, recordId);
if (recordList.isEmpty()) {
throw new ServiceException("答题记录不存在", 404);
}
java.util.Map<String, Object> record = recordList.get(0);
// 查询答题详情
Long surveyId = record.get("survey_id") != null ? Long.valueOf(record.get("survey_id").toString()) : null;
// 1. 获取问卷题目顺序
List<HqSatSurveyQuestionVo> questionVos = surveyQuestionService.getSurveyQuestionsWithDetail(surveyId);
// 题目顺序Map
Map<Long, Integer> questionOrderMap = new HashMap<>();
for (int i = 0; i < questionVos.size(); i++) {
questionOrderMap.put(questionVos.get(i).getQuestionId(), i);
}
// 2. 查询所有选项顺序
Map<Long, List<HqSatOptionVo>> questionOptionsMap = new HashMap<>();
for (HqSatSurveyQuestionVo qvo : questionVos) {
HqSatOptionBo bo = new HqSatOptionBo();
bo.setQuestionId(qvo.getQuestionId());
bo.setDeleted("0");
List<HqSatOptionVo> options = optionService.queryList(bo);
options.sort((a, b) -> {
if (a.getOptionOrder() == null && b.getOptionOrder() == null) return 0;
if (a.getOptionOrder() == null) return 1;
if (b.getOptionOrder() == null) return -1;
return a.getOptionOrder().compareTo(b.getOptionOrder());
});
questionOptionsMap.put(qvo.getQuestionId(), options);
}
// 3. 查询答题详情
String answerSql = "SELECT a.question_id, a.question_type, a.answer_value, " +
"a.option_id, a.option_value, a.option_text, a.answer_score, " +
"a.option_id, a.option_value, a.option_text, a.answer_score, a.remark, " +
"q.question_title " +
"FROM hq_sat_answer a " +
"LEFT JOIN hq_sat_question q ON a.question_id = q.id " +
"WHERE a.record_id = ? AND a.deleted = 0 " +
"ORDER BY a.question_id, a.option_id";
"WHERE a.record_id = ? AND a.deleted = 0 ";
java.util.List<java.util.Map<String, Object>> answers = jdbcTemplate.queryForList(answerSql, recordId);
// 按题目分组答案
java.util.Map<Long, java.util.Map<String, Object>> questionAnswerMap = new java.util.HashMap<>();
for (java.util.Map<String, Object> answer : answers) {
Long questionId = Long.valueOf(answer.get("question_id").toString());
if (!questionAnswerMap.containsKey(questionId)) {
java.util.Map<String, Object> questionAnswer = new java.util.HashMap<>();
questionAnswer.put("id", questionId);
questionAnswer.put("questionId", questionId);
questionAnswer.put("questionTitle", answer.get("question_title"));
questionAnswer.put("questionType", answer.get("question_type"));
questionAnswer.put("answerValue", answer.get("answer_value"));
questionAnswer.put("score", answer.get("answer_score"));
questionAnswer.put("options", new java.util.ArrayList<java.util.Map<String, Object>>());
questionAnswerMap.put(questionId, questionAnswer);
// 按题目分组
Map<Long, List<Map<String, Object>>> answerGroup = new HashMap<>();
for (Map<String, Object> answer : answers) {
Long qid = Long.valueOf(answer.get("question_id").toString());
answerGroup.computeIfAbsent(qid, k -> new ArrayList<>()).add(answer);
}
// 组装answers,按题目顺序
List<Map<String, Object>> resultAnswers = new ArrayList<>();
for (HqSatSurveyQuestionVo qvo : questionVos) {
Long qid = qvo.getQuestionId();
List<Map<String, Object>> answerList = answerGroup.getOrDefault(qid, new ArrayList<>());
Map<String, Object> answerObj = new HashMap<>();
answerObj.put("id", qid);
answerObj.put("questionId", qid);
answerObj.put("questionTitle", qvo.getQuestionTitle());
answerObj.put("questionType", qvo.getQuestionType());
double totalScore = 0;
for (Map<String, Object> a : answerList) {
Object scoreStr = a.get("answer_score");
if (scoreStr != null) {
try { totalScore += Double.parseDouble(scoreStr.toString()); } catch (Exception ignore) {}
}
}
// 如果是选择题,添加选项信息
if (answer.get("option_id") != null) {
java.util.Map<String, Object> option = new java.util.HashMap<>();
option.put("optionId", answer.get("option_id"));
option.put("optionValue", answer.get("option_value"));
option.put("optionText", answer.get("option_text"));
option.put("isSelected", true); // 查询出来的都是已选择的选项
@SuppressWarnings("unchecked")
java.util.List<java.util.Map<String, Object>> options =
(java.util.List<java.util.Map<String, Object>>) questionAnswerMap.get(questionId).get("options");
options.add(option);
answerObj.put("score", totalScore);
// 问答题
if ("3".equals(qvo.getQuestionType())) {
String remark = answerList.isEmpty() ? "" : (answerList.get(0).get("remark") != null ? answerList.get(0).get("remark").toString() : "");
answerObj.put("answerValue", remark);
answerObj.put("options", new ArrayList<>());
} else if ("1".equals(qvo.getQuestionType()) || "2".equals(qvo.getQuestionType())) {
// 选择题,组装选项,按optionOrder排序
List<HqSatOptionVo> allOptions = questionOptionsMap.getOrDefault(qid, new ArrayList<>());
List<Map<String, Object>> options = new ArrayList<>();
for (HqSatOptionVo opt : allOptions) {
Map<String, Object> optMap = new HashMap<>();
optMap.put("optionId", opt.getId());
optMap.put("optionValue", opt.getOptionContent());
optMap.put("optionText", opt.getOptionContent());
optMap.put("optionScore", opt.getOptionScore());
optMap.put("isSelected", false);
// 检查是否被选中
for (Map<String, Object> a : answerList) {
if (a.get("option_id") != null && opt.getId().toString().equals(a.get("option_id").toString())) {
optMap.put("isSelected", true);
break;
}
}
options.add(optMap);
}
answerObj.put("options", options);
// answerValue为所有选中optionValue的拼接
StringBuilder answerValue = new StringBuilder();
for (Map<String, Object> opt : options) {
if (Boolean.TRUE.equals(opt.get("isSelected"))) {
if (answerValue.length() > 0) answerValue.append(",");
answerValue.append(opt.get("optionValue"));
}
}
answerObj.put("answerValue", answerValue.toString());
} else {
answerObj.put("options", new ArrayList<>());
answerObj.put("answerValue", "");
}
resultAnswers.add(answerObj);
}
// 构建返回结果
java.util.Map<String, Object> result = new java.util.HashMap<>();
result.put("id", record.get("id"));
@@ -1210,11 +1424,9 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
result.put("duration", 300 + (int)(Math.random() * 600)); // 暂时使用随机值
result.put("totalScore", record.get("total_score"));
result.put("status", record.get("submit_time") != null ? "1" : "0");
result.put("answers", new java.util.ArrayList<>(questionAnswerMap.values()));
log.info("获取答题记录详情完成,记录ID: {}, 答案数量: {}", recordId, questionAnswerMap.size());
result.put("answers", resultAnswers);
log.info("获取答题记录详情完成,记录ID: {}, 答案数量: {}", recordId, resultAnswers.size());
return result;
} catch (Exception e) {
log.error("获取答题记录详情失败,记录ID: {}, 错误: {}", recordId, e.getMessage(), e);
throw new ServiceException("获取答题记录详情失败: " + e.getMessage());
@@ -450,6 +450,8 @@ await Promise.all(promises);
| 1.0.1 | 2025-07-17 | 优化题目选项获取接口,增强前端题目选项显示功能 | Cursor |
| 1.0.2 | 2025-07-17 | 修复选项排序值保存后实时更新功能,优化用户体验 | wuzilong |
| 1.0.3 | 2025-07-17 | 添加选项保存防重复提交机制,解决幂等性控制冲突 | wuzilong |
| 1.0.4 | 2025-07-18 | 优化问答题文本回答展示功能,提高Vue渲染稳定性 | wuzilong |
| 1.0.5 | 2025-07-18 | 增强问卷答题记录接口功能,返回答题详情数据 | wuzilong |
## 附录
@@ -465,7 +467,207 @@ await Promise.all(promises);
- 总分(total_score) = 所有题目的得分之和
- 平均分(avg_score) = 总分 ÷ 有分值的题目数量(不包括问答题)
### 问答题文本回答数据结构
**统计分析接口中问答题文本回答数据结构**:
问卷统计分析接口(`/hq/satSurvey/{id}/statistics`)返回的问答题文本回答数据结构如下:
```json
"textAnswers": [
{
"id": 393,
"answer_text": "用户回答内容示例",
"text_length": 12,
"answer_time": "2025-07-18 10:40:35"
}
]
```
**字段说明**:
| 字段名 | 类型 | 描述 |
|-------|-----|------|
| id | Integer | 回答ID,唯一标识 |
| answer_text | String | 回答文本内容 |
| text_length | Integer | 文本长度(字符数) |
| answer_time | String | 回答时间,格式:yyyy-MM-dd HH:mm:ss |
**前端使用注意事项**:
- 在Vue循环渲染textAnswers数组时,应使用answer.id作为key值,而非索引,确保Vue的虚拟DOM正确更新
- 示例:`<div v-for="(answer, idx) in question.textAnswers.slice(0, 5)" :key="answer.id || idx" class="answer-item">`
- 2025-07-18修复:解决了文本回答因key不唯一导致的渲染问题,详见`docs/变更管理/code-adjust-record-2025-07-18-wuzilong.md`
#### 2025-07-18 接口返回顺序与问答题答案优化
- `/hq/satSurvey/record/{recordId}` 接口返回参数调整:
1. `answers` 数组顺序严格按照问卷题目顺序返回。
2. `answers.options` 选项数组顺序严格按照题目原始选项顺序返回。
3. 问答题(`questionType=3`)的 `answerValue` 字段取自 `remark` 字段,真实还原用户填写内容。
> 以上调整保证了前端展示与问卷设计一致,提升了数据准确性。
### 获取问卷的所有答题记录列表
**请求方式**:GET
**请求路径**:`/hq/satSurvey/{surveyId}/records`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|-------|-----|------|------|
| surveyId | Long | 是 | 问卷ID,路径参数 |
| status | String | 否 | 答题状态(1-已提交,0-未提交) |
| userName | String | 否 | 用户姓名,模糊查询 |
| dateRange | String[] | 否 | 日期范围 |
| pageNum | Integer | 否 | 页码,默认1 |
| pageSize | Integer | 否 | 每页条数,默认10 |
**请求示例**:
```
GET /hq/satSurvey/1945836064483778562/records?pageNum=1&pageSize=10&surveyId=1945836064483778562
```
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"total": 5,
"rows": [
{
"user_id": 2008,
"userName": "智控学院郑十",
"deptName": "智能控制学院",
"id": "1946035501017587714",
"survey_id": "1945836064483778562",
"submit_time": "2025-07-18 10:33:14",
"total_score": "24.00",
"create_time": "2025-07-18 10:33:14",
"status": "1",
"duration": 313,
"startTime": "2025-07-18T10:33:14",
"endTime": "2025-07-18T10:33:14",
"answers": [
{
"id": 1945836064483778563,
"questionId": 1945836064483778563,
"questionTitle": "您对后勤服务总体满意度如何?",
"questionType": "1",
"score": 4.0,
"answerValue": "B",
"options": [
{
"optionId": 1945836064483778569,
"optionValue": "B",
"optionText": "满意",
"isSelected": true
}
]
},
{
"id": 1945836064483778564,
"questionId": 1945836064483778564,
"questionTitle": "您认为校园环境有哪些需要改善的地方?",
"questionType": "2",
"score": 11.0,
"answerValue": "A,C,D",
"options": [
{
"optionId": 1945836064483778570,
"optionValue": "A",
"optionText": "绿化维护",
"isSelected": true
},
{
"optionId": 1945836064483778572,
"optionValue": "C",
"optionText": "卫生状况",
"isSelected": true
},
{
"optionId": 1945836064483778573,
"optionValue": "D",
"optionText": "基础设施",
"isSelected": true
}
]
},
{
"id": 1945836064483778565,
"questionId": 1945836064483778565,
"questionTitle": "您对校园后勤服务的建议",
"questionType": "3",
"score": 0,
"answerValue": "希望增加自习室的开放时间,目前晚上关门太早"
}
]
},
{
"user_id": 4,
"userName": "仅本人 密码666666",
"deptName": "行政单位",
"id": null,
"survey_id": null,
"submit_time": null,
"total_score": 0,
"create_time": null,
"status": "0",
"duration": 0
}
]
}
}
```
**特殊说明**:
1. **功能强化(2025-07-18)**:接口现在返回已提交记录的答题详情(`answers`字段),无需额外请求
2. 每条记录(已提交状态)包含完整的答题详情数据,包括:
- 题目信息:`questionId`、`questionTitle`、`questionType`
- 答案内容:`score`、`answerValue`
- 选项数据(单选/多选题):`options`数组,包含所有选中的选项
3. 问题类型(`questionType`):
- `1`:单选题
- `2`:多选题
- `3`:问答题
4. 未提交的记录(`status="0"`)不包含答题详情
**最佳实践**:
- 前端可直接使用返回的`answers`数据渲染答题详情,无需额外请求
- 单选题和多选题显示选项,问答题显示文本内容
### 相关文件
- 数据库表结构:`docs/设计文档/database/hzdyjs_xg.sql`
- 变更记录:`docs/变更管理/code-adjust-record-2025-07-01-cursor.md`
- 文本回答展示修复:`docs/变更管理/code-adjust-record-2025-07-18-wuzilong.md`
- 答题详情功能优化:`docs/变更管理/code-adjust-record-2025-07-18-wuzilong.md`
## FAQ 常见问题解答
### 1. 问卷答题记录查询报错 "Unknown column 'u.deleted' in 'where clause'"
**问题描述**:
获取问卷答题记录列表时报错:`java.sql.SQLSyntaxErrorException: Unknown column 'u.deleted' in 'where clause'`
**原因分析**:
在SQL查询中使用了 `u.deleted = 0` 条件,但实际上 `sys_user` 表中逻辑删除字段名是 `del_flag` 而非 `deleted`。这是系统表与业务表之间的字段命名差异导致的。
**解决方案**:
修改SQL查询,将 `WHERE u.deleted = 0` 改为 `WHERE u.del_flag = '0'`(注意类型也从数字改为了字符串)。
**相关代码**:
```java
// 修改前
sql.append("WHERE u.deleted = 0 ");
// 修改后
sql.append("WHERE u.del_flag = '0' ");
```
**注意事项**:
- 系统表(如sys_user, sys_role等)使用 `del_flag` 作为逻辑删除字段,类型为char(1)
- 业务表(如hq_sat_survey等)使用 `deleted` 作为逻辑删除字段,类型为tinyint(1)
- 在跨表关联查询时,务必注意这一差异,避免类似错误
+1
View File
@@ -526,6 +526,7 @@ export interface SatAnswerVO {
optionValue: string;
optionText: string;
isSelected: boolean;
optionScore: string | number;
}>;
}
+5 -1
View File
@@ -51,7 +51,11 @@ html.dark svg {
#app .sidebar-container .sub-menu-title-noDropdown,
#app .sidebar-container .el-sub-menu__title {
margin: 0px 8px 0 0 !important;
margin: 0px 0px 0 0 !important;
}
#app .sidebar-container .nest-menu .el-sub-menu > .el-sub-menu__title, #app .sidebar-container .el-sub-menu .el-menu-item{
margin: 0px 0px 0 0 !important;
}
*,
+1 -1
View File
@@ -235,7 +235,7 @@
// 嵌套菜单项样式
.nest-menu .el-sub-menu > .el-sub-menu__title,
.el-sub-menu .el-menu-item {
max-width: calc(100% - 16px) !important;
max-width: calc(100%) !important;
width: auto !important;
border-radius: var(--border-radius-xs) !important;
margin: 1px 8px !important;