master
cody 4 months ago
parent 73f7ae6942
commit ea30b44f10

@ -0,0 +1,239 @@
<?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')
->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/' . date('Y/m');
// 确保目录存在
$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';
}
}

@ -53,7 +53,9 @@ class SupplyDemandController extends CommonController
$query->select('id', 'nickname', 'name', 'headimgurl', 'username');
}
])->where(function ($query) use ($all, $status) {
$query->where('status', $status);
if($status){
$query->where('status', $status);
}
if (isset($all['type'])) {
$query->where('type', $all['type']);
}

@ -4,6 +4,7 @@ namespace App\Models;
class Book extends SoftDeletesModel
{
protected $casts = ['other_data' => 'json'];
public function cover()
{

@ -26,6 +26,7 @@ return new class extends Migration {
$table->mediumText('description')->nullable()->comment('图书简介');
$table->integer('cover_id')->nullable()->comment('图书封面');
$table->tinyInteger('status')->default(0)->comment('状态0可借阅1已借出2维护中');
$table->json('other_data')->nullable()->comment('其他数据');
$table->timestamps();
$table->softDeletes();
});

Loading…
Cancel
Save