這個作業屬于哪個課程 | 2020春s班 |
---|---|
這個作業要求在哪裡 | 結對第二次作業——某次疫情統計可視化的實作 |
結對學号 | 041702303、221701238 |
這個作業的目标 | 1、結合寒假第二次作業的成果,使用web技術實作原型設計中的部分功能 2、使用github進行合作開發 3、學習《建構之法》第四章至第五章的内容。 |
作業正文 | 1、本部落格正文 2、github項目代碼 |
其他參考文獻 | 1、《碼出高效_阿裡巴巴Java開發手冊》 2、《建構之法》 |
1、Github項目位址和代碼規範連結
Github項目主倉庫
代碼規範連結
2、成品展示
2.1 基本功能實作
1、進入首頁,顯示目前最新的全國疫情資料以及相應的顯示現有确診人數的疫情地圖,滑鼠移到每個省份會顯示高亮和該省份的具體資訊的懸浮塊。
2、選擇指定的日期,點選“确認”後即可顯示該日期的全國疫情資料以及相應的疫情地圖。
3、點選“現有确診”,顯示現有确診人數的疫情地圖;點選“累計确診”,則顯示累計确診人數的疫情地圖。
4、點選地圖具體資訊懸浮塊中的詳情,即可跳轉到對應省份的詳情疫情資訊的界面,包含了最新疫情資料以及相關資料的折線圖(預設顯示新增确診趨勢圖)。
5、選擇指定的日期,點選“确認”後即可顯示該日期的該省份疫情資料以及以及相關資料的折線圖(預設顯示新增确診趨勢圖)。
6、點選“新增确診趨勢”,顯示新增确診趨勢的折線圖;點選“累計确診趨勢”,顯示累計确診趨勢的折線圖;點選“累計治愈/死亡”,顯示累計治愈/死亡的折線圖。
2.2 拓展功能實作
1、點選“熱點新聞”,即可使用熱點新聞功能,檢視最新最熱門的疫情新聞。
2、點選“疫情科普”,即可使用疫情科普功能,學習新冠病毒的相關知識,防患于未然。
3、點選“口罩預約”,即可使用口罩預約功能,填寫相關資訊預約口罩,資訊無誤則提示預約成功。
備注:由于技術和其他問題,拓展功能并沒有完全能夠實作。希望在今後的學習中能夠得以完善(*^▽^*)。
3、結對過程
3.1 總體流程
- 明确分工,一個人主要負責前端界面設計,一個人主要負責後端開發,并做好時間規劃
- 分别學習相關的知識和技術,并加以應用
- 通過視訊和文字交流探讨項目的開發
- 實作過程發現開發過程中的困難,思考讨論尋找解決方案
- 逐漸合作完成部落格内容,總結過程
3.2 結對照片
3.3 部分聊天記錄截圖
4、設計實作過程
4.1 設計工具和技術
- 開發工具:Eclipse,Intellij IDEA,Firefox浏覽器。
- 使用技術:JSP,Servlet,Css,Javascript,JQuery,Echarts,Sqlite,Ajax。
4.2 項目分解與實作
- 項目結構設計:使用JavaEE架構,即使用JSP+Servlet+JavaBean制作動态網頁。
- 疫情資料:一開始想使用爬蟲在網頁上抓取資料,但感覺難度有點大便放棄了這個想法,轉而使用Sqlite資料庫,并從相關檔案中讀取資料儲存到資料庫中。
- 疫情地圖及省份疫情趨勢折線圖:使用Echarts制作,并嵌入到指定JSP中。
- 疫情資料按日期查詢:使用Ajax技術,輸入日期确認後,背景擷取響應的json後取出資料并設定相應參數的值,使網頁可以快速顯示該日期的具體疫情資料以及相應Echarts圖像。
4.3 功能結構圖
5、關鍵代碼說明
1、ProvinceServlet:處理首頁的疫情資料和疫情地圖的請求,預設發送最新一天的資料。通過接收首頁表單中填寫的日期資訊,從資料庫中讀取該日期及前一天的所有資料并發送回首頁和疫情地圖。
public class ProvinceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
ProvinceDAO provinceDAO = new ProvinceDAOImpl();
String year = request.getParameter("year");
String month = request.getParameter("month");
String day = request.getParameter("day");
String type = request.getParameter("type");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Map<String, Object> map = new HashMap<String, Object>();
List<String> provinceName = new ArrayList<String>();
List<Integer> provinceValue = new ArrayList<Integer>();
List<Integer> nation = new ArrayList<Integer>();
String dateString = "";
String yesterdayString = "";
//判斷日期合法
if (year != null && month != null && day != null && type != null) {
if (!year.equals("") && !month.equals("") && !day.equals("")) {
if (year.compareTo("2020") == 0
&& ((Integer.parseInt(month) == 2 && Integer.parseInt(day) > 0 && Integer.parseInt(day) < 3)
|| (Integer.parseInt(month) == 1 && Integer.parseInt(day) > 18
&& Integer.parseInt(day) < 32))) {
dateString = year + "-" + month + "-" + day;
} else {
dateString = "2020-02-02";
}
} else {
dateString = "2020-02-02";
}
try {
yesterdayString = DateUtil.getYesterday(dateString);
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
//寫入日期前一天資料
if (format.parse(yesterdayString).getTime() < format.parse("2020-01-19").getTime()) {
nation.add(0);
nation.add(0);
nation.add(0);
nation.add(0);
nation.add(0);
nation.add(0);
} else {
Date yesterday = new Date(format.parse(yesterdayString).getTime());
Province p = provinceDAO.get("全國", yesterday);
nation.add(p.getNowIp());
nation.add(p.getNowSp());
nation.add(p.getAllIp());
nation.add(p.getAllSp());
nation.add(p.getAllCure());
nation.add(p.getAlldead());
}
Province data[] = provinceDAO.getListByDate(new Date(format.parse(dateString).getTime()));
for (Province p : data) {
if (!p.getName().equals("全國")) {
provinceName.add(p.getName());
if (type != null && type.equals("now")) {
provinceValue.add(p.getNowIp());
} else {
provinceValue.add(p.getAllIp());
}
} else {
//寫入當天日期資料
nation.add(p.getNowIp());
nation.add(p.getNowSp());
nation.add(p.getAllIp());
nation.add(p.getAllSp());
nation.add(p.getAllCure());
nation.add(p.getAlldead());
map.put("全國", nation);
}
}
//寫入map
map.put("省名", provinceName);
map.put("值", provinceValue);
//轉換成接送發送
JSONObject json = JSONObject.parseObject(JSON.toJSONString(map));
out.write(json.toString());
out.flush();
out.close();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
2、MoreServlet:處理省份詳情界面的疫情資料和疫情地圖的請求,預設發送最新一天的資料。通過接收首頁表單中填寫的日期資訊,從資料庫中讀取該日期及前一天的所有資料并發送回首頁和疫情趨勢折線圖。
public class MoreServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
ProvinceDAO provinceDAO = new ProvinceDAOImpl();
String year = request.getParameter("year");
String month = request.getParameter("month");
String day = request.getParameter("day");
String type = request.getParameter("type");
String name = request.getParameter("name");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Map<String, Object> map = new HashMap<String, Object>();
List<Integer> province = new ArrayList<Integer>();
List<Integer> allIpData = new ArrayList<Integer>();
List<Integer> newIpData = new ArrayList<Integer>();
List<Integer> allCureData = new ArrayList<Integer>();
List<Integer> allDeadData = new ArrayList<Integer>();
List<String> dates = new ArrayList<String>();
String dateString = "";
String yesterdayString = "";
//檢測資料合法
if (year != null && month != null && day != null && type != null && name != null) {
if (!year.equals("") && !month.equals("") && !day.equals("")) {
if (year.compareTo("2020") == 0
&& ((Integer.parseInt(month) == 2 && Integer.parseInt(day) > 0 && Integer.parseInt(day) < 3)
|| (Integer.parseInt(month) == 1 && Integer.parseInt(day) > 18
&& Integer.parseInt(day) < 32))) {
dateString = year + "-" + month + "-" + day;
}
else {
dateString = "2020-02-02";
}
} else {
dateString = "2020-02-02";
}
try {
yesterdayString = DateUtil.getYesterday(dateString);
Date today = new Date(format.parse(dateString).getTime());
Province data[] = provinceDAO.getListByName(name);
Province p = provinceDAO.get(name, new Date(format.parse(dateString).getTime()));
//寫入當天日期疫情資料
province.add(p.getNowIp());
province.add(p.getNowSp());
province.add(p.getAllIp());
province.add(p.getAllSp());
province.add(p.getAllCure());
province.add(p.getAlldead());
//寫入前一天疫情資料
if (format.parse(yesterdayString).getTime() < format.parse("2020-01-19").getTime()) {
province.add(0);
province.add(0);
province.add(0);
province.add(0);
province.add(0);
province.add(0);
} else {
p = provinceDAO.get(name, new Date(format.parse(yesterdayString).getTime()));
province.add(p.getNowIp());
province.add(p.getNowSp());
province.add(p.getAllIp());
province.add(p.getAllSp());
province.add(p.getAllCure());
province.add(p.getAlldead());
}
String tempday = "2020-01-19";
//提取小于當天的對象
for (Province p1 : data) {
if (p1.getDate().getTime() <= today.getTime()) {
//将未含有資料的對象資料(0)寫入
if (format.format(p1.getDate()).compareTo(tempday) > 0) {
while (format.format(p1.getDate()).compareTo(tempday) > 0) {
dates.add(tempday);
newIpData.add(0);
allCureData.add(0);
allDeadData.add(0);
allIpData.add(0);
tempday = DateUtil.getNextday(tempday);
}
tempday = "2020-12-31";
}
//按照類型寫入資料
if (type.equals("newIp")) {
yesterdayString = DateUtil.getYesterday(format.format(p1.getDate()));
//寫入新增--累計的內插補點
if (format.parse(yesterdayString).getTime() < format.parse("2020-01-19").getTime()) {
newIpData.add(p1.getAllIp());
} else {
p = provinceDAO.get(name, new Date(format.parse(yesterdayString).getTime()));
newIpData.add(p1.getAllIp() - p.getAllIp());
}
} else if (type.equals("allIp")) {
allIpData.add(p1.getAllIp());
} else {
allCureData.add(p1.getAllCure());
allDeadData.add(p1.getAlldead());
}
//寫入日期
dates.add(format.format(p1.getDate()));
}
}
//存入map
map.put("data", province);
map.put("allCure", allCureData);
map.put("allDead", allDeadData);
map.put("allIp", allIpData);
map.put("newIp", newIpData);
map.put("date", dates);
//轉換成json發送
JSONObject json = JSONObject.parseObject(JSON.toJSONString(map));
out.write(json.toString());
out.flush();
out.close();
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
> 3、nationStatistic.jsp中的getData函數:發送請求到ProvinceServlet并擷取指定日期的全國疫情資料,并傳回在首頁中顯示詳細資料
function getData() {
$.ajax({
type : "get",
async : true, //異步請求(同步請求将會鎖住浏覽器,使用者其他操作必須等待請求完成才可以執行)
url : "test", //請求發送到ProvinceServlet處
data : {
year : $("#year").val(),
month : $("#month").val(),
day : $("#day").val(),
type : type
},
dataType : "json", //傳回資料形式為json
success : function(result) {
//解析json
var nationData = result["全國"];
var provinceName = result["省名"];
var provinceValue = result["值"];
var provinceData = new Array();
for (var i = 0; i < provinceName.length; i++) {
var row = new Object();
row.name = provinceName[i];
row.value = provinceValue[i];
provinceData.push(row);
}
//設定資料區域
$(".dataNum1").text(nationData[6]);
$(".dataNumYesterday1").text(
showSign(nationData[6] - nationData[0]));
$(".dataNum2").text(nationData[7]);
$(".dataNumYesterday2").text(
showSign(nationData[7] - nationData[1]));
$(".dataNum4").text(nationData[8]);
$(".dataNumYesterday4").text(
showSign(nationData[8] - nationData[2]));
$(".dataNum5").text(nationData[10]);
$(".dataNumYesterday5").text(
showSign(nationData[10] - nationData[4]));
$(".dataNum6").text(nationData[11]);
$(".dataNumYesterday6").text(
showSign(nationData[11] - nationData[5]));
myChart.setOption(fillData(provinceData), true);
}
})
}
> 4、trend.jsp中的getNewIpData函數:發送請求到MoreServlet并擷取指定日期的省份新增确診人數資料,并傳回到省份詳情頁面中設定新增确診趨勢的折線圖。
function getNewIpData() {
$.ajax({ //使用JQuery内置的Ajax方法
type : "get", //post請求方式
async : true, //異步請求(同步請求将會鎖住浏覽器,使用者其他操作必須等待請求完成才可以執行)
url : "MoreServlet",
data : {
name : name,
type : type,
year : $("#year").val(),
month : $("#month").val(),
day : $("#day").val()
},
dataType : "json", //傳回資料形式為json
success : function(result) {
//document.write("123");
//請求成功時執行該函數内容,result即為伺服器傳回的json對象
if (result != null) {
//解析json
newIp = result["newIp"];
dates = result["date"];
data = result["data"];
$(".dataNum1").text(data[0]);
$(".dataNumYesterday1").text(showSign(data[0] - data[6]));
$(".dataNum2").text(data[2]);
$(".dataNumYesterday2").text(showSign(data[2] - data[8]));
$(".dataNum3").text(data[4]);
$(".dataNumYesterday3").text(showSign(data[4] - data[10]));
$(".dataNum4").text(data[5]);
$(".dataNumYesterday4").text(showSign(data[5] - data[11]));
...
}
},
})
}
}
6、總結與感想
6.1 閱讀心得
軟體都是在互相合作中完成的,在合作中,首先應先制定好統一的代碼标準,在開發過程中需要互相審查對方的代碼以便發現問題。團隊開發按照一定的軟體開發流程的體系可以讓工作更加簡單明了,提高項目完成度。
6.2 工作感想
周同學:在本次項目中我又學習到了新的知識,也更加感受到了結對合作的魅力。在完成項目後我感到非常開心。希望在接下來的團隊作業中能夠持續發光發亮。
陳同學:這次作業是為了完成第一次作業原型的部分功能,剛開始原型是做成app形式,但是由于時間以及作業要求,決定與隊友做成web,主要用jsp以及servlet完成的(剛學隻會這個),前期困難點在于資料的擷取(爬蟲),在嘗試查閱資料後果斷選擇了内嵌資料庫,使用sqlite還算勉強可以上手,接着是地圖和趨勢圖的完成,經過學習學會和利用echarts+jsp+JavaScript+jQuery實作Ajax動态請求資料響應使用者。這次作業主要還是進行結對的練習,是以内容也相對團隊項目要求簡單,在經過幾天的學習,體會到了合作程式設計的要求和需求,同時自己在echarts圖表和jsp、servlet有了更深入的了解掌握,有了此次經驗相信在以後的團隊合作能夠更加熟練。
6.3 隊友評價
周同學:陳同學學習能力、程式設計能力強,易于溝通交流,是一個非常棒的隊友!他的後端開發給予我極大的幫助,也讓本次作業能夠更好地完成。
陳同學:隊友是個比較會鑽研的同學,在結對作業中對我還是幫助很多的,相對于我而言,隊友的前端布局設計給了這次作業很大的幫助,同時隊友還是很認真的完成作業,這是比較令人開心的,總之隊友是個好學生以後也會是個好員工或者好隊友。