1. 项目概述
这个高中信息技术课程在线测试系统小程序,是我去年带队完成的一个校企合作项目。当时本地一所重点高中急需一套能适配Android设备的移动端考试系统,要求支持课堂随测、单元考试和错题集功能。我们基于SpringBoot+Vue技术栈,用微信小程序作为前端载体,最终交付的这套系统不仅满足了基础考试需求,还加入了智能组卷、答题过程回放等特色功能。
从技术实现角度看,这个项目典型地体现了教育类小程序的特点:既要保证在高并发考试场景下的稳定性,又要兼顾移动端操作的便捷性。系统后台采用微服务架构,支持远程调试和动态扩容,前端则针对Android平台的微信客户端做了深度优化。下面我就从设计思路到具体实现,把整个项目的关键技术点拆解给大家。
2. 核心需求解析
2.1 教育场景的特殊性
高中信息技术课程与其他学科不同,其考试内容往往包含实操题型(如流程图绘制、简单代码填空等)。我们在需求分析阶段发现几个关键痛点:
- 教师需要快速创建包含图文混排题目的试卷
- 考试期间可能突发网络中断,需要本地缓存作答记录
- 客观题需要即时批改反馈
- 主观题要支持拍照上传作答
这些需求直接影响了我们的技术选型。比如为解决离线作答问题,小程序端采用了localStorage+定时同步机制;为支持复杂题型,后台使用Quill富文本编辑器存储题目内容。
2.2 技术栈选型考量
为什么选择SpringBoot+Vue的组合?主要基于三点判断:
- 学校原有教务系统采用Java技术栈,SpringBoot可以无缝对接
- Vue的响应式特性非常适合动态渲染各类题型组件
- 微信小程序对WebSocket的良好支持,便于实现实时监考功能
这里有个实际踩坑经验:初期考虑过用Flutter实现跨平台,但测试发现部分Android机型在渲染复杂富文本时存在性能问题,最终改用小程序原生方案。
3. 系统架构设计
3.1 后端微服务划分
code复制[考试服务]
├── 试卷管理(Spring Data JPA)
├── 考试监控(WebSocket)
├── 智能阅卷(规则引擎)
└── 数据分析(Elasticsearch)
[用户服务]
├── 权限控制(Spring Security)
└── 行为日志(AOP切面)
[文件服务]
├── 图片上传(七牛云SDK)
└── 文档导出(POI+TBS)
每个服务都封装为独立Docker容器,通过Nginx实现负载均衡。特别要注意的是考试服务的线程池配置,我们的经验公式:
java复制// 根据服务器核心数动态设置
int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
3.2 小程序端关键技术
-
页面栈管理:采用分包加载策略,将考试流程拆分为:
- 考前准备页(预加载资源)
- 答题主页面(常驻内存)
- 提交确认页(独立分包)
-
数据同步机制:
javascript复制// 双保险存储策略
function saveAnswer(answer) {
wx.setStorageSync(answer.questionId, answer); // 本地持久化
uploadAnswerWhenOnline(answer); // 网络恢复后自动提交
}
- Android适配要点:
- 禁用下拉刷新(防止误触)
- 动态调整textarea高度(适配不同输入法)
- 使用cover-view覆盖原生组件
4. 核心功能实现细节
4.1 智能组卷算法
教师端可以设置组卷规则(知识点分布、难度系数等),系统自动从题库筛选题目。核心算法如下:
java复制public List<Question> generatePaper(PaperRule rule) {
return questionRepository.findAll()
.stream()
.filter(q -> q.getDifficulty() <= rule.getMaxDifficulty())
.collect(Collectors.groupingBy(Question::getKnowledgePoint))
.values()
.stream()
.flatMap(list -> list.stream().limit(rule.getPerPointCount()))
.collect(Collectors.toList());
}
实际使用中发现需要增加随机性因子,否则容易产生固定题目组合。改进方案是在筛选后调用Collections.shuffle()。
4.2 答题过程回放
这个创新功能使用rxjs记录操作事件流:
typescript复制const event$ = fromEvent(document, 'click').pipe(
map(e => ({
type: 'CLICK',
target: e.target.id,
timestamp: Date.now()
}))
);
// 存储为可回放的JSON序列
const replayData = await event$.pipe(
bufferTime(5000),
filter(events => events.length > 0)
).toPromise();
回放时通过虚拟DOM重演操作序列,帮助教师分析学生的答题思路。
5. 性能优化实践
5.1 首屏加载加速
针对Android微信环境做的专项优化:
- 关键资源预加载:在app.onLaunch时预请求试卷模板
- 图片懒加载:使用IntersectionObserver API
- 接口合并:将多个初始化请求合并为batch请求
实测数据:
| 优化前 | 优化后 |
|---|---|
| 2.8s | 1.2s |
5.2 高并发应对方案
模拟考试场景下可能遇到瞬时高峰,我们采用三级缓存策略:
- 本地缓存:Redis存储热点试卷数据
- 分布式锁:Redisson防止重复提交
- 降级方案:当压力过大时切换为简版试卷
压力测试结果(单机配置:4核8G):
| 并发数 | 平均响应时间 |
|---|---|
| 500 | 238ms |
| 1000 | 417ms |
| 2000 | 812ms(触发降级) |
6. 典型问题排查实录
6.1 Android输入法遮挡问题
现象:部分机型答题时输入法会遮挡文本框
解决方案:
css复制.answer-area {
position: fixed;
bottom: env(safe-area-inset-bottom);
}
配合JS动态计算可视区域高度:
javascript复制wx.onWindowResize(res => {
this.setData({ windowHeight: res.windowHeight });
})
6.2 微信缓存更新延迟
遇到代码更新后部分用户仍看到旧版本,我们的处理方案:
- 在app.js中加入版本校验逻辑
- 检测到旧版本时提示强制刷新
- 后端接口增加版本兼容层
javascript复制// 版本检查逻辑
const currentVer = '1.2.0';
wx.getUpdateManager().onCheckForUpdate(res => {
if (res.hasUpdate) {
wx.showModal({title: '发现新版本'});
}
});
7. 项目扩展方向
这套系统在实际部署后,根据师生反馈我们又迭代了几个实用功能:
- 错题智能推荐:基于协同过滤算法,向学生推荐相似题目
python复制# 简单的基于用户的CF算法
def recommend_wrong_questions(user):
similar_users = find_similar_users(user)
return get_common_wrong_questions(similar_users)
- 课堂实时问答:教师可以随时发起临时提问,学生扫码参与
- 编程题自动评测:集成Docker沙箱运行学生提交的代码
有个值得分享的实现细节:编程题评测时,我们使用容器限流策略防止恶意代码:
bash复制docker run --ulimit cpu=10:10 -m 100MB code-sandbox
这个项目让我深刻体会到教育信息化产品的特殊性——既要保证技术的可靠性,又要符合教学场景的实际使用习惯。比如最初我们设计的自动交卷功能,在实际使用中发现需要增加"最后5分钟"提醒,这就是从真实课堂需求中获得的改进点。
