datasetsForPeriod($month); if (! $this->ready($dataset, $items)) { return ['ready' => false, 'month' => $month, 'total' => 0.0, 'row_count' => 0]; } return Cache::remember( 'installation-fee-summary:v1:' . $month, now()->addMinutes(self::CACHE_MINUTES), function () use ($dataset, $items, $month): array { [$from, $to] = $this->monthRange($month); $row = $this->baseQuery($dataset->targetTable(), $items->targetTable(), $from, $to) ->selectRaw('COALESCE(SUM(CAST(i.settleItemAmount AS DECIMAL(18,2))), 0) AS total') ->selectRaw('COUNT(*) AS row_count') ->first(); return [ 'ready' => true, 'month' => $month, 'total' => round((float) ($row->total ?? 0), 2), 'row_count' => (int) ($row->row_count ?? 0), ]; }, ); } /** * @return array{ready:bool,month:string,total:float,rows:list} */ public function details(string $month): array { [$dataset, $items] = $this->datasetsForPeriod($month); if (! $this->ready($dataset, $items)) { return ['ready' => false, 'month' => $month, 'total' => 0.0, 'rows' => []]; } return Cache::remember( 'installation-fee-details:v1:' . $month, now()->addMinutes(self::CACHE_MINUTES), function () use ($dataset, $items, $month): array { [$from, $to] = $this->monthRange($month); $rows = $this->baseQuery($dataset->targetTable(), $items->targetTable(), $from, $to) ->selectRaw("COALESCE(NULLIF(TRIM(v.shop_name), ''), '未填写店铺') AS store_name") ->selectRaw("COALESCE(NULLIF(TRIM(v.installCompanyName), ''), '未填写安装公司') AS installation_company") ->selectRaw("CASE WHEN v.costWay IS NULL OR TRIM(v.costWay) = '' THEN '包安装' ELSE '不包安装' END AS payment_channel") ->selectRaw('ROUND(SUM(CAST(i.settleItemAmount AS DECIMAL(18,2))), 2) AS amount') ->groupByRaw( "COALESCE(NULLIF(TRIM(v.shop_name), ''), '未填写店铺')," . " COALESCE(NULLIF(TRIM(v.installCompanyName), ''), '未填写安装公司')," . " CASE WHEN v.costWay IS NULL OR TRIM(v.costWay) = '' THEN '包安装' ELSE '不包安装' END" ) ->havingRaw('ABS(SUM(CAST(i.settleItemAmount AS DECIMAL(18,2)))) > 0.004') ->get() ->map(static fn (object $row): array => [ 'store_name' => trim((string) ($row->store_name ?? '')), 'installation_company' => trim((string) ($row->installation_company ?? '')), 'payment_channel' => (string) ($row->payment_channel ?? '包安装'), 'amount' => round((float) ($row->amount ?? 0), 2), ]) ->sort(static fn (array $left, array $right): int => [abs($right['amount']), $left['store_name'], $left['installation_company'], $left['payment_channel']] <=> [abs($left['amount']), $right['store_name'], $right['installation_company'], $right['payment_channel']] ) ->values() ->all(); return [ 'ready' => true, 'month' => $month, 'total' => round(array_sum(array_column($rows, 'amount')), 2), 'rows' => $rows, ]; }, ); } /** @return array{0:\Modules\EcommerceFinance\Contracts\BusinessMirrorDataset,1:\Modules\EcommerceFinance\Contracts\BusinessMirrorDataset} */ private function datasetsForPeriod(string $month): array { $this->monthRange($month); return [ $this->datasets->get(self::MAIN_DATASET), $this->datasets->get(self::ITEM_DATASET), ]; } /** @return array{0:string,1:string} */ private function monthRange(string $month): array { $from = Carbon::createFromFormat('!Y-m', $month)->startOfMonth(); if ($from === false || $from->format('Y-m') !== $month) { throw new InvalidArgumentException('安装费月份格式无效。'); } return [$from->toDateTimeString(), $from->copy()->addMonth()->toDateTimeString()]; } private function ready($dataset, $items): bool { $connection = $this->mirror->connection(); $schema = Schema::connection($connection->getName()); return $this->mirror->ready($dataset) && $this->mirror->ready($items) && $schema->hasTable($dataset->targetTable()) && $schema->hasTable($items->targetTable()); } private function baseQuery(string $mainTable, string $itemTable, string $from, string $to) { $connection = $this->mirror->connection(); $latest = $connection->table($mainTable) ->selectRaw('MAX(Id) AS id') ->groupBy('SourceGuid'); return $connection->table($mainTable . ' AS v') ->joinSub($latest, 'latest_install', static fn ($join) => $join->on('latest_install.id', '=', 'v.Id')) ->join($itemTable . ' AS i', 'i.guid', '=', 'v.SourceGuid') ->whereNotNull('v.ShipTIme') ->where('v.ShipTIme', '>=', $from) ->where('v.ShipTIme', '<', $to); } }