generated from liutk/owl-admin-base
157 lines
5.1 KiB
PHP
157 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Admin\Filters\LedgerFilter;
|
|
use App\Admin\Filters\StoreFilter;
|
|
use App\Http\Resources\StoreResource;
|
|
use App\Models\Ledger;
|
|
use App\Models\Store;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Arr;
|
|
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)
|
|
{
|
|
$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' => '结束日期',
|
|
],
|
|
);
|
|
|
|
$storeLedgerStats = Ledger::select(['store_id', DB::raw('SUM(sales) as sales')])
|
|
->whereBetween('date', [$request->input('start_at'), $request->input('end_at')])
|
|
->groupBy('store_id');
|
|
|
|
$stores = Store::filter($this->defaultFilterInput($request), StoreFilter::class)
|
|
->leftJoinSub($storeLedgerStats, 'store_ledger_stats', fn ($join) => $join->on('stores.id', '=', 'store_ledger_stats.store_id'))
|
|
->orderBy('sales', 'desc')
|
|
->orderBy('id', 'asc')
|
|
->get();
|
|
|
|
return $stores->map(function (Store $store) {
|
|
return [
|
|
'store' => StoreResource::make($store),
|
|
'sales' => trim_zeros($store->sales ?: 0),
|
|
];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 准备趋势数据
|
|
*/
|
|
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;
|
|
}
|
|
}
|