Files
tougao/application/api/controller/Author.php
2026-06-05 11:27:11 +08:00

303 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\api\controller;
use app\common\service\AuthorBackgroundService;
use think\Controller;
/**
* 作者背调HTML 报告页 + JSON API
*
* 主接口index / background_report / due_diligence
*/
class Author extends Controller
{
/** @var AuthorBackgroundService */
private $bgService;
public function __construct(\think\Request $request = null)
{
parent::__construct($request);
$this->bgService = new AuthorBackgroundService();
}
/**
* 作者背调 HTML 页面入口
*
* 1. 传了 ORCID → 直接生成报告
* 2. 未传 ORCID + 姓氏(机构选填)→ 仅按姓名搜 ORCID1 条直接报告,多条显示选择列表
*/
public function index()
{
@set_time_limit(120);
$formAction = $this->resolveFormAction();
$params = $this->resolveBackgroundParams();
$orcidNorm = $this->bgService->normalizeOrcid($params['orcid']);
if ($orcidNorm === ''
&& $params['last_name'] === ''
&& $params['first_name'] === ''
&& $params['institution'] === ''
) {
$this->assign('form_action', $formAction);
return $this->fetch('author/index');
}
// 1. 有 ORCID → 直接报告页
if ($orcidNorm !== '') {
return $this->renderReportPage($params, $formAction);
}
// 2. 无 ORCID → 姓氏必填,机构选填
if ($params['last_name'] === '') {
$this->assign([
'form_action' => $formAction,
'error_msg' => '未填 ORCID 时,请填写姓氏',
'last_name' => $params['last_name'],
'first_name' => $params['first_name'],
'institution' => $params['institution'],
]);
return $this->fetch('author/index');
}
// 3. 仅按姓名搜 ORCID机构只做排序校验
$search = $this->bgService->searchOrcidCandidates(
$params['last_name'],
$params['first_name'],
$params['institution']
);
$candidates = $search['candidates'] ?? [];
if (empty($candidates)) {
return $this->renderOrcidRequiredPage($params, $formAction, '已在 OpenAlex、ORCID 官网、Scopus 按姓名检索,未找到带 ORCID 的作者');
}
if (count($candidates) > 1) {
$this->assignCandidateListView($candidates, $params, $formAction);
return $this->fetch('author/select_orcid');
}
return $this->redirect($this->buildReportEntryUrl($formAction, $params, $candidates[0]['orcid']));
}
/**
* 医学期刊作者背景调查报告ORCID 必填)
*
* POST/GET 参数:
* orcid / orcid_id ORCID必填
* lastName / last_name 姓(选填,用于 PubMed 辅助检索与报告展示)
* firstName / first_name 名(选填)
* institution / affiliation 机构(选填)
*/
public function background_report()
{
@set_time_limit(120);
$params = $this->resolveBackgroundParams();
$result = $this->bgService->buildReport(
$params['orcid'],
$params['last_name'],
$params['first_name'],
$params['institution']
);
if (empty($result['ok'])) {
$code = !empty($result['need_select']) ? 2 : 0;
return json([
'code' => $code,
'msg' => $result['msg'] ?? '查询失败',
'data' => $result['data'] ?? null,
]);
}
return json([
'code' => 1,
'msg' => 'success',
'data' => $result['data'],
]);
}
/** 与 background_report 相同(路由兼容) */
public function due_diligence()
{
return $this->background_report();
}
/**
* 解析背调查询参数(兼容多种命名)
*/
private function resolveBackgroundParams()
{
$pick = function (...$keys) {
foreach ($keys as $k) {
$v = trim((string) input('param.' . $k, ''));
if ($v === '') {
$v = trim((string) input('post.' . $k, ''));
}
if ($v === '') {
$v = trim((string) input('get.' . $k, ''));
}
if ($v !== '') {
return $v;
}
}
return '';
};
return [
'orcid' => $pick('orcid', 'orcid_id'),
'last_name' => $pick('lastName', 'last_name', 'lastname', 'surname'),
'first_name' => $pick('firstName', 'first_name', 'firstname', 'given_name'),
'institution' => $pick('institution', 'affiliation', 'affil', 'org'),
];
}
private function resolveFormAction()
{
return rtrim($this->request->root(), '/') . '/api/author/index';
}
private function renderReportPage(array $params, $formAction)
{
$result = $this->bgService->buildReport(
$params['orcid'],
$params['last_name'],
$params['first_name'],
$params['institution']
);
if (empty($result['ok'])) {
$data = $result['data'] ?? [];
if (!empty($result['need_select'])) {
$this->assignCandidateListView($data['candidates'] ?? [], $params, $formAction);
return $this->fetch('author/select_orcid');
}
if (!empty($data['orcid_required'])) {
return $this->renderOrcidRequiredPage($params, $formAction, $data['hint'] ?? '');
}
$this->assign([
'form_action' => $formAction,
'error_msg' => $result['msg'] ?? '查询失败',
]);
return $this->fetch('author/index');
}
$this->assignReportView($result['data'], $formAction);
return $this->fetch('author/report');
}
private function renderOrcidRequiredPage(array $params, $formAction, $hint = '')
{
$this->assign([
'form_action' => $formAction,
'submitted_name' => trim($params['first_name'] . ' ' . $params['last_name']),
'submitted_institution' => $params['institution'],
'last_name' => $params['last_name'],
'first_name' => $params['first_name'],
'institution' => $params['institution'],
'hint' => $hint,
]);
return $this->fetch('author/orcid_required');
}
private function buildReportEntryUrl($formAction, array $params, $orcid)
{
return $formAction . '?' . http_build_query(
array_filter([
'orcid' => $orcid,
'lastName' => $params['last_name'] ?? '',
'firstName' => $params['first_name'] ?? '',
'institution' => $params['institution'] ?? '',
], function ($v) {
return trim((string) $v) !== '';
}),
'',
'&',
PHP_QUERY_RFC3986
);
}
private function assignCandidateListView(array $candidates, array $params, $formAction)
{
foreach ($candidates as $idx => $item) {
$candidates[$idx]['report_url'] = $this->buildReportEntryUrl(
$formAction,
$params,
$item['orcid'] ?? ''
);
$candidates[$idx]['matched_class'] = !empty($item['institution_matched']) ? 'match' : '';
$name = trim((string) ($item['display_name'] ?? ''));
$candidates[$idx]['avatar_letter'] = $name !== ''
? mb_strtoupper(mb_substr($name, 0, 1))
: '?';
}
$this->assign([
'form_action' => $formAction,
'candidates' => $candidates,
'candidate_count' => count($candidates),
'submitted_name' => trim(($params['first_name'] ?? '') . ' ' . ($params['last_name'] ?? '')),
'submitted_institution' => $params['institution'] ?? '',
'last_name' => $params['last_name'] ?? '',
'first_name' => $params['first_name'] ?? '',
'institution' => $params['institution'] ?? '',
]);
}
private function assignReportView(array $report, $formAction)
{
$dupPaperCount = 0;
$duplicates = $report['duplicates'] ?? [];
foreach ($duplicates as $idx => $dg) {
$duplicates[$idx]['paper_count'] = count($dg['papers'] ?? []);
$dupPaperCount += $duplicates[$idx]['paper_count'];
foreach ($duplicates[$idx]['papers'] as $pi => $dp) {
$src = strtolower((string) ($dp['source'] ?? 'orcid'));
$duplicates[$idx]['papers'][$pi]['source_class'] = in_array($src, ['orcid', 'pubmed'], true) ? $src : 'orcid';
}
}
$report['duplicates'] = $duplicates;
$rw = $report['retraction_watch'] ?? [];
$items = $rw['items'] ?? [];
foreach ($items as $idx => $it) {
$title = !empty($it['author_title']) ? $it['author_title'] : ($it['title'] ?? '');
$items[$idx]['display_title'] = mb_substr($title, 0, 120);
$items[$idx]['reason_short'] = mb_substr((string) ($it['reason'] ?? ''), 0, 200);
$linkUrl = trim((string) ($it['url'] ?? ''));
if ($linkUrl === '') {
$linkUrl = 'https://retractionwatch.com/?s=' . rawurlencode((string) ($it['title'] ?? ''));
}
$items[$idx]['link_url'] = $linkUrl;
}
$report['retraction_watch']['items'] = $items;
$riskLevel = (string) ($report['conclusion']['risk_level'] ?? '');
$riskClass = 'risk-default';
if (strpos($riskLevel, '高风险') !== false) {
$riskClass = 'risk-high';
} elseif (strpos($riskLevel, '中风险') !== false) {
$riskClass = 'risk-mid';
} elseif (strpos($riskLevel, '低风险') !== false) {
$riskClass = 'risk-low';
}
$this->assign([
'form_action' => $formAction,
'report' => $report,
'risk_class' => $riskClass,
'orcid_affiliations_text' => implode('', $report['basic']['orcid_affiliations'] ?? []),
'openalex_institutions_text' => implode('', $report['basic']['openalex_institutions'] ?? []),
'topics_text' => implode('', $report['metrics']['topics'] ?? []),
'rw_match_total' => (int) ($rw['doi_match_count'] ?? 0)
+ (int) ($rw['name_match_count'] ?? 0)
+ (int) ($rw['name_loose_match_count'] ?? 0),
'dup_group_count' => count($duplicates),
'dup_paper_count' => $dupPaperCount,
'pubmed_list_count' => min(10, count($report['pubmed_papers'] ?? [])),
'orcid_section_num' => (($report['metrics']['pubmed_total'] ?? 0) > 0) ? '七' : '六',
]);
}
}