CaiWuXiTong/app/Business/FundForecastPage.php

161 lines
6.3 KiB
PHP

<?php
namespace App\Business;
use App\Models\Business\FundForecast;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Schema;
class FundForecastPage
{
public const GROUPS = [
['key' => 'balance', 'label' => '余额', 'fields' => FundForecast::BALANCE_FIELDS],
['key' => 'income', 'label' => '收入', 'fields' => FundForecast::INCOME_FIELDS],
['key' => 'expense', 'label' => '支出', 'fields' => FundForecast::EXPENSE_FIELDS],
['key' => 'summary', 'label' => '汇总', 'fields' => FundForecast::SUMMARY_FIELDS],
];
public function data(Request $request): array
{
$filters = $this->filters($request);
if (! Schema::hasColumns('business_fund_forecast', ['company_name', 'balance_bank', 'gap_surplus'])) {
return $this->pendingData($request, $filters);
}
$base = FundForecast::query();
$this->applyFilters($base, $filters);
$scopeCounts = [
'all' => (clone $base)->count(),
'gap' => $this->applyScope(clone $base, 'gap')->count(),
'unreported' => $this->applyScope(clone $base, 'unreported')->count(),
'pending_review' => $this->applyScope(clone $base, 'pending_review')->count(),
];
$query = $this->applyScope(clone $base, $filters['view']);
$summaryQuery = clone $query;
$rows = $query
->orderByDesc('budget_month')
->orderBy('company_name')
->orderByDesc('id')
->paginate(50)
->withQueryString();
$labels = FundForecast::columnLabels();
$groups = array_map(function (array $group) use ($labels): array {
$group['columns'] = array_map(
fn (string $field): array => ['field' => $field, 'label' => $labels[$field]],
$group['fields'],
);
return $group;
}, self::GROUPS);
return [
'ready' => true,
'filters' => $filters,
'months' => FundForecast::query()->whereNotNull('budget_month')->where('budget_month', '<>', '')
->distinct()->orderByDesc('budget_month')->pluck('budget_month')->all(),
'regions' => FundForecast::query()->whereNotNull('region')->where('region', '<>', '')
->distinct()->orderBy('region')->pluck('region')->all(),
'rows' => $rows,
'groups' => $groups,
'scopeCounts' => $scopeCounts,
'summary' => [
'company_count' => (clone $summaryQuery)->whereNotNull('company_name')->where('company_name', '<>', '')
->distinct()->count('company_name'),
'balance_total' => $this->sumColumns(clone $summaryQuery, FundForecast::BALANCE_FIELDS),
'income_total' => (float) (clone $summaryQuery)->sum('forecast_month_income'),
'gap_surplus' => (float) (clone $summaryQuery)->sum('gap_surplus'),
],
];
}
private function pendingData(Request $request, array $filters): array
{
$labels = FundForecast::columnLabels();
$groups = array_map(function (array $group) use ($labels): array {
$group['columns'] = array_map(
fn (string $field): array => ['field' => $field, 'label' => $labels[$field]],
$group['fields'],
);
return $group;
}, self::GROUPS);
return [
'ready' => false,
'filters' => $filters,
'months' => [],
'regions' => [],
'rows' => new LengthAwarePaginator([], 0, 50, max((int) $request->query('page', 1), 1), [
'path' => $request->url(),
'query' => $request->query(),
]),
'groups' => $groups,
'scopeCounts' => ['all' => 0, 'gap' => 0, 'unreported' => 0, 'pending_review' => 0],
'summary' => ['company_count' => 0, 'balance_total' => 0.0, 'income_total' => 0.0, 'gap_surplus' => 0.0],
];
}
private function filters(Request $request): array
{
$month = trim((string) $request->query('budget_month', ''));
$month = preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $month) === 1 ? $month : '';
$status = trim((string) $request->query('report_status', ''));
$status = in_array($status, ['待填报', '待审核', '已审核'], true) ? $status : '';
$view = trim((string) $request->query('view', 'all'));
$view = in_array($view, ['all', 'gap', 'unreported', 'pending_review'], true) ? $view : 'all';
return [
'budget_month' => $month,
'region' => mb_substr(trim((string) $request->query('region', '')), 0, 64),
'report_status' => $status,
'company_name' => mb_substr(trim((string) $request->query('company_name', '')), 0, 191),
'view' => $view,
];
}
private function applyFilters(Builder $query, array $filters): void
{
if ($filters['budget_month'] !== '') {
$query->where('budget_month', $filters['budget_month']);
}
if ($filters['region'] !== '') {
$query->where('region', $filters['region']);
}
if ($filters['report_status'] !== '') {
$query->where('report_status', $filters['report_status']);
}
if ($filters['company_name'] !== '') {
$query->where('company_name', 'like', '%' . addcslashes($filters['company_name'], '%_\\') . '%');
}
}
private function applyScope(Builder $query, string $scope): Builder
{
return match ($scope) {
'gap' => $query->where(function (Builder $nested): void {
$nested->where('has_gap', true)->orWhere('gap_surplus', '<', 0);
}),
'unreported' => $query->where(function (Builder $nested): void {
$nested->whereNull('report_status')->orWhere('report_status', '待填报');
}),
'pending_review' => $query->where('report_status', '待审核'),
default => $query,
};
}
private function sumColumns(Builder $query, array $columns): float
{
return array_reduce(
$columns,
fn (float $sum, string $column): float => $sum + (float) (clone $query)->sum($column),
0.0,
);
}
}