listWorksheetInfo($path); if (count($info) !== 1 || $info[0]['totalRows'] > 5001 || $info[0]['totalColumns'] > 20) { throw ValidationException::withMessages(['file' => '请使用单工作表、最多5000条配置的Excel文件。']); } $book = $reader->load($path); try { $sheet = $book->getSheet(0); $headers = []; for ($column = 1; $column <= $info[0]['totalColumns']; $column++) { $label = trim((string) $sheet->getCell([$column, 1])->getValue()); $label = str_replace(['(', ')', '%', ' '], ['(', ')', '%', ''], $label); $headers[$label] = $column; } $rateColumn = $headers['扣款比率(%)'] ?? $headers['扣款比例(%)'] ?? null; foreach (['快递代码', '快递公司', '创建时间', '创建人'] as $header) { if (!isset($headers[$header])) { throw ValidationException::withMessages(['file' => '缺少列:' . $header]); } } if ($rateColumn === null) { throw ValidationException::withMessages(['file' => '缺少列:扣款比率(%)']); } $rows = []; $codes = []; $names = []; for ($number = 2; $number <= $sheet->getHighestDataRow(); $number++) { $values = []; foreach (['courier_code' => $headers['快递代码'], 'courier_name' => $headers['快递公司'], 'source_created_at' => $headers['创建时间'], 'creator' => $headers['创建人'], 'rate' => $rateColumn] as $key => $column) { $cell = $sheet->getCell([$column, $number]); if ($cell->getDataType() === 'f') { throw ValidationException::withMessages(['file' => "第{$number}行含公式,请使用实际配置值。"]); } $values[$key] = trim((string) $cell->getFormattedValue()); if ($key === 'source_created_at' && is_numeric($cell->getValue()) && ExcelDate::isDateTime($cell)) { $values[$key] = ExcelDate::excelToDateTimeObject($cell->getValue())->format('Y-m-d H:i:s'); } } if (implode('', $values) === '') { continue; } if ($values['courier_code'] === '' || mb_strlen($values['courier_code']) > 64 || $values['courier_name'] === '' || mb_strlen($values['courier_name']) > 191 || $values['creator'] === '' || mb_strlen($values['creator']) > 191) { throw ValidationException::withMessages(['file' => "第{$number}行快递代码、名称或创建人为空或超长。"]); } if (isset($codes[mb_strtolower($values['courier_code'])]) || isset($names[mb_strtolower($values['courier_name'])])) { throw ValidationException::withMessages(['file' => "第{$number}行快递代码或名称重复。"]); } $codes[mb_strtolower($values['courier_code'])] = true; $names[mb_strtolower($values['courier_name'])] = true; $rate = AccessoryReshipRateCalculator::basisPoints($values['rate']); $date = $values['source_created_at']; if ($date !== '' && (!preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $date) || !checkdate((int) substr($date, 5, 2), (int) substr($date, 8, 2), (int) substr($date, 0, 4)) || substr($date, 11, 2) > '23' || substr($date, 14, 2) > '59' || substr($date, 17, 2) > '59')) { throw ValidationException::withMessages(['file' => "第{$number}行创建时间无效。"]); } unset($values['rate']); $values['source_created_at'] = $date === '' ? null : $date; $values['rate_basis_points'] = $rate; $rows[] = $values; } if ($rows === []) { throw ValidationException::withMessages(['file' => '配置表没有有效记录。']); } return $rows; } finally { $book->disconnectWorksheets(); } } }