1. 项目背景与核心价值
去年帮学弟调试毕业设计时,发现市面上很多所谓的"完整项目"实际上都是残缺不全的玩具代码。要么缺少关键业务逻辑,要么数据库设计不合理,甚至前后端对接的接口文档都漏洞百出。这个基于SpringBoot+Vue的在线问卷系统,是我在指导过程中重构了三次的实战级项目,包含了企业级开发中真正会遇到的各类技术细节。
不同于教学演示项目,这个系统实现了:
- 完整的问卷生命周期管理(创建->发布->回收->分析)
- 基于RBAC的动态权限控制
- 可视化统计报表生成
- 支持17种常见题型(包括矩阵题、排序题等复杂类型)
- 响应式布局适配PC/移动端
特别适合需要真实项目练手的Java学习者,以及正在寻找高质量毕设素材的高校学生。整套代码严格按照阿里Java开发规范编写,注释覆盖率超过80%,接口文档采用Swagger+YAPI双模式维护。
2. 技术架构解析
2.1 后端技术栈
SpringBoot 2.7.3选型经过严格验证:
- 相比2.4.x版本优化了OOM处理
- 对JDK17的兼容性更好
- 内置的Actuator端点更适合问卷系统的监控需求
核心模块划分:
code复制src/
├── main/
│ ├── java/
│ │ ├── com.questionnaire/
│ │ │ ├── config/ # 安全配置/跨域处理
│ │ │ ├── controller/ # 7个功能模块API
│ │ │ ├── dao/ # MyBatis-Plus动态SQL
│ │ │ ├── entity/ # 22个数据实体
│ │ │ ├── service/ # 事务管理
│ │ │ ├── util/ # 自定义工具类
│ │ │ └── vo/ # 前后端交互对象
│ │ └── resources/
│ │ ├── mapper/ # XML映射文件
│ │ ├── static/ # 导出模板
│ │ └── application.yml # 多环境配置
数据库设计亮点:
- 采用雪花ID替代自增主键
- 问卷与题目的一对多关系使用JSON字段存储选项配置
- 回答记录表使用水平分表策略(按月份拆分)
2.2 前端技术栈
Vue 3.2 + Element Plus的组合经过多个项目验证:
- 使用Composition API编写可复用逻辑
- 动态表单渲染采用递归组件实现
- ECharts 5.3实现可视化分析
关键目录结构:
code复制src/
├── api/ # Axios封装
├── assets/ # 静态资源
├── components/ # 35个业务组件
├── router/ # 动态路由
├── store/ # Pinia状态管理
├── utils/ # 工具函数
└── views/ # 页面组件
性能优化措施:
- 路由懒加载
- 大数据量采用虚拟滚动
- 本地缓存问卷模板
3. 核心功能实现
3.1 问卷创建引擎
题型配置的难点在于处理不同题型的差异化参数。我们采用策略模式:
java复制public interface QuestionStrategy {
JSONObject buildConfig(QuestionVO vo);
ValidationResult validate(AnswerDTO dto);
}
@Service
public class MatrixStrategy implements QuestionStrategy {
// 实现矩阵题的特殊逻辑
}
前端动态表单渲染的关键代码:
vue复制<template v-for="item in questions">
<component :is="getComponent(item.type)"
:config="item.config"
@change="handleAnswer"/>
</template>
3.2 权限控制系统
RBAC实现的关键SQL(MyBatis-Plus动态SQL):
xml复制<select id="selectMenuTreeByUserId" resultType="Menu">
SELECT DISTINCT m.* FROM sys_menu m
LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id
LEFT JOIN sys_user_role ur ON rm.role_id = ur.role_id
WHERE ur.user_id = #{userId}
AND m.status = 0
ORDER BY m.parent_id, m.order_num
</select>
前端权限指令封装:
javascript复制const hasPermission = (el, binding) => {
const value = binding.value
const permissions = store.state.user.permissions
if (!permissions.includes(value)) {
el.parentNode?.removeChild(el)
}
}
4. 项目部署指南
4.1 数据库准备
SQL脚本包含:
- 表结构定义(含索引优化)
- 基础数据初始化
- 存储过程(用于月度数据归档)
重要配置项:
sql复制-- 建议配置
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256M
query_cache_type = 0
4.2 后端部署
多环境配置示例:
yaml复制spring:
profiles:
active: dev
datasource:
druid:
initial-size: 5
max-active: 20
validation-query: SELECT 1 FROM DUAL
启动参数建议:
code复制java -Xms512m -Xmx1024m
-XX:+HeapDumpOnOutOfMemoryError
-jar questionnaire.jar
--spring.profiles.active=prod
4.3 前端部署
生产环境构建命令:
bash复制VITE_API_BASE_URL=/api/ npm run build
Nginx关键配置:
nginx复制location /api/ {
proxy_pass http://backend:8080;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
try_files $uri $uri/ /index.html;
gzip_static on;
}
5. 开发经验分享
5.1 接口文档管理
采用Swagger + YAPI双模式:
- Swagger用于开发阶段实时调试
- YAPI用于团队协作和版本管理
接口版本控制方案:
java复制@RestController
@RequestMapping("/api/v1/question")
public class QuestionController {
@ApiVersion
@GetMapping("/{id}")
public Result<QuestionVO> getById(@PathVariable Long id) {
//...
}
}
5.2 典型问题排查
- Vue响应式丢失问题:
javascript复制// 错误做法
state.questions[0].title = '新标题'
// 正确做法
state.questions = state.questions.map((q, i) =>
i === 0 ? {...q, title: '新标题'} : q
)
- MyBatis-Plus逻辑删除冲突:
yaml复制mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
5.3 性能优化记录
- 问卷列表查询从1200ms优化到200ms:
- 添加复合索引
(creator_id, status, create_time) - 使用DTO替代Entity返回
- 启用MyBatis二级缓存
- 大数据导出优化:
- 采用POI的SXSSFWorkbook模式
- 分页查询避免OOM
- 压缩ZIP文件后再传输
这套代码库最宝贵的不是实现功能本身,而是包含了真实项目开发中会遇到的各种边界情况处理。比如当问卷同时有10万人在线填写时,如何处理并发提交?当题目选项需要动态联动时,前端如何优雅地管理状态?这些在文档和代码注释中都有详细说明
