Files
sam-manage/app/Services/DashboardStatService.php

298 lines
10 KiB
PHP
Raw Normal View History

<?php
namespace App\Services;
use App\Models\Stats\StatAlert;
use App\Models\Stats\StatFinanceDaily;
use App\Models\Stats\StatFinanceMonthly;
use App\Models\Stats\StatInventoryDaily;
use App\Models\Stats\StatProductionDaily;
use App\Models\Stats\StatSalesDaily;
use App\Models\Stats\StatSalesMonthly;
use App\Models\Stats\StatSystemDaily;
use Illuminate\Support\Carbon;
class DashboardStatService
{
/**
* 요약 카드 데이터 (오늘 + 어제)
*/
public function getSummaryCards(?int $tenantId): array
{
$today = Carbon::today()->toDateString();
$yesterday = Carbon::yesterday()->toDateString();
$salesToday = $this->getDailySales($tenantId, $today);
$salesYesterday = $this->getDailySales($tenantId, $yesterday);
$financeToday = $this->getDailyFinance($tenantId, $today);
$financeYesterday = $this->getDailyFinance($tenantId, $yesterday);
$productionToday = $this->getDailyProduction($tenantId, $today);
$productionYesterday = $this->getDailyProduction($tenantId, $yesterday);
$inventoryToday = $this->getDailyInventory($tenantId, $today);
$inventoryYesterday = $this->getDailyInventory($tenantId, $yesterday);
$systemToday = $this->getDailySystem($tenantId, $today);
$systemYesterday = $this->getDailySystem($tenantId, $yesterday);
return [
'orders' => [
'count' => $salesToday['order_count'],
'amount' => $salesToday['order_amount'],
'delta_count' => $salesToday['order_count'] - $salesYesterday['order_count'],
'delta_amount' => $salesToday['order_amount'] - $salesYesterday['order_amount'],
],
'sales' => [
'amount' => $salesToday['sales_amount'],
'delta' => $salesToday['sales_amount'] - $salesYesterday['sales_amount'],
],
'cashflow' => [
'net' => $financeToday['net_cashflow'],
'delta' => $financeToday['net_cashflow'] - $financeYesterday['net_cashflow'],
],
'production' => [
'efficiency' => $productionToday['efficiency_rate'],
'defect_rate' => $productionToday['defect_rate'],
'delta_efficiency' => $productionToday['efficiency_rate'] - $productionYesterday['efficiency_rate'],
],
'inventory' => [
'below_safety' => $inventoryToday['below_safety_count'],
'delta' => $inventoryToday['below_safety_count'] - $inventoryYesterday['below_safety_count'],
],
'system' => [
'active_users' => $systemToday['active_user_count'],
'delta' => $systemToday['active_user_count'] - $systemYesterday['active_user_count'],
],
];
}
/**
* 매출 추이 (7)
*/
public function getSalesTrend(?int $tenantId, int $days = 7): array
{
$from = Carbon::today()->subDays($days - 1)->toDateString();
$to = Carbon::today()->toDateString();
$rows = StatSalesDaily::forTenant($tenantId)
->forDateRange($from, $to)
->selectRaw('stat_date, SUM(order_amount) as order_amount, SUM(sales_amount) as sales_amount')
->groupBy('stat_date')
->orderBy('stat_date')
->get();
return $this->fillDates($this->generateDateRange($from, $to), $rows, ['order_amount', 'sales_amount']);
}
/**
* 자금 흐름 추이 (7)
*/
public function getFinanceTrend(?int $tenantId, int $days = 7): array
{
$from = Carbon::today()->subDays($days - 1)->toDateString();
$to = Carbon::today()->toDateString();
$rows = StatFinanceDaily::forTenant($tenantId)
->forDateRange($from, $to)
->selectRaw('stat_date, SUM(deposit_amount) as deposit_amount, SUM(withdrawal_amount) as withdrawal_amount')
->groupBy('stat_date')
->orderBy('stat_date')
->get();
return $this->fillDates($this->generateDateRange($from, $to), $rows, ['deposit_amount', 'withdrawal_amount']);
}
/**
* 생산 현황 추이 (7)
*/
public function getProductionTrend(?int $tenantId, int $days = 7): array
{
$from = Carbon::today()->subDays($days - 1)->toDateString();
$to = Carbon::today()->toDateString();
$rows = StatProductionDaily::forTenant($tenantId)
->forDateRange($from, $to)
->selectRaw('stat_date, SUM(production_qty) as production_qty, AVG(defect_rate) as defect_rate')
->groupBy('stat_date')
->orderBy('stat_date')
->get();
return $this->fillDates($this->generateDateRange($from, $to), $rows, ['production_qty', 'defect_rate']);
}
/**
* 시스템 활동 추이 (7)
*/
public function getSystemTrend(?int $tenantId, int $days = 7): array
{
$from = Carbon::today()->subDays($days - 1)->toDateString();
$to = Carbon::today()->toDateString();
$rows = StatSystemDaily::forTenant($tenantId)
->forDateRange($from, $to)
->selectRaw('stat_date, SUM(active_user_count) as active_user_count, SUM(api_request_count) as api_request_count')
->groupBy('stat_date')
->orderBy('stat_date')
->get();
return $this->fillDates($this->generateDateRange($from, $to), $rows, ['active_user_count', 'api_request_count']);
}
/**
* 최근 알림 5
*/
public function getRecentAlerts(?int $tenantId, int $limit = 5): \Illuminate\Database\Eloquent\Collection
{
$query = StatAlert::query()
->where('is_resolved', false)
->orderByDesc('created_at')
->limit($limit);
if ($tenantId) {
$query->where('tenant_id', $tenantId);
}
return $query->get();
}
/**
* 이번 도메인별 요약
*/
public function getMonthlySummary(?int $tenantId): array
{
$year = (int) Carbon::now()->format('Y');
$month = (int) Carbon::now()->format('n');
$monthLabel = Carbon::now()->format('Y-m');
$sales = StatSalesMonthly::forTenant($tenantId)
->forMonth($year, $month)
->selectRaw('SUM(order_count) as order_count, SUM(order_amount) as order_amount, SUM(sales_amount) as sales_amount')
->first();
$finance = StatFinanceMonthly::forTenant($tenantId)
->forMonth($year, $month)
->selectRaw('SUM(deposit_total) as deposit_total, SUM(withdrawal_total) as withdrawal_total, SUM(net_cashflow) as net_cashflow')
->first();
return [
'month' => $monthLabel,
'sales' => [
'order_count' => (int) ($sales->order_count ?? 0),
'order_amount' => (float) ($sales->order_amount ?? 0),
'sales_amount' => (float) ($sales->sales_amount ?? 0),
],
'finance' => [
'deposit_total' => (float) ($finance->deposit_total ?? 0),
'withdrawal_total' => (float) ($finance->withdrawal_total ?? 0),
'net_cashflow' => (float) ($finance->net_cashflow ?? 0),
],
];
}
// --- Private Helpers ---
private function getDailySales(?int $tenantId, string $date): array
{
$row = StatSalesDaily::forTenant($tenantId)
->forDate($date)
->selectRaw('SUM(order_count) as order_count, SUM(order_amount) as order_amount, SUM(sales_amount) as sales_amount')
->first();
return [
'order_count' => (int) ($row->order_count ?? 0),
'order_amount' => (float) ($row->order_amount ?? 0),
'sales_amount' => (float) ($row->sales_amount ?? 0),
];
}
private function getDailyFinance(?int $tenantId, string $date): array
{
$row = StatFinanceDaily::forTenant($tenantId)
->forDate($date)
->selectRaw('SUM(net_cashflow) as net_cashflow, SUM(deposit_amount) as deposit_amount, SUM(withdrawal_amount) as withdrawal_amount')
->first();
return [
'net_cashflow' => (float) ($row->net_cashflow ?? 0),
'deposit_amount' => (float) ($row->deposit_amount ?? 0),
'withdrawal_amount' => (float) ($row->withdrawal_amount ?? 0),
];
}
private function getDailyProduction(?int $tenantId, string $date): array
{
$row = StatProductionDaily::forTenant($tenantId)
->forDate($date)
->selectRaw('AVG(efficiency_rate) as efficiency_rate, AVG(defect_rate) as defect_rate')
->first();
return [
'efficiency_rate' => (float) ($row->efficiency_rate ?? 0),
'defect_rate' => (float) ($row->defect_rate ?? 0),
];
}
private function getDailyInventory(?int $tenantId, string $date): array
{
$row = StatInventoryDaily::forTenant($tenantId)
->forDate($date)
->selectRaw('SUM(below_safety_count) as below_safety_count')
->first();
return [
'below_safety_count' => (int) ($row->below_safety_count ?? 0),
];
}
private function getDailySystem(?int $tenantId, string $date): array
{
$row = StatSystemDaily::forTenant($tenantId)
->forDate($date)
->selectRaw('SUM(active_user_count) as active_user_count')
->first();
return [
'active_user_count' => (int) ($row->active_user_count ?? 0),
];
}
private function generateDateRange(string $from, string $to): array
{
$dates = [];
$current = Carbon::parse($from);
$end = Carbon::parse($to);
while ($current->lte($end)) {
$dates[] = $current->toDateString();
$current->addDay();
}
return $dates;
}
private function fillDates(array $dates, $rows, array $fields): array
{
$indexed = $rows->keyBy(fn ($row) => Carbon::parse($row->stat_date)->toDateString());
$result = [
'labels' => [],
'datasets' => [],
];
foreach ($fields as $field) {
$result['datasets'][$field] = [];
}
foreach ($dates as $date) {
$result['labels'][] = Carbon::parse($date)->format('m/d');
foreach ($fields as $field) {
$result['datasets'][$field][] = (float) ($indexed[$date]?->$field ?? 0);
}
}
return $result;
}
}