store-manage/app/Http/Controllers/Api/StatisticsController.php

243 lines
8.3 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Admin\Filters\LedgerFilter;
use App\Admin\Filters\LedgerItemFilter;
use App\Models\Keyword;
use App\Models\Ledger;
use App\Models\LedgerItem;
use App\Services\StatisticService;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
class StatisticsController extends Controller
{
/**
* 首页统计
*/
public function dashboard(Request $request): array
{
$query = Ledger::filter(
$this->defaultFilterInput($request), LedgerFilter::class
);
// 昨天
$yesterday = Carbon::yesterday();
//--------------------------------------------------------------------------
// 本月总账录入
//--------------------------------------------------------------------------
$currentMonthLedger = (clone $query)
->select([DB::raw('SUM(sales) as sales'), DB::raw('SUM(expenditure) as expenditure')])
->whereBetween('date', [$yesterday->copy()->startOfMonth()->format('Y-m-d'), $yesterday->format('Y-m-d')])
->first();
//--------------------------------------------------------------------------
// 昨日总账录入
//--------------------------------------------------------------------------
$yesterdayLedger = (clone $query)->where('date', $yesterday->format('Y-m-d'))->first();
//--------------------------------------------------------------------------
// 近 30 天趋势数据
//--------------------------------------------------------------------------
$start = $yesterday->copy()->subDays(29);
$end = $yesterday->copy();
/** @var \Illuminate\Database\Eloquent\Collection */
$ledgers30days = (clone $query)
->whereBetween('date', [$start, $end])
->get(['date', 'sales', 'expenditure'])
->keyBy('date');
return [
// 本月总账录入
'current_month_ledger' => [
// 截止日期
'deadline' => $yesterday->format('Y-m-d'),
'sales' => trim_zeros($currentMonthLedger->sales ?? 0),
'expenditure' => trim_zeros($currentMonthLedger->expenditure ?? 0),
],
// 昨日累计金额
'yesterday_ledger' => [
'date' => $yesterday->format('Y-m-d'),
'sales' => trim_zeros($yesterdayLedger->sales ?? 0),
'expenditure' => trim_zeros($yesterdayLedger->expenditure ?? 0),
],
// 近30天趋势数据
'trend_data_of_30days' => $this->prepareTrendData($start->copy(), $end->copy(), $ledgers30days),
];
}
/**
* 门店统计
*/
public function stores(Request $request, StatisticService $statisticService)
{
$request->validate(
rules: [
'start_at' => ['bail', 'required', 'date_format:Y-m-d'],
'end_at' => ['bail', 'required', 'date_format:Y-m-d'],
],
attributes: [
'start_at' => '开始日期',
'end_at' => '结束日期',
],
);
$input = array_merge(
$this->defaultFilterInput($request),
$request->only(['start_at', 'end_at']),
);
$sorts = [['sales', 'desc'], ['id', 'asc']];
return $statisticService->stores($input, $sorts);
}
/**
* 销售统计
*/
public function sales(Request $request): array
{
$request->validate(
rules: [
'start_at' => ['bail', 'required', 'date_format:Y-m-d'],
'end_at' => ['bail', 'required', 'date_format:Y-m-d'],
],
attributes: [
'start_at' => '开始日期',
'end_at' => '结束日期',
],
);
// 开始日期
$startAt = Carbon::parse($request->input('start_at'));
// 结束日期
$endAt = Carbon::parse($request->input('end_at'));
$input = $this->defaultFilterInput($request);
/** @var \Illuminate\Database\Eloquent\Collection */
$lotteryTypes = Keyword::where('parent_key', 'lottery_type')->get();
/** @var \Illuminate\Support\Collection */
$ledgerStatistics = Ledger::select([
'date',
DB::raw('SUM(new_customers) as new_customers'),
DB::raw('SUM(sales) as sales'),
DB::raw('SUM(expenditure) as expenditure')
])
->filter($input, LedgerFilter::class)
->whereBetween('date', [$startAt->format('Y-m-d'), $endAt->format('Y-m-d')])
->groupBy(['date'])
->get()
->keyBy('date');
/** @var \Illuminate\Support\Collection */
$ledgerItemStatistics = LedgerItem::select([
'date',
'ledger_item_type_id',
DB::raw('SUM(sales) as sales'),
DB::raw('SUM(expenditure) as expenditure'),
])
->filter($input, LedgerItemFilter::class)
->whereIn('ledger_item_type_id', $lotteryTypes->pluck('key'))
->whereBetween('date', [$startAt->format('Y-m-d'), $endAt->format('Y-m-d')])
->groupBy(['date', 'ledger_item_type_id'])
->get()
->groupBy('date');
$data = collect();
$date = $endAt->copy();
while ($date->gte($startAt)) {
$_date = $date->format('Y-m-d');
$ledgerStatistic = $ledgerStatistics->get($_date);
/** @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' => [
'new_customers' => $ledgerStatistic->new_customers ?? 0,
'sales' => trim_zeros($ledgerStatistic->sales ?? 0),
'expenditure' => trim_zeros($ledgerStatistic->expenditure ?? 0),
],
'lottery_types' => $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),
];
}),
]);
$date->subDay();
}
return $data->all();
}
/**
* 准备趋势数据
*/
protected function prepareTrendData(Carbon $start, Carbon $end, Collection $ledgers): array
{
$data = collect();
do {
$ledger = $ledgers->get(
$date = $start->format('Y-m-d')
);
$data->push([
'date' => $date,
'sales' => trim_zeros($ledger->sales ?? 0),
'expenditure' => trim_zeros($ledger->expenditure ?? 0),
]);
$start->addDay();
} while ($start->lte($end));
return $data->all();
}
/**
* 处理区域和门店过滤条件
*/
protected function defaultFilterInput(Request $request): array
{
$input = [];
if ($request->filled('store_id')) {
$input['store_id'] = $request->input('store_id');
} else {
$region = [];
if ($request->filled('province_code')) {
$region['provinceCode'] = $request->input('province_code');
}
if ($request->filled('city_code')) {
$region['cityCode'] = $request->input('city_code');
}
$input['region'] = $region;
}
return $input;
}
}