diff --git a/app/admin/controller/Statistics.php b/app/admin/controller/Statistics.php index 98d86ee..9f67e36 100755 --- a/app/admin/controller/Statistics.php +++ b/app/admin/controller/Statistics.php @@ -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 + ]; + } + } diff --git a/app/admin/route/app.php b/app/admin/route/app.php index 4c5375a..074f438 100755 --- a/app/admin/route/app.php +++ b/app/admin/route/app.php @@ -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'); \ No newline at end of file +Route::rule('statistics_getSummaryStatistics', 'Statistics/getSummaryStatistics', 'GET'); +Route::rule('statistics_exportProfit', 'Statistics/exportProfit', 'GET'); \ No newline at end of file diff --git a/app/admin/view/Statistics/profit.html b/app/admin/view/Statistics/profit.html index ceb22a3..407b9f8 100755 --- a/app/admin/view/Statistics/profit.html +++ b/app/admin/view/Statistics/profit.html @@ -47,6 +47,11 @@ +
+ +
@@ -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;