|
|
<?php
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
use App\Models\Book;
|
|
|
use App\Models\Config;
|
|
|
use App\Models\Upload;
|
|
|
use Illuminate\Console\Command;
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
use Illuminate\Support\Str;
|
|
|
|
|
|
class UpdateBookIsbnData extends Command
|
|
|
{
|
|
|
/**
|
|
|
* The name and signature of the console command.
|
|
|
*
|
|
|
* @var string
|
|
|
*/
|
|
|
protected $signature = 'book:update-isbn-data';
|
|
|
|
|
|
/**
|
|
|
* The console command description.
|
|
|
*
|
|
|
* @var string
|
|
|
*/
|
|
|
protected $description = '从ISBN接口获取书籍数据并下载封面图片';
|
|
|
|
|
|
/**
|
|
|
* Create a new command instance.
|
|
|
*
|
|
|
* @return void
|
|
|
*/
|
|
|
public function __construct()
|
|
|
{
|
|
|
parent::__construct();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Execute the console command.
|
|
|
*
|
|
|
* @return mixed
|
|
|
*/
|
|
|
public function handle()
|
|
|
{
|
|
|
$apiKey = Config::getValueByKey('book_key');
|
|
|
|
|
|
// 获取所有有ISBN的书籍
|
|
|
$books = Book::whereNotNull('isbn')
|
|
|
->where('isbn', '!=', '')
|
|
|
->whereNull('cover_id')
|
|
|
->where('id', 1)
|
|
|
->get();
|
|
|
|
|
|
if ($books->isEmpty()) {
|
|
|
$this->info('没有找到未处理封面的书籍');
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
$this->info("找到 {$books->count()} 本书需要处理");
|
|
|
|
|
|
$bar = $this->output->createProgressBar($books->count());
|
|
|
$bar->start();
|
|
|
|
|
|
$successCount = 0;
|
|
|
$failCount = 0;
|
|
|
|
|
|
foreach ($books as $book) {
|
|
|
try {
|
|
|
$result = $this->processBook($book, $apiKey);
|
|
|
if ($result) {
|
|
|
$successCount++;
|
|
|
$this->line("\n✓ 成功处理书籍: {$book->title} (ISBN: {$book->isbn})");
|
|
|
} else {
|
|
|
$failCount++;
|
|
|
$this->line("\n✗ 处理失败: {$book->title} (ISBN: {$book->isbn})");
|
|
|
}
|
|
|
} catch (\Exception $e) {
|
|
|
$failCount++;
|
|
|
$this->line("\n✗ 处理异常: {$book->title} - {$e->getMessage()}");
|
|
|
}
|
|
|
|
|
|
$bar->advance();
|
|
|
|
|
|
// 添加延迟避免API请求过快
|
|
|
sleep(1);
|
|
|
}
|
|
|
|
|
|
$bar->finish();
|
|
|
|
|
|
$this->line('');
|
|
|
$this->info("处理完成!成功: {$successCount}, 失败: {$failCount}");
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 处理单本书籍
|
|
|
*
|
|
|
* @param Book $book
|
|
|
* @param string $apiKey
|
|
|
* @return bool
|
|
|
*/
|
|
|
private function processBook(Book $book, string $apiKey): bool
|
|
|
{
|
|
|
// 调用ISBN接口
|
|
|
$response = Http::timeout(30)->get('https://api.tanshuapi.com/api/isbn/v2/index', [
|
|
|
'key' => $apiKey,
|
|
|
'isbn' => $book->isbn
|
|
|
]);
|
|
|
|
|
|
if (!$response->successful()) {
|
|
|
$this->error("API请求失败: HTTP {$response->status()}");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
$data = $response->json();
|
|
|
|
|
|
if (!$data || $data['code'] !== 1) {
|
|
|
$this->error("API返回错误: " . ($data['msg'] ?? '未知错误'));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
$bookData = $data['data'];
|
|
|
|
|
|
// 更新书籍的other_data字段
|
|
|
$book->other_data = $bookData;
|
|
|
|
|
|
// 如果有图片URL,下载图片
|
|
|
if (!empty($bookData['img'])) {
|
|
|
$coverId = $this->downloadAndSaveImage($bookData['img'], $book);
|
|
|
if ($coverId) {
|
|
|
$book->cover_id = $coverId;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$book->save();
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 下载图片并保存到本地
|
|
|
*
|
|
|
* @param string $imageUrl
|
|
|
* @param Book $book
|
|
|
* @return int|null
|
|
|
*/
|
|
|
private function downloadAndSaveImage(string $imageUrl, Book $book): ?int
|
|
|
{
|
|
|
try {
|
|
|
// 下载图片
|
|
|
$response = Http::timeout(30)->get($imageUrl);
|
|
|
|
|
|
if (!$response->successful()) {
|
|
|
$this->error("图片下载失败: {$imageUrl}");
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
$imageContent = $response->body();
|
|
|
|
|
|
// 获取文件扩展名
|
|
|
$extension = $this->getImageExtension($imageUrl, $response->header('Content-Type'));
|
|
|
|
|
|
// 生成文件名
|
|
|
$filename = 'book_cover_' . $book->id . '_' . time() . '.' . $extension;
|
|
|
|
|
|
// 定义存储目录
|
|
|
$folder = 'uploads/book_covers';
|
|
|
|
|
|
// 确保目录存在
|
|
|
$fullPath = public_path($folder);
|
|
|
if (!file_exists($fullPath)) {
|
|
|
mkdir($fullPath, 0755, true);
|
|
|
}
|
|
|
|
|
|
// 保存文件
|
|
|
$filePath = $folder . '/' . $filename;
|
|
|
file_put_contents(public_path($filePath), $imageContent);
|
|
|
|
|
|
// 获取文件大小
|
|
|
$fileSize = strlen($imageContent);
|
|
|
|
|
|
// 创建uploads记录
|
|
|
$upload = Upload::create([
|
|
|
'belongs_type' => 'App\\Models\\Book',
|
|
|
'belongs_id' => $book->id,
|
|
|
'original_name' => basename($imageUrl),
|
|
|
'folder' => $folder,
|
|
|
'name' => $filename,
|
|
|
'extension' => $extension,
|
|
|
'size' => $fileSize,
|
|
|
'creator_type' => 'console',
|
|
|
'creator_id' => null,
|
|
|
]);
|
|
|
|
|
|
return $upload->id;
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
$this->error("保存图片时出错: {$e->getMessage()}");
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取图片扩展名
|
|
|
*
|
|
|
* @param string $url
|
|
|
* @param string|null $contentType
|
|
|
* @return string
|
|
|
*/
|
|
|
private function getImageExtension(string $url, ?string $contentType = null): string
|
|
|
{
|
|
|
// 首先尝试从URL获取扩展名
|
|
|
$pathInfo = pathinfo(parse_url($url, PHP_URL_PATH));
|
|
|
if (!empty($pathInfo['extension'])) {
|
|
|
return strtolower($pathInfo['extension']);
|
|
|
}
|
|
|
|
|
|
// 从Content-Type获取扩展名
|
|
|
if ($contentType) {
|
|
|
$mimeToExt = [
|
|
|
'image/jpeg' => 'jpg',
|
|
|
'image/jpg' => 'jpg',
|
|
|
'image/png' => 'png',
|
|
|
'image/gif' => 'gif',
|
|
|
'image/webp' => 'webp',
|
|
|
'image/bmp' => 'bmp',
|
|
|
];
|
|
|
|
|
|
$contentType = strtolower(trim(explode(';', $contentType)[0]));
|
|
|
if (isset($mimeToExt[$contentType])) {
|
|
|
return $mimeToExt[$contentType];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 默认返回jpg
|
|
|
return 'jpg';
|
|
|
}
|
|
|
}
|