结对作业二

这个作业属于哪个课程 2021春软件工程实践W班(福州大学)
这个作业要求在哪里 结对作业二
结对学号 221801414、051805124
这个作业的目标 完成一个论文信息平台的论文列表修改,顶会热词统计分析等部分功能
其他参考文献 《构建之法》

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 50 80
• Estimate • 估计这个任务需要多少时间 50 80
Development 开发 3220 3700
• Analysis • 需求分析 (包括学习新技术) 60 60
• Design Spec • 项目编程 3000 3400
• Design Review • 项目代码复审 20 20
• Design • 部署到服务器 120 200
• Test • 测试(网页测试,提交修改) 20 20
Reporting 报告 60 80
• Test Repor • 项目报告 40 60
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 20 20
合计 3330 3860

代码规范链接

代码规范

Github仓库地址

Github项目地址

项目访问地址

项目访问地址

成品展示

  • 注册与登录功能展示

image

  • 论文列表信息展示,显示所有的论文信息

image

  • 可对论文列表进行题目、摘要、关键词的精确或模糊搜索

image

  • 这是对摘要或关键词的搜索

image

  • 点击查看可以查看该论文的详细内容并且进行修改

image

  • 点击删除可以删除某行论文内容

image

  • 点击论文分析可展示全部论文关键词中的热词TOP10,切换年份查看某年年度TOP10热词

image

  • 点击关键词可以展示含有该关键词的论文列表信息

image

  • 热词走势展示某年年度热词TOP10在三大顶会中各占多少(PS:因为数据较少及ICCV与ECCV隔年举行并且错开,导致展示的视觉效果不是很好)

image

  • 最后是一个简单的三大顶会介绍

image

结对讨论过程描述

  • 刚开始的时候因为两个人都对web技术的不熟悉,显得有点手足无措,不知道从哪入手。然后根据要求提示考虑要不要用框架,经过讨论后决定用JSP来写。于是跟着教程配置环境之后就开始了快乐(漫长而痛苦)的编程之路。
  • 分工的话主要是(051805124)同学负责整体框架的搭建+功能一大部分功能+数据库的设计+云服务器的部署(PS:真棒),(221801414)同学负责功能二的图表+界面优化。但是整个过程并不是隔绝开的,都有对彼此负责的部分进行讨论。比如具体的细节和实现的方法,还有bug的讨论!

讨论过程截图

配置环境中:

image

对GitHub使用的不熟悉产生的疑惑(PS:还有不熟悉导致.gitignore被(221801414)同学commit到main分支下):

image

图表部分讨论后采用比较常见和熟悉的Ecahrt模型,结果两个图表饼图和折线图卡住了好久

image

讨论折线图的实现方法(折线图数据太多绕都绕晕了)

image

设计实现过程

以第一次结对作业设计的原型为基础,在进一步了解和讨论了功能需求之后,派发了论文搜索、查改删操作、图表分析等功能。大致分出了四个子模块,首先是用户的登录和注册,进入到论文信息平台之后,就可以使用论文信息模块,包括模糊查询和一些基本的数据操作;论文分析模块包含了关键词图谱和折线图热词趋势,动态展示数据模型,点击可以切换对应的年份,也可以实现跳转到搜索页面;最后是关于三大顶会的背景知识模块,能对三大顶会有个大致的了解。

功能结构图

image

整体架构

image

代码说明

思路:项目采用的是JSP框架,分别建立对应的jsp文件,实现页面之间的跳转连接。后端采用的是JDBC库与Mysql相连接,通过sql语句将增删改查操作封装成数据接口。给界面的组件设置监听动作,将表单数据提交至servlet文件,再由servlet调用数据接口实现动态请求。

  • 前台登录注册页面
<h1>欢迎使用,</h1>
    <h1>论文信息平台</h1>
    <div>
        <div>
            <form action="douseradd" method="post">
                
                <div id="divTop">
                	<label for="user">账号:</label>
                	<input type="text" name="username" placeholder="请输入账号" size="40" />
                </div>
            
            	<div id="divTop">
            		<label for="pwd">密码:</label>
                	<input type="password" name="pwd" placeholder="请输入密码" size="40" autocomplete />
            	</div>
            	
            	<div id="divTop">
            		<label for="pwd">密码:</label>
                	<input type="password" name="repwd" placeholder="请输入再次密码" size="40" autocomplete />
            	</div>
            
                <input id="button1" type="submit" value="立即注册"/>
                
            </form>
        </div>
        <div class="label">
        	<label>已有账号?去</label><label><a href="login.jsp">登录</a></label>
  • 登录注册接口
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		
		String userName = request.getParameter("username");
		String psw = request.getParameter("pwd");
		String repsw = request.getParameter("repwd");
		if(userName.equals("") || psw.equals("") || repsw.equals("")) {
			PrintWriter out = response.getWriter();
			out.write("<script>");
			out.write("alert('请输入账号密码');");
			out.write("window.location.href='register.jsp';");
			out.write("</script>");
			return;
		}
		if(!psw.equals(repsw)) {
			PrintWriter out = response.getWriter();
			out.write("<script>");
			out.write("alert('两次密码不一致');");
			out.write("window.location.href='register.jsp';");
			out.write("</script>");
			return;
		}
		User user = new User(userName,psw);
		
		//输入到数据库
		int count = UserDao.insert(user);
		
		if(count > 0) {
			response.sendRedirect("login.jsp");
		}else {
			PrintWriter out = response.getWriter();
			out.write("<script>");
			out.write("alert('用户注册失败');");
			out.write("window.location.href='register.jsp';");
			out.write("</script>");
		}
	}
  • 论文搜索界面,通过搜索按钮提交表单到servlet,进行后台数据处理,再通过后台回传数据,<c:if test=""></c:if>实现多选框选项不变。
<div>
    ...
    <div class="contentright">
    	<div>
    		<form action="dopapersearch" method="get">
    			<select name="plugin">
    				<c:if test="${option=='title'}">
						<option value ="title" selected="selected" >题目</option>
					</c:if>
					<c:if test="${option!='title'}">
						<option value ="title" >题目</option>
					</c:if>
					<c:if test="${option=='conclude'}">
						<option value ="conclude" selected="selected" >摘要</option>
					</c:if>
					<c:if test="${option!='conclude'}">
						<option value ="conclude" >摘要</option>
					</c:if>
					<c:if test="${option=='keyword'}">
						<option value ="keyword" selected="selected" >关键词</option>
					</c:if>
					<c:if test="${option!='keyword'}">
						<option value ="keyword" >关键词</option>
					</c:if>
				</select>
	            <input class="searchInput" type="text" name="searchContent" placeholder="请输入论文题目,关键词等"  value="${content}" size="40" />
	            <input class="searchBtn" id="button" type="submit" value="搜索"/>
            </form>
    	</div>
  • servlet接收到action及其表单调用后台数据接口,并进行分页
@WebServlet("/dopapersearch")
public class DoPaperSearch extends HttpServlet {

	static String curContent = "";
	static String curOption = "";
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//当前页
		int curPage = 1;
        
		//······
        
		list = PaperDao.searchPaper(content,option,curPage);
		for(int i=0;i < list.size();i++) {
			list.get(i).setKeyword(PaperDao.getKeyWord(list.get(i).getPaperNum()));
		}
		
		int itemNum = PaperDao.getItemNum(content, option);
		int totalPage = itemNum/6;
		if(itemNum%6 != 0) {
			totalPage++;
		}
			
		request.setAttribute("option", option);
		request.setAttribute("content", content);
		request.setAttribute("totalPage", totalPage);
		request.setAttribute("curPage", curPage);
		request.setAttribute("totalNum", itemNum);
		request.setAttribute("curContent", curContent);
		request.setAttribute("paperlist", list);
		request.getRequestDispatcher("index_two_search.jsp").forward(request, response);
	}
}
  • 论文搜索、查看、修改、删除接口,通过执行sql语句,获取对应的对象数组(通过sql语句limit的使用实现分页使得数据的获取速度更快)
        public static ArrayList<PaperBean> searchPaper(String sql){
		Connection conn = Basedao.getconnection();
		ResultSet rs = null;
		PreparedStatement ps = null;
		ArrayList<PaperBean> res = new ArrayList<PaperBean>();
		try {
			ps = conn.prepareStatement(sql);
			rs = ps.executeQuery();
			while(rs.next()) {
				res.add(new PaperBean(
						rs.getInt("id"),
						rs.getString("title"),
						rs.getString("link"),
						rs.getString("conclude"),
						rs.getString("year"),
						rs.getString("magazine")
						));
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			Basedao.closeAll(ps,conn);
		}
		return res;
	}
    
        public static int deletePaper(int id) {
		String sql = "delete from article where id = "+ id;
		System.out.println(sql);
		return Basedao.deletePaper(sql);
	}
	
	public static String getKeyWord(int id) {
		String sql = "select * from keywords where id = "+ id;
		System.out.println(sql);
		return Basedao.getKeyWord(sql);
	}
        public static int updatePaper(int id, String title, String year, String magazine, String link, String abst) {
		String sql = "update article set title = \'"+title+"\', year = "+year+", conclude = \'"+abst+"\', link = \'"+link+"\', magazine = \'"+magazine+"\' where id = "+id;
		System.out.println(sql);
		return Basedao.updatePaper(sql);
	}
  • 饼图中关键词TOP10的统计参考了寒假作业二的WordCount来实现(map存储关键词和count键值对并排序取前十):
        Map<String, Integer> map = new HashMap<String,Integer>();
		
		String[] keywords = new String[MAX_NUM];
		int[] occur = new int[MAX_NUM];
		int i = 0;
		
		String year = request.getParameter("year");
		ArrayList<String> list = new ArrayList<String>();
		if(year.equals("total")) {
			list = PaperDao.getKeyWords();
		} else {
			list = PaperDao.getKeyWordsByYear(year);
		}
			for(String str : list) {
			if (map.containsKey(str)) {
    			int occurs = map.get(str);
    			map.put(str, occurs+1);
    		} else {
    			map.put(str, 1);
    		}
		}
		map = map.entrySet().stream()
	    		.sorted(Map.Entry.<String, Integer>comparingByValue()
	            .reversed().thenComparing(Map.Entry.comparingByKey())).limit(MAX_NUM)                      
	            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue
	            		, (e1, e2) -> e1, LinkedHashMap::new));
		ArrayList<HashMap.Entry<String, Integer>> Top10 = new ArrayList <HashMap.Entry <String, Integer> > (map.entrySet());
		for(HashMap.Entry<String, Integer> Map : Top10) {
            keywords[i] = Map.getKey();
            occur[i++] = Map.getValue();
        }
		
		request.setAttribute("year", year);
		request.setAttribute("keyword", keywords);
		request.setAttribute("occur", occur);
		request.getRequestDispatcher("index_three_chart.jsp").forward(request, response);
	}		
}
  • 折线图中年度关键词TOP10统计与饼图相同,TOP10中不同顶会所占的数量采用了很笨的方法(空间消耗大),定义了一个Map类型的ArrayList,将三大顶会十大热词出现次数分别存入十个Map中,然后在遍历myMap得到要展示的数据:
//...与饼图相同
               ArrayList<String> magazineList1 = PaperDao.getMagazineByYK(year, keywords[0]);
		//省略9个list获取
		
		Map<String, Integer> map1 = PaperDao.getMagazineOccur(magazineList1);
		//省略9个map获取
		
		ArrayList<Map<String , Integer>> myMap  = new ArrayList<Map<String,Integer>>();
		myMap.add(0,map1);
		//省略9个.add
		
		int j = 0;
		int[] occurCVPR = new int[KEYWORD_MAX_NUM];
		int[] occurICCV = new int[KEYWORD_MAX_NUM];
		int[] occurECCV = new int[KEYWORD_MAX_NUM];
		for (Map<String, Integer> m : myMap) {
			if(m.containsKey("CVPR")) {
				occurCVPR[j] = (m.get("CVPR"));
			} else {
				occurCVPR[j] = 0;
			}
	        if(m.containsKey("ICCV")) {
	        	occurICCV[j] = (m.get("ICCV"));
	        } else {
	        	occurICCV[j] = 0;
	        }
	        if(m.containsKey("ECCV")) {
	        	occurECCV[j] = (m.get("ECCV"));
	        } else {
	        	occurECCV[j] = 0;
	        }
	        j++;
	    }
  • 数据分析接口,高效分析整合数据
        public static ArrayList<String> getKeyWordsByYear(String year) {
		String sql = "select keyword from keywords where id in "
				+ "(select id from article where year = " + year + ")";
		System.out.println(sql);
		return Basedao.getKeyWords(sql);
	}
	public static ArrayList<String> getMagazineByYK(String year, String keyword) {
		String sql = "select magazine from article where year = "+ year + " and id in "
				+ "(select id from keywords where keyword = \'" + keyword + "\') ";
		System.out.println(sql);
		return Basedao.getMagazine(sql);
	}
	public static Map<String, Integer> getMagazineOccur(ArrayList<String> list){
		Map<String, Integer> map = new HashMap<String,Integer>();
		for(String str : list) {
			if (map.containsKey(str)) {
    			int occurs = map.get(str);
    			map.put(str, occurs+1);
    		} else {
    			map.put(str, 1);
    		}
		}
		return map;
	}

心路历程和收获

  • (051805124):
    之前并没有接触过JSP框架,所以在初期上手的时候比较艰难。以及最后部署到服务器也是第一次尝试也遇到了一些困难,但最后也都克服了。我们的分工也很明确,遇到一些问题也都能积极的讨论解决,在讨论学习中完成各个功能需求。总的来说,不管是技术经验层面或是和队友的配合协作方面都有所收获。
    image
  • (221801414):
    第一次完成一个web项目,学到了很多东西。也是没想到我也能这么肝,经常做到很晚。前期刚开始没有头绪,后面就比较上手了。整个过程中经历了很多次让人很崩溃的瞬间,如找bug!!!(明明教程上写的就是这样,运行起来就崩了)经常是毫无头绪,一卡就是好久。但是在队友的帮助下,还是比较顺利和愉快的。学到了git分支还有版本回退(commit冲突!),JSP框架及servlet的使用,echart模型的使用。
    image

评价结对队友

  • (051805124 To 221801414):
    在分工负责的论文数据分析模块完成的很不错!图表功能的质量以及数据统计的效率都很有亮点!对页面和功能的细节也很细心的注意到了,并高效地进行完善,对功能的具体开发方面也有独立的思考和想法。
  • (221801414 To 051805124):
    在项目前期包揽了整体框架的搭建,后期云服务器的部署,业务能力很强。能够和我积极的讨论。经常在我对bug不知所措的时候指出一条明路!写代码效率很高,逻辑清晰。是一个很好的队友,可以抱大腿(不是)。
posted @ 2021-03-31 18:36  鲸落12138  阅读(94)  评论(6编辑  收藏  举报