Merge branch 'master' of 192.168.195.14:server/manghe

This commit is contained in:
youda 2025-05-27 06:55:42 +08:00
commit a43d9ae922
4 changed files with 424 additions and 347 deletions

View File

@ -2148,4 +2148,120 @@ class Statistics extends Base
} }
} }
/**
* 获取每日数据统计
* @return \think\response\Json
*/
public function getDailyStatistics()
{
try {
// 获取日期范围默认最近30天
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime('-30 days'));
// 1. 按天订单数据统计
$orderStats = Db::query("
SELECT
DATE(FROM_UNIXTIME(o.addtime)) as day,
COUNT(1) as order_count,
COUNT(DISTINCT o.user_id) as user_count,
IFNULL(SUM(price), 0) as price,
IFNULL(SUM(use_money), 0) as use_money,
IFNULL(SUM(use_integral), 0) as use_integral,
IFNULL(SUM(use_money2), 0) as use_money2
FROM `order` o
LEFT JOIN `user` u ON o.user_id = u.id
WHERE u.status = 1
AND u.istest = 0
AND o.status = 1
AND o.addtime >= UNIX_TIMESTAMP(?)
AND o.addtime < UNIX_TIMESTAMP(?)
GROUP BY DATE(FROM_UNIXTIME(o.addtime))
", [$startDate, $endDate]);
// 2. 登录人数统计
$loginStats = Db::query("
SELECT
login_date as day,
COUNT(1) as user_login
FROM user_login_log
WHERE login_date >= ?
AND login_date < ?
GROUP BY login_date
", [$startDate, $endDate]);
// 3. 每日新增用户统计
$registerStats = Db::query("
SELECT
DATE(FROM_UNIXTIME(addtime)) as day,
COUNT(1) as user_register
FROM `user`
WHERE status = 1
AND istest = 0
AND addtime >= UNIX_TIMESTAMP(?)
AND addtime < UNIX_TIMESTAMP(?)
GROUP BY DATE(FROM_UNIXTIME(addtime))
", [$startDate, $endDate]);
// 4. 钻石每日消费数据
$diamondStats = Db::query("
SELECT
DATE(o.created_at) as day,
IFNULL(SUM(amount_paid), 0) as amount_paid
FROM diamond_orders o
LEFT JOIN `user` u ON o.user_id = u.id
WHERE o.status = 'success'
AND u.status = 1
AND u.istest = 0
AND o.created_at >= ?
AND o.created_at < ?
GROUP BY DATE(o.created_at)
", [$startDate, $endDate]);
// 生成日期范围内的所有日期
$allDates = [];
$currentDate = strtotime($startDate);
$endTimestamp = strtotime($endDate);
while ($currentDate < $endTimestamp) {
$allDates[] = date('Y-m-d', $currentDate);
$currentDate = strtotime('+1 day', $currentDate);
}
// 将统计数据转换为以日期为键的关联数组
$orderMap = array_column($orderStats, null, 'day');
$loginMap = array_column($loginStats, null, 'day');
$registerMap = array_column($registerStats, null, 'day');
$diamondMap = array_column($diamondStats, null, 'day');
// 合并所有数据
$result = [];
foreach ($allDates as $date) {
$result[] = [
'day' => $date,
'order_count' => isset($orderMap[$date]) ? intval($orderMap[$date]['order_count']) : 0,
'user_count' => isset($orderMap[$date]) ? intval($orderMap[$date]['user_count']) : 0,
'price' => isset($orderMap[$date]) ? floatval($orderMap[$date]['price']) : 0,
'use_money' => isset($orderMap[$date]) ? floatval($orderMap[$date]['use_money']) : 0,
'use_integral' => isset($orderMap[$date]) ? floatval($orderMap[$date]['use_integral']) : 0,
'use_money2' => isset($orderMap[$date]) ? floatval($orderMap[$date]['use_money2']) : 0,
'user_login' => isset($loginMap[$date]) ? intval($loginMap[$date]['user_login']) : 0,
'user_register' => isset($registerMap[$date]) ? intval($registerMap[$date]['user_register']) : 0,
'amount_paid' => isset($diamondMap[$date]) ? floatval($diamondMap[$date]['amount_paid']) : 0
];
}
return json([
'code' => 0,
'msg' => '获取成功',
'data' => $result
]);
} catch (\Exception $e) {
return json([
'code' => 1,
'msg' => '获取数据失败:' . $e->getMessage()
]);
}
}
} }

View File

@ -336,6 +336,8 @@ Route::rule('statistics_orderList', 'Statistics/goodsList', 'GET');
Route::rule('statistics_order', 'Statistics/orderList', 'GET'); Route::rule('statistics_order', 'Statistics/orderList', 'GET');
//出货概览 //出货概览
Route::rule('statistics_productsOverview', 'Statistics/productsOverview', 'GET'); Route::rule('statistics_productsOverview', 'Statistics/productsOverview', 'GET');
//每日数据统计
Route::rule('statistics_dailyStatistics', 'Statistics/getDailyStatistics', 'GET');
//利润支出 //利润支出
Route::rule('ProfitExpenses', 'Profit/index', 'GET'); Route::rule('ProfitExpenses', 'Profit/index', 'GET');

View File

@ -8,30 +8,34 @@
</style> </style>
<body> <body>
<!-- 表格容器移到上面 -->
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<table id="dataTable" lay-filter="dataTable"></table>
</div>
</div>
</div>
<!-- 统计图表移到下面 -->
<div class="layui-fluid"> <div class="layui-fluid">
<div class="layui-card"> <div class="layui-card">
<div class="layui-row"> <div class="layui-row">
<div class="layui-col-xs6"> <div class="layui-col-xs6">
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-body" style="padding: 0px;"> <div class="layui-card-body" style="padding: 0px;">
<div class="layui-row"> <div class="layui-row">
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 100%;height:350px;"></div> <div id="main" style="width: 100%;height:350px;"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="layui-col-xs6"> <div class="layui-col-xs6">
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-body" style="padding: 0px;"> <div class="layui-card-body" style="padding: 0px;">
<div class="layui-row"> <div class="layui-row">
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="chongzhi" style="width: 100%;height:350px;"></div> <div id="chongzhi" style="width: 100%;height:350px;"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -39,62 +43,58 @@
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-body" style="padding: 0px;"> <div class="layui-card-body" style="padding: 0px;">
<div class="layui-row"> <div class="layui-row">
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="lirun" style="width: 100%;height:350px;"></div> <div id="lirun" style="width: 100%;height:350px;"></div>
</div> </div>
<div>
备注:总收入=订单支付(微信收入+钻石收入)+其他收入;<br />
备注:总支出=订单出货+其他支出;<br />
备注:利润=总收入-总支出;<br />
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="layui-col-xs6"> <div class="layui-col-xs6">
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-body" style="padding: 0px;"> <div class="layui-card-body" style="padding: 0px;">
<div class="layui-row"> <div class="layui-row">
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="qita" style="width: 100%;height:350px;"></div> <div id="qita" style="width: 100%;height:350px;"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
{include file="Public:footer"/} {include file="Public:footer"/}
<script type="text/javascript"> <script type="text/javascript">
// 通用图表初始化方法
function initChart(divId, option) {
var chart = echarts.init(document.getElementById(divId));
chart.setOption(option);
return chart;
}
// 假设这是你的 JSON 数据 // 假设这是你的 JSON 数据
var jsonData = {$list|json_encode|raw}; // var jsonData = { $list| json_encode | raw};
// 将 JSON 数据转换为以日期为键的对象,方便快速查找 // 将 JSON 数据转换为以日期为键的对象,方便快速查找
var dataMap = {}; var dataMap = {};
jsonData.forEach(function (item) { // jsonData.forEach(function (item) {
var date = item.record_date; // 提取日期 // var date = item.record_date; // 提取日期
dataMap[date] = { // dataMap[date] = {
login: item.login_count, // login: item.login_count,
register: item.register_count, // register: item.register_count,
consume: item.consume_user_count, // consume: item.consume_user_count,
consume_rmb_count: item.consume_rmb_count, // consume_rmb_count: item.consume_rmb_count,
recharge_amount: item.recharge_amount, // recharge_amount: item.recharge_amount,
balance_consume: item.balance_consume, // balance_consume: item.balance_consume,
recharge_sum: item.recharge_sum, // recharge_sum: item.recharge_sum,
shipment_money: item.shipment_money, // shipment_money: item.shipment_money,
send_money: item.send_money, // send_money: item.send_money,
recycle_money: item.recycle_money, // recycle_money: item.recycle_money,
profit_money: item.profit_money, // profit_money: item.profit_money,
all_shipment_money: item.all_shipment_money, // all_shipment_money: item.all_shipment_money,
all_recycle_money: item.all_recycle_money, // all_recycle_money: item.all_recycle_money,
all_money: item.all_money, // all_money: item.all_money,
}; // };
}); // });
// 生成从 2025-03-01 到当前日期的所有日期 // 生成从 2025-03-01 到当前日期的所有日期
function generateDateRange(startDate, endDate) { function generateDateRange(startDate, endDate) {
@ -130,44 +130,40 @@
var all_shipment_money = []; var all_shipment_money = [];
var all_recycle_money = []; var all_recycle_money = [];
var all_money = []; var all_money = [];
dateRange.forEach(function (date) { $(function () {
var data = dataMap[date] || { $.ajax({
login: 0, register: 0, consume: 0, consume_rmb_count: 0, url: '/admin/statistics_dailyStatistics',
recharge_amount: 0, type: 'GET',
balance_consume: 0, success: function (res) {
recharge_sum: 0, console.log(res);
shipment_money: 0, var recharge_amount = [];
send_money: 0, var recharge_d = [];
recycle_money: 0, res.data.forEach(function (item) {
profit_money: 0, // {
all_shipment_money: 0, // "day": "2025-04-29",
all_recycle_money: 0, // "order_count": 615,
all_money: 0 // "user_count": 1,
}; // 如果数据不存在,则默认为 0 // "price": 0,
loginData.push([date, data.login]); // "use_money": 70495,
registerData.push([date, data.register]); // "use_integral": 50,
consumeData.push([date, data.consume]); // "use_money2": 222450,
consume_rmb_count.push([date, data.consume_rmb_count]); // "user_login": 6,
recharge_amount.push([date, data.recharge_amount]); // "user_register": 0,
balance_consume.push([date, data.balance_consume]); // "amount_paid": 0
recharge_sum.push([date, data.recharge_sum]); // }
loginData.push([item.day, item.user_login]);
shipment_money.push([date, data.shipment_money]); registerData.push([item.day, item.user_register]);
send_money.push([date, data.send_money]); recharge_amount.push([item.day, item.price]);
recycle_money.push([date, data.recycle_money]); recharge_d.push([item.day, item.use_money]);
profit_money.push([date, data.profit_money]); });
console.log(loginData);
all_shipment_money.push([date, data.all_shipment_money]); console.log(registerData);
all_recycle_money.push([date, data.all_recycle_money]); initUserOption();
all_money.push([date, data.all_money]); initChongzhiOption(recharge_amount, recharge_d);
initDataTable(res.data); // 初始化表格
}
}); })
})
// 输出结果
console.log('登录人数数据:', loginData);
console.log('注册人数数据:', registerData);
console.log('充值人数数据:', consumeData);
// 生成时间序列数据 // 生成时间序列数据
function generateTimeSeriesData(startDate, count, interval) { function generateTimeSeriesData(startDate, count, interval) {
@ -183,289 +179,252 @@
return data; return data;
} }
function initUserOption() {
var option = {
title: {
text: '用户数据'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['登录', '注册']
},
grid: {
left: '3%',
right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'time', // 设置为时间轴
boundaryGap: false,
axisLabel: {
formatter: '{MM}-{dd}' // 自定义时间显示格式
}
},
yAxis: {
type: 'value'
},
dataZoom: [ // 添加 dataZoom 组件
{
type: 'slider', // 滑动条型 dataZoom
xAxisIndex: 0, // 控制 x 轴
start: 50, // 默认起始位置0%
end: 100 // 默认结束位置50%
},
{
type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放
xAxisIndex: 0
}
],
series: [
{
name: '登录',
type: 'line',
data: loginData // 使用生成的时间序列数据
},
{
name: '注册',
type: 'line',
data: registerData // 使用生成的时间序列数据
},
// {
// name: '消费人数',
// type: 'line',
// data: consumeData // 使用生成的时间序列数据
// },
// {
// name: '微信充值人数',
// type: 'line',
// stack: 'Total',
// data: consume_rmb_count // 使用生成的时间序列数据
// }
]
};
// 使用刚指定的配置项和数据显示图表
var myChart = initChart('main', option);
}
// 指定图表的配置项和数据 // 指定图表的配置项和数据
var option = {
title: { function initChongzhiOption(recharge_amount, recharge_d) {
text: '用户数据' // 指定图表的配置项和数据
}, var option1 = {
tooltip: { title: {
trigger: 'axis' text: '订单支付数据'
},
legend: {
data: ['登录', '注册']
},
grid: {
left: '3%',
right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'time', // 设置为时间轴
boundaryGap: false,
axisLabel: {
formatter: '{MM}-{dd}' // 自定义时间显示格式
}
},
yAxis: {
type: 'value'
},
dataZoom: [ // 添加 dataZoom 组件
{
type: 'slider', // 滑动条型 dataZoom
xAxisIndex: 0, // 控制 x 轴
start: 50, // 默认起始位置0%
end: 100 // 默认结束位置50%
}, },
{ tooltip: {
type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放 trigger: 'axis'
xAxisIndex: 0
}
],
series: [
{
name: '登录',
type: 'line',
data: loginData // 使用生成的时间序列数据
}, },
{ legend: {
name: '注册', data: ['RMB支付', '钻石支付']
type: 'line',
data: registerData // 使用生成的时间序列数据
}, },
// { grid: {
// name: '消费人数', left: '3%',
// type: 'line', right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
// data: consumeData // 使用生成的时间序列数据 containLabel: true
// },
// {
// name: '微信充值人数',
// type: 'line',
// stack: 'Total',
// data: consume_rmb_count // 使用生成的时间序列数据
// }
]
};
// 使用刚指定的配置项和数据显示图表
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option);
// 指定图表的配置项和数据
var option1 = {
title: {
text: '充值数据'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['总消费', '微信消费', '钻石消费']
},
grid: {
left: '3%',
right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'time', // 设置为时间轴
boundaryGap: false,
axisLabel: {
formatter: '{MM}-{dd}' // 自定义时间显示格式
}
},
yAxis: {
type: 'value'
},
dataZoom: [ // 添加 dataZoom 组件
{
type: 'slider', // 滑动条型 dataZoom
xAxisIndex: 0, // 控制 x 轴
start: 50, // 默认起始位置0%
end: 100 // 默认结束位置50%
}, },
{ toolbox: {
type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放 feature: {
xAxisIndex: 0 saveAsImage: {}
} }
],
series: [
{
name: '总消费',
type: 'line',
data: recharge_sum // 使用生成的时间序列数据
}, },
{ xAxis: {
name: '微信消费', type: 'time', // 设置为时间轴
type: 'line', boundaryGap: false,
axisLabel: {
data: recharge_amount // 使用生成的时间序列数据 formatter: '{MM}-{dd}' // 自定义时间显示格式
}
}, },
{ yAxis: {
name: '钻石消费', type: 'value'
type: 'line',
data: balance_consume // 使用生成的时间序列数据
}
]
};
// 使用刚指定的配置项和数据显示图表
var chongzhi = echarts.init(document.getElementById('chongzhi'));
chongzhi.setOption(option1);
// 指定图表的配置项和数据
var option2 = {
title: {
text: '充值数据'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['总收入', '总支出']
},
grid: {
left: '3%',
right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'time', // 设置为时间轴
boundaryGap: false,
axisLabel: {
formatter: '{MM}-{dd}' // 自定义时间显示格式
}
},
yAxis: {
type: 'value'
},
dataZoom: [ // 添加 dataZoom 组件
{
type: 'slider', // 滑动条型 dataZoom
xAxisIndex: 0, // 控制 x 轴
start: 50, // 默认起始位置0%
end: 100 // 默认结束位置50%
}, },
{ dataZoom: [ // 添加 dataZoom 组件
type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放 {
xAxisIndex: 0 type: 'slider', // 滑动条型 dataZoom
} xAxisIndex: 0, // 控制 x 轴
], start: 50, // 默认起始位置0%
series: [ end: 100 // 默认结束位置50%
// { },
// name: '利润', {
// type: 'line', type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放
xAxisIndex: 0
}
],
series: [
{
name: 'RMB支付',
type: 'line',
// data: all_money // 使用生成的时间序列数据 data: recharge_amount // 使用生成的时间序列数据
// }, },
{ {
name: '总收入', name: '钻石支付',
type: 'line', type: 'line',
data: all_recycle_money // 使用生成的时间序列数据 data: recharge_d // 使用生成的时间序列数据
}
]
};
// 使用刚指定的配置项和数据显示图表
var chongzhi = initChart('chongzhi', option1);
}
function initLirunOption() {
// 指定图表的配置项和数据
var option3 = {
title: {
text: '其他数据'
}, },
{ tooltip: {
name: '总支出', trigger: 'axis'
type: 'line',
data: all_shipment_money // 使用生成的时间序列数据
}
]
};
// 使用刚指定的配置项和数据显示图表
// var lirun = echarts.init(document.getElementById('lirun'));
// lirun.setOption(option2);
// 指定图表的配置项和数据
var option3 = {
title: {
text: '其他数据'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['出货价值','发货价值', '回收价值']
},
grid: {
left: '3%',
right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'time', // 设置为时间轴
boundaryGap: false,
axisLabel: {
formatter: '{MM}-{dd}' // 自定义时间显示格式
}
},
yAxis: {
type: 'value'
},
dataZoom: [ // 添加 dataZoom 组件
{
type: 'slider', // 滑动条型 dataZoom
xAxisIndex: 0, // 控制 x 轴
start: 50, // 默认起始位置0%
end: 100 // 默认结束位置50%
}, },
{ legend: {
type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放 data: ['出货价值', '发货价值', '回收价值']
xAxisIndex: 0
}
],
series: [
{
name: '出货价值',
type: 'line',
data: shipment_money // 使用生成的时间序列数据
}, },
{ grid: {
name: '发货价值', left: '3%',
type: 'line', right: '4%',
bottom: '15%', // 为 dataZoom 留出空间
data: send_money // 使用生成的时间序列数据 containLabel: true
}, },
{ toolbox: {
name: '回收价值', feature: {
type: 'line', saveAsImage: {}
}
},
xAxis: {
type: 'time', // 设置为时间轴
boundaryGap: false,
axisLabel: {
formatter: '{MM}-{dd}' // 自定义时间显示格式
}
},
yAxis: {
type: 'value'
},
dataZoom: [ // 添加 dataZoom 组件
{
type: 'slider', // 滑动条型 dataZoom
xAxisIndex: 0, // 控制 x 轴
start: 50, // 默认起始位置0%
end: 100 // 默认结束位置50%
},
{
type: 'inside', // 内置型 dataZoom支持鼠标滚轮缩放
xAxisIndex: 0
}
],
series: [
{
name: '出货价值',
type: 'line',
data: recycle_money // 使用生成的时间序列数据 data: shipment_money // 使用生成的时间序列数据
} },
] {
}; name: '发货价值',
// 使用刚指定的配置项和数据显示图表 type: 'line',
// var qita = echarts.init(document.getElementById('qita'));
// qita.setOption(option3); data: send_money // 使用生成的时间序列数据
},
{
name: '回收价值',
type: 'line',
data: recycle_money // 使用生成的时间序列数据
}
]
};
// 使用刚指定的配置项和数据显示图表
var qita = initChart('qita', option3);
}
// 初始化数据表格
function initDataTable(data) {
layui.use('table', function () {
var table = layui.table;
// 表格初始化
table.render({
elem: '#dataTable',
data: data,
title: '统计数据',
cols: [[
{ field: 'day', title: '日期', width: 180, sort: true, rowspan: 3 },
{ title: '用户信息', colspan: 2 },
{ title: '订单信息', colspan: 6 },
{ title: '钻石商城',colspan: 2 }
], [
{ field: 'user_register', title: '注册人数', width: 120 },
{ field: 'user_login', title: '登录人数', width: 120 },
{ field: 'order_count', title: '订单数量', width: 120 },
{ field: 'user_count', title: '用户数量', width: 120 },
{ field: 'price', title: 'RMB支付', width: 120 },
{ field: 'use_money', title: '钻石支付', width: 120 },
{ field: 'use_integral', title: 'UU币支付', width: 120 },
{ field: 'use_money2', title: '哒哒券支付', width: 120 },
{ field: 'amount_paid', title: '支付总额', width: 120 }
]],
page: true, // 开启分页
limit: 10, // 每页显示的条数
limits: [10, 20, 30, 50], // 每页条数的选择项
height: 'full-200', // 表格高度
text: {
none: '暂无相关数据'
}
});
});
}
</script> </script>
</body> </body>

View File

@ -112,10 +112,10 @@ return [
'url' => '/admin/ProfitRvenue', 'url' => '/admin/ProfitRvenue',
'name' => '其它收入', 'name' => '其它收入',
], ],
// [ [
// 'url' => '/admin/user_statistics', 'url' => '/admin/user_statistics',
// 'name' => '数据统计', 'name' => '每日数据统计',
// ], ],
], ],
], ],
[ [