提交代码
This commit is contained in:
parent
426d6dd943
commit
b2ed70fe28
|
|
@ -17,6 +17,10 @@ use think\facade\Db;
|
|||
use app\common\model\Shang;
|
||||
use app\common\model\ProfitExpenses;
|
||||
use app\common\model\ProfitRvenue;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
|
||||
class Statistics extends Base
|
||||
{
|
||||
|
|
@ -914,4 +918,416 @@ class Statistics extends Base
|
|||
return json(['code' => 0, 'msg' => '获取成功', 'data' => $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出盒子利润统计数据
|
||||
* @return void
|
||||
*/
|
||||
public function exportProfit()
|
||||
{
|
||||
// 获取查询参数
|
||||
$goodId = trim(input('get.goodId'));
|
||||
$title = trim(input('get.title'));
|
||||
$status = trim(input('get.status'));
|
||||
$type = trim(input('get.type'));
|
||||
$addtime = trim(input('get.addtime'));
|
||||
|
||||
// 解析时间范围
|
||||
$hasTimeRange = false;
|
||||
$startTime = 0;
|
||||
$endTime = 0;
|
||||
if (!empty($addtime)) {
|
||||
$hasTimeRange = true;
|
||||
$times = explode(' - ', $addtime);
|
||||
$startTime = strtotime($times[0]);
|
||||
$endTime = strtotime($times[1]);
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
$where = [['delete_time', '=', null]];
|
||||
if ($goodId) {
|
||||
$where[] = ['id', '=', $goodId];
|
||||
}
|
||||
if ($title) {
|
||||
$where[] = ['title', 'like', '%' . $title . '%'];
|
||||
}
|
||||
if ($status) {
|
||||
$where[] = ['status', '=', $status];
|
||||
}
|
||||
if ($type) {
|
||||
$where[] = ['type', '=', $type];
|
||||
}
|
||||
|
||||
// 获取测试用户ID
|
||||
$testUsers = User::where('istest', '>', 0)->column('id');
|
||||
$testUserIds = empty($testUsers) ? [0] : $testUsers;
|
||||
|
||||
// 获取盒子类型名称映射
|
||||
$typesList = $this->getGoodsTypes();
|
||||
$typesMap = [];
|
||||
foreach ($typesList as $item) {
|
||||
$typesMap[$item['value']] = $item['fl_name'];
|
||||
}
|
||||
|
||||
// 查询所有符合条件的盒子
|
||||
$goods = Db::name('goods')
|
||||
->where($where)
|
||||
->field(['id', 'title', 'status', 'type', 'price', 'imgurl'])
|
||||
->order('id desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 如果没有数据,返回错误提示
|
||||
if (empty($goods)) {
|
||||
return json(['code' => 1, 'msg' => '没有找到符合条件的数据']);
|
||||
}
|
||||
|
||||
// 创建一个新的工作簿
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
// 设置工作表名称
|
||||
$sheet->setTitle('盒子利润统计');
|
||||
|
||||
// 设置表头
|
||||
$headers = [
|
||||
'ID', '盒子名称', '盒子类型', '状态', '单价', '抽奖次数',
|
||||
'收入(微信+钻石)', '出货价值', '已兑换达达卷', '已申请发货',
|
||||
'利润', '利润率(%)'
|
||||
];
|
||||
|
||||
foreach ($headers as $col => $header) {
|
||||
$sheet->setCellValueByColumnAndRow($col + 1, 1, $header);
|
||||
}
|
||||
|
||||
// 设置表头样式
|
||||
$headerStyle = [
|
||||
'font' => ['bold' => true],
|
||||
'alignment' => [
|
||||
'horizontal' => Alignment::HORIZONTAL_CENTER,
|
||||
],
|
||||
'fill' => [
|
||||
'fillType' => Fill::FILL_SOLID,
|
||||
'startColor' => ['rgb' => 'E0E0E0'],
|
||||
],
|
||||
];
|
||||
$sheet->getStyle('A1:L1')->applyFromArray($headerStyle);
|
||||
|
||||
// 填充数据
|
||||
$row = 2;
|
||||
foreach ($goods as $item) {
|
||||
// 获取该盒子的统计数据
|
||||
$statsData = $this->getBoxProfitStats($item['id'], $hasTimeRange, $startTime, $endTime, $testUserIds);
|
||||
|
||||
// 获取状态文字
|
||||
$statusText = '';
|
||||
switch ($item['status']) {
|
||||
case 1: $statusText = '上架'; break;
|
||||
case 2: $statusText = '下架'; break;
|
||||
case 3: $statusText = '售罄'; break;
|
||||
default: $statusText = '自动下架'; break;
|
||||
}
|
||||
|
||||
// 填充一行数据
|
||||
$sheet->setCellValue('A' . $row, $item['id']);
|
||||
$sheet->setCellValue('B' . $row, $item['title']);
|
||||
$sheet->setCellValue('C' . $row, isset($typesMap[$item['type']]) ? $typesMap[$item['type']] : '未知类型');
|
||||
$sheet->setCellValue('D' . $row, $statusText);
|
||||
$sheet->setCellValue('E' . $row, $item['price']);
|
||||
$sheet->setCellValue('F' . $row, $statsData['cj_count']);
|
||||
$sheet->setCellValue('G' . $row, $statsData['use_money']);
|
||||
$sheet->setCellValue('H' . $row, $statsData['sc_money']);
|
||||
$sheet->setCellValue('I' . $row, $statsData['re_money']);
|
||||
$sheet->setCellValue('J' . $row, $statsData['fh_money']);
|
||||
$sheet->setCellValue('K' . $row, $statsData['profit']);
|
||||
$sheet->setCellValue('L' . $row, $statsData['profit_rate']);
|
||||
|
||||
// 设置负利润的行为红色背景
|
||||
if ($statsData['profit'] < 0) {
|
||||
$sheet->getStyle('A' . $row . ':L' . $row)->getFill()
|
||||
->setFillType(Fill::FILL_SOLID)
|
||||
->getStartColor()->setRGB('FFEBEE');
|
||||
}
|
||||
|
||||
$row++;
|
||||
}
|
||||
|
||||
// 调整列宽
|
||||
foreach (range('A', 'L') as $col) {
|
||||
$sheet->getColumnDimension($col)->setAutoSize(true);
|
||||
}
|
||||
|
||||
// 添加汇总行
|
||||
$summaryData = $this->getSummaryStatisticsData($goodId, $title, $status, $type, $addtime);
|
||||
|
||||
$row++;
|
||||
$sheet->setCellValue('A' . $row, '汇总');
|
||||
$sheet->mergeCells('A' . $row . ':F' . $row);
|
||||
$sheet->setCellValue('G' . $row, $summaryData['totalIncome']);
|
||||
$sheet->setCellValue('H' . $row, $summaryData['totalCost']);
|
||||
$sheet->setCellValue('I' . $row, $summaryData['totalReMoney']);
|
||||
$sheet->setCellValue('J' . $row, $summaryData['totalFhMoney']);
|
||||
$sheet->setCellValue('K' . $row, $summaryData['totalProfit']);
|
||||
|
||||
// 计算总体利润率
|
||||
$profitRate = 0;
|
||||
if ($summaryData['totalIncome'] > 0) {
|
||||
$profitRate = ($summaryData['totalProfit'] / $summaryData['totalIncome']) * 100;
|
||||
}
|
||||
$sheet->setCellValue('L' . $row, round($profitRate, 2));
|
||||
|
||||
// 设置汇总行样式
|
||||
$summaryStyle = [
|
||||
'font' => ['bold' => true],
|
||||
'fill' => [
|
||||
'fillType' => Fill::FILL_SOLID,
|
||||
'startColor' => ['rgb' => ($summaryData['totalProfit'] >= 0 ? 'E8F5E9' : 'FFEBEE')],
|
||||
],
|
||||
];
|
||||
$sheet->getStyle('A' . $row . ':L' . $row)->applyFromArray($summaryStyle);
|
||||
|
||||
// 设置Excel文件属性
|
||||
$spreadsheet->getProperties()
|
||||
->setCreator('盒子利润统计系统')
|
||||
->setLastModifiedBy('盒子利润统计系统')
|
||||
->setTitle('盒子利润统计数据')
|
||||
->setSubject('盒子利润统计数据')
|
||||
->setDescription('导出的盒子利润统计数据');
|
||||
|
||||
// 设置输出文件名
|
||||
$filename = '盒子利润统计_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
// 设置HTTP头,表明这是一个Excel文件
|
||||
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
header('Content-Disposition: attachment;filename="' . $filename . '"');
|
||||
header('Cache-Control: max-age=0');
|
||||
|
||||
// 将工作簿写入到PHP输出流
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save('php://output');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个盒子的利润统计数据(用于导出)
|
||||
* @param int $goodId 盒子ID
|
||||
* @param bool $hasTimeRange 是否有时间范围限制
|
||||
* @param int $startTime 开始时间
|
||||
* @param int $endTime 结束时间
|
||||
* @param array $testUserIds 测试用户ID列表
|
||||
* @return array 统计数据
|
||||
*/
|
||||
private function getBoxProfitStats($goodId, $hasTimeRange, $startTime, $endTime, $testUserIds)
|
||||
{
|
||||
// 查询1:获取充值金额和余额消费总和
|
||||
$orderQuery = Db::name('order')
|
||||
->where('status', '=', 1)
|
||||
->where(function ($query) {
|
||||
$query->where('price', '>', 0)
|
||||
->whereOr('use_money', '>', 0);
|
||||
})
|
||||
->where('goods_id', '=', $goodId)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
// 只有在有时间范围时才添加时间条件
|
||||
if ($hasTimeRange) {
|
||||
$orderQuery->where('pay_time', '>', $startTime)
|
||||
->where('pay_time', '<', $endTime);
|
||||
}
|
||||
|
||||
// 分别获取price和use_money的总和后相加
|
||||
$priceSum = $orderQuery->sum('price');
|
||||
$useMoneySum = $orderQuery->sum('use_money');
|
||||
$useMoney = floatval($priceSum) + floatval($useMoneySum);
|
||||
|
||||
// 查询2:获取出货成本
|
||||
$scMoneyQuery = Db::name('order_list')
|
||||
->where('goods_id', '=', $goodId)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$scMoneyQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$scMoney = $scMoneyQuery->sum('goodslist_money');
|
||||
|
||||
// 查询3:获取兑换成本
|
||||
$reMoneyQuery = Db::name('order_list')
|
||||
->where('goods_id', '=', $goodId)
|
||||
->where('LENGTH(recovery_num)', '>', 0)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$reMoneyQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$reMoney = $reMoneyQuery->sum('goodslist_money');
|
||||
|
||||
// 查询4:获取发货成本
|
||||
$fhMoneyQuery = Db::name('order_list')
|
||||
->where('goods_id', '=', $goodId)
|
||||
->where('LENGTH(send_num)', '>', 0)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$fhMoneyQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$fhMoney = $fhMoneyQuery->sum('goodslist_money');
|
||||
|
||||
// 查询5:获取抽奖次数
|
||||
$cjCountQuery = Db::name('order_list')
|
||||
->where('goods_id', '=', $goodId)
|
||||
->where('user_id', 'not in', $testUserIds)
|
||||
->where('parent_goods_list_id', '=', 0);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$cjCountQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$cjCount = $cjCountQuery->count();
|
||||
|
||||
// 计算利润和利润率
|
||||
$profit = floatval($useMoney) - floatval($scMoney);
|
||||
$profitRate = 0;
|
||||
if ($useMoney > 0) {
|
||||
$profitRate = round(($profit / $useMoney) * 100, 2);
|
||||
}
|
||||
|
||||
return [
|
||||
'use_money' => floatval($useMoney),
|
||||
'sc_money' => floatval($scMoney),
|
||||
're_money' => floatval($reMoney),
|
||||
'fh_money' => floatval($fhMoney),
|
||||
'cj_count' => intval($cjCount),
|
||||
'profit' => $profit,
|
||||
'profit_rate' => $profitRate
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取汇总统计数据(用于导出)
|
||||
*/
|
||||
private function getSummaryStatisticsData($goodId, $title, $status, $type, $addtime)
|
||||
{
|
||||
// 解析时间范围
|
||||
$hasTimeRange = false;
|
||||
$startTime = 0;
|
||||
$endTime = 0;
|
||||
if (!empty($addtime)) {
|
||||
$hasTimeRange = true;
|
||||
$times = explode(' - ', $addtime);
|
||||
$startTime = strtotime($times[0]);
|
||||
$endTime = strtotime($times[1]);
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
$goodsWhere = [['delete_time', '=', null]];
|
||||
if ($goodId) {
|
||||
$goodsWhere[] = ['id', '=', $goodId];
|
||||
}
|
||||
if ($title) {
|
||||
$goodsWhere[] = ['title', 'like', '%' . $title . '%'];
|
||||
}
|
||||
if ($status) {
|
||||
$goodsWhere[] = ['status', '=', $status];
|
||||
}
|
||||
if ($type) {
|
||||
$goodsWhere[] = ['type', '=', $type];
|
||||
}
|
||||
|
||||
// 获取符合条件的盒子ID列表
|
||||
$goodsIds = Db::name('goods')
|
||||
->where($goodsWhere)
|
||||
->column('id');
|
||||
|
||||
if (empty($goodsIds)) {
|
||||
return [
|
||||
'totalIncome' => 0,
|
||||
'totalCost' => 0,
|
||||
'totalProfit' => 0,
|
||||
'totalReMoney' => 0,
|
||||
'totalFhMoney' => 0
|
||||
];
|
||||
}
|
||||
|
||||
// 获取测试用户ID
|
||||
$testUsers = User::where('istest', '>', 0)->column('id');
|
||||
$testUserIds = empty($testUsers) ? [0] : $testUsers;
|
||||
|
||||
// 查询1:获取充值金额和余额消费总和
|
||||
$orderQuery = Db::name('order')
|
||||
->where('status', '=', 1)
|
||||
->where(function ($query) {
|
||||
$query->where('price', '>', 0)
|
||||
->whereOr('use_money', '>', 0);
|
||||
})
|
||||
->where('goods_id', 'in', $goodsIds)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
// 只有在有时间范围时才添加时间条件
|
||||
if ($hasTimeRange) {
|
||||
$orderQuery->where('pay_time', '>', $startTime)
|
||||
->where('pay_time', '<', $endTime);
|
||||
}
|
||||
|
||||
// 分别获取price和use_money的总和后相加
|
||||
$priceSum = $orderQuery->sum('price');
|
||||
$useMoneySum = $orderQuery->sum('use_money');
|
||||
$totalIncome = floatval($priceSum) + floatval($useMoneySum);
|
||||
|
||||
// 查询2:获取出货成本
|
||||
$scMoneyQuery = Db::name('order_list')
|
||||
->where('goods_id', 'in', $goodsIds)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$scMoneyQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$totalCost = floatval($scMoneyQuery->sum('goodslist_money'));
|
||||
|
||||
// 查询3:获取兑换成本
|
||||
$reMoneyQuery = Db::name('order_list')
|
||||
->where('goods_id', 'in', $goodsIds)
|
||||
->where('LENGTH(recovery_num)', '>', 0)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$reMoneyQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$totalReMoney = floatval($reMoneyQuery->sum('goodslist_money'));
|
||||
|
||||
// 查询4:获取发货成本
|
||||
$fhMoneyQuery = Db::name('order_list')
|
||||
->where('goods_id', 'in', $goodsIds)
|
||||
->where('LENGTH(send_num)', '>', 0)
|
||||
->where('user_id', 'not in', $testUserIds);
|
||||
|
||||
if ($hasTimeRange) {
|
||||
$fhMoneyQuery->where('addtime', '>', $startTime)
|
||||
->where('addtime', '<', $endTime);
|
||||
}
|
||||
|
||||
$totalFhMoney = floatval($fhMoneyQuery->sum('goodslist_money'));
|
||||
|
||||
// 计算总利润
|
||||
$totalProfit = $totalIncome - $totalCost;
|
||||
|
||||
// 返回汇总数据
|
||||
return [
|
||||
'totalIncome' => $totalIncome,
|
||||
'totalCost' => $totalCost,
|
||||
'totalProfit' => $totalProfit,
|
||||
'totalReMoney' => $totalReMoney,
|
||||
'totalFhMoney' => $totalFhMoney
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -428,4 +428,5 @@ Route::rule('statistics_exchangeList', 'Statistics/exchangeList', 'GET');
|
|||
Route::rule('statistics_shipmentList', 'Statistics/shipmentList', 'GET');
|
||||
Route::rule('statistics_productsOverview', 'Statistics/productsOverview', 'GET');
|
||||
Route::rule('statistics_getBoxStatistics', 'Statistics/getBoxStatistics', 'GET');
|
||||
Route::rule('statistics_getSummaryStatistics', 'Statistics/getSummaryStatistics', 'GET');
|
||||
Route::rule('statistics_getSummaryStatistics', 'Statistics/getSummaryStatistics', 'GET');
|
||||
Route::rule('statistics_exportProfit', 'Statistics/exportProfit', 'GET');
|
||||
|
|
@ -47,6 +47,11 @@
|
|||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layui-btn-normal" id="exportBtn" type="button">
|
||||
<i class="layui-icon layui-icon-export"></i> 导出Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
@ -463,6 +468,46 @@
|
|||
loadSummaryStatistics();
|
||||
});
|
||||
|
||||
// 导出按钮点击事件
|
||||
$('#exportBtn').on('click', function () {
|
||||
// 获取当前查询参数
|
||||
var params = getSearchParams();
|
||||
|
||||
// 弹出确认对话框
|
||||
layer.confirm('确定要导出当前条件下的所有盒子利润统计数据吗?', {
|
||||
btn: ['确定', '取消'],
|
||||
title: '导出确认'
|
||||
}, function(index) {
|
||||
layer.close(index);
|
||||
|
||||
// 显示加载层
|
||||
var loadIndex = layer.load(2, {shade: [0.3, '#fff']});
|
||||
|
||||
// 构建导出URL及参数
|
||||
var exportUrl = '{:url("/admin/statistics_exportProfit")}';
|
||||
var queryString = $.param(params);
|
||||
if (queryString) {
|
||||
exportUrl += '?' + queryString;
|
||||
}
|
||||
|
||||
// 创建一个隐藏的a标签用于下载
|
||||
var downloadLink = document.createElement('a');
|
||||
downloadLink.style.display = 'none';
|
||||
downloadLink.href = exportUrl;
|
||||
downloadLink.target = '_blank';
|
||||
document.body.appendChild(downloadLink);
|
||||
|
||||
// 模拟点击下载
|
||||
downloadLink.click();
|
||||
|
||||
// 移除临时元素
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(downloadLink);
|
||||
layer.close(loadIndex);
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
// 监听表格工具条事件
|
||||
table.on('tool(profitTable)', function (obj) {
|
||||
var data = obj.data;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user