结对作业二
这个作业属于哪个课程 | 2021春软件工程实践S班(福州大学) |
---|---|
这个作业要求在哪里 | 结对作业二 |
结对学号 | 221801108 、221801115 |
这个作业的目标 | 阅读《构建之法》第4章,将结对作业一的原型设计具体实现出来 |
其他参考文献 | CSDN、博客园、《构建之法》 |
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 2775 | 2825 |
• Analysis | • 需求分析 (包括学习新技术) | 200 | 190 |
• Design Spec | • 生成设计文档 | 80 | 120 |
• Design Review | • 设计复审 | 15 | 15 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
• Design | • 具体设计 | 60 | 30 |
• Coding | • 具体编码 | 2000 | 2100 |
• Code Review | • 代码复审 | 30 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 360 | 320 |
Reporting | 报告 | 120 | 150 |
• Test Repor | • 测试报告 | 30 | 70 |
• Size Measurement | • 计算工作量 | 30 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 2915 | 2995 |
git仓库链接
代码规范链接
项目地址
成品展示
-
输入部分信息进行模糊搜索,也可输入完整信息进行精确搜索
-
点击论文列表查看论文详细信息,点击右上角按钮返回
-
翻页功能
-
对论文列表进行删除
-
展示Top 10热词词云,动态展示20年三大顶会的热度走势,可以暂停或者调整进度 (PS:由于数据库信息过大,所以通过云服务器访问此页面显示大概需要5分钟,需要耐心等待)
结对讨论过程描述
设计实现过程
数据库
paper表:标题title, 摘要summary, 原文链接link, 关键词keywords, 发表年份year, 会议conference
前端
采用html+css的方式进行页面设计,paperList.jsp为论文列表界面,paper.jsp为论文详细信息界面,chartPage.jsp为热词研究界面,三个界面引用外部css文件实现具体样式。
后端
功能结构图
代码说明
- paperList.jsp代码,显示的是论文列表。该页面在点击搜索、上一页以及下一页按钮时,通过PaperListServlet从数据库取出相应数据并分页显示。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<% String path = request.getContextPath(); %>
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="css/paperList.css"/>
<script type="text/javascript">
function deletePaper(paperTitle) {
var t = document.getElementById("query_text");
var p = document.getElementById("pageNum");
var c = document.getElementById("changeNum");
var tv = t.getAttribute('value');
var pv = p.getAttribute('value');
var cv = c.getAttribute('value');
if (window.confirm("是否删除此篇论文?")) {
window.location.href = "<%=path%>/PaperListServlet?operation=deletePaper&paperTitle="
+ paperTitle+ "&str=" + tv + "&pn=" + pv + "&cn=" + cv;
}
}
function show(paperTitle) {
window.location.href = "<%=path%>/PaperListServlet?operation=showPaper&paperTitle=" + paperTitle;
}
function changePage(num) {
document.getElementById("changeNum").value = num;
document.getElementById("queryForm").submit();
}
</script>
</head>
<body>
<div class="header" >
<div class="logo_box">
<img class="logo" src="./img/2.png" >
</div>
<div class="navigation_box">
<div class="nav">
<ul>
<li class="nav_post"><a class="a1" href="<%=path%>/paperList.jsp" >论文列表</a></li>
<li><a class="a2" href="<%=path%>/paperAnalysisPage/chartPage.jsp" >热门研究</a></li>
</ul>
</div>
</div>
<div id="div_paperQueryForm">
<form action="<%=path%>/PaperListServlet" method="post" id="queryForm">
<input type="text" name="query" id="query_text" value="${requestScope.info}">
<button type="button" id="query_btn" onclick="changePage(0)">搜索</button>
<input type="hidden" name="pageNum" id="pageNum" value="${requestScope.pageNum}" />
<input type="hidden" id="changeNum" name="changeNum" value="" />
</form>
</div>
</div>
<div class="main">
<div id="div_paperList">
<table id="papertable">
<tr>
<th class="num">序号</th>
<th class="title">论文题目</th>
<th class="conference">会议</th>
<th class="summary">摘要</th>
<th class="link">原文链接</th>
<th class="link">关键词</th>
<th class="delete">操作</th>
</tr>
<c:forEach items="${requestScope.paperList}" var="pl" varStatus="vs">
<tr>
<td class="num" onclick="show('${pl.title}')"> ${vs.index + 1}</td>
<td class="title" onclick="show('${pl.title}')"> ${pl.title} </td>
<td class="conference" onclick="show('${pl.title}')"> ${pl.conference} </td>
<td class="summary" onclick="show('${pl.title}')"> ${pl.summary} </td>
<td class="link" onclick="show('${pl.title}')"> <a href=${pl.link} >${pl.link}</a> </td>
<td class="link" onclick="show('${pl.title}')"> ${pl.keywords} </td>
<td class="delete"> <button type="button" id="delete_button" onclick="deletePaper('${pl.title}')"></button> </td>
</tr>
</c:forEach>
</table>
<div id="div_paging">
<button id="btn_lastPage" type="button" onclick="changePage(-1)">上一页</button>
当前第 <span class="page_num">${requestScope.pageNum}</span> 页 <button id="btn_nextPage" type="button" onclick="changePage(1)">下一页</button> 共 ${requestScope.totalPage} 页,共 ${requestScope.totalNum} 条记录
</div>
</div>
</div>
</body>
</html>
- PaperListServlet的doGet方法通过jsp传来的参数判断要执行的操作。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String oper = "";
if(req.getParameter("operation") != null)
oper = req.getParameter("operation");
switch (oper) {
case "deletePaper":
deletePaper(req, resp);
break;
case "showPaper":
showPaper(req, resp);
break;
case "queryKeyPaper":
queryKeyPaper(req, resp);
break;
default:
queryPaper(req, resp);
break;
}
}
/* 模糊查询论文并分页 */
public void queryPaper(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String str = req.getParameter("query");
String pageNum = req.getParameter("pageNum");
String changeNum = req.getParameter("changeNum");
//linesPerPage:每页行数, page:当前页码, change:页码要改变的数量, totalNum:总论文数
int linesPerPage = 20, page = 1, change = 0, totalNum = paperDAO.getTotal(str);
//totalPage:总页数
int totalPage = totalNum / linesPerPage + (totalNum % linesPerPage == 0 ? 0 : 1);
if (totalPage == 0) {
totalPage = 1;
}
if (pageNum != null && !"".equals(pageNum)) {
page = Integer.parseInt(pageNum);
}
if (changeNum != null && !"".equals(changeNum)) {
change = Integer.parseInt(changeNum);
}
if (!(page == 1 && change == -1) && !(page == totalPage && change == 1)) {
page += change;
}
if (page > totalPage) {
page = totalPage;
}
ArrayList<Paper> paperList = paperDAO.list(str, page, linesPerPage);
req.setAttribute("info", str);
req.setAttribute("pageNum", page);
req.setAttribute("totalPage", totalPage);
req.setAttribute("totalNum", totalNum);
req.setAttribute("paperList", paperList);
req.getRequestDispatcher("/paperList.jsp").forward(req,resp);
}
/* 删除论文 */
public void deletePaper(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
/* 查询单篇论文详细信息 */
public void showPaper(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
/* 通过关键词字段查询论文 */
public void queryKeyPaper(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
- PaperDAO的函数
public interface PaperDAO {
/* 返回模糊查询到的论文总数 */
int getTotal(String str);
/* 根据论文题目删除对应论文 */
void delete(String title);
/* 模糊查询分页用 */
ArrayList<Paper> list(String str, int pageNum, int lineNum);
/* 根据论文题目返回对应论文 */
Paper get(String title);
}
- KeywordDAOImpl函数
public class KeywordDAOImpl implements KeywordDAO{
/* 返回关键词内含有keyword的论文数 */
public int getTotal(String keyword) {...}
/* 根据关键词返回包含该关键词的论文列表 */
public ArrayList<Paper> keyList(String keyword,int pageNum, int lineNum) {...}
/* 根据关键词、年份、会议查询论文,返回查询总数 */
public int keyListNum(String keyword, int year, String conference) {...}
/* 按着频率大小返回频率最高的十个关键词 */
public ArrayList<Keyword> GetTop10Keywords() {...}
/* 返回十大关键词在该会议每年的频率列表 */
public ArrayList<YearFrequency> getKeywordFrequencyEachYear(String conference) {
ArrayList<YearFrequency> yearFrequencyList = new ArrayList<>();
ArrayList<Keyword> kwList = GetTop10Keywords();
YearDAO yearDAO = new YearDAOImpl();
ArrayList<Integer> years = yearDAO.getYears();
for (int y:years) {
YearFrequency yf = new YearFrequency();
yf.setYear(y);
ArrayList<Integer> frequency = new ArrayList<>();
for (int i = 0; i < 10; i++) {
frequency.add(keyListNum(kwList.get(i).getName(), y, conference));
}
yf.setFrequency(frequency);
yearFrequencyList.add(yf);
}
return yearFrequencyList;
}
}
- YearDAOImpl函数
public class YearDAOImpl implements YearDAO{
/* 返回数据库中论文所有的发表年份 */
public ArrayList<Integer> getYears() {
ArrayList<Integer> years = new ArrayList<>();
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rs = null;
try{
conn = DBUtil.getConnection();
pstm = conn.prepareStatement("select year from paper group by year order by year");
rs = pstm.executeQuery();
while (rs.next()) {
years.add(rs.getInt(1));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstm, conn);
}
return years;
}
}
心路历程和收获
- Srr(221801108):
这次结对编程是第一次从需求分析开始,到设计方案,原型创作,再到具体实现功能,最后把项目部署到服务器上,让项目投入使用,这么一个较为完整的项目流程。说实话一开始也有怀疑能不能完成这个任务,后来摸索着配置服务器从Linux到放弃转用Windows也很痛苦,好在最终还是顺利结束了,中间还经历了一次团队合作,这十来天可以说是过的非常充实了。实践出真知,这次的结对编程不仅是一次了解项目流程的实战经历,也将之前学的web知识和还在学习的JavaEE知识整合巩固了一遍,收获不小。
- Lmh(221801115):
本次结对编程负责的是实现后端的任务,一开始的几天通过看b站视频学习了servlet、jsp的知识,非常大的帮助了之后的后端代码实现。在写代码过程中遇到了很多困难,一开始大多是数据传输的问题,后面熟悉了以后写代码也就容易了起来。在此期间也看了不少相关的视频来学习,比如分页功能就是看视频写的。
评价结对队友
- 宋日荣(221801108)对林明昊(221801115)的评价:
队友很靠谱,对待任务也很认真负责,作业一发布就马上进行讨论以及任务的分配,在具体实现功能的过程中出现的问题,能够及时地查找资料找出解决方法。和队友沟通很融洽,很好地发挥出1+1>2的效果,是一个很好的结对伙伴。
- 林明昊(221801115)对宋日荣(221801108)的评价:
队友很用心地一起讨论一起完成任务,对待工作也很认真。分配的任务能够很好地完成,工作起来井井有条,和他交流很愉快也很高效,面对问题也很有见解。