Files
fdesk/app/inquiry/service/FbFileImportService.php
T
2025-12-11 11:04:41 +00:00

154 lines
4.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\inquiry\service;
use PhpOffice\PhpSpreadsheet\IOFactory;
use support\Db;
use app\inquiry\utils\FbLeadHeaderMapper;
use support\exception\BusinessException;
use support\Log;
class FbFileImportService
{
/**
* 导入指定目录下所有 .xlsx 文件
*
* @param string $dir 绝对目录路径
* @param int $channelId 渠道ID(写入 fb_leads.channel)
* @return int 实际导入的文件数量(按文件数计,不是行数)
* @throws BusinessException
*/
public function importDir(string $dir, int $channelId): int
{
if (!is_dir($dir)) {
throw new BusinessException("目录不存在:{$dir}");
}
// 只扫描 .xlsx,之前处理过的 .xlsx.done 不会被匹配到
$files = glob($dir . DIRECTORY_SEPARATOR . '*.xlsx');
if (!$files) {
return 0;
}
$count = 0;
foreach ($files as $filePath) {
if (!is_file($filePath)) {
continue;
}
// 这里可以再做一道保险:如果已经有同名的 .done 文件,可以认为它已经导过,直接跳过
$doneFlag = $filePath . '.done';
if (is_file($doneFlag)) {
// 理论上不会出现(因为 .xlsx 已经被改名了),但防御性写法无害
continue;
}
$rows = $this->importOneFile($filePath, $channelId);
$count++;
// 导入成功后,把文件重命名为 .xlsx.done,避免下次重复导入
$newPath = $filePath . '.done';
if (!@rename($filePath, $newPath)) {
Log::warning("FbFileImportService: 文件导入成功,但重命名失败:{$filePath} -> {$newPath}");
} else {
Log::info("FbFileImportService: 文件导入并标记完成:{$newPath}(行数:{$rows})");
}
}
return $count;
}
/**
* 导入单个 Excel 到 fb_leads
*
* @param string $filePath
* @param int $channelId 渠道ID
* @return int 导入的记录条数
* @throws BusinessException
*/
public function importOneFile(string $filePath, int $channelId): int
{
if (!is_file($filePath)) {
throw new BusinessException("Excel 文件不存在:{$filePath}");
}
$spreadsheet = IOFactory::load($filePath);
$sheet = $spreadsheet->getActiveSheet();
$rows = $sheet->toArray(null, true, true, true);
if (empty($rows)) {
throw new BusinessException('Excel 内容为空:' . $filePath);
}
// 表头
$headerRow = array_shift($rows);
$mapper = new FbLeadHeaderMapper();
// 如有未知字段,会在这里抛异常
$colToField = $mapper->buildColumnFieldMap($headerRow);
$insertData = [];
$now = time();
$sourceFile = basename($filePath);
foreach ($rows as $row) {
$item = [];
foreach ($colToField as $colIndex => $field) {
$raw = $row[$colIndex] ?? null;
switch ($field) {
case 'is_organic':
$item['is_organic'] = $this->normalizeIsOrganic($raw);
break;
case 'fb_created_time':
$item['fb_created_time'] = $this->normalizeCreatedTime($raw);
break;
default:
$item[$field] = $raw;
break;
}
}
$item['source_file'] = $sourceFile;
$item['channel'] = $channelId; // 👈 渠道ID记录到 fb_leads
$item['created_at'] = $now;
$item['updated_at'] = $now;
$item['sync_status'] = 0; // 未同步到 inquiry_log
$insertData[] = $item;
}
if ($insertData) {
Db::table('fb_leads')->insert($insertData);
}
return count($insertData);
}
/**
* 规范化 is_organic 字段为 0/1
*/
protected function normalizeIsOrganic($raw): int
{
if (is_bool($raw)) {
return $raw ? 1 : 0;
}
$str = strtolower((string)$raw);
if ($str === '') {
return 0;
}
return in_array($str, ['1', 'true', 'yes', 'organic'], true) ? 1 : 0;
}
/**
* FB 创建时间目前按原始字符串存储
*/
protected function normalizeCreatedTime($raw)
{
return $raw;
}
}