天天看点

结对作业二

这个作业属于哪个课程 2021春软件工程实践|S班 (福州大学)
这个作业要求在哪里 结对作业二/结对作业二
结对学号 221801330陈少彬、221801332李达明
这个作业的目标 对上一次结对作业的功能进行实现
其他参考文献 博客园、CSDN

目录

  • git仓库链接和代码规范链接
    • git仓库链接
    • 代码规范链接
    • 技术和框架
  • PSP表格
  • 云访问链接
  • 成品展示
  • 结对讨论过程描述
    • 过程
    • 截图
  • 设计实现过程
    • 前端页面设计实现:
    • 后端设计实现:
  • 代码说明
    • 布局(bootstrap的栅栏系统)
    • 后端代码
  • 心路历程与收获
  • 评价结对队友

https://github.com/Weirdo341/PairProject

https://github.com/Weirdo341/PairProject/blob/main/221801330%26221801332/codestyle.md

  • 前端:bootstrap、bootstrap-table框架以及ECharts。
  • 后端:springboot框架

221801332的PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20
•Estimate 估计这个任务需要多少时间
Development 开发 2170 2500
•Analysis 需求分析 (包括学习新技术) 600 650
•Design Spec 生成设计文档 40 60
•Design Review 设计复审
•Coding Standard 代码规范 (为目前的开发制定合适的规范) 50
•Design 具体设计
•Coding 具体编码 1100 1200
•Code Review 代码复审 80
•Test 测试(自我测试,修改代码,提交修改) 300 400
Report 报告
•Test Report 测试报告 30
•Size Measurement 计算工作量 10
•Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划
合计 2250 2570
221801330的PSP
1870
540 500
1000
250 200
1950

http://112.74.177.186:8080/paperlist.html

(首页加载很慢,需要三五分钟,请提前耐心等待)

主页面,展示论文列表,对论文列表进行分页,右上角搜索框可对论文信息进行模糊搜索

结对作业二

论文的搜索

结对作业二

当鼠标停留时显示论文列表的完整信息

结对作业二

可通过多选框进行多选删除,左上角为本页面的列全选

结对作业二

点击删除按钮可删除列表中的论文信息,不会对后端数据产生影响

(此处截图看不出明显效果,暂不放截图)

右下角可选择每页列数

结对作业二

左上角为各个页面的导航,点击可跳转到相应页面

结对作业二

点击左上角跳转到top10热词

结对作业二

点击表格能跳转到首页展示该行热词相关论文同时搜索框默认值为Computer vision(这里点击的是Computer vision)

结对作业二

热词走势是根据十大热词在14年到20年的热度数据制作

结对作业二

可对走势折线进行筛选

结对作业二

点击右上角的保存可将走势图保存为本地图片

结对作业二

  拿到题目后,经过讨论,最开始是打算纯前端的编码的(因为后端springboot框架都不熟悉),我们决定使用bootstrap开发框架来进行设计,完成了论文列表的设计之后觉得时间还充裕,便也开始后端技术的学习和编写。约定了两个人的代码规范之后就开始了页面的设计。页面需要有如下几部分:首页、论文列表、top10列表、热词折线图、十大热词列表。然后我们经过一段时间设计后发现,可以将全部论文列表直接置于首页,然后在首页加入实时搜索框以便进行筛选,然后点击相关的十大热词能显示响应关键词的相关论文,这个我们也决定将其与首页结合,即点击关键词跳到首页然后全部论文列表变为相应的论文列表。

(以下沙雕表情包请自动忽略)

结对作业二
结对作业二
结对作业二
结对作业二

  • 论文列表页面:通过bootstrap官方文档以及网上的资源学习其使用,以列表的形式对论文信息进行显示。论文列表的加载是通过js语句实现的,通过bootstrap-table自带的表格加载功能实现论文数据的展示。论文数据的获取通过后端发送的数据获取,通过getTable接口接收论文数据。
  • 热词排行:通过hashmap统计每个热词的频数,用list排序获取前十个热词。
  • 热词走势:根据所获取的前十个热词,根据年份信息统计该热词每年的频数。

  • 数据库设计:用一个数据表存储论文信息,根据当前所有的论文数据对数据表进行填充。

结对作业二

  • 左上角下拉菜单栏设计:
<div class="dropdown">
        <button type="button" class="btn dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">首页
            <span class="caret"></span>
        </button>
        <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
            <li role="presentation">
                <a role="menuitem" tabindex="-1" href="paperlist.html">首页</a>
            </li>
    
            <li role="presentation" class="divider"></li>
            <li role="presentation">
                <a role="menuitem" tabindex="-1" href="index1.html">Top10 热门领域排行榜</a>
            </li>
            <li role="presentation">
                <a role="menuitem" tabindex="-1" href="index2.html">热词走势对比</a>
            </li>
        </ul>
           
  • top10表格设计
<div class="col-md-8">
      <div class="panel panel-default">
        <div class="panel-body">
          <table class="table">
            <caption>Top10 热门领域排行榜</caption>
            <thead>
              <tr>
                <th>排名</th>
                <th>关键词</th>
                <th>搜索热度</th>
              </tr>
            </thead>
            
            <tbody>
              <tr onClick="window.location.href='paperlist.html?tj_type=Cameras'">
                <th scope="row">1</th>
                <td id="keyword1">热词1</td>
                <td id="search_heat1"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Training'">
                <th scope="row">2</th>
                <td id="keyword2">热词2</td>
                <td id="search_heat2"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Computer vision'">
                <th scope="row">3</th>
                <td id="keyword3">热词3</td>
                <td id="search_heat3"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Feature extraction'">
                <th scope="row">4</th>
                <td id="keyword4">热词4</td>
                <td id="search_heat4"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Shape'">
                <th scope="row">5</th>
                <td id="keyword5">热词5</td>
                <td id="search_heat5"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Image segmentation'">
                <th scope="row">6</th>
                <td id="keyword6">热词6</td>
                <td id="search_heat6"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Robustness'">
                <th scope="row">7</th>
                <td id="keyword7">热词7</td>
                <td id="search_heat7"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Visualization'">
                <th scope="row">8</th>
                <td id="keyword8">热词8</td>
                <td id="search_heat8"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Three-dimensional displays'">
                <th scope="row">9</th>
                <td id="keyword9">热词9</td>
                <td id="search_heat9"></td>
              </tr>

              <tr onClick="window.location.href='paperlist.html?tj_type=Image reconstruction'">
                <th scope="row">10</th>
                <td id="keyword10">热词10</td>
                <td id="search_heat10"></td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
           
  • 热词走势图设计
<script type="text/javascript">
      var dom = document.getElementById("container");
      var myChart = echarts.init(dom);
      
      var app = {};

      var option;
      option = {
          tooltip: {
              trigger: 'axis'
          },
          legend: {
              data: ['热词1','热词2','热词3','热词4','热词5','热词6','热词7','热词8','热词9','热词10']
          },
          grid: {
              left: '3%',
              right: '4%',
              bottom: '3%',
              containLabel: true
          },
          toolbox: {
              feature: {
                  saveAsImage: {}
              }
          },
          xAxis: {
              type: 'category',
              boundaryGap: false,
              data: ['2014年', '2015年', '2016年', '2017年', '2018年', '2019年', '2020年']
          },
          yAxis: {
              type: 'value'
          },
          series: [
              {
                  name: 'Cameras',
                  type: 'line',
                  stack: '热词1',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Training',
                  type: 'line',
                  stack: '热词2',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Computer vision',
                  type: 'line',
                  stack: '热词3',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Feature extraction',
                  type: 'line',
                  stack: '热词4',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Shape',
                  type: 'line',
                  stack: '热词5',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Image segmentation',
                  type: 'line',
                  stack: '热词6',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Robustness',
                  type: 'line',
                  stack: '热词7',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Visualization',
                  type: 'line',
                  stack: '热词8',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Three-dimensional displays',
                  type: 'line',
                  stack: '热词9',
                  data: ['0','0','0','0','0','0','0']
              },
              {
                  name: 'Image reconstruction',
                  type: 'line',
                  stack: '热词10',
                  data: ['0','0','0','0','0','0','0']
              },
          ]
      };
           
  • 论文列表加载
$('#myTable').bootstrapTable({
            method: 'get',
            url: "getTable", // 请求路径
            striped: true, // 是否显示行间隔色
            pageNumber: 1, // 初始化加载第一页
            pagination: true, // 是否分页
            sidePagination: 'client', // server:服务器端分页|client:前端分页
            sortable: true,
            sortName:'论文名称',
            sortOrder: 'desc',
            pageSize: 10, // 单页记录数
            pageList: [5, 20, 30],
            search: true,
            paginationLoop: true,
            paginationHAlign: 'left',
            paginationDetailHAlign: 'right',
            paginationPreText: '上一页',
            paginationNextText: '下一页',
            // showRefresh : true,// 刷新按钮
            queryParams: function(params) { // 上传服务器的参数
                var temp = {
                    name: 1
                };
                return temp;
            },
            columns: [{
                checkbox: true
            }, {
                title: '论文名称', //表头
                field: 'title',  //数据源
                sort: true,
                searchable:true,
                formatter:paramsMatter, //paramsMatter为显示表格信息的一个函数
            }, {
                title: '原文链接',
                field: 'url',
                searchable:false,
                formatter:paramsMatter,
            }, {
                title: '会议',
                field: 'meeting',
                searchable:true,
            },{
                title: '年份',
                field: 'year',
                searchable:true,
            }, {
                title: '摘要',
                field: 'paperAbstract',
                searchable:false,
                formatter:paramsMatter,
            }, {
                title: '关键词',
                field: 'keyWords',
                searchable:true,
                formatter:paramsMatter,
            }
            ]
        });
           

  • 热词走势获取
public ArrayList<KeyWord> getKwdFrequency(HashMap<String, Integer> topTenWords){
        String word;
        ArrayList<KeyWord> keyWords = new ArrayList<>();
        for(HashMap.Entry<String, Integer> entry : topTenWords.entrySet()) {
            HashMap<String, Integer> wordsFrequency = new HashMap<>();
            word = entry.getKey();
            Paper paper = new Paper(word);
            //根据前10关键词获取论文集合
            ArrayList<Paper> papers = getYearByKwd(paper);
            //根据论文的年份计算该年热词频数
            for (int i = 0; i < papers.size() ; i++){
                paper = papers.get(i);
                if (wordsFrequency.containsKey(paper.getYear())) {
                    //key存在
                    Integer value = wordsFrequency.get(paper.getYear());
                    value++;
                    wordsFrequency.put(paper.getYear(), value);
                    //System.out.println(s);
                } else {
                    //key不存在
                    wordsFrequency.put(paper.getYear(), 1);
                }
            }
            //遍历HashMap,给Keyword对象赋值存到队列中
            for (HashMap.Entry<String, Integer> haEntry : wordsFrequency.entrySet()
            ) {
                KeyWord keyWord = new KeyWord();
                keyWord.setFrequency(haEntry.getValue());
                keyWord.setWord(word);
                keyWord.setYear(haEntry.getKey());
                keyWords.add(keyWord);
                System.out.println(keyWord.getWord() + " " + keyWord.getYear() + " " + keyWord.getFrequency());
            }
        }
        return keyWords;
    }
           
  • 热词获取
public HashMap<String, Integer> sortMap( HashMap<String, Integer> map){
        List<Map.Entry<String, Integer>> list = new ArrayList<>();
        HashMap<String, Integer> topTenWords = new HashMap<>();
        Integer value ;
        String keyWord;
        //将map放入list中
        for (Map.Entry<String, Integer> entry : map.entrySet()){
            list.add(entry);
        }
        list.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                return o2.getValue() - o1.getValue();  //从大到小排列
            }
        });
        //将列表前十个存到hashmap
        for (int i = 0 ; i < 10 ; i ++ ){
            value = list.get(i).getValue();
            keyWord = list.get(i).getKey();
            System.out.println(value + " " + keyWord);
            topTenWords.put(keyWord, value);
        }
        return topTenWords;
    }
           
  • 关键词统计
public HashMap<String,Integer> getKeyWords(ArrayList<Paper> papers){
        HashMap<String,Integer> map = new HashMap<>();
        Paper paper;
        for (int i = 0; i < papers.size() ; i++){
            paper = papers.get(i);
            String[] words = paper.getKeyWords().split(",");
            for (String s:words) {
                if (map.containsKey(s)) {
                    //key存在
                    Integer value = map.get(s);
                    value++;
                    map.put(s, value);
                    //System.out.println(s);
                } else {
                    //key不存在
                    map.put(s, 1);
                }
            }
        }
        return map;
    }
           
  • 论文数据的获取
JSONObject jsonObject = JSON.parseObject(readMMAP(file)); //转化为JSON格式数据
            Paper paper = new Paper();
            paper.setTitle(jsonObject.getString("论文名称"));  //根据key获取value值
            paper.setPaperAbstract(jsonObject.getString("摘要"));
            paper.setUrl(jsonObject.getString("原文链接"));
            paper.setMeeting("ECCV");
            paper.setYear(jsonObject.getString("会议和年份").split(" ")[1]);
            JSONArray keys = jsonObject.getJSONArray("关键词");
            StringBuilder sb = new StringBuilder();
            for (Object key : keys) {
                sb.append(key).append(",");
            }
            paper.setKeyWords(sb.toString());
            papers.add(paper);
           
  • 文件读取
public static String readMMAP(File file){
        RandomAccessFile raf = null;
        MappedByteBuffer mbb = null;
        try {
            raf = new RandomAccessFile(file, "r");
            mbb = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            if (mbb != null){
                return StandardCharsets.UTF_8.decode(mbb).toString();
            } else {
                return "";
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (mbb != null){
                Cleaner var1 = ((DirectBuffer)mbb).cleaner();
                if (var1 != null) {
                    var1.clean();
                }
            }
            if (raf != null){
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "";
    }
           
  • 读取eccv文件
public static List<Paper> readECCV() {
        File root = new File(ECCV_ROOT);
        File[] files = root.listFiles();
        List<Paper> papers = new ArrayList<>(4096);
        for (File file : files) {

            JSONObject jsonObject = JSON.parseObject(readMMAP(file));
            Paper paper = new Paper();
            paper.setTitle(jsonObject.getString("论文名称"));
            paper.setPaperAbstract(jsonObject.getString("摘要"));
            paper.setUrl(jsonObject.getString("原文链接"));
            paper.setMeeting("ECCV");
            paper.setYear(jsonObject.getString("会议和年份").split(" ")[1]);
            JSONArray keys = jsonObject.getJSONArray("关键词");
            StringBuilder sb = new StringBuilder();
            for (Object key : keys) {
                sb.append(key).append(",");
            }
            paper.setKeyWords(sb.toString());
            papers.add(paper);
        }
        return papers;
    }
           
  • 将对象列表写入数据库
private PapersService papersService;

    @GetMapping("/paper")
    public String create(){
        ReadJsonFile readJsonFile = new ReadJsonFile();
        Paper paper = new Paper();
        List<Paper> papers = new ArrayList<>();
        papers = ReadJsonFile.readCVPR();
        for (int i = 0;i < papers.size(); i++){
            papersService.insertMessage(papers.get(i));
        }
}
           
  • 将数据写入数据库中
public void insertMessage(Paper paper){
        papersMapper.insertMessage(paper);
    }
           
  • 数据库操作(mybatis)
<insert id="insertMessage" keyProperty="id" useGeneratedKeys="true" parameterType="com.daming.bootstring.model.Paper">
        insert into papers(keywords,abstract,title,meeting,year,url)
        values (#{keyWords},#{paperAbstract},#{title},#{meeting},#{year},#{url})
    </insert>
           

  •   221801330:李对于这次作业的整体方向和细节提出了很好的建议,虽然作业的界面比较简陋,但是我们都付出了很大的努力。并且通过这次结对作业,我深刻体会到了什么是“1+1>2”。很多时候我模糊的地方他刚好擅长,就形成了一种很好的良性互补,非常有利于作业的进行与完成。而且,在两个人进行代码交流的时候,往往能发现对方代码中难以发现的错误,进而进行修改或者完善,这样不仅互相能从中得到提醒,并且能减少这种问题在以后点中出现的概率,提升代码质量,取长补短,互利共赢。通过这次结对编程,两个人彼此监督学习很有效果,对自己来说也是种新的尝试,也培养了我们的团队意识,在这次作业中也学到了很多东西。最后也要谢谢队友的帮助,让我们顺利地完成了作业。
  •   221801332:由于没学过springboot,最开始是打算以纯前端的方式来完成这次作业,也由于较少写前端,前面两天浪费了较多的时间在bootstrap的学习、查看官方文档等。写完论文列表之后发现还有较多的时间,想着应该还能来得及便也开始学习springboot的使用。springboot框架让我的工作率大大提高,在较短的时间内便完成了一些后端所需要的操作。本次结对作业让我掌握了未曾了解过的知识,也更加了解了前后端的交互。由于个人原因导致了这次的作业完成比较简陋,前后端兼顾的学习让我花费了较多的时间,没有实现更多的功能,也是这次作业的一个遗憾。

  •   221801330:李的个人能力很强,前后端均有所涉猎,在这次作业中我有很多不懂的地方也向他请教。他能合理地安排时间来进行学习和作业,做事认真负责,遇到不懂上网去查询相关的资料让我们一起去学习,去弄懂,能够在这次作业积极地提出自己的建议和看法,并且就此进行讨论与修改。
  •   221801332:对于任务的分配、编程过程中的讨论能够较及时的反馈,但完成度没有达到较理想的程度。能够根据我所提的需求在网上搜索学习,也较积极的向我讨论所遇到的一些问题。