CaiWuXiTong/modules/EcommerceFinance/Services/BusinessDataPlacement.php

109 lines
3.4 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Facades\Schema;
use InvalidArgumentException;
class BusinessDataPlacement
{
private readonly ConnectionInterface $connection;
public function __construct(DatabaseManager $database)
{
$this->connection = $database->connection(
(string) config('upstream_mirrors.connection', 'business_mirror'),
);
}
/** @return array<string,array<string,mixed>> */
public function policies(): array
{
return (array) config('business_data.datasets', []);
}
/** @return array<string,mixed> */
public function policy(string $datasetKey): array
{
$policy = (array) ($this->policies()[$datasetKey] ?? []);
if ($policy === []) {
// Missing and runtime-registered connector datasets fail closed to
// MariaDB. `business-data:plan` still fails CI/operations for a
// missing code-owned policy, while a typo can never silently route
// production data into ClickHouse.
return [
'role' => 'external_dimension',
'target_store' => 'mariadb',
'consumer' => '外部登记数据集',
'sync' => 'always',
];
}
return $policy + ['sync' => 'always'];
}
public function targetStore(string $datasetKey): string
{
return (string) ($this->policy($datasetKey)['target_store'] ?? 'mariadb');
}
public function shouldSchedule(string $datasetKey): bool
{
$sync = (string) ($this->policy($datasetKey)['sync'] ?? 'always');
return match ($sync) {
'always' => true,
'disabled' => false,
'after_columnar_activation' => $this->isActive($datasetKey, 'clickhouse'),
default => throw new InvalidArgumentException('业务数据集同步策略无效:' . $datasetKey),
};
}
public function isActive(string $datasetKey, ?string $store = null): bool
{
$row = $this->state($datasetKey);
if ($row === null || ($row['phase'] ?? null) !== 'active') {
return false;
}
return $store === null || ($row['target_store'] ?? null) === $store;
}
/** @return array<string,mixed>|null */
public function state(string $datasetKey): ?array
{
if (! Schema::connection($this->connection->getName())->hasTable('business_data_placements')) {
return null;
}
$row = $this->connection->table('business_data_placements')
->where('dataset_key', $datasetKey)
->first();
return $row === null ? null : (array) $row;
}
/** @param array<string,mixed> $values */
public function save(string $datasetKey, array $values): void
{
$now = now();
$this->connection->table('business_data_placements')->insertOrIgnore([
'dataset_key' => $datasetKey,
'target_store' => $this->targetStore($datasetKey),
'phase' => 'planned',
'created_at' => $now,
'updated_at' => $now,
]);
$this->connection->table('business_data_placements')
->where('dataset_key', $datasetKey)
->update(['updated_at' => $now] + $values);
}
public function connection(): ConnectionInterface
{
return $this->connection;
}
}