参考文献作者堆叠
参考文献相关性检测 作者ai写作辅助检测工作
This commit is contained in:
@@ -104,6 +104,64 @@ class BackgroundCheckService
|
||||
return ['count' => count($list), 'list' => $list, 'source' => 'openalex'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 DOI 从 OpenAlex 获取文献及作者身份(用于引用堆叠精准作者定位)
|
||||
*/
|
||||
public function fetchOpenAlexWorkByDoi($doi)
|
||||
{
|
||||
$doi = $this->cleanDoi($doi);
|
||||
if ($doi === '') {
|
||||
return ['success' => false, 'error' => 'DOI为空'];
|
||||
}
|
||||
|
||||
$res = $this->openAlexGet('/works/https://doi.org/' . rawurlencode($doi));
|
||||
if (!$res['success']) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
return ['success' => true, 'work' => $res['data']];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 OpenAlex work 中的作者身份(OpenAlex ID + ORCID)
|
||||
*
|
||||
* @return array<int, array{openalex_id:string,orcid:string,display_name:string,author_position:string,is_corresponding:bool,identity_keys:string[]}>
|
||||
*/
|
||||
public function parseWorkAuthorships(array $work)
|
||||
{
|
||||
$list = [];
|
||||
foreach ($work['authorships'] ?? [] as $auth) {
|
||||
if (!is_array($auth)) {
|
||||
continue;
|
||||
}
|
||||
$author = is_array($auth['author'] ?? null) ? $auth['author'] : [];
|
||||
$openalexId = $this->extractOpenAlexId($author['id'] ?? '');
|
||||
$orcid = $this->cleanOrcid($author['orcid'] ?? '');
|
||||
if ($openalexId === '' && $orcid === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$identityKeys = [];
|
||||
if ($openalexId !== '') {
|
||||
$identityKeys[] = 'openalex:' . $openalexId;
|
||||
}
|
||||
if ($orcid !== '') {
|
||||
$identityKeys[] = 'orcid:' . $orcid;
|
||||
}
|
||||
|
||||
$list[] = [
|
||||
'openalex_id' => $openalexId,
|
||||
'orcid' => $orcid,
|
||||
'display_name' => trim((string)($author['display_name'] ?? '')),
|
||||
'author_position' => (string)($auth['author_position'] ?? ''),
|
||||
'is_corresponding' => !empty($auth['is_corresponding']),
|
||||
'identity_keys' => $identityKeys,
|
||||
];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function fetchRecentWorks($openAlexId, $limit = 5)
|
||||
{
|
||||
$res = $this->openAlexGet('/works', [
|
||||
@@ -604,7 +662,7 @@ class BackgroundCheckService
|
||||
];
|
||||
}
|
||||
|
||||
private function parseCrossRefAuthors($authorList)
|
||||
public function parseCrossRefAuthors($authorList)
|
||||
{
|
||||
if (empty($authorList) || !is_array($authorList)) {
|
||||
return [];
|
||||
@@ -612,16 +670,52 @@ class BackgroundCheckService
|
||||
|
||||
$result = [];
|
||||
foreach ($authorList as $a) {
|
||||
$orcid = '';
|
||||
if (!empty($a['ORCID']) && is_array($a['ORCID'])) {
|
||||
$orcid = $this->cleanOrcid((string)($a['ORCID'][0] ?? ''));
|
||||
} elseif (!empty($a['ORCID']) && is_string($a['ORCID'])) {
|
||||
$orcid = $this->cleanOrcid($a['ORCID']);
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'given' => $a['given'] ?? '',
|
||||
'family' => $a['family'] ?? '',
|
||||
'name' => isset($a['name']) ? $a['name'] : trim(($a['given'] ?? '') . ' ' . ($a['family'] ?? '')),
|
||||
'orcid' => $a['ORCID'] ?? '',
|
||||
'orcid' => $orcid,
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crossref 作者列表 → 带 identity_keys 的结构(ORCID 兜底)
|
||||
*
|
||||
* @return array<int, array{openalex_id:string,orcid:string,display_name:string,author_position:string,is_corresponding:bool,identity_keys:string[]}>
|
||||
*/
|
||||
public function authorshipsFromCrossrefAuthors(array $authorList)
|
||||
{
|
||||
$parsed = $this->parseCrossRefAuthors($authorList);
|
||||
$list = [];
|
||||
$total = count($parsed);
|
||||
foreach ($parsed as $i => $a) {
|
||||
$orcid = trim((string)($a['orcid'] ?? ''));
|
||||
if ($orcid === '') {
|
||||
continue;
|
||||
}
|
||||
$displayName = trim((string)($a['name'] ?? ''));
|
||||
$position = ($i === 0) ? 'first' : (($i === $total - 1) ? 'last' : 'middle');
|
||||
$list[] = [
|
||||
'openalex_id' => '',
|
||||
'orcid' => $orcid,
|
||||
'display_name' => $displayName,
|
||||
'author_position' => $position,
|
||||
'is_corresponding' => false,
|
||||
'identity_keys' => ['orcid:' . $orcid],
|
||||
];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
private function parseDateParts($dateObj)
|
||||
{
|
||||
if (!isset($dateObj['date-parts'][0])) {
|
||||
|
||||
Reference in New Issue
Block a user