基于JSP实现的在线投票系统

Benjamin

发布日期: 2019-04-17 10:42:57 浏览量: 322
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

一、设计方案

1.1 投票系统的功能组成

投票系统功能有:选择投票和个人操作及设置,投票设置,投票结果分析,投票操作保障。

1.1.1 选择投票和个人操作及设置

  • 列出所有正在进行的投票活动的简略信息,供投票人选择:(也应可查看已有结果的投票活动情况)列出信息有:投票活动的名称、发起人、投票人数、投票时间等

  • 点击选择某投票活动后,列出该次投票活动的详细内容介绍,可选择进入投票

  • 进行投票操作:要先输入验证信息(如系统自动检测该机IP地址是否合法,检测投票人是否合法,检测验证码是否合法等),如无错误才可以进行投票,按投票规则填写投票提交表,提交完成投票

  • 设置及记录服务器地址:即让客户端机器能顺利连接到服务器端,并且该客户端IP地址合法等

  • 个人资料管理:是投票人的个人资料登记、密码设置等,可参考论坛式的注册、登陆、管理模式

1.1.2 投票设置

  • 设置候选人资料:

    • 填写个人信息(包括姓名、性别、年龄、民族、出生年月日、政治面貌、家庭住址、联系方式等……)
    • 编码:由系统自动生成,投票前后需一致
    • 推荐意见:事迹介绍、或者是个人介绍等
  • 设置投票时间:设置该次投票的开始及结束时间,投票人只能在投票有效期间投票

  • 设置投票类型、投票规则:如该次投票是单选、还是多选、(选多少人)、还是评分制(最低分、最高分多少)、是否可投弃权票、是否可投反对票、多选最终选出多少人、评分制最终选出多少人等

  • 设置合法投票者:设置投票机器的IP、投票人的名单等

1.1.3 投票结果分析

  • 排名结果:单选的结果、多选的结果(按得票数排列,胜出人显著显示)、评分制结果(按分数排列,胜出人显著显示)

  • 投票的统计信息:(需要做到实时变化以及最终结果显示)包括投票的剩余时间、投票的人数情况等

1.1.4 投票操作保障

  • IP验证:验证投票人的机器IP地址是否合法

  • 时间验证:投票时间的控制,时间到即结束该次投票活动(对局域网,可不用考虑延时问题; 但如果是基于internet的投票,要考虑: 即客户端投票时,还在有效投票时间内,但数据传到服务器端, 已经过了有效投票时间, 这时应该如何计算? 如果要使得系统设计得更合理, 希望能实现按投票当时的时间,而不是按数据到达时间, 又要防止客户端在时间上欺骗, 应该如何设计?)

  • 投票人验证:投票人是本系统用户,但要验证其是否享有对某次投票活动的投票权利,并且验证其帐号、密码的正确性,不可多次投票

  • 投票对象验证:所投的人是否存在于候选人列表中,或是否符合本次投票活动规则(因为某些投票活动可另填自己认为可以的候选人),如不符合是否当弃权处理

  • 验证码验证:防止利用软件连续投票,或自动投票

1.2 投票系统的界面组成

投票系统前台界面

投票系统后台界面

1.3 投票系统的算法、数据结构

1.3.1 投票系统前端

前端主要用到了盒子模型,使用到的数据结构主要是数组,集合来用于存储从数据库查询到的投票列表以及候选人信息。

1.3.2 投票系统后台

后台使用到的算法主要是查找算法,例如从数组或集合中查找到该用户,或者查找到用户点击的投票选项。数据结构使用了Map,ArrayList来存储提示信息和用户session。

二、开发过程

2.1 投票系统的设计

2.1.1 数据库设计

根据实验要求,抽取出四个数据库表,分别是用户表(user),投票活动表(item),候选人信息表(item_options),投票结果表(result)。其中投票活动表和候选人表是一对多关系,投票活动表和投票结果表也是一对多关系,用户表和投票活动表是一对一关系。四个表的ER图如下所示:

2.1.2 系统架构设计

本投票系统使用MVC架构,使用JSP+HTML+JQuery+CSS作为视图层,Servlet作为控制器,JDBC+MySQL作为数据模型层。架构图如下:

2.1.3 页面设计

使用盒子模型,分为上中下三个盒子,其中数据显示主要在中间区域。设计如下:

2.2 投票系统的实现

2.2.1 实现技术

利用Java语言,使用JavaWeb技术体系中的:JSP+Servlet+JDBC+MySQL技术,来开发一个web端的投票系统。

2.2.2 数据访问层实现(请看db包和dao包下的所有文件。这里只贴DB.java)

  1. public class DB {
  2. public static final String DB_DRIVER = "com.mysql.jdbc.Driver";
  3. public static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/db_vote?characterEncoding=utf-8";
  4. public static final String USER = "root";
  5. public static final String PWD = "123456";
  6. private Connection con = null;
  7. private Statement stmt = null;
  8. private PreparedStatement pstmt = null;
  9. public DB() {
  10. createConnection();
  11. try {
  12. stmt = con.createStatement();
  13. } catch (SQLException ex) {
  14. System.err.println("Error: " + ex.getMessage());
  15. }
  16. }
  17. public DB(String preparedSql) {
  18. createConnection();
  19. try {
  20. pstmt = con.prepareStatement(preparedSql,Statement.RETURN_GENERATED_KEYS);
  21. } catch (SQLException ex) {
  22. System.err.println("Error: " + ex.getMessage());
  23. }
  24. }
  25. private void createConnection() {
  26. try {
  27. Class.forName(DB_DRIVER);
  28. con = DriverManager.getConnection(DB_URL, USER, PWD);
  29. } catch (ClassNotFoundException ex) {
  30. System.err.println("Error: 类不存在!" + ex.getMessage());
  31. } catch (SQLException ex) {
  32. System.err.println("Error: 连接数据库失败!" + ex.getMessage());
  33. }
  34. }
  35. public ResultSet select(String sql) {
  36. ResultSet rs = null;
  37. try {
  38. rs = stmt.executeQuery(sql);
  39. } catch (SQLException ex) {
  40. System.err.println("Error: " + ex.getMessage());
  41. }
  42. return rs;
  43. }
  44. //preparedStatement的查询方法
  45. public ResultSet select() {
  46. ResultSet rs = null;
  47. try {
  48. rs = pstmt.executeQuery();
  49. } catch (SQLException ex) {
  50. System.err.println("Error: " + ex.getMessage());
  51. }
  52. return rs;
  53. }
  54. public int update(String sql) {
  55. int result = 0;
  56. try {
  57. result = stmt.executeUpdate(sql);
  58. } catch (SQLException ex) {
  59. System.err.println("Error: " + ex.getMessage());
  60. }
  61. return result;
  62. }
  63. //preparedStatement的更新
  64. public int update() {
  65. int result = 0;
  66. try {
  67. result = pstmt.executeUpdate();
  68. } catch (SQLException ex) {
  69. System.err.println("Error: " + ex.getMessage());
  70. }
  71. return result;
  72. }
  73. public int getInsertId(){
  74. int autoInckey = -1;
  75. ResultSet rs = null; // 获取结果
  76. try {
  77. rs = pstmt.getGeneratedKeys();
  78. if (rs.next()){
  79. autoInckey = rs.getInt(1);
  80. }
  81. } catch (SQLException e) {
  82. e.printStackTrace();
  83. }
  84. return autoInckey;
  85. }
  86. //以下方法为使用动态SQL语句方式时,设置prestmt的参数的方法
  87. //其他类型的参数对应的方法,请自行补充
  88. public void setString(int index, String value) {
  89. try {
  90. pstmt.setString(index, value);
  91. } catch (SQLException ex) {
  92. System.err.println("Error: " + ex.getMessage());
  93. }
  94. }
  95. public void setInt(int index, int value) {
  96. try {
  97. pstmt.setInt(index, value);
  98. } catch (SQLException ex) {
  99. System.err.println("Error: " + ex.getMessage());
  100. }
  101. }
  102. public void setLong(int index, long value) {
  103. try {
  104. pstmt.setLong(index, value);
  105. } catch (SQLException ex) {
  106. System.err.println("Error: " + ex.getMessage());
  107. }
  108. }
  109. public void setDouble(int index, double value) {
  110. try {
  111. pstmt.setDouble(index, value);
  112. } catch (SQLException ex) {
  113. System.err.println("Error: " + ex.getMessage());
  114. }
  115. }
  116. public void close() {
  117. try {
  118. if (stmt != null) {
  119. stmt.close();
  120. }
  121. if (pstmt != null) {
  122. pstmt.close();
  123. }
  124. if (con != null) {
  125. con.close();
  126. }
  127. } catch (SQLException ex) {
  128. System.err.println("Error: " + ex.getMessage());
  129. }
  130. }
  131. }

2.2.3 业务逻辑层实现(请看service包下的所有(14个)文件。这里只贴首页的控制器代 码IndexServlet.java以及点击投票的代码IndexVoteIdServlet.java)

IndexServlet.java

  1. @WebServlet({"/index","/index/vote/list","/index/vote/search"})
  2. public class IndexServlet extends HttpServlet {
  3. private static final long serialVersionUID = 7209195686057464382L;
  4. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  5. //由于get传递参数要乱码,所以弄成post,这个问题之后在解决
  6. //System.out.println("搜索投票");
  7. request.setCharacterEncoding("UTF-8");
  8. ItemDao itemDao = new ItemDao();
  9. String path = request.getRequestURI();
  10. List itemList;
  11. if (path.indexOf("/search") > -1){
  12. String content = request.getParameter("content").trim();
  13. itemList = itemDao.getSearchItemList(content);
  14. request.setAttribute("itemList",itemList);
  15. request.setAttribute("active",2);
  16. request.getRequestDispatcher("/WEB-INF/view/vote.jsp").forward(request,response);
  17. }
  18. }
  19. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  20. HttpSession session = request.getSession();
  21. request.setCharacterEncoding("UTF-8");
  22. ItemDao itemDao = new ItemDao();
  23. String path = request.getRequestURI();
  24. List itemList;
  25. if (path.indexOf("vote/list") > -1){
  26. session.setAttribute("active",2);
  27. itemList = itemDao.getAllItemList();
  28. request.setAttribute("itemList",itemList);
  29. request.getRequestDispatcher("/WEB-INF/view/vote.jsp").forward(request,response);
  30. }else {
  31. session.setAttribute("active",1);
  32. itemList = itemDao.getLimitItemList(5);//获取最新五条
  33. request.setAttribute("itemList",itemList);
  34. request.getRequestDispatcher("/WEB-INF/view/index.jsp").forward(request,response);
  35. }
  36. }
  37. }

IndexVoteIdServlet.java

  1. @WebServlet(name = "IndexVoteIdServlet",urlPatterns = "/index/vote")
  2. public class IndexVoteIdServlet extends HttpServlet {
  3. private static final long serialVersionUID = -6818273018879095397L;
  4. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  5. HttpSession session = request.getSession();
  6. int id = Integer.parseInt(request.getParameter("id"));
  7. String[] selectIds = request.getParameterValues("answer");
  8. String ip = this.getRemortIP(request);
  9. ResultDao resultDao = new ResultDao();
  10. User user = (User)session.getAttribute("user");
  11. int success = resultDao.add(user.getId(),id,selectIds,ip);
  12. if (success > 0){
  13. session.setAttribute("msg","投票成功");
  14. response.sendRedirect(request.getContextPath() + "/index/vote/list");
  15. }else{
  16. session.setAttribute("msg","投票失败,请不要用同一ip投票");
  17. response.sendRedirect(request.getContextPath() + "/index/vote?id=" + id);
  18. }
  19. }
  20. @SuppressWarnings("rawtypes")
  21. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  22. HttpSession session = request.getSession();
  23. session.setAttribute("active",2);
  24. int id = Integer.parseInt(request.getParameter("id"));
  25. ItemDao itemDao = new ItemDao();
  26. OptionDao optionDao = new OptionDao();
  27. Item item = itemDao.getItemById(id);
  28. if (itemDao.getVoteStatus(item.getStartTime(),item.getStopTime()) != 2){
  29. //判断是否可以投票,即投票是否结束,防攻击
  30. response.sendRedirect(request.getContextPath() + "/index/vote/list");
  31. return;
  32. }
  33. List optionList = optionDao.getOptionList(item.getId(),item.getAllVoteCount());
  34. request.setAttribute("item",item);
  35. request.setAttribute("optionList",optionList);
  36. String isWaiver="不可以弃权";
  37. String isOppose="不可以投反对票";
  38. System.out.println(item.getIsWaiver());
  39. System.out.println(item.getIsOppose());
  40. if( "1".equals(item.getIsWaiver()) ){
  41. isWaiver="可以弃权";
  42. }
  43. if( "1".equals(item.getIsOppose()) ){
  44. isOppose="可以投反对票";
  45. }
  46. if( "1".equals(item.getType()) ) {
  47. request.setAttribute("type","(单选,"+isWaiver+","+isOppose+")");
  48. }else if( "2".equals(item.getType()) ){
  49. request.setAttribute("type","(多选,可选人数:"+item.getNumber()+","+isWaiver+","+isOppose+")");
  50. }else {
  51. request.setAttribute("type","(评分制,可选人数:"+item.getNumber()+","+isWaiver+","+isOppose+")");
  52. }
  53. request.getRequestDispatcher("/WEB-INF/view/info.jsp").forward(request,response);
  54. }
  55. /*获取用户的ip,防止多次投票*/
  56. private String getRemortIP(HttpServletRequest request) {
  57. if (request.getHeader("x-forwarded-for") == null) {
  58. return request.getRemoteAddr();
  59. }
  60. return request.getHeader("x-forwarded-for");
  61. }
  62. }

2.2.4 表现层实现(请查看../WebContent/WEB-INF/view下的所有(14个)文件,这里只贴添 加投票的页面代码:addvote.jsp)

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@include file="head.jsp"%>
  3. <div class="container-wrap">
  4. <footer>
  5. <div class="row copyright" >
  6. <div class="col-md-12 text-center">
  7. <big>新增投票</big>
  8. </div>
  9. </div>
  10. </footer>
  11. </div><!-- END container-wrap -->
  12. <div class="container-wrap">
  13. <div id="fh5co-contact">
  14. <div class="row">
  15. <div class="col-md-2 col-md-push-1 animate-box">
  16. </div>
  17. <div class="col-md-6 col-md-push-1 animate-box">
  18. <form action="${pageContext.request.contextPath}/admin/vote/add" method="post" id="vote_add_form">
  19. <div class="row">
  20. <div class="col-md-12">
  21. <div class="form-group">
  22. <label for="" class="col-sm-3 control-label">主题</label>
  23. <div class="col-sm-9">
  24. <input type="text" name="theme" class="form-control" placeholder="">
  25. </div>
  26. </div>
  27. </div>
  28. <div class="col-md-12">
  29. <div class="form-group">
  30. <label for="" class="col-sm-3 control-label">发起人</label>
  31. <div class="col-sm-9">
  32. <input type="text" class="form-control" value="${user.realName}" placeholder="" disabled>
  33. </div>
  34. </div>
  35. </div>
  36. <div class="col-md-12">
  37. <div class="form-group">
  38. <label for="" class="col-sm-3 control-label">发起时间</label>
  39. <div class="col-sm-9">
  40. <input type="text" name="start_time" class="form-control" placeholder="如2018-10-30 15:07:02">
  41. </div>
  42. </div>
  43. </div>
  44. <div class="col-md-12">
  45. <div class="form-group">
  46. <label for="" class="col-sm-3 control-label">结束时间</label>
  47. <div class="col-sm-9">
  48. <input type="text" name="stop_time" class="form-control" placeholder="如2018-10-30 15:07:02">
  49. </div>
  50. </div>
  51. </div>
  52. <div class="col-md-12">
  53. <div class="form-group">
  54. <label for="" class="col-sm-3 control-label">投票类型</label>
  55. <div class="col-sm-9">
  56. <select name="type" id="" class="form-control">
  57. <option value="1">单选</option>
  58. <option value="2">多选</option>
  59. <option value="3">评分制</option>
  60. </select>
  61. </div>
  62. </div>
  63. </div>
  64. <div class="col-md-12">
  65. <div class="form-group">
  66. <label for="" class="col-sm-3 control-label">投票规则</label>
  67. <div class="col-sm-9">
  68. <select name="is_waiver" class="form-control">
  69. <option value ="0" selected="selected">不能弃权</option>
  70. <option value ="1">可以弃权</option>
  71. </select>
  72. <select name="is_oppose" class="form-control">
  73. <option value ="0" selected="selected">不能反对</option>
  74. <option value ="1">可以反对</option>
  75. </select>
  76. <input type="text" name="number" class="form-control" placeholder="多选或评分制的可选人数">
  77. </div>
  78. </div>
  79. </div>
  80. <div class="col-md-12">
  81. <div class="form-group">
  82. <label for="" class="col-sm-3 control-label">候选人名单</label>
  83. <div class="col-sm-9">
  84. <textarea rows="5" cols="7" name="option_content" class="form-control" placeholder="候选人名之间用按回车键(换行)隔开,如:&#13;&#10;陈铭海&#13;&#10;李世民"></textarea>
  85. </div>
  86. </div>
  87. </div>
  88. <div class="col-md-12">
  89. <div class="form-group">
  90. <label for="" class="col-sm-4 control-label"></label>
  91. <input type="button" id="submit1" name="select" value="提 交" class="btn btn-primary btn-modify">
  92. <a href="${pageContext.request.contextPath}/admin"><input type="button" value="返 回 " class="btn btn-primary btn-modify"></a>
  93. </div>
  94. </div>
  95. </div>
  96. </form>
  97. </div>
  98. </div>
  99. </div>
  100. </div><!-- END container-wrap -->
  101. <%@include file="foot.jsp"%>
  102. <script type="text/javascript">
  103. $('#submit1').click(function () {
  104. var date = /^(\d{4})-(\d{2})-(\d{2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
  105. var theme = $('input[name=theme]').val();
  106. var start_time = $('input[name=start_time]').val();
  107. var stop_time = $('input[name=stop_time]').val();
  108. var number = $('input[name=number]').val();
  109. var option_content = $('textarea').val();
  110. var type= $('select[name=type]').val();
  111. if(!theme || (theme.length > 255)){
  112. alert('主题不能为空且长度不能超过255个字符');
  113. return;
  114. }
  115. if(!start_time){
  116. alert('发起时间不能为空');
  117. return;
  118. }
  119. if(!date.test(start_time)){
  120. alert('发起时间要符合日期格式');
  121. return;
  122. }
  123. if(!stop_time){
  124. alert('结束时间不能为空');
  125. return;
  126. }
  127. if(!date.test(stop_time)){
  128. alert('结束时间要符合日期格式');
  129. return;
  130. }
  131. if(type !="1" && !number){
  132. alert('多选或评分制类型的投票的可选人数不能为空');
  133. return;
  134. }
  135. if(!option_content){
  136. alert('选项不能为空');
  137. return;
  138. }
  139. var start_date = new Date(start_time);
  140. var stop_date = new Date(stop_time);
  141. if (stop_date.getTime() < start_date.getTime()){
  142. alert('结束时间要比开始时间大');
  143. return;
  144. }
  145. document.getElementById('vote_add_form').submit();
  146. });
  147. </script>

2.3 投票系统的测试

2.3.1 黑盒测试(经过测试,发现功能基本完成)

安全机制(防止同一ip重复投票,未登录不能投票,非管理员不能进入后台)

用户登录成功后,查看所有投票活动

管理员进入后台

管理员进入后台后,可以添加投票活动

给新添投票活动投票

2.3.2 系统测试

经过各个系统软件的集合,测试结果显示本软件暂时没有发现bug。

三、技术讨论

3.1 存在问题

  • 没有实现评分制的投票类型

    解决方案:由于在数据库设计阶段有预留该字段,因此可以一个输入评分框给用户输入来解决

  • 没有对用户登录次数进行限制

    解决方案:可以在后台对用户每次登录,在session里添加一次,再用ajax和在后台进行测试

  • 当访问人数多时,可能会卡

    解决方案:可以使用缓存来缓存所有投票活动,或者或者前几天的投票活动,来加快访问速度

3.2 改进方向

  • 可以加上安全机制,例如使用 Spring Security框架来加入安全机制

  • 可以继续开发手机APP或微信小程序这些手机端应用

  • 可以使用Redis作为缓存加快访问速度

上传的附件 cloud_download 基于JSP实现的在线投票系统.7z ( 1.24mb, 3次下载 )
error_outline 下载需要14点积分

发送私信

你愿苦其自身,必将掌声雷动

8
文章数
11
评论数
最近文章
eject