$materialCodes * @return array{ * unique:array, * ambiguous:array>, * missing:list, * source_rows:int * } */ public function resolve(array $materialCodes, ?UpstreamReadOnlyQuery $query = null): array { $normalized = []; foreach ($materialCodes as $code) { $materialCode = self::normalizeMaterialCode((string) $code); if ($materialCode !== null) { $normalized[$materialCode] = true; } } $materialCodes = array_keys($normalized); sort($materialCodes, SORT_STRING); if ($materialCodes === []) { return [ 'unique' => [], 'ambiguous' => [], 'missing' => [], 'source_rows' => 0, ]; } $query ??= new UpstreamReadOnlyQuery(self::SOURCE_KEY, 15); /** @var array> $candidates */ $candidates = []; $sourceRows = 0; try { foreach (array_chunk($materialCodes, 200) as $chunk) { $placeholders = implode(', ', array_fill(0, count($chunk), '?')); $rows = $query->select( 'WITH material_skus AS (' . 'SELECT DISTINCT ' . "UPPER(BTRIM(updates->>'materialNo')) AS material_no, " . 'COALESCE(NULLIF(BTRIM("normalizedSku"), \'\'), NULLIF(BTRIM("skuCode"), \'\')) AS sku_code ' . 'FROM public."BarcodePrintImportRecord" ' . "WHERE UPPER(BTRIM(updates->>'materialNo')) IN ({$placeholders}) " . 'AND COALESCE(NULLIF(BTRIM("normalizedSku"), \'\'), NULLIF(BTRIM("skuCode"), \'\')) IS NOT NULL' . '), barcode_matches AS (' . 'SELECT DISTINCT material_skus.material_no, ' . "UPPER(NULLIF(BTRIM(product_rows.updates->>'barcode'), '')) AS mapped_sku, " . 'product_rows."createdAt" AS import_record_created_at ' . 'FROM material_skus ' . 'JOIN public."BarcodePrintImportRecord" product_rows ' . 'ON COALESCE(NULLIF(BTRIM(product_rows."normalizedSku"), \'\'), ' . 'NULLIF(BTRIM(product_rows."skuCode"), \'\')) = material_skus.sku_code ' . "WHERE NULLIF(BTRIM(product_rows.updates->>'barcode'), '') IS NOT NULL" . '), active_barcode_matches AS (' . 'SELECT barcode_matches.material_no, barcode_matches.mapped_sku, ' . 'archive."createdAt" AS candidate_created_at, ' . 'archive."businessCreatedAt" AS business_created_at, ' . 'archive."productStatus" AS product_status, ' . 'barcode_matches.import_record_created_at ' . 'FROM barcode_matches ' . 'JOIN public."SupplyChainProductArchive" archive ' . 'ON UPPER(BTRIM(archive.barcode)) = barcode_matches.mapped_sku ' . "WHERE archive.\"productStatus\" = '销售'" . '), base_sku_matches AS (' . 'SELECT material_skus.material_no, ' . 'UPPER(BTRIM(archive.sku::text)) AS mapped_sku, ' . 'archive."createdAt" AS candidate_created_at, ' . 'archive."businessCreatedAt" AS business_created_at, ' . 'archive."productStatus" AS product_status, ' . 'NULL::timestamp AS import_record_created_at ' . 'FROM material_skus ' . 'JOIN public."SupplyChainProductArchive" archive ' . 'ON UPPER(BTRIM(archive.sku::text)) = UPPER(BTRIM(material_skus.sku_code)) ' . 'WHERE NOT EXISTS (' . 'SELECT 1 FROM barcode_matches ' . 'WHERE barcode_matches.material_no = material_skus.material_no' . ') ' . "AND archive.\"productStatus\" = '销售'" . ') ' . 'SELECT material_no, mapped_sku, candidate_created_at, business_created_at, ' . 'product_status, import_record_created_at ' . 'FROM active_barcode_matches ' . 'UNION ' . 'SELECT material_no, mapped_sku, candidate_created_at, business_created_at, ' . 'product_status, import_record_created_at ' . 'FROM base_sku_matches', $chunk, ); foreach ($rows as $row) { $sourceRows++; $materialCode = self::normalizeMaterialCode((string) ($row['material_no'] ?? '')); $mappedSku = mb_strtoupper(trim((string) ($row['mapped_sku'] ?? ''))); $productStatus = trim((string) ($row['product_status'] ?? '')); if ($materialCode === null || $mappedSku === '' || $productStatus !== '销售') { continue; } $createdAt = trim((string) ( $row['candidate_created_at'] ?? $row['import_record_created_at'] ?? '' )); if ( ! isset($candidates[$materialCode][$mappedSku]) || $createdAt > $candidates[$materialCode][$mappedSku] ) { $candidates[$materialCode][$mappedSku] = $createdAt; } } } } catch (Throwable $exception) { throw new RuntimeException( '91 基础料号条码映射读取失败,请检查商品条码映射库只读连接:' . $exception->getMessage(), 0, $exception, ); } $unique = []; $ambiguous = []; $missing = []; foreach ($materialCodes as $materialCode) { $mappedCandidates = $candidates[$materialCode] ?? []; if ($mappedCandidates === []) { $missing[] = $materialCode; continue; } $latestCreatedAt = max(array_values($mappedCandidates)); $mappedSkus = []; foreach ($mappedCandidates as $mappedSku => $createdAt) { if ($createdAt === $latestCreatedAt) { $mappedSkus[] = (string) $mappedSku; } } sort($mappedSkus, SORT_STRING); if (count($mappedSkus) === 1) { $unique[$materialCode] = $mappedSkus[0]; } else { $ambiguous[$materialCode] = $mappedSkus; } } return [ 'unique' => $unique, 'ambiguous' => $ambiguous, 'missing' => $missing, 'source_rows' => $sourceRows, ]; } }