這個作業屬於哪個課程 | 2021春軟件工程實踐W班 (福州大學) |
---|---|
這個作業要求在哪里 | 結對第二次作業——頂會熱詞統計的實現 |
結對學號 | 081700318 221801306 |
這個作業的目標 | 實現結對作業一中設計原型的部分功能 |
其他參考文獻 | CSDN等相關技術性博客 |
雲服務器訪問鏈接
論文查詢
(因為數據統計頁面采用的是echart 所以會導致加載較久,推薦用firefox和chrome瀏覽器訪問)
git倉庫鏈接和代碼規范鏈接
PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
Estimate | 估計這個任務需要多少時間 | 5days | 7days |
Development | 開發 | 1080 | 1320 |
Understanding | 需求理解 | 30 | 30 |
Analysis | 需求分析(包括學習新技術) | 60 | 90 |
Coding Standard | 代碼規范 | 30 | 20 |
Discussing | 結對討論交流 | 60 | 60 |
Design | 具體設計 | 60 | 30 |
Coding | 具體編碼 | 720 | 1000 |
Test | 測試 | 60 | 60 |
Deploying | 部署到雲服務器 | 60 | 30 |
Reporting | 報告 | 70 | 50 |
Size Measurement | 計算工作量 | 20 | 10 |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 20 | 10 |
Writing | 撰寫報告 | 30 | 30 |
合計 | 1150 | 1370 |
成品展示
首頁部分
首頁為論文查詢入口,頂欄部分分為首頁、數據統計、收藏論文列表
搜索功能
可以通過四種方式進行搜索,包括模糊查詢、題目、關鍵詞、文章內容
如根據關鍵詞搜索Deep
根據標題搜索UAV
可以分頁顯示論文列表
對於每篇論文,可點擊標題到該論文的鏈接
也可以點擊相關關鍵詞查看相關論文 同時右下角的按鈕支持收藏和取消收藏
數據統計部分
在數據統計頁面中,可以查看熱門領域的玫瑰統計圖,和熱詞雲圖
可以查看不同年份、不同頂會的熱門領域統計
餅庄圖是有大到小排序的可以看出熱度的趨勢
並且餅狀圖和詞雲支持通過直接點擊圖的部分跳轉到對應關鍵詞的搜索頁
收藏列表部分
考慮到安全性的問題就沒有給使用者提供直接移除數據庫中論文的功能
而是通過為使用者維護一張收藏列表,使用者可以在搜索界面把中意的論文添加進收藏論文列表,並且再收藏論文列表中管理
在收藏論文列表中,可以查看已收藏的論文 並且可以模糊搜索已經收藏的論文,右下角的按鈕同樣支持取消收藏的功能。
結對討論過程描述
討論分工
關於熱門領域統計的討論
最后采用了玫瑰圖來展現熱門領域的統計
設計實現過程
這次作業是采用了servlet+jsp的方式實現開發的
前端的各種樣式根據原型進行開發。
樣式方面使用了bootstrap
一部分功能使用了js
與數據庫的交互只使用了jdbc,沒有使用框架。
這次作業使用了jsp+servlet的方式,並沒有實現前后端的分離,但采用了mvc的設計思想。
servlet只用來控制跳轉和傳參。
dao層則和數據交互 里面封裝了jdbc的代碼。
pojo則是一些數據模型類。
utils中是各種工具 包括創建數據庫連接之類的。
service層則是封裝了業務邏輯的類。
至於前端的顯示則由jsp來負責。
處理一個請求的具體流程就是servlet調用service處理完業務邏輯 將結果轉發給jsp呈現給用戶。
還有一部分功能是由js調用ajax實現的。
代碼說明
數據庫設計
首先先介紹一下數據庫設計,三張表分別對應三個功能
CREATE TABLE `Thesis` (
`title` varchar(255) CHARACTER
`thesisyear` int(20) NOT NULL,
`keyword` varchar(400) CHARACTER
`publishdate` varchar(255) CHARACTER
`link` varchar(255) CHARACTER NOT NULL,
`meeting` varchar(20) CHARACTER NOT NULL,
`abstract` varchar(4000) CHARACTER
`id` int(255) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
首先是用來存論文數據的表,搜索功能的實現就是在這個表中查找,值得一提的是keyword,直接通過一條字符串將所有keyword存在表中,雖然導致重復的存取,但搜索某一篇論文的時候不用再關聯到另一個表一個一個取出keyword,讓另一個表只負責關鍵詞的統計和數量的計算,犧牲了一部分空間減少了執行查找語句的數量。作為主鍵的id是自增的,不用特別設計。
然后就可以根據這個表直接構建出對應的模型類
public class Thesis {
private String title;
private String date;
...}
然后是用於統計關鍵詞的表keywords,表中ThesisID為外鍵,同樣重復存取了meeting和ThesisYear幫助統計,不需要關聯到Thesis表中執行統計。這種設計主要是考慮到這次數據庫中的數據不算多,重復存取的開銷不算大,如果是很大的數據就考慮用別的方法了
CREATE TABLE `Keywords` (
`keyword` varchar(255)
`ThesisID` int(255) NOT NULL,
`Thesisyear` int(20) NOT NULL,
`meeting` varchar(20) NOT NULL,
KEY `id` (`ThesisID`),
CONSTRAINT `id` FOREIGN KEY (`ThesisID`) REFERENCES `Thesis` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)
最后就是LikeList了,包含了descripson和作為外鍵的ID,原本一開始還想添加筆記功能和多用戶功能,但由於時間和水平有限沒有實現。實際上用上了只有論文id項目,但是還是保留了一定的拓展性,為日后添加功能留了空間。
CREATE TABLE `LikeList` (
`descripsion` varchar(255),
`ThesisID` int(255) ,
'UserID' int(255) ,
CONSTRAINT `ThesisID` FOREIGN KEY (`ThesisID`) REFERENCES `Thesis` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)
與數據庫交互(dao)
首先創建一個jdbc的工具類 ,DButil,設置數據庫地址和賬戶, 通過這個工具類快速獲得數據庫連接和關閉數據庫連接。
public class DBUtil {
static String ip ;
static int port;
static String database;
static String encoding ;
static String loginName;
static String password ;
public static Connection getConnection() throws SQLException ;//獲取連接
public static void close(ResultSet rs, Statement stmt, Connection conn);//關閉連接
}
然后所有數據庫接口都按照和哪個表交互來設計
然后類中只封裝着和數據庫交互的代碼,不包含任何業務邏輯,所有方法大概都是遵從如下流程設計
public void deleteBymeeting(String meeting)
{
Connection ThesisConnection = null;
try {
ThesisConnection = DBUtil.getConnection();//獲取鏈接
Statement ThesisStatement = ThesisConnection.createStatement();
String sql = "delete from Thesis where meeting = '?'";//生成語句
PreparedStatement Ptmt = ThesisConnection.prepareStatement(sql);//預編譯
Ptmt.setString(1, meeting);//插入值 防止sql注入
Ptmt.execute(sql);//執行
DBUtil.close(null,ThesisStatement,ThesisConnection);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//如有結果 在此處返回結果
}
執行業務邏輯(service)
所有的業務邏輯都被包含到service中,servlet和dao都不會去處理業務邏輯。
分頁功能
由於是在搜索結果中分頁,所以這部分方法就實現在SearchService中
首先實現一個pagebean模型類
public class PageBean {
private int PageNum;//當前所處頁數
private int PageSize;//一頁中含有幾條記錄
private int TotalRecord;//所有記錄數
private int TotalPage;//所有頁數
private String SearchType;//搜索類型
private String Input;//搜索的輸入
private List<Thesis> list;//搜索到的論文結構
}
包含所有展示一個分頁所需要的東西,之后這個東西會傳遞給jsp頁面,jsp根據pagebean就可以顯示對應的頁面和生成頁面跳轉所需要的連接。
然后在service類中實現如下方法。
static public PageBean search(String type, String input,ThesisDAO SearchThesisDAO,int PageNum)
{
int TotalResultNum;
int SearchStart;
int SearchLength;
PageBean SearchResult=null;
//初始化
TotalResultNum=SearchThesisDAO.getNum(input,type);
//從數據庫中獲取一共有幾條結果
SearchStart=(PageNum-1)*pagesize;
//通過當前所在頁數計算出接下來數據庫要從第幾條結果開始取
SearchLength=PageNum*pagesize<TotalResultNum?5:TotalResultNum-(PageNum-1)*pagesize;
//通過當前所在頁數和總頁數和總共接過數計算出接下來數據庫要取到第幾條結果
SearchResult=new
PageBean(PageNum,pagesize,TotalResultNum,SearchThesisDAO.getLimit(SearchStart,SearchLength,input,type),type,input);
//構建一個pagebean並且傳出
//getLimit(SearchStart,SearchLength,input,type) 取得從SearchStart開始 Searchend結束的論文條目 也就是當前頁所擁有的搜索結果
return SearchResult;
}
傳入參數和轉發請求(servlet)
servlet大概就包含兩種
處理需要跳轉請求
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String type = (String) request.getParameter("searchtype");//獲取參數
String input =(String) request.getParameter("input");
PageBean SearchResults=null;//執行封裝好的業務邏輯並取得結果
SearchResults = SearchService.search(type, input, SearchThesisDAO, Integer.parseInt(request.getParameter("pagenum")));
//設置參數
request.setAttribute("result",SearchResults);
request.setAttribute("searchtype",type);
//轉發請求給jsp 讓jsp顯示
try {
request.getRequestDispatcher("/WEB-INF/views/SearchResult.jsp").forward(request,response);
} catch (ServletException e) {
e.printStackTrace();
}
}
可以看出servlet只做出了簡單的四件事,獲取參數,執行邏輯,設置參數,轉發請求。具體的邏輯都包含在service類中了,顯得比較簡潔,大部分功能都是按照這樣設計的。
處理需要數據的請求
在不需要跳轉至jsp時,一些servlet還需要有將結果打包成json字符串發送。來處理前端的一些對於數據的請求
public void doGet(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/json;charset=utf-8");// 指定返回的格式為JSON格式
response.setCharacterEncoding("UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");//跨域
//獲取參數
....
//調用業務邏輯
...
//返回json字符串
try {
out = response.getWriter();
JSONArray Jarray = (JSONArray) JSON.toJSON(list);
out.write(Jarray.toJSONString());
} catch (IOException e) {
e.printStackTrace();
}
}
顯示頁面(JSP JS)
Jsp可以從request對象中直接獲取servlet傳來的對象,非常方便,並且直接用在頁面的顯示之中,省去處理json的步驟
例:顯示搜索結果
<%PageBean Pb=(PageBean) request.getAttribute("result");
List<Thesis> result=Pb.getList();
if(result.size()!=0)
{
for(Thesis i:result)
{%>
<li class="list-group-item thesis-item">
<h3><a href="<%=i.getLink()%>"><%=i.getTitle()%></a></h3>//顯示標題
<span class="label label-default"><%=i.getMeeting()%></span>//顯示會議
<span class="label label-default"><%=i.getYear()%></span>//顯示年份
<div class='thesis-content'><%=i.getAbstractContent()%>
</div>
</li>
<%}
但是預見圖表時碰到了困難,因為圖表的各種參數是在js中實現的,直接將js寫在jsp中顯得很臃腫,因此這部分改為傳統的通過ajax對servlet發出請求獲取json數據,並且設置圖表的參數
function genData(count) {
var legendData = [];
var seriesData = [];
$.ajax({
url: "Count?count="+count,
dataType: "json", //數據格式
type: "get", //請求方式
async: false, //是否異步請求
success: function (data) {
//如果請求成功,返回數據。
$.each(data, function (i, item) {
legendData.push(item.keyword);
seriesData.push({
name: item.keyword,
value: item.nums,
url:
"Search?searchtype=title&input=" +
item.keyword,
});
});
},
});
return {
legendData: legendData,
seriesData: seriesData,
};
總的來說,這次的代碼沒有用到很復雜的方法和算法,主要還是在做crud,因此這里主要就貼一些規范設計和我打代碼的時候分層思想。
心路歷程和收獲
林逸暉:第一次設計一個完整的系統,發現不是那么容易的事情。熟練了對於servlet和jsp的使用。因為是第一次做,所以整了個前后端沒有分離的系統,希望能在之后軟件工程實踐中能學習前后端分離的項目形式。
程文健:對於兩人結對編程還是不熟悉,盡管中間插入了一次軟件工程小組實踐,學習了一下如何使用Git進行多人協作,但畢竟已經是結對后期了,所以整個過程中還是一一種野路子的方法進行的。有時候感覺GitHub挺麻煩的,還是直接qq傳文件或者直接復制比較容易,導致一些commit可能有點問題,希望這一塊以后能在實踐中加強。雖然我負責的部分不多,但對前后端的處理有了一定的理解,之后的軟工實踐繼續努力。
評價結對隊友
TO 林逸暉:程文健對負責的任務做得還行,能發現一些細節上的問題,編碼過程中也提出了有用的意見,在整個項目中還是起到了重要的作用。
TO 程文健:林逸暉實在是太強了我的哥!他的整體把控能力很好,整個項目做下來有條不紊,感覺我只是個腿部掛件。他真的……為什么這么……我真的……我不會形容,他真的太強了。