* } */ public function forMonth(?CarbonImmutable $asOf = null): array { $forecastMonth = ($asOf ?? CarbonImmutable::now())->startOfMonth(); $sourceMonth = $forecastMonth->subMonthNoOverflow(); $cacheKey = 'internal-flow-projection:v1:' . $sourceMonth->format('Y-m'); $resolve = fn (): array => $this->build($forecastMonth, $sourceMonth); try { return Cache::remember($cacheKey, now()->addMinutes(10), $resolve); } catch (Throwable $exception) { report($exception); return $resolve(); } } /** * @return array{ * forecast_month:string, * forecast_month_label:string, * source_month:string, * source_month_label:string, * sales_by_company:array * } */ private function build(CarbonImmutable $forecastMonth, CarbonImmutable $sourceMonth): array { $stores = $this->businessInformation->receivingAccountStores(); foreach ($stores as &$store) { // 标记系统主体已知,避免 StoreReportPeriodBuilder 再次按店铺批量查询主体。 $store['system_company_name'] = $store['company_name'] ?? null; } unset($store); $rows = $this->storeReports->rows($stores, $sourceMonth->year, []); $incomeKey = sprintf('income_%02d', $sourceMonth->month); $totals = []; foreach ($rows as $row) { $entity = trim((string) ($row['entity'] ?? '')); if ($entity === '') { continue; } $totals[$entity] ??= ['has_value' => false, 'amount' => 0.0]; $value = $row[$incomeKey] ?? null; if ($value === null || $value === '') { continue; } $totals[$entity]['has_value'] = true; $totals[$entity]['amount'] += (float) $value; } return [ 'forecast_month' => $forecastMonth->format('Y-m'), 'forecast_month_label' => $forecastMonth->format('n') . '月', 'source_month' => $sourceMonth->format('Y-m'), 'source_month_label' => $sourceMonth->format('n') . '月', 'sales_by_company' => array_map( static fn (array $total): ?float => $total['has_value'] ? round((float) $total['amount'], 6) : null, $totals, ), ]; } }