结对作业二
作业基本信息
这个作业属于哪个课程 | 2021春软件工程实践 | W班 (福州大学) |
---|---|
这个作业要求在哪里 | 结对第二次作业——顶会热词统计的实现 |
结对学号 | 221801119 | 221801134 |
这个作业的目标 | 1. 学会搭建web网站 2. 深入感受结对编程 3. 学习前端知识 4. 学会部署网站到云服务器 |
其他参考文献 | CSDN、博客园、简书、git |
Github仓库地址 | PairProject |
一、 作业链接
网站链接
论文爬取站
开放账户:
用户名 | 密码 |
---|---|
admin | admin |
git仓库链接
代码规范链接
二、PSP表格
Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|
计划 | 10 | 10 |
估计这个任务需要多少时间 | 10 | 10 |
开发 | 4400 | 4710 |
原型复审 | 10 | 10 |
需求分析 (包括学习新技术) | 60 | 120 |
生成设计文档 | 40 | 30 |
设计复审 | 10 | 10 |
代码规范 | 30 | 20 |
具体设计 | 30 | 40 |
具体编码 | 4000 | 4200 |
代码复审 | 40 | 40 |
网站部署 | 30 | 60 |
测试 | 150 | 180 |
报告 | 60 | 90 |
测试报告 | 15 | 20 |
计算工作量 | 15 | 10 |
事后总结, 并提出过程改进计划 | 30 | 60 |
合计 | 4470 | 4810 |
三、 成品展示
1. 首页+登录注册+退出登录
2. 论文管理界面展示,显示论文标题、编号、会议、时间、五个关键词和摘要,以及本地论文的top10关键词
3. 论文导入界面展示,导入论文、关键词跳转功能
4. 动态分析界面展示,关键词跳转功能
5. 搜索功能,分别在本地和论文库按标题、编号、关键词和不限来搜索
6. 在论文管理界面和搜索界面实现关键词跳转功能(收藏夹界面、查看界面也可以实现关键词跳转)
7. 在论文管理界面删除论文,搜索界面添加论文
8. 在论文管理界面收藏和取消收藏论文,并给论文添加和修改备注(搜索界面和收藏夹界面也可实现相应的功能)
9. 收藏夹界面展示,查看收藏夹内的论文,添加收藏夹、移动论文至其他收藏夹功能
10. 论文查看界面展示,从原文链接导入论文的正文展示在页面上(由于论文网站访问较慢,加载原文需要等待5s左右)
11. 在论文查看界面还有添加学习笔记的功能(当论文没有添加到本地时没有该功能)
四、 结对讨论过程描述
结对过程总述
刚开始拿到题目的时候,因为都没有开发Web的经验,无从入手,有想到过使用现下流行的vue框架和spring框架,但考虑到学习时间有限就放弃了。又想到上学期web实践课有学过Html+css+js+php,于是就想着还是从最简单的开始,原生开发,没有用到什么框架。
刚开始的两天是最辛苦的,因为什么都不懂,在编程方式的选择上花费了一些时间,后面几天就顺利了起来,比如今天小欣同学完成了论文列表的UI设计,小蕾同学完成了动态分析图,明天完成对数据的爬取和导入……这次结对的分工也很明确,两人同步敲代码,当小欣同学设计论文管理等UI的时候,小蕾同学进行动态图表的设计和导入,并且包揽了服务器的部署和数据库导入的工作,为我们制定代码规范,分析项目需求,小欣同学则负责网站的逻辑跳转以及后端的操作,并在已有功能的基础上进行扩展。
讨论细节详述
-
前端开发由于之前有编写过静态的网页,所以还是很好上手的,但没有做过后端的开发,所以在后端的实现上费了一些时间,特别是前后端交互的实现,因为原生开发的局限性,在实现上我们要先用js提取html元素的数据,再用js传值给php,这个过程很容易出错,我们也试图使用过ajax来传递,但一直无法实现。最终还是用window.href来传值,虽然过程有点繁琐,但理解起来并不难,诟病就是每次都要刷新一下界面。
-
第一次接触到爬取的知识,两个人都一头雾水,各自用PHP和JAVA语言在网上查找相应的爬取的资料,我们学会了通过分析网站的HTML元素,使用正则表达式获取到需要的信息。
-
初次使用GitHub进行合作,刚开始建立了分支,但小欣同学因为操作失误,不知怎么切换到了main主分支下,并且不知不觉中已经在main下提交了5次,直到删除项目才后知后觉切错了分支,也是闹了个乌龙。(小欣gitHub使用不顺手,出现了问题)
- 由于要用到php的开发环境,于是我们都安装了wamp环境,但由于wamp里的phpmyadmin和本地的mysql有冲突,小蕾同学wamp的数据库一直是无法运行的,但好在可以使用navicat进行数据库管理,并且navicat导出的sql文件phpmyadmin也可以导入,解决了这个问题。(在wamp使用和数据库使用navicat还是phpMyadmin出现了点问题)
- 互相找bug,修改细节
五、 设计实现过程
功能结构图
过程描述
1. 首页
包含注册、登录等功能,用户登录后用SESSION来保存用户id,可随时退出登录。
2. 论文导入
实现单篇导入和批量导入论文的功能,论文导入后,将跳转至论文管理界面。然而,在实现过程中,由于觉得页面上只有一个搜索框实在过于单调,于是在搜索框的上方放置了一个词云。词云是依据网站搜集的论文的关键词定制的,出现频率高的词在词云中占比更大,这部分依靠查询数据库生成关键词数组实现。同时,用户点击关键词可跳转至相关论文(类似于搜索关键词的功能)。
其中导入文件的功能,先用JS解析文件的内容,再转换内容传递给PHP进行搜索,这里按换行符来分割每一个论文标题,再对每个标题在数据库进行搜索内容,当论文存在且未加入个人论文库,则添加至论文库。
3. 论文管理
该界面显示了用户的个人论文库。界面三分之二是论文列表,对于每一篇论文,依次显示论文标题、编号、会议、发布时间、五个关键词和摘要,并放置备注、收藏、查看和删除的操作按钮。界面三分之一显示是搜索框和本地论文库的top10关键词。
其中查看论文功能,是通过分析原文链接的HTML标签来获取论文的正文,显示到界面上(但由于访问原链接较慢,正文会加载5-10s)。在本地论文的论文查看界面,还新增添加笔记和查看学习笔记的功能。
搜索操作,每次向PHP传递搜索渠道、搜索方式和搜索内容参数进行搜索,同时还会显示搜索结果条数。
4. 收藏夹
用户可以新增收藏夹、移动论文进入另一收藏夹,这里移动论文需要向PHP传递论文ID、原收藏夹名和目标收藏夹名以及用户id参数。用户可以查看每一个收藏夹内对应的论文,这里我们设置每个用户都有一个默认收藏夹,当用户收藏论文时自动将论文收藏至默认收藏夹。
5. 动态分析
设计时将网页分为左、右两侧,左侧显示历年热词,右侧显示关键词饼图,这两个图表均引用highcharts的模板。由于左侧的条形图只能显示一年的热词词频,于是在图表上增加了三个年份的按钮,供用户切换历年视图。右侧的关键词饼图也添加了点击事件,用户点击关键词区块后,可跳转至论文管理界面,查看该关键词相关的论文。不过导入数据后,发现关键词长度过长,会挤压图表的宽度,使之变得不美观,于是最终改用上下排版。
六、 代码说明
-
判断用户是否登录
//对用户的登录状态进行异常处理 if(!isset($_SESSION["userid"])){ echo '<script>alert("请先登录!");window.location.href="../view/login.php";</script>'; } //用SESSION获取用户ID,并从数据库获取用户名 $userid=$_SESSION["userid"]; $sql = "select * from user where userid = '$userid' "; $result = $conn->query($sql); $number = mysqli_num_rows($result); $row=$result ->fetch_assoc(); echo $row["username"];
//用户退出登录处理,使用注销SESSION的方法 <?php session_start(); if(isset($_SESSION['userid'])) { unset($_SESSION['userid']); } echo ' <script> alert("退出登录成功!"); window.location.href="../index.php"; </script>'; ?>
-
论文导入
论文导入时的内部逻辑。//查看文件 function findFile(){ var inputObj = document.createElement('input'); inputObj.setAttribute('id','file'); inputObj.setAttribute('type','file'); inputObj.setAttribute("style",'visibility:hidden'); document.body.appendChild(inputObj); inputObj.click(); inputObj.onchange=readFile; } //读取文件 function readFile() { var objFile = document.getElementById('file'); var files = objFile.files; var reader = new FileReader(); var userid='<?php echo $userid;?>'; reader.readAsText(files[0], "UTF-8"); reader.onload = function(e){ var fstr = e.target.result; var str=fstr.replace(/\n/g,"%0a"); window.location.href="../form/importPaper.php? str="+str+"&userid="+userid; } }
-
为动态分析图表获取热门关键词排行
从数据库论文表逐行读取论文关键词并保存,然后遍历关键词结果,将关键词存储至一个数组中,该数组的索引为关键词,值为关键词出现次数,最后按值排序。由此便得到一个热门关键词数组。function getTopKeys($sql, $conn, $nums) { $result = $conn->query($sql); $result_array = array(); $key_array = array(); if ($result->num_rows > 0) { while($row = $result->fetch_assoc()) { $result_array[] = $row["key1"]; $result_array[] = $row["key2"]; $result_array[] = $row["key3"]; $result_array[] = $row["key4"]; $result_array[] = $row["key5"]; } } foreach($result_array as $value){ if(!empty($value)){ $value = ucfirst($value); if(array_key_exists($value, $key_array)){ $key_array[$value] += 1; } else{ $key_array[$value] = 1; } } } arsort($key_array); return array_slice($key_array, 0, $nums); //$nums指定获取热度前几的关键词 } //调用函数 $top_ten_key = getTopKeys("SELECT key1, key2, key3, key4, key5 FROM paper", $conn, 10); $top_key_array = array_keys($top_ten_key); $top_value_array = array_values($top_ten_key);
然后动态分析图表(条形图)用该数组来初始化x轴的类别:
xAxis: { categories: json_encode(array_slice($top_key_array, 0, 5)), //…… },
同样,在论文导入界面生成词云时,也用到了这个热门关键词数组:
series: [{ data:".json_encode($arr).", //…… }],
-
搜索功能
//进行搜索的时候有两种传递方式,一种是用表单POST另一种是用window.location.href,这两种接受数据的方式是不一样的,于是要对数据进行判断 if(isset($_GET['searchName'])){ $searchname=$_GET['searchName']; $searchtype=$_GET["searchSelect"]; if(isset($_GET['searching'])) $searching=$_GET['searching']; else $searching=2; } else if(isset($_POST['searchName'])){ $searchname=$_POST['searchName']; $searchtype=$_POST["searchSelect"]; $searching=$_POST["searching"]; } //搜索方式判断,由于有四种搜索方式,于是要进行判断,并分别进行数据库搜索 if($searchtype==1){ $sql="select * from paper where title like '%$searchname%' " ; } else if($searchtype==2){ $sql="select * from paper where pid like '%$searchname%' "; } else if($searchtype==3){ $sql="select * from paper where (key1 like '%$searchname%' or key2 like '%$searchname%' or key3 like '%$searchname%' or key4 like '%$searchname%' or key5 like '%$searchname%') "; } else{ $sql="select * from paper where (title like '%$searchname%' or pid like '%$searchname%' or key1 like '%$searchname%' or key2 like '%$searchname%' or key3 like '%$searchname%' or key4 like '%$searchname%' or key5 like '%$searchname%') "; }\
//搜索完毕,使用Js填充搜索结果条数 <script type="text/javascript"> var n='<?php echo $resultNum;?>'; document.getElementById("resultNum").innerHTML=n; </script>
//搜索界面收藏论文时需要传递7个参数,其中3个参数是返回搜索界面需要用到的 function addCollect(e){ window.event.returnValue=false; var pid=e.id; var userid='<?php echo $userid;?>'; var searchname='<?php echo $searchname;?>'; var searchtype='<?php echo $searchtype;?>'; var searching='<?php echo $searching;?>'; window.location.href="../form/collected.php? pid="+pid+"&userid="+userid+"&c=1"+"&view=2"+"&searching="+searching+"&searchtype="+searchtype+"&searchname="+searchname; }
-
由于a标签会导致window.location.href跳转无效,于是在代码中加入
window.event.returnValue=false; if (window.event.preventDefault) window.event.preventDefault();
-
论文列表的显示,在Html中嵌入php数据库代码后,用echo''输出
<h3 >'.$row2["title"].'</h3> <div class="blog-meta big-meta"> <small>'.$pid.'</small> <small>'.$row2["meeting"].' '.$row2["year"].'</small> <small>'.$row2["ptime"].'</small> </div> <div class="blog-meta big-meta"> <small><a href="'.$row2["link"].'" title="" target="_blank"><i class="fa fa-eye"></i>原文链接:'.$row2["link"].'</a></small> </div> <div class="blog-title-area"> <div class="tag-cloud-single"> <a href="" onclick="keyButton(this)" id="'.$row2["key1"].'"><span style="background-color:#FC9D9A">'.$row2["key1"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key2"].'"><span style="background-color:#ffacac">'.$row2["key2"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key3"].'"><span style="background-color:#FF9999">'.$row2["key3"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key4"].'"><span style="background-color:#ffacac">'.$row2["key4"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key5"].'"><span style="background-color:#FC9D9A">'.$row2["key5"].'</span></a> </div> </div> <div class="blog-content" > <div class="pp" > <p>'.$row2["summary"].'</p> </div><!-- end pp --> </div>
-
用php爬取原文链接中的正文内容,用到了正则表达式
<?php $text=file_get_contents($row2["link"]); preg_match_all('/<section id="Sec([0-9])*"(.*?)>(.*?)<\/section>/is', $text, $match); for($i=0;$i<count($match[0]);$i++){ print_r($match[0][$i]); } ?>
-
收藏夹功能
//在收藏夹界面,用PHP获取当前选中的收藏夹,使用js来使当前收藏夹样式变化 <script type="text/javascript"> var f='<?php echo $folder;?>'; document.getElementById(f).style.color="white"; document.getElementById(f).style.background="#FFCCCC"; </script>
//移动收藏夹这个功能,不仅要获取元素相对于的论文id还要获取目标收藏夹 function moveButton(e){ window.event.returnValue=false; if (window.event.preventDefault) window.event.preventDefault(); var pid=e.id; var userid='<?php echo $userid;?>'; var f=e.childNodes.item(3).id; window.location.href="../form/updateFolder.php? folder="+f+"&userid="+userid+"&pid="+pid; }
-
实现数据库收藏夹的添加和删除,用GET获取JS传递的数据,并判断是添加还是删除操作
<?php $pid=$_GET['pid']; $userid=$_GET['userid']; $c=$_GET['c']; $view=$_GET['view']; $conn = new mysqli('localhost','root','','paperdb'); $conn->query("SET NAMES utf8"); if($c==0){ $sql = "update userPaper set collect='$c' where pid = '$pid' and userid='$userid' "; $sql2 = "update userPaper set folder='' where pid = '$pid' and userid='$userid' "; } else{ $sql = "update userPaper set collect='$c' where pid = '$pid' and userid='$userid' "; $sql2 = "update userPaper set folder='默认收藏夹' where pid = '$pid' and userid='$userid' "; } $conn->query($sql); $conn->query($sql2); if($c==0) echo '<script>alert("取消收藏成功!");</script>'; else echo '<script>alert("已添加到默认收藏夹!");</script>'; if($view==1) echo '<script>window.location.href="../view/manage.php";</script>'; else if($view==3) echo '<script>window.location.href="../view/collect.php";</script>'; else{ $searchname=$_GET['searchname']; $searchtype=$_GET['searchtype']; $searching=$_GET['searching']; } ?>
<script type="text/javascript"> var userid='<?php echo $userid;?>'; var searchname='<?php echo $searchname;?>'; var searchtype='<?php echo $searchtype;?>'; var searching='<?php echo $searching;?>'; window.location.href="../view/search.php?searchName="+searchname+"&searching="+searching+"&searchSelect="+ searchtype+"&userid="+userid; </script>
-
分享网站,复制到剪切板功能,由于当前只有IE浏览器支持该功能,因此加上了对IE浏览器的判断
if (!!window.ActiveXObject || "ActiveXObject" in window){ window.clipboardData.setData("text",'http://222.77.0.199:8090/'); alert("已复制网址http://222.77.0.199:8090/至剪切板"); } else{ alert("当前浏览器暂不支持改功能!"); }
-
获取本地论文
<?php $conn = new mysqli('localhost','root','','paperdb'); $conn->query("SET NAMES utf8"); $sql="select * from paper "; $result = $conn->query($sql); $sql = "select * from userPaper where userid = '$userid' "; $res = $conn->query($sql); $resultNum=0; if($res->num_rows >0){ $row=$res->fetch_assoc(); if( $result->num_rows >0 ){ while( $row2=$result->fetch_assoc()){ $pid=$row2["pid"]; if($pid==$row["pid"]){ $resultNum++; echo ' <div class="paper"> <div class="page-wrapper"> <div class="blog-title-area" > '; if($row["remarks"]!=""){ echo '<span style="color:#FE4365">['.$row["remarks"].']</span>'; } else{ echo '<span style="color:#C0C0C0">[点击添加备注]</span>'; } echo ' <a href="" onclick="remarkButton(this)" id="'.$pid.'"></a> <a href="" style="float:right" onclick="deleteButton(this)" id="'.$pid.'" ></a> <a href="" style="float:right;" onclick="checkButton(this)" id="'.$pid.'"></a> '; if($row["collect"]==0){ echo ' <a href="" style="float:right" onclick="addCollect(this)" id="'.$pid.'"></a>'; } else{ echo' <a href="" style="float:right" onclick="deleteCollect(this)" id="'.$pid.'"></a>'; } echo' <br/><h3>'.$row2["title"].'</h3> <div class="blog-meta big-meta"> <small>'.$pid.'</small> <small>'.$row2["meeting"].' '.$row2["year"].'</small> <small>'.$row2["ptime"].'</small> </div> <div class="blog-meta big-meta"> <small> <a href="'.$row2["link"].'" title="" target="_blank"> <i class="fa fa-eye"></i>原文链接:'.$row2["link"].' </a> </small> </div> <div class="blog-title-area"> <div class="tag-cloud-single"> <a href="" onclick="keyButton(this)" id="'.$row2["key1"].'"><span style="background-color:#FC9D9A">'.$row2["key1"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key2"].'"><span style="background-color:#ffacac">'.$row2["key2"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key3"].'"><span style="background-color:#FF9999">'.$row2["key3"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key4"].'"><span style="background-color:#ffacac">'.$row2["key4"].'</span></a> <a href="" onclick="keyButton(this)" id="'.$row2["key5"].'"><span style="background-color:#FC9D9A">'.$row2["key5"].'</span></a> </div> </div> </div> <div class="blog-content" > <div class="pp" > <p>'.$row2["summary"].'</p> </div><!-- end pp --> </div> </div> </div> '; if(!($row=$res->fetch_assoc())) break; } } } } ?>
七、 心路历程和收获
小欣同学(221801134)
这次结对编程过程中,连续一周每天除了吃饭看剧上课睡觉剩余的时间就是敲代码,虽然过程很艰辛但是过的很充实,也收获了很多,熟练使用了Web的原生开发,在之后的项目中,我会更进一步使用框架前后端分离来进行开发。同时也学会使用了用GitHub进行开发合作,虽然每次push和pull都会遇到超时无法连接甚至要弄个半小时才能push上去。这次使用php语言尝试爬取了论文网站的相关信息,虽然中途出现了不少的问题,但还是收获满满。
小蕾同学(221801119)
刚接到一个新项目的时有些焦虑,但在与结对伙伴一起编程的过程中,焦虑得到了缓解。写代码的过程中,总会碰到一些一时无法解决的报错,或是意想不到的状况,虽然遇到这些困难时会很烦恼,但在尝试解决问题的过程中,也收获了很多知识。在这次项目中,学会了如何在云服务器上搭建环境、部署网,学会了如何通过Github协作、在Github上发布版本,更是提升了编程能力。总之就是,即使度过了许多头疼的夜晚,但网站成功部署并能从外部访问的那一刻真的好开心。
八、 队友评价
小欣同学(221801134)
小蕾同学很细心,对比我马虎的性格我们形成了互补,在功能的测试上她会找到BUG给我反馈。虽然她慢悠悠的性子常常让我很担心我们项目的进度,但她很努力也很积极做配置服务器、动态分析和数据库维护等多方面复杂的工作,让我们的项目进展得很顺利。
小蕾同学(221801119)
小欣同学很有干劲,每天都花很多时间来写代码、完善功能,不时会提出新的想法,让我很有动力,但是我们的步调不太一致,这导致我们初期需要一些时间来协调与沟通。不过互相适应之后,我们找到了两人都可以接受的合作模式。这次很高兴能跟小欣同学合作。