结对作业二
结对作业二
这个作业属于哪个课程 | 2021春软件工程实践|S班 |
---|---|
这个作业要求在哪里 | 结对作业二 |
结对学号 | 221801214、221801225 |
这个作业的目标 | fork git仓库、原型设计实现 |
其他参考文献 | CSDN、百度、b站 |
一.Github仓库地址
项目github地址:https://github.com/Leizhenkang/PairProject
代码规范: https://github.com/Leizhenkang/PairProject/blob/dev/221801214_221801225/codestyle.md
二、PSP表格
1、PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 50 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 20 |
Development | 开发 | 1000 | 1200 |
• Analysis | • 需求分析 (包括学习新技术) | 1800 | 2000 |
• Team Communication | • 结对讨论 | 360 | 420 |
• Design | • 界面原型设计 | 10 | 20 |
Reporting | 报告 | 60 | 60 |
• Test Repor | • 测试报告 | 30 | 30 |
• Size Measurement | • 计算工作量 | 30 | 30 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计 | 120 | 240 |
• Size Measurement | 合计 | 3380 | 4070 |
三、项目访问链接
项目访问链接:
四、成品展示
1.论文列表的展示: 每页包含五篇论文,可通过点击上下页查看不同论文,点击查看可显示文章具体信息
2.论文列表题目搜索:可通过标题搜索论文,当论文标题不完整时,则会使用模糊搜索来查询结果,返回结果以论文编号来进行排序
3.论文列表摘要搜索:通过摘要对论文进行查找(也可实现模糊搜索)
4.论文列表关键词搜索:通过关键词来查找论文
5.关键词图谱:展示了论文列表中出现次数最多的关键词,点击关键词可展开相关论文。
6.顶会论文-年份走势图:趋势图反应三大会议不同时间提交论文数量
五、结对讨论过程描述
-
结对作业二在返校之后,因为我们在同一个宿舍,所以本次作业的交流大部分都是面对面进行的
-
第一次讨论,主要分配了本次结对的任务,决定使用前后端分离的Vue+Springboot来完成项目
-
在对各自负责部分有稍微的了解后,又讨论了数据库如何建立的初次方案,以下是曾在ipad做过的记录(精简).
-
由于结对过程中两个人都不太熟悉github的操作,所以很多时候都是互传代码或项目来实现交互的,有些后悔,应该在一开始之前,就先学习git使用的...以致后面的commit次数有些集中且不足.
-
结对的过程遇到的困难和解决方法:
-
项目不熟悉,知识欠缺 --------花了很多时间进行学习,看视频(可见b站网页历史记录一排下来的Springboot 和Vue.....)
-
对爬虫技术的不了解 ------- 用助教爬取的数据建立数据库
-
前端界面没有使用组件,常常因为样式是否美观,合理而重新设计,最后在不断地改变之后,与我们第一次的原型设计稍微有些偏差。
-
后端在写关键词搜索的时候没有找到合适的算法,效率低下。 ------自学以及请教大佬。
-
六.设计实现过程
项目技术:采用前后端分离Vue+SpringBoot
功能结构图
设计思路:
-
数据库:数据库表实现较为简单,分为paper论文表,存储论文列表所需相关信息;keyword表,当中存储关键词内容,以及关键词id和论文paper_id,多对多关系;还有一张keyword_count记录改关键词出现次数,以供关键词图谱功能实现的便利。
-
.前端项目结构图: Addpaper.vue 查看页面的组件, Echartstest.vue 热词分析图谱的组件 ,HdView.vue 导航栏组件 , Laa.vue Element-Ui引入的下拉选择框组件 , PaperItem.vue显示单篇文章的组件 ,ShowView显示一页文章的组件以及换页等方法 , Trend.vue 趋势图组件,折线图 , index,js 路由配置 , App.vue 渲染主页面
-
后端项目结构图:按照接口要求设计, 1.建立实体类(entity),跟数据库表字段保持一致 。 2. 2.建立mapper接口,定义要操作数据库的动作 。 3.建立mapper的xml文件,写具体的sql语句 。 4.建立service类,处理业务逻辑 5.在controller类展示处理的结果 。 不过由于对整个SpingBoot不是很理解,写到后面对数据库进行操作的时候就按原生方式写了,熟悉之后会组件按照这个接口设计来写接口的。
七.关键代码展示
- 利用axios get方法调用后端接口,并且处理后端数据,利用循环将取出的最多十个关键字存储到前端并且渲染
mounted() {
const _this = this;
axios({
url: "http://localhost:8080/keyword",
}).then((res) => {
for (let i = 0; i < 10; i++) {
// console.log(res.data[i].count);
// console.log(res)
_this.data2[i] = res.data[i].count;
_this.data1[i] = res.data[i].content;
}
console.log(_this.data1);
console.log(_this.data2);
this.drawLine();
});
// this.$nextTick(this.drawLine());
},
-
导航栏的html,利用了vue.js的路由链接
<template> <div class="header"> <div class="logo"> <img src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4090351118,2973307229&fm=26&gp=0.jpg" alt="" /> <div class="text">论文系统</div> </div> <div class="nav"> <router-link to="/show">论文列表</router-link> <router-link to="/echar">热词分析</router-link> <router-link to="/trend">趋势图</router-link> </div> </div> </template>
-
用$router.relace跳转页面,点击查看按钮可以查看单独一篇文章,传递当前循环的参数进去,以便渲染查看页面
methods: {
deleted() {
location.reload();
},
See(e) {
window.location.href = e;
},
change: function () {
this.$router.replace("/add");
this.$nextTick(() => {
eventVue.$emit("aa", this.itemObj);
});
},
mounted() {
eventVue.$on("ak", (message) => {
this.itemObj = message;
console.log(this.itemObj);
});
},
},
-
滚动事件的绑定,实现换页自动滚动到头部
mounted() { //此处true 需要加上 不加滚动事件可能绑定不成功 window.addEventListener("scroll", this.handleScroll, true); if (this.searchtext != null) { this.searchtext = this.$route.params.id; } }, handleScroll() { let scrolltop = document.documentElement.scrolltop || document.body.scrollTop; scrolltop > 30 ? (this.gotop = true) : (this.gotop = false); },
-
渲染列表页面的参数接受和数据导入,判断是否从热词分析点击事件过来,接受热词点击页面的参数,可以用来实现热词分析中点击关键词直接跳转到展示页面并且搜索该关键词的论文
created() { axios({ url: "http://localhost:8080/changepage", }).then((res) => { this.listArr2 = res.data; this.listArr1 = this.listArr2.slice(0, 5); if (this.searchtext != null) { this.value = "关键词"; this.selectwhat = 3; this.search1(); } else { this.selectwhat = 1; } }); }
-
查找和搜索的功能实现,使用过滤器实现模糊搜索,直接使用前端实现.使用slice函数取出一页的数据来实现换页,首次加载页面的时候取出所有的内容放在一个data里,每次分页调取另外五条内容
search1() { this.current = 1; console.log(this.value); if (this.value == "题目") { this.selectwhat = 2; this.listArr1 = this.listArr2 .filter((item, index) => item.title.includes(this.searchtext)) .slice(0, 5); } else if (this.value == "摘要") { this.selectwhat = 3; this.listArr1 = this.listArr2 .filter((item, index) => item.abstracted.includes(this.searchtext)) .slice(0, 5); } else if (this.value == "关键词") { console.log(this.value); this.selectwhat = 4; this.listArr1 = this.listArr2 .filter((item, index) => item.keyword.indexOf(this.searchtext) > -1) .slice(0, 5); } }, nextpage() { let top = document.documentElement.scrollTop || document.body.scrollTop; //实现滚动效果 const timeTop = setInterval(() => { document.body.scrollTop = document.documentElement.scrollTop = top -= 50; if (top <= 0) { clearInterval(timeTop); } }, 10); this.current++; if (this.selectwhat == 1) { this.listArr1 = this.listArr2.slice( this.current * 5 - 5, this.current * 5 ); } else if (this.selectwhat == 2) { this.listArr1 = this.listArr2 .filter((item, index) => item.title.includes(this.searchtext)) .slice(this.current * 5 - 5, this.current * 5); } else if (this.selectwhat == 3) { this.listArr1 = this.listArr2 .filter((item, index) => item.abstracted.includes(this.searchtext)) .slice(this.current * 5 - 5, this.current * 5); } else { this.listArr1 = this.listArr2 .filter((item, index) => item.keyword.indexOf(this.searchtext) > -1) .slice(this.current * 5 - 5, this.current * 5); } },
-
接收参数来构造年份趋势图的折线图
mounted() { const _this = this; axios({ url: "http://localhost:8080/magazine", }).then((res) => { let j = 0; let m = 0; let l = 0; for (let i = 1; i < 36; i++) { if (res.data[i].magezine == "ICCV") { _this.data2[j] = res.data[i].publication_year - 0; _this.data1[j] = res.data[i].number; j++; } else if (res.data[i].magezine == "ECCV") { _this.data3[m] = res.data[i].number; _this.data4[m] = res.data[i].publication_year - 0; m++; } else if (res.data[i].magezine == "CVPR") { _this.data5[l] = res.data[i].number; _this.data6[l] = res.data[i].publication_year - 0; l++; } _this.datax = _this.data6; _this.datay = _this.data5; } console.log(_this.data5); console.log(_this.data6); this.$nextTick(function () { this.drawLine("main"); }); }); },
-
点击切换不同会议的折线图曲线
clickEC() { this.datax = this.data4; this.datay = this.data3; this.msg="ECCV论文年份趋势图"; this.$nextTick(function () { this.drawLine("main"); }); }, clickIC() { this.datax = this.data2; this.datay = this.data1; this.msg="ICCV论文年份趋势图"; this.$nextTick(function () { this.drawLine("main"); }); }, clickPR() { this.datax = this.data6; this.datay = this.data5; this.msg="CVPR论文年份趋势图"; this.$nextTick(function () { this.drawLine("main"); }); }, },
-
热词分析的接口,keyword_count表中存放了每个关键字的数量,利用sql语句进行排序,可以获取前十个(也可以使用limit直接获取)
@CrossOrigin(origins = "*",maxAge = 3600) @RequestMapping("keyword") public List<Keyword> getKeyword(){ return keywordService.findTopten(); <select id="findTopten" resultType="jiekou.demo.entity.Keyword"> SELECT keyword_count_id,keyword,count FROM keyword_count ORDER BY count DESC </select>
-
趋势图接口, 返回三个参数的List列表,使用数据库group by
@CrossOrigin(origins = "*",maxAge = 3600) @RequestMapping("/magazine") public List<Magazine> getMagezineAll(){ return magazineService.findmagazine(); @CrossOrigin(origins = "*",maxAge = 3600) @RequestMapping("/paperkey") public List<Paperkey> getPaperkey() { return paperkeyService.findAll(); } <select id="findmagazine" resultType="jiekou.demo.entity.Magazine"> select magazine,count(*) as number,publication_year from paper group by magazine,publication_year ORDER BY publication_year ASC <select>
-
返回所有关键词以及对应的论文id
@CrossOrigin(origins = "*",maxAge = 3600) @RequestMapping("/paperkey") public List<Paperkey> getPaperkey() { return paperkeyService.findAll(); }
-
返回所有文章
@RequestMapping("/paper") public List<Paper> getPaper(){ return paperService.findAll(); }
-
返回所有文章内容接口
@CrossOrigin(origins = "*",maxAge = 3600) @GetMapping("/changepage") public List<PaperKeyword> getPaging(){ List<Paperkey> paperkeys=paperkeyService.findAll(); List<Paper> papers=paperService.findAll(); List<PaperKeyword> paperKeywords=new ArrayList<>(); Paper paper=new Paper(); int q=0; for(int i=0;i<papers.size(); i++){ List<String> s=new ArrayList<>(); paper=papers.get(i); while(paper.getPaper_id()==paperkeys.get(q).getPaper_id()){ s.add(paperkeys.get(q).getWord()); q++; } PaperKeyword paperKeyword=new PaperKeyword(paper.getPaper_id(),paper.getTitle(),paper.getLink(),paper.getPublication_year(),paper.getMagazine(),paper.getAbstracted(),s); paperKeywords.add(paperKeyword); } return paperKeywords; }
八.心路历程与评价
1、心路历程与收获
雷振康的感受: 首先感受就是之前掌握的东西太少了,这次结对作业先是去学习了vue.js框架的应用,然后看着视频学了几天开始写这次的前端,一开始只觉得vue.js框架只是对dom操作的一些简化,并没有感觉很好用,然后写一半的时候发现了可以利用element ui来实现一些功能,会更加的方便,然后就是后端的学习,因为在团队作业中我负责的是后端,所以也学习了一些后端的东西,就是感觉配置maven有点问题,会经常一直下载同一些jar包,导致项目导不进去,然后springboot的数据库操作我不是特别熟练,不知道多表连接应该如何去创建一个实体类,这次时间不太够,下次团队作业应该就会学习好这些操作了。
李进明的感受:对这次结对作业最大的感受是无力,之前并没有参与过前后端分离开发项目,在作业发布初期对整个项目束手无策。在项目开发过程中,得一边学习新的知识,一边运用知识,在这个部分体会到对一个知识点要做到依葫芦画瓢,必须要掌握基础知识点,这又让我觉得大一大二的时光过得太轻松,以至于许多比较基础的知识都没能运用起来。不过很感谢这次能有一个结对伙伴,在他进行自己任务比较顺利的时候,能抽出空来帮助我,这让我再次感受到结对作业的作用。整次的结对作业下来,我对后端开发有了比较初步的了解,同时明白了笨鸟还得先飞,在自己基础比较差的情况下,就得更先一步去学习别人已经掌握的知识,这样才能在整个团队协作中不总是掉队,拖后腿。
2.结对评价
雷振康对李进明的评价; 因为是舍友,所以这次结对编程比较方便,可以在凌晨两三点还在一起打代码,然后可以起到一些督促的作用,可以叫他起来学习,并且修改前后端的接口的时候也比较方便,随时更改需求。
李进明对雷振康的评价:很感谢伙伴在这次结对作业对我的帮助,如果有一个词说明在这次结对作业中振康的角色,那就是“全栈开发者”。在这次结对作业中,振康很勤奋,能比我早进行项目的开发,还有着比我更扎实的基础,有些我这边出现bug的时候都是振康帮忙调试出来的。一方面,如果可以的话,我希望振康能在这次结对作业中拿到更高的分数占比,感谢他在这次结对的辛苦付出。另一方面,振康有时也会说玩笑话刺激我,显著起到了督促我的作用。在这里,我希望能有一天,我能做得更好,对着振康说“你求我,我就教你”(doge)。