contentEx = array('BookInfo_Plugin', 'parse');
Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('BookInfo_Plugin', 'parse');
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('BookInfo_Plugin', 'renderButton');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('BookInfo_Plugin', 'renderButton');
$cacheDir = dirname(__FILE__) . '/cache/';
if (!file_exists($cacheDir)) mkdir($cacheDir, 0755, true);
return '插件激活成功!';
}
/**
* 禁用插件
*/
public static function deactivate()
{
return '插件已禁用';
}
/**
* 配置面板
*/
public static function config(Typecho_Widget_Helper_Form $form)
{
$cacheEnable = new Typecho_Widget_Helper_Form_Element_Radio('cacheEnable',
array('1' => '启用', '0' => '禁用'),
'1', '启用缓存', '缓存图书信息,提升访问速度');
$form->addInput($cacheEnable);
$cacheTime = new Typecho_Widget_Helper_Form_Element_Text('cacheTime', NULL, '30',
'缓存时间(天)', '图书信息缓存保留天数');
$cacheTime->addRule('isInteger', '请输入整数');
$form->addInput($cacheTime);
$imageProxy = new Typecho_Widget_Helper_Form_Element_Text('imageProxy', NULL,
'https://images.weserv.nl/?url=', '图片代理', '用于加载豆瓣图片');
$form->addInput($imageProxy);
$defaultCover = new Typecho_Widget_Helper_Form_Element_Text('defaultCover', NULL,
'https://img9.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif',
'默认封面', '当无法获取封面时显示的图片');
$form->addInput($defaultCover);
$summaryLength = new Typecho_Widget_Helper_Form_Element_Text('summaryLength', NULL, '200',
'简介显示长度', '简介默认显示的最大字符数,超出部分可展开查看');
$summaryLength->addRule('isInteger', '请输入整数');
$form->addInput($summaryLength);
$expandText = new Typecho_Widget_Helper_Form_Element_Text('expandText', NULL, '展开',
'"展开"文字', '点击展开完整简介的文字');
$form->addInput($expandText);
$collapseText = new Typecho_Widget_Helper_Form_Element_Text('collapseText', NULL, '收起',
'"收起"文字', '点击收起简介的文字');
$form->addInput($collapseText);
$expandColor = new Typecho_Widget_Helper_Form_Element_Text('expandColor', NULL, '#0073aa',
'展开按钮颜色', '展开/收起按钮的文字颜色');
$form->addInput($expandColor);
// 新增:独立页面每页显示条数设置
$pageSize = new Typecho_Widget_Helper_Form_Element_Text('pageSize', NULL, '10',
'独立页面每页显示条数', '在独立页面中每页显示的图书数量(1-50)');
$pageSize->addRule('isInteger', '请输入整数');
$pageSize->addRule(array(new BookInfo_Plugin, 'validatePageSize'), '请输入1-50之间的整数');
$form->addInput($pageSize);
}
/**
* 验证页面显示条数
*/
public static function validatePageSize($value)
{
$value = intval($value);
if ($value < 1 || $value > 50) {
throw new Typecho_Widget_Exception('请输入1-50之间的整数');
}
return true;
}
/**
* 个人配置面板
*/
public static function personalConfig(Typecho_Widget_Helper_Form $form)
{
}
/**
* 解析短代码
*/
public static function parse($content, $widget, $lastResult)
{
$content = empty($lastResult) ? $content : $lastResult;
// 如果是独立页面,且内容中包含[all_books]标记,则显示所有图书
if ($widget instanceof Widget_Archive && $widget->is('page')) {
// 支持[all_books]和[all_books:page=1]格式
$pattern = '/\[all_books(?::page=(\d+))?\]/i';
if (preg_match_all($pattern, $content, $matches)) {
foreach ($matches[0] as $key => $match) {
$page = isset($matches[1][$key]) ? intval($matches[1][$key]) : 1;
$allBooksHtml = self::renderAllBooks($page);
$content = str_replace($match, $allBooksHtml, $content);
}
}
}
// 如果是单篇文章,解析图书短代码
if ($widget instanceof Widget_Archive && $widget->is('single')) {
// 匹配 [book:数字] 或 [book:数字:短评] 格式
$pattern = '/\[book:(\d+)(?::([^\]]+))?\]/i';
if (preg_match_all($pattern, $content, $matches)) {
foreach ($matches[0] as $key => $match) {
$bookId = $matches[1][$key];
$reviewWithCustom = isset($matches[2][$key]) ? trim($matches[2][$key]) : '';
$review = '';
$customData = array();
// 解码短评和自定义数据
if (!empty($reviewWithCustom)) {
// 分离短评和自定义数据
if (strpos($reviewWithCustom, '|CUSTOM:') !== false) {
list($review, $customJson) = explode('|CUSTOM:', $reviewWithCustom, 2);
// 解码自定义数据
if (!empty($customJson)) {
// URL解码
$decodedJson = urldecode($customJson);
// 解析JSON
$customData = json_decode($decodedJson, true);
if (!is_array($customData)) {
$customData = array();
}
}
} else {
// 检查是否是纯自定义数据(没有短评)
if (preg_match('/^\(自定义:(.*)\)$/', $reviewWithCustom, $customMatch)) {
// 这是旧格式的自定义数据,需要转换
$review = '';
$customData = self::parseLegacyCustomData($customMatch[1]);
} else {
// 重要修复:处理中文自定义数据(如:短评(自定义:开始阅读:2025.12.03))
// 检查是否包含中文括号格式的自定义数据
if (preg_match('/^(.*?)(自定义:(.*))$/u', $reviewWithCustom, $customMatch)) {
// 第一部分是短评
$review = trim($customMatch[1]);
// 第二部分是自定义数据
$customData = self::parseLegacyCustomData($customMatch[2]);
} else {
// 没有自定义数据,只有短评
$review = $reviewWithCustom;
}
}
}
// 解码短评(处理特殊字符)- 修复:只解码真正的短评部分
if (!empty($review)) {
// 处理HTML实体转义
$review = str_replace(array('[', ']'), array('[', ']'), $review);
}
}
// 获取图书数据(包含短评和自定义数据)
$bookHtml = self::renderBook($bookId, $review, $customData);
$content = str_replace($match, $bookHtml, $content);
}
}
}
return $content;
}
/**
* 解析旧格式的自定义数据
*/
private static function parseLegacyCustomData($customText)
{
$customData = array();
// 解析旧格式:开始阅读:2025.12.03,结束阅读:2025.12.07,阅读方法:速读,图书分类:小说,推荐指数:★★
$pairs = explode(',', $customText);
foreach ($pairs as $pair) {
if (strpos($pair, ':') !== false) {
list($key, $value) = explode(':', $pair, 2);
switch (trim($key)) {
case '开始阅读':
$customData['startDate'] = trim($value);
break;
case '结束阅读':
$customData['readDate'] = trim($value);
break;
case '阅读方法':
$customData['readMethod'] = trim($value);
break;
case '图书分类':
$customData['bookCategory'] = trim($value);
break;
case '推荐指数':
// 计算星星数量
$starCount = substr_count($value, '★');
$customData['recommendation'] = $starCount;
break;
}
}
}
return $customData;
}
/**
* 渲染单本图书信息
*/
private static function renderBook($bookId, $review = '', $customData = array())
{
// 获取图书数据(包含短评和自定义数据)
$bookData = self::getBookData($bookId, $review, $customData);
if (!$bookData || empty($bookData['title'])) {
return '
获取图书信息失败,ID:' . htmlspecialchars($bookId) . '
';
}
$options = Typecho_Widget::widget('Widget_Options')->plugin('BookInfo');
$imageProxy = isset($options->imageProxy) ? $options->imageProxy : 'https://images.weserv.nl/?url=';
$defaultCover = isset($options->defaultCover) ? $options->defaultCover :
'https://img9.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif';
$summaryLength = isset($options->summaryLength) ? intval($options->summaryLength) : 200;
$expandText = isset($options->expandText) ? $options->expandText : '展开';
$collapseText = isset($options->collapseText) ? $options->collapseText : '收起';
$expandColor = isset($options->expandColor) ? $options->expandColor : '#0073aa';
$title = htmlspecialchars($bookData['title']);
$author = is_array($bookData['author']) ? implode(', ', $bookData['author']) : htmlspecialchars($bookData['author']);
$summary = isset($bookData['summary']) ? $bookData['summary'] : '';
// 重要修复:直接从传入的$review获取短评,确保与编辑器输入一致
$review = htmlspecialchars($review);
// 豆瓣抓取字段
$publisher = isset($bookData['publisher']) ? htmlspecialchars($bookData['publisher']) : '未知';
$pubdate = isset($bookData['pubdate']) ? htmlspecialchars($bookData['pubdate']) : '未知';
$pages = isset($bookData['pages']) ? htmlspecialchars($bookData['pages']) : '未知';
$rating = isset($bookData['rating']) ? floatval($bookData['rating']) : 0;
$ratingCount = isset($bookData['rating_count']) ? intval($bookData['rating_count']) : 0;
// 自定义字段 - 修复:确保正确处理customData
$startDate = isset($customData['startDate']) ? htmlspecialchars($customData['startDate']) : '';
$readDate = isset($customData['readDate']) ? htmlspecialchars($customData['readDate']) : '';
$readMethod = isset($customData['readMethod']) ? htmlspecialchars($customData['readMethod']) : '';
$bookCategory = isset($customData['bookCategory']) ? htmlspecialchars($customData['bookCategory']) : '';
$recommendation = isset($customData['recommendation']) ? intval($customData['recommendation']) : 0;
// 如果customData中没有,尝试从bookData中获取
if (empty($startDate) && isset($bookData['custom_start_date']) && !empty($bookData['custom_start_date'])) {
$startDate = htmlspecialchars($bookData['custom_start_date']);
}
if (empty($readDate) && isset($bookData['custom_read_date']) && !empty($bookData['custom_read_date'])) {
$readDate = htmlspecialchars($bookData['custom_read_date']);
}
if (empty($readMethod) && isset($bookData['custom_read_method']) && !empty($bookData['custom_read_method'])) {
$readMethod = htmlspecialchars($bookData['custom_read_method']);
}
if (empty($bookCategory) && isset($bookData['custom_book_category']) && !empty($bookData['custom_book_category'])) {
$bookCategory = htmlspecialchars($bookData['custom_book_category']);
}
if ($recommendation == 0 && isset($bookData['custom_recommendation'])) {
$recommendation = intval($bookData['custom_recommendation']);
}
// 检查简介是否超过当前设置的限制长度
$isSummaryLong = false;
$summaryShort = $summary;
// 移除HTML标签来计算纯文本长度
$plainSummary = strip_tags($summary);
if (mb_strlen($plainSummary, 'UTF-8') > $summaryLength) {
$isSummaryLong = true;
// 截取纯文本
$plainShort = mb_substr($plainSummary, 0, $summaryLength, 'UTF-8');
// 尝试保持HTML结构,但这是一个简化的处理
$summaryShort = $plainShort . '...';
}
$coverUrl = !empty($bookData['image']) ? $bookData['image'] : $defaultCover;
$coverSrc = $imageProxy . urlencode($coverUrl);
// 生成评分显示
$ratingHtml = '';
if ($rating > 0) {
$ratingHtml = '
豆瓣评分:
' . number_format($rating, 1) . '';
if ($ratingCount > 0) {
$ratingHtml .= ' (' . number_format($ratingCount) . '人评价)';
}
$ratingHtml .= '
';
}
// 生成推荐指数星星
$recommendationHtml = '';
if ($recommendation > 0) {
$recommendationHtml = '
推荐指数:';
for ($i = 1; $i <= 5; $i++) {
if ($i <= $recommendation) {
$recommendationHtml .= '★';
} else {
$recommendationHtml .= '★';
}
}
$recommendationHtml .= '
';
}
// 生成自定义信息HTML
$customInfoHtml = '';
$hasCustomInfo = false;
// 生成右侧栏的自定义信息(无标题,样式与中间栏一致)
if ($startDate || $readDate || $readMethod || $bookCategory) {
$hasCustomInfo = true;
if ($startDate) {
$customInfoHtml .= '
开始阅读:
' . $startDate . '
';
}
if ($readDate) {
$customInfoHtml .= '
结束阅读:
' . $readDate . '
';
}
if ($readMethod) {
$customInfoHtml .= '
阅读方法:
' . $readMethod . '
';
}
if ($bookCategory) {
$customInfoHtml .= '
图书分类:
' . $bookCategory . '
';
}
}
// 生成右侧栏的完整HTML
$rightColumnHtml = '';
if ($recommendationHtml) {
$rightColumnHtml .= $recommendationHtml;
}
if ($customInfoHtml) {
$rightColumnHtml .= $customInfoHtml;
} else {
// 如果没有自定义信息,显示占位符
$rightColumnHtml .= '
阅读记录:
暂无记录
';
}
// 生成简介部分HTML,包含展开/收起功能
$summaryHtml = '';
if ($isSummaryLong) {
// 长简介:显示短版本 + 展开按钮
$summaryHtml .= '
';
} else {
// 短简介:直接显示完整内容
$summaryHtml .= '
' . $summary . '
';
}
$summaryHtml .= '
';
// 生成短评HTML(如果有短评)- 只显示纯粹的短评
$reviewHtml = '';
if (!empty($review)) {
$reviewHtml = '
💭 我的短评
' . nl2br($review) . '
';
}
$html = <<
作者:
{$author}
出版社:
{$publisher}
出版年:
{$pubdate}
页数:
{$pages}
{$ratingHtml}
{$rightColumnHtml}
HTML;
// 添加JavaScript切换函数
$html .= '
';
return $html;
}
/**
* 获取所有图书数据(支持分页)
*/
private static function getAllBooksData($page = 1, $pageSize = 10)
{
$cacheDir = dirname(__FILE__) . '/cache/';
$allBooks = array();
// 扫描缓存目录
if (file_exists($cacheDir)) {
$files = scandir($cacheDir);
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'json') {
$filePath = $cacheDir . $file;
$content = file_get_contents($filePath);
if ($content) {
$data = json_decode($content, true);
if ($data && isset($data['fetched_at'])) {
// 添加文件名作为bookId
$bookId = pathinfo($file, PATHINFO_FILENAME);
$data['bookId'] = $bookId;
// 使用文件修改时间作为添加时间(如果文件不存在,使用fetched_at)
if (file_exists($filePath)) {
$data['added_time'] = filemtime($filePath);
} else {
$data['added_time'] = isset($data['fetched_at']) ? $data['fetched_at'] : time();
}
$allBooks[] = $data;
}
}
}
}
}
// 按added_time倒序排序(最新的在最前面)
usort($allBooks, function($a, $b) {
return $b['added_time'] - $a['added_time'];
});
$total = count($allBooks);
$totalPages = ceil($total / $pageSize);
// 限制页码范围
$page = max(1, min($page, $totalPages));
// 分页处理
$startIndex = ($page - 1) * $pageSize;
$paginatedBooks = array_slice($allBooks, $startIndex, $pageSize);
return array(
'books' => $paginatedBooks,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize,
'totalPages' => $totalPages,
'startIndex' => $startIndex + 1,
'endIndex' => min($startIndex + $pageSize, $total)
);
}
/**
* 生成分页HTML
*/
private static function generatePagination($currentPage, $totalPages, $baseUrl = '')
{
if ($totalPages <= 1) {
return '';
}
$html = '';
return $html;
}
/**
* 获取页面URL
*/
private static function getPageUrl($page, $baseUrl = '')
{
if (empty($baseUrl)) {
// 获取当前页面URL
$currentUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
// 移除现有的page参数
$currentUrl = preg_replace('/[&?]page=\d+/', '', $currentUrl);
$currentUrl = rtrim($currentUrl, '?&');
// 添加分页参数
$separator = strpos($currentUrl, '?') === false ? '?' : '&';
return $currentUrl . $separator . 'page=' . $page;
}
return $baseUrl . (strpos($baseUrl, '?') === false ? '?' : '&') . 'page=' . $page;
}
/**
* 渲染所有图书列表(支持深色模式和封面显示)
*/
public static function renderAllBooks($page = 1)
{
// 从GET参数获取页码(优先级高于参数)
if (isset($_GET['page']) && is_numeric($_GET['page'])) {
$page = intval($_GET['page']);
}
$options = Typecho_Widget::widget('Widget_Options')->plugin('BookInfo');
$pageSize = isset($options->pageSize) ? intval($options->pageSize) : 10;
$imageProxy = isset($options->imageProxy) ? $options->imageProxy : 'https://images.weserv.nl/?url=';
$defaultCover = isset($options->defaultCover) ? $options->defaultCover :
'https://img9.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif';
// 获取分页数据
$paginationData = self::getAllBooksData($page, $pageSize);
$allBooks = $paginationData['books'];
$total = $paginationData['total'];
$currentPage = $paginationData['page'];
$totalPages = $paginationData['totalPages'];
$startIndex = $paginationData['startIndex'];
$endIndex = $paginationData['endIndex'];
if (empty($allBooks)) {
return '
暂无图书数据
请先在文章中使用[book:ID]短代码添加图书
';
}
$html = '';
// 标题模块
$html .= '
';
$html .= '
我的全部已读图书
';
$html .= '
已读' . $total . '本图书,本数据2025.12.08开始统计
';
$html .= '
';
// 页码信息
$html .= '
';
$html .= '
';
$html .= '
';
$html .= '显示:';
$html .= '' . $startIndex . '-' . $endIndex . ' / ';
$html .= '' . $total . '';
$html .= '
';
$html .= '
';
$html .= '当前:';
$html .= '第 ' . $currentPage . ' 页 / ';
$html .= '共 ' . $totalPages . ' 页';
$html .= '
';
$html .= '
';
$html .= '
';
// 计算倒序序号(最大的序号在最前面)
$totalCount = $total;
$currentIndex = $totalCount - (($currentPage - 1) * $pageSize);
foreach ($allBooks as $book) {
$bookId = $book['bookId'];
$title = isset($book['title']) ? htmlspecialchars($book['title']) : '未知图书';
// 封面图片
$coverUrl = !empty($book['image']) ? $book['image'] : $defaultCover;
$coverSrc = $imageProxy . urlencode($coverUrl);
// 自定义字段
$startDate = isset($book['custom_start_date']) ? htmlspecialchars($book['custom_start_date']) : '';
$readDate = isset($book['custom_read_date']) ? htmlspecialchars($book['custom_read_date']) : '';
$readMethod = isset($book['custom_read_method']) ? htmlspecialchars($book['custom_read_method']) : '';
$bookCategory = isset($book['custom_book_category']) ? htmlspecialchars($book['custom_book_category']) : '';
// 作者处理
$author = '未知作者';
if (isset($book['author'])) {
if (is_array($book['author'])) {
$author = implode(', ', $book['author']);
} else {
$author = $book['author'];
}
}
$author = htmlspecialchars($author);
// 豆瓣信息
$publisher = isset($book['publisher']) ? htmlspecialchars($book['publisher']) : '未知';
$pubdate = isset($book['pubdate']) ? htmlspecialchars($book['pubdate']) : '未知';
$pages = isset($book['pages']) ? htmlspecialchars($book['pages']) : '未知';
// 豆瓣评分
$rating = isset($book['rating']) ? floatval($book['rating']) : 0;
$ratingDisplay = $rating > 0 ? number_format($rating, 1) . '分' : '暂无评分';
// 短评
$review = isset($book['review']) ? htmlspecialchars($book['review']) : '';
// 构建日期范围显示
$dateRange = '';
if ($startDate && $readDate) {
$dateRange = $startDate . '-' . $readDate;
} elseif ($startDate) {
$dateRange = $startDate . '-至今';
} elseif ($readDate) {
$dateRange = '未知-' . $readDate;
}
// 构建图书分类显示
$categoryDisplay = $bookCategory ? $bookCategory : '未分类';
// 构建阅读方法显示
$methodDisplay = $readMethod ? $readMethod : '未知';
// 获取序号颜色
$indexColor = self::getIndexColor($currentIndex);
$html .= '
';
$html .= '
';
// 左侧:封面图片带序号
$html .= '
';
$html .= '
';
$html .= '
';
// 右侧:图书信息
$html .= '
';
// 第一行:书名(带豆瓣链接)/分类/日期范围/阅读方法
$html .= '
';
$html .= '
《' . $title . '》';
if ($categoryDisplay || $dateRange || $methodDisplay) {
$html .= '
/';
$html .= '
' . $categoryDisplay . '';
if ($dateRange) {
$html .= '
/';
$html .= '
' . $dateRange . '';
}
if ($methodDisplay) {
$html .= '
/';
$html .= '
' . $methodDisplay . '';
}
}
$html .= '
';
// 第二行:作者/出版社/出版年/页数/豆瓣评分
$html .= '
';
$html .= '作者:' . $author . '';
$html .= '|';
$html .= '出版社:' . $publisher . '';
$html .= '|';
$html .= '出版年:' . $pubdate . '';
$html .= '|';
$html .= '页数:' . $pages . '页';
$html .= '|';
// 豆瓣评分显示,如果是0分则显示"暂无评分",否则显示具体分数
if ($rating > 0) {
$html .= '豆瓣评分:' . $ratingDisplay . '';
} else {
$html .= '豆瓣评分:' . $ratingDisplay . '';
}
$html .= '
';
// 第三行:短评
if ($review) {
$html .= '
';
$html .= '
📝 短评:
';
$html .= '
';
$html .= nl2br($review);
$html .= '
';
$html .= '
';
}
$html .= '
';
$html .= '
';
$html .= '
';
$currentIndex--;
}
// 分页导航
if ($totalPages > 1) {
$html .= self::generatePagination($currentPage, $totalPages);
}
$html .= '
';
return $html;
}
/**
* 获取序号颜色
*/
private static function getIndexColor($index)
{
$colors = [
'linear-gradient(135deg, #0073aa, #0056b3)', // 蓝色
'linear-gradient(135deg, #28a745, #218838)', // 绿色
'linear-gradient(135deg, #e67e22, #d35400)', // 橙色
'linear-gradient(135deg, #9b59b6, #8e44ad)', // 紫色
'linear-gradient(135deg, #e74c3c, #c0392b)', // 红色
];
return $colors[($index - 1) % count($colors)];
}
/**
* 获取图书数据(包含短评和自定义信息)
*/
private static function getBookData($bookId, $review = '', $customData = array())
{
if (!is_numeric($bookId)) return null;
$cacheFile = dirname(__FILE__) . '/cache/' . $bookId . '.json';
$options = Typecho_Widget::widget('Widget_Options')->plugin('BookInfo');
$cacheEnable = isset($options->cacheEnable) ? $options->cacheEnable : '1';
$cacheTime = isset($options->cacheTime) ? intval($options->cacheTime) : 7;
$data = null;
$needUpdate = false;
// 检查缓存
if ($cacheEnable == '1' && file_exists($cacheFile)) {
$fileTime = filemtime($cacheFile);
$expireTime = $cacheTime * 24 * 3600;
if (time() - $fileTime < $expireTime) {
$cacheContent = file_get_contents($cacheFile);
if ($cacheContent) {
$data = json_decode($cacheContent, true);
}
}
}
// 如果缓存不存在或已过期,从豆瓣获取基本信息
if (!$data || empty($data['title'])) {
$data = self::fetchFromDouban($bookId);
$needUpdate = true;
}
// 重要修复:只在$review不为空且与当前数据不同时更新
if (!empty($review) && (!isset($data['review']) || $data['review'] !== $review)) {
$data['review'] = $review;
$data['review_updated'] = time();
$needUpdate = true;
}
// 更新自定义数据 - 使用传入的customData更新
if (!empty($customData)) {
if (isset($customData['startDate']) && (!isset($data['custom_start_date']) || $data['custom_start_date'] !== $customData['startDate'])) {
$data['custom_start_date'] = $customData['startDate'];
$needUpdate = true;
}
if (isset($customData['readDate']) && (!isset($data['custom_read_date']) || $data['custom_read_date'] !== $customData['readDate'])) {
$data['custom_read_date'] = $customData['readDate'];
$needUpdate = true;
}
if (isset($customData['readMethod']) && (!isset($data['custom_read_method']) || $data['custom_read_method'] !== $customData['readMethod'])) {
$data['custom_read_method'] = $customData['readMethod'];
$needUpdate = true;
}
if (isset($customData['bookCategory']) && (!isset($data['custom_book_category']) || $data['custom_book_category'] !== $customData['bookCategory'])) {
$data['custom_book_category'] = $customData['bookCategory'];
$needUpdate = true;
}
if (isset($customData['recommendation']) && (!isset($data['custom_recommendation']) || $data['custom_recommendation'] != $customData['recommendation'])) {
$data['custom_recommendation'] = intval($customData['recommendation']);
$needUpdate = true;
}
}
// 确保自定义字段存在
$customFields = array(
'custom_start_date' => '',
'custom_read_date' => '',
'custom_read_method' => '',
'custom_book_category' => '',
'custom_recommendation' => 0
);
foreach ($customFields as $field => $default) {
if (!isset($data[$field])) {
$data[$field] = $default;
}
}
// 如果需要更新缓存,保存到文件
if ($needUpdate && $data && !empty($data['title'])) {
file_put_contents($cacheFile, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
return $data;
}
/**
* 从豆瓣获取数据 - 修复:保留HTML标签的摘要抓取
*/
private static function fetchFromDouban($bookId)
{
$url = "https://book.douban.com/subject/{$bookId}/";
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
CURLOPT_ENCODING => 'gzip, deflate',
CURLOPT_REFERER => 'https://book.douban.com/',
CURLOPT_HTTPHEADER => array(
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control: no-cache',
'Connection: keep-alive'
)
));
$html = curl_exec($ch);
if (curl_errno($ch)) {
curl_close($ch);
return null;
}
curl_close($ch);
if (empty($html)) return null;
$data = array();
// 1. 提取标题
if (preg_match('/]*>\s*]*>([^<]+)<\/span>/', $html, $matches)) {
$data['title'] = trim(strip_tags(html_entity_decode($matches[1], ENT_QUOTES, 'UTF-8')));
}
// 2. 提取封面
if (preg_match('/]*src="([^"]+)"[^>]*id="mainpic"/', $html, $matches)) {
$data['image'] = trim($matches[1]);
}
// 3. 提取描述(内容简介)- 修复:保留HTML标签
$summary = '';
// 豆瓣页面可能有多个intro,第一个通常是内容简介
if (preg_match_all('/