您的位置:首页 > Web前端 > BootStrap

基于SpringMVC +前台页面基于bootstrap 的 echats+jquery.slider 堆积图

2016-12-09 17:40 627 查看
实现要点

1、基于jquery.slider 动态设置series,数据基于前台动态渲染。

2、legend的动态创建。

效果图:



JSP页面源码:

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%long version = java.lang.System.currentTimeMillis();%>
<!DOCTYPE html>

<!--[if IE 8]> <html lang="en" class="ie8 no-js"> <![endif]-->

<!--[if IE 9]> <html lang="en" class="ie9 no-js"> <![endif]-->

<!--[if !IE]><!--> <html lang="en" class="no-js"> <!--<![endif]-->

<!-- BEGIN HEAD -->
<head>
<meta charset="utf-8" />
<title>APP统计 | 神州专车</title>
<link href="${baseContextPath}static/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/bootstrap-responsive.min.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/font-awesome.min.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/style-metro.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/style.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/style-responsive.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/uniform.default.css" rel="stylesheet" type="text/css"/>
<!-- END GLOBAL MANDATORY STYLES -->

<!-- BEGIN PAGE LEVEL STYLES -->
<link rel="stylesheet" type="text/css" href="${baseContextPath}static/css/select2_metro.css" />
<link rel="stylesheet" type="text/css" href="${baseContextPath}static/css/chosen.css" />
<link href="${baseContextPath}static/css/jquery.gritter.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/fullcalendar.css" rel="stylesheet" type="text/css"/>
<link href="${baseContextPath}static/css/jqvmap.css" rel="stylesheet" type="text/css" ${baseContextPath}static="screen"/>
<link href="${baseContextPath}static/css/jquery.easy-pie-chart.css" rel="stylesheet" type="text/css" ${baseContextPath}static="screen"/>
<!-- END PAGE LEVEL STYLES -->
<link rel="shortcut icon" href="${baseContextPath}static/image/ucar/favicon.ico" />

<link rel="stylesheet" type="text/css" href="${baseContextPath}static/css/datepicker.css" />
<link rel="stylesheet" type="text/css" href="${baseContextPath}static/css/daterangepicker.css" />
<link rel="stylesheet" type="text/css" href="${baseContextPath}static/css/datetimepicker.css" />
<link rel="stylesheet" type="text/css" href="${baseContextPath}static/css/timepicker.css" />
<link rel="stylesheet" type="text/css" href="${baseContextPath}static/plugins/bootstrap-slider4.4.0/bootstrap-slider.min.css" />
<script type="text/javascript">var BATH_PATH='${baseContextPath}';</script>
<style type="text/css">
/*以下样式重写slider的样式*/
.slider-selection{background:#e48d0c;}
.slider-track{background-image: linear-gradient(to bottom,#BABABA 0,#BABABA 100%);}
.slider-handle{width:10px;}
</style>
</head>

<!-- END HEAD -->

<!-- BEGIN BODY -->

<body>
<!-- search layout begin -->
<div class="row-fluid">
<div class="span12">
<!-- BEGIN VALIDATION STATES-->
<div class="portlet box blue">
<div class="portlet-title">
<div class="caption"><i class="icon-reorder"></i>查询条件</div>
</div>
<div class="portlet-body form">
<!-- BEGIN FORM-->
<form id="searchForm" action="${baseContextPath}retentionAnalysis/loadForRetention" class="form-horizontal">
<input id="appVersionList" name="appVersionList" value="${appVersion}" type="hidden"/>
<input id="dataType" name="dataType" value="${empty searchObj.dataType ? 'JDZ' : searchObj.dataType}" type="hidden"/>
<input id="timeType" name="timeType" value="${empty searchObj.timeType ? 'day' : searchObj.timeType}" type="hidden"/>
<input id="from" name="from" value="${empty searchObj.from ? 0 : searchObj.from}" type="hidden"/>
<input id="to" name="to" value="${empty searchObj.to ? 31 : searchObj.to}" type="hidden"/>
<!--/row-->
<div class="row-fluid">
<div class="span4 ">
<div class="control-group">
<label class="control-label">开始时间</label>
<div class="controls">
<input id="beginTime" name="beginTime" size="16" type="text" value="${searchObj.beginTime }" readonly class="m-wrap span12 form_dateday">
</div>
</div>
</div>
<!--/span-->
<div class="span4 ">
<div class="control-group">
<label class="control-label">结束时间</label>
<div class="controls">
<input id="endTime" name="endTime" size="16" type="text" value="${searchObj.endTime }" readonly class="m-wrap span12 form_dateday">
</div>
</div>
</div>
<!--/span-->
</div>
<!--/row-->
<div class="row-fluid">
<div class="span4 ">
<div class="control-group">
<label class="control-label">客户端类型<span class="required"></span></label>
<div class="controls select2-wrapper">
<select id="form_2_select1" class="span12" name="appType" onchange="clkAppType()">
<option value=""></option>
<option value="D_android" <c:if test="${searchObj.appType=='D_android'}">selected="selected" </c:if> >神州专车-司机端</option>
<option value="M_iOS" <c:if test="${searchObj.appType=='M_iOS'}">selected="selected" </c:if> >神州专车-IOS</option>
<option value="M_android" <c:if test="${searchObj.appType=='M_android'}">selected="selected" </c:if> >神州专车-android</option>
</select>
</div>
</div>
</div>
<div class="span4 ">
<div class="control-group">
<label class="control-label">APP版本<span class="required"></span></label>
<div class="controls select2-wrapper">
<select id="form_2_select2" class="span12" name="appVersion">
<option value=""></option>
<c:forEach var="version" items="${appVersion}" varStatus="status">
<option value="${version }" <c:if test="${searchObj.appVersion==version}">selected="selected" </c:if> >${version }</option>
</c:forEach>
</select>
</div>
</div>
</div>
<div class="span4 ">
<div class="control-group">
<label class="control-label">下载渠道<span class="required"></span></label>
<div class="controls select2-wrapper">
<select id="form_2_select3" class="span12" name="appChannel">
<option value=""></option>
<c:forEach var="channel" items="${appChannel }" varStatus="status">
<option value="${channel }" <c:if test="${searchObj.appChannel==channel}">selected="selected" </c:if>>${channel }</option>
</c:forEach>
</select>
</div>
</div>
</div>
</div>
<div class="form-actions">
<button id="btnQuery" type="button" class="btn blue"><i class="icon-ok"></i>查询</button>
<input id="slideBarCheckbox" type="checkbox"/>用户成分分析: <input id="slideBar" type="text" value=""/> <b id="slideBarTips">当天至7天前新增</b>
</div>
</form>
<!-- END FORM-->
</div>
</div>
<!-- END VALIDATION STATES-->
</div>
</div>
<!-- search layout end -->

<!-- charts layout begin -->
<div class="row-fluid">
<div class="span12 ">
<div class="portlet box blue tabbable">
<div class="portlet-title">
<div class="caption"><i class="icon-reorder"></i>用户新鲜度</div>
</div>
<div class="portlet-body">
<div class="tabbable portlet-tabs">
<ul class="nav nav-tabs">
<li><a eleType="tab" href="#portlet_tab1" data-toggle="tab" id="tab_ratio" dataType="BFB">百分比</a></li>
<li class="active"><a eleType="tab" href="#portlet_tab2" data-toggle="tab" id="tab_num" dataType="JDZ">绝对值</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="portlet_tab1">
<div id="chart_div" style="width:100%;height:500px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- charts layout end -->

<script src="${baseContextPath}static/js/jquery-1.10.1.min.js" type="text/javascript"></script>
<script src="${baseContextPath}static/js/jquery-migrate-1.2.1.min.js" type="text/javascript"></script>
<!-- IMPORTANT! Load jquery-ui-1.10.1.custom.min.js before bootstrap.min.js to fix bootstrap tooltip conflict with jquery ui tooltip -->
<!-- <script src="${baseContextPath}static/js/jquery-ui-1.10.1.custom.min.js" type="text/javascript"></script> -->
<script src="${baseContextPath}static/js/bootstrap.min.js" type="text/javascript"></script>
<!--[if lt IE 9]>
<script src="${baseContextPath}static/js/excanvas.min.js"></script>
<script src="${baseContextPath}static/js/respond.min.js"></script>
<![endif]-->
<script src="${baseContextPath}static/js/jquery.slimscroll.min.js" type="text/javascript"></script>
<script src="${baseContextPath}static/js/jquery.blockui.min.js" type="text/javascript"></script>
<script src="${baseContextPath}static/js/jquery.cookie.min.js" type="text/javascript"></script>
<script src="${baseContextPath}static/js/jquery.uniform.min.js" type="text/javascript" ></script>
<!-- END CORE PLUGINS -->
<!-- BEGIN PAGE LEVEL PLUGINS -->
<script type="text/javascript" src="${baseContextPath}static/js/chosen.jquery.min.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/wysihtml5-0.3.0.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/bootstrap-wysihtml5.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/jquery.tagsinput.min.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/jquery.toggle.buttons.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/bootstrap-datetimepicker.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/clockface.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/date.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/daterangepicker.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/bootstrap-colorpicker.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/bootstrap-timepicker.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/jquery.inputmask.bundle.min.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/jquery.input-ip-address-control-1.0.min.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/jquery.multi-select.js"></script>
<script src="${baseContextPath}static/js/bootstrap-modal.js" type="text/javascript" ></script>
<script src="${baseContextPath}static/js/bootstrap-modalmanager.js" type="text/javascript" ></script>
<script type="text/javascript" src="${baseContextPath}static/js/select2.min.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="${baseContextPath}static/js/DT_bootstrap.js"></script>
<!-- END PAGE LEVEL PLUGINS -->
<script type="text/javascript" src="${baseContextPath}static/js/jquery.validate.min.js"></script>
<!-- BEGIN PAGE LEVEL SCRIPTS -->
<script src="${baseContextPath}static/js/app.js"></script>
<script src="${baseContextPath}static/js/table-managed.js"></script>
<script src="${baseContextPath}static/js/form-validation.js"></script>
<script src="${baseContextPath}static/selfJs/common.js" type="text/javascript"></script>
<!-- END PAGE LEVEL SCRIPTS -->
<script src="${baseContextPath}static/selfJs/datepicker.js" type="text/javascript"></script>
<script src="${baseContextPath}static/echarts3/echarts.min.js"></script>
<script src="${baseContextPath}static/plugins/bootstrap-slider4.4.0/bootstrap-slider.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
// initiate layout and plugins
App.init();
FormValidation.init();
});
</script>
<script type="text/javascript" src="${baseContextPath}static/import/retention/freshness.js?v=<%=version%>"></script>
</body>
</html>

freshness.js源码:
/**
* 用户新鲜度模块
* @author 石冬冬
* @date 2016/12/1
*/
var Freshness = {
Cts:{
charts:{
render:null,
viewData:null,
totals:[]
},
limit:[0,31],
rs:null
},
/**
* 初始化
*/
init:function(){
this.initEvts();
this.initVals();
this.initSlideBar(false);
this.initData();
this.renderCharts();
},
/**
* 初始化事件
*/
initEvts:function(){
var _this = Freshness;
$('#btnQuery').off().live('click',function(){
_this.initData();
_this.renderCharts();
});
//选项卡切换
$('a[eleType=tab]').off().live('click',function(){
var dataType = $(this).attr('dataType');
$('#dataType').val(dataType);
_this.initData();
_this.renderCharts();
});

$('#slideBarCheckbox').off().live('click',function(){
var checked = $(this).attr('checked');
var enabled = checked=='checked';
_this.initSlideBar(enabled);
});
},
/**
* 初始化相关元素的值
*/
initVals:function(){
var appVersion = $('#appVersionList').val();
if(appVersion){
appVersion = eval('('+appVersion+')');
loadData(appVersion);
}
},
/**
* 初始化标尺滚动条
* @param enabled
* API:http://demo.htmleaf.com/1502/201502041438/index.html
* http://blog.csdn.net/u011127019/article/details/52992654 */
initSlideBar:function(enabled){
var _this = Freshness;
//初始化配置项
$("#slideBar").slider({
handle:'round',
min:0,
max:31,
step:1,
value:[0,31],
tooltip_split:true,
formatter: function(value) {
var start = value[0];
var end = value[1];
if(start==0){
start = '当天';
}else{
start+='天前';
}
if(end==31){
end = '31天+前';
}else{
end+='天前';
}
var tips = start+'至'+end+'新增';
if(value && $.isArray(value) && value.length==2){
$('#slideBarTips').text(tips);
}
return value;
}
});
//监听滑动事件
$("#slideBar").on("change", function() {
var value = this.value.split(",");
if(value){
_this.Cts.limit=value;
_this.renderCharts();//刷新加载echarts
}
});
//根据可用多选框设置控件是否可用
if(enabled){
$('#slideBarTips').show();
$("#slideBar").slider("enable");
}else{
$('#slideBarTips').hide();
$("#slideBar").slider("setValue",[0,31]);
$("#slideBar").slider("disable");
_this.Cts.limit=[0,31];
_this.renderCharts();//刷新加载echarts
}
},
/**
* 初始化报表
* @param args 对象
* @param xAxisData x坐标数组
*/
initCharts:function(args,xAxisData){
var dataType = $('#dataType').val();
var _yAxis = [
{
type : 'value',
boundaryGap : false
}
];
if(dataType=='BFB'){
_yAxis = [
{
type : 'value',
max:'100',
axisLabel: {
show: true,
interval: 'auto',
formatter: '{value} %'
},
boundaryGap : false
}
];
}
var _this = Freshness;
var series = args.seriesData;
var legendData = args.legendData;
var myChart = echarts.init(document.getElementById("chart_div"), 'line');
this.Cts.charts.render = myChart;
var option = {
title : {
},
tooltip : {
trigger: 'axis',
formatter:function (params,ticket,callback) {
var tips = '';
if(dataType=='JDZ'){
var seriesName = params.seriesName;
var value = params.value;
var name = params.name;
var total = _this.findTotal(name);
var ratio = 0;
if(total!=0){
ratio = ((value / total)*100).toFixed(2);
}
tips = name + '<br/><br/>在' + seriesName + '新增(占当日活跃比):<br/>'+ value + '('+ratio+'%)' + '<br/><br/>当日全部活跃:'+total;
}
if(dataType=='BFB'){
var seriesName = params.seriesName;
var name = params.name;
var total = _this.findTotal(name);
var value = params.value;
value = value/100*total;
var ratio = 0;
if(total!=0){
ratio = ((value / total)*100).toFixed(2);
}
tips = name + '<br/><br/>在' + seriesName + '新增(占当日活跃比):<br/>'+ value + '('+ratio+'%)' + '<br/><br/>当日全部活跃:'+total;
}
return tips;
}
},
legend: {
show:false,
width:'80%',
padding: [5,100,5,100],
itemGap: 10,
data : legendData
},
dataZoom:{},
toolbox: {
show : true,
feature : {
mark : {show: false},
dataView : {show: true, readOnly: false},
magicType: {show: true, type: ['line','bar','stack', 'tiled']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis : [{
type:'category',
interval:0,
boundaryGap:false,
data:xAxisData
}],
yAxis : _yAxis,
series : series
};
myChart.setOption(option);
},
/**
* 根据series的datas构建seriesData和legendData
* @param datas
*/
initSeries:function(datas){
var _this = Freshness;
var limit = _this.Cts.limit;
console.log('limit=%s',JSON.stringify(limit));
var from = Number(limit[0]) ;
var to = Number(limit[1]);
var legendData = [];
var series = [];
for(var i=to;i>=from;i--){
var name = i+'天前';
if(i==0){
name = '当天';
}
if(i==31){
name = '30+天前';
}
legendData.push(name);
var vo = {
name: name,
type: 'line',
stack:'1',
tooltip : {
show : true,
trigger: 'item'
},
areaStyle: {normal: {}},
data: datas[i]
};
series.push(vo);
}
return {
seriesData:series,
legendData:legendData
};
},
/**
* 初始化数据
*/
initData:function(){
var params = $('#searchForm').serialize();
var _this = Freshness;
$.ajax({
type : "post",
url : BATH_PATH + "retentionAnalysis/loadForFreshness",
data: params,
dataType : "json",
async : false,
error : function(xhr, status, err) {
//alert(err);
},
success : function(rs) {
_this.Cts.rs = rs;
}
});
},
/**
* 根据容量构建二维数组
* @returns {Array} 二维数组
*/
initArrays:function(){
var capacity = 31;
var arrays = [];
for(var i = 0;i<=capacity;i++){
arrays.push([]);
}
return arrays;
},
/**
* 根据xAxisName 查询对应的 活跃总数
* @param xAxisName
*/
findTotal:function(xAxisName){
var totals = this.Cts.charts.totals;
var total = 0;
if(totals){
$.each(totals,function(index){
//console.log('key=%s,val=%s',totals[index].key,val);
if(totals[index].key == xAxisName){
total = totals[index].total;
return;
}
});
}
return total;
},
/**
* 渲染Charts
* @param rs
*/
renderCharts:function(){
var _this = Freshness;
var rs = _this.Cts.rs;
var dataType = $('#dataType').val();
var xAxisData = [];
var datas = this.initArrays();
var totals = [];
if(rs){
var data = rs.dataMap;
//======================绝对值=============================
if(dataType=='JDZ'){
$.each(data,function(key,vo){
xAxisData.push(key);
totals.push({key:key,total:vo.total});
//console.log('key=%s,total=%s',key,vo.total);
$.each(vo.groupMap,function(index,val){
datas[index].push(val);
});
});
//console.info("arrays=%s",JSON.stringify(datas));
}
//======================百分比=============================
if(dataType=='BFB'){
$.each(data,function(key,vo){
xAxisData.push(key);
var total = vo.total;
totals.push({key:key,total:total});
//console.log('key=%s,total=%s',key,vo.total);
$.each(vo.groupMap,function(index,val){
var percent = (val==0)?0:((val/total)*100).toFixed(2);
datas[index].push(percent);
});
});
}

}
//console.log('totals=%s',JSON.stringify(totals));
_this.Cts.charts.totals = totals;
//console.log('totals=%s',JSON.stringify(_this.Cts.charts.totals));
var args = _this.initSeries(datas);
_this.initCharts(args,xAxisData);
}
};
Freshness.init();

Controller
package com.ucar.appcount.web.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.ucar.appcount.common.Common;
import com.ucar.appcount.common.vo.SearchObject;
import com.ucar.appcount.service.AdminManagerServiceNew;
import com.ucar.appcount.service.RetentionAnalysisService;
import com.zuche.framework.utils.StringUtils;
/**
* 留存分析
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-11-28 下午6:53:41
*/
@Controller
@RequestMapping("retentionAnalysis")
public class RetentionAnalysisController {
private static final Logger logger = Logger.getLogger(RetentionAnalysisController.class);
@Autowired
private AdminManagerServiceNew adminManagerServiceNew;
@Autowired
private RetentionAnalysisService retentionAnalysisService;

private final String PAGE_PREFIX = "retention/";
/**
* 留存用户|页面
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-11-28 下午7:01:07
* @param model
* @param searchObj
* @return
*/
@RequestMapping("retention")
public String retention(Model model,SearchObject searchObj){
try {
List<String> appChannel = adminManagerServiceNew.getAppChannel();
List<String> appVersion = adminManagerServiceNew.getAppVersion();
String timeType = searchObj.getTimeType();
String columnTitleSuffix = "天后";
if(StringUtils.isEmpty(timeType)){
timeType = Common.DATATYPE.DAY;
}
initSearchObj(searchObj);
if(Common.DATATYPE.DAY.equals(timeType)){
model.addAttribute("columns", RetentionAnalysisService.ReportTitle.DAILY);
columnTitleSuffix = "天后";
}
if(Common.DATATYPE.WEEK.equals(timeType)){
model.addAttribute("columns", RetentionAnalysisService.ReportTitle.WEEK);
columnTitleSuffix = "周后";
}
if(Common.DATATYPE.MONTH.equals(timeType)){
model.addAttribute("columns", RetentionAnalysisService.ReportTitle.MONTH);
columnTitleSuffix = "月后";
}
model.addAttribute("columnTitleSuffix", columnTitleSuffix);
model.addAttribute("searchObj", searchObj);
model.addAttribute("appChannel",appChannel);
model.addAttribute("appVersion", appVersion);
} catch (Exception e) {
logger.error("留存用户初始化异常",e);
}
return PAGE_PREFIX.concat("retention");
}
/**
* 用户新鲜度
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-12-1 下午2:00:31
* @param model
* @param searchObj
* @return
*/
@RequestMapping("freshness")
public String freshness(Model model,SearchObject searchObj){
try {
List<String> appChannel = adminManagerServiceNew.getAppChannel();
List<String> appVersion = adminManagerServiceNew.getAppVersion();
String timeType = searchObj.getTimeType();
if(StringUtils.isEmpty(timeType)){
timeType = Common.DATATYPE.DAY;
}
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
//开始时间为空则取三个月前
if(StringUtils.isBlank(searchObj.getBeginTime())){
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.MONTH,-1);
searchObj.setBeginTime(sdf.format(calendar.getTime()));
}
if(StringUtils.isBlank(searchObj.getEndTime())){
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR,-1);
searchObj.setEndTime(sdf.format(calendar.getTime()));
}
searchObj.setDataType("JDZ");//默认 绝对值
model.addAttribute("searchObj", searchObj);
model.addAttribute("appChannel",appChannel);
model.addAttribute("appVersion", appVersion);
} catch (Exception e) {
logger.error("留存用户初始化异常",e);
}
return PAGE_PREFIX.concat("freshness");
}

/**
* 留存用户
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-11-29 上午9:53:00
* @param searchObj
* @return
*/
@RequestMapping("loadForRetention")
@ResponseBody
public Object loadForRetention(SearchObject searchObj){
Map<String,Object> dataMap = new HashMap<String,Object>();
if(!StringUtils.isBlank(searchObj.getTimeType())){
Map<String, Map<Integer,Double>> result = this.retentionAnalysisService.retention(searchObj);
dataMap.put("data", result);
}
return dataMap;
}

/**
* 用户新鲜度
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-11-29 上午9:53:00
* @param searchObj
* @return
*/
@RequestMapping("loadForFreshness")
@ResponseBody
public Object loadForFreshness(SearchObject searchObj){
return this.retentionAnalysisService.freshness(searchObj);
}

@RequestMapping("activeDegree")
public String toActiveDegree(Model model,SearchObject searchObj){
List<String> appChannel = adminManagerServiceNew.getAppChannel();
List<String> appVersion = adminManagerServiceNew.getAppVersion();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
//开始时间为空则取三个月前
if(StringUtils.isBlank(searchObj.getBeginTime())){
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.MONTH,-1);
searchObj.setBeginTime(sdf.format(calendar.getTime()));
}
if(StringUtils.isBlank(searchObj.getEndTime())){
Calendar calendar=Calendar.getInstance();
//calendar.add(Calendar.DAY_OF_YEAR,-1);
searchObj.setEndTime(sdf.format(calendar.getTime()));
}
model.addAttribute("searchObj", searchObj);
model.addAttribute("appChannel",appChannel);
model.addAttribute("appVersion", appVersion);
return PAGE_PREFIX+"/activeDegree";
}

@RequestMapping("loadActiveDegree")
@ResponseBody
public Object loadActiveDegree(SearchObject searchObj){
initSearchObj(searchObj);
Map<String,Map<Integer,Integer>> map=new HashMap<String, Map<Integer, Integer>>();
try {
map=retentionAnalysisService.getActiveDegree(searchObj);
} catch (ParseException e) {
e.printStackTrace();
}
return map;
}
@RequestMapping("loadActive15Degree")
@ResponseBody
public Object loadActive15Degree(SearchObject searchObj){
initSearchObj(searchObj);
Map<String,Map<Integer,Integer>> map=new HashMap<String, Map<Integer, Integer>>();
try {
map=retentionAnalysisService.getActive15Degree(searchObj);
} catch (ParseException e) {
e.printStackTrace();
}
return map;
}

private void initSearchObj(SearchObject seachObj) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
//开始时间为空则取三个月前
if(StringUtils.isBlank(seachObj.getBeginTime())){
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.WEEK_OF_YEAR,-1);
seachObj.setBeginTime(sdf.format(calendar.getTime()));
}
if(StringUtils.isBlank(seachObj.getEndTime())){
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR,-1);
seachObj.setEndTime(sdf.format(calendar.getTime()));
}

}
}


Service
package com.ucar.appcount.service.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.hadoop.hbase.util.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.ucar.appcount.common.Common;
import com.ucar.appcount.common.CommonUtils;
import com.ucar.appcount.common.DateFormatUtil;
import com.ucar.appcount.common.vo.RetentionFreshnessVo;
import com.ucar.appcount.common.vo.SearchObject;
import com.ucar.appcount.component.SearchComponent;
import com.ucar.appcount.hbase.service.DevActive15DegreeTabServiceNew;
import com.ucar.appcount.hbase.service.DevActiveDegreeTabServiceNew;
import com.ucar.appcount.hbase.service.impl.BaseOperator;
import com.ucar.appcount.service.DevUserServiceNew;
import com.ucar.appcount.service.RetentionAnalysisDailyTabService;
import com.ucar.appcount.service.RetentionAnalysisMonthTabService;
import com.ucar.appcount.service.RetentionAnalysisService;
import com.ucar.appcount.service.RetentionAnalysisWeekTabService;
import com.ucar.appcount.service.RetentionFreshnessDailyTabService;
import com.zuche.framework.nosql.hbase098.vo.Column;
import com.zuche.framework.nosql.hbase098.vo.RowVo;
import com.zuche.framework.utils.StringUtils;
@Service
public class RetentionAnalysisServiceImpl extends BaseOperator implements RetentionAnalysisService {
private static final Logger logger = LoggerFactory.getLogger(RetentionAnalysisServiceImpl.class);
@Autowired
private SearchComponent searchComponent;
@Autowired
private RetentionAnalysisDailyTabService retentionAnalysisDailyTabService;
@Autowired
private RetentionAnalysisWeekTabService retentionAnalysisWeekTabService;
@Autowired
private RetentionAnalysisMonthTabService retentionAnalysisMonthTabService;
@Autowired
private RetentionFreshnessDailyTabService retentionFreshnessDailyTabService;
@Autowired
private DevActiveDegreeTabServiceNew activeDegreeTabService;
@Autowired
private DevActive15DegreeTabServiceNew active15DegreeTabService;
@Autowired
private DevUserServiceNew devUserServiceNew;

@Override
public List<RowVo> queryRowVoList(SearchObject searchObj,String filterList){
List<RowVo> rs = Collections.emptyList();
String timeType= searchObj.getTimeType();
if(Common.DATATYPE.DAY.equals(timeType)){
rs = this.retentionAnalysisDailyTabService.queryRetentionRows(searchObj, filterList);
}
if(Common.DATATYPE.WEEK.equals(timeType)){
rs = this.retentionAnalysisWeekTabService.queryRetentionRows(searchObj, filterList);
}
if(Common.DATATYPE.MONTH.equals(timeType)){
rs = this.retentionAnalysisMonthTabService.queryRetentionRows(searchObj, filterList);
}
return rs;
}
@Override
public Map<String, Map<Integer, Double>> retention(SearchObject searchObj) {
Map<String, Map<Integer, Double>> dataMap = new LinkedHashMap<String, Map<Integer,Double>>();
try {
String timeType= searchObj.getTimeType();
final boolean byDay = Common.DATATYPE.DAY.equals(timeType);
final boolean byWeek = Common.DATATYPE.WEEK.equals(timeType);
final boolean byMonth = Common.DATATYPE.MONTH.equals(timeType);
/*****************************************************************/
/**
* 如果是按月统计:对开始时间 和 结束时间 逻辑控制
* 开始时间如果非开始时间当月的第一天,需要重置为下个月的第一天。
* 结束时间如果非结束时间当月的第一条,需要重置为上个月的第一天。
*/
/*****************************************************************/
RetentionHandler.resetSearchTimeForByMonth(byMonth,searchObj);
if(byMonth){
Date start = DateFormatUtil.convertToDate(searchObj.getBeginTime(), DateFormatUtil.FORMAT_DATE_DISPLAY);
Date end = DateFormatUtil.convertToDate(searchObj.getEndTime(), DateFormatUtil.FORMAT_DATE_DISPLAY);
if(start.getTime()!=end.getTime()&&!end.after(start)){
return dataMap;
}
}
//1、构建查询结果Map
this.searchComponent.initDataMapForRetention(dataMap,searchObj);
String filterList = this.searchComponent.initFilterList(searchObj);
//2、获取新增用户Map
Map<String,Integer> userAddMap = this.devUserServiceNew.getNewUserNum(searchObj);
//2.1、按时间装载新增用户数
Set<Entry<String, Map<Integer, Double>>> entrySet = dataMap.entrySet();
for(Entry<String, Map<Integer, Double>> entry : entrySet){
String dataKey = entry.getKey();
if(userAddMap.containsKey(dataKey)){
Map<Integer, Double> columnMap = entry.getValue();
Double addUsers = Double.valueOf(userAddMap.get(dataKey).doubleValue());
columnMap.put(0, addUsers);
}
}
/*****************************************************************/
/**
* rowkey特殊规则说明:新增日期#APP类型#APP版本#下载渠道#统计日期
* 按天:(20161125#M_android#600340#360shoujizhushou#20161128)
* 按周:(20161128#M_android#600340#360shoujizhushou#20161130)
* 按月:(20161101#M_android#600340#360shoujizhushou#20161201)
*/
/*****************************************************************/
//3、获取留存用户
List<RowVo> rs = this.queryRowVoList(searchObj, filterList);
for(RowVo row : rs){
String key = row.getRowKey();//新增日期
String rowKey = row.getRowKey();
String tjrq = rowKey.substring(rowKey.lastIndexOf("#")+1);
key = key.substring(0,key.indexOf("#",0));
key = CommonUtils.handlerTime(key, timeType);
tjrq = CommonUtils.handlerTime(tjrq, timeType);//统计日期
if(byWeek){//按周统计,把日期都转换成这个星期的第一天
key = DateFormatUtil.getFirstDayOfWeek(key);
key = key + "~" + DateFormatUtil.getLastDayOfWeek(key);
}
int days = 0;//天数
if(byDay||byWeek){
days=Long.valueOf(DateFormatUtil.getTwoDateDifference(key,tjrq)).intValue();
}else{
String s=key.concat("-01");
String e=tjrq.concat("-01");
days=Long.valueOf(DateFormatUtil.getTwoDateDifference(s,e)).intValue();
}
List<Column> columns = row.getColumns();
Integer count = Integer.valueOf(getValue(columns,Bytes.toBytes("statistical"), Bytes.toBytes("count")));
if(dataMap.containsKey(key)){
Map<Integer, Double> columnMap = dataMap.get(key);
int columnIndex = RetentionHandler.getColumnIndexForRetention(timeType, days);//获取时间类型(天/周/月)对列索引
Double retention = columnMap.get(columnIndex);
logger.error("留存用户载入:retention={},columnMap={}",retention,columnMap);
if(null!=columnMap && null!=retention){
columnMap.put(columnIndex, count+retention);
}
dataMap.put(key, columnMap);
}
}
logger.error("留存用户加载数据:dataMap={}",JSON.toJSONString(dataMap));
} catch (Exception e) {
logger.error("留存用户加载数据异常:dataMap={}",JSON.toJSONString(dataMap),e);
}
return dataMap;
}

@Override
public Map<String, Object> freshness(SearchObject searchObj){
Map<String, Object> resultMap = new HashMap<String, Object>();
try {
Map<String, RetentionFreshnessVo> dataMap = new LinkedHashMap<String, RetentionFreshnessVo>();
String timeType = searchObj.getTimeType();
Integer from = searchObj.getFrom();
Integer to = searchObj.getTo();
String filterList = this.searchComponent.initFilterList(searchObj);
//2、获取用户新鲜度
List<RowVo> rs = this.retentionFreshnessDailyTabService.queryFreshnessRows(searchObj, filterList);
for(RowVo row : rs){
String key = row.getRowKey();//登录日期
String rowKey = row.getRowKey();
String xzrq = rowKey.substring(rowKey.lastIndexOf("#")+1);
key = key.substring(0,key.indexOf("#",0));
key = CommonUtils.handlerTime(key, timeType);
xzrq = CommonUtils.handlerTime(xzrq, timeType);//新增日期
List<Column> columns = row.getColumns();
Integer count = Integer.valueOf(getValue(columns,Bytes.toBytes("statistical"), Bytes.toBytes("count")));
if(dataMap.containsKey(key)){
RetentionFreshnessVo freshnessVo = dataMap.get(key);
int orig = freshnessVo.getTotal();
freshnessVo.setTotal(orig + count);
int duration = RetentionHandler.getColumnIndexForFreshness(key, xzrq,to);
if(duration<from){
continue;
}
Map<Integer,Integer> groupMap = freshnessVo.getGroupMap();
groupMap.put(duration, count + groupMap.get(duration));
dataMap.put(key, freshnessVo);
}else{
RetentionFreshnessVo freshnessVo = new RetentionFreshnessVo();
Map<Integer, Integer> groupMap = new LinkedHashMap<Integer, Integer>();
for(int d=to;d>=from;d--){
groupMap.put(d, 0);
}
freshnessVo.setGroupMap(groupMap);
dataMap.put(key, freshnessVo);
}
}
logger.error("用户新鲜度加载数据:dataMap={}",JSON.toJSONString(dataMap));
resultMap.put("dataMap", dataMap);
} catch (Exception e) {
logger.error("用户新鲜度加载数据异常:dataMap={}",JSON.toJSONString(resultMap),e);
}
return resultMap;
}

/**
* 获取用户活跃度数据
*
* @param searchObj
* @return
*/
@Override
public Map<String, Map<Integer, Integer>> getActiveDegree(SearchObject searchObj) throws ParseException {
Map<String, Map<Integer, Integer>> map=new LinkedHashMap<String, Map<Integer, Integer>>();
//20161113#M_android#600221#360shoujizhushou#2
String filterList = ".*";
if(!StringUtils.isEmpty(searchObj.getAppType())){
filterList += "#"+searchObj.getAppType();
}else{
filterList += "#.*";
}
if(!StringUtils.isEmpty(searchObj.getAppVersion())){
filterList += "#"+searchObj.getAppVersion();
}else{
filterList += "#.*";
}
if(!StringUtils.isEmpty(searchObj.getAppChannel())){
filterList += "#"+searchObj.getAppChannel()+".*";
}else{
filterList += "#.*";
}

//20160828#M_android#600171#SGCM 01#1
searchObj.setTimeType(Common.DATATYPE.DAY);
//结束时间默认加一天
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateFormat.parse(searchObj.getEndTime()));
calendar.add(Calendar.DATE, 1);
searchObj.setEndTime(dateFormat.format(calendar.getTime()));
List<RowVo> activeList = activeDegreeTabService.getActiveDegree(searchObj, filterList);
for(RowVo r : activeList){
String key = r.getRowKey();
Integer days=Integer.valueOf(key.substring(key.lastIndexOf("#") + 1));
key = key.substring(0, key.indexOf("#", 0));
key = CommonUtils.handlerTime(key, searchObj.getTimeType());

List<Column> columns = r.getColumns();
Integer count = Integer.valueOf(getValue(columns,Bytes.toBytes("statistical"), Bytes.toBytes("count")));
if(map.containsKey(key)){
if(map.get(key).containsKey(days)) {
map.get(key).put(days, map.get(key).get(days)+count);
}else{
map.get(key).put(days,count);
}
}else{
Map<Integer,Integer> tempMap=new HashMap<Integer, Integer>();
tempMap.put(days,count);
map.put(key,tempMap);
}
}

return map;
}

/**
* 获取用户活跃度数据
*
* @param searchObj
* @return
*/
@Override
public Map<String, Map<Integer, Integer>> getActive15Degree(SearchObject searchObj) throws ParseException {
Map<String, Map<Integer, Integer>> map=new LinkedHashMap<String, Map<Integer, Integer>>();
//20161113#M_android#600221#360shoujizhushou#2
String filterList = ".*";
if(!StringUtils.isEmpty(searchObj.getAppType())){
filterList += "#"+searchObj.getAppType();
}else{
filterList += "#.*";
}
if(!StringUtils.isEmpty(searchObj.getAppVersion())){
filterList += "#"+searchObj.getAppVersion();
}else{
filterList += "#.*";
}
if(!StringUtils.isEmpty(searchObj.getAppChannel())){
filterList += "#"+searchObj.getAppChannel()+".*";
}else{
filterList += "#.*";
}

//20160828#M_android#600171#SGCM 01#1
searchObj.setTimeType(Common.DATATYPE.DAY);
//结束时间默认加一天
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateFormat.parse(searchObj.getEndTime()));
calendar.add(Calendar.DATE,1);
searchObj.setEndTime(dateFormat.format(calendar.getTime()));
List<RowVo> activeList = active15DegreeTabService.getActiveDegree(searchObj, filterList);
for(RowVo r : activeList){
String key = r.getRowKey();
key = key.substring(0, key.indexOf("#", 0));
key = CommonUtils.handlerTime(key, searchObj.getTimeType());

Integer days=Integer.valueOf(key.substring(key.lastIndexOf("#") + 1));

List<Column> columns = r.getColumns();
Integer count = Integer.valueOf(getValue(columns,Bytes.toBytes("statistical"), Bytes.toBytes("count")));
if(map.containsKey(key)){
if(map.get(key).containsKey(days)) {
map.get(key).put(days, map.get(key).get(days)+count);
}else{
map.get(key).put(days,count);
}
}else{
Map<Integer,Integer> tempMap=new HashMap<Integer, Integer>();
tempMap.put(days,count);
map.put(key,tempMap);
}
}

return map;
}

/**
* 留存用户数据处理类
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-11-30 上午9:54:17
*/
public static class RetentionHandler{
/**
* 根据时间类型(日|周|月)获取对应列头对应的索引
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-11-29 下午3:48:35
* @param timeType 时间类型
* @param days 差异天数
* @return
*/
public static int getColumnIndexForRetention(String timeType,int days){
int columnIndex = 0;
if(days==0){
return -1;
}
if(Common.DATATYPE.DAY.equals(timeType)){
if(days==0){
return -1;
}
if(days<7){
columnIndex = ReportTitle.DAILY[days-1];
}else if(days==14){
columnIndex = ReportTitle.DAILY[7];
}else if(days==30){
columnIndex = ReportTitle.DAILY[8];
}
}
if(Common.DATATYPE.WEEK.equals(timeType)){
int week = (days/7)-1;
if(week<0){
return -1;
}
columnIndex = ReportTitle.WEEK[week];
}
if(Common.DATATYPE.MONTH.equals(timeType)){
int month = (days/30)-1;
if(month<0){
return -1;
}
columnIndex = ReportTitle.MONTH[month];
}
return columnIndex;
}
/**
* 用户新鲜度|获取登录日期与新增日期的差异天数
* @author 石冬冬-Heil Hilter(dd.shi02@zuche.com)
* @date 2016-12-1 下午3:01:57
* @param dlrq 登录日期
* @param xzrq 新增日期
* @return
*/
public static int getColumnIndexForFreshness(String dlrq,String xzrq,Integer to){
int duration = 0;
duration = Long.valueOf(DateFormatUtil.getTwoDateDifference(xzrq,dlrq)).intValue();
if(duration>=to){
return to;
}
return duration;
}
/*****************************************************************/
/**
* 如果是按月统计:对开始时间 和 结束时间 逻辑控制
* 开始时间如果非开始时间当月的第一天,需要重置为下个月的第一天。
* 结束时间如果非结束时间当月的第一条,需要重置为上个月的第一天。
*/
/*****************************************************************/
public static void resetSearchTimeForByMonth(boolean byMonth,SearchObject searchObj){
if(!byMonth){
return;
}
String beginTime = searchObj.getBeginTime();
String endTime = searchObj.getEndTime();
if(!StringUtils.isEmpty(beginTime)){
Calendar calendar = Calendar.getInstance();
calendar.setTime(DateFormatUtil.convertToDate(beginTime,DateFormatUtil.FORMAT_DATE_DISPLAY));
int today = calendar.get(Calendar.DAY_OF_MONTH);
if(today!=1){
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, 1);
String newTime = DateFormatUtil.convertDateToString(calendar.getTime(), DateFormatUtil.FORMAT_DATE_DISPLAY);
searchObj.setBeginTime(newTime);
}
}
if(!StringUtils.isEmpty(endTime)){
Calendar calendar = Calendar.getInstance();
calendar.setTime(DateFormatUtil.convertToDate(endTime,DateFormatUtil.FORMAT_DATE_DISPLAY));
int today = calendar.get(Calendar.DAY_OF_MONTH);
boolean reset = false;
//下面有些情况却要确认。。。。
if(today!=1){
calendar.add(Calendar.MONTH, -1);
calendar.set(Calendar.DAY_OF_MONTH, 1);
reset = true;
}else{
int month = calendar.get(Calendar.MONTH);
Calendar nowCalendar = Calendar.getInstance();
int currentMonth = nowCalendar.get(Calendar.MONTH);
//非本月第一天时,
if(month==currentMonth){
calendar.add(Calendar.MONTH, -1);
reset = true;
}
}
if(reset){
String newTime = DateFormatUtil.convertDateToString(calendar.getTime(), DateFormatUtil.FORMAT_DATE_DISPLAY);
searchObj.setEndTime(newTime);
}
}
}

public static void main(String[] args) {
//System.out.println(getColumnIndex(Common.DATATYPE.WEEK, 7));
System.out.println(RetentionAnalysisService.ReportTitle.MONTH_COLS);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring mvc echarts