结对作业二
这个作业属于哪个课程 | 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仓库地址
项目访问地址
成品展示
- 注册与登录功能展示
- 论文列表信息展示,显示所有的论文信息
- 可对论文列表进行题目、摘要、关键词的精确或模糊搜索
- 这是对摘要或关键词的搜索
- 点击查看可以查看该论文的详细内容并且进行修改
- 点击删除可以删除某行论文内容
- 点击论文分析可展示全部论文关键词中的热词TOP10,切换年份查看某年年度TOP10热词
- 点击关键词可以展示含有该关键词的论文列表信息
- 热词走势展示某年年度热词TOP10在三大顶会中各占多少(PS:因为数据较少及ICCV与ECCV隔年举行并且错开,导致展示的视觉效果不是很好)
- 最后是一个简单的三大顶会介绍
结对讨论过程描述
- 刚开始的时候因为两个人都对web技术的不熟悉,显得有点手足无措,不知道从哪入手。然后根据要求提示考虑要不要用框架,经过讨论后决定用JSP来写。于是跟着教程配置环境之后就开始了
快乐(漫长而痛苦)的编程之路。 - 分工的话主要是(051805124)同学负责整体框架的搭建+功能一大部分功能+数据库的设计+云服务器的部署(PS:真棒),(221801414)同学负责功能二的图表+界面优化。但是整个过程并不是隔绝开的,都有对彼此负责的部分进行讨论。比如具体的细节和实现的方法,还有bug的讨论。
讨论过程截图
配置环境中:
对GitHub使用的不熟悉产生的疑惑(PS:还有不熟悉导致.gitignore被(221801414)同学commit到main分支下):
图表部分讨论后采用比较常见和熟悉的Ecahrt模型,结果两个图表饼图和折线图卡住了好久
讨论折线图的实现方法(折线图数据太多绕都绕晕了)
设计实现过程
以第一次结对作业设计的原型为基础,在进一步了解和讨论了功能需求之后,派发了论文搜索、查改删操作、图表分析等功能。大致分出了四个子模块,首先是用户的登录和注册,进入到论文信息平台之后,就可以使用论文信息模块,包括模糊查询和一些基本的数据操作;论文分析模块包含了关键词图谱和折线图热词趋势,动态展示数据模型,点击可以切换对应的年份,也可以实现跳转到搜索页面;最后是关于三大顶会的背景知识模块,能对三大顶会有个大致的了解。
功能结构图
整体架构
代码说明
思路:项目采用的是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,进行后台数据处理
<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中:
//...与饼图相同
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框架,所以在初期上手的时候比较艰难。以及最后部署到服务器也是第一次尝试也遇到了一些困难,但最后也都克服了。我们的分工也很明确,遇到一些问题也都能积极的讨论解决,在讨论学习中完成各个功能需求。总的来说,不管是技术经验层面或是和队友的配合协作方面都有所收获。
- (221801414):
第一次完成一个web项目,学到了很多东西。也是没想到我也能这么肝,经常做到很晚。前期刚开始没有头绪,后面就比较上手了。整个过程中经历了很多次让人很崩溃的瞬间,如找bug!!!(明明网络上写的就是这样,运行起来就崩了)经常是毫无头绪,一卡就是好久。但是在队友的帮助下,还是比较顺利和愉快的。学到了git分支还有版本回退(commit冲突!),JSP框架及servlet的使用,echart模型的使用。
评价结对队友
- (051805124 To 221801414):
在分工负责的论文数据分析模块完成的很不错!图表功能的质量以及数据统计的效率都很有亮点!对页面和功能的细节也很细心的注意到了,并高效地进行完善,对功能的具体开发方面也有独立的思考和想法。
- (221801414 To 051805124):
在项目前期包揽了整体框架的搭建,后期云服务器的部署,业务能力很强。能够和我积极的讨论。经常在我对bug不知所措的时候指出一条明路!写代码效率很高,逻辑清晰。是一个很好的队友,可以抱大腿(不是)。