基于SSM框架的在线问卷设计与数据统计系统 - 源码深度解析

JavaJavaScriptHTMLCSSSSM框架MavenMySQL
2026-02-0710 浏览

文章摘要

本项目是基于SSM(Spring+Spring MVC+MyBatis)框架开发的在线问卷设计与数据统计系统,旨在为用户提供一站式的问卷创建、发布、回收与数据分析服务。系统核心解决了传统纸质问卷或简单在线工具在数据采集效率低、统计功能薄弱、难以集中管理等方面的痛点,通过自动化的流程与可视化的数据呈现...

在当今数据驱动的决策环境中,高效、精准地收集和分析用户反馈变得至关重要。传统纸质问卷或功能单一的在线工具往往存在数据采集效率低下、统计功能薄弱、难以集中管理等痛点。为此,我们设计并实现了一套专业级的问卷调研与数据分析平台,该系统基于成熟的SSM(Spring + Spring MVC + MyBatis)技术栈构建,为企业市场部门、学术研究人员及教育培训机构提供了从问卷设计、发布到数据回收与深度分析的全流程解决方案。

系统架构与技术栈

该平台采用经典的三层架构设计,确保了系统的高内聚、低耦合特性。表现层由Spring MVC框架负责,通过注解驱动的控制器(Controller)处理前端请求,并利用JSP结合JSTL标签库动态渲染视图。业务逻辑层依托Spring框架的IoC容器,统一管理各类服务组件(Service),处理复杂的问卷业务逻辑,如题目顺序校验、答卷有效性判断及统计计算。数据持久层则采用MyBatis框架,通过灵活的XML映射文件实现Java对象与数据库表字段的关联,高效执行增删改查操作。

技术选型上,系统使用Maven进行项目构建和依赖管理,前端采用HTML、CSS和JavaScript实现交互界面,并集成Jackson库为前端提供RESTful风格的JSON接口。数据库选用MySQL,确保了数据存储的稳定性和可靠性。

数据库设计亮点

数据库设计是系统稳定性的基石。该平台的数据库 schema 体现了高度的规范性和可扩展性,核心表之间通过外键关联,确保了数据的完整性和一致性。

问卷主表(tb_survey)设计分析

CREATE TABLE `tb_survey` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `title` varchar(100) DEFAULT NULL COMMENT '问卷标题',
  `remark` varchar(200) DEFAULT NULL COMMENT '问卷备注',
  `bounds` int(1) DEFAULT NULL COMMENT '0:不限制;1:限制',
  `start_time` datetime DEFAULT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
  `rules` int(1) DEFAULT NULL COMMENT '0公开;1密码',
  `password` varchar(50) DEFAULT NULL COMMENT '密码',
  `url` varchar(200) DEFAULT NULL COMMENT '问卷链接',
  `state` varchar(50) DEFAULT NULL COMMENT '创建、执行中、结束',
  `logo` varchar(200) DEFAULT NULL COMMENT 'logo图片',
  `bgimg` varchar(200) DEFAULT NULL COMMENT '背景图片',
  `anon` int(1) DEFAULT NULL COMMENT '0匿名;1不匿名',
  `creator` int(11) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC COMMENT='问卷表'

该表设计具有以下技术亮点:

  • 字段类型精准匹配业务需求boundsrulesanon等字段使用int(1)类型,有效存储布尔型状态值,节省存储空间
  • 时间范围控制start_timeend_time字段精确到datetime粒度,支持问卷的精确时间控制
  • 多状态管理state字段采用varchar(50)类型,灵活支持"创建、执行中、结束"等多种状态
  • 权限控制完善:通过rulespassword字段实现问卷的公开/密码访问控制
  • 索引优化:主键采用自增ID,确保数据插入性能和数据有序性

问题表(tb_question)与答案表设计

CREATE TABLE `tb_question` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `title` varchar(200) DEFAULT NULL COMMENT '问题标题',
  `remark` varchar(200) DEFAULT NULL COMMENT '问题备注',
  `type` int(1) DEFAULT NULL COMMENT '1radio|2checkbox|3text|4textarea',
  `required` int(1) DEFAULT NULL COMMENT '0非必填1必填',
  `check_style` varchar(50) DEFAULT NULL COMMENT 'text;number;date',
  `order_style` int(1) DEFAULT NULL COMMENT '0顺序1随机',
  `show_style` int(1) DEFAULT NULL COMMENT '1;2;3;4',
  `test` int(1) DEFAULT NULL COMMENT '0不测评1测评',
  `score` int(3) DEFAULT NULL COMMENT '分数',
  `orderby` int(11) DEFAULT NULL COMMENT '排序',
  `creator` int(11) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `survey_id` int(11) DEFAULT NULL COMMENT '问卷ID',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `FK_Reference_1` (`survey_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC COMMENT='问题表'

问题表的设计体现了高度的灵活性:

  • 多题型支持type字段支持单选、多选、文本、文本域四种题型,满足多样化数据采集需求
  • 验证机制check_style字段支持文本、数字、日期等格式验证,提升数据质量
  • 显示控制order_styleshow_style字段实现题目顺序和显示样式的灵活配置
  • 外键优化survey_id字段建立索引,优化问卷与问题之间的关联查询性能

答案表采用分表设计策略,tb_answer_opt存储选项类答案,tb_answer_txt存储文本类答案,这种设计有效避免了数据冗余,提升了查询效率。

核心功能实现

1. 问卷设计与管理系统

系统提供直观的问卷设计界面,支持拖拽式题目添加和实时预览。管理员可以灵活配置问卷属性,包括访问权限、时间限制、匿名设置等。

问卷设计界面

控制器实现代码:

@Controller
@RequestMapping("/survey")
public class SurveyController {
    
    @Autowired
    private SurveyService surveyService;
    
    @PostMapping("/create")
    @ResponseBody
    public Map<String,Object> create(@RequestBody Survey survey){
        survey.setCreateTime(new Date());
        survey.setState("创建");
        survey.setUrl(generateUniqueUrl());
        
        int result = surveyService.create(survey);
        if(result <= 0){
            return MapControl.getInstance().error().getMap();
        }
        return MapControl.getInstance().success().add("id", survey.getId()).getMap();
    }
    
    @PostMapping("/update")
    @ResponseBody
    public Map<String,Object> update(@RequestBody Survey survey){
        int result = surveyService.update(survey);
        if(result <= 0){
            return MapControl.getInstance().error().getMap();
        }
        return MapControl.getInstance().success().getMap();
    }
    
    private String generateUniqueUrl() {
        return "survey_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
    }
}

服务层业务逻辑:

@Service
public class SurveyService {
    
    @Autowired
    private SurveyMapper surveyMapper;
    
    public int create(Survey survey) {
        // 验证问卷时间有效性
        if(survey.getBounds() == 1) {
            if(survey.getStartTime().after(survey.getEndTime())) {
                throw new BusinessException("开始时间不能晚于结束时间");
            }
        }
        
        // 设置默认值
        if(survey.getAnon() == null) {
            survey.setAnon(0); // 默认匿名
        }
        
        return surveyMapper.insert(survey);
    }
    
    public List<Survey> queryByCreator(Integer creatorId) {
        return surveyMapper.selectByCreator(creatorId);
    }
}

2. 题目管理与动态渲染

系统支持多种题型配置,包括单选题、多选题、文本题等,并提供了丰富的题目属性设置。

题目编辑界面

题目实体类设计:

public class Question extends Entity {
    private Integer id;
    private String title;
    private String remark;
    private Integer type; // 1:单选 2:多选 3:文本 4:文本域
    private Integer required; // 0:非必填 1:必填
    private String checkStyle; // 验证格式
    private Integer orderStyle; // 排序方式
    private Integer showStyle; // 显示样式
    private Integer test; // 是否测评
    private Integer score; // 分数
    private Integer orderby; // 排序
    private Integer creator;
    private Date createTime;
    private Integer surveyId;
    
    // 关联的选项列表
    private List<Option> options;
    
    // getter和setter方法
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    // ... 其他getter/setter方法
}

题目查询与渲染逻辑:

@Controller
@RequestMapping("/question")
public class QuestionController {
    
    @Autowired
    private QuestionService questionService;
    
    @GetMapping("/list")
    public String list(Integer surveyId, ModelMap modelMap) {
        List<Question> questions = questionService.queryBySurvey(surveyId);
        modelMap.addAttribute("questions", questions);
        modelMap.addAttribute("surveyId", surveyId);
        return "question/list";
    }
    
    @PostMapping("/queryBySurvey")
    @ResponseBody
    public Map<String,Object> queryBySurvey(Integer surveyId) {
        List<Question> questions = questionService.queryBySurvey(surveyId);
        return MapControl.getInstance().success().add("data", questions).getMap();
    }
}

3. 答卷提交与数据验证

系统提供友好的答卷界面,支持前端实时验证和服务器端数据校验,确保数据的完整性和准确性。

答卷提交界面

答卷提交控制器:

@Controller
@RequestMapping("/answer")
public class AnswerController {
    
    @Autowired
    private AnswerService answerService;
    
    @PostMapping("/submit")
    @ResponseBody
    public Map<String,Object> submit(@RequestBody AnswerForm answerForm) {
        try {
            // 验证问卷状态
            Survey survey = surveyService.detail(answerForm.getSurveyId());
            if(!"执行中".equals(survey.getState())) {
                return MapControl.getInstance().error("问卷已结束").getMap();
            }
            
            // 验证时间限制
            if(survey.getBounds() == 1) {
                Date now = new Date();
                if(now.before(survey.getStartTime()) || now.after(survey.getEndTime())) {
                    return MapControl.getInstance().error("不在问卷开放时间内").getMap();
                }
            }
            
            // 保存答案
            answerService.saveAnswer(answerForm);
            return MapControl.getInstance().success().getMap();
            
        } catch (Exception e) {
            return MapControl.getInstance().error("提交失败").getMap();
        }
    }
}

答案服务层实现:

@Service
@Transactional
public class AnswerService {
    
    @Autowired
    private AnswerOptMapper answerOptMapper;
    
    @Autowired
    private AnswerTxtMapper answerTxtMapper;
    
    @Autowired
    private QuestionMapper questionMapper;
    
    public void saveAnswer(AnswerForm answerForm) {
        String voter = generateVoterId(answerForm);
        
        for (AnswerDetail detail : answerForm.getDetails()) {
            Question question = questionMapper.selectById(detail.getQuestionId());
            
            // 必填验证
            if(question.getRequired() == 1 && 
               (detail.getOptIds() == null || detail.getOptIds().isEmpty()) &&
               (detail.getTextResult() == null || detail.getTextResult().trim().isEmpty())) {
                throw new BusinessException("问题" + question.getTitle() + "为必填项");
            }
            
            // 根据题型保存答案
            if(question.getType() == 1 || question.getType() == 2) {
                saveOptionAnswer(detail, voter, answerForm.getSurveyId());
            } else {
                saveTextAnswer(detail, voter, answerForm.getSurveyId());
            }
        }
    }
    
    private void saveOptionAnswer(AnswerDetail detail, String voter, Integer surveyId) {
        for (Integer optId : detail.getOptIds()) {
            AnswerOpt answerOpt = new AnswerOpt();
            answerOpt.setSurveyId(surveyId);
            answerOpt.setQuestionId(detail.getQuestionId());
            answerOpt.setOptId(optId);
            answerOpt.setType(detail.getQuestionType() == 1 ? "radio" : "checkbox");
            answerOpt.setVoter(voter);
            answerOpt.setCreateTime(new Date());
            answerOptMapper.insert(answerOpt);
        }
    }
    
    private void saveTextAnswer(AnswerDetail detail, String voter, Integer surveyId) {
        AnswerTxt answerTxt = new AnswerTxt();
        answerTxt.setSurveyId(surveyId);
        answerTxt.setQuestionId(detail.getQuestionId());
        answerTxt.setResult(detail.getTextResult());
        answerTxt.setVoter(voter);
        answerTxt.setCreateTime(new Date());
        answerTxtMapper.insert(answerTxt);
    }
    
    private String generateVoterId(AnswerForm answerForm) {
        // 生成唯一投票者标识,支持匿名和非匿名模式
        if(answerForm.isAnonymous()) {
            return "anon_" + System.currentTimeMillis() + "_" + 
                   UUID.randomUUID().toString().substring(0, 6);
        } else {
            return "user_" + answerForm.getUserId();
        }
    }
}

4. 数据统计与可视化分析

系统提供强大的数据统计功能,支持多种图表展示和交叉分析,帮助用户深入理解数据背后的洞察。

数据统计界面

统计服务层实现:

@Service
public class StatisticsService {
    
    @Autowired
    private AnswerOptMapper answerOptMapper;
    
    @Autowired
    private AnswerTxtMapper answerTxtMapper;
    
    public SurveyStatistics getSurveyStatistics(Integer surveyId) {
        SurveyStatistics statistics = new SurveyStatistics();
        
        // 获取问卷基本信息
        Survey survey = surveyMapper.selectById(surveyId);
        statistics.setSurvey(survey);
        
        // 统计答卷数量
        Integer answerCount = getAnswerCount(surveyId);
        statistics.setTotalAnswers(answerCount);
        
        // 统计各题目答案分布
        List<QuestionStatistics> questionStats = getQuestionStatistics(surveyId);
        statistics.setQuestionStatistics(questionStats);
        
        return statistics;
    }
    
    private List<QuestionStatistics> getQuestionStatistics(Integer surveyId) {
        List<Question> questions = questionMapper.selectBySurvey(surveyId);
        List<QuestionStatistics> statsList = new ArrayList<>();
        
        for (Question question : questions) {
            QuestionStatistics stats = new QuestionStatistics();
            stats.setQuestion(question);
            
            if(question.getType() == 1 || question.getType() == 2) {
                // 选项类题目的统计
                List<OptionStatistics> optionStats = getOptionStatistics(question.getId());
                stats.setOptionStatistics(optionStats);
            } else {
                // 文本类题目的统计(词频分析等)
                List<TextAnswer> textAnswers = answerTxtMapper.selectByQuestion(question.getId());
                stats.setTextAnswers(textAnswers);
            }
            
            statsList.add(stats);
        }
        
        return statsList;
    }
    
    private List<OptionStatistics> getOptionStatistics(Integer questionId) {
        return answerOptMapper.countByQuestionAndOption(questionId);
    }
}

实体模型设计

系统采用面向对象的实体设计,每个数据库表对应一个Java实体类,通过MyBatis实现ORM映射。

管理员实体类优化:

package com.yanzhen.entity;

import com.yanzhen.utils.Entity;
import java.util.Date;

/**
 * 管理员实体类
 * @author 596183363@qq.com
 * @time 2020-06-09 10:18:04
 */
public class Admin extends Entity {
    private Integer id;
    private String account;
    private String name;
    private String password;
    private String phone;
    private String remark;
    private Date createTime;
    private Date updateTime;
    
    // 业务逻辑字段(不持久化到数据库)
    private transient String confirmPassword;
    private transient String newPassword;
    
    public Admin() {}
    
    public Admin(String account, String password) {
        this.account = account;
        this.password = password;
    }
    
    // Getter和Setter方法
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    
    public String getAccount() { return account; }
    public void setAccount(String account) { 
        if(account != null) {
            this.account = account.trim();
        }
    }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { 
        if(password != null && !password.isEmpty()) {
            this.password = MD5Utils.getMD5(password);
        }
    }
    
    public String getPhone() { return phone; }
    public void setPhone(String phone) { 
        // 手机号格式验证
        if(phone != null && !phone.matches("^1[3-9]\\d{9}$")) {
            throw new IllegalArgumentException("手机号格式不正确");
        }
        this.phone = phone; 
    }
    
    public String getRemark() { return remark; }
    public void setRemark(String remark) { this.remark = remark; }
    
    public Date getCreateTime() { return createTime; }
    public void setCreateTime(Date create
本文关键词
SSM框架在线问卷系统数据统计系统源码解析数据库设计

上下篇

上一篇
没有更多文章
下一篇
没有更多文章