normalizeForcedMonth($forcedMonth); $connection = (string) config('upstream_mirrors.connection', 'business_mirror'); $now = now(); $maps = $this->shortNameMap(); $reader = IOFactory::createReaderForFile($path); $reader->setReadDataOnly(true); $spreadsheet = $reader->load($path); $sheet = $spreadsheet->getSheet(0); try { return DB::connection($connection)->transaction(function () use ( $connection, $sheet, $maps, $forcedMonth, $now, ): array { DB::connection($connection)->table(self::TABLE)->delete(); $batch = []; $imported = 0; $mapped = 0; $skipped = 0; foreach ($sheet->getRowIterator(2) as $row) { $rowNumber = $row->getRowIndex(); $cells = []; foreach ($row->getCellIterator('A', 'U') as $cell) { $cells[] = $cell->getFormattedValue(); } $warehouse = trim((string) ($cells[14] ?? '')); $createdRaw = trim((string) ($cells[16] ?? '')); if ($warehouse === '' && $createdRaw === '') { $skipped++; continue; } $month = $forcedMonth !== '' ? $forcedMonth : $this->monthOf($createdRaw); if ($month === '') { $skipped++; continue; } // 代发仓按仓储方匹配;其它仓库按仓库名称做唯一匹配。 $shortName = ''; if ($warehouse !== '') { $shortName = str_contains($warehouse, '代发仓') ? ($maps['provider'][$warehouse] ?? $maps['warehouse'][$warehouse] ?? '') : ($maps['warehouse'][$warehouse] ?? ''); } if ($shortName !== '') { $mapped++; } $batch[] = [ 'report_month' => $month, 'row_key' => sha1(substr($month, 0, 7) . '|' . $rowNumber), 'source_no' => $this->text($cells[0] ?? ''), 'document_no' => $this->text($cells[1] ?? ''), 'created_serial' => $this->text($cells[2] ?? ''), 'supplier' => $this->text($cells[3] ?? ''), 'item_no' => $this->text($cells[4] ?? ''), 'barcode' => $this->text($cells[5] ?? ''), 'spec_name' => $this->text($cells[6] ?? ''), 'quantity' => $this->number($cells[7] ?? ''), 'unit_price' => $this->number($cells[8] ?? ''), 'amount' => $this->number($cells[9] ?? ''), 'estimated_unit_price' => $this->number($cells[10] ?? ''), 'estimated_amount' => $this->number($cells[11] ?? ''), 'supplier_barcode' => $this->text($cells[12] ?? ''), 'remark' => $this->text($cells[13] ?? ''), 'warehouse' => $warehouse !== '' ? $warehouse : null, 'warehouse_short_name' => $shortName !== '' ? $shortName : null, 'created_at_raw' => $this->moment($createdRaw), 'express_name' => $this->text($cells[17] ?? ''), 'express_no' => $this->text($cells[18] ?? ''), 'not_sent' => $this->text($cells[19] ?? ''), 'owner_name' => $this->text($cells[20] ?? ''), 'imported_at' => $now, ]; $imported++; if (count($batch) >= 1000) { DB::connection($connection)->table(self::TABLE)->insertOrIgnore($batch); $batch = []; } } if ($batch !== []) { DB::connection($connection)->table(self::TABLE)->insertOrIgnore($batch); } if ($imported === 0) { throw new RuntimeException('入库明细文件中没有可导入的数据。'); } return [ 'imported' => $imported, 'mapped' => $mapped, 'skipped' => $skipped, 'total' => DB::connection($connection)->table(self::TABLE)->count(), ]; }); } finally { $spreadsheet->disconnectWorksheets(); } } private function normalizeForcedMonth(string $month): string { $month = trim($month); if ($month === '') { return ''; } if (preg_match('/^\d{4}-\d{2}$/', $month) === 1) { return $month . '-01'; } if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $month) === 1) { return $month; } throw new RuntimeException('指定月份格式应为 YYYY-MM。'); } /** @return array{warehouse:array,provider:array} */ private function shortNameMap(): array { $rows = InventoryWarehouseNameMapping::withoutGlobalScopes() ->get(['warehouse_provider', 'warehouse', 'warehouse_short_name']); $grouped = []; foreach ($rows as $row) { $warehouse = trim((string) $row->warehouse); if ($warehouse === '') { continue; } $grouped[$warehouse][] = trim((string) $row->warehouse_short_name); } $byWarehouse = []; foreach ($grouped as $warehouse => $names) { $unique = array_values(array_unique($names)); if (count($unique) === 1 && $unique[0] !== '') { $byWarehouse[$warehouse] = $unique[0]; } } $byProvider = []; foreach ($rows as $row) { $provider = trim((string) $row->warehouse_provider); if ($provider !== '') { $byProvider[$provider][] = trim((string) $row->warehouse_short_name); } } foreach ($byProvider as $provider => $names) { $unique = array_values(array_unique($names)); if (count($unique) === 1 && $unique[0] !== '') { $byProvider[$provider] = $unique[0]; } } return ['warehouse' => $byWarehouse, 'provider' => $byProvider]; } private function monthOf(string $value): string { $value = trim($value); if ($value === '') { return ''; } foreach (['Y/m/d H:i:s', 'Y-m-d H:i:s', 'Y/m/d', 'Y-m-d'] as $format) { $parsed = DateTimeImmutable::createFromFormat($format, $value); if ($parsed instanceof DateTimeImmutable) { return $parsed->format('Y-m') . '-01'; } } $timestamp = strtotime($value); return $timestamp === false ? '' : date('Y-m-01', $timestamp); } private function moment(string $value): ?string { $value = trim($value); if ($value === '') { return null; } $timestamp = strtotime($value); return $timestamp === false ? null : date('Y-m-d H:i:s', $timestamp); } private function text(mixed $value): ?string { $text = trim((string) $value); return $text === '' ? null : $text; } private function number(mixed $value): ?float { $text = str_replace(',', '', trim((string) $value)); if ($text === '' || ! is_numeric($text)) { return null; } return (float) $text; } }