1. 项目概述
这个基于SpringBoot和Vue框架的问卷调查系统,是我去年为一个教育机构开发的实际项目。当时客户需要一套能够快速创建、分发和分析问卷的在线平台,要求支持多种题型、实时数据统计和权限管理。经过技术选型,最终决定采用前后端分离架构,后端用SpringBoot提供RESTful API,前端用Vue实现响应式界面。
这种技术组合有几个明显优势:SpringBoot的自动配置和起步依赖让后端开发效率极高,而Vue的组件化开发模式则非常适合构建复杂的表单交互界面。系统上线后,客户反馈问卷回收效率提升了3倍,数据分析时间缩短了80%。
2. 技术架构设计
2.1 后端技术栈
后端采用SpringBoot 2.7作为基础框架,主要依赖包括:
- Spring Data JPA:用于问卷数据的持久化操作
- Spring Security:实现基于JWT的认证授权
- Lombok:简化实体类代码
- MapStruct:处理DTO转换
- Hibernate Validator:参数校验
数据库选用MySQL 8.0,考虑到问卷数据的关系型特征,设计了以下核心表:
- 用户表(user)
- 问卷表(survey)
- 问题表(question)
- 选项表(option)
- 答卷表(answer)
- 答卷详情表(answer_detail)
特别在问卷表设计中加入了version字段实现乐观锁,防止多人同时编辑冲突。
2.2 前端技术栈
前端使用Vue 3组合式API开发,主要技术选型:
- Element Plus:UI组件库
- Axios:HTTP请求
- Vue Router:路由管理
- Pinia:状态管理
- ECharts:数据可视化
采用单页面应用(SPA)架构,通过动态路由实现权限控制。例如:
javascript复制{
path: '/survey/:id/edit',
component: SurveyEditor,
meta: { requiresAuth: true, roles: ['admin', 'editor'] }
}
3. 核心功能实现
3.1 问卷创建模块
后端提供的主要API接口:
java复制@RestController
@RequestMapping("/api/surveys")
public class SurveyController {
@PostMapping
public ResponseEntity<SurveyDTO> createSurvey(@Valid @RequestBody SurveyDTO dto) {
// 实现逻辑
}
@GetMapping("/{id}")
public ResponseEntity<SurveyDTO> getSurvey(@PathVariable Long id) {
// 实现逻辑
}
}
前端使用动态表单实现问卷设计器,关键代码如下:
vue复制<template>
<el-form :model="form">
<component
v-for="(question, index) in form.questions"
:key="question.id"
:is="getComponent(question.type)"
v-model="question.content"
@delete="removeQuestion(index)"
/>
</el-form>
</template>
3.2 问卷分发与收集
实现多种分发方式:
- 直接链接分享
- 嵌入网页iframe
- 邮件邀请
- 微信小程序跳转
收集答卷时采用防重复提交机制:
java复制@PostMapping("/answers")
public ResponseEntity<?> submitAnswer(
@RequestBody AnswerDTO dto,
@RequestHeader("X-Client-Id") String clientId) {
if (redisTemplate.opsForValue().get(clientId) != null) {
throw new DuplicateSubmissionException();
}
redisTemplate.opsForValue().set(clientId, "1", 30, TimeUnit.MINUTES);
// 处理答卷逻辑
}
4. 数据分析与可视化
4.1 实时统计看板
使用ECharts实现的数据可视化方案:
javascript复制const initChart = () => {
const chart = echarts.init(dom)
chart.setOption({
tooltip: {
trigger: 'item'
},
series: [{
type: 'pie',
data: [
{ value: 1048, name: '选项A' },
{ value: 735, name: '选项B' }
]
}]
})
}
4.2 数据导出功能
支持导出格式:
- Excel(使用Apache POI)
- PDF(使用iText)
- CSV(直接生成)
关键代码示例:
java复制@GetMapping("/export/excel")
public void exportToExcel(@RequestParam Long surveyId,
HttpServletResponse response) {
List<AnswerDTO> data = answerService.getAnswers(surveyId);
Workbook workbook = new XSSFWorkbook();
// 填充数据逻辑
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=survey.xlsx");
workbook.write(response.getOutputStream());
}
5. 系统安全与性能优化
5.1 安全措施
- 接口防刷:使用Guava RateLimiter限流
- XSS防护:前端使用DOMPurify净化,后端使用Jackson转义
- SQL注入:全部使用参数化查询
- CSRF防护:虽然RESTful API无状态,但仍验证Referer
5.2 性能优化
- 问卷列表接口添加Redis缓存:
java复制@Cacheable(value = "surveys", key = "#pageable.pageNumber")
public Page<Survey> getPublishedSurveys(Pageable pageable) {
return surveyRepository.findByStatus(SurveyStatus.PUBLISHED, pageable);
}
- 使用Hibernate二级缓存减少数据库压力
- 前端组件懒加载:
javascript复制const QuestionEditor = () => import('./QuestionEditor.vue')
6. 部署方案
6.1 后端部署
采用Docker容器化部署:
dockerfile复制FROM openjdk:17-jdk
COPY target/survey-system.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
使用Nginx配置负载均衡:
nginx复制upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location /api {
proxy_pass http://backend;
}
}
6.2 前端部署
Vue项目构建优化:
- 配置gzip压缩
- 启用CDN加速
- 拆分chunk减小首屏体积
vue.config.js配置示例:
javascript复制configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
7. 踩坑经验分享
-
Vue响应式更新问题:
当问卷问题顺序变化时,发现视图不更新。解决方案是给v-for添加唯一的:key,或者使用Vue.set强制更新。 -
SpringBoot事务失效:
在同一个类内部调用@Transactional方法会导致事务失效。最终将方法拆分到不同Service解决。 -
跨域问题:
开发环境遇到CORS问题,解决方案:java复制@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*"); } } -
大数据量导出OOM:
导出10万条以上数据时出现内存溢出。最终采用分页查询+流式写入解决。
这个项目让我深刻体会到,技术选型要权衡开发效率与系统性能。SpringBoot+Vue的组合确实能快速实现业务需求,但在复杂交互和大数据处理场景下,还需要针对性地进行优化。
