DB 字段名 * 注意:key 用的是“原始表头字符串”(包括下划线) */ protected array $headerMap = [ // ===== 基础公共字段 ===== 'id' => 'fb_lead_id', 'created_time' => 'fb_created_time', 'ad_id' => 'ad_id', 'ad_name' => 'ad_name', 'adset_id' => 'adset_id', 'adset_name' => 'adset_name', 'campaign_id' => 'campaign_id', 'campaign_name' => 'campaign_name', 'form_id' => 'form_id', 'form_name' => 'form_name', 'is_organic' => 'is_organic', 'platform' => 'platform', 'full_name' => 'full_name', 'phone' => 'phone', 'phone_number' => 'phone', // 兼容写法 'email' => 'email', 'lead_status' => 'lead_status', 'inbox_url' => 'inbox_url', // ===== 车型相关:全部归到 car_model ===== 'which_model_of_this_car_would_you_like_to_purchase?_' => 'car_model', 'which_model_of_this_car_would_you_like_to_purchase?' => 'car_model', 'which_car_model_would_you_like_to_purchase?' => 'car_model', 'which_model_do_you_want_to_purchase?' => 'car_model', 'which_model_would_you_like_to_purchase?' => 'car_model', 'which_version_do_you_want_to_buy?' => 'car_model', // 这里也归到车型/版本字段 // ===== 数量相关:全部归到 car_quantity ===== 'how_many_cars_would_you_like_to_buy?_' => 'car_quantity', 'how_many_cars_would_you_like_to_buy?' => 'car_quantity', '_how_many_cars_would_you_like_to_buy?' => 'car_quantity', 'how_many_cars_do_you_buy?' => 'car_quantity', 'how_many_cars_do_you_want_to_purchase?' => 'car_quantity', 'how_many_cars_do_you_want_to_buy?' => 'car_quantity', 'what_quantity_do_you_want_to_buy?' => 'car_quantity', // ===== 颜色相关:归到 favorite_color ===== 'what_color_do_you_like?' => 'favorite_color', 'which_color_would_you_like_to_purchase?' => 'favorite_color', // ===== 用途相关:归到 usage_type ===== 'are_you_using_it_for_business_or_personal_use?' => 'usage_type', 'business_use_or_personal_use?' => 'usage_type', 'personal_use_or_business_use?' => 'usage_type', // ===== 座位数相关 ===== 'do_you_want_to_buy_six_seats_or_seven_seats?' => 'seat_preference', ]; /** * 返回所有支持的 Excel 表头 */ public function getSupportedHeaders(): array { return array_keys($this->headerMap); } /** * 单个表头映射:原始表头 -> DB 字段名 */ public function mapHeader(string $rawHeader): ?string { $key = $this->normalizeHeader($rawHeader); return $this->headerMap[$key] ?? null; } /** * 校验一整行表头: * - 有任何不认识的表头,就抛 BusinessException */ public function validateHeaders(array $rawHeaders): void { $unsupported = []; foreach ($rawHeaders as $header) { $header = (string)$header; $key = $this->normalizeHeader($header); // 允许空表头(多余空列) if ($key === '') { continue; } if (!isset($this->headerMap[$key])) { $unsupported[] = $header; } } if (!empty($unsupported)) { $msg = 'Excel 表头包含未兼容字段:' . implode('、', $unsupported) . '。请检查这些字段是否需要加入映射配置。'; throw new BusinessException($msg); } } /** * 根据第一行表头,构建 “列索引 => DB 字段” 映射 */ public function buildColumnFieldMap(array $rawHeaders): array { // 先做一遍校验(发现问题直接抛异常) $this->validateHeaders($rawHeaders); $colToField = []; foreach ($rawHeaders as $index => $header) { $key = $this->normalizeHeader((string)$header); if ($key === '') { continue; } $field = $this->headerMap[$key] ?? null; if ($field === null) { continue; // 理论上不会到这儿,双保险 } $colToField[$index] = $field; } return $colToField; } /** * 表头标准化: * 目前只去 BOM + trim,保持和 headerMap 中 key 完全一致 * 如果后面再出现“有空格/大小写”的版本,我们可以在这里做 strtolower / 替换空格 为 _ */ protected function normalizeHeader(string $header): string { // 去掉 UTF-8 BOM $header = preg_replace('/^\xEF\xBB\xBF/', '', $header); // 去掉前后空白 $header = trim($header); return $header; } }