103 lines
4.5 KiB
PHP
103 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Services;
|
|
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Database\DatabaseManager;
|
|
|
|
/** A small index into the existing raw mirror; the original JSON stays authoritative. */
|
|
class WorkOrderWaybillLookup
|
|
{
|
|
public const SOURCE = 'js_jst_work_order_info';
|
|
public const TABLE = 'work_order_waybill_lookup';
|
|
public const STATE = 'work_order_waybill_lookup_states';
|
|
|
|
private readonly ConnectionInterface $connection;
|
|
private ?bool $available = null;
|
|
|
|
public function __construct(DatabaseManager $database)
|
|
{
|
|
$this->connection = $database->connection((string) config('upstream_mirrors.connection', 'business_mirror'));
|
|
}
|
|
|
|
public function available(): bool
|
|
{
|
|
return $this->available ??= $this->connection->getSchemaBuilder()->hasTable(self::TABLE)
|
|
&& $this->connection->getSchemaBuilder()->hasTable(self::STATE);
|
|
}
|
|
|
|
public function ready(): bool
|
|
{
|
|
return $this->available() && $this->connection->table(self::STATE)->where('id', 1)->whereNotNull('completed_at')->exists();
|
|
}
|
|
|
|
/** Called inside the raw mirror's transaction, including changes to title/waybill. */
|
|
public function storeBatch(array $rows): void
|
|
{
|
|
if ($rows === [] || ! $this->available()) return;
|
|
$keys = [];
|
|
$values = [];
|
|
foreach ($rows as $row) {
|
|
$row = (array) $row;
|
|
$key = (string) $row['_mirror_key'];
|
|
$keys[] = $key;
|
|
if (($row['title'] ?? '') !== '查件') continue;
|
|
$fields = json_decode((string) ($row['businessFieldList'] ?? ''), true);
|
|
if (! is_array($fields)) continue;
|
|
$waybill = '';
|
|
// Same last-field-wins semantics as the existing settlement parser.
|
|
foreach ($fields as $field) {
|
|
if (is_array($field) && ($field['fieldId'] ?? null) === 'lIdField') {
|
|
$waybill = trim((string) ($field['value'] ?? ''));
|
|
}
|
|
}
|
|
if ($waybill !== '' && mb_strlen($waybill) <= 191) {
|
|
$values[] = ['mirror_key' => $key, 'waybill' => $waybill, 'source_id' => (int) ($row['Id'] ?? 0)];
|
|
}
|
|
}
|
|
// Remove renamed/invalid records too, so an old waybill never survives an update.
|
|
$this->connection->table(self::TABLE)->whereIn('mirror_key', $keys)->delete();
|
|
foreach (array_chunk($values, 500) as $chunk) {
|
|
$this->connection->table(self::TABLE)->insert($chunk);
|
|
}
|
|
}
|
|
|
|
public function prune(): void
|
|
{
|
|
if (! $this->available()) return;
|
|
$this->connection->table(self::TABLE)->whereNotExists(function ($query): void {
|
|
$query->selectRaw('1')->from(self::SOURCE)
|
|
->whereColumn(self::SOURCE . '._mirror_key', self::TABLE . '.mirror_key');
|
|
})->delete();
|
|
}
|
|
|
|
/** Resume a bounded keyset pass; production invokes this through the global sync lock. */
|
|
public function backfill(int $batchSize = 500, int $maxBatches = 2, int $pauseMs = 50): array
|
|
{
|
|
if (! $this->available()) throw new \RuntimeException('work_order_waybill_lookup_not_migrated');
|
|
$state = $this->connection->table(self::STATE)->where('id', 1)->first();
|
|
if ($state?->completed_at) return ['complete' => true, 'rows' => 0, 'batches' => 0];
|
|
$cursor = $state?->cursor;
|
|
$rows = $batches = 0;
|
|
$batchSize = min(max(1, $batchSize), 1000);
|
|
do {
|
|
$batch = $this->connection->table(self::SOURCE)->when($cursor, fn ($query) => $query->where('_mirror_key', '>', $cursor))
|
|
->orderBy('_mirror_key')->limit($batchSize)->get(['_mirror_key', 'Id', 'title', 'businessFieldList']);
|
|
$complete = $batch->count() < $batchSize;
|
|
$this->connection->transaction(function () use ($batch, $complete, &$cursor): void {
|
|
$this->storeBatch($batch->all());
|
|
$cursor = $batch->last()?->_mirror_key ?? $cursor;
|
|
if ($complete) $this->prune();
|
|
$this->connection->table(self::STATE)->updateOrInsert(['id' => 1], [
|
|
'cursor' => $cursor, 'completed_at' => $complete ? now() : null, 'updated_at' => now(),
|
|
]);
|
|
});
|
|
$rows += $batch->count();
|
|
$batches++;
|
|
if (! $complete && $pauseMs > 0) usleep(min($pauseMs, 1000) * 1000);
|
|
} while (! $complete && ($maxBatches === 0 || $batches < $maxBatches));
|
|
|
|
return compact('complete', 'rows', 'batches');
|
|
}
|
|
}
|