效果图
HTML代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>系统报表</title>
</head>
<link href="/pages/plugins/bootstrap-datetimepicker/css/datetimepicker.css" rel="stylesheet">
<link href="/pages/common/layui/css/layui.css" rel="stylesheet">
<link href="./css/public.css" rel="stylesheet">
<style>
body {
margin: 0;
}
.yellow {
background-color: yellow;
}
</style>
<script type="text/javascript">
Date.prototype.Format = function (fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
</script>
<body>
<div class="layui-container" style="width: 90%;margin-top: 30px;">
<div class="layui-row layui-col-space15">
<!-- 全局时间范围选择 -->
<div class="layui-col-sm12">
<div class="layui-card">
<div class="layui-card-header" style="height: 52px;">
<div class="layui-form"
style="min-width: 400px;max-height: 30px;display: inline-flex; margin-top: 8px;">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" style="padding: 9px 8px">时间范围</label>
<div class="layui-input-inline">
<input class="layui-input" id="globalDate" value="" placeholder=" " type="text">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 80px;">
<select class="global-time-unit" name="timeUnit" lay-filter="global-time-unit">
<option value="1">月</option>
<option value="2" selected>天</option>
<option value="3">小时</option>
<option value="4">分钟</option>
</select>
</div>
</div>
<div class="layui-inline">
<span style="color: red;" class="prompt_info"></span>
</div>
</div>
<button type="button" lay-submit="" class="layui-btn layui-btn-warm" id="exportExcel" name="exportExcel">
<i class="layui-icon"></i>导出Excel</button>
</div>
</div>
</div>
</div>
<!-- 用户注册 -->
<div class="layui-col-sm12">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-row">
<div class="layui-col-sm12">
<div class="layui-carousel layadmin-carousel layadmin-dataview" data-anim="fade"
lay-filter="LAY-index-pagetwo" lay-anim="fade" style="width: 100%; ">
<!-- <div carousel-item="" id="LAY-index-pagetwo"> -->
<div id="userRegisterCount" style="width:100%;height:300px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<table class="layui-table" lay-filter="statisticsOpenAppCount_table" id="statisticsOpenAppCount_table">
<thead>
<tr id="template">
<th lay-data="width:80" id="timeStr">时间</th>
<th lay-data="width:80, sort:true" id="count">启动次数</th>
<th lay-data="width:100" id="countRate">启动次数占比</th>
</tr>
</thead>
</table>
</div>
</div>
<script type="text/javascript" src="/pages/common/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/pages/common/jquery/jquery.md5.js"></script>
<script type="text/javascript" src="/pages/common/layui/layui.all.js"></script>
<script type="text/javascript" src="/pages/common/echarts/echarts.min.js"></script>
<script type="text/javascript" src="/pages/common/echarts/shine.js"></script>
<script type="text/javascript" src="/pages/console/js/common.js?v=2019082401"></script>
<script type="text/javascript" src="/pages/console/js/statisticsOpenAppCount.js?v=2019082401"></script>
<script type="text/javascript">
</script>
</body>
</html>
JS代码
/**
* 统计报表相关的js
*/
var startDateVal;
var endDateVal;
var timeUnitVal;
layui.config({//配置并导入excel插件
base: '../common/layui_exts/'
}).use(['excel', 'layer'], function () {
var $ = layui.$;
var layer = layui.layer;
var excel = layui.excel;
$('#exportExcel').on('click', function () {
// 模拟从后端接口读取需要导出的数据
$.ajax({
url: '/console/statistics/openAppCount'
, data: {
startDate: startDateVal, //搜索的关键字
endDate: endDateVal,
timeUnit: timeUnitVal
}
, dataType: 'json'
, success(res) {
var data = res.data.data2;
console.log(res);
// 重点!!!如果后端给的数据顺序和映射关系不对,请执行梳理函数后导出
data = excel.filterExportData(data, [
'timeStr'
, 'count'
, 'countRate'
]);
// 重点2!!!一般都需要加一个表头,表头的键名顺序需要与最终导出的数据一致
data.unshift({
timeStr: "时间",
count: "启动次数",
countRate: '启动次数占比',
});
excel.exportExcel(data, '启动次数数据统计.xlsx', 'xlsx');
}
, error() {
layer.alert('获取数据失败,请检查是否部署在本地服务器环境下');
}
});
});
});
$(function(){
layui.use('laydate', function(){
var laydate = layui.laydate,
form = layui.form;
//日期范围
var time = laydate.render({
elem: '#globalDate'
,range: "~"
,done: function(value, date, endDate){ // choose end
//console.log("date callBack====>>>"+value); //得到日期生成的值,如:2017-08-18
var startDate = value.split("~")[0];
var endDate = value.split("~")[1];
var timeUnit = $(".global-time-unit").val()
Count.loadUserRegisterCount(startDate,endDate,timeUnit);
}
,max: 0
});
Count.loadUserRegisterCount();
layui.form.on('select(global-time-unit)', function(data){
var dateRange = $("#globalDate").val();
// if(data.value==3){//时间单位切换到小时
//}else
if(data.value==4){ //时间单位切换到分钟
$("#globalDate").val();
$("#globalDate").attr("disabled","");
$(".prompt_info").text("注:时间单位若为分钟,不能选择时间范围,只会显示当前这一天的数据");
dateRange = "";
}else{
$("#globalDate").removeAttr("disabled");
$(".prompt_info").text("");
}
Count.loadUserRegisterCount(dateRange.split("~")[0],dateRange.split("~")[1],data.value);
});
});
});
var Count = {
//加载用户注册数据
loadUserRegisterCount : function (startDate,endDate,timeUnit){
startDateVal = startDate;
endDateVal = endDate;
timeUnitVal = timeUnit;
Common.invoke({
path : request('/console/statistics/openAppCount'),
data : {
startDate:startDate,
endDate:endDate,
timeUnit:timeUnit
},
successMsg : false,
errorMsg : "加载数据失败,请稍后重试",
successCb : function(result) {
var data1 = result.data.data1;
var data2 = result.data.data2;
$('table tbody').html('');
for (var i=0;i<data2.length;i++)
{
var row = $("#template").clone();
row.find("#timeStr").text(data2[i].timeStr);
row.find("#count").text(data2[i].count);
row.find("#countRate").text(data2[i].countRate);
row.appendTo("#statisticsOpenAppCount_table");
}
//基于准备好的dom,初始化echarts实例
var userRegister = echarts.init(document.getElementById('userRegisterCount'),'shine');
// 使用刚指定的配置项和数据显示图表。
// userRegister.setOption(option);
userRegister.setOption(option = {
title: {
text: '启动次数统计图'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
data: data1.map(function (item) {
for(var time in item){ return time; }
})
},
yAxis: {
splitLine: {
show: false
}
},
toolbox: {
left: 'center',
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
dataZoom: [{
startValue: '2014-06-01'
}, {
type: 'inside'
}],
visualMap: {
top: 10,
right: 10,
pieces: [{
gt: 0,
lte: 50,
color: '#096'
}, {
gt: 50,
lte: 100,
color: '#ffde33'
}, {
gt: 100,
lte: 150,
color: '#ff9933'
}, {
gt: 150,
lte: 200,
color: '#cc0033'
}, {
gt: 200,
lte: 300,
color: '#660099'
}, {
gt: 300,
color: '#7e0023'
}],
outOfRange: {
color: '#999'
}
},
series: {
name: '启动次数',
type: 'line',
data: data1.map(function (item) {
for( var time in item){ return item[time]}
})
}
});
},
errorCb : function(result) {
}
});
},
}
JAVA代码
Controller
/**
* 数据统计:启动次数
*/
@RequestMapping(value = "/openAppCount")
@ApiOperation(value = "数据统计:启动次数", notes = "数据统计:启动次数")
@ApiImplicitParams({
@ApiImplicitParam(name = "timeUnit", value = "统计类型,1: 每个月的数据,2:每天的数据,3.每小时数据,4.每分钟的数据 (小时)", dataType = "short"),
@ApiImplicitParam(name = "clientType", value = "android/iOS", dataType = "String"),
@ApiImplicitParam(name = "startDate", value = "开始", dataType = "String"),
@ApiImplicitParam(name = "endDate", value = "结束", dataType = "String")
})
@ApiResponse(code = KConstants.ResultCode.Success, message = "成功", response = Object.class)
public JSONMessage openAppCount(@RequestParam(defaultValue = "2") short timeUnit,
@RequestParam(required = false) String clientType, @RequestParam(defaultValue = "") String startDate, @RequestParam(defaultValue = "") String endDate) {
JSONObject jsonObject = new JSONObject();
Object data1 = getStatisticsServiceImpl().openAppCount(startDate.trim(), endDate.trim(), timeUnit, clientType);
jsonObject.put("data1", data1);
List<Map<String, Double>> data2 = (List<Map<String, Double>>) getStatisticsServiceImpl().openAppCount(startDate.trim(), endDate.trim(), (short) 2, clientType);
//时间段内总的启动总次数
Double openTotal = 0.0;
for (Map<String, Double> stringDoubleMap : data2) {
for (String key:stringDoubleMap.keySet()
) {
Double aDouble = stringDoubleMap.get(key);
openTotal += aDouble;
}
}
List<StatisticsOpenAppCountVO> statisticsOpenAppCountVOS = new ArrayList<>();
NumberFormat nf = NumberFormat.getPercentInstance();
nf.setMinimumFractionDigits(2);
for (Map<String, Double> stringDoubleMap : data2) {
for (String key:stringDoubleMap.keySet()
) {
StatisticsOpenAppCountVO statisticsOpenAppCountVO = new StatisticsOpenAppCountVO();
//时间
statisticsOpenAppCountVO.setTimeStr(key);
Double count = stringDoubleMap.get(key);
//启动次数
statisticsOpenAppCountVO.setCount(count);
//启动次数占比
double k1 = count/ openTotal;
double countRate = new BigDecimal(k1).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue();
statisticsOpenAppCountVO.setCountRate(nf.format(countRate));
statisticsOpenAppCountVOS.add(statisticsOpenAppCountVO);
}
}
jsonObject.put("data2", statisticsOpenAppCountVOS);
return JSONMessage.success(null, jsonObject);
}
service
@Override
public Object openAppCount(String startDate, String endDate, short timeUnit, String clientType) {
List<Object> countData = new ArrayList<>();
long startTime = 0; //开始时间(秒)
long endTime = 0; //结束时间(秒),默认为当前时间
/**
* 如时间单位为月和天,默认开始时间为当前时间的一年前 ; 时间单位为小时,默认开始时间为当前时间的一个月前;
* 时间单位为分钟,则默认开始时间为当前这一天的0点
*/
long defStartTime = timeUnit == 4 ? DateUtil.getTodayMorning().getTime()
: timeUnit == 3 ? DateUtil.getLastMonth().getTime() : DateUtil.getMonthMorning().getTime() ;
startTime = StringUtil.isEmpty(startDate) ? defStartTime : DateUtil.toDate(startDate).getTime() ;
endTime = StringUtil.isEmpty(endDate) ? System.currentTimeMillis() : DateUtil.toDate(endDate).getTime();
BasicDBObject queryTime = new BasicDBObject("$ne", null);
if (startTime != 0 && endTime != 0) {
queryTime.append("$gt", startTime);
queryTime.append("$lt", endTime);
}
BasicDBObject query = new BasicDBObject();
query.put("operationTime", queryTime);
query.put("eventType", new BasicDBObject("$eq", OPEN_APP_COUNT));
if(EmptyUtils.isNotEmpty(clientType)) {
query.put("clientType", new BasicDBObject("$eq", clientType));
}
//获得用户集合对象
DBCollection collection = SKBeanUtils.getDatastore().getCollection(getEntityClass());
String mapStr = "function Map() { "
+ "var date = new Date(this.operationTime);"
+ "var year = date.getFullYear();"
+ "var month = (\"0\" + (date.getMonth()+1)).slice(-2);" //month 从0开始,此处要加1
+ "var day = (\"0\" + date.getDate()).slice(-2);"
+ "var hour = (\"0\" + date.getHours()).slice(-2);"
+ "var minute = (\"0\" + date.getMinutes()).slice(-2);"
+ "var dateStr = date.getFullYear()" + "+'-'+" + "(parseInt(date.getMonth())+1)" + "+'-'+" + "date.getDate();";
if (timeUnit == 1) { // counType=1: 每个月的数据
mapStr += "var key= year + '-'+ month;";
} else if (timeUnit == 2) { // counType=2:每天的数据
mapStr += "var key= year + '-'+ month + '-' + day;";
} else if (timeUnit == 3) { //counType=3 :每小时数据
mapStr += "var key= year + '-'+ month + '-' + day + ' ' + hour +' : 00';";
} else if (timeUnit == 4) { //counType=4 :每分钟的数据
mapStr += "var key= year + '-'+ month + '-' + day + ' ' + hour + ':'+ minute;";
}
mapStr += "emit(key,1);}";
String reduce = "function Reduce(key, values) {" +
"return Array.sum(values);" +
"}";
MapReduceCommand.OutputType type = MapReduceCommand.OutputType.INLINE;//
MapReduceCommand command = new MapReduceCommand(collection, mapStr, reduce, null, type, query);
MapReduceOutput mapReduceOutput = collection.mapReduce(command);
Iterable<DBObject> results = mapReduceOutput.results();
Map<String, Double> map = new HashMap<String, Double>();
for (Iterator iterator = results.iterator(); iterator.hasNext(); ) {
DBObject obj = (DBObject) iterator.next();
map.put((String) obj.get("_id"), (Double) obj.get("value"));
countData.add(JSON.toJSON(map));
map.clear();
}
return countData;
}