109 lines
3.4 KiB
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;
|
|
}
|
|
}
|