From 61f229cb706a1daedae3c6eec7bc031034b0e018 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Wed, 24 Apr 2024 13:09:38 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Admin/Controllers/CockpitController.php | 110 +++---------- .../Finance/StoreStatisticController.php | 13 +- .../Controllers/Api/StatisticsController.php | 59 +++---- app/Services/StatisticService.php | 147 ++++++++++++------ 4 files changed, 161 insertions(+), 168 deletions(-) diff --git a/app/Admin/Controllers/CockpitController.php b/app/Admin/Controllers/CockpitController.php index 1bf5618..32e21d9 100644 --- a/app/Admin/Controllers/CockpitController.php +++ b/app/Admin/Controllers/CockpitController.php @@ -6,10 +6,10 @@ use App\Admin\Filters\StoreFilter; use App\Http\Controllers\Controller; use App\Models\Employee; use App\Models\Keyword; -use App\Models\Ledger; use App\Models\LedgerItem; use App\Models\Store; use App\Models\TaskPerformance; +use App\Services\StatisticService; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; @@ -38,7 +38,7 @@ class CockpitController extends Controller /** * 销售趋势 */ - public function salesTrend(Request $request): array + public function salesTrend(Request $request, StatisticService $statisticService): array { $request->validate( rules: [ @@ -48,15 +48,12 @@ class CockpitController extends Controller $last = $request->input('last'); - $data = collect(); - if (in_array($last, ['7days', '30days'])) { // 按天 $days = match ($last) { '7days' => 7, '30days' => 30, }; - // 今天 $today = Carbon::today(); // 开始时间 @@ -64,24 +61,7 @@ class CockpitController extends Controller // 结束时间 $endAt = $today->copy()->subDay(); - $ledgers = Ledger::select(['date', DB::raw('SUM(sales) as sales')]) - ->whereBetween('date', [$startAt->toDateString(), $endAt->toDateString()]) - ->groupBy('date') - ->get() - ->keyBy('date'); - - while ($startAt->lte($endAt)) { - $date = $startAt->toDateString(); - - $ledger = $ledgers->get($date); - - $data->push([ - 'date' => $date, - 'sales' => trim_zeros($ledger->sales ?? 0), - ]); - - $startAt->addDay(); - } + return $statisticService->dailyLedgerTrend($startAt, $endAt); } elseif (in_array($last, ['180days', '365days'])) { // 按月 @@ -89,7 +69,6 @@ class CockpitController extends Controller '180days' => 6, // 6个月 '365days' => 12, // 12个月 }; - // 今天 $today = Carbon::today(); // 开始时间 @@ -97,27 +76,10 @@ class CockpitController extends Controller // 结束时间 $endAt = $today->copy()->startOfMonth()->subMonth()->endOfMonth(); - $ledgers = Ledger::select([DB::raw("DATE_FORMAT(`date`, '%Y-%m') as month"), DB::raw('SUM(sales) as sales')]) - ->whereBetween('date', [$startAt->toDateString(), $endAt->toDateString()]) - ->groupBy('month') - ->get() - ->keyBy('month'); - - for ($i=0; $i < $months; $i++) { - $month = $startAt->format('Y-m'); - - $ledger = $ledgers->get($month); - - $data->push([ - 'month' => $month, - 'sales' => trim_zeros($ledger->sales ?? 0), - ]); - - $startAt->addMonth(); - } + return $statisticService->monthlyLedgerTrend($startAt, $endAt); } - return $data->all(); + return []; } /** @@ -243,7 +205,7 @@ class CockpitController extends Controller /** * 门店销量排名 */ - public function storeSalesRanking(Request $request): array + public function storeSalesRanking(Request $request, StatisticService $statisticService): array { $request->validate( rules: [ @@ -251,50 +213,25 @@ class CockpitController extends Controller ], ); + $today = Carbon::today(); $last = $request->input('last'); + $input = ['sort' => '-sales']; - $storeSales = Ledger::select(['store_id', DB::raw('SUM(sales) as sales')]) - ->when($last, function ($query, $last) { - $today = Carbon::today(); + if (in_array($last, ['7days', '30days'])) { + $days = match ($last) { + '7days' => 7, + '30days' => 30, + }; + $input['date_range'] = $today->copy()->subDays($days)->toDateString().','.$today->copy()->subDay()->toDateString(); + } elseif (in_array($last, ['180days', '365days'])) { + $months = match ($last) { + '180days' => 6, // 6个月 + '365days' => 12, // 12个月 + }; + $input['date_range'] = $today->copy()->startOfMonth()->subMonths($months)->toDateString().','.$today->copy()->startOfMonth()->subMonth()->endOfMonth()->toDateString(); + } - if (in_array($last, ['7days', '30days'])) { - $days = match ($last) { - '7days' => 7, - '30days' => 30, - }; - - $query->whereBetween('date', [ - $today->copy()->subDays($days)->toDateString(), - $today->copy()->subDay()->toDateString(), - ]); - } elseif (in_array($last, ['180days', '365days'])) { - $months = match ($last) { - '180days' => 6, // 6个月 - '365days' => 12, // 12个月 - }; - - $query->whereBetween('date', [ - $today->copy()->startOfMonth()->subMonths($months)->toDateString(), - $today->copy()->startOfMonth()->subMonth()->endOfMonth()->toDateString(), - ]); - } - }) - ->groupBy('store_id'); - - $stores = Store::leftJoinSub($storeSales, 'store_sales', fn ($join) => $join->on('stores.id', '=', 'store_sales.store_id')) - ->orderBy('store_sales.sales', 'DESC') - ->limit(30) - ->get(); - - return $stores->map(function (Store $store) { - return [ - 'store' => [ - 'id' => $store->id, - 'title' => $store->title, - ], - 'sales' => trim_zeros($store->sales ?: 0), - ]; - })->all(); + return $statisticService->storeRanking($input, 30); } /** @@ -380,6 +317,9 @@ class CockpitController extends Controller })->all(); } + /** + * 门店分类 + */ public function storeCategory(Request $request): array { $request->validate( diff --git a/app/Admin/Controllers/Finance/StoreStatisticController.php b/app/Admin/Controllers/Finance/StoreStatisticController.php index 306245d..fd054ee 100644 --- a/app/Admin/Controllers/Finance/StoreStatisticController.php +++ b/app/Admin/Controllers/Finance/StoreStatisticController.php @@ -4,21 +4,22 @@ namespace App\Admin\Controllers\Finance; use App\Admin\Controllers\AdminController; use App\Services\StatisticService; +use Illuminate\Support\Arr; class StoreStatisticController extends AdminController { public function index() { if ($this->actionOfGetData()) { - $input = request()->input(); + $input = Arr::except(request()->input(), ['orderBy', 'orderDir']); - $sorts = [ - [request()->input('orderBy') ?: 'sales', request()->input('orderDir') ?: 'desc'], - ['id', 'asc'], - ]; + $orderBy = request()->input('orderBy') ?: 'sales'; + $orderDir = request()->input('orderDir') ?: 'desc'; + + $input['sort'] = ($orderDir === 'desc' ? '-' : '').$orderBy; return $this->response()->success([ - 'items' => (new StatisticService())->stores($input, $sorts), + 'items' => (new StatisticService())->storeRanking($input), ]); } diff --git a/app/Http/Controllers/Api/StatisticsController.php b/app/Http/Controllers/Api/StatisticsController.php index 4539bbe..f6a8525 100644 --- a/app/Http/Controllers/Api/StatisticsController.php +++ b/app/Http/Controllers/Api/StatisticsController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api; use App\Services\StatisticService; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Carbon; class StatisticsController extends Controller @@ -18,7 +19,7 @@ class StatisticsController extends Controller attributes: ['date' => '日期'], ); - $input = $this->defaultFilterInput($request); + $input = Arr::except($this->filterInput($request), 'date'); $date = Carbon::yesterday(); if ($request->filled('date')) { @@ -29,9 +30,10 @@ class StatisticsController extends Controller $monthLedger = array_merge( ['deadline' => $date->format('Y-m-d')], $statisticService->ledger( - $date->copy()->startOfMonth(), - $date->copy(), - $input, + array_merge($input, [ + 'start_at' => $date->copy()->startOfMonth()->toDateString(), + 'end_at' => $date->copy()->toDateString(), + ]) ), ); @@ -39,14 +41,15 @@ class StatisticsController extends Controller $yesdayLedger = array_merge( ['date' => $date->format('Y-m-d')], $statisticService->ledger( - $date->copy(), - $date->copy(), - $input, + array_merge($input, [ + 'start_at' => $date->copy()->toDateString(), + 'end_at' => $date->copy()->toDateString(), + ]) ), ); // 近30天趋势数据 - $trendsOf30days = $statisticService->ledgerTrends( + $trendsOf30days = $statisticService->dailyLedgerTrend( $date->copy()->subDays(29), $date->copy(), $input, @@ -82,18 +85,20 @@ class StatisticsController extends Controller ], ); - $input = $this->defaultFilterInput($request); + $input = $this->filterInput($request); $ledger = $statisticService->ledger( - Carbon::parse($request->input('start_at')), - Carbon::parse($request->input('end_at')), - $input, + array_merge($input, [ + 'start_at' => $request->input('start_at'), + 'end_at' => $request->input('end_at'), + ]) ); $beforeLedger = $statisticService->ledger( - Carbon::parse($request->input('before_start_at')), - Carbon::parse($request->input('before_end_at')), - $input, + array_merge($input, [ + 'start_at' => $request->input('before_start_at'), + 'end_at' => $request->input('before_end_at'), + ]) ); // 销售涨幅 @@ -128,13 +133,11 @@ class StatisticsController extends Controller ); $input = array_merge( - $this->defaultFilterInput($request), + $this->filterInput($request), $request->only(['start_at', 'end_at']), ); - $sorts = [['sales', 'desc'], ['id', 'asc']]; - - return $statisticService->stores($input, $sorts); + return $statisticService->storeRanking($input); } /** @@ -156,28 +159,30 @@ class StatisticsController extends Controller return $statisticService->sales( Carbon::parse($request->input('start_at')), Carbon::parse($request->input('end_at')), - $this->defaultFilterInput($request), + $this->filterInput($request), ); } /** * 处理区域和门店过滤条件 */ - protected function defaultFilterInput(Request $request): array + protected function filterInput(Request $request): array { - $input = []; + $input = Arr::except($request->input(), ['store_id', 'province_code', 'city_code']); if ($request->filled('store_id')) { $input['store_id'] = $request->input('store_id'); - } else { + } elseif ($request->anyFilled(['province_code', 'city_code'])) { $region = []; - if ($request->filled('province_code')) { - $region['provinceCode'] = $request->input('province_code'); + $provinceCode = (string) $request->input('province_code'); + if ($provinceCode !== '') { + $region['provinceCode'] = $provinceCode; } - if ($request->filled('city_code')) { - $region['cityCode'] = $request->input('city_code'); + $cityCode = (string) $request->input('city_code'); + if ($cityCode !== '') { + $region['cityCode'] = $provinceCode; } $input['region'] = $region; diff --git a/app/Services/StatisticService.php b/app/Services/StatisticService.php index abb310c..d58095f 100644 --- a/app/Services/StatisticService.php +++ b/app/Services/StatisticService.php @@ -18,77 +18,99 @@ class StatisticService /** * 总账统计 */ - public function ledger(Carbon $start, Carbon $end, array $input = []): array + public function ledger(array $input = []): array { - $ledger = Ledger::filter($input, LedgerFilter::class) + $aggregate = Ledger::filter($input, LedgerFilter::class) ->select([DB::raw('SUM(sales) as sales'), DB::raw('SUM(expenditure) as expenditure')]) - ->whereBetween('date', [$start->format('Y-m-d'), $end->format('Y-m-d')]) ->first(); return [ - 'sales' => trim_zeros($ledger->sales ?? 0), - 'expenditure' => trim_zeros($ledger->expenditure ?? 0), + 'sales' => trim_zeros($aggregate->sales ?? 0), + 'expenditure' => trim_zeros($aggregate->expenditure ?? 0), ]; } /** - * 总账数据趋势 + * 总账数据趋势(日) */ - public function ledgerTrends(Carbon $start, Carbon $end, array $input = []): array + public function dailyLedgerTrend(Carbon $start, Carbon $end, array $input = []): array { - $ledgers = Ledger::filter($input, LedgerFilter::class) - ->whereBetween('date', [$start->format('Y-m-d'), $end->format('Y-m-d')]) - ->get(['date', 'sales', 'expenditure']) + $input = array_merge($input, [ + 'date_range' => $start->toDateString().','.$end->toDateString(), + ]); + + $aggregates = Ledger::select(['date', DB::raw('SUM(`sales`) as `sales`'), DB::raw('SUM(`expenditure`) as `expenditure`')]) + ->filter($input, LedgerFilter::class) + ->groupBy('date') + ->get() ->keyBy('date'); - $data = collect(); + $trend = collect(); while ($start->lte($end)) { - $ledger = $ledgers->get( - $date = $start->format('Y-m-d') - ); + $date = $start->toDateString(); - $data->push([ + $aggregate = $aggregates->get($date); + + $trend->push([ 'date' => $date, - 'sales' => trim_zeros($ledger->sales ?? 0), - 'expenditure' => trim_zeros($ledger->expenditure ?? 0), + 'sales' => trim_zeros($aggregate->sales ?? 0), + 'expenditure' => trim_zeros($aggregate->expenditure ?? 0), ]); $start->addDay(); } - return $data->all(); + return $trend->all(); } /** - * 门店统计 + * 总账数据趋势(月) */ - public function stores(array $input = [], array $sorts = []): array + public function monthlyLedgerTrend(Carbon $start, Carbon $end, array $input = []): array { - $storeLedgerStats = Ledger::select(['store_id', DB::raw('SUM(sales) as sales'), DB::raw('SUM(expenditure) as expenditure')]) - ->filter(Arr::only($input, ['date_range', 'start_at', 'end_at']), LedgerFilter::class) - ->groupBy('store_id'); + $input = array_merge($input, [ + 'date_range' => $start->toDateString().','.$end->toDateString(), + ]); - $stores = Store::filter(Arr::only($input, ['store_id', 'region']), StoreFilter::class) - ->leftJoinSub($storeLedgerStats, 'store_ledger_stats', fn ($join) => $join->on('stores.id', '=', 'store_ledger_stats.store_id')) - ->when($sorts, function ($query, $sorts) { - foreach ($sorts as $sort) { - $query->orderBy($sort[0], $sort[1]); + $aggregates = Ledger::select([DB::raw("DATE_FORMAT(`date`, '%Y-%m') as `month`"), DB::raw('SUM(`sales`) as `sales`'), DB::raw('SUM(`expenditure`) as `expenditure`')]) + ->filter($input, LedgerFilter::class) + ->groupBy('month') + ->get() + ->keyBy('month'); + + $diffMonths = 0; + if ($start->lte($end)) { + $datetime = $start->copy(); + + while (true) { + $diffMonths += 1; + + if ($datetime->isSameMonth($end)) { + break; } - }) - ->get(); - return $stores->map(function (Store $store, $key) { - return [ - 'ranking' => $key + 1, - 'store' => [ - 'id' => $store->id, - 'title' => $store->title, - ], - 'sales' => trim_zeros($store->sales ?: 0), - 'expenditure' => trim_zeros($store->expenditure ?: 0), - ]; - })->all(); + $datetime->addMonth(); + } + } + + $trend = collect(); + + for ($i=0; $i < $diffMonths; $i++) { + $month = $start->format('Y-m'); + + $aggregate = $aggregates->get($month); + + $trend->push([ + 'month' => $month, + 'sales' => trim_zeros($aggregate->sales ?? 0), + 'expenditure' => trim_zeros($aggregate->expenditure ?? 0), + ]); + + $start->addMonth(); + } + + return $trend->all(); } /** @@ -135,15 +157,6 @@ class StatisticService /** @var \Illuminate\Support\Collection */ $lotteryTypeStatistics = $ledgerItemStatistics->get($date, collect())->keyBy('ledger_item_type_id'); - $lotteryTypes->map(function ($lotteryType) use ($lotteryTypeStatistics) { - $lotteryTypeStatistic = $lotteryTypeStatistics->get($lotteryType->key); - return [ - 'name' => $lotteryType->name, - 'sales' => trim_zeros($lotteryTypeStatistic->sales ?? 0), - 'expenditure' => trim_zeros($lotteryTypeStatistic->expenditure ?? 0), - ]; - }); - $data->push([ 'date' => $date, 'ledger' => [ @@ -166,4 +179,38 @@ class StatisticService return $data->all(); } + + /** + * 门店排名 + */ + public function storeRanking(array $input = [], int $top = 0): array + { + $storeLedgers = Ledger::select(['store_id', DB::raw('SUM(sales) as sales'), DB::raw('SUM(expenditure) as expenditure')]) + ->filter(Arr::except($input, ['region']), LedgerFilter::class) + ->groupBy('store_id'); + + $stores = Store::filter($input, StoreFilter::class) + ->leftJoinSub($storeLedgers, 'store_ledgers', fn ($join) => $join->on('stores.id', '=', 'store_ledgers.store_id')) + ->when($input['sort'] ?? '', function ($query, $sort) { + foreach (explode(',', $sort) as $sort) { + $column = ltrim($sort, '-'); + $direction = str_starts_with($sort, '-') ? 'desc' : 'asc'; + $query->orderBy($column, $direction); + } + }, fn ($query) => $query->orderBy('sales', 'desc')) + ->when($top > 0, fn ($query) => $query->limit($top)) + ->get(); + + return $stores->map(function (Store $store, $key) { + return [ + 'ranking' => $key + 1, + 'store' => [ + 'id' => $store->id, + 'title' => $store->title, + ], + 'sales' => trim_zeros($store->sales ?: 0), + 'expenditure' => trim_zeros($store->expenditure ?: 0), + ]; + })->all(); + } }