结对作业二
这个作业属于哪个课程 | 2021春软件工程实践S班 |
---|---|
这个作业要求在哪里 | 结对作业二 |
结对学号 | 221801218, 221801219 |
这个作业的目标 | 搭建一个平台爬取论文 |
其他参考文献 | 百度 |
1.git仓库链接,代码规范链接和部署链接
GitHub仓库 (ErrorPay/PairProject)
代码规范链接 (codestyle.md)
2.PSP表格
221801218
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 10 | 20 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 480 | 540 |
• Design Spec | • 生成设计文档 | 30 | 15 |
• Design Review | • 设计复审 | 30 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 15 |
• Design | • 具体设计 | 480 | 540 |
• Coding | • 具体编码 | 480 | 540 |
• Code Review | • 代码复审 | 420 | 540 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 30 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 15 | 15 |
• Size Measurement | • 计算工作量 | 15 | 15 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 2065 | 2340 |
221801219
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 10 | 20 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 380 | 440 |
• Design Spec | • 生成设计文档 | 30 | 15 |
• Design Review | • 设计复审 | 30 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 15 |
• Design | • 具体设计 | 380 | 440 |
• Coding | • 具体编码 | 380 | 440 |
• Code Review | • 代码复审 | 320 | 440 |
• Test | • 测试(自我测试,修改代码,提交修改) | 230 | 230 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 15 | 15 |
• Size Measurement | • 计算工作量 | 15 | 15 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 1865 | 2140 |
3.成品展示
-
登陆界面
-
注册界面
-
论文列表
-
论文详情
-
论文搜索
-
论文数据统计
4.结对讨论过程描述
4.1 要求分析
最开始拿到JSON格式的数据时分析解析和存储需要的数据。用户需要在系统中能够登陆注册以使用论文列表功能,用户也能够查看历年顶会的热门关键词以及热门趋势,同时也需要论文的查询功能。其中查询功能又能够分为普通查询以及高级查询。
4.2 确定实现方法
4.2.1 前端
前端采用了react+antdesign,由于react的特性,html的内容主要写在js里,这样便于动态生成界面内容,并添加响应事件,利用state这个属性,再不用刷新界面的条件下,实时渲染界面,不仅效率提高了,用户也能够得到更好的视觉体验。
图表方面用了wordcloud与echarts,wordcloud支持以关键词图谱的形式展现数据,而echarts有丰富的图表,并且在数据表现方面有较强的支持。
在前后端交互方面,登陆注册实现的方法是前端接受由后端发来的set-cookie,里面记录了后端存储的seeion,这样可以实现用户的认证。
前段界面大多采用了ant design的组件,该组件不用设计的样式,只需要提供几个属性方法,就能实现各种各样的功能。
页面跳转用了route与history传参结合,使得代码简单且能传递各种各样的参数。
4.2.2 后端
后端采用Golang编写。使用高性能的Gin框架提供http服务,选择xorm作为本项目的ORM框架管理数据,登陆方面使用session进行验证,用Cors处理跨域问题。
4.2.3 结对讨论
5.设计实现过程
5.1 描述实现过程
5.1.1 前端
先设置路由,设定一个主组件,所有的页面都要经过该组件的渲染,在这个主组件里写页面的结构,结构分为页头,侧边栏,主体部分和页尾,主体部分发生变化时,url路径改变,并且渲染不同的组件。主页面包含logo图案和一个搜索框,该页面一共有三个搜索框,第一个是导航栏上的搜索框,第二个是侧边栏的高级搜索,第三个是主体部分的搜索框。搜索框的逻辑如下:点击搜索触发点击事件,路由发生改变,同时,附加上用户输入的搜索参数,包括标题,关键词,所属会议。最后由一个swith条件语句判断要跳转的路由渲染的对应组件,对应组件获取搜索参数,完成搜索功能。
前后端交互用了axios,在实现的时候遇到了一个问题,就是axios是异步执行的,在组件没有渲染的时候,axios异步发送请求,会导致渲染时请求还在处理的情况,解决方法就是设置state,先在组件渲染时设为false。等axios结束后再设为true,再次渲染界面,就可以正常显示。
5.1.2 后端
设定router来接受前端的特定请求,再使用api通过handler函数对前端的请求进行处理。api可以通过调用service服务来进行深入的操作,如将JSON字符串解析并绑定为service结构体,再由特定的service函数来处理。service处理完所有的任务由serializer来序列化为JSON返回给前端。通过model对数据库进行CRUD操作。
5.2 功能结构图
5.3 数据库设计
6.代码说明
6.1 前端部分
6.1.1 关键代码
-
根据路由跳转界面
const ChoosePage = ({ id }) => { switch (id) { case "SearchPaperDetail": return (<CreateSearchDetailPage />); case "MyPaperList": return (<CreateListPage />); case "SearchPaperList": return (<CreateSearchPageList />); case "StatisticPage": return (<StatisticPage />); default: return (<CreateSearchPage />); } } class Home extends React.Component { onSearchP = () => { let { history } = this.props console.log('!!!' + a + b + c) history.push({ pathname: '/SearchPaperList', state: { paperNameSearch: a, paperMeetingSearch: b, paperWordsSearch: c } }) }
-
数据可视化跳转界面
<h2>TOP10关键词</h2> <ReactWordcloud style={{ width: 600, height: 400 }} words={words} /> <ReactECharts style={{ width: 1200, height: 350 }} option={this.getOption()} notMerge={true} lazyUpdate={true} theme={"theme_name"} />
6.2 后端部分
6.2.1 代码目录结构
.
├── api //用于处理前端发送的请求
├── config //服务器配置
├── deserializer //反序列化JSON
├── go.mod //Go Module包管理
├── go.sum
├── main.go //主函数入口
├── middleware //服务端中间件
├── model //数据库模型
├── serializer //序列化JSON
├── server //服务器路由
├── service //处理请求的服务
└── util //一些工具
6.2.2关键代码
-
用户登录功能。通过Session和cookie设置登陆状态。
func (service *UserLoginService) Login(c *gin.Context) serializer.Response { if has, _ := model.Engine.Where("uid = ?", service.Uid).Exist(&model.User{}); !has { return serializer.ParamErr("账号或密码错误", nil) } user := new(model.User) _, _ = model.Engine.Where("uid = ?", service.Uid).Get(user) if user.CheckPassword(service.Password) == false { return serializer.ParamErr("账号或密码错误", nil) } service.setSession(c, *user) return serializer.BuildUserResponse(*user) }
-
model包使用结构体绑定数据库表。如:
package model type User struct { Id int64 Uid string `xorm:"varchar(200) unique notnull"` PasswordDigest string `xorm:"varchar(200) notnull"` }
-
serializer序列化参数为JSON格式以便返回给前端。
// 所有序列化器 // Response 基础序列化器 type Response struct { Code int `json:"code"` Data interface{} `json:"data,omitempty"` Msg string `json:"msg"` Error string `json:"error,omitempty"` }
-
api包作为前端请求的Handler函数,传递前端数据给Service。
func UserRegister(c *gin.Context) { var service service.UserRegisterService if err := c.ShouldBind(&service); err == nil { res := service.Register() c.JSON(200, res) } else { c.JSON(200, ErrorResponse(err)) } }
-
util包存放一些工具函数,如Log格式化输出日志。
func (ll *Logger) Error(format string, v ...interface{}) { if LevelError > ll.level { return } msg := fmt.Sprintf("[E] "+format, v...) ll.Println(msg) } func (ll *Logger) Warning(format string, v ...interface{}) { if LevelWarning > ll.level { return } msg := fmt.Sprintf("[W] "+format, v...) ll.Println(msg) }
7.心路历程和收获
221801218
一直以来我对于一个完整Web应用的设计经验是很少的,这一次一个人独立的完整开发一个Web后端给我带来了不小的收获。前期设计和数据库规划的时候,经验的缺乏给项目的推进带来了不小的麻烦,但也让我对设计一个合理系统以及数据库更加有经验。作为一个Golang的初学者,这一次的项目也给我带来的非常大的锻炼的机会。让我对Golang这门语言又了更深的理解。一个本不那么熟悉的东西在不断的实践中变得熟悉起来,这给了我很大的动力。
221801219
这次结对作业,我学会了使用成熟的框架做一个网页,之前一直是自己用纯html,css写,费时费力,而且很花时间,组件虽然可扩展性差,但是一般能够满足我们的需求还有学习了npm包管理,用node进行网络通讯,以及最最重要的前后端分离技术。我也发现前端前期会出现难以调试自己请求的情况,尤其是后端没有开启服务的情况,
所以前端前期进度缓慢。在路由跳转以及异步处理的问题上我花了大量时间,虽然最后都解决了,但是了解到自己掌握的不够透彻,今后会加强学习
8.评价结对队友
221801218
我的队友在结对作业的过程体现了非常大的积极性,做事也有着很高的效率。在设计和开发过程中也给我提了很多很有建设性的建议,给我很多的帮助。
221801219
郑麟轩同学工作效率很高,善于听取他人的意见,知识面也十分广,会站在对方的立场上看待问题,会理解我的观点和看法。所以我们交流起来非常顺利。
同时他的一些看似工作量庞大的任务他都能够在很短的时间内解决。在他的任务完成后,也会热心地帮我分担一些任务,我觉得他有责任心。