
简介这是一套基于Spring Boot构建的在线问卷调查系统源码项目命名为questionnaire-master面向Java后端及全栈学习者覆盖用户注册登录、问卷模板管理、在线创建编辑、社交网络分享、结果图表分析与个人中心等完整业务闭环。资源共294个文件包含100个java源码、26个html页面、26个js脚本、8个css样式、4个sql数据库脚本以及75个gif演示图压缩包整体37.17MB结构清晰便于按模块阅读。项目集成了Spring Security认证授权、Spring Data JPA数据访问、RESTful API接口设计并附带Dockerfile、mavenwrapper等工程化配置适合作为学习Spring Boot主流技术栈的综合实战项目。已有269人学习下载若想通过一个完整项目串联用户权限、数据建模、接口开发与前后端协作这份源码能提供直观参考。 拿到这套“基于Spring Boot的在线问卷调查系统源码”我先说句实在话问卷系统是Java Web练手项目里被低估的好东西。它既有增删改查的常规操作又有动态表单、数据统计、权限控制这些进阶点而且业务模型足够直观不像电商系统那样上来就被订单状态机绕晕。这篇文章我就把整套源码从技术选型到数据库设计再到核心代码实现从里到外拆一遍你拿过去既能当课程设计参考也能当Spring Boot入门到进阶的实战教案。如果你是刚学完SSM、想看看Spring Boot到底怎么组织项目的人或者正在准备毕设、需要一套能讲清楚原理的源码这篇拆解都适合你。我尽量不说废话直接把每个环节的“为什么这样做”“踩过什么坑”讲透。1. 项目概述与技术选型为什么是Spring Boot1.1 这个系统到底解决了什么问题在线问卷调查的老需求一直没变创建问卷、发布链接、用户填写、回收数据、查看统计。早年间用JSPServlet写一套流程下来代码量惊人光是处理表单参数就得写一堆request.getParameter。换成Spring Boot之后整个事情被极大简化一个RestController加几个实体类就能把核心接口撑起来。这套源码的价值点在于它把这个经典需求完整落地了。管理员可以维护问卷的增删改查可以针对单选题、多选题、填空题、评分题做动态表单渲染用户提交后系统自动完成数据回收和图表统计。它不是一个只跑通CRUD的玩具而是把“动态表单”和“统计报表”这两个最麻烦的点都做到了可运行的程度所以我建议你拿到源码后第一件事不是改功能而是先把整个数据流转跑一遍理解核心链路。1.2 技术栈选型背后的取舍从源码的pom.xml和目录结构能看出这套系统选型非常务实Spring Boot负责自动装配和应用生命周期管理MyBatis-Plus负责持久层操作MySQL做数据存储前端用Thymeleaf模板引擎配合Bootstrap和原生JavaScript完成页面交互统计图表用了ECharts或Chart.js这一类的开源库。这里有个值得展开的取舍点为什么不用前后端完全分离因为问卷系统的核心用户是“非技术背景的运营和管理人员”他们的核心诉求是打开浏览器就能用部署越简单越好。前后端分离意味着需要额外处理跨域、Token鉴权、前端构建部署工程复杂度翻倍但对这类系统来说收益并不明显。用Thymeleaf做服务端渲染天然避开了跨域问题后端接口和页面在同一个应用里部署时一个jar包搞定运维成本极低。另一个值得玩味的是MyBatis-Plus的选择。纯MyBatis要手写大量XML映射JPA虽然省事但复杂查询不够直观。MyBatis-Plus在两者之间取了个平衡单表操作直接调用内置方法多表统计查询用注解SQL也能搞定对问卷这种以单表CRUD和基础聚合为主的业务非常契合。如果你的统计需求非常复杂这套代码也可以平滑切换到XML自定义映射不会卡住。2. 系统设计思路核心模块与业务流转2.1 三类核心角色与权限边界这套系统虽然规模不大但在角色设计上并没有偷懒。整体分三类角色系统管理员负责用户和问卷的全局管理问卷创建者可以复用管理员账号也可以独立角色负责自己名下问卷的编辑、发布、关闭和数据查看答卷用户则不需要登录通过链接即可参与填写。这个设计的精妙之处在于它把“管理端”和“填写端”彻底解耦了。管理端需要登录校验填写端走的是公开访问的匿名接口两者在Controller层做了清晰的包路径区分比如/admin/**走拦截器鉴权/survey/**或者/fill/**开放访问。这个分层习惯很重要很多初学者容易把权限判断散落在各个业务方法里最后写成一团乱麻而这套源码用拦截器加注解的方式把权限逻辑收敛到了一处。权限控制的具体实现上建议你关注一下登录态的处理方式。常见方案有Session和JWT两种这套源码大概率用的是Session方案因为服务端渲染天然适配Session实现简单且在单体应用下不存在水平扩展的顾虑。如果你后续要拆前后端分离再把登录态切换成JWT也不迟源码的User实体和登录接口可以复用。2.2 题型设计与动态表单的交互逻辑在线问卷最核心的难点是你怎么知道前端要渲染什么控件这个问题落到代码层面就是题型QuestionType的设计。常规做法是用一个枚举类定义题型比如单选用RADIO多选用CHECKBOX填空用TEXT评分用RATING或者SCORE然后在数据库里给每个问题存一个type字段。动态表单的渲染逻辑是这样的用户打开问卷时后端一次性查出问卷下的所有题目和选项列表组装成嵌套的DTO返回给前端。前端拿到题目类型后用条件判断或者模板分支决定渲染成单选按钮、复选按钮还是输入框。这个过程用Thymeleaf实现很直观用Vue也行核心在于一个题目的data结构必须包含题干、类型、选项列表填空题没有、是否必填。我见过不少人在这个环节犯的错误是把选项和题目分开查询循环里再查一次数据库导致N1查询问题。正确做法是一次join查出所有题目和选项在内存里按题目ID分组拼装。这套源码的做法值得学习它通过对象嵌套映射把题目和选项一次性查出来性能和代码可读性都兼顾了。2.3 答卷提交链路的隐藏细节答卷提交是整个系统里最容易出问题的地方涉及到参数校验、防重复提交、数据落库三个环节。用户提交时前端会把问卷ID和答题明细列表形式传到后端后端要做三件事第一校验问卷是否存在且处于发布状态第二校验必填题是否有答案选择题答案是否在合法选项范围内第三在同一事务里写入一条主记录和若干条明细记录。事务在这里很关键。答卷主表记录“谁在什么时间提交了哪份问卷”明细表记录“某道题选了哪些选项或者填了什么内容”。如果明细写入过程中失败了主记录也必须回滚否则就会出现“有答卷但没有答案”的脏数据。源码中这段逻辑用了Transactional注解你可以在Service实现类里看到。防重复提交这块简单方案是前端提交后禁用按钮但这只能防普通人防不了快速刷新和重复请求。更稳妥的方法是用一个临时Token或数据库唯一约束用户在打开问卷时获取一个submissionToken提交时带上这个Token后端在写入前检查Token是否已使用。如果这套源码没做这一个环节建议你二次开发时给它补上这个功能在数据统计准确性上非常实用。3. 数据库设计与统计报表的实现思路3.1 核心表结构拆分为什么是这五张表这套系统的数据库设计非常典型核心不复杂但每张表都有它存在的理由。我基于通用实践给你拆一下你对着源码的SQL文件看会更清晰。用户表sys_user保存管理员账号密码密码建议用BCrypt加密存储登录接口用用户名加密码换取会话。问卷表survey保存问卷标题、描述、状态草稿/发布/关闭、创建时间等状态字段控制前端是否允许填写。题目表survey_question包含所属问卷ID、题型type、题干content、是否必填required、排序字段sort_no。问卷下所有题目通过sort_no排序保证创建者调整顺序后展示顺序不变。选项表question_option包含所属题目ID、选项内容、排序字段。单选和多选题才有选项填空和评分题不依赖这张表。答卷主表survey_submission与答卷明细表submission_item主表存提交时间和答卷人标记可以用IP或OpenID明细表存题目ID、选项ID或文本内容。一张答卷对应多条明细。为了统计方便明细表中的答案字段建议冗余存储比如既存optionId也存optionText。这样统计时直接用文本分组不用再回查选项表性能更好。这里也能看出为什么单独拆出明细表一份问卷可能有几十个问题如果所有答案塞在一条记录里统计时就要做字符串解析极其痛苦。用明细表后每个问题一行SQL聚合非常顺手。3.2 统计报表一条GROUP BY搞定图表数据问卷系统的统计需求通常长这样某道单选题每个选项选了多少人多选题哪个组合比较多整体回收量是多少这些需求用SQL聚合非常简洁核心就是GROUP BY加COUNT。以单选题为例统计SQL大概是这样的SELECT qo.option_text, COUNT(si.id) AS answer_count FROM question_option qo LEFT JOIN submission_item si ON si.option_id qo.id WHERE qo.question_id #{questionId} GROUP BY qo.id, qo.option_text ORDER BY answer_count DESC这段SQL要重点解释一下为什么用LEFT JOIN而不是JOIN因为有的选项可能没人选如果用内连接那些0票的选项直接不显示图表上会缺点看起来像数据丢了。左连接配合COUNT(si.id)能保留所有选项没人选的选项会显示0。多选题统计的思路略有不同因为一个用户可能选多个选项明细表里的答案可能存成逗号分隔的选项ID列表。这时候要么拆表设计要么在统计时用FIND_IN_SET或者LIKE匹配但最稳妥的做法是给多选题单独建一张多选题答案关联表把“用户选了哪个选项”拆成多行存。如果原源码没有拆你在二次开发时遇到多选统计不准的问题大概率就是这里的原因。图表数据的组装在后端完成查询出统计结果后转成两个数组——一个存选项名称一个存数量扔给前端直接渲染柱状图或饼图。这样前端只负责画图所有统计口径都由后端保证逻辑清晰且方便单元测试。4. 源码实操从零跑通项目的关键步骤4.1 Maven工程结构与目录规范拿到源码后先把目录结构看一遍。这套源码应该是标准的Maven工程顶层有pom.xml包结构大致按controller、service、mapper、entity、common、config、util分层。这种分包方式的好处是职责清晰controller只做参数接收和响应封装service放业务逻辑mapper只和数据库打交道。我特别建议你留意common和config这两个包。common里一般放着统一返回结果类比如Result 、异常处理类、枚举类。config里放着拦截器配置、跨域配置虽然服务端渲染用不多、MyBatis-Plus分页插件配置。这些都属于“基础设施”代码量不大但决定了整个项目的规范程度面试讲解项目时从这些包切入会显得很有条理。工程启动前确认本地环境有JDK 8、Maven 3.6和MySQL 5.7以上。Spring Boot版本一般对应2.7.x或3.x如果用JDK 17建议优先选Spring Boot 3.x如果源码是2.x且你本机是JDK 17可能需要改一些javax到jakarta的包名引用这个坑留意一下。4.2 数据库初始化与三项核心配置源码包里一般会带SQL脚本先按脚本建库建表。如果你的MySQL版本大于8注意时区问题连接串里建议加serverTimezoneAsia/Shanghai不然启动时会报时区错误。接下来是application.yml里需要你确认的三项配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/survey?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl第一项是数据库连接账号密码改成你自己的第二项是端口如果8080被占用就改成8081第三项是MyBatis-Plus配置其中log-impl可以在开发阶段打印SQL方便你追踪每一条执行语句但上线前建议关掉。把这些配好后启动主类看到“Started Application in x seconds”就说明环境通了。跑通后先不要急着看页面我推荐你按这个顺序验证接口先用管理账号登录拿到会话然后创建一个包含单选、多选、填空的测试问卷发布后用浏览器无痕窗口访问填写链接提交一份答案最后去后台查看统计图表。这个流程走通整套源码的核心闭环你就拿捏了。4.3 核心代码实现三个业务片段拆解创建问卷和题目的接口是典型的嵌套保存逻辑。一次请求里既包含问卷基本信息又包含题目列表每个题目里又内含选项列表。事务要求是问卷没保存成功题目就不能入库所以这段逻辑放在一个事务里核心代码大概长这样Transactional(rollbackFor Exception.class) public Long createSurvey(SurveyDTO dto) { Survey survey new Survey(); survey.setTitle(dto.getTitle()); survey.setDescription(dto.getDescription()); survey.setStatus(0); // 默认草稿 surveyMapper.insert(survey); if (CollectionUtils.isNotEmpty(dto.getQuestions())) { for (QuestionDTO q : dto.getQuestions()) { Question question new Question(); question.setSurveyId(survey.getId()); // 类型、题干、是否必填、排序号 questionMapper.insert(question); if (CollectionUtils.isNotEmpty(q.getOptions())) { for (OptionDTO opt : q.getOptions()) { Option option new Option(); option.setQuestionId(question.getId()); // 选项内容、排序号 optionMapper.insert(option); } } } } return survey.getId(); }提交答卷的接口同样需要事务保护。还有一个容易被忽略的点提交时用批量插入一次性写入明细而不是循环单条插入。数据量小的时候差别不明显当一份问卷有几十道题、一天有几千份答卷时循环插入会拖垮性能。统计查询接口是体现MyBatis-Plus灵活性的地方直接用Select注解写聚合SQL返回一个Map列表或DTO列表。这类查询不需要实体映射适合用轻量DTO接结果避免和数据库表强绑定。4.4 Actuator与监控配置学有余力的加分项如果源码里带了spring-boot-starter-actuator依赖我建议你花20分钟研究一下。Actuator可以暴露健康检查、指标信息、环境属性等监控端点虽然它对小型问卷系统不是必须的但它是Spring Boot生态里一个很重要的生产运维组件。配置时注意端点暴露范围不要全开。生产环境一般只开放health和info端点管理端点通过management.endpoints.web.exposure.include配置。management: endpoints: web: exposure: include: health,info把Actuator用起来配合Micrometer和Prometheus的对接思路对你之后理解微服务监控体系有直接的帮助因为这算是一个从“能用”到“可运维”的分水岭。5. 常见问题排查与避坑技巧实录5.1 启动阶段的经典问题这套源码虽然是课程设计级别但启动阶段的坑一点不少。最常见的是数据库连不上报Communications link failure。这种问题九成是连接串写错或MySQL服务没启动少数情况是MySQL 8的驱动类名需要改成com.mysql.cj.jdbc.Driver旧的com.mysql.jdbc.Driver已经废弃了。第二个高频问题是端口占用。Spring Boot默认8080如果你本机装了Nginx或别的服务很容易撞车。用命令行netstat -ano | findstr 8080查一下是谁占用的要么关掉对应进程要么直接把server.port改成别的端口我是建议直接改端口省得影响其他环境。第三个坑是Lombok相关的编译报错。源码里大量用了Data这类注解如果IDE没装Lombok插件编译时会出现getter/setter找不到的报错。IDEA里在插件市场装一下Lombok然后打开注解处理开关Enable annotation processing就能解决。5.2 业务逻辑里的隐藏bug问卷系统的业务逻辑bug很有迷惑性不容易第一时间发现。我整理了一个自查清单你逐个对照现象根因解决方案发布后问卷访问404问卷状态没改成已发布对外查询接口过滤掉了草稿确认发布接口正确更新status字段单选题统计结果偏少使用了COUNT(*)导致LEFT JOIN产生的NULL被计数改成COUNT(明细表主键ID)多选统计出现奇怪占比多选答案存成逗号分隔字符串拆关联表或用FIND_IN_SET匹配后再COUNT刷新页面重复提交缺少防重复提交机制增加Token校验或唯一约束动态表单选项顺序乱了查询时没有按sort_no排序题目和选项都增加ORDER BY排序字段中文内容变成问号数据库字符集和连接字符集不一致统一使用utf8mb4连接串加characterEncodingutf8这张表是我拆过好几套问卷源码后总结出来的共性问题后面几个坑尤其隐蔽。比如统计偏少这个问题我刚工作时就踩过一次排查了一下午才发现是COUNT(1)把NULL也算进去了就差一个字段的事。5.3 安全与性能的几点提醒问卷系统看着不起眼但安全上不能裸奔。第一管理端接口必须做权限校验不能让人绕过登录直接调接口这个通过拦截器加白名单机制就能做填写链接相关的路径放行管理接口全部拦截。第二用户填写的文本内容存库前要做XSS过滤别让用户往问卷标题里塞一段脚本其他用户一打开就执行这是典型存储型XSS。第三SQL查询全用预编译機制MyBatis自带的#{}参数绑定就是预编译注意别在动态SQL里用${}拼接字符串。性能上问卷系统的瓶颈通常在统计报表查询上因为聚合查询要扫大量明细数据。优化思路很简单给外键字段question_id、survey_id加索引给状态字段加索引统计接口加一层缓存问卷变化不那么频繁的结果可以缓存在Redis里。如果短期内访问量突然暴增优先考虑CDN加速静态资源再考虑数据库读写分离。6. 围绕这套源码还能做什么扩展6.1 从“能用”到“好用”的进化路线这套源码完成了一个合格问卷系统的核心闭环但如果要上真实业务有几个方向值得扩展。第一是增加逻辑跳转用户选了某个选项后自动跳过某些题目这对问卷体验提升非常明显但实现上需要在题目表增加跳转配置字段。第二是增加答卷导出Excel方便运营人员离线分析数据用EasyExcel或POI都能做导出的格式按题分组还是按答卷分组要提前想好。第三是增加问卷模板库把常用问卷结构存成模板创建时一键套用这个需求在实际运营中特别常见。从技术角度还可以把答卷提交改成异步化。当前方案是同步写MySQL高并发下数据库压力大。可以引入消息队列先把答卷丢进消息队列后端消费者异步落库这样用户提交速度快数据库也扛得住。这个改动深度比较大但如果你想给毕设或项目经历加分这绝对是一个能拿出来讲的亮点。6.2 二次开发时建议保留的代码习惯我自己拆过不少源码好的代码习惯和坏的代码习惯对比太明显了。这套源码里下面这些习惯是你二次开发时一定要保留的统一返回结果对象、全局异常处理、Service层接口编程而非类直接实现、枚举代替魔法值、配置项集中到application.yml。这些都不是什么高深技术但会让你的代码在三个月后回看时依然看得懂。如果你打算在这个基础上换前端框架比如改成Vue Element UI建议只保留后端的API接口重新做一套前后端分离的前端工程。改造时不需要删后端代码把Controller的返回格式统一成JSON静态页面部分抽离出去就行。思路就是“后端当API Server前端专心做交互”这也是主流方向。最后说一点我在实际使用中的体会这类课程设计或实战源码最忌讳的就是拿到手只改个标题就交差。你花一个周末把数据库表、Controller层、Service层逐行过一遍用debug方式走通一条完整链路收获比看十篇教程都大。踩过几次坑之后你就会发现动态表单设计、事务边界划分、聚合统计查询这三点弄明白了市面上大部分管理系统类项目你都能hold住。后续想扩展方向的话先把Actuator监控和Redis缓存加上这套问卷系统就能真正跑到生产环境里接受考验了。本文还有配套的精品资源点击获取