参考文献格式--book

This commit is contained in:
wangjinlei
2026-08-07 16:04:59 +08:00
parent 175d2edc55
commit a1e151048c
3 changed files with 475 additions and 92 deletions

View File

@@ -24,11 +24,15 @@ class ReferenceDispatchService
/** @var BookMetadataService */
private $bookMetadata;
/** @var BookCitationParser */
private $bookParser;
public function __construct()
{
$this->classifier = new ReferenceTypeClassifier(['use_llm' => true]);
$this->metadata = new ReferenceMetadataService();
$this->bookMetadata = new BookMetadataService();
$this->bookParser = new BookCitationParser();
}
/**
@@ -181,7 +185,9 @@ class ReferenceDispatchService
$summary = is_array($meta) ? $meta['crossref_summary'] : null;
$raw = is_array($summary) ? ($summary['raw'] ?? []) : [];
$hasMeta = is_array($meta)
&& (trim((string)$meta['title']) !== '' || trim((string)$meta['author']) !== '');
&& (trim((string)$meta['title']) !== '' || trim((string)$meta['author']) !== '')
// 图书著录里挂的 DOI 常常是被错误关联的期刊文章,题名对不上就不能拿来覆盖
&& $this->metaMatchesContent($meta, $content);
if ($hasMeta) {
$authorCitation = trim((string)$meta['author']);
@@ -217,6 +223,44 @@ class ReferenceDispatchService
Db::name('production_article_refer')->where('p_refer_id', $pReferId)->update($update);
}
/**
* DOI 抓回来的题名是否确实是这条著录说的那本书
*/
private function metaMatchesContent($meta, $content)
{
$title = $this->normalizeForMatch((string)$meta['title']);
if ($title === '') {
return false;
}
$blob = $this->normalizeForMatch($content);
if ($blob === '' || strpos($blob, $title) !== false) {
return true;
}
$words = array_filter(explode(' ', $title), function ($w) {
return strlen($w) > 3;
});
if (empty($words)) {
return false;
}
$hit = 0;
foreach ($words as $word) {
if (strpos($blob, $word) !== false) {
$hit++;
}
}
return ($hit / count($words)) >= 0.6;
}
private function normalizeForMatch($text)
{
$text = strtolower(trim((string)$text));
$text = preg_replace('/[^a-z0-9\x{4e00}-\x{9fff}\s]+/u', ' ', $text);
return trim(preg_replace('/\s+/u', ' ', $text));
}
/**
* 按书名/作者/年份/版次反查 ISBN顺带补齐缺失的出版社与出版年
*
@@ -299,50 +343,22 @@ class ReferenceDispatchService
}
/**
* 无 Crossref 时从原文尽量抽取 book 结构化字段
* 无可用 DOI 元数据时,从原文著录抽取 book 结构化字段
*/
private function parseBookFromContent($content)
{
$content = trim((string)$content);
$out = [
'author' => '',
'title' => '',
'joura' => '',
'dateno' => '',
'isbn' => '',
$parsed = $this->bookParser->parse($content);
$author = trim((string)$parsed['author']);
return [
'author' => $author !== '' ? rtrim($author, '.') . '.' : '',
'title' => trim((string)$parsed['title']),
'joura' => trim((string)$parsed['publisher']),
'dateno' => trim((string)$parsed['year']),
'isbn' => trim((string)$parsed['isbn']),
'is_ja' => 1,
];
if ($content === '') {
return $out;
}
if (preg_match('/\bISBN[:\s]*([\d\-Xx\s]+)/i', $content, $m)) {
$out['isbn'] = preg_replace('/\s+/', '-', trim($m[1]));
}
if (preg_match('/\b(19|20)\d{2}\b/', $content, $m)) {
$out['dateno'] = $m[0];
}
// Place: Publisher; Year → joura 取 Publisher
if (preg_match('/:\s*([^;]+);\s*(19|20)\d{2}/', $content, $m)) {
$out['joura'] = trim($m[1]);
} elseif (preg_match('/\b([A-Z][A-Za-z .&]+(?:Press|Publishing|Publisher|Books?))\b/i', $content, $m)) {
$out['joura'] = trim($m[1]);
}
// 作者. 标题. ... 简单拆分
$parts = preg_split('/\.\s+/', $content, 3);
if (is_array($parts) && count($parts) >= 2) {
$out['author'] = trim($parts[0]);
if (substr($out['author'], -1) !== '.') {
$out['author'] .= '.';
}
$out['title'] = trim(rtrim($parts[1], '.'));
}
return $out;
}
private function normalizeDoi($doi)