> $rows */ public function decorate(string $dataset, array $rows, int $companyId): array { if ($rows === []) { return []; } $keys = []; foreach ($rows as $row) { $keys[] = $this->key($dataset, $row); } $overrides = $this->connection()->table('supplier_payable_overrides') ->where('company_id', $companyId) ->where('dataset', $dataset) ->whereIn('record_key', $keys) ->get() ->keyBy('record_key'); foreach ($rows as $index => &$row) { $key = $keys[$index]; $row['_action_key'] = $key; $override = $overrides->get($key); if ($override) { $values = json_decode((string) $override->values, true) ?: []; $row = array_merge($row, $values); $row['_overridden'] = true; } else { $row['_overridden'] = false; } } unset($row); return $rows; } /** @return array{created:int,updated:int} */ public function save(string $dataset, string $recordKey, array $values, int $companyId, ?int $userId, string $source = 'manual'): array { $connection = $this->connection(); $existing = $connection->table('supplier_payable_overrides') ->where(['company_id' => $companyId, 'dataset' => $dataset, 'record_key' => $recordKey]) ->first(); $before = $existing ? (json_decode((string) $existing->values, true) ?: []) : null; $now = now(); $connection->transaction(function () use ($connection, $existing, $dataset, $recordKey, $values, $companyId, $userId, $now, $source, $before): void { $connection->table('supplier_payable_overrides')->updateOrInsert( ['company_id' => $companyId, 'dataset' => $dataset, 'record_key' => $recordKey], [ 'values' => json_encode($values, JSON_UNESCAPED_UNICODE), 'updated_by' => $userId, 'created_by' => $existing->created_by ?? $userId, 'created_at' => $existing->created_at ?? $now, 'updated_at' => $now, ], ); $connection->table('supplier_payable_override_events')->insert([ 'company_id' => $companyId, 'dataset' => $dataset, 'record_key' => $recordKey, 'source' => $source, 'before_values' => $before === null ? null : json_encode($before, JSON_UNESCAPED_UNICODE), 'after_values' => json_encode($values, JSON_UNESCAPED_UNICODE), 'user_id' => $userId, 'created_at' => $now, ]); }); return ['created' => $existing ? 0 : 1, 'updated' => $existing ? 1 : 0]; } public function transaction(Closure $callback): mixed { return $this->connection()->transaction($callback); } /** * 列出某个数据集已经保存的全部记录(「终版」目录的独立数据就是这些行)。 * * @return array> */ public function listRows(string $dataset, int $companyId): array { return $this->connection()->table('supplier_payable_overrides') ->where('company_id', $companyId) ->where('dataset', $dataset) ->orderBy('id') ->get() ->map(static function (object $record): array { $values = json_decode((string) $record->values, true) ?: []; $values['_action_key'] = (string) $record->record_key; return $values; }) ->all(); } private function connection(): ConnectionInterface { return DB::connection(config('database.supplier_payable_maintenance_connection')); } }