結對作業二


這個作業屬於哪個課程 2021春軟件工程實踐|W班 (福州大學)
這個作業要求在哪里 結對作業二
結對學號 221801208、221801227
這個作業的目標 實現頂會熱詞統計
其他參考文獻 github、CSDN

網站鏈接

論文爬取

tips

  • 由於注冊功能還未完成,所以提供了兩個可用的初始登錄賬號密碼:
    • 賬戶1:賬號:admin ,密碼:admin
    • 賬戶2: 賬號:user ,密碼:123456
  • “熱詞圖譜”加載較慢,需等待10秒才能加載出完整界面,請不要在剛打開“熱詞圖譜”界面時就點擊關鍵詞;
  • 搜索論文時,個別關鍵詞匹配的論文較多,需要等待;
  • 請用除IE以外的瀏覽器打開

git倉庫鏈接和代碼規范鏈接

倉庫鏈接
代碼規范

PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鍾) 實際耗時(分鍾)
Development 開發
• Analysis • 需求分析 40 30
• Design Spec • 生成設計文檔 20 30
• Design Review • 設計復審 10 10
• Coding Standard • 代碼規范 80 100
• Coding • 具體編碼 3500 4000
• Code Review • 代碼復審 200 160
• Test • 測試 200 300
Reporting 報告
• Test Repor • 測試報告 50 40
• Size Measurement • 計算工作量 20 15
• Postmortem & Process Improvement Plan • 事后總結, 並提出過程改進計划 50 60
合計 4170 4745

成品展示

賬戶1: 賬號:admin ,密碼:admin

賬戶2: 賬號:user ,密碼:123456

界面展示

登錄、注冊、找回密碼界面

目前只實現登錄功能,注冊與找回密碼只有界面

主頁

查詢論文

支持標題模糊查詢、標簽查詢;查詢結果分頁展示,每頁展示10條結果;有兩個查詢對象:1. 對爬取的所有論文查詢;2. 對已經收藏的論文進行查詢;

(圖1)對爬取的所有論文進行查詢,支持標題模糊搜索


(圖2)對已經收藏的論文進行查詢,支持標題以及關鍵字查詢

熱詞分析

其中共有熱詞圖譜,熱度走勢,熱度對比三大部分:

(圖3)熱詞圖譜,3大會議的熱詞詞雲,以輪播圖的方式展示


(圖4)熱度走勢:Top10熱詞按 年份-頻數 得出的折線圖


(圖5)熱詞熱度:以餅圖的形式對比Top10的熱詞熱度

更多功能

(圖6)詞雲圖上的詞語,點擊跳轉至相應詞的搜索結果


(圖7)對搜索結果的地址進行一鍵復制

結對討論過程描述

1.前后端交互討論

    一開始不確定用什么框架,經討論之后確定為vue和springboot,這是我們小組用的相對來說最熟悉的框架。vue有一定基礎,而且java接觸較多,所以選擇springboot作為后端框架。比較麻煩的是數據交換,JSONArray和JSONObject相互嵌套,后端跟前端的同學解釋了好一會兒返回值的意義。后面放棄了,還是老老實實寫個接口文檔,幫助理解。

2.github使用討論

    之前沒有用過遠程分支,再加上還是用命令行管理項目版本的,所以始終整不明白怎么在遠程建立分支並提交。ch建議換成github desktop可視化操作,理解和操作都簡單了許多。

3.框架使用討論

    springboot花了不少時間找教程,目的是快速上手。對於新手來說易於理解的教程找起來不容易,二人互相發了很多教程,費了不少功夫才找到簡單的一個demo,自己慢慢理解。注解在springboot中出現頻率最高,和java語言不同,沒有前后端交互經驗的初學者不好理解,沒看懂的時候就和隊友討論。

4.數據庫討論

    數據庫表結構關乎后端模型。討論出的第一種方案是一張表解決,把所有關鍵詞作為text存儲。起初以為在同一張表存儲所有信息會加快查找速度,但是不利於編碼,而且每次取出來還要分割字符串會降低性能(果然討論比個人的想法要周全)。另一種方案是兩張表。最后還是決定對於每個會議建了兩張表,一張主表存一對一信息(id為主鍵),另一張表存id對應的所有關鍵詞(id為外鍵)。

設計實現過程

1.后台數據管理:
    論文與關鍵詞是一對多關系,所以需要兩張表:表一包含編號、題目、摘要、原文鏈接、年份,表二包含編號、關鍵詞。依照數據庫的設計原則,這種方式可以避免冗余。后端代碼中一個Dao接口服務一個Controller,將單個Controller的所有數據庫操作封裝到唯一的Dao中,調用的時候不易混亂。
2.后端框架選擇:
    后端使用springboot框架。由於集成了spring和mybatis,編程時能更多地集中於與需求相關的實現上,像數據庫連接關閉、解析前端請求和給前端返回數據都,這些都只要通過簡單的注釋就能解決。實現邏輯放在Controller下,數據庫相關操作放在Dao層,對象模型放在model,三者分離開有利於debug和后期維護。
3. 前端總體
    前端使用Vue框架,使用了axiosechartsview-designvue-routervue-woedcloudvuescrollvuex插件。

功能結構圖

界面結構圖

設計概述

1.登錄

  • 前端參數:用戶輸入賬號、密碼,賬號密碼打包成JSONObject,傳給后端驗證。
  • 后端處理:查看數據庫里已有的賬號密碼,看是否有完全匹配的元組。
  • 后端返回值:返回一個status和一個驗證結果。(status為狀態碼)
  • 登陸后,主頁為側邊導航菜單以及主面板構成,共有5個界面分別為:論文搜索 、本地收藏、熱詞圖譜、熱度走勢、熱度對比。

2.論文搜索

  • 前端布局:搜索結果在搜索框下,是一個無限滾動的論文列表。每個論文項帶有題目、編號、摘要、原文鏈接、年份和關鍵詞。
  • 前端參數:用戶在搜索框搜索題目或者題目的一部分,title作為給后端的參數。
  • 后端處理:將title作為查詢條件,查詢對應論文的所有信息。
  • 后端返回值:后端經過模糊匹配(可以通過數據庫的模糊匹配實現),返回給前端一個JSONArray,內部包含多個JSONObject,每個JSONObject是一篇論文的所有信息。

3.本地收藏按鈕

  • 前端參數:被收藏的論文的所有信息。
  • 后端處理:將收藏論文插入數據庫表。

4.論文刪除

  • 前端參數:前端提供論文id。
  • 后端處理:將對應論文從數據庫刪除。

5.論文本地查找

  • 前端布局:和論文搜索界面相似,多了一個關鍵詞搜索
  • 前端參數:如果是模糊搜索則參數名為fuzzyTitle,如果是關鍵詞搜索參數名為keyword,調用的后端接口不同。
  • 后端處理:用子查詢的方式查詢兩個表。
  • 后端返回值:返回論文列表,是一個JSONArray類型的數據。

6.top10熱詞(用於關鍵詞圖譜)

  • 前端界面:關鍵詞雲圖,支持點擊跳出相應論文。分別由三張雲圖,對應三個會議,以輪播圖的形式展現。
  • 后端處理:借助單詞統計的算法實現,統計所有關鍵詞的頻度。
  • 后端返回值:返回top10熱詞及其頻度。

7.關鍵詞走勢

  • 前端界面:熱度走勢為Top10的每個熱詞的不同年份與出現頻數的折線圖。用戶易於看出近期熱詞。
  • 后端處理:對top10的關鍵詞查詢數據庫,按年份統計出現次數。
  • 后端返回值:年份橫坐標,出現頻度縱坐標,都為JSONArray。

8.top10熱詞的餅圖

  • 前端界面:展示top10熱詞各自的占比,有助於用戶了解長期以來熱度較高的詞。

代碼說明

前端代碼

圖表組件及其復用

使用props向寫好的圖表組件傳入數據,進行渲染,實現復用

圖表主要使用echarts組件實現折線圖與餅圖

使用vue-wordcloud實現詞雲圖

 <div id="myChart2"></div>

// data部分
data () {
    return {
      data1: [5, 10, 12, 69, 25, 14, 18, 55, 47, 33],
      data2: ['key1', 'key2', 'key3', 'key4', 'key5', 'key6', 'key7', 'key8', 'key9', 'key10'],
      datat: [],
      color: ['#349dff', '#fbd438', '#33c45e', '#f2637b', '#6dd48c', '#fbd437', '#4ecb73', '#eaa674', '#88d1ea', '#36cbcb']
    }

// 渲染折線圖的代碼
drawLine () {
      this.chartLine = echarts.init(document.getElementById('myChart2'))
      const option = {
        tooltip: { // 設置tip提示
          formatter: '{a}<br/>{b}:{c} ({d}%)'
        },

        legend: { // 設置區分(哪條線屬於什么)
          y: 'bottom',
          x: 'center',
          data: this.data2
        },
        color: this.color, // 設置區分(每條線是什么顏色,和 legend 一一對應)
        series: [
          {
            name: '出現次數',
            data: this.datat,
            type: 'pie'
            // radius: ['50%', '70%']
            // center: ['50%', '50%']
          }
        ]
      }
      // 使用剛指定的配置項和數據顯示圖表。
      this.chartLine.setOption(option)
    },

論文列表的展示:調用接口后傳來的數據存在一個數組變量中,使用vue的方法v-for對這個數組中的數據遍歷展示

<div class="recordItem" v-for="(item,index) in searchResult" :key="index">
        <div class="recordTitle">{{item.title}}</div>
        <div class="recordCode"><span>論文編號:</span>{{item.number}}</div>
        <div class="recordTag">
          <span v-for="(item1,index1) in item.keyword.slice(0,3)" :key="index1">{{item1}}</span>
        </div>
        <div class="recordContent"><span>摘要內容:</span>{{item.abstract}}</div>
        <div class="recordAddress">
          <div @click="copy(item.link)">復制原文地址</div>
        </div>
        <div class="opeBtn">
<!--          <Button shape="circle" icon="ios-create-outline"></Button>-->
          <Button shape="circle" icon="ios-trash-outline" @click="myDelete(item)"></Button>
        </div>
      </div>

前端接口調用,使用的是axios。封裝axis。

this.$axios.post('http://localhost:8081/PaperOperationController/fuzzyQuery', {
  fuzzyTitle: this.searchContent
})
  .then(res => {
    this.searchResult = res.data.result
    this.totle = res.data.item_num
    this.showResult = this.searchResult.slice(0, 10)
  })
  .catch(err => {
    console.log(err)
  })
  .finally({
  })

后端代碼

1.論文title的模糊查詢:為了將數據區分開,保證論文id的唯一性,三個會議分別建了2個表。所以下面的模糊查詢需要針對不同的的會議數據庫表查詢,index = 0,1,2分別代表三個會議。模糊查詢的本質是數據庫的LIKE模糊查詢。先通過題目查詢論文id及其他信息,再通過論文id查詢關鍵詞。

public int queryPaper(JSONArray result,int index,String fuzzyTitle,int itemNum){
        List<Conference> conferenceList = null;
        if(index == 0){
            conferenceList = paperOperationDao.getCvpr(fuzzyTitle);
        }else if(index == 1){
            conferenceList = paperOperationDao.getEccv(fuzzyTitle);
        }else if(index == 2){
            conferenceList = paperOperationDao.getIccv(fuzzyTitle);
        }

        for (int j = 0; j < conferenceList.size(); j++) {
            Conference conference = conferenceList.get(j);
            JSONObject paperInfo = new JSONObject();
            paperInfo.put("title", conference.getTitle());
            paperInfo.put("number", conference.getNumber());
            paperInfo.put("abstract", conference.getPaperabstract());
            paperInfo.put("link", conference.getLink());
            paperInfo.put("year", conference.getYear());
            paperInfo.put("type",type[index]);

            List<ConferenceKwd> conferenceKwdList = null;

            if(index == 0){
                conferenceKwdList = paperOperationDao.getCvprKwd(conference.getNumber());
            }else if(index == 1){
                conferenceKwdList = paperOperationDao.getEccvKwd(conference.getNumber());
            }else if(index == 2){
                conferenceKwdList = paperOperationDao.getIccvKwd(conference.getNumber());
            }

            JSONArray keywordArray = new JSONArray();
            for (int k = 0; k < conferenceKwdList.size(); k++) {
                ConferenceKwd conferenceKwd = conferenceKwdList.get(k);
                keywordArray.add(conferenceKwd.getKeyword());
            }

            paperInfo.put("keyword", keywordArray);
            result.add(paperInfo);
            itemNum++;
        }
        return itemNum;
    }

2.基於論文關鍵詞的查詢:論文關鍵詞查詢和模糊查詢方法區別不大。先用關鍵詞查找id,再通過id查找論文的所有信息。代碼類似,就不展示了。

3.生成關鍵詞圖譜的熱詞:前端需要top10熱詞以及熱詞在圖譜中的字體大小,后端用隨機數對熱詞字體進行變化,界面展示的時候會更美觀。

public JSONArray getWordMap(){
        Random random = new Random();
        JSONArray map = new JSONArray();
        JSONObject result = getHotWords();
        JSONArray hotWords = result.getJSONArray("hotWord");
        for(int i = 0;i < hotWords.size();i++){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("name",hotWords.get(i));
            jsonObject.put("value",random.nextInt(16)+12);
            map.add(jsonObject);
        }
        return map;
    }

4.單個熱詞的走勢:用一個數組保存關鍵詞2000~2007年的頻率。前端需要的用echarts繪制折線圖,所以返回兩個數組,分別表示橫縱坐標。

public JSONObject getTrend(@RequestBody JSONObject request){
        String keyword = request.getString("keyword");

        int[] frequency = new int[8];
        for(int i = 0;i < frequency.length;i++){
            frequency[i] = 0;
        }
        JSONObject trend = new JSONObject();
        JSONArray horizontal = new JSONArray();
        JSONArray vertical = new JSONArray();

        for(int i = 0;i < 3;i++){
            List<ConferenceKwd> conferenceKwdList = null;
            if(i == 0){
                conferenceKwdList = keywordTrendDao.getCvprKwd(keyword);
            }else if(i == 1){
                conferenceKwdList = keywordTrendDao.getEccvKwd(keyword);
            }else if(i == 2){
                conferenceKwdList = keywordTrendDao.getIccvKwd(keyword);
            }
            for(int j = 0;j < conferenceKwdList.size();j++){
                ConferenceKwd conferenceKwd = conferenceKwdList.get(j);
                int number = conferenceKwd.getNumber();
                List<String> yearList = null;
                if(i == 0){
                    yearList = keywordTrendDao.getCvprYear(number);
                }else if(i == 1){
                    yearList = keywordTrendDao.getEccvYear(number);
                }else if(i == 2){
                    yearList = keywordTrendDao.getIccvYear(number);
                }
                String year = yearList.get(0);
                String lastCharacter = year.substring(year.length() - 1);
                int index = Integer.parseInt(lastCharacter);
                frequency[index]++;
            }
        }
        for(int i = 0;i < frequency.length;i++){
            horizontal.add(2000+i);
            vertical.add(frequency[i]);
        }
        trend.put("year",horizontal);
        trend.put("frequency",vertical);

        return trend;
    }

5.獲取top10熱詞及其對應頻率:這項功能涉及到單詞的統計。為提高效率,加快統計速度,將HashMap作為從關鍵詞映射到頻率的數據結構。HashMap本身不具有排序功能,所以需要把關鍵詞和頻率封裝到類Word,放在TreeSet排序。具體實現步驟:select所有關鍵詞--》插入HashMap--》插入TreeSet。

public JSONObject getHotWords(){
        Map<String, Integer> map = new HashMap<>();
        Set<Word> set = new TreeSet<>();
        for(int i = 0;i < 3;i++) {
            List<ConferenceKwd> conferenceKwdList = null;
            if(i == 0){
                conferenceKwdList = hotWordFrequencyDao.getCvprKwd();
            }else if(i == 1){
                conferenceKwdList = hotWordFrequencyDao.getEccvKwd();
            }else if(i == 2){
                conferenceKwdList = hotWordFrequencyDao.getIccvKwd();
            }
            for (int j = 0; j < conferenceKwdList.size(); j++) {
                ConferenceKwd conferenceKwd = conferenceKwdList.get(j);
                wordToHashMap(conferenceKwd.getKeyword(), map);
            }
        }

        return frequency(set,map);
    }

心路歷程和收獲

    hj:整個過程還是比較曲折的。為了確定數據庫表,對表結構進行了兩次修改,因此數據反復導入花了不少時間。最開始編碼的時候,第一次使用springboot框架,改配置和debug也挺困難。后期逐漸熟練,慢慢對springboot有了整體的理解。總的來說收獲頗豐,學會了應用新框架,學會了借助官方文檔和demo入手新知識,學會了寫接口文檔,github管理版本的操作也更熟練。經驗教訓也沒比學到的知識少:短時間開發盡量選擇自己使用過的框架或工具,避免開發效率低;數據庫對編碼和類結構設計的影響極大,寧可多花些時間在數據庫表設計。
    ch:剛看到作業時是比較焦慮的,因為我不僅有這一項作業,還多出上學期生病拉下的緩考,幸好延遲了時間。我在此之前已經有過相應的項目經歷,但由於較久沒有接觸並且沒有嘗試過快速的,從0開始的兩個人以結對的形式共同完成的方式。還是有一段適應期。慢慢地就和隊友磨合好了。在寫代碼這一塊,我主要在圖表生成與前后端數據格式要求上遇到較大問題,但在多次的溝通以及查詢資料后逐個解決了。

評價結對隊友

    hj:在前端方面ch算是我們當中的專家了,所以和他合作起來很輕松。前后端交互測試的時候,前端基本沒bug,很快就完成銜接完了。另外,ch還是個做事精益求精的人,如果界面布局不夠美觀或者功能展示不夠合理,ch會再三考慮並給出可行的方案,並且短時間內完成代碼修改,效率很高。ch解決了很多前端技術上的難題,是學習能力很強的隊友,是最佳的partner。
    ch:hj是一個認真,有干勁的隊友。對項目的布局、進度都有較好的安排。他很努力的進行相關知識的學習。打代碼也很勤奮,帶動我一起認真對待。我們磨合的十分順利,很高心能跟hj同學合作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM