222 lines
7.3 KiB
Markdown
222 lines
7.3 KiB
Markdown
# 问卷功能完整实现方案
|
||
|
||
## 📋 需求概述
|
||
|
||
系统需要支持完整的问卷功能,包括:
|
||
1. **问卷在量表管理中显示**:创建的问卷能够出现在量表管理中
|
||
2. **问卷答题功能**:用户填写完问卷后,客观题自动打分,主观题传给管理员进行打分
|
||
3. **多种题目类型支持**:单选、多选、判断、填空、排序、计算、简答、问答、作文等
|
||
4. **多种组卷形式**:自助组卷、随机组卷、手动随机相结合
|
||
5. **成绩排名统计**:客观题系统自动实现打分,成绩自动排名统计
|
||
|
||
---
|
||
|
||
## 🎯 实现方案
|
||
|
||
### 阶段一:让问卷显示在量表管理中
|
||
|
||
#### 方案1:统一查询接口(推荐)
|
||
- 修改量表列表查询,同时返回问卷数据
|
||
- 添加类型标识字段(`sourceType`: 'scale' 或 'questionnaire')
|
||
- 前端统一显示,根据类型标识区分操作
|
||
|
||
#### 方案2:创建统一视图
|
||
- 在数据库创建视图,统一量表和问卷
|
||
- 查询时使用视图
|
||
|
||
**选择方案1**,因为更灵活,不需要修改数据库结构。
|
||
|
||
---
|
||
|
||
### 阶段二:问卷答题功能
|
||
|
||
参考量表测评的实现方式:
|
||
|
||
1. **开始问卷**:创建问卷答题记录
|
||
2. **获取题目**:根据组卷方式获取题目列表
|
||
3. **保存答案**:实时保存用户答案
|
||
4. **提交问卷**:
|
||
- 客观题自动计分
|
||
- 主观题标记为待评分
|
||
- 计算客观题总分
|
||
- 更新排名
|
||
|
||
---
|
||
|
||
### 阶段三:自动打分功能
|
||
|
||
#### 客观题类型
|
||
- **radio(单选)**:根据选项的`is_correct`和`option_score`计分
|
||
- **checkbox(多选)**:全对得满分,部分对按比例得分
|
||
- **boolean(判断)**:根据选项的`is_correct`计分
|
||
- **input(填空)**:如果有标准答案,进行文本匹配(支持模糊匹配)
|
||
- **sort(排序)**:顺序完全正确得满分,部分正确按比例得分
|
||
- **calculate(计算)**:数值匹配,允许误差范围
|
||
|
||
#### 主观题类型
|
||
- **text(简答)**:需要管理员评分
|
||
- **textarea(问答)**:需要管理员评分
|
||
- **essay(作文)**:需要管理员评分
|
||
- **input(无标准答案)**:需要管理员评分
|
||
|
||
---
|
||
|
||
### 阶段四:主观题评分管理
|
||
|
||
1. **待评分列表**:显示所有待评分的主观题
|
||
2. **评分界面**:管理员可以查看题目、答案,进行评分
|
||
3. **批量评分**:支持批量评分功能
|
||
4. **评分后更新**:评分后更新总分和排名
|
||
|
||
---
|
||
|
||
### 阶段五:成绩排名统计
|
||
|
||
1. **排名计算**:
|
||
- 按总分从高到低排序
|
||
- 相同分数按提交时间排序(先提交的排名靠前)
|
||
2. **排名更新**:提交问卷或评分后自动更新排名
|
||
3. **排名查询**:支持按问卷查询排名列表
|
||
|
||
---
|
||
|
||
## 📁 需要创建/修改的文件
|
||
|
||
### 数据库
|
||
- [x] `psy_questionnaire` - 问卷表(已存在)
|
||
- [x] `psy_questionnaire_item` - 问卷题目表(已存在)
|
||
- [x] `psy_questionnaire_option` - 问卷选项表(已存在)
|
||
- [x] `psy_questionnaire_answer` - 问卷答题记录表(已存在)
|
||
- [ ] `psy_questionnaire_answer_detail` - 问卷答案详情表(需要创建)
|
||
|
||
### 后端Java文件
|
||
- [ ] `PsyQuestionnaireAnswerDetail.java` - 问卷答案详情实体类
|
||
- [ ] `PsyQuestionnaireAnswerDetailMapper.java` - Mapper接口
|
||
- [ ] `PsyQuestionnaireAnswerDetailMapper.xml` - MyBatis映射
|
||
- [ ] `IPsyQuestionnaireAnswerService.java` - 问卷答题服务接口
|
||
- [ ] `PsyQuestionnaireAnswerServiceImpl.java` - 问卷答题服务实现
|
||
- [ ] `PsyQuestionnaireController.java` - 问卷控制器(需要增强)
|
||
- [ ] `PsyScaleController.java` - 量表控制器(需要修改列表查询)
|
||
|
||
### 前端Vue文件
|
||
- [ ] `questionnaire/taking.vue` - 问卷答题页面
|
||
- [ ] `questionnaire/start.vue` - 开始问卷页面
|
||
- [ ] `questionnaire/scoring.vue` - 主观题评分页面
|
||
- [ ] `questionnaire/ranking.vue` - 成绩排名页面
|
||
- [ ] `scale/index.vue` - 量表管理页面(需要修改,显示问卷)
|
||
|
||
---
|
||
|
||
## 🔧 技术实现细节
|
||
|
||
### 1. 量表列表统一显示问卷
|
||
|
||
**后端修改**:
|
||
```java
|
||
// PsyScaleController.java
|
||
@GetMapping("/list")
|
||
public TableDataInfo list(PsyScale scale, @RequestParam(required = false) Boolean includeQuestionnaire)
|
||
{
|
||
startPage();
|
||
List<PsyScale> scaleList = scaleService.selectScaleList(scale);
|
||
|
||
// 如果需要包含问卷
|
||
if (includeQuestionnaire != null && includeQuestionnaire) {
|
||
List<PsyQuestionnaire> questionnaireList = questionnaireService.selectQuestionnaireList(...);
|
||
// 转换为统一的Scale格式,添加sourceType标识
|
||
// 合并到scaleList
|
||
}
|
||
|
||
return getDataTable(scaleList);
|
||
}
|
||
```
|
||
|
||
**前端修改**:
|
||
```javascript
|
||
// scale/index.vue
|
||
// 在表格中添加类型列
|
||
<el-table-column label="类型" prop="sourceType" width="100">
|
||
<template slot-scope="scope">
|
||
<el-tag v-if="scope.row.sourceType === 'questionnaire'" type="warning">问卷</el-tag>
|
||
<el-tag v-else type="primary">量表</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
```
|
||
|
||
### 2. 问卷答题流程
|
||
|
||
参考量表测评的实现:
|
||
1. 开始问卷 → 创建`PsyQuestionnaireAnswer`记录
|
||
2. 获取题目 → 根据`paper_type`获取题目列表
|
||
3. 保存答案 → 保存到`PsyQuestionnaireAnswerDetail`
|
||
4. 提交问卷 → 自动计分 + 更新排名
|
||
|
||
### 3. 自动计分逻辑
|
||
|
||
```java
|
||
// 客观题计分
|
||
private BigDecimal calculateObjectiveScore(PsyQuestionnaireItem item, AnswerDetailVO answer) {
|
||
switch(item.getItemType()) {
|
||
case "radio":
|
||
// 单选:检查选项是否正确
|
||
return checkOptionCorrect(item, answer.getOptionId()) ? item.getScore() : BigDecimal.ZERO;
|
||
case "checkbox":
|
||
// 多选:检查所有选项是否正确
|
||
return calculateMultiChoiceScore(item, answer.getOptionIds());
|
||
case "boolean":
|
||
// 判断:检查选项是否正确
|
||
return checkOptionCorrect(item, answer.getOptionId()) ? item.getScore() : BigDecimal.ZERO;
|
||
case "input":
|
||
// 填空:文本匹配
|
||
return checkTextMatch(item, answer.getAnswerText()) ? item.getScore() : BigDecimal.ZERO;
|
||
// ... 其他类型
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 排名计算
|
||
|
||
```sql
|
||
-- 更新排名
|
||
UPDATE psy_questionnaire_answer qa
|
||
SET qa.rank = (
|
||
SELECT COUNT(*) + 1
|
||
FROM psy_questionnaire_answer qa2
|
||
WHERE qa2.questionnaire_id = qa.questionnaire_id
|
||
AND (
|
||
qa2.total_score > qa.total_score
|
||
OR (qa2.total_score = qa.total_score AND qa2.submit_time < qa.submit_time)
|
||
)
|
||
)
|
||
WHERE qa.questionnaire_id = #{questionnaireId}
|
||
AND qa.status = '1'
|
||
```
|
||
|
||
---
|
||
|
||
## 📌 实施步骤
|
||
|
||
1. ✅ **创建问卷答案详情表**(SQL)
|
||
2. ⏳ **修改量表列表查询,包含问卷**(后端)
|
||
3. ⏳ **修改量表管理页面,显示问卷**(前端)
|
||
4. ⏳ **创建问卷答题功能**(后端+前端)
|
||
5. ⏳ **实现自动计分功能**(后端)
|
||
6. ⏳ **实现主观题评分功能**(后端+前端)
|
||
7. ⏳ **实现成绩排名功能**(后端+前端)
|
||
|
||
---
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
1. 问卷和量表的数据结构不同,需要统一转换
|
||
2. 组卷方式(随机、手动、混合)需要在获取题目时实现
|
||
3. 主观题评分需要权限控制
|
||
4. 排名计算要考虑性能,可能需要定时任务
|
||
5. 填空题的文本匹配需要考虑容错性
|
||
|
||
---
|
||
|
||
**创建时间**: 2025-01-XX
|
||
**最后更新**: 2025-01-XX
|
||
|