问卷星云平台:基于SSH框架的企业级问卷管理系统
项目背景与意义
在数字化调研时代,传统纸质问卷和简易在线工具已无法满足现代企业对数据收集的专业需求。纸质问卷存在分发效率低、数据录入易错、统计分析困难等问题,而市面上简单的在线问卷工具往往缺乏企业级的数据安全性和定制化功能。本系统正是针对这些痛点,采用成熟的SSH框架技术栈,构建了一个功能完备、性能稳定的企业级问卷管理平台。
该系统通过标准化的问卷设计流程、智能化的数据收集机制和可视化的统计分析功能,为企业人力资源部门、教育机构和市场研究团队提供了完整的调研解决方案。平台支持多角色权限管理,实现了问卷创建、分发、回收、统计的全生命周期管理,显著提升了调研工作的效率和数据价值。
系统架构与技术栈
平台采用经典的三层架构设计,各层职责分明,耦合度低,便于维护和扩展。
表现层基于Struts2框架实现,通过配置struts.xml文件定义请求映射关系,利用JSP页面和Struts标签库渲染动态内容。前端采用HTML+CSS+JavaScript技术组合,确保用户界面的友好性和交互体验的流畅性。
<!-- struts.xml配置示例 -->
<struts>
<package name="questionnaire" extends="struts-default">
<action name="createQuestionnaire" class="questionnaireAction" method="create">
<result name="success">/questionnaire/create_success.jsp</result>
<result name="input">/questionnaire/create.jsp</result>
</action>
</package>
</struts>
业务逻辑层由Spring框架统一管理,通过依赖注入实现各业务组件之间的松耦合。平台核心服务包括用户管理服务、问卷设计服务、答卷收集服务和统计分析服务等。
// Spring配置示例
@Configuration
@ComponentScan("com.questionnaire.service")
public class AppConfig {
@Bean
public QuestionnaireService questionnaireService() {
return new QuestionnaireServiceImpl();
}
@Bean
public UserService userService() {
return new UserServiceImpl();
}
}
数据持久层基于Hibernate ORM框架,将Java对象与数据库表结构映射,简化了数据访问操作。通过HQL查询语言和Criteria API,实现了复杂的数据查询和统计分析功能。
// Hibernate实体映射示例
@Entity
@Table(name = "tb_subject")
public class Subject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "SUBJECT_NAME", nullable = false, length = 20)
private String subjectName;
// 其他字段映射...
}
数据库设计亮点
主题表设计分析
tb_subject表作为系统的核心数据表,存储了所有问卷的基本信息。表结构设计体现了良好的规范化原则:
CREATE TABLE `tb_subject` (
`ID` int(11) NOT NULL COMMENT '主题ID',
`SUBJECT_NAME` varchar(20) NOT NULL COMMENT '主题名称',
`optionType` varchar(20) NOT NULL COMMENT '选项类型',
`OPTION_NUM` int(11) NOT NULL COMMENT '选项数量',
`CREATE_TIME` datetime NOT NULL COMMENT '创建时间',
`REMARK` varchar(200) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='主题表'
设计亮点:
- 主键ID采用自增整数,确保唯一性和查询性能
- SUBJECT_NAME字段长度限制为20字符,符合问卷标题的合理长度范围
- CREATE_TIME字段记录问卷创建时间,便于按时间维度进行数据统计和分析
- optionType字段明确区分单选、多选等题型,为前端渲染提供类型依据
- 备注字段采用可变长度设计,既节省存储空间又满足扩展需求
选项表外键关系设计
tb_option表通过外键与主题表建立关联,体现了良好的关系数据库设计原则:
CREATE TABLE `tb_option` (
`OPTION_ID` int(11) NOT NULL COMMENT '选项ID',
`OPTION_CONTENT` varchar(20) NOT NULL COMMENT '选项内容',
`VOTES` int(11) DEFAULT NULL COMMENT '票数',
`SUBJECT_ID` int(11) DEFAULT NULL COMMENT '主题ID',
`REMARK` varchar(200) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`OPTION_ID`),
KEY `FK9B4F77665C1F5DDE` (`SUBJECT_ID`),
CONSTRAINT `FK9B4F77665C1F5DDE` FOREIGN KEY (`SUBJECT_ID`) REFERENCES `tb_subject` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='选项表'
外键优化策略:
- 通过SUBJECT_ID外键确保数据完整性,防止孤儿记录
- 为外键字段建立索引,提升联表查询性能
- VOTES字段实时统计选项票数,避免频繁的COUNT查询
- OPTION_CONTENT长度限制保证选项内容的简洁性
用户选项表的关联设计
tb_user_option表作为关联表,记录了用户与选项的多对多关系,体现了复杂业务场景的数据建模能力:
CREATE TABLE `tb_user_option` (
`ID` int(11) NOT NULL COMMENT '主键ID',
`USER_ID` int(11) NOT NULL COMMENT '用户ID',
`SUBJECT_ID` int(11) NOT NULL COMMENT '主题ID',
`IS_VOTED` varchar(200) DEFAULT NULL COMMENT '是否投票',
`REMARK` varchar(200) DEFAULT NULL COMMENT '备注',
`OPTION_ID` int(11) DEFAULT NULL COMMENT '选项ID',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='用户选项表'
关联设计优势:
- 复合业务键设计,支持同一用户对同一问卷的多次投票记录
- IS_VOTED字段采用字符串存储,支持扩展投票状态(如:已提交、未提交、审核中)
- 通过USER_ID和SUBJECT_ID组合索引,优化用户投票记录的查询效率
核心功能实现
问卷设计与创建功能
平台提供直观的问卷设计界面,支持多种题型配置和选项设置。设计过程采用AJAX异步交互,提升用户体验。

核心实现代码:
// QuestionnaireAction.java - 问卷创建Action类
public class QuestionnaireAction extends ActionSupport {
private Subject subject;
private List<Option> options;
private QuestionnaireService questionnaireService;
public String create() {
try {
// 设置问卷创建时间
subject.setCreateTime(new Date());
// 保存问卷主题和选项
questionnaireService.createQuestionnaire(subject, options);
addActionMessage("问卷创建成功!");
return SUCCESS;
} catch (Exception e) {
addActionError("问卷创建失败:" + e.getMessage());
return INPUT;
}
}
// Getter和Setter方法
public Subject getSubject() { return subject; }
public void setSubject(Subject subject) { this.subject = subject; }
// 其他getter/setter...
}
// questionnaire_design.js - 前端动态添加选项
function addOption() {
var optionType = document.getElementById('optionType').value;
var optionContainer = document.getElementById('optionsContainer');
var optionDiv = document.createElement('div');
optionDiv.className = 'option-item';
optionDiv.innerHTML = `
<input type="text" name="options" placeholder="请输入选项内容" maxlength="20" required>
<button type="button" onclick="removeOption(this)">删除</button>
`;
optionContainer.appendChild(optionDiv);
updateOptionCount();
}
function updateOptionCount() {
var count = document.querySelectorAll('.option-item').length;
document.getElementById('optionNum').value = count;
}
权限管理与多角色支持
系统实现精细化的权限控制,区分管理员和普通用户的操作权限。管理员拥有问卷管理、用户管理和数据统计等高级功能。

权限控制实现:
// UserAction.java - 用户登录和权限验证
public class UserAction extends ActionSupport {
private String username;
private String password;
private UserService userService;
private Map<String, Object> session;
public String login() {
User user = userService.authenticate(username, password);
if (user != null) {
// 将用户信息存入session
session.put("currentUser", user);
// 根据角色跳转到不同页面
if ("admin".equals(user.getRole())) {
return "admin";
} else {
return "user";
}
} else {
addActionError("用户名或密码错误!");
return INPUT;
}
}
// 权限拦截器配置
public class AuthorizationInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Map<String, Object> session = invocation.getInvocationContext().getSession();
User user = (User) session.get("currentUser");
if (user == null) {
return "login";
}
// 检查管理员权限
Action action = (Action) invocation.getAction();
if (action instanceof AdminAction && !"admin".equals(user.getRole())) {
return "unauthorized";
}
return invocation.invoke();
}
}
}
问卷数据统计与分析
平台提供强大的数据统计功能,支持实时查看问卷结果和生成可视化图表。

统计分析实现:
// StatisticsService.java - 统计服务实现
@Service
@Transactional
public class StatisticsServiceImpl implements StatisticsService {
@Autowired
private QuestionnaireDao questionnaireDao;
@Override
public Map<String, Object> getQuestionnaireStats(Integer subjectId) {
Map<String, Object> stats = new HashMap<>();
// 获取总投票数
String totalVotesHql = "SELECT COUNT(*) FROM UserOption uo WHERE uo.subject.id = :subjectId";
Long totalVotes = questionnaireDao.getCountByHql(totalVotesHql,
Collections.singletonMap("subjectId", subjectId));
stats.put("totalVotes", totalVotes);
// 获取各选项投票分布
String optionStatsHql = "SELECT o.optionContent, o.votes FROM Option o WHERE o.subject.id = :subjectId";
List<Object[]> optionStats = questionnaireDao.findByHql(optionStatsHql,
Collections.singletonMap("subjectId", subjectId));
stats.put("optionStats", optionStats);
// 计算投票率
String userCountHql = "SELECT COUNT(*) FROM User";
Long totalUsers = questionnaireDao.getCountByHql(userCountHql, null);
if (totalUsers > 0) {
double participationRate = (double) totalVotes / totalUsers * 100;
stats.put("participationRate", String.format("%.2f%%", participationRate));
}
return stats;
}
}
<!-- 统计查询HQL映射 -->
<hibernate-mapping>
<query name="findVoteDetails">
<![CDATA[
SELECT u.realname, o.optionContent, uo.createTime
FROM UserOption uo
JOIN uo.user u
JOIN uo.option o
WHERE uo.subject.id = :subjectId
ORDER BY uo.createTime DESC
]]>
</query>
</hibernate-mapping>
问卷管理功能
管理员可以全面管理平台上的所有问卷,包括查看、编辑、删除等操作。

管理功能实现:
// AdminQuestionnaireAction.java - 管理员问卷管理
public class AdminQuestionnaireAction extends ActionSupport {
private List<Subject> questionnaires;
private Integer subjectId;
private QuestionnaireService questionnaireService;
// 获取所有问卷列表
public String list() {
questionnaires = questionnaireService.getAllQuestionnaires();
return SUCCESS;
}
// 删除问卷
public String delete() {
try {
questionnaireService.deleteQuestionnaire(subjectId);
addActionMessage("问卷删除成功!");
} catch (Exception e) {
addActionError("删除失败:" + e.getMessage());
}
return list();
}
// 批量操作
public String batchDelete() {
String[] ids = request.getParameterValues("subjectIds");
if (ids != null && ids.length > 0) {
for (String id : ids) {
questionnaireService.deleteQuestionnaire(Integer.parseInt(id));
}
addActionMessage("批量删除成功!");
}
return list();
}
}
实体模型设计
系统采用面向对象的设计思想,通过Hibernate实体类映射数据库表结构,实现业务对象与数据存储的分离。
核心实体类设计:
// User.java - 用户实体类
@Entity
@Table(name = "tb_users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "USERNAME", unique = true, nullable = false, length = 20)
private String username;
@Column(name = "PASSWORD", nullable = false, length = 20)
private String password;
@Column(name = "REALNAME", nullable = false, length = 50)
private String realname;
@Column(name = "AGE", nullable = false)
private Integer age;
@Column(name = "SEX", nullable = false, length = 50)
private String sex;
@Column(name = "PHONE", nullable = false, length = 50)
private String phone;
@Column(name = "ROLE", nullable = false, length = 50)
private String role;
// 一对多关系:用户与投票记录
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<UserOption> userOptions = new HashSet<>();
// 构造方法、getter、setter等...
}
// Subject.java - 问卷主题实体类
@Entity
@Table(name = "tb_subject")
public class Subject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "SUBJECT_NAME", nullable = false, length = 20)
private String subjectName;
@Column(name = "optionType", nullable = false, length = 20)
private String optionType;
@Column(name = "OPTION_NUM", nullable = false)
private Integer optionNum;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATE_TIME", nullable = false)
private Date createTime;
// 一对多关系:问卷与选项
@OneToMany(mappedBy = "subject", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Option> options = new ArrayList<>();
// 一对多关系:问卷与用户投票记录
@OneToMany(mappedBy = "subject", cascade = CascadeType.ALL)
private Set<UserOption> userOptions = new HashSet<>();
// 业务逻辑方法
public void addOption(Option option) {
option.setSubject(this);
this.options.add(option);
}
// 其他方法...
}
功能展望与优化
技术架构优化方向
引入Redis缓存层:针对频繁访问的问卷统计数据和用户会话信息,可以引入Redis作为缓存层,显著提升系统性能。
// Redis缓存配置示例
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
@Service
public class QuestionnaireServiceWithCache {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Cacheable(value = "questionnaireStats", key = "#subjectId")
public Map<String, Object> getQuestionnaireStatsWithCache(Integer subjectId) {
// 数据库查询逻辑...
}
}
微服务架构改造:将单体应用拆分为用户服务、问卷服务、统计服务等微服务,提升系统的可扩展性和维护性。
# 微服务配置示例
spring:
application:
name: questionnaire-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
questionnaire:
service:
user-service: http://user-service:8080
stats-service: http://stats-service:8081
功能扩展建议
实时数据分析看板:开发管理员专用的数据看板,实时展示平台关键指标和问卷参与情况。
// 实时统计服务
@Service
public class RealTimeStatsService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Scheduled(fixedRate = 5000) // 每5秒更新一次
public void pushRealTimeStats() {
Map<String, Object> stats = getRealTimeStatistics();
messagingTemplate.convertAndSend("/topic/stats", stats);
}
private Map<String, Object> getRealTimeStatistics() {
// 实时数据统计逻辑...
}
}
移动端适配与PWA支持:开发响应式界面,支持PWA技术,使系统在移动设备上具有原生应用般的体验。
// 服务工作者注册
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration =>