CaiWuXiTong/modules/EcommerceFinance/Repositories/InstallationFeeSummaryRepository.php

151 lines
6.6 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Repositories;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use InvalidArgumentException;
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
/** 业绩明细「安装费金额」:按发货月份汇总安装结算明细。 */
class InstallationFeeSummaryRepository
{
public const MAIN_DATASET = 'jiansun.dbo.fys_Ticketing_install_view';
public const ITEM_DATASET = 'jiansun.dbo.fys_Ticketing_install_item';
private const CACHE_MINUTES = 5;
public function __construct(
private readonly GenericBusinessMirrorRepository $mirror,
private readonly BusinessMirrorDatasetRegistry $datasets,
) {
}
/** @return array{ready:bool,month:string,total:float,row_count:int} */
public function summary(string $month): array
{
[$dataset, $items] = $this->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<array{store_name:string,installation_company:string,payment_channel:string,amount:float}>}
*/
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);
}
}