feat: sam_stat P2 도메인 + 통계 API + 대시보드 전환 (Phase 4)
- 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>
This commit is contained in:
92
app/Services/Stats/ProjectStatService.php
Normal file
92
app/Services/Stats/ProjectStatService.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user