该项目是问卷发布平台的设计与实现,问卷发布平台是用于收集资料的一种方式,个人或者企业收集资料的原因可能是为了分析解决一些问题或者调查现状再或者预测某种趋势等等,通过收集资料、分析数据来达到某种目的。
近年来,出现了很多生活上的问题需要调查调研,分析预测结果,在这个大数据互联网时代,用电子问卷形式无疑是最好的选择,问卷发布平台主要用来设计问卷、生成问卷、发放填写问卷以及分析问卷结果功能,可以快速收集不同用户对于问卷上面的答案进行统计分析。
(1)后台功能需要用户登录后使用
(2)登录后首页显示该用户已创建问卷列表及相应信息:
显示网站标题、登录用户名
问卷列表,对列表中每个已经存在的问卷进行
编辑修改(设计问卷)
分析下载(查看问卷答题结果)
发布/取消发布问卷
创建问卷
(3)编辑/新建问卷,编辑已有问卷或新增新的问卷,可以参考如下图界面:
问卷标题的编辑和修改
增加不同类型的题目(选题题要求能够增加和删除选项)
保存问卷
(4)发布/取消发布问卷
问卷保存后,生成一个访问链接
在问卷列表中点击【发布】后,可以使用该链接访问并填写问卷。在问卷列表中点击【取消发布】后,该链接无法访问,例如提示”问卷未发布“。
(5)查看问卷结果:
以表格方式查看问卷填写结果
查询结果导出为EXCEL表格
数据库:MySQL8.0
Web容器:Apache Tomcat 8.5
项目管理工具:Maven
后端技术:Spring + Spring MVC + Mybatis
后端技术:LayUI(SSM框架)
IDE:IDEA
表tb_admin
id用户编号
account用户名
password密码
name名字
phone电话
remark备注
type身份
表tb_question
id问题编号
title标题
remark描述
type类型
require必选
check_style验证类型
order_style排列方式
show_style展示方式
test简答
score分数
orderby排序号
creator创建者
create_time创建时间
survey_id问卷id
表tb_survey
id问卷id
title标题
remark描述
bounds限制范围
start_time开始时间
end_time结束时间
rules规则
password密码
url问卷网址
state问卷状态
logo问卷图标
bgimg背景突变
anon匿名/不匿名
creator创建者
create_time创建时间
表tb_answer_txt
id结果id
survey_id问卷id
question_id问题id
result结果
create_time创建时间
voter投票人
表tb_answer_opt
id答案选项id
survey_id问卷id
question_id问题id
opt_id选项id
type类型
create_time创建时间
voter投票人
表tb_question_opt
id问题选项id
survey_id问卷id
question_id问题id
type类型
opt选项
orderby排序
answer答案
2.3 界面设计
登录界面
问卷界面
2.4系统详细设计
2.4.1 Controller层
Controller层负责对应前端转发来的信息的分配,其中一共有七个类。
AdminController对应这管理员的操作,其中对应的URL分别有/admin/create(用户创建操作)、/admin/delete(用户/管理员的删除操作)、/admin/update(信息更新操作)、/admin/list(用户/管理员展示列表)、/admin/query(管理员的查询操作)、/admin/detail(管理员修改前的细节展示)。
DynamicController层负责对应问卷发布的界面控制,其中对应的URL分别有/dy/{uuid}(其中的uuid是我们设计后问卷发布的)、/dy/success(问卷提交成功后的显示页面)。
IndexController层负责渲染首页的显示,其中对应的URL分别有/index(对首页进行展示)、/info(对应登录用户的个人信息)。
LoginController层负责对应登录界面的控制,其中对应的URL有/login(对应检查用户名和密码登录)、/pwd(对应管理员密码登录是否正确)。
QuestionController层负责问卷题目的管理控制,其中对应的URL分别有/question/create(问题创建操作)、/question/delete(问题的删除操作)、/question/update(文件信息更新操作)、/question/list(问题展示列表)、/question/query(问题的查询操作)、/question/detail(问题修改前的细节展示)。
RegController层负责用户注册的时候的控制,其中对应的URL的/reg(账号注册页面)。
SurveryController层负责问卷表单的控制,其中对应的URL分别有/survey/create(问卷创建操作)、/survey/delete(问卷的删除操作)、/survey/update(文件信息更新操作)、/survey/list(问卷展示列表)、/survey/query(问卷的查询操作)、/survey/detail(问卷修改前的细节展示)、/survey /preview/{id}(问卷的预览,id为问卷的编号)、/survey/upload(问卷背景的上传)、/survey/publish(问卷的公布)、/survey/submit(问卷的提交)。
2.4.2 Service层
AdminService:负责管理员/用户的业务服务,所有关于他们的内容都通过这个业务类。
QuestionService:负责问题的处理业务,其中有添加、删除等业务。
SurveyService:负责问卷的处理业务,其中的发布、预览、添加、删除等业务。
2.4.3 Mapper层
AdminMapper:用户表数据库操作层。
AnswerOptMapper:选择题回答表选项数据库操作层。
AnswerTxtMapper:简答题回答表数据库操作层。
QuestionMapper:问题表数据库操作层。
QuestionOptMapper:问题选项表数据库操作层。
SurveyMapper:问卷表数据库操作层
2.4.4 POJO层
Admin:用户实体类。
AnswerOpt:选择题选项实体类。
AnswerTxt:简答题实体类
Question:问题实体类
QuestionOpt:问题选项实体类
Survey:问卷实体类
3.系统实现
3.1系统的项目结构
3.1.1结构图
3.1.2 结构图说明
Java为主要的实现代码,其中有Controller包,entity包,mapper包,service包,utils包。
Resources为配置环境内容
Webapp为关于网页的内容配置
3.2系统全局配置说明
applicationContext.xml
<!--加载数据库配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="com.wangfan.service"/>
<!--配置数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--获取SqlSessionFactory工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--Mybatis主配置文件加载-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/wangfan/mapper/*.xml</value>
</list>
</property>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
helperDialect=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
<!--配置切面类-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--配置扫描Mapper文件-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.wangfan.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<bean class="com.wangfan.utils.SystemInit" init-method="init"></bean>
<bean class="com.wangfan.utils.ScheduleTask"/>
Spring-mvc.xml
<context:component-scan base-package="com.wangfan.controller"/>
<bean id="charset" class="java.nio.charset.Charset" factory-method="lookup">
<constructor-arg value="UTF-8"/>
</bean>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.wangfan.utils.DateConvert"></bean>
</set>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--https://www.cnblogs.com/mophy/p/8465598.html-->
<mvc:default-servlet-handler/>
<!--message-converters:HttpMessageConvert 负责将一个请求信息转换为一个对象(类型为T),将对象(类型为T)转换为响应信息。-->
<!--ConversionService:Spring MVC通过反射机制对目标处理方法签名进行分析,将请求消息绑定到处理方法入参中,核心部件是DataBinder-->
<mvc:annotation-driven conversion-service="conversionService">
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg name="defaultCharset" ref="charset"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件的最大大小,单位为字节 -->
<property name="maxUploadSize" value="10485760"></property>
<!-- 上传文件的编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*"/>
<mvc:exclude-mapping path="/reg"/>
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/dy/*"/>
<mvc:exclude-mapping path="/static/**"/>
<bean class="com.wangfan.utils.SessionInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
加载过程,加载web.xml然后在文件中加载applicationContext.xml文件,然后在applicationContext.xml文件,获取SqlSessionFactory工厂后加载加载mybatis-config.xml文件,工厂加载一个mapper映射文件进行数据库操作,加载配置扫描Mapper文件。
3.3实现说明
3.3.1 controll包
AdminController类
package com.wangfan.controller;
import com.wangfan.entity.Admin;
import com.wangfan.service.AdminService;
import com.wangfan.utils.MD5Utils;
import com.wangfan.utils.MapControl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/admin")
public class AdminController {
@Autowired
private AdminService adminService;
@GetMapping("/create")
public String v_create(){
return "admin/add";
}
@PostMapping("/create")
@ResponseBody
public Map<String,Object> create(@RequestBody Admin admin){
admin.setPassword(MD5Utils.getMD5(admin.getPassword()));
admin.setType(Admin.TYPE_ADMIN);
int result = adminService.create(admin);
if(result<=0){
//失败的情况下
return MapControl.getInstance().error().getMap();
}
return MapControl.getInstance().success().getMap();
}
@PostMapping("/delete")
@ResponseBody
public Map<String,Object> delete(String ids){
int result = adminService.deleteBatch(ids);
if(result<=0){
//失败的情况下
return MapControl.getInstance().error().getMap();
}
return MapControl.getInstance().success().getMap();
}
@PostMapping("/update")
@ResponseBody
public Map<String, Object> update(@RequestBody Admin admin){
int result = adminService.update(admin);
if(result<=0){
//失败的情况下
return MapControl.getInstance().error().getMap();
}
return MapControl.getInstance().success().getMap();
}
@GetMapping("/list")
public String list(){
return "admin/list";
}
@PostMapping("/query")
@ResponseBody
public Map<String,Object> query(@RequestBody Admin admin, ModelMap modelMap){
List<Admin> list = adminService.query(admin);
Integer count = adminService.count(admin);
return MapControl.getInstance().page(list,count).getMap();
}
@GetMapping("/detail")
public String detail(Integer id,ModelMap modelMap){
Admin admin = adminService.detail(id);
modelMap.addAttribute("admin",admin);
return "admin/update";
}
}
通过mapper扫描器,将mapper中的url进行匹配,通过调用adminService类进行数据库的调用,进行结构。
3.3.2 entity包(实体类)
Admin
package com.wangfan.entity;
import com.wangfan.utils.Entity;
import java.util.Date;
public class Admin extends Entity {
//管理员
public static Integer TYPE_ADMIN = 0;
//注册用户
public static Integer TYPE_REG = 1;
/**
* 账户名
*/
private String account;
/**
* 账户id
*/
private Integer id;
/**
* 用户名
*/
private String name;
/**
* 用户密码
*/
private String password;
/**
* 用户电话号码
*/
private String phone;
/**
* 用户备注
*/
private String remark;
private Date now;
private Integer type;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getNow() {
return now;
}
public void setNow(Date now) {
this.now = now;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
用户的实体类,提供setter和getter方法
3.3.3 mapper包
QuestionDao,提供xml的接口
package com.wangfan.mapper;
import java.util.List;
import java.util.Map;
import com.wangfan.entity.Question;
public interface QuestionDao {
public int create(Question pi);
public int delete(Map<String, Object> paramMap);
public int update(Map<String, Object> paramMap);
public List<Question> query(Map<String, Object> paramMap);
public Question detail(Map<String, Object> paramMap);
public int count(Map<String, Object> paramMap);
}
QuestionMapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "" target="_blank">http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangfan.mapper.QuestionDao">
<resultMap type="com.wangfan.entity.Question" id="Question">
<id column="id" property="id"/>
<result column="check_style" property="checkStyle"/>
<result column="create_time" property="createTime"/>
<result column="creator" property="creator"/>
<result column="id" property="id"/>
<result column="order_style" property="orderStyle"/>
<result column="orderby" property="orderby"/>
<result column="remark" property="remark"/>
<result column="required" property="required"/>
<result column="score" property="score"/>
<result column="show_style" property="showStyle"/>
<result column="survey_id" property="surveyId"/>
<result column="test" property="test"/>
<result column="title" property="title"/>
<result column="type" property="type"/>
</resultMap>
<insert id="create" keyProperty="id" useGeneratedKeys="true" parameterType="com.wangfan.entity.Question">
insert into tb_question(
check_style,
create_time,
creator,
id,
order_style,
orderby,
remark,
required,
score,
show_style,
survey_id,
test,
title,
type
)values(
#{checkStyle},
now(),
#{creator},
#{id},
#{orderStyle},
#{orderby},
#{remark},
#{required},
#{score},
#{showStyle},
#{surveyId},
#{test},
#{title},
#{type}
)
</insert>
<select id="query" resultMap="Question">
select * from tb_question
<include refid="QuestionFindCriteria"/>
<if test="order_by_column!=null and order_by_column!=''">order by ${order_by_column} ${order_by}</if>
<if test="offset!=null and rows!=null">limit ${offset} , ${rows}</if>
</select>
<select id="count" resultType="int">
select count(1) from tb_question
<include refid="QuestionFindCriteria"/>
</select>
<select id="detail" resultMap="Question">
select * from tb_question
<include refid="QuestionFindCriteria"/>
limit 1
</select>
<delete id="delete">
delete from tb_question
<include refid="QuestionFindCriteria" />
</delete>
<update id="update">
update tb_question
<include refid="QuestionUpdateCriteria"/>
<include refid="QuestionFindCriteria"/>
</update>
<sql id="QuestionFindCriteria">
<where>
<if test="checkStyle != null">and check_style = #{checkStyle}</if>
<if test="creator != null">and creator = #{creator}</if>
<if test="id != null">and id = #{id}</if>
<if test="orderStyle != null">and order_style = #{orderStyle}</if>
<if test="orderby != null">and orderby = #{orderby}</if>
<if test="remark != null">and remark = #{remark}</if>
<if test="required != null">and required = #{required}</if>
<if test="score != null">and score = #{score}</if>
<if test="showStyle != null">and show_style = #{showStyle}</if>
<if test="surveyId != null">and survey_id = #{surveyId}</if>
<if test="test != null">and test = #{test}</if>
<if test="title != null">and title = #{title}</if>
<if test="type != null">and type = #{type}</if>
</where>
</sql>
<sql id="QuestionUpdateCriteria">
<set>
<if test="updateCheckStyle != null">check_style = #{updateCheckStyle},</if>
<if test="updateCreateTime != null">create_time = #{updateCreateTime},</if>
<if test="updateCreator != null">creator = #{updateCreator},</if>
<if test="updateId != null">id = #{updateId},</if>
<if test="updateOrderStyle != null">order_style = #{updateOrderStyle},</if>
<if test="updateOrderby != null">orderby = #{updateOrderby},</if>
<if test="updateRemark != null">remark = #{updateRemark},</if>
<if test="updateRequired != null">required = #{updateRequired},</if>
<if test="updateScore != null">score = #{updateScore},</if>
<if test="updateShowStyle != null">show_style = #{updateShowStyle},</if>
<if test="updateSurveyId != null">survey_id = #{updateSurveyId},</if>
<if test="updateTest != null">test = #{updateTest},</if>
<if test="updateTitle != null">title = #{updateTitle},</if>
<if test="updateType != null">type = #{updateType},</if>
</set>
</sql>
</mapper>
其中为实现dao层中的接口,将其从数据库中进行一个提取数据。
3.3.4 service层
AdminService
package com.wangfan.service;
import com.github.pagehelper.PageHelper;
import com.google.common.base.Splitter;
import com.wangfan.entity.Admin;
import com.wangfan.mapper.AdminDao;
import com.wangfan.utils.BeanMapUtils;
import com.wangfan.utils.MapParameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
@Service
@Transactional
public class AdminService {
@Autowired
private AdminDao adminDao;
public int create(Admin pi){
return adminDao.create(pi);
}
public int deleteBatch(String ids){
int flag = 0;
List<String> list = Splitter.on(",").splitToList(ids);
for (String s : list) {
adminDao.delete(MapParameter.getInstance().addId(Integer.parseInt(s)).getMap());
flag++;
}
return flag;
}
public int delete(Integer id){
return adminDao.delete(MapParameter.getInstance().addId(id).getMap());
}
public int update(Admin admin){
Map<String, Object> map = MapParameter.getInstance().put(BeanMapUtils.beanToMapForUpdate(admin)).addId(admin.getId()).getMap();
return adminDao.update(map);
}
public List<Admin> query(Admin admin){
if(admin != null && admin.getPage() != null){
PageHelper.startPage(admin.getPage(),admin.getLimit());
}
Map<String, Object> map = MapParameter.getInstance().put(BeanMapUtils.beanToMap(admin)).getMap();
return adminDao.query(map);
}
public Admin detail(Integer id){
return adminDao.detail(MapParameter.getInstance().addId(id).getMap());
}
public int count(Admin admin){
Map<String, Object> map = MapParameter.getInstance().put(BeanMapUtils.beanToMap(admin)).getMap();
return adminDao.count(map);
}
/**
* 管理员登录
* @param account
* @param password
* @return
*/
public Admin login(String account,String password){
Map<String, Object> map = MapParameter.getInstance().add("account",account).add("password",password).getMap();
return adminDao.detail(map);
}
}
通过这一层进行业务的转接,接通一个controller中方法,进行一个数据库的调用。
3.3.5 entity层
MD5Utils
package com.wangfan.utils;
import org.springframework.util.DigestUtils;
public class MD5Utils {
//密码加密的秘钥
private static final String salt = "Survey###$$@@";
public static String getMD5(String string){
String val = string+salt;
return DigestUtils.md5DigestAsHex(val.getBytes());
}
public static void main(String[] args) {
System.out.println(getMD5("123456"));
}