咱們在投訴模塊中還有一個功能沒有實現:javascript
統計:根據年度將相應年度的每月的投訴數進行統計,並以圖表的形式展現在頁面中;在頁面中能夠選擇查看當前年度及其前4年的投訴數。在頁面中能夠選擇不一樣的年度,而後頁面展現該年度的曲線統計圖。php
咱們到目前爲止是沒有學過任何的統計圖的工具的,那麼咱們要怎麼解決這個功能呢???咱們有另外的組件來把統計圖顯示出來:FusionChartshtml
FusionCharts 是使用javascript 實現統計圖表的js組件;其官網地址:http://www.fusioncharts.com。前端
具體的是怎麼操做的能夠看官方文檔,咱們以項目的需求來完成對應的功能就好了。java
首先,咱們要把對應的JS文檔加入到咱們的項目中:ajax
咱們只要根據修改Demo的值就能夠實現出咱們想要的效果了。sql
<!DOCTYPE HTML> <html> <head> <%@include file="/common/header.jsp" %> <title>年度投訴統計圖</title> <script type="text/javascript" src="${basePath}js/fusioncharts.js"></script> <script type="text/javascript" src="${basePath}js/fusioncharts.charts.js"></script> <script type="text/javascript" src="${basePath}js/themes/fusioncharts.theme.fint.js"></script> <script> FusionCharts.ready(function () { var revenueChart = new FusionCharts({ "type": "column2d", "renderAt": "chartContainer", "width": "500", "height": "300", "dataFormat": "json", "dataSource": { "chart": { "caption": "Monthly revenue for last year", "subCaption": "Harry's SuperMart", "xAxisName": "Month", "yAxisName": "Revenues (In USD)", "theme": "fint" }, "data": [ { "label": "Jan", "value": "420000" }, { "label": "Feb", "value": "810000" }, { "label": "Mar", "value": "720000" }, { "label": "Apr", "value": "550000" }, { "label": "May", "value": "910000" }, { "label": "Jun", "value": "510000" }, { "label": "Jul", "value": "680000" }, { "label": "Aug", "value": "620000" }, { "label": "Sep", "value": "610000" }, { "label": "Oct", "value": "490000" }, { "label": "Nov", "value": "900000" }, { "label": "Dec", "value": "730000" } ] } }); revenueChart.render(); }) </script> </head> <body> <br> <s:select id="year" list="{2015}" onchange="doAnnualStatistic()"></s:select> <br> <div id="chartContainer"></div> </body> </html>
再次回到咱們的需求原型圖,咱們看看是怎麼樣的:數據庫
根據不一樣的年份,就顯示出不一樣的統計圖數據…..這明顯就用到了ajax技術。 所以能夠肯定下來,咱們的前端就是用ajax進行交互,渲染出對應的統計圖的。編程
咱們的後端就是根據不一樣的年份,去獲取不一樣的年份每月的數據,返回給瀏覽器…json
咱們的需求是得讓咱們顯示近5年的統計圖…因而下拉框是咱們近5年的….
咱們雖然是能夠把option中的數據寫死,可是呢,若是過了一年的話,那麼咱們的數據是不會同步的。當我在2017年寫的時候,到2018年,頁面顯示仍是2017年的數據….因此這明顯是不合理的….
要想近5年是動態產生的,就不可以把數據寫死….因而咱們能夠在JSP頁面上獲得當前年的值,根據當前年就很是容易推出近5年的數據了…
因而咱們又可使用到Calendar這個日曆類了…
在JSP頁面獲得當前年的數據,並裝載到list集合中
Calendar calendar = Calendar.getInstance(); //獲得當前年的值 int year = calendar.get(Calendar.YEAR); //把年份用一個集合裝載 List yearList = new ArrayList(); //獲取近5年的值 for(int i =0; i<5; i++) { yearList.add(i, year--); } request.setAttribute("yearList", yearList); request.setAttribute("year", year);
在Struts的select標籤中把這個集合迭代出來
<s:select id="year" list="#request.yearList" onchange="doAnnualStatistic()"></s:select>
下面是咱們的效果:
接着,咱們發現FusionCharts這個組件,想要把數據顯示在統計圖表中,咱們的JSON數據的格式是須要這樣的:
還有值得注意的地方是,一進入頁面中須要加載當前年度的投訴統計數
<script> //頁面一加載就執行方法 $(function () { doAnnualStatistic(); }); //根據年份獲取投訴數 function doAnnualStatistic() { //獲取當前年份 var $year = $("#year option:selected").val(); //一進來,若是沒有選擇任何的年數,就顯示當前年份的 if($year=="" || $year==undefined) { $year = "${year}"; } //二、統計年度投訴數據並展現圖表 $.ajax({ url: "${basePath}complain/complain_getAnnualStatisticData.action", type: "post", dataType: "json", data: {"year",$year}, success: function (backData) { if(backData!=null && backData!=""){ var revenueChart = new FusionCharts({ "type": "line", "renderAt": "chartContainer", "width": "600", "height": "400", "dataFormat": "json", "dataSource": { "chart": { "caption": "年度統計投訴數", "xAxisName": "月 份", "yAxisName": "投 訴 數", "theme": "fint" }, "data":backData.chartData } }); revenueChart.render(); } }, error:function () { alert("統計投訴數失敗!"); } }); } </script>
咱們的後端就是根據年份,獲取對應的值,返回一個JSON格式給瀏覽器,那就好了…
可是呢,咱們還有其餘的細節須要考慮:今年是2017年7月,可是在查詢年度投訴數是要把整個年的信息查詢出來,8-12月的投訴數確定是沒有的。那麼咱們會將還沒到的時間設置成「」,若是在2016年的某月是沒有投訴數的,咱們應該將其替換成0,而不是「」….
在action中,咱們得獲取到用戶傳遞過來的年份,咱們調用service、dao層的方法獲取該年度對應每月的投訴數,轉換成JSON格式輸出就好了。
咱們知道前端須要的JSON格式是一個對象數組,最終目的就是數組:Struts2框架在最後解析的時候,會把集合解析成是數組。對象數組在java編程語言就是List集合中嵌套着Map集合。
在後端中,還有一個難點,就是咱們的SQL語句該怎麼寫????咱們要從數據庫查詢的是該年份每月的投訴數….
經過該年而查詢每月,咱們能夠很快地想到要用到分組查詢。可是還有一個問題,咱們在進行分組查詢的時候,若是表中是沒有1月或2月等數組的話,分組查詢出來的數據是沒有這些月份的。而咱們的統計圖是須要全部月份的數據的。咋看一下,咱們是須要把查詢出來的數據作循環判斷,得看看有沒有該月份,若是沒有該月份還得把數據填充進去。。還得判斷該月份是否是本年度的….這樣想一下就以爲麻煩了……
select month(comp_time) as '月份',count(*) '總數' from complain where year(comp_time)=? group by month(comp_time)
再次回到前面分析的,若是本年度的月份尚未到,那麼將該月的數據設置爲「」,若是是其餘年份的的月份查出的數據爲null,那麼咱們應該把這些月份的投訴數設置爲0而不是」「…..
可是呢,咱們如今有一個辦法,能夠在查詢的時候,無論該月份有沒有數據,都得顯示出來….這就是左外鏈接
因而咱們本身手動生成一張擁有12個月份的數據表,跟咱們的投訴表進行左外鏈接…
select imonth, count(comp_id) from t_month left join complain on imonth=month(comp_time) and year(comp_time)=2017 group by imonth order by imonth;
上面的sql語句的查詢效率是有點低的,咱們改造一下,改爲是子查詢:
select imonth,c2 from t_month left join (select month(comp_time) c1, count(comp_id) c2 from complain where year(comp_time)=? group by month(comp_time)) t on imonth = c1 order by imonth;
dao層根據年份查詢出每月份的投訴數據
/** * * @param year 根據年獲取數據 * @return 返回的是一個列表數組 */ @Override public List<Object[]> getAnnualStatisticByYear(int year) { //拼接SQL語句 StringBuffer buffer = new StringBuffer(); buffer.append(" SELECT imonth,c2 ") .append(" FROM t_month") .append(" LEFT JOIN (SELECT month(comp_time) c1, count(comp_id) c2 FROM complain WHERE YEAR(comp_time)=? GROUP BY MONTH(comp_time)) t") .append(" ON imonth = c1") .append(" ORDER BY imonth;"); SQLQuery sqlQuery = getSession().createSQLQuery(buffer.toString()); sqlQuery.setParameter(0, year); List<Object[]> list = sqlQuery.list(); return list; }
service層拿到Dao層的數據,判斷是不是本年度的,若是是本年度的,那麼尚未到的月份的數據就設置爲」「,若是已通過的了月份,若是沒有數據就設置爲0.
返回一個List集合嵌套着Map集合,就能夠給前臺解析了。
@Override public List getAnnualStatisticByYear(int year) { List<Object[]> annualStatisticByYear = complainDao.getAnnualStatisticByYear(year); List<Map> returnList = new ArrayList<>(); //獲得本年度和本月份 int curYear = Calendar.getInstance().get(Calendar.YEAR); //Calerdar月份從0開始, int curMonth = Calendar.getInstance().get(Calendar.MONTH)+1; //使用Map集合裝載着數據 Map<String,Object> map = null; for (Object[] objects : annualStatisticByYear) { map = new HashedMap(); //獲得月份 Integer month = Integer.valueOf(objects[0] + ""); map.put("label", month + "月"); if (curYear == year) { //是本年度,那麼看看月份是否大於本月份 if (month > curMonth) { //將數據設置爲"" map.put("value", ""); } else { if (objects[1] != null) { map.put("value", objects[1]); } else { map.put("value", "0"); } } }else {//不是本年度 if (objects[1] != null) { map.put("value", objects[1]); } else { map.put("value", "0"); } } returnList.add(map); } return returnList; }
action層把service層的數據封裝到Map集合中,嵌套ajax解析Map集合,獲得的就是對象數組了。
//返回JSON格式的數據,這裏咱們就直接用Struts2框架來返回對應的數據就好了。 public String getAnnualStatisticData() { //獲取用戶傳遞過來的年份 String str_year = ServletActionContext.getRequest().getParameter("year"); if (str_year != null) { int year = Integer.valueOf(str_year); //根據年份去獲取每月的投訴數 map.put("msg", "success"); map.put("chartData", complainServiceImpl.getAnnualStatisticByYear(year)); } return "getAnnualStatisticData"; }
前臺把年份提交給Action,解析出後臺返回的數據,渲染成折線圖….
function doAnnualStatistic() { //獲取當前年份 var $year = $("#year option:selected").val(); //一進來,若是沒有選擇任何的年數,就顯示當前年份的 if($year=="" || $year==undefined) { $year = "${year}"; } //二、統計年度投訴數據並展現圖表 $.ajax({ url: "${basePath}complain/complain_getAnnualStatisticData.action", type: "post", dataType: "json", data: {"year":$year}, success: function (backData) { if(backData!=null && backData!=""){ var revenueChart = new FusionCharts({ "type": "line", "renderAt": "chartContainer", "width": "600", "height": "400", "dataFormat": "json", "dataSource": { "chart": { "caption": "年度統計投訴數", "xAxisName": "月 份", "yAxisName": "投 訴 數", "theme": "fint" }, "data":backData.chartData } }); revenueChart.render(); } }, error:function () { alert("統計投訴數失敗!"); } }); }