- 4.1: stat_project_monthly + ProjectStatService (건설/프로젝트 월간) - 4.2: stat_system_daily + SystemStatService (API/감사/FCM/파일/결재) - 4.3: stat_events, stat_snapshots + StatEventService + StatEventObserver - 4.4: StatController (summary/daily/monthly/alerts) + StatQueryService + FormRequest 3개 + routes/stats.php - 4.5: DashboardService sam_stat 우선 조회 + 원본 DB 폴백 패턴 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
93 lines
3.2 KiB
PHP
93 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Stats;
|
|
|
|
use App\Models\Stats\Monthly\StatProjectMonthly;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class ProjectStatService implements StatDomainServiceInterface
|
|
{
|
|
public function aggregateDaily(int $tenantId, Carbon $date): int
|
|
{
|
|
// 건설/프로젝트는 월간 전용 (일간 집계 없음)
|
|
return 0;
|
|
}
|
|
|
|
public function aggregateMonthly(int $tenantId, int $year, int $month): int
|
|
{
|
|
$startDate = Carbon::create($year, $month, 1)->startOfDay();
|
|
$endDate = $startDate->copy()->endOfMonth();
|
|
|
|
// 현장 현황
|
|
$activeSites = DB::connection('mysql')
|
|
->table('sites')
|
|
->where('tenant_id', $tenantId)
|
|
->where('status', 'active')
|
|
->whereNull('deleted_at')
|
|
->count();
|
|
|
|
$completedSites = DB::connection('mysql')
|
|
->table('sites')
|
|
->where('tenant_id', $tenantId)
|
|
->where('status', 'completed')
|
|
->whereNull('deleted_at')
|
|
->count();
|
|
|
|
// 계약 현황 (해당 월 신규 계약)
|
|
$contractStats = DB::connection('mysql')
|
|
->table('contracts')
|
|
->where('tenant_id', $tenantId)
|
|
->whereBetween('created_at', [$startDate, $endDate])
|
|
->whereNull('deleted_at')
|
|
->selectRaw('
|
|
COUNT(*) as new_count,
|
|
COALESCE(SUM(contract_amount), 0) as total_amount
|
|
')
|
|
->first();
|
|
|
|
// 지출예상 (해당 월)
|
|
$expenseStats = DB::connection('mysql')
|
|
->table('expected_expenses')
|
|
->where('tenant_id', $tenantId)
|
|
->whereYear('expected_payment_date', $year)
|
|
->whereMonth('expected_payment_date', $month)
|
|
->whereNull('deleted_at')
|
|
->selectRaw('
|
|
COALESCE(SUM(amount), 0) as expected_total,
|
|
COALESCE(SUM(CASE WHEN payment_status = \'paid\' THEN amount ELSE 0 END), 0) as actual_total
|
|
')
|
|
->first();
|
|
|
|
// 수익률 계산
|
|
$contractTotal = (float) ($contractStats->total_amount ?? 0);
|
|
$actualExpense = (float) ($expenseStats->actual_total ?? 0);
|
|
$grossProfit = $contractTotal - $actualExpense;
|
|
$grossProfitRate = $contractTotal > 0 ? ($grossProfit / $contractTotal) * 100 : 0;
|
|
|
|
StatProjectMonthly::updateOrCreate(
|
|
[
|
|
'tenant_id' => $tenantId,
|
|
'stat_year' => $year,
|
|
'stat_month' => $month,
|
|
],
|
|
[
|
|
'active_site_count' => $activeSites,
|
|
'completed_site_count' => $completedSites,
|
|
'new_contract_count' => $contractStats->new_count ?? 0,
|
|
'contract_total_amount' => $contractTotal,
|
|
'expected_expense_total' => $expenseStats->expected_total ?? 0,
|
|
'actual_expense_total' => $actualExpense,
|
|
'labor_cost_total' => 0,
|
|
'material_cost_total' => 0,
|
|
'gross_profit' => $grossProfit,
|
|
'gross_profit_rate' => $grossProfitRate,
|
|
'handover_report_count' => 0,
|
|
'structure_review_count' => 0,
|
|
]
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
}
|