尧图精选

基于Spring Boot+Vue的在线问卷调查系统设计与实现

🕒 发布时间:2026/9/19 17:26:26 📁 来源:尧图网络
简介一份基于Spring Boot与Vue的在线问卷调查系统毕业设计论文文档面向计算机相关专业毕业生及需要完成类似课题的开发者提供从需求分析、系统设计到编码实现、测试评估的完整论述。论文核心解决问卷调查信息管理不规范、效率低等问题涵盖问卷管理、用户管理、题目管理、问卷调查管理、新闻资讯管理等模块并使用MySQL数据库、Java语言及Spring Boot框架完成技术落地。资源为1个doc文档容量约4.04MB包含完整论文正文方便查阅、修改与二次编辑。目前已有198人浏览学习适合用于毕业设计参考、论文结构梳理或技术方案借鉴。文档内含系统架构说明、数据库设计思路、后端API与前端页面开发要点还能了解系统测试与稳定性评估方法为独立完成类似项目提供有力支撑。1. 在线问卷调查系统从毕业设计到可落地的 Spring Boot Vue 全栈项目问卷系统的难点从来不在「能填」而在「怎么设计题目、怎么控制发布状态、怎么保证一次作答不重复提交」。这套基于 Spring Boot Vue 的在线问卷调查系统覆盖了问卷管理、题目管理、用户管理、问卷调查记录和新闻资讯模块是一个完整的前后端分离案例。后端用 Spring Boot 提供 REST API前端用 Vue 做页面交互数据落在 MySQL适合正在做 Java 方向毕业设计的人也适合想快速搭一套内部调研工具的开发者。它的表结构设计——问卷表、题目表、答卷记录表——足够简洁稍加扩展就能接进真实业务。下面从数据模型开始拆解这套系统的实现路径。2. 数据模型先行问卷、题目与答卷记录的表结构设计在线问卷调查系统的核心不在页面而在数据库里那几张表的关系。问卷与题目是一对多问卷与答卷记录是一对多用户与答卷记录也是一对多。这三层关系理顺了后面写接口和页面都会顺畅很多。2.1 问卷表状态、时长与结束语的设计问卷表对应实体是「问卷」字段在论文中给出了明确设计建表 SQL 如下CREATE TABLE exampaper ( id int(20) NOT NULL AUTO_INCREMENT COMMENT 主键, exampaper_name varchar(200) NOT NULL COMMENT 问卷名称, exampaper_date int(11) NOT NULL COMMENT 时长分钟, exampaper_jieshuyu varchar(255) DEFAULT NULL COMMENT 结束语, exampaper_state tinyint(1) DEFAULT 0 COMMENT 问卷状态0-草稿 1-发布 2-关闭, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT问卷表;字段里值得注意的有两个。exampaper_state用 tinyint 而不是 varchar 存状态是为了方便做状态流转判断后端只比对数值即可exampaper_jieshuyu允许为空因为问卷创建初期不需要立刻填写结束语可以发布前再补。exampaper_date设定的时长不是计时上限而是提醒阈值——用户进入问卷后前端根据这个值启动倒计时提醒但不强制交卷避免数据丢失。create_time用数据库默认值生成后端插入时不需要手动赋值。2.2 题目表与问卷调查记录表一对多关系落到字段上题目表在设计上必须携带所属问卷的 ID否则无法建立关联。问卷调查记录表则是每次作答的「流水账」记录谁在什么时间答了哪份问卷。两张表的设计如下CREATE TABLE exampaper_question ( id int(20) NOT NULL AUTO_INCREMENT COMMENT 题目ID, exampaper_id int(20) NOT NULL COMMENT 所属问卷ID, question_name varchar(255) NOT NULL COMMENT 试题名称, question_type tinyint(1) DEFAULT 1 COMMENT 试题类型1-单选 2-多选 3-填空, question_option text COMMENT 选项内容用JSON数组存储, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_exampaper_id (exampaper_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目表; CREATE TABLE exampaper_record ( id int(20) NOT NULL AUTO_INCREMENT COMMENT 问卷调查记录ID, record_no varchar(32) DEFAULT NULL COMMENT 问卷调查编号, user_id int(20) NOT NULL COMMENT 问卷调查用户ID, exampaper_id int(20) NOT NULL COMMENT 所属问卷ID, answer_content text COMMENT 答卷内容JSON格式存储, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT问卷调查记录表;题目的question_option用 text 存 JSON 数组比单独建一张选项表更省事。问卷系统的题目选项一般是固定几个文本不需要对选项做单独检索或统计JSON 存储能减少一次关联查询。答题记录表的record_no是业务编号格式可以定为「问卷ID 用户ID 时间戳」用于前端展示查询answer_content存放用户提交的答案映射格式为{题目ID: 答答案}这样无论题目怎么增删记录结构都不用变。2.3 用户表最小集认证所需字段与扩展边界用户表在论文实体属性中给出了较多字段——账户、密码、姓名、性别、手机号、照片、身份证号。实际做系统时身份证号这类敏感字段能不放就不放尤其毕设阶段没必要引入额外合规负担。建议保留核心字段CREATE TABLE sys_user ( id int(20) NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) NOT NULL COMMENT 账户, password varchar(100) NOT NULL COMMENT 密码BCrypt加密, real_name varchar(50) DEFAULT NULL COMMENT 用户姓名, sex varchar(10) DEFAULT NULL COMMENT 性别, phone varchar(20) DEFAULT NULL COMMENT 手机号, avatar varchar(255) DEFAULT NULL COMMENT 照片, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;密码字段长度设为 100是因为 Spring Security 的 BCrypt 加密结果长度约为 60 字符如果按直觉设成 varchar(50)加密后写入会报数据过长错误。username加唯一索引这是登录查询的命脉字段创建时就要预防重复注册。3. Spring Boot 后端实现Controller、Service 与 MyBatis 的工程落地后端部分的核心不是代码量而是三层结构的清晰度。Controller 只接收参数和返回结果Service 处理业务逻辑Mapper 层负责 SQL 操作。以问卷管理模块为例把这套结构完整走一遍。3.1 问卷管理接口分页查询与状态过滤问卷列表页最常见的需求是分页展示并支持按状态筛选。Controller 层代码RestController RequestMapping(/api/exampaper) public class ExampaperController { Autowired private ExampaperService exampaperService; GetMapping(/page) public Result page(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String exampaperName, RequestParam(required false) Integer exampaperState) { PageInfoExampaper pageInfo exampaperService.findPage(pageNum, pageSize, exampaperName, exampaperState); return Result.success(pageInfo); } }Service 层对应实现Service public class ExampaperServiceImpl implements ExampaperService { Autowired private ExampaperMapper exampaperMapper; Override public PageInfoExampaper findPage(Integer pageNum, Integer pageSize, String exampaperName, Integer exampaperState) { PageHelper.startPage(pageNum, pageSize); ExampaperQuery query new ExampaperQuery(); query.setExampaperName(exampaperName); query.setExampaperState(exampaperState); ListExampaper list exampaperMapper.selectByCondition(query); return new PageInfo(list); } }PageHelper.startPage(pageNum, pageSize)是 MyBatis 分页插件的入口写在这行之后的第一个查询会自动追加 limit 语句。这里有个常见的坑startPage 必须紧跟查询语句中间不能插入其他查询操作否则分页会作用到错误的 SQL 上。exampaperName和exampaperState都允许为空查询条件里要做动态 SQL 判断避免空值参与拼接导致查不到数据。3.2 新增与编辑参数校验和状态判断创建问卷时只保存名称、时长和状态字段题目先不处理。这里关键在于状态机的约束——只有处于草稿状态的问卷才能编辑题目和基础信息已发布的问卷只允许关闭不允许再改结构。PostMapping(/save) public Result save(RequestBody Exampaper exampaper) { if (exampaper.getId() null) { exampaper.setExampaperState(0); exampaperMapper.insert(exampaper); } else { Exampaper dbPaper exampaperMapper.selectById(exampaper.getId()); if (dbPaper.getExampaperState() ! 0) { return Result.error(当前状态不允许编辑); } exampaperMapper.updateById(exampaper); } return Result.success(); }新增和编辑共用一个接口通过id是否为空来区分。如果前端传入的 id 和数据库中的状态不一致——比如数据库里已经是发布状态但请求试图修改问卷名称——后端必须拦截只靠前端按钮隐藏没有任何意义。3.3 题目批量保存事务与级联删除编辑问卷题目时常见的做法是前端一次提交全部题目后端先删后插。先按问卷 ID 删除旧题目再批量插入新题目。这个操作必须加Transactional否则删除成功但插入失败时题库会变成空表对用户来说等于问卷的题全丢了。Transactional(rollbackFor Exception.class) public void saveQuestions(Long exampaperId, ListQuestion questions) { exampaperQuestionMapper.deleteByExampaperId(exampaperId); for (Question q : questions) { q.setExampaperId(exampaperId); exampaperQuestionMapper.insert(q); } }rollbackFor Exception.class指定了异常即回滚不加这个参数的话Spring 默认只在运行时异常时回滚检查异常不会触发回滚数据就可能处在不一致状态。批量插入也可以在 Mapper 里用 foreach 实现减少数据库连接往返次数。3.4 登录与权限拦截器判断管理员与普通用户系统分了管理员和普通用户两个角色最简单的实现方式是拦截器 角色标记位不需要把 Spring Security 全套引入毕设或小型系统这样足够清晰Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getMethod().equals(OPTIONS)) { return true; } HttpSession session request.getSession(); User loginUser (User) session.getAttribute(loginUser); if (loginUser null) { response.setStatus(401); return false; } return true; } }Controller 中通过request.getSession().getAttribute(loginUser)判断当前用户角色管理员接口再校验user.getRole()是否为管理员。这种方案的取舍是非登录用户会被统一拦截到 401前端 axios 响应拦截器里捕获 401 后跳转登录页但登录页本身要放行否则会死循环。4. Vue 前端实现问卷列表到答题页的路由与状态管理前端的核心页面是问卷列表、答题页面和答卷记录查看。Vue 2 的 Options API 写法在这类项目里最常用代码结构直观生态资料也多网上搜到的组件和插件基本都兼容。4.1 问卷列表页axios 请求与分页渲染问卷列表页根据登录角色的不同展示不同功能——管理员看到的是「编辑」「发布」「关闭」操作按钮普通用户看到的是「参与答题」。页面初始化时调用分页接口export default { data() { return { pageNum: 1, pageSize: 10, total: 0, exampaperList: [], exampaperName: , exampaperState: undefined }; }, created() { this.loadPage(); }, methods: { async loadPage() { const res await axios.get(/api/exampaper/page, { params: { pageNum: this.pageNum, pageSize: this.pageSize, exampaperName: this.exampaperName, exampaperState: this.exampaperState } }); this.exampaperList res.data.data.list; this.total res.data.data.total; } } };params传参会自动进行 URL 编码不需要手动拼接查询字符串。created钩子里调用加载方法确保页面渲染前数据已经就绪。分页组件切换页码时把pageNum改成目标值并再次调用loadPage()。注意不要在data里把exampaperState定义为空字符串否则请求参数会带上exampaperState后端接收时可能因类型转换报错。4.2 答题页动态渲染根据题目类型加载组件答题页拿到问卷 ID 后调用题目查询接口获取题目列表。题目类型是单选、多选、填空三种前端使用v-if判断组件类型template div v-for(question, index) in questionList :keyquestion.id div classquestion-title{{ index 1 }}. {{ question.questionName }}/div el-radio-group v-ifquestion.questionType 1 v-modelanswers[question.id] el-radio v-foropt in question.optionList :keyopt :labelopt{{ opt }}/el-radio /el-radio-group el-checkbox-group v-else-ifquestion.questionType 2 v-modelanswers[question.id] el-checkbox v-foropt in question.optionList :keyopt :labelopt{{ opt }}/el-checkbox /el-checkbox-group el-input v-else-ifquestion.questionType 3 v-modelanswers[question.id] typetextarea placeholder请输入你的回答/el-input /div /templateanswers是用题目 id 做 key 的对象answers {}初始化运行时根据题目 id 动态添加属性。这样设计的好处是提交时直接把整个answers对象序列化成 JSON后端存进answer_content字段不需要前端额外组装。4.3 答题时长控制与提交倒计时与防重复时长提醒放在问卷页的路由守卫里beforeRouteLeave(to, from, next) { if (this.timer) { clearInterval(this.timer); this.timer null; } next(); }进入问卷时读取exampaperDate字段启动一个 setInterval 倒计时页面离开时清除定时器避免内存泄漏。提交时把问卷 ID 和答案对象一起传给后端后端检查该用户是否有这条问卷的已提交记录如果存在则拒绝保存并提示「请勿重复提交」。5. 发布前验证用测试用例和服务状态机守住问卷的可用性系统写完到交付之间最有价值的验证方式不是对着页面点点点而是把关键流程整理成可执行的测试用例同时把问卷的状态流转逻辑明确到代码里防止后面接手的人把状态改乱。5.1 测试场景设计登录、答题、结果校验下面是一组覆盖率较高的测试用例表按照这个顺序去做基本能跑通日常路径用例编号测试场景操作步骤预期结果TC-01管理员登录输入正确账号密码跳转后台首页显示问卷管理菜单TC-02管理员创建问卷填写名称及时长点击保存列表出现「草稿」状态的新问卷TC-03管理员编辑题目进入草稿问卷添加单选/多选/填空各一题题目数显示为 3重复进入后数据仍在TC-04管理员发布问卷点击发布按钮用户端问卷列表可见该问卷TC-05用户提交问卷填写全部题目后点提交跳转完成页显示结束语「感谢参与」TC-06用户重复提交再次进入该问卷提交答案后端返回「请勿重复提交」不写入新记录TC-07用户查看记录进入「问卷调查记录」菜单展示历史提交时间及问卷名称测试中要注意「编辑题目后发布」的顺序问题。发布完成后再去修改题目用户端正在作答的问卷就会出现题目错位——用户已经答了第 1 题后端数据库中第 1 题的名称变了但答案 ID 对应的还是旧题目。这就是为什么要在后端锁死编辑权限只允许修改草稿状态的问卷。压测场景下问卷通常不要求极高的并发如果预计参与人数破千数据库索引的重点放在exampaper_record表的exampaper_id和user_id联合索引上防止重复提交校验的查询拖慢写入。5.2 状态机的边界处理草稿、发布到关闭的生命周期问卷状态不能只是「改个数字」要设计成不可逆的状态流转。从草稿到发布允许撤回草稿但保留原答案从发布到关闭拒绝编辑和删除只保留查看统计的权限。用枚举管理状态字段的语义public enum ExampaperState { DRAFT(0, 草稿), PUBLISHED(1, 发布), CLOSED(2, 关闭); private final int code; private final String desc; ExampaperState(int code, String desc) { this.code code; this.desc desc; } }实际开发时把枚举替换成「状态常量类」也能运行但枚举的好处是编译期就能发现状态值写错的问题。关闭问卷后用户端列表不再展示「参与答题」入口已提交的答卷保留在记录表中管理员仍然能查看结果。这套生命周期设计解决了在线问卷最容易出现的「已经收集了数据却不小心被改坏」的故障。写完后用 TC-04 到 TC-07 跑一遍再手动验证一下「发布后试图编辑」的拦截是否正确这份系统就可以从毕设演示平滑过渡到小范围的正式使用了。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →