当前位置: 首页 > news >正文

简易在线考试系统 - 结对编程项目文档

简易在线考试系统 - 结对编程项目文档

项目概述

本项目是一个简易在线考试系统,支持客观题考试,包含题目管理、考生答题、自动判分三大核心功能。系统支持单选题、多选题、判断题的管理和考试,并提供限时答题、实时保存答案和自动生成成绩报告等功能。

团队成员及分工

成员A

  • 负责模块:后端核心逻辑、数据存储、路由处理
  • 具体工作
    • 设计并实现内存存储方案
    • 开发题目管理的CRUD操作
    • 实现自动判分逻辑
    • 配置Express服务器和路由
    • 解决技术问题和性能优化

成员B

  • 负责模块:前端界面、用户交互、响应式设计
  • 具体工作
    • 设计并实现系统界面
    • 开发考试页面的倒计时功能
    • 实现实时保存答案功能
    • 优化用户体验和界面美观度
    • 测试系统功能和用户流程

技术栈选择

  • 前端:HTML5、CSS3、JavaScript (ES6+)、Bootstrap 4
  • 后端:Node.js 24.5.0、Express 4.18.2
  • 存储:内存存储
  • 视图引擎:EJS 3.1.9
  • 依赖管理:npm 10.x

系统架构

整体架构

image

模块划分

  1. 题目管理模块:负责题目的增删改查操作
  2. 考生答题模块:负责考试界面和答题功能
  3. 自动判分模块:负责答案判分和成绩生成

详细设计

1. 数据存储模块

设计思路(成员A负责)

  • 数据结构设计:使用内存数组存储题目、考试和答案数据
  • 数据操作接口:实现统一的数据操作接口,支持增删改查
  • 数据初始化:提供示例数据,方便测试和演示

核心实现

// 数据存储结构
let questions = [];  // 题目数组
let exams = [];      // 考试数组
let answers = [];    // 答案数组
let questionId = 1;  // 题目ID自增器
let examId = 1;      // 考试ID自增器
let answerId = 1;    // 答案ID自增器// 数据操作接口
const db = {// 查询所有数据all: function(query, callback) {// 根据查询类型返回对应的数据// ...},// 查询单条数据get: function(query, params, callback) {// 根据条件查询单条数据// ...},// 执行写操作run: function(query, params, callback) {// 执行数据插入、更新、删除操作// ...}
};// 示例数据初始化
function initSampleData() {// 初始化3道示例题目// ...
}

2. 路由处理模块

设计思路(成员A负责)

  • 路由规划:设计RESTful风格的路由
  • 请求处理:处理前端的各种请求
  • 业务逻辑:实现核心业务逻辑,如题目管理、答案判分等

核心实现

// 题目管理路由
router.get('/questions', (req, res) => { /* ... */ });  // 获取题目列表
router.get('/questions/add', (req, res) => { /* ... */ });  // 添加题目页面
router.post('/questions/add', (req, res) => { /* ... */ });  // 添加题目
router.get('/questions/edit/:id', (req, res) => { /* ... */ });  // 编辑题目页面
router.post('/questions/edit/:id', (req, res) => { /* ... */ });  // 更新题目
router.get('/questions/delete/:id', (req, res) => { /* ... */ });  // 删除题目// 考试路由
router.get('/exam', (req, res) => { /* ... */ });  // 考试页面
router.post('/exam/submit', (req, res) => { /* ... */ });  // 提交答案// 答案判分逻辑
function judgeAnswers(studentAnswers, questions) {// 实现不同类型题目的判分逻辑// ...
}

3. 前端界面模块

设计思路(成员B负责)

  • 界面设计:采用Bootstrap 4实现响应式界面
  • 用户交互:实现倒计时、实时保存等功能
  • 体验优化:确保界面美观、操作流畅

核心实现

考试页面
<!-- 倒计时功能 -->
<div class="countdown">剩余时间:<span id="time">30:00</span></div><!-- 题目展示 -->
<% questions.forEach((question, index) => { %><div class="card mb-4"><div class="card-header"><!-- 题目类型和分数 --></div><div class="card-body"><p class="card-text"><%= question.content %></p><!-- 判断题选项 --><% if (question.type === 'judgment') { %><div class="form-group"><div class="form-check"><input type="radio" name="question-<%= question.id %>" value="A"><label>A: 正确</label></div><div class="form-check"><input type="radio" name="question-<%= question.id %>" value="B"><label>B: 错误</label></div></div><% } %><!-- 选择题选项 --><% else if (question.options) { %><div class="form-group"><!-- 选项列表 --></div><% } %></div></div>
<% }); %><!-- 实时保存答案 -->
<script>function saveAnswers() {// 收集表单数据// 存储到localStorage}// 监听表单变化document.getElementById('examForm').addEventListener('change', saveAnswers);
</script>

image

image

成绩报告页面
<!-- 成绩汇总 -->
<div class="card mb-4"><div class="card-body"><h5 class="card-title">成绩汇总</h5><p class="card-text">得分:<%= score %> / <%= totalScore %></p><p class="card-text">正确率:<%= percentage %>%</p><div class="progress"><div class="progress-bar" style="width: <%= percentage %>%"><%= percentage %></div></div></div>
</div><!-- 错题解析 -->
<% if (wrongQuestions && wrongQuestions.length > 0) { %><h2 class="mb-4">错题解析</h2><% wrongQuestions.forEach((question, index) => { %><div class="card mb-4"><!-- 错题详情 --></div><% }); %>
<% } %>

image

核心算法详解

1. 倒计时算法(成员B负责)

算法思路

  • 使用setInterval函数每秒递减倒计时
  • 实时更新页面显示
  • 时间结束时自动提交表单

实现代码

let timeLeft = 30 * 60; // 30分钟
const timeElement = document.getElementById('time');function updateCountdown() {const minutes = Math.floor(timeLeft / 60);const seconds = timeLeft % 60;timeElement.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;if (timeLeft <= 0) {clearInterval(countdownInterval);document.getElementById('examForm').submit();}timeLeft--;
}const countdownInterval = setInterval(updateCountdown, 1000);

2. 实时保存算法(成员B负责)

算法思路

  • 监听表单变化事件
  • 收集表单数据并序列化为JSON
  • 存储到localStorage
  • 页面加载时恢复答案

实现代码

// 保存答案
function saveAnswers() {const formData = new FormData(document.getElementById('examForm'));const answers = {};formData.forEach((value, key) => {if (answers[key]) {if (Array.isArray(answers[key])) {answers[key].push(value);} else {answers[key] = [answers[key], value];}} else {answers[key] = value;}});localStorage.setItem('examAnswers', JSON.stringify(answers));
}// 加载答案
window.addEventListener('load', () => {const savedAnswers = localStorage.getItem('examAnswers');if (savedAnswers) {const answers = JSON.parse(savedAnswers);Object.keys(answers).forEach(key => {const values = Array.isArray(answers[key]) ? answers[key] : [answers[key]];values.forEach(value => {const input = document.querySelector(`input[name="${key}"][value="${value}"]`);if (input) {input.checked = true;}});});}
});

3. 自动判分算法(成员A负责)

算法思路

  • 遍历所有题目
  • 根据题目类型执行不同的判分逻辑
  • 计算总分和正确率
  • 收集错题信息

实现代码

function judgeAnswers(answers, questions) {let score = 0;let totalScore = 0;let wrongQuestions = [];questions.forEach(question => {totalScore += question.score;let studentAnswer = answers[`question-${question.id}`];// 处理多选题答案if (question.type === 'multiple' && Array.isArray(studentAnswer)) {studentAnswer = studentAnswer.sort().join('');}if (studentAnswer === question.answer) {score += question.score;} else {wrongQuestions.push({id: question.id,content: question.content,type: question.type,options: question.options,studentAnswer: Array.isArray(answers[`question-${question.id}`]) ? answers[`question-${question.id}`].join(',') : answers[`question-${question.id}`],correctAnswer: question.answer,explanation: question.explanation});}});return {score,totalScore,percentage: Math.round((score / totalScore) * 100),wrongQuestions};
}

系统测试

功能测试

1. 题目管理测试

测试项 预期结果 实际结果 状态
添加单选题 题目成功添加到列表 通过
添加多选题 题目成功添加到列表 通过
添加判断题 题目成功添加到列表 通过
编辑题目 题目信息成功更新 通过
删除题目 题目成功从列表中移除 通过

image

2. 考试功能测试

测试项 预期结果 实际结果 状态
倒计时功能 30分钟倒计时,结束自动提交 通过
实时保存答案 答案自动保存到本地存储 通过
单选题答题 正确显示选项,可选择答案 通过
多选题答题 正确显示选项,可选择多个答案 通过
判断题答题 显示正确/错误选项 通过

3. 自动判分测试

测试项 预期结果 实际结果 状态
单选题判分 正确判断答案是否正确 通过
多选题判分 正确判断答案是否正确(忽略顺序) 通过
判断题判分 正确判断答案是否正确 通过
成绩计算 正确计算得分和正确率 通过
错题解析 生成包含错题和解析的报告 通过

性能测试

测试项 预期结果 实际结果 状态
页面加载时间 < 2秒 通过
响应速度 操作响应时间 < 1秒 通过
内存使用 内存占用 < 100MB 通过

1. 判断题选项显示问题

问题:判断题没有显示选项,用户无法答题。

解决方案:在前端代码中为判断题添加固定的"正确"和"错误"选项。

2. 多选题答案判断错误

问题:多选题答案顺序不同时判断为错误。

解决方案:在判分时对多选题答案进行排序后再比较。

3. 端口占用问题

问题:启动服务器时遇到端口3000被占用。

解决方案:使用netstat命令查找占用端口的进程,然后使用taskkill命令终止该进程。

项目成果

功能实现

  1. 题目管理:支持单选、多选、判断题的增删改查
  2. 考生答题:实现限时答题、实时保存答案功能
  3. 自动判分:考试结束后立即计算得分,生成包含错题解析的成绩报告
  4. 用户体验:响应式设计,界面美观,操作流畅

技术亮点

  1. 内存存储方案:采用JavaScript对象存储数据,无需外部数据库,简化部署
  2. 实时保存:使用localStorage实现答案自动保存,防止意外丢失
  3. 智能判分:支持不同类型题目的判分逻辑,准确计算成绩
  4. 模块化设计:代码结构清晰,前后台分离,易于维护

结对编程体会

成员A的体会

通过本次结对编程,我深入学习了Node.js和Express框架,掌握了内存存储方案的实现方法。在开发过程中,我负责后端核心逻辑,包括数据存储、路由处理和自动判分算法。遇到问题时,我和成员B积极沟通,共同寻找解决方案,提高了问题解决能力和团队协作能力。

成员B的体会

在本次项目中,我负责前端界面开发,使用Bootstrap 4实现了响应式设计。我学习了如何实现倒计时功能和实时保存答案功能,提高了前端开发技能。通过与成员A的合作,我了解了前后端交互的流程,增强了对全栈开发的理解。

团队协作经验

  1. 明确分工:根据各自的优势和兴趣划分任务,提高开发效率
  2. 有效沟通:定期交流进度和问题,及时解决技术难题
  3. 互相学习:从对方身上学习不同领域的知识和技能
  4. 共同成长:通过合作完成项目,积累了宝贵的开发经验

未来改进方向

  1. 数据持久化:使用文件存储或数据库替代内存存储,保证数据不丢失
  2. 用户系统:添加用户注册、登录功能,支持多用户考试
  3. 考试管理:支持创建和管理多个考试,设置不同的考试参数
  4. 统计分析:添加考试数据统计和分析功能,生成详细的成绩报表
  5. 安全性:加强系统安全,防止作弊和恶意攻击

总结

本项目成功实现了一个简易在线考试系统,包含题目管理、考生答题和自动判分三大核心功能。通过结对编程的方式,我们充分发挥了各自的优势,共同完成了系统的开发。项目的成功不仅是技术实现的成果,也是团队协作的结晶。

http://www.jsqmd.com/news/668191/

相关文章:

  • Token消耗激增的根源及系统性优化方案:用户消耗远超购买量
  • 【PolarCTF】x64
  • FastGPT连接OneAPI实战:如何用一套密钥管理多个大模型(通义千问、ChatGLM等)
  • 2026青岛成人高考机构排行榜:Top5深度测评,帮你避开选机构的“坑” - 商业科技观察
  • 3K 行代码造一个越用越聪明的 AI Agent:GenericAgent 登顶 GitHub Trending
  • 用FFmpeg无损剪辑H.264视频翻车实录:从‘-c copy’报错到成功导出MP4的完整避坑指南
  • Python在图片上画圆形:从入门到实战
  • 3步恢复Windows 11 LTSC微软商店:完整应用生态一键安装指南
  • 【Linux从入门到精通】第6篇:管道符、重定向与通配符——命令行效率的核心秘诀
  • Windows服务器运维:如何用mstsc命令和.rdp配置文件打造你的专属远程桌面管理库
  • 【传播模型】CoVeni计算并可视化了病毒附Matlab代码
  • 别光会binwalk了!CTF MISC实战中这5个冷门但好用的文件分析工具,帮你快速定位flag
  • 三步搞定Windows ADB驱动安装:告别繁琐配置,专注Android开发
  • 阿里云盘的FatalError
  • Win11Debloat:三步彻底清理Windows系统,让电脑重获新生
  • 【数字信号调制】自适应调制解调通信系统误码率仿真【含Matlab源码 15364期】
  • LangGraph 并行执行优化:如何提升多智能体任务处理效率?
  • 告别Tomcat:Spring Boot应用改造为纯War包,适配宝兰德等商用中间件全指南
  • Python在图片上画多边形:从简单轮廓到复杂区域标注
  • **发散创新:用Python实现因果推理在推荐系统中的落地应用**在当今数据
  • 【AI面试八股文 Vol.1.1 | 专题4:Conditional Edge】Conditional Edge:动态路由分支逻辑实现
  • SolidWorks参数化设计避坑指南:为什么你的VBA宏跑一次就报错?
  • 家庭网络总断网?可能是你家的路由器接错了!用环路检测功能快速排查
  • Unity Magica Cloth:从入门到精通,打造次世代角色动态服饰
  • 别再只用MD5了!聊聊PBKDF2如何用‘盐’和‘慢炖’保护你的用户密码
  • OpenClaw怎么搭建?2026年4月云端大模型Coding Plan配置指南
  • 如何快速掌握CREST:药物设计中分子构象采样的完整指南
  • NVIDIA Profile Inspector 终极指南:解锁隐藏设置,轻松优化游戏性能
  • 2026年降AI后重新检测还是偏高怎么处理:多轮降AI完整攻略
  • Orwell Dev-C++ 和 Embarcadero Dev-C++ 哪个更好