where('sync_status', 0) ->where('channel', $channelId) ->get(); if ($leads->isEmpty()) { return 0; } $now = time(); // 转数组,后面循环更快 $leadArrList = []; foreach ($leads as $lead) { $leadArrList[] = (array)$lead; } // ① 收集本批手机号(去重) $phoneList = $this->collectPhonesFromLeads($leadArrList); // ② 批量查出 inquiry_log 已存在手机号集合(索引加持) $existingTelSet = $this->getExistingTelSet($phoneList); $insert = []; $idsToMarkProcessed = []; // 插入成功 or 重复跳过,统一标记已处理 $insertCount = 0; Db::transaction(function () use ( $leadArrList, $existingTelSet, $enterUserId, $channelId, $now, &$insert, &$idsToMarkProcessed, &$insertCount ) { foreach ($leadArrList as $lead) { $leadId = (int)($lead['id'] ?? 0); $tel = trim((string)($lead['phone'] ?? '')); // 如果手机号存在于 inquiry_log,跳过同步(但要标记 fb_leads 已处理) if ($tel !== '' && isset($existingTelSet[$tel])) { if ($leadId > 0) { $idsToMarkProcessed[] = $leadId; } continue; } $insert[] = $this->mapLeadToInquiry($lead, $now, $enterUserId, $channelId); if ($leadId > 0) { $idsToMarkProcessed[] = $leadId; } } if (!empty($insert)) { Db::table('inquiry_log')->insert($insert); $insertCount = count($insert); } if (!empty($idsToMarkProcessed)) { Db::table('fb_leads') ->whereIn('id', $idsToMarkProcessed) ->update([ 'sync_status' => 1, 'updated_at' => $now, ]); } }); return $insertCount; } /** * 从本批 leads 中收集手机号(tel),去重后返回 */ protected function collectPhonesFromLeads(array $leadArrList): array { $phones = []; foreach ($leadArrList as $lead) { $tel = trim((string)($lead['phone'] ?? '')); if ($tel !== '') { // 先用原值精确匹配(你后续要做归一化再改这里即可) $phones[$tel] = true; } } return array_keys($phones); } /** * 批量查出 inquiry_log 已存在手机号集合(返回 set:tel => true) * - 自动分块 whereIn,避免 SQL 过长 * - 建议给 inquiry_log.tel 加索引 idx_tel */ protected function getExistingTelSet(array $phoneList, int $chunkSize = 800): array { $phoneList = array_values(array_unique(array_filter(array_map('trim', $phoneList)))); if (empty($phoneList)) { return []; } $existingTelSet = []; foreach (array_chunk($phoneList, $chunkSize) as $chunk) { $rows = Db::table('inquiry_log') ->select('tel') ->where('tel', '<>', '') ->whereIn('tel', $chunk) ->get(); foreach ($rows as $r) { $t = trim((string)($r->tel ?? '')); if ($t !== '') { $existingTelSet[$t] = true; } } } return $existingTelSet; } /** * 映射一条 fb_leads -> inquiry_log */ protected function mapLeadToInquiry(array $lead, int $now, int $enterUserId, int $channelId): array { $email = (string)($lead['email'] ?? ''); $tel = (string)($lead['phone'] ?? ''); $wa = $tel; $countryId = PhoneCountryHelper::detectCountryId($tel ?: $wa); return [ 'order_no' => $this->orderNum(), 'enter_user_id' => $enterUserId, 'recipient' => 0, 'quality' => 2, 'channel' => $channelId, // 👈 inquiry_log 里同样用这个渠道ID 'email' => $email, 'tel' => $tel, 'whatsapp' => $wa, 'country' => $countryId ?: 0, 'contacts' => (string)($lead['full_name'] ?? ''), 'content' => $this->buildContentHtml($lead), 'status' => 0, 'r_time' => 0, 't_time' => 0, 'sync_crm' => 0, 'created_at' => $now, 'updated_at' => $now, ]; } protected function orderNum(): string { return 'SN' . date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } /** * 富文本内容拼装 —— 基于你之前的版本 */ protected function buildContentHtml(array $lead): string { $e = function ($v) { return htmlspecialchars((string)$v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }; $lines = []; if (!empty($lead['car_model'])) { $lines[] = '
意向车型:' . $e($lead['car_model']) . '
'; } if (!empty($lead['car_quantity']) || !empty($lead['car_quantity_text'])) { $qty = $lead['car_quantity'] ?? $lead['car_quantity_text'] ?? ''; $lines[] = '意向数量:' . $e($qty) . '
'; } if (!empty($lead['favorite_color'])) { $lines[] = '意向颜色:' . $e($lead['favorite_color']) . '
'; } if (!empty($lead['usage_type'])) { $lines[] = '用途:' . $e($lead['usage_type']) . '
'; } if (!empty($lead['seat_preference'])) { $lines[] = '座位数偏好:' . $e($lead['seat_preference']) . '
'; } if (!empty($lead['drive_type'])) { $lines[] = '驱动形式偏好:' . $e($lead['drive_type']) . '
'; } if (!empty($lead['extra_data'])) { $extra = json_decode((string)$lead['extra_data'], true); if (is_array($extra) && $extra) { $lines[] = '补充问答信息:
'; foreach ($extra as $k => $v) { $lines[] = '' . $e($k) . ':' . $e($v) . '
'; } } } $lines[] = '广告与表单信息:
'; $lines[] = '平台:' . $e($lead['platform'] ?? '') . '
'; $lines[] = 'Campaign:' . $e($lead['campaign_name'] ?? '') . ' (#' . $e($lead['campaign_id'] ?? '') . ')
'; $lines[] = 'Adset:' . $e($lead['adset_name'] ?? '') . ' (#' . $e($lead['adset_id'] ?? '') . ')
'; $lines[] = 'Ad:' . $e($lead['ad_name'] ?? '') . ' (#' . $e($lead['ad_id'] ?? '') . ')
'; $lines[] = 'Form:' . $e($lead['form_name'] ?? '') . ' (#' . $e($lead['form_id'] ?? '') . ')
'; $lines[] = 'Meta 信息:
'; $lines[] = 'Lead ID:' . $e($lead['fb_lead_id'] ?? '') . '
'; $lines[] = 'FB 创建时间原始值:' . $e($lead['fb_created_time'] ?? '') . '
'; if (!empty($lead['inbox_url'])) { $url = $e($lead['inbox_url']); $lines[] = 'Inbox 链接:' . $url . '
'; } return implode("\n", $lines); } }